color-picker.js 9.5 KB

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