color-picker.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /**
  2. * @output wp-admin/js/color-picker.js
  3. */
  4. /* global wpColorPickerL10n */
  5. ( function( $, undef ) {
  6. var ColorPicker,
  7. _before = '<button type="button" class="button wp-color-result" aria-expanded="false"><span class="wp-color-result-text"></span></button>',
  8. _after = '<div class="wp-picker-holder" />',
  9. _wrap = '<div class="wp-picker-container" />',
  10. _button = '<input type="button" class="button button-small" />',
  11. _wrappingLabel = '<label></label>',
  12. _wrappingLabelText = '<span class="screen-reader-text"></span>';
  13. /**
  14. * Creates a jQuery UI color picker that is used in the theme customizer.
  15. *
  16. * @class $.widget.wp.wpColorPicker
  17. *
  18. * @since 3.5.0
  19. */
  20. ColorPicker = /** @lends $.widget.wp.wpColorPicker.prototype */{
  21. options: {
  22. defaultColor: false,
  23. change: false,
  24. clear: false,
  25. hide: true,
  26. palettes: true,
  27. width: 255,
  28. mode: 'hsv',
  29. type: 'full',
  30. slider: 'horizontal'
  31. },
  32. /**
  33. * Creates a color picker that only allows you to adjust the hue.
  34. *
  35. * @since 3.5.0
  36. *
  37. * @access private
  38. *
  39. * @returns {void}
  40. */
  41. _createHueOnly: function() {
  42. var self = this,
  43. el = self.element,
  44. color;
  45. el.hide();
  46. // Set the saturation to the maximum level.
  47. color = 'hsl(' + el.val() + ', 100, 50)';
  48. // Create an instance of the color picker, using the hsl mode.
  49. el.iris( {
  50. mode: 'hsl',
  51. type: 'hue',
  52. hide: false,
  53. color: color,
  54. /**
  55. * Handles the onChange event if one has been defined in the options.
  56. *
  57. * @ignore
  58. *
  59. * @param {Event} event The event that's being called.
  60. * @param {HTMLElement} ui The HTMLElement containing the color picker.
  61. *
  62. * @returns {void}
  63. */
  64. change: function( event, ui ) {
  65. if ( $.isFunction( self.options.change ) ) {
  66. self.options.change.call( this, event, ui );
  67. }
  68. },
  69. width: self.options.width,
  70. slider: self.options.slider
  71. } );
  72. },
  73. /**
  74. * Creates the color picker, sets default values, css classes and wraps it all in HTML.
  75. *
  76. * @since 3.5.0
  77. *
  78. * @access private
  79. *
  80. * @returns {void}
  81. */
  82. _create: function() {
  83. // Return early if Iris support is missing.
  84. if ( ! $.support.iris ) {
  85. return;
  86. }
  87. var self = this,
  88. el = self.element;
  89. // Override default options with options bound to the element.
  90. $.extend( self.options, el.data() );
  91. // Create a color picker which only allows adjustments to the hue.
  92. if ( self.options.type === 'hue' ) {
  93. return self._createHueOnly();
  94. }
  95. // Bind the close event.
  96. self.close = $.proxy( self.close, self );
  97. self.initialValue = el.val();
  98. // Add a CSS class to the input field.
  99. el.addClass( 'wp-color-picker' );
  100. /*
  101. * Check if there's already a wrapping label, e.g. in the Customizer.
  102. * If there's no label, add a default one to match the Customizer template.
  103. */
  104. if ( ! el.parent( 'label' ).length ) {
  105. // Wrap the input field in the default label.
  106. el.wrap( _wrappingLabel );
  107. // Insert the default label text.
  108. self.wrappingLabelText = $( _wrappingLabelText )
  109. .insertBefore( el )
  110. .text( wpColorPickerL10n.defaultLabel );
  111. }
  112. /*
  113. * At this point, either it's the standalone version or the Customizer
  114. * one, we have a wrapping label to use as hook in the DOM, let's store it.
  115. */
  116. self.wrappingLabel = el.parent();
  117. // Wrap the label in the main wrapper.
  118. self.wrappingLabel.wrap( _wrap );
  119. // Store a reference to the main wrapper.
  120. self.wrap = self.wrappingLabel.parent();
  121. // Set up the toggle button and insert it before the wrapping label.
  122. self.toggler = $( _before )
  123. .insertBefore( self.wrappingLabel )
  124. .css( { backgroundColor: self.initialValue } );
  125. // Set the toggle button span element text.
  126. self.toggler.find( '.wp-color-result-text' ).text( wpColorPickerL10n.pick );
  127. // Set up the Iris container and insert it after the wrapping label.
  128. self.pickerContainer = $( _after ).insertAfter( self.wrappingLabel );
  129. // Store a reference to the Clear/Default button.
  130. self.button = $( _button );
  131. // Set up the Clear/Default button.
  132. if ( self.options.defaultColor ) {
  133. self.button
  134. .addClass( 'wp-picker-default' )
  135. .val( wpColorPickerL10n.defaultString )
  136. .attr( 'aria-label', wpColorPickerL10n.defaultAriaLabel );
  137. } else {
  138. self.button
  139. .addClass( 'wp-picker-clear' )
  140. .val( wpColorPickerL10n.clear )
  141. .attr( 'aria-label', wpColorPickerL10n.clearAriaLabel );
  142. }
  143. // Wrap the wrapping label in its wrapper and append the Clear/Default button.
  144. self.wrappingLabel
  145. .wrap( '<span class="wp-picker-input-wrap hidden" />' )
  146. .after( self.button );
  147. /*
  148. * The input wrapper now contains the label+input+Clear/Default button.
  149. * Store a reference to the input wrapper: we'll use this to toggle
  150. * the controls visibility.
  151. */
  152. self.inputWrapper = el.closest( '.wp-picker-input-wrap' );
  153. el.iris( {
  154. target: self.pickerContainer,
  155. hide: self.options.hide,
  156. width: self.options.width,
  157. mode: self.options.mode,
  158. palettes: self.options.palettes,
  159. /**
  160. * Handles the onChange event if one has been defined in the options and additionally
  161. * sets the background color for the toggler element.
  162. *
  163. * @since 3.5.0
  164. *
  165. * @ignore
  166. *
  167. * @param {Event} event The event that's being called.
  168. * @param {HTMLElement} ui The HTMLElement containing the color picker.
  169. *
  170. * @returns {void}
  171. */
  172. change: function( event, ui ) {
  173. self.toggler.css( { backgroundColor: ui.color.toString() } );
  174. if ( $.isFunction( self.options.change ) ) {
  175. self.options.change.call( this, event, ui );
  176. }
  177. }
  178. } );
  179. el.val( self.initialValue );
  180. self._addListeners();
  181. // Force the color picker to always be closed on initial load.
  182. if ( ! self.options.hide ) {
  183. self.toggler.click();
  184. }
  185. },
  186. /**
  187. * Binds event listeners to the color picker.
  188. *
  189. * @since 3.5.0
  190. *
  191. * @access private
  192. *
  193. * @returns {void}
  194. */
  195. _addListeners: function() {
  196. var self = this;
  197. /**
  198. * Prevent any clicks inside this widget from leaking to the top and closing it.
  199. *
  200. * @since 3.5.0
  201. *
  202. * @param {Event} event The event that's being called.
  203. *
  204. * @returs {void}
  205. */
  206. self.wrap.on( 'click.wpcolorpicker', function( event ) {
  207. event.stopPropagation();
  208. });
  209. /**
  210. * Open or close the color picker depending on the class.
  211. *
  212. * @since 3.5
  213. */
  214. self.toggler.click( function(){
  215. if ( self.toggler.hasClass( 'wp-picker-open' ) ) {
  216. self.close();
  217. } else {
  218. self.open();
  219. }
  220. });
  221. /**
  222. * Checks if value is empty when changing the color in the color picker.
  223. * If so, the background color is cleared.
  224. *
  225. * @since 3.5.0
  226. *
  227. * @param {Event} event The event that's being called.
  228. *
  229. * @returns {void}
  230. */
  231. self.element.change( function( event ) {
  232. var me = $( this ),
  233. val = me.val();
  234. if ( val === '' || val === '#' ) {
  235. self.toggler.css( 'backgroundColor', '' );
  236. // Fire clear callback if we have one.
  237. if ( $.isFunction( self.options.clear ) ) {
  238. self.options.clear.call( this, event );
  239. }
  240. }
  241. });
  242. /**
  243. * Enables the user to either clear the color in the color picker or revert back to the default color.
  244. *
  245. * @since 3.5.0
  246. *
  247. * @param {Event} event The event that's being called.
  248. *
  249. * @returns {void}
  250. */
  251. self.button.click( function( event ) {
  252. var me = $( this );
  253. if ( me.hasClass( 'wp-picker-clear' ) ) {
  254. self.element.val( '' );
  255. self.toggler.css( 'backgroundColor', '' );
  256. if ( $.isFunction( self.options.clear ) ) {
  257. self.options.clear.call( this, event );
  258. }
  259. } else if ( me.hasClass( 'wp-picker-default' ) ) {
  260. self.element.val( self.options.defaultColor ).change();
  261. }
  262. });
  263. },
  264. /**
  265. * Opens the color picker dialog.
  266. *
  267. * @since 3.5.0
  268. *
  269. * @returns {void}
  270. */
  271. open: function() {
  272. this.element.iris( 'toggle' );
  273. this.inputWrapper.removeClass( 'hidden' );
  274. this.wrap.addClass( 'wp-picker-active' );
  275. this.toggler
  276. .addClass( 'wp-picker-open' )
  277. .attr( 'aria-expanded', 'true' );
  278. $( 'body' ).trigger( 'click.wpcolorpicker' ).on( 'click.wpcolorpicker', this.close );
  279. },
  280. /**
  281. * Closes the color picker dialog.
  282. *
  283. * @since 3.5.0
  284. *
  285. * @returns {void}
  286. */
  287. close: function() {
  288. this.element.iris( 'toggle' );
  289. this.inputWrapper.addClass( 'hidden' );
  290. this.wrap.removeClass( 'wp-picker-active' );
  291. this.toggler
  292. .removeClass( 'wp-picker-open' )
  293. .attr( 'aria-expanded', 'false' );
  294. $( 'body' ).off( 'click.wpcolorpicker', this.close );
  295. },
  296. /**
  297. * Returns the iris object if no new color is provided. If a new color is provided, it sets the new color.
  298. *
  299. * @param newColor {string|*} The new color to use. Can be undefined.
  300. *
  301. * @since 3.5.0
  302. *
  303. * @returns {string} The element's color
  304. */
  305. color: function( newColor ) {
  306. if ( newColor === undef ) {
  307. return this.element.iris( 'option', 'color' );
  308. }
  309. this.element.iris( 'option', 'color', newColor );
  310. },
  311. /**
  312. * Returns the iris object if no new default color is provided.
  313. * If a new default color is provided, it sets the new default color.
  314. *
  315. * @param newDefaultColor {string|*} The new default color to use. Can be undefined.
  316. *
  317. * @since 3.5.0
  318. *
  319. * @returns {boolean|string} The element's color.
  320. */
  321. defaultColor: function( newDefaultColor ) {
  322. if ( newDefaultColor === undef ) {
  323. return this.options.defaultColor;
  324. }
  325. this.options.defaultColor = newDefaultColor;
  326. }
  327. };
  328. // Register the color picker as a widget.
  329. $.widget( 'wp.wpColorPicker', ColorPicker );
  330. }( jQuery ) );