@thednp/color-picker 1.0.1 → 2.0.0-alpha10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/.eslintrc.cjs +199 -0
  2. package/.lgtm.yml +9 -0
  3. package/.prettierrc.json +15 -0
  4. package/.stylelintrc.json +236 -0
  5. package/README.md +55 -72
  6. package/compile.js +51 -0
  7. package/cypress/e2e/color-palette.cy.ts +128 -0
  8. package/cypress/e2e/color-picker.cy.ts +909 -0
  9. package/cypress/fixtures/colorNamesFrench.js +3 -0
  10. package/cypress/fixtures/componentLabelsFrench.js +21 -0
  11. package/cypress/fixtures/format.js +3 -0
  12. package/cypress/fixtures/getMarkup.js +35 -0
  13. package/cypress/fixtures/getRandomInt.js +6 -0
  14. package/cypress/fixtures/sampleWebcolors.js +18 -0
  15. package/cypress/fixtures/testSample.js +8 -0
  16. package/cypress/plugins/esbuild-istanbul.ts +50 -0
  17. package/cypress/plugins/tsCompile.ts +34 -0
  18. package/cypress/support/commands.ts +0 -0
  19. package/cypress/support/e2e.ts +21 -0
  20. package/cypress/test.html +23 -0
  21. package/cypress.config.ts +29 -0
  22. package/dist/css/color-picker.css +25 -54
  23. package/dist/css/color-picker.min.css +2 -2
  24. package/dist/css/color-picker.rtl.css +25 -54
  25. package/dist/css/color-picker.rtl.min.css +2 -2
  26. package/dist/js/color-picker.cjs +8 -0
  27. package/dist/js/color-picker.cjs.map +1 -0
  28. package/dist/js/color-picker.d.ts +296 -0
  29. package/dist/js/color-picker.js +5 -3570
  30. package/dist/js/color-picker.js.map +1 -0
  31. package/dist/js/color-picker.mjs +2618 -0
  32. package/dist/js/color-picker.mjs.map +1 -0
  33. package/dts.config.ts +15 -0
  34. package/package.json +116 -108
  35. package/src/scss/_variables.scss +0 -1
  36. package/src/scss/color-picker.rtl.scss +4 -0
  37. package/src/scss/color-picker.scss +93 -51
  38. package/src/ts/colorPalette.ts +89 -0
  39. package/src/{js/color-picker.js → ts/index.ts} +498 -509
  40. package/src/ts/interface/ColorNames.ts +20 -0
  41. package/src/ts/interface/colorPickerLabels.ts +20 -0
  42. package/src/ts/interface/colorPickerOptions.ts +11 -0
  43. package/src/ts/interface/paletteOptions.ts +6 -0
  44. package/src/ts/util/colorNames.ts +21 -0
  45. package/src/{js/util/colorPickerLabels.js → ts/util/colorPickerLabels.ts} +4 -2
  46. package/src/ts/util/getColorControls.ts +90 -0
  47. package/src/{js/util/getColorForm.js → ts/util/getColorForm.ts} +28 -18
  48. package/src/{js/util/getColorMenu.js → ts/util/getColorMenu.ts} +21 -30
  49. package/src/ts/util/isValidJSON.ts +19 -0
  50. package/src/{js/util/setMarkup.js → ts/util/setMarkup.ts} +61 -50
  51. package/tsconfig.json +29 -0
  52. package/vite.config.ts +34 -0
  53. package/dist/js/color-esm.js +0 -1164
  54. package/dist/js/color-esm.min.js +0 -2
  55. package/dist/js/color-palette-esm.js +0 -1235
  56. package/dist/js/color-palette-esm.min.js +0 -2
  57. package/dist/js/color-palette.js +0 -1243
  58. package/dist/js/color-palette.min.js +0 -2
  59. package/dist/js/color-picker-element-esm.js +0 -3718
  60. package/dist/js/color-picker-element-esm.min.js +0 -2
  61. package/dist/js/color-picker-element.js +0 -3726
  62. package/dist/js/color-picker-element.min.js +0 -2
  63. package/dist/js/color-picker-esm.js +0 -3565
  64. package/dist/js/color-picker-esm.min.js +0 -2
  65. package/dist/js/color-picker.min.js +0 -2
  66. package/dist/js/color.js +0 -1172
  67. package/dist/js/color.min.js +0 -2
  68. package/src/js/color-palette.js +0 -75
  69. package/src/js/color-picker-element.js +0 -107
  70. package/src/js/color.js +0 -1104
  71. package/src/js/index.js +0 -8
  72. package/src/js/util/colorNames.js +0 -6
  73. package/src/js/util/getColorControls.js +0 -103
  74. package/src/js/util/isValidJSON.js +0 -13
  75. package/src/js/util/nonColors.js +0 -5
  76. package/src/js/util/roundPart.js +0 -9
  77. package/src/js/util/setCSSProperties.js +0 -12
  78. package/src/js/util/tabindex.js +0 -3
  79. package/src/js/util/toggleCEAttr.js +0 -70
  80. package/src/js/util/version.js +0 -5
  81. package/src/js/version.js +0 -5
  82. package/types/cp.d.ts +0 -558
  83. package/types/index.d.ts +0 -44
  84. package/types/source/source.ts +0 -4
  85. package/types/source/types.d.ts +0 -92
  86. /package/src/{js/util/vHidden.js → ts/util/vHidden.ts} +0 -0
@@ -1,3573 +1,8 @@
1
- /*!
2
- * ColorPicker v1.0.1 (http://thednp.github.io/color-picker)
3
- * Copyright 2022 © thednp
4
- * Licensed under MIT (https://github.com/thednp/color-picker/blob/master/LICENSE)
5
- */
6
- (function (global, factory) {
7
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8
- typeof define === 'function' && define.amd ? define(factory) :
9
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ColorPicker = factory());
10
- })(this, (function () { 'use strict';
11
-
12
- /** @type {Record<string, any>} */
13
- const EventRegistry = {};
14
-
15
- /**
16
- * The global event listener.
17
- *
18
- * @type {EventListener}
19
- * @this {EventTarget}
20
- */
21
- function globalListener(e) {
22
- const that = this;
23
- const { type } = e;
24
-
25
- [...EventRegistry[type]].forEach((elementsMap) => {
26
- const [element, listenersMap] = elementsMap;
27
- /* istanbul ignore else */
28
- if (element === that) {
29
- [...listenersMap].forEach((listenerMap) => {
30
- const [listener, options] = listenerMap;
31
- listener.apply(element, [e]);
32
-
33
- if (options && options.once) {
34
- removeListener(element, type, listener, options);
35
- }
36
- });
37
- }
38
- });
39
- }
40
-
41
- /**
42
- * Register a new listener with its options and attach the `globalListener`
43
- * to the target if this is the first listener.
44
- *
45
- * @type {Listener.ListenerAction<EventTarget>}
46
- */
47
- const addListener = (element, eventType, listener, options) => {
48
- // get element listeners first
49
- if (!EventRegistry[eventType]) {
50
- EventRegistry[eventType] = new Map();
51
- }
52
- const oneEventMap = EventRegistry[eventType];
53
-
54
- if (!oneEventMap.has(element)) {
55
- oneEventMap.set(element, new Map());
56
- }
57
- const oneElementMap = oneEventMap.get(element);
58
-
59
- // get listeners size
60
- const { size } = oneElementMap;
61
-
62
- // register listener with its options
63
- oneElementMap.set(listener, options);
64
-
65
- // add listener last
66
- if (!size) {
67
- element.addEventListener(eventType, globalListener, options);
68
- }
69
- };
70
-
71
- /**
72
- * Remove a listener from registry and detach the `globalListener`
73
- * if no listeners are found in the registry.
74
- *
75
- * @type {Listener.ListenerAction<EventTarget>}
76
- */
77
- const removeListener = (element, eventType, listener, options) => {
78
- // get listener first
79
- const oneEventMap = EventRegistry[eventType];
80
- const oneElementMap = oneEventMap && oneEventMap.get(element);
81
- const savedOptions = oneElementMap && oneElementMap.get(listener);
82
-
83
- // also recover initial options
84
- const { options: eventOptions } = savedOptions !== undefined
85
- ? savedOptions
86
- : { options };
87
-
88
- // unsubscribe second, remove from registry
89
- if (oneElementMap && oneElementMap.has(listener)) oneElementMap.delete(listener);
90
- if (oneEventMap && (!oneElementMap || !oneElementMap.size)) oneEventMap.delete(element);
91
- if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType];
92
-
93
- // remove listener last
94
- /* istanbul ignore else */
95
- if (!oneElementMap || !oneElementMap.size) {
96
- element.removeEventListener(eventType, globalListener, eventOptions);
97
- }
98
- };
99
-
100
- /**
101
- * A global namespace for aria-description.
102
- * @type {string}
103
- */
104
- const ariaDescription = 'aria-description';
105
-
106
- /**
107
- * A global namespace for aria-selected.
108
- * @type {string}
109
- */
110
- const ariaSelected = 'aria-selected';
111
-
112
- /**
113
- * A global namespace for aria-expanded.
114
- * @type {string}
115
- */
116
- const ariaExpanded = 'aria-expanded';
117
-
118
- /**
119
- * A global namespace for aria-valuetext.
120
- * @type {string}
121
- */
122
- const ariaValueText = 'aria-valuetext';
123
-
124
- /**
125
- * A global namespace for aria-valuenow.
126
- * @type {string}
127
- */
128
- const ariaValueNow = 'aria-valuenow';
129
-
130
- /**
131
- * A global namespace for `ArrowDown` key.
132
- * @type {string} e.which = 40 equivalent
133
- */
134
- const keyArrowDown = 'ArrowDown';
135
-
136
- /**
137
- * A global namespace for `ArrowUp` key.
138
- * @type {string} e.which = 38 equivalent
139
- */
140
- const keyArrowUp = 'ArrowUp';
141
-
142
- /**
143
- * A global namespace for `ArrowLeft` key.
144
- * @type {string} e.which = 37 equivalent
145
- */
146
- const keyArrowLeft = 'ArrowLeft';
147
-
148
- /**
149
- * A global namespace for `ArrowRight` key.
150
- * @type {string} e.which = 39 equivalent
151
- */
152
- const keyArrowRight = 'ArrowRight';
153
-
154
- /**
155
- * A global namespace for `Enter` key.
156
- * @type {string} e.which = 13 equivalent
157
- */
158
- const keyEnter = 'Enter';
159
-
160
- /**
161
- * A global namespace for `Space` key.
162
- * @type {string} e.which = 32 equivalent
163
- */
164
- const keySpace = 'Space';
165
-
166
- /**
167
- * A global namespace for `Escape` key.
168
- * @type {string} e.which = 27 equivalent
169
- */
170
- const keyEscape = 'Escape';
171
-
172
- /**
173
- * A global namespace for `focusin` event.
174
- * @type {string}
175
- */
176
- const focusinEvent = 'focusin';
177
-
178
- /**
179
- * A global namespace for `click` event.
180
- * @type {string}
181
- */
182
- const mouseclickEvent = 'click';
183
-
184
- /**
185
- * A global namespace for `keydown` event.
186
- * @type {string}
187
- */
188
- const keydownEvent = 'keydown';
189
-
190
- /**
191
- * A global namespace for `change` event.
192
- * @type {string}
193
- */
194
- const changeEvent = 'change';
195
-
196
- /**
197
- * A global namespace for `touchmove` event.
198
- * @type {string}
199
- */
200
- const touchmoveEvent = 'touchmove';
201
-
202
- /**
203
- * A global namespace for `pointerdown` event.
204
- * @type {string}
205
- */
206
- const pointerdownEvent = 'pointerdown';
207
-
208
- /**
209
- * A global namespace for `pointermove` event.
210
- * @type {string}
211
- */
212
- const pointermoveEvent = 'pointermove';
213
-
214
- /**
215
- * A global namespace for `pointerup` event.
216
- * @type {string}
217
- */
218
- const pointerupEvent = 'pointerup';
219
-
220
- /**
221
- * A global namespace for `scroll` event.
222
- * @type {string}
223
- */
224
- const scrollEvent = 'scroll';
225
-
226
- /**
227
- * A global namespace for `keyup` event.
228
- * @type {string}
229
- */
230
- const keyupEvent = 'keyup';
231
-
232
- /**
233
- * A global namespace for `resize` event.
234
- * @type {string}
235
- */
236
- const resizeEvent = 'resize';
237
-
238
- /**
239
- * A global namespace for `focusout` event.
240
- * @type {string}
241
- */
242
- const focusoutEvent = 'focusout';
243
-
244
- /**
245
- * Returns the `document` or the `#document` element.
246
- * @see https://github.com/floating-ui/floating-ui
247
- * @param {(Node | HTMLElement | Element | globalThis)=} node
248
- * @returns {Document}
249
- */
250
- function getDocument(node) {
251
- if (node instanceof HTMLElement) return node.ownerDocument;
252
- if (node instanceof Window) return node.document;
253
- return window.document;
254
- }
255
-
256
- /**
257
- * Returns the `document.documentElement` or the `<html>` element.
258
- *
259
- * @param {(Node | HTMLElement | Element | globalThis)=} node
260
- * @returns {HTMLElement | HTMLHtmlElement}
261
- */
262
- function getDocumentElement(node) {
263
- return getDocument(node).documentElement;
264
- }
265
-
266
- /**
267
- * Shortcut for `window.getComputedStyle(element).propertyName`
268
- * static method.
269
- *
270
- * * If `element` parameter is not an `HTMLElement`, `getComputedStyle`
271
- * throws a `ReferenceError`.
272
- *
273
- * @param {HTMLElement | Element} element target
274
- * @param {string} property the css property
275
- * @return {string} the css property value
276
- */
277
- function getElementStyle(element, property) {
278
- const computedStyle = getComputedStyle(element);
279
-
280
- // @ts-ignore -- must use camelcase strings,
281
- // or non-camelcase strings with `getPropertyValue`
282
- return property in computedStyle ? computedStyle[property] : '';
283
- }
284
-
285
- let elementUID = 0;
286
- let elementMapUID = 0;
287
- const elementIDMap = new Map();
288
-
289
- /**
290
- * Returns a unique identifier for popover, tooltip, scrollspy.
291
- *
292
- * @param {HTMLElement | Element} element target element
293
- * @param {string=} key predefined key
294
- * @returns {number} an existing or new unique ID
295
- */
296
- function getUID(element, key) {
297
- let result = key ? elementUID : elementMapUID;
298
-
299
- if (key) {
300
- const elID = getUID(element);
301
- const elMap = elementIDMap.get(elID) || new Map();
302
- if (!elementIDMap.has(elID)) {
303
- elementIDMap.set(elID, elMap);
304
- }
305
- if (!elMap.has(key)) {
306
- elMap.set(key, result);
307
- elementUID += 1;
308
- } else result = elMap.get(key);
309
- } else {
310
- const elkey = element.id || element;
311
-
312
- if (!elementIDMap.has(elkey)) {
313
- elementIDMap.set(elkey, result);
314
- elementMapUID += 1;
315
- } else result = elementIDMap.get(elkey);
316
- }
317
- return result;
318
- }
319
-
320
- /**
321
- * Returns the bounding client rect of a target `HTMLElement`.
322
- *
323
- * @see https://github.com/floating-ui/floating-ui
324
- *
325
- * @param {HTMLElement | Element} element event.target
326
- * @param {boolean=} includeScale when *true*, the target scale is also computed
327
- * @returns {SHORTER.BoundingClientRect} the bounding client rect object
328
- */
329
- function getBoundingClientRect(element, includeScale) {
330
- const {
331
- width, height, top, right, bottom, left,
332
- } = element.getBoundingClientRect();
333
- let scaleX = 1;
334
- let scaleY = 1;
335
-
336
- if (includeScale && element instanceof HTMLElement) {
337
- const { offsetWidth, offsetHeight } = element;
338
- scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth || 1 : 1;
339
- scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight || 1 : 1;
340
- }
341
-
342
- return {
343
- width: width / scaleX,
344
- height: height / scaleY,
345
- top: top / scaleY,
346
- right: right / scaleX,
347
- bottom: bottom / scaleY,
348
- left: left / scaleX,
349
- x: left / scaleX,
350
- y: top / scaleY,
351
- };
352
- }
353
-
354
- /**
355
- * A global namespace for 'transitionDuration' string.
356
- * @type {string}
357
- */
358
- const transitionDuration = 'transitionDuration';
359
-
360
- /**
361
- * A global namespace for `transitionProperty` string for modern browsers.
362
- *
363
- * @type {string}
364
- */
365
- const transitionProperty = 'transitionProperty';
366
-
367
- /**
368
- * Utility to get the computed `transitionDuration`
369
- * from Element in miliseconds.
370
- *
371
- * @param {HTMLElement | Element} element target
372
- * @return {number} the value in miliseconds
373
- */
374
- function getElementTransitionDuration(element) {
375
- const propertyValue = getElementStyle(element, transitionProperty);
376
- const durationValue = getElementStyle(element, transitionDuration);
377
- const durationScale = durationValue.includes('ms') ? 1 : 1000;
378
- const duration = propertyValue && propertyValue !== 'none'
379
- ? parseFloat(durationValue) * durationScale : 0;
380
-
381
- return !Number.isNaN(duration) ? duration : 0;
382
- }
383
-
384
- /**
385
- * A global array of possible `ParentNode`.
386
- */
387
- const parentNodes = [Document, Element, HTMLElement];
388
-
389
- /**
390
- * A global array with `Element` | `HTMLElement`.
391
- */
392
- const elementNodes = [Element, HTMLElement];
393
-
394
- /**
395
- * Utility to check if target is typeof `HTMLElement`, `Element`, `Node`
396
- * or find one that matches a selector.
397
- *
398
- * @param {HTMLElement | Element | string} selector the input selector or target element
399
- * @param {(HTMLElement | Element | Document)=} parent optional node to look into
400
- * @return {(HTMLElement | Element)?} the `HTMLElement` or `querySelector` result
401
- */
402
- function querySelector(selector, parent) {
403
- const lookUp = parentNodes.some((x) => parent instanceof x)
404
- ? parent : getDocument();
405
-
406
- // @ts-ignore
407
- return elementNodes.some((x) => selector instanceof x)
408
- // @ts-ignore
409
- ? selector : lookUp.querySelector(selector);
410
- }
411
-
412
- /**
413
- * Shortcut for `HTMLElement.closest` method which also works
414
- * with children of `ShadowRoot`. The order of the parameters
415
- * is intentional since they're both required.
416
- *
417
- * @see https://stackoverflow.com/q/54520554/803358
418
- *
419
- * @param {HTMLElement | Element} element Element to look into
420
- * @param {string} selector the selector name
421
- * @return {(HTMLElement | Element)?} the query result
422
- */
423
- function closest(element, selector) {
424
- return element ? (element.closest(selector)
425
- // @ts-ignore -- break out of `ShadowRoot`
426
- || closest(element.getRootNode().host, selector)) : null;
427
- }
428
-
429
- /**
430
- * Shortcut for `HTMLElement.getElementsByClassName` method. Some `Node` elements
431
- * like `ShadowRoot` do not support `getElementsByClassName`.
432
- *
433
- * @param {string} selector the class name
434
- * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
435
- * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
436
- */
437
- function getElementsByClassName(selector, parent) {
438
- const lookUp = parent && parentNodes.some((x) => parent instanceof x)
439
- ? parent : getDocument();
440
- return lookUp.getElementsByClassName(selector);
441
- }
442
-
443
- /**
444
- * Shortcut for the `Element.dispatchEvent(Event)` method.
445
- *
446
- * @param {HTMLElement | Element} element is the target
447
- * @param {Event} event is the `Event` object
448
- */
449
- const dispatchEvent = (element, event) => element.dispatchEvent(event);
450
-
451
- /**
452
- * Shortcut for `Object.assign()` static method.
453
- * @param {Record<string, any>} obj a target object
454
- * @param {Record<string, any>} source a source object
455
- */
456
- const ObjectAssign = (obj, source) => Object.assign(obj, source);
457
-
458
- /** @type {Map<string, Map<HTMLElement | Element, Record<string, any>>>} */
459
- const componentData = new Map();
460
- /**
461
- * An interface for web components background data.
462
- * @see https://github.com/thednp/bootstrap.native/blob/master/src/components/base-component.js
463
- */
464
- const Data = {
465
- /**
466
- * Sets web components data.
467
- * @param {HTMLElement | Element | string} target target element
468
- * @param {string} component the component's name or a unique key
469
- * @param {Record<string, any>} instance the component instance
470
- */
471
- set: (target, component, instance) => {
472
- const element = querySelector(target);
473
- if (!element) return;
474
-
475
- if (!componentData.has(component)) {
476
- componentData.set(component, new Map());
477
- }
478
-
479
- const instanceMap = componentData.get(component);
480
- // @ts-ignore - not undefined, but defined right above
481
- instanceMap.set(element, instance);
482
- },
483
-
484
- /**
485
- * Returns all instances for specified component.
486
- * @param {string} component the component's name or a unique key
487
- * @returns {Map<HTMLElement | Element, Record<string, any>>?} all the component instances
488
- */
489
- getAllFor: (component) => {
490
- const instanceMap = componentData.get(component);
491
-
492
- return instanceMap || null;
493
- },
494
-
495
- /**
496
- * Returns the instance associated with the target.
497
- * @param {HTMLElement | Element | string} target target element
498
- * @param {string} component the component's name or a unique key
499
- * @returns {Record<string, any>?} the instance
500
- */
501
- get: (target, component) => {
502
- const element = querySelector(target);
503
- const allForC = Data.getAllFor(component);
504
- const instance = element && allForC && allForC.get(element);
505
-
506
- return instance || null;
507
- },
508
-
509
- /**
510
- * Removes web components data.
511
- * @param {HTMLElement | Element | string} target target element
512
- * @param {string} component the component's name or a unique key
513
- */
514
- remove: (target, component) => {
515
- const element = querySelector(target);
516
- const instanceMap = componentData.get(component);
517
- if (!instanceMap || !element) return;
518
-
519
- instanceMap.delete(element);
520
-
521
- if (instanceMap.size === 0) {
522
- componentData.delete(component);
523
- }
524
- },
525
- };
526
-
527
- /**
528
- * An alias for `Data.get()`.
529
- * @type {SHORTER.getInstance<any>}
530
- */
531
- const getInstance = (target, component) => Data.get(target, component);
532
-
533
- /**
534
- * Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
535
- * @param {HTMLElement | Element} element target element
536
- * @param {Partial<CSSStyleDeclaration>} styles attribute value
537
- */
538
- // @ts-ignore
539
- const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
540
-
541
- /**
542
- * Shortcut for `HTMLElement.getAttribute()` method.
543
- * @param {HTMLElement | Element} element target element
544
- * @param {string} attribute attribute name
545
- * @returns {string?} attribute value
546
- */
547
- const getAttribute = (element, attribute) => element.getAttribute(attribute);
548
-
549
- /**
550
- * The raw value or a given component option.
551
- *
552
- * @typedef {string | HTMLElement | Function | number | boolean | null} niceValue
553
- */
554
-
555
- /**
556
- * Utility to normalize component options
557
- *
558
- * @param {any} value the input value
559
- * @return {niceValue} the normalized value
560
- */
561
- function normalizeValue(value) {
562
- if (value === 'true') { // boolean
563
- return true;
564
- }
565
-
566
- if (value === 'false') { // boolean
567
- return false;
568
- }
569
-
570
- if (!Number.isNaN(+value)) { // number
571
- return +value;
572
- }
573
-
574
- if (value === '' || value === 'null') { // null
575
- return null;
576
- }
577
-
578
- // string / function / HTMLElement / object
579
- return value;
580
- }
581
-
582
- /**
583
- * Shortcut for `Object.keys()` static method.
584
- * @param {Record<string, any>} obj a target object
585
- * @returns {string[]}
586
- */
587
- const ObjectKeys = (obj) => Object.keys(obj);
588
-
589
- /**
590
- * Shortcut for `String.toLowerCase()`.
591
- *
592
- * @param {string} source input string
593
- * @returns {string} lowercase output string
594
- */
595
- const toLowerCase = (source) => source.toLowerCase();
596
-
597
- /**
598
- * Utility to normalize component options.
599
- *
600
- * @param {HTMLElement | Element} element target
601
- * @param {Record<string, any>} defaultOps component default options
602
- * @param {Record<string, any>} inputOps component instance options
603
- * @param {string=} ns component namespace
604
- * @return {Record<string, any>} normalized component options object
605
- */
606
- function normalizeOptions(element, defaultOps, inputOps, ns) {
607
- // @ts-ignore -- our targets are always `HTMLElement`
608
- const data = { ...element.dataset };
609
- /** @type {Record<string, any>} */
610
- const normalOps = {};
611
- /** @type {Record<string, any>} */
612
- const dataOps = {};
613
- const title = 'title';
614
-
615
- ObjectKeys(data).forEach((k) => {
616
- const key = ns && k.includes(ns)
617
- ? k.replace(ns, '').replace(/[A-Z]/, (match) => toLowerCase(match))
618
- : k;
619
-
620
- dataOps[key] = normalizeValue(data[k]);
621
- });
622
-
623
- ObjectKeys(inputOps).forEach((k) => {
624
- inputOps[k] = normalizeValue(inputOps[k]);
625
- });
626
-
627
- ObjectKeys(defaultOps).forEach((k) => {
628
- if (k in inputOps) {
629
- normalOps[k] = inputOps[k];
630
- } else if (k in dataOps) {
631
- normalOps[k] = dataOps[k];
632
- } else {
633
- normalOps[k] = k === title
634
- ? getAttribute(element, title)
635
- : defaultOps[k];
636
- }
637
- });
638
-
639
- return normalOps;
640
- }
641
-
642
- /**
643
- * Utility to force re-paint of an `HTMLElement` target.
644
- *
645
- * @param {HTMLElement | Element} element is the target
646
- * @return {number} the `Element.offsetHeight` value
647
- */
648
- // @ts-ignore
649
- const reflow = (element) => element.offsetHeight;
650
-
651
- /**
652
- * Utility to focus an `HTMLElement` target.
653
- *
654
- * @param {HTMLElement | Element} element is the target
655
- */
656
- // @ts-ignore -- `Element`s resulted from querySelector can focus too
657
- const focus = (element) => element.focus();
658
-
659
- /**
660
- * Check class in `HTMLElement.classList`.
661
- *
662
- * @param {HTMLElement | Element} element target
663
- * @param {string} classNAME to check
664
- * @returns {boolean}
665
- */
666
- function hasClass(element, classNAME) {
667
- return element.classList.contains(classNAME);
668
- }
669
-
670
- /**
671
- * Add class to `HTMLElement.classList`.
672
- *
673
- * @param {HTMLElement | Element} element target
674
- * @param {string} classNAME to add
675
- * @returns {void}
676
- */
677
- function addClass(element, classNAME) {
678
- element.classList.add(classNAME);
679
- }
680
-
681
- /**
682
- * Remove class from `HTMLElement.classList`.
683
- *
684
- * @param {HTMLElement | Element} element target
685
- * @param {string} classNAME to remove
686
- * @returns {void}
687
- */
688
- function removeClass(element, classNAME) {
689
- element.classList.remove(classNAME);
690
- }
691
-
692
- /**
693
- * Shortcut for `HTMLElement.setAttribute()` method.
694
- * @param {HTMLElement | Element} element target element
695
- * @param {string} attribute attribute name
696
- * @param {string} value attribute value
697
- * @returns {void}
698
- */
699
- const setAttribute = (element, attribute, value) => element.setAttribute(attribute, value);
700
-
701
- /**
702
- * Shortcut for `HTMLElement.removeAttribute()` method.
703
- * @param {HTMLElement | Element} element target element
704
- * @param {string} attribute attribute name
705
- * @returns {void}
706
- */
707
- const removeAttribute = (element, attribute) => element.removeAttribute(attribute);
708
-
709
- /**
710
- * A global namespace for `document.head`.
711
- */
712
- const { head: documentHead } = document;
713
-
714
- /**
715
- * A list of explicit default non-color values.
716
- */
717
- const nonColors = ['transparent', 'currentColor', 'inherit', 'revert', 'initial'];
718
-
719
- /**
720
- * Round colour components, for all formats except HEX.
721
- * @param {number} v one of the colour components
722
- * @returns {number} the rounded number
723
- */
724
- function roundPart(v) {
725
- const floor = Math.floor(v);
726
- return v - floor < 0.5 ? floor : Math.round(v);
727
- }
728
-
729
- // Color supported formats
730
- const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
731
-
732
- // Hue angles
733
- const ANGLES = 'deg|rad|grad|turn';
734
-
735
- // <http://www.w3.org/TR/css3-values/#integers>
736
- const CSS_INTEGER = '[-\\+]?\\d+%?';
737
-
738
- // Include CSS3 Module
739
- // <http://www.w3.org/TR/css3-values/#number-value>
740
- const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
741
-
742
- // Include CSS4 Module Hue degrees unit
743
- // <https://www.w3.org/TR/css3-values/#angle-value>
744
- const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
745
-
746
- // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
747
- const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
748
-
749
- // Add angles to the mix
750
- const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
751
-
752
- // Start & end
753
- const START_MATCH = '(?:[\\s|\\(\\s|\\s\\(\\s]+)?';
754
- const END_MATCH = '(?:[\\s|\\)\\s]+)?';
755
- // Components separation
756
- const SEP = '(?:[,|\\s]+)';
757
- const SEP2 = '(?:[,|\\/\\s]*)?';
758
-
759
- // Actual matching.
760
- // Parentheses and commas are optional, but not required.
761
- // Whitespace can take the place of commas or opening paren
762
- const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
763
-
764
- const matchers = {
765
- CSS_UNIT: new RegExp(CSS_UNIT2),
766
- hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
767
- rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
768
- hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
769
- hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
770
- hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
771
- hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
772
- hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
773
- hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
774
- };
775
-
776
- /**
777
- * Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
778
- * <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
779
- * @param {string} n testing number
780
- * @returns {boolean} the query result
781
- */
782
- function isOnePointZero(n) {
783
- return `${n}`.includes('.') && parseFloat(n) === 1;
784
- }
785
-
786
- /**
787
- * Check to see if string passed in is a percentage
788
- * @param {string} n testing number
789
- * @returns {boolean} the query result
790
- */
791
- function isPercentage(n) {
792
- return `${n}`.includes('%');
793
- }
794
-
795
- /**
796
- * Check to see if string passed is a web safe colour.
797
- * @see https://stackoverflow.com/a/16994164
798
- * @param {string} color a colour name, EG: *red*
799
- * @returns {boolean} the query result
800
- */
801
- function isColorName(color) {
802
- if (nonColors.includes(color)
803
- || ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
804
-
805
- if (['black', 'white'].includes(color)) return true;
806
-
807
- return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
808
- setElementStyle(documentHead, { color });
809
- const computedColor = getElementStyle(documentHead, 'color');
810
- setElementStyle(documentHead, { color: '' });
811
- return computedColor !== c;
812
- });
813
- }
814
-
815
- /**
816
- * Check to see if it looks like a CSS unit
817
- * (see `matchers` above for definition).
818
- * @param {string | number} color testing value
819
- * @returns {boolean} the query result
820
- */
821
- function isValidCSSUnit(color) {
822
- return Boolean(matchers.CSS_UNIT.exec(String(color)));
823
- }
824
-
825
- /**
826
- * Take input from [0, n] and return it as [0, 1]
827
- * @param {*} N the input number
828
- * @param {number} max the number maximum value
829
- * @returns {number} the number in [0, 1] value range
830
- */
831
- function bound01(N, max) {
832
- let n = N;
833
-
834
- if (typeof N === 'number'
835
- && Math.min(N, 0) === 0 // round values to 6 decimals Math.round(N * (10 ** 6)) / 10 ** 6
836
- && Math.max(N, 1) === 1) return N;
837
-
838
- if (isOnePointZero(N)) n = '100%';
839
-
840
- const processPercent = isPercentage(n);
841
- n = max === 360
842
- ? parseFloat(n)
843
- : Math.min(max, Math.max(0, parseFloat(n)));
844
-
845
- // Automatically convert percentage into number
846
- if (processPercent) n = (n * max) / 100;
847
-
848
- // Handle floating point rounding errors
849
- if (Math.abs(n - max) < 0.000001) {
850
- return 1;
851
- }
852
- // Convert into [0, 1] range if it isn't already
853
- if (max === 360) {
854
- // If n is a hue given in degrees,
855
- // wrap around out-of-range values into [0, 360] range
856
- // then convert into [0, 1].
857
- n = (n < 0 ? (n % max) + max : n % max) / max;
858
- } else {
859
- // If n not a hue given in degrees
860
- // Convert into [0, 1] range if it isn't already.
861
- n = (n % max) / max;
862
- }
863
- return n;
864
- }
865
-
866
- /**
867
- * Return a valid alpha value [0,1] with all invalid values being set to 1.
868
- * @param {string | number} a transparency value
869
- * @returns {number} a transparency value in the [0, 1] range
870
- */
871
- function boundAlpha(a) {
872
- let na = parseFloat(`${a}`);
873
-
874
- if (Number.isNaN(na) || na < 0 || na > 1) {
875
- na = 1;
876
- }
877
-
878
- return na;
879
- }
880
-
881
- /**
882
- * Force a number between 0 and 1.
883
- * @param {number} v the float number
884
- * @returns {number} - the resulting number
885
- */
886
- function clamp01(v) {
887
- return Math.min(1, Math.max(0, v));
888
- }
889
-
890
- /**
891
- * Returns the hexadecimal value of a web safe colour.
892
- * @param {string} name
893
- * @returns {string}
894
- */
895
- function getRGBFromName(name) {
896
- setElementStyle(documentHead, { color: name });
897
- const colorName = getElementStyle(documentHead, 'color');
898
- setElementStyle(documentHead, { color: '' });
899
- return colorName;
900
- }
901
-
902
- /**
903
- * Converts a decimal value to hexadecimal.
904
- * @param {number} d the input number
905
- * @returns {string} - the hexadecimal value
906
- */
907
- function convertDecimalToHex(d) {
908
- return roundPart(d * 255).toString(16);
909
- }
910
-
911
- /**
912
- * Converts a hexadecimal value to decimal.
913
- * @param {string} h hexadecimal value
914
- * @returns {number} number in decimal format
915
- */
916
- function convertHexToDecimal(h) {
917
- return parseIntFromHex(h) / 255;
918
- }
919
-
920
- /**
921
- * Converts a base-16 hexadecimal value into a base-10 integer.
922
- * @param {string} val
923
- * @returns {number}
924
- */
925
- function parseIntFromHex(val) {
926
- return parseInt(val, 16);
927
- }
928
-
929
- /**
930
- * Force a hexadecimal value to have 2 characters.
931
- * @param {string} c string with [0-9A-F] ranged values
932
- * @returns {string} 0 => 00, a => 0a
933
- */
934
- function pad2(c) {
935
- return c.length === 1 ? `0${c}` : String(c);
936
- }
937
-
938
- /**
939
- * Converts an RGB colour value to HSL.
940
- *
941
- * @param {number} r Red component [0, 1]
942
- * @param {number} g Green component [0, 1]
943
- * @param {number} b Blue component [0, 1]
944
- * @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
945
- */
946
- function rgbToHsl(r, g, b) {
947
- const max = Math.max(r, g, b);
948
- const min = Math.min(r, g, b);
949
- let h = 0;
950
- let s = 0;
951
- const l = (max + min) / 2;
952
- if (max === min) {
953
- s = 0;
954
- h = 0; // achromatic
955
- } else {
956
- const d = max - min;
957
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
958
- if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
959
- if (max === g) h = (b - r) / d + 2;
960
- if (max === b) h = (r - g) / d + 4;
961
-
962
- h /= 6;
963
- }
964
- return { h, s, l };
965
- }
966
-
967
- /**
968
- * Returns a normalized RGB component value.
969
- * @param {number} p
970
- * @param {number} q
971
- * @param {number} t
972
- * @returns {number}
973
- */
974
- function hueToRgb(p, q, t) {
975
- let T = t;
976
- if (T < 0) T += 1;
977
- if (T > 1) T -= 1;
978
- if (T < 1 / 6) return p + (q - p) * (6 * T);
979
- if (T < 1 / 2) return q;
980
- if (T < 2 / 3) return p + (q - p) * (2 / 3 - T) * 6;
981
- return p;
982
- }
983
-
984
- /**
985
- * Converts an HSL colour value to RGB.
986
- *
987
- * @param {number} h Hue Angle [0, 1]
988
- * @param {number} s Saturation [0, 1]
989
- * @param {number} l Lightness Angle [0, 1]
990
- * @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
991
- */
992
- function hslToRgb(h, s, l) {
993
- let r = 0;
994
- let g = 0;
995
- let b = 0;
996
-
997
- if (s === 0) {
998
- // achromatic
999
- g = l;
1000
- b = l;
1001
- r = l;
1002
- } else {
1003
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1004
- const p = 2 * l - q;
1005
- r = hueToRgb(p, q, h + 1 / 3);
1006
- g = hueToRgb(p, q, h);
1007
- b = hueToRgb(p, q, h - 1 / 3);
1008
- }
1009
-
1010
- return { r, g, b };
1011
- }
1012
-
1013
- /**
1014
- * Returns an HWB colour object from an RGB colour object.
1015
- * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
1016
- * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
1017
- *
1018
- * @param {number} r Red component [0, 1]
1019
- * @param {number} g Green [0, 1]
1020
- * @param {number} b Blue [0, 1]
1021
- * @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
1022
- */
1023
- function rgbToHwb(r, g, b) {
1024
- let f = 0;
1025
- let i = 0;
1026
- const whiteness = Math.min(r, g, b);
1027
- const max = Math.max(r, g, b);
1028
- const black = 1 - max;
1029
-
1030
- if (max === whiteness) return { h: 0, w: whiteness, b: black };
1031
- if (r === whiteness) {
1032
- f = g - b;
1033
- i = 3;
1034
- } else {
1035
- f = g === whiteness ? b - r : r - g;
1036
- i = g === whiteness ? 5 : 1;
1037
- }
1038
-
1039
- const h = (i - f / (max - whiteness)) / 6;
1040
- return {
1041
- h: h === 1 ? 0 : h,
1042
- w: whiteness,
1043
- b: black,
1044
- };
1045
- }
1046
-
1047
- /**
1048
- * Returns an RGB colour object from an HWB colour.
1049
- *
1050
- * @param {number} H Hue Angle [0, 1]
1051
- * @param {number} W Whiteness [0, 1]
1052
- * @param {number} B Blackness [0, 1]
1053
- * @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
1054
- *
1055
- * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
1056
- * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
1057
- */
1058
- function hwbToRgb(H, W, B) {
1059
- if (W + B >= 1) {
1060
- const gray = W / (W + B);
1061
- return { r: gray, g: gray, b: gray };
1062
- }
1063
- let { r, g, b } = hslToRgb(H, 1, 0.5);
1064
- [r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
1065
-
1066
- return { r, g, b };
1067
- }
1068
-
1069
- /**
1070
- * Converts an RGB colour value to HSV.
1071
- *
1072
- * @param {number} r Red component [0, 1]
1073
- * @param {number} g Green [0, 1]
1074
- * @param {number} b Blue [0, 1]
1075
- * @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
1076
- */
1077
- function rgbToHsv(r, g, b) {
1078
- const max = Math.max(r, g, b);
1079
- const min = Math.min(r, g, b);
1080
- let h = 0;
1081
- const v = max;
1082
- const d = max - min;
1083
- const s = max === 0 ? 0 : d / max;
1084
- if (max === min) {
1085
- h = 0; // achromatic
1086
- } else {
1087
- if (r === max) h = (g - b) / d + (g < b ? 6 : 0);
1088
- if (g === max) h = (b - r) / d + 2;
1089
- if (b === max) h = (r - g) / d + 4;
1090
-
1091
- h /= 6;
1092
- }
1093
- return { h, s, v };
1094
- }
1095
-
1096
- /**
1097
- * Converts an HSV colour value to RGB.
1098
- *
1099
- * @param {number} H Hue Angle [0, 1]
1100
- * @param {number} S Saturation [0, 1]
1101
- * @param {number} V Brightness Angle [0, 1]
1102
- * @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
1103
- */
1104
- function hsvToRgb(H, S, V) {
1105
- const h = H * 6;
1106
- const s = S;
1107
- const v = V;
1108
- const i = Math.floor(h);
1109
- const f = h - i;
1110
- const p = v * (1 - s);
1111
- const q = v * (1 - f * s);
1112
- const t = v * (1 - (1 - f) * s);
1113
- const mod = i % 6;
1114
- const r = [v, q, p, p, t, v][mod];
1115
- const g = [t, v, v, q, p, p][mod];
1116
- const b = [p, p, t, v, v, q][mod];
1117
- return { r, g, b };
1118
- }
1119
-
1120
- /**
1121
- * Converts an RGB colour to hex
1122
- *
1123
- * Assumes r, g, and b are contained in the set [0, 255]
1124
- * Returns a 3 or 6 character hex
1125
- * @param {number} r Red component [0, 255]
1126
- * @param {number} g Green [0, 255]
1127
- * @param {number} b Blue [0, 255]
1128
- * @param {boolean=} allow3Char
1129
- * @returns {string}
1130
- */
1131
- function rgbToHex(r, g, b, allow3Char) {
1132
- const hex = [
1133
- pad2(roundPart(r).toString(16)),
1134
- pad2(roundPart(g).toString(16)),
1135
- pad2(roundPart(b).toString(16)),
1136
- ];
1137
-
1138
- // Return a 3 character hex if possible
1139
- if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
1140
- && hex[1].charAt(0) === hex[1].charAt(1)
1141
- && hex[2].charAt(0) === hex[2].charAt(1)) {
1142
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
1143
- }
1144
-
1145
- return hex.join('');
1146
- }
1147
-
1148
- /**
1149
- * Converts an RGBA color plus alpha transparency to hex8.
1150
- *
1151
- * @param {number} r Red component [0, 255]
1152
- * @param {number} g Green [0, 255]
1153
- * @param {number} b Blue [0, 255]
1154
- * @param {number} a Alpha transparency [0, 1]
1155
- * @param {boolean=} allow4Char when *true* it will also find hex shorthand
1156
- * @returns {string} a hexadecimal value with alpha transparency
1157
- */
1158
- function rgbaToHex(r, g, b, a, allow4Char) {
1159
- const hex = [
1160
- pad2(roundPart(r).toString(16)),
1161
- pad2(roundPart(g).toString(16)),
1162
- pad2(roundPart(b).toString(16)),
1163
- pad2(convertDecimalToHex(a)),
1164
- ];
1165
-
1166
- // Return a 4 character hex if possible
1167
- if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
1168
- && hex[1].charAt(0) === hex[1].charAt(1)
1169
- && hex[2].charAt(0) === hex[2].charAt(1)
1170
- && hex[3].charAt(0) === hex[3].charAt(1)) {
1171
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
1172
- }
1173
- return hex.join('');
1174
- }
1175
-
1176
- /**
1177
- * Permissive string parsing. Take in a number of formats, and output an object
1178
- * based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
1179
- * @param {string} input colour value in any format
1180
- * @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
1181
- */
1182
- function stringInputToObject(input) {
1183
- let color = toLowerCase(input.trim());
1184
-
1185
- if (color.length === 0) {
1186
- return {
1187
- r: 0, g: 0, b: 0, a: 1,
1188
- };
1189
- }
1190
-
1191
- if (isColorName(color)) {
1192
- color = getRGBFromName(color);
1193
- } else if (nonColors.includes(color)) {
1194
- const a = color === 'transparent' ? 0 : 1;
1195
- return {
1196
- r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
1197
- };
1198
- }
1199
-
1200
- // Try to match string input using regular expressions.
1201
- // Keep most of the number bounding out of this function,
1202
- // don't worry about [0,1] or [0,100] or [0,360]
1203
- // Just return an object and let the conversion functions handle that.
1204
- // This way the result will be the same whether Color is initialized with string or object.
1205
- let [, m1, m2, m3, m4] = matchers.rgb.exec(color) || [];
1206
- if (m1 && m2 && m3/* && m4 */) {
1207
- return {
1208
- r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
1209
- };
1210
- }
1211
-
1212
- [, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
1213
- if (m1 && m2 && m3/* && m4 */) {
1214
- return {
1215
- h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
1216
- };
1217
- }
1218
-
1219
- [, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
1220
- if (m1 && m2 && m3/* && m4 */) {
1221
- return {
1222
- h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
1223
- };
1224
- }
1225
-
1226
- [, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
1227
- if (m1 && m2 && m3) {
1228
- return {
1229
- h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
1230
- };
1231
- }
1232
-
1233
- [, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
1234
- if (m1 && m2 && m3 && m4) {
1235
- return {
1236
- r: parseIntFromHex(m1),
1237
- g: parseIntFromHex(m2),
1238
- b: parseIntFromHex(m3),
1239
- a: convertHexToDecimal(m4),
1240
- format: 'hex',
1241
- };
1242
- }
1243
-
1244
- [, m1, m2, m3] = matchers.hex6.exec(color) || [];
1245
- if (m1 && m2 && m3) {
1246
- return {
1247
- r: parseIntFromHex(m1),
1248
- g: parseIntFromHex(m2),
1249
- b: parseIntFromHex(m3),
1250
- format: 'hex',
1251
- };
1252
- }
1253
-
1254
- [, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
1255
- if (m1 && m2 && m3 && m4) {
1256
- return {
1257
- r: parseIntFromHex(m1 + m1),
1258
- g: parseIntFromHex(m2 + m2),
1259
- b: parseIntFromHex(m3 + m3),
1260
- a: convertHexToDecimal(m4 + m4),
1261
- format: 'hex',
1262
- };
1263
- }
1264
-
1265
- [, m1, m2, m3] = matchers.hex3.exec(color) || [];
1266
- if (m1 && m2 && m3) {
1267
- return {
1268
- r: parseIntFromHex(m1 + m1),
1269
- g: parseIntFromHex(m2 + m2),
1270
- b: parseIntFromHex(m3 + m3),
1271
- format: 'hex',
1272
- };
1273
- }
1274
-
1275
- return false;
1276
- }
1277
-
1278
- /**
1279
- * Given a string or object, convert that input to RGB
1280
- *
1281
- * Possible string inputs:
1282
- * ```
1283
- * "red"
1284
- * "#f00" or "f00"
1285
- * "#ff0000" or "ff0000"
1286
- * "#ff000000" or "ff000000" // CSS4 Module
1287
- * "rgb 255 0 0" or "rgb (255, 0, 0)"
1288
- * "rgb 1.0 0 0" or "rgb (1, 0, 0)"
1289
- * "rgba(255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
1290
- * "rgba(1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
1291
- * "rgb(255 0 0 / 10%)" or "rgb 255 0 0 0.1" // CSS4 Module
1292
- * "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
1293
- * "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
1294
- * "hsl(0deg 100% 50% / 50%)" or "hsl 0 100 50 50" // CSS4 Module
1295
- * "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
1296
- * "hsva(0, 100%, 100%, 0.1)" or "hsva 0 100% 100% 0.1"
1297
- * "hsv(0deg 100% 100% / 10%)" or "hsv 0 100 100 0.1" // CSS4 Module
1298
- * "hwb(0deg, 100%, 100%, 100%)" or "hwb 0 100% 100% 0.1" // CSS4 Module
1299
- * ```
1300
- * @param {string | Record<string, any>} input
1301
- * @returns {CP.ColorObject}
1302
- */
1303
- function inputToRGB(input) {
1304
- let rgb = { r: 0, g: 0, b: 0 };
1305
- /** @type {*} */
1306
- let color = input;
1307
- /** @type {string | number} */
1308
- let a = 1;
1309
- let s = null;
1310
- let v = null;
1311
- let l = null;
1312
- let w = null;
1313
- let b = null;
1314
- let h = null;
1315
- let r = null;
1316
- let g = null;
1317
- let ok = false;
1318
- const inputFormat = typeof color === 'object' && color.format;
1319
- let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
1320
-
1321
- if (typeof input === 'string') {
1322
- color = stringInputToObject(input);
1323
- if (color) ok = true;
1324
- }
1325
- if (typeof color === 'object') {
1326
- if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
1327
- ({ r, g, b } = color);
1328
- // RGB values now are all in [0, 1] range
1329
- [r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255));
1330
- rgb = { r, g, b };
1331
- ok = true;
1332
- format = color.format || 'rgb';
1333
- }
1334
- if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
1335
- ({ h, s, v } = color);
1336
- h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1337
- s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
1338
- v = bound01(v, 100); // brightness can be `5%` or a [0, 1] value
1339
- rgb = hsvToRgb(h, s, v);
1340
- ok = true;
1341
- format = 'hsv';
1342
- }
1343
- if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
1344
- ({ h, s, l } = color);
1345
- h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1346
- s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
1347
- l = bound01(l, 100); // lightness can be `5%` or a [0, 1] value
1348
- rgb = hslToRgb(h, s, l);
1349
- ok = true;
1350
- format = 'hsl';
1351
- }
1352
- if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
1353
- ({ h, w, b } = color);
1354
- h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1355
- w = bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
1356
- b = bound01(b, 100); // blackness can be `5%` or a [0, 1] value
1357
- rgb = hwbToRgb(h, w, b);
1358
- ok = true;
1359
- format = 'hwb';
1360
- }
1361
- if (isValidCSSUnit(color.a)) {
1362
- a = color.a;
1363
- a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
1364
- }
1365
- }
1366
- if (typeof color === 'undefined') {
1367
- ok = true;
1368
- }
1369
-
1370
- return {
1371
- ok,
1372
- format,
1373
- r: rgb.r,
1374
- g: rgb.g,
1375
- b: rgb.b,
1376
- a: boundAlpha(a),
1377
- };
1378
- }
1379
-
1380
- /**
1381
- * @class
1382
- * Returns a new `Color` instance.
1383
- * @see https://github.com/bgrins/TinyColor
1384
- */
1385
- class Color {
1386
- /**
1387
- * @constructor
1388
- * @param {CP.ColorInput} input the given colour value
1389
- * @param {CP.ColorFormats=} config the given format
1390
- */
1391
- constructor(input, config) {
1392
- let color = input;
1393
- const configFormat = config && COLOR_FORMAT.includes(config)
1394
- ? config : '';
1395
-
1396
- // If input is already a `Color`, clone its values
1397
- if (color instanceof Color) {
1398
- color = inputToRGB(color);
1399
- }
1400
-
1401
- const {
1402
- r, g, b, a, ok, format,
1403
- } = inputToRGB(color);
1404
-
1405
- // bind
1406
- const self = this;
1407
-
1408
- /** @type {CP.ColorInput} */
1409
- self.originalInput = input;
1410
- /** @type {number} */
1411
- self.r = r;
1412
- /** @type {number} */
1413
- self.g = g;
1414
- /** @type {number} */
1415
- self.b = b;
1416
- /** @type {number} */
1417
- self.a = a;
1418
- /** @type {boolean} */
1419
- self.ok = ok;
1420
- /** @type {CP.ColorFormats} */
1421
- self.format = configFormat || format;
1422
- }
1423
-
1424
- /**
1425
- * Checks if the current input value is a valid colour.
1426
- * @returns {boolean} the query result
1427
- */
1428
- get isValid() {
1429
- return this.ok;
1430
- }
1431
-
1432
- /**
1433
- * Checks if the current colour requires a light text colour.
1434
- * @returns {boolean} the query result
1435
- */
1436
- get isDark() {
1437
- return this.brightness < 120;
1438
- }
1439
-
1440
- /**
1441
- * Returns the perceived luminance of a colour.
1442
- * @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
1443
- * @returns {number} a number in the [0, 1] range
1444
- */
1445
- get luminance() {
1446
- const { r, g, b } = this;
1447
- let R = 0;
1448
- let G = 0;
1449
- let B = 0;
1450
-
1451
- if (r <= 0.03928) {
1452
- R = r / 12.92;
1453
- } else {
1454
- R = ((r + 0.055) / 1.055) ** 2.4;
1455
- }
1456
- if (g <= 0.03928) {
1457
- G = g / 12.92;
1458
- } else {
1459
- G = ((g + 0.055) / 1.055) ** 2.4;
1460
- }
1461
- if (b <= 0.03928) {
1462
- B = b / 12.92;
1463
- } else {
1464
- B = ((b + 0.055) / 1.055) ** 2.4;
1465
- }
1466
- return 0.2126 * R + 0.7152 * G + 0.0722 * B;
1467
- }
1468
-
1469
- /**
1470
- * Returns the perceived brightness of the colour.
1471
- * @returns {number} a number in the [0, 255] range
1472
- */
1473
- get brightness() {
1474
- const { r, g, b } = this.toRgb();
1475
- return (r * 299 + g * 587 + b * 114) / 1000;
1476
- }
1477
-
1478
- /**
1479
- * Returns the colour as an RGBA object.
1480
- * @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
1481
- */
1482
- toRgb() {
1483
- let {
1484
- r, g, b, a,
1485
- } = this;
1486
-
1487
- [r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
1488
- a = roundPart(a * 100) / 100;
1489
- return {
1490
- r, g, b, a,
1491
- };
1492
- }
1493
-
1494
- /**
1495
- * Returns the RGBA values concatenated into a CSS3 Module string format.
1496
- * * rgb(255,255,255)
1497
- * * rgba(255,255,255,0.5)
1498
- * @returns {string} the CSS valid colour in RGB/RGBA format
1499
- */
1500
- toRgbString() {
1501
- const {
1502
- r, g, b, a,
1503
- } = this.toRgb();
1504
- const [R, G, B] = [r, g, b].map(roundPart);
1505
-
1506
- return a === 1
1507
- ? `rgb(${R}, ${G}, ${B})`
1508
- : `rgba(${R}, ${G}, ${B}, ${a})`;
1509
- }
1510
-
1511
- /**
1512
- * Returns the RGBA values concatenated into a CSS4 Module string format.
1513
- * * rgb(255 255 255)
1514
- * * rgb(255 255 255 / 50%)
1515
- * @returns {string} the CSS valid colour in CSS4 RGB format
1516
- */
1517
- toRgbCSS4String() {
1518
- const {
1519
- r, g, b, a,
1520
- } = this.toRgb();
1521
- const [R, G, B] = [r, g, b].map(roundPart);
1522
- const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
1523
-
1524
- return `rgb(${R} ${G} ${B}${A})`;
1525
- }
1526
-
1527
- /**
1528
- * Returns the hexadecimal value of the colour. When the parameter is *true*
1529
- * it will find a 3 characters shorthand of the decimal value.
1530
- *
1531
- * @param {boolean=} allow3Char when `true` returns shorthand HEX
1532
- * @returns {string} the hexadecimal colour format
1533
- */
1534
- toHex(allow3Char) {
1535
- const {
1536
- r, g, b, a,
1537
- } = this.toRgb();
1538
-
1539
- return a === 1
1540
- ? rgbToHex(r, g, b, allow3Char)
1541
- : rgbaToHex(r, g, b, a, allow3Char);
1542
- }
1543
-
1544
- /**
1545
- * Returns the CSS valid hexadecimal vaue of the colour. When the parameter is *true*
1546
- * it will find a 3 characters shorthand of the value.
1547
- *
1548
- * @param {boolean=} allow3Char when `true` returns shorthand HEX
1549
- * @returns {string} the CSS valid colour in hexadecimal format
1550
- */
1551
- toHexString(allow3Char) {
1552
- return `#${this.toHex(allow3Char)}`;
1553
- }
1554
-
1555
- /**
1556
- * Returns the HEX8 value of the colour.
1557
- * @param {boolean=} allow4Char when `true` returns shorthand HEX
1558
- * @returns {string} the CSS valid colour in hexadecimal format
1559
- */
1560
- toHex8(allow4Char) {
1561
- const {
1562
- r, g, b, a,
1563
- } = this.toRgb();
1564
-
1565
- return rgbaToHex(r, g, b, a, allow4Char);
1566
- }
1567
-
1568
- /**
1569
- * Returns the HEX8 value of the colour.
1570
- * @param {boolean=} allow4Char when `true` returns shorthand HEX
1571
- * @returns {string} the CSS valid colour in hexadecimal format
1572
- */
1573
- toHex8String(allow4Char) {
1574
- return `#${this.toHex8(allow4Char)}`;
1575
- }
1576
-
1577
- /**
1578
- * Returns the colour as a HSVA object.
1579
- * @returns {CP.HSVA} the `{h,s,v,a}` object with [0, 1] ranged values
1580
- */
1581
- toHsv() {
1582
- const {
1583
- r, g, b, a,
1584
- } = this;
1585
- const { h, s, v } = rgbToHsv(r, g, b);
1586
-
1587
- return {
1588
- h, s, v, a,
1589
- };
1590
- }
1591
-
1592
- /**
1593
- * Returns the colour as an HSLA object.
1594
- * @returns {CP.HSLA} the `{h,s,l,a}` object with [0, 1] ranged values
1595
- */
1596
- toHsl() {
1597
- const {
1598
- r, g, b, a,
1599
- } = this;
1600
- const { h, s, l } = rgbToHsl(r, g, b);
1601
-
1602
- return {
1603
- h, s, l, a,
1604
- };
1605
- }
1606
-
1607
- /**
1608
- * Returns the HSLA values concatenated into a CSS3 Module format string.
1609
- * * `hsl(150, 100%, 50%)`
1610
- * * `hsla(150, 100%, 50%, 0.5)`
1611
- * @returns {string} the CSS valid colour in HSL/HSLA format
1612
- */
1613
- toHslString() {
1614
- let {
1615
- h, s, l, a,
1616
- } = this.toHsl();
1617
- h = roundPart(h * 360);
1618
- s = roundPart(s * 100);
1619
- l = roundPart(l * 100);
1620
- a = roundPart(a * 100) / 100;
1621
-
1622
- return a === 1
1623
- ? `hsl(${h}, ${s}%, ${l}%)`
1624
- : `hsla(${h}, ${s}%, ${l}%, ${a})`;
1625
- }
1626
-
1627
- /**
1628
- * Returns the HSLA values concatenated into a CSS4 Module format string.
1629
- * * `hsl(150deg 100% 50%)`
1630
- * * `hsl(150deg 100% 50% / 50%)`
1631
- * @returns {string} the CSS valid colour in CSS4 HSL format
1632
- */
1633
- toHslCSS4String() {
1634
- let {
1635
- h, s, l, a,
1636
- } = this.toHsl();
1637
- h = roundPart(h * 360);
1638
- s = roundPart(s * 100);
1639
- l = roundPart(l * 100);
1640
- a = roundPart(a * 100);
1641
- const A = a < 100 ? ` / ${roundPart(a)}%` : '';
1642
-
1643
- return `hsl(${h}deg ${s}% ${l}%${A})`;
1644
- }
1645
-
1646
- /**
1647
- * Returns the colour as an HWBA object.
1648
- * @returns {CP.HWBA} the `{h,w,b,a}` object with [0, 1] ranged values
1649
- */
1650
- toHwb() {
1651
- const {
1652
- r, g, b, a,
1653
- } = this;
1654
- const { h, w, b: bl } = rgbToHwb(r, g, b);
1655
- return {
1656
- h, w, b: bl, a,
1657
- };
1658
- }
1659
-
1660
- /**
1661
- * Returns the HWBA values concatenated into a string.
1662
- * @returns {string} the CSS valid colour in HWB format
1663
- */
1664
- toHwbString() {
1665
- let {
1666
- h, w, b, a,
1667
- } = this.toHwb();
1668
- h = roundPart(h * 360);
1669
- w = roundPart(w * 100);
1670
- b = roundPart(b * 100);
1671
- a = roundPart(a * 100);
1672
- const A = a < 100 ? ` / ${roundPart(a)}%` : '';
1673
-
1674
- return `hwb(${h}deg ${w}% ${b}%${A})`;
1675
- }
1676
-
1677
- /**
1678
- * Sets the alpha value of the current colour.
1679
- * @param {number} alpha a new alpha value in the [0, 1] range.
1680
- * @returns {Color} the `Color` instance
1681
- */
1682
- setAlpha(alpha) {
1683
- const self = this;
1684
- if (typeof alpha !== 'number') return self;
1685
- self.a = boundAlpha(alpha);
1686
- return self;
1687
- }
1688
-
1689
- /**
1690
- * Saturate the colour with a given amount.
1691
- * @param {number=} amount a value in the [0, 100] range
1692
- * @returns {Color} the `Color` instance
1693
- */
1694
- saturate(amount) {
1695
- const self = this;
1696
- if (typeof amount !== 'number') return self;
1697
- const { h, s, l } = self.toHsl();
1698
- const { r, g, b } = hslToRgb(h, clamp01(s + amount / 100), l);
1699
-
1700
- ObjectAssign(self, { r, g, b });
1701
- return self;
1702
- }
1703
-
1704
- /**
1705
- * Desaturate the colour with a given amount.
1706
- * @param {number=} amount a value in the [0, 100] range
1707
- * @returns {Color} the `Color` instance
1708
- */
1709
- desaturate(amount) {
1710
- return typeof amount === 'number' ? this.saturate(-amount) : this;
1711
- }
1712
-
1713
- /**
1714
- * Completely desaturates a colour into greyscale.
1715
- * Same as calling `desaturate(100)`
1716
- * @returns {Color} the `Color` instance
1717
- */
1718
- greyscale() {
1719
- return this.saturate(-100);
1720
- }
1721
-
1722
- /**
1723
- * Increase the colour lightness with a given amount.
1724
- * @param {number=} amount a value in the [0, 100] range
1725
- * @returns {Color} the `Color` instance
1726
- */
1727
- lighten(amount) {
1728
- const self = this;
1729
- if (typeof amount !== 'number') return self;
1730
-
1731
- const { h, s, l } = self.toHsl();
1732
- const { r, g, b } = hslToRgb(h, s, clamp01(l + amount / 100));
1733
-
1734
- ObjectAssign(self, { r, g, b });
1735
- return self;
1736
- }
1737
-
1738
- /**
1739
- * Decrease the colour lightness with a given amount.
1740
- * @param {number=} amount a value in the [0, 100] range
1741
- * @returns {Color} the `Color` instance
1742
- */
1743
- darken(amount) {
1744
- return typeof amount === 'number' ? this.lighten(-amount) : this;
1745
- }
1746
-
1747
- /**
1748
- * Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
1749
- * Values outside of this range will be wrapped into this range.
1750
- *
1751
- * @param {number=} amount a value in the [0, 100] range
1752
- * @returns {Color} the `Color` instance
1753
- */
1754
- spin(amount) {
1755
- const self = this;
1756
- if (typeof amount !== 'number') return self;
1757
-
1758
- const { h, s, l } = self.toHsl();
1759
- const { r, g, b } = hslToRgb(clamp01(((h * 360 + amount) % 360) / 360), s, l);
1760
-
1761
- ObjectAssign(self, { r, g, b });
1762
- return self;
1763
- }
1764
-
1765
- /** Returns a clone of the current `Color` instance. */
1766
- clone() {
1767
- return new Color(this);
1768
- }
1769
-
1770
- /**
1771
- * Returns the colour value in CSS valid string format.
1772
- * @param {boolean=} allowShort when *true*, HEX values can be shorthand
1773
- * @returns {string} the CSS valid colour in the configured format
1774
- */
1775
- toString(allowShort) {
1776
- const self = this;
1777
- const { format } = self;
1778
-
1779
- if (format === 'hex') return self.toHexString(allowShort);
1780
- if (format === 'hsl') return self.toHslString();
1781
- if (format === 'hwb') return self.toHwbString();
1782
-
1783
- return self.toRgbString();
1784
- }
1785
- }
1786
-
1787
- ObjectAssign(Color, {
1788
- ANGLES,
1789
- CSS_ANGLE,
1790
- CSS_INTEGER,
1791
- CSS_NUMBER,
1792
- CSS_UNIT,
1793
- CSS_UNIT2,
1794
- PERMISSIVE_MATCH,
1795
- matchers,
1796
- isOnePointZero,
1797
- isPercentage,
1798
- isValidCSSUnit,
1799
- isColorName,
1800
- pad2,
1801
- clamp01,
1802
- bound01,
1803
- boundAlpha,
1804
- getRGBFromName,
1805
- convertHexToDecimal,
1806
- convertDecimalToHex,
1807
- rgbToHsl,
1808
- rgbToHex,
1809
- rgbToHsv,
1810
- rgbToHwb,
1811
- rgbaToHex,
1812
- hslToRgb,
1813
- hsvToRgb,
1814
- hueToRgb,
1815
- hwbToRgb,
1816
- parseIntFromHex,
1817
- stringInputToObject,
1818
- inputToRGB,
1819
- roundPart,
1820
- getElementStyle,
1821
- setElementStyle,
1822
- ObjectAssign,
1823
- });
1824
-
1825
- /**
1826
- * @class
1827
- * Returns a color palette with a given set of parameters.
1828
- * @example
1829
- * new ColorPalette(0, 12, 10);
1830
- * // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: Array<Color> }
1831
- */
1832
- class ColorPalette {
1833
- /**
1834
- * The `hue` parameter is optional, which would be set to 0.
1835
- * @param {number[]} args represeinting hue, hueSteps, lightSteps
1836
- * * `args.hue` the starting Hue [0, 360]
1837
- * * `args.hueSteps` Hue Steps Count [5, 24]
1838
- * * `args.lightSteps` Lightness Steps Count [5, 12]
1839
- */
1840
- constructor(...args) {
1841
- let hue = 0;
1842
- let hueSteps = 12;
1843
- let lightSteps = 10;
1844
- let lightnessArray = [0.5];
1845
-
1846
- if (args.length === 3) {
1847
- [hue, hueSteps, lightSteps] = args;
1848
- } else if (args.length === 2) {
1849
- [hueSteps, lightSteps] = args;
1850
- if ([hueSteps, lightSteps].some((n) => n < 1)) {
1851
- throw TypeError('ColorPalette: both arguments must be higher than 0.');
1852
- }
1853
- }
1854
-
1855
- /** @type {*} */
1856
- const colors = [];
1857
- const hueStep = 360 / hueSteps;
1858
- const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
1859
- const steps1To13 = [0.25, 0.2, 0.15, 0.11, 0.09, 0.075];
1860
- const lightSets = [[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]];
1861
- const closestSet = lightSets.find((set) => set.includes(lightSteps));
1862
-
1863
- // find a lightStep that won't go beyond black and white
1864
- // something within the [10-90] range of lightness
1865
- const lightStep = closestSet
1866
- ? steps1To13[lightSets.indexOf(closestSet)]
1867
- : (100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100);
1868
-
1869
- // light tints
1870
- for (let i = 1; i < half + 1; i += 1) {
1871
- lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
1872
- }
1873
-
1874
- // dark tints
1875
- for (let i = 1; i < lightSteps - half; i += 1) {
1876
- lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
1877
- }
1878
-
1879
- // feed `colors` Array
1880
- for (let i = 0; i < hueSteps; i += 1) {
1881
- const currentHue = ((hue + i * hueStep) % 360) / 360;
1882
- lightnessArray.forEach((l) => {
1883
- colors.push(new Color({ h: currentHue, s: 1, l }));
1884
- });
1885
- }
1886
-
1887
- this.hue = hue;
1888
- this.hueSteps = hueSteps;
1889
- this.lightSteps = lightSteps;
1890
- this.colors = colors;
1891
- }
1892
- }
1893
-
1894
- ObjectAssign(ColorPalette, { Color });
1895
-
1896
- /** @type {Record<string, string>} */
1897
- const colorPickerLabels = {
1898
- pickerLabel: 'Colour Picker',
1899
- appearanceLabel: 'Colour Appearance',
1900
- valueLabel: 'Colour Value',
1901
- toggleLabel: 'Select Colour',
1902
- presetsLabel: 'Colour Presets',
1903
- defaultsLabel: 'Colour Defaults',
1904
- formatLabel: 'Format',
1905
- alphaLabel: 'Alpha',
1906
- hexLabel: 'Hexadecimal',
1907
- hueLabel: 'Hue',
1908
- whitenessLabel: 'Whiteness',
1909
- blacknessLabel: 'Blackness',
1910
- saturationLabel: 'Saturation',
1911
- lightnessLabel: 'Lightness',
1912
- redLabel: 'Red',
1913
- greenLabel: 'Green',
1914
- blueLabel: 'Blue',
1915
- };
1916
-
1917
- /**
1918
- * A list of 17 color names used for WAI-ARIA compliance.
1919
- * @type {string[]}
1920
- */
1921
- const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
1922
-
1923
- const tabIndex = 'tabindex';
1924
-
1925
- /**
1926
- * Check if a string is valid JSON string.
1927
- * @param {string} str the string input
1928
- * @returns {boolean} the query result
1929
- */
1930
- function isValidJSON(str) {
1931
- try {
1932
- JSON.parse(str);
1933
- } catch (e) {
1934
- return false;
1935
- }
1936
- return true;
1937
- }
1938
-
1939
- /**
1940
- * Shortcut for `String.toUpperCase()`.
1941
- *
1942
- * @param {string} source input string
1943
- * @returns {string} uppercase output string
1944
- */
1945
- const toUpperCase = (source) => source.toUpperCase();
1946
-
1947
- /**
1948
- * A global namespace for aria-haspopup.
1949
- * @type {string}
1950
- */
1951
- const ariaHasPopup = 'aria-haspopup';
1952
-
1953
- /**
1954
- * A global namespace for aria-hidden.
1955
- * @type {string}
1956
- */
1957
- const ariaHidden = 'aria-hidden';
1958
-
1959
- /**
1960
- * A global namespace for aria-labelledby.
1961
- * @type {string}
1962
- */
1963
- const ariaLabelledBy = 'aria-labelledby';
1964
-
1965
- /**
1966
- * This is a shortie for `document.createElement` method
1967
- * which allows you to create a new `HTMLElement` for a given `tagName`
1968
- * or based on an object with specific non-readonly attributes:
1969
- * `id`, `className`, `textContent`, `style`, etc.
1970
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
1971
- *
1972
- * @param {Record<string, string> | string} param `tagName` or object
1973
- * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
1974
- */
1975
- function createElement(param) {
1976
- if (typeof param === 'string') {
1977
- return getDocument().createElement(param);
1978
- }
1979
-
1980
- const { tagName } = param;
1981
- const attr = { ...param };
1982
- const newElement = createElement(tagName);
1983
- delete attr.tagName;
1984
- ObjectAssign(newElement, attr);
1985
- return newElement;
1986
- }
1987
-
1988
- /**
1989
- * This is a shortie for `document.createElementNS` method
1990
- * which allows you to create a new `HTMLElement` for a given `tagName`
1991
- * or based on an object with specific non-readonly attributes:
1992
- * `id`, `className`, `textContent`, `style`, etc.
1993
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
1994
- *
1995
- * @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
1996
- * @param {Record<string, string> | string} param `tagName` or object
1997
- * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
1998
- */
1999
- function createElementNS(namespace, param) {
2000
- if (typeof param === 'string') {
2001
- return getDocument().createElementNS(namespace, param);
2002
- }
2003
-
2004
- const { tagName } = param;
2005
- const attr = { ...param };
2006
- const newElement = createElementNS(namespace, tagName);
2007
- delete attr.tagName;
2008
- ObjectAssign(newElement, attr);
2009
- return newElement;
2010
- }
2011
-
2012
- const vHidden = 'v-hidden';
2013
-
2014
- /**
2015
- * Returns the color form for `ColorPicker`.
2016
- *
2017
- * @param {CP.ColorPicker} self the `ColorPicker` instance
2018
- * @returns {HTMLElement | Element} a new `<div>` element with color component `<input>`
2019
- */
2020
- function getColorForm(self) {
2021
- const { format, id, componentLabels } = self;
2022
- const colorForm = createElement({
2023
- tagName: 'div',
2024
- className: `color-form ${format}`,
2025
- });
2026
-
2027
- let components = ['hex'];
2028
- if (format === 'rgb') components = ['red', 'green', 'blue', 'alpha'];
2029
- else if (format === 'hsl') components = ['hue', 'saturation', 'lightness', 'alpha'];
2030
- else if (format === 'hwb') components = ['hue', 'whiteness', 'blackness', 'alpha'];
2031
-
2032
- components.forEach((c) => {
2033
- const [C] = format === 'hex' ? ['#'] : toUpperCase(c).split('');
2034
- const cID = `color_${format}_${c}_${id}`;
2035
- const formatLabel = componentLabels[`${c}Label`];
2036
- const cInputLabel = createElement({ tagName: 'label' });
2037
- setAttribute(cInputLabel, 'for', cID);
2038
- cInputLabel.append(
2039
- createElement({ tagName: 'span', ariaHidden: 'true', innerText: `${C}:` }),
2040
- createElement({ tagName: 'span', className: vHidden, innerText: formatLabel }),
2041
- );
2042
- const cInput = createElement({
2043
- tagName: 'input',
2044
- id: cID,
2045
- // name: cID, - prevent saving the value to a form
2046
- type: format === 'hex' ? 'text' : 'number',
2047
- value: c === 'alpha' ? '100' : '0',
2048
- className: `color-input ${c}`,
2049
- });
2050
- setAttribute(cInput, 'autocomplete', 'off');
2051
- setAttribute(cInput, 'spellcheck', 'false');
2052
-
2053
- // alpha
2054
- let max = '100';
2055
- let step = '1';
2056
- if (c !== 'alpha') {
2057
- if (format === 'rgb') {
2058
- max = '255'; step = '1';
2059
- } else if (c === 'hue') {
2060
- max = '360'; step = '1';
2061
- }
2062
- }
2063
- ObjectAssign(cInput, {
2064
- min: '0',
2065
- max,
2066
- step,
2067
- });
2068
- colorForm.append(cInputLabel, cInput);
2069
- });
2070
- return colorForm;
2071
- }
2072
-
2073
- /**
2074
- * A global namespace for aria-label.
2075
- * @type {string}
2076
- */
2077
- const ariaLabel = 'aria-label';
2078
-
2079
- /**
2080
- * A global namespace for aria-valuemin.
2081
- * @type {string}
2082
- */
2083
- const ariaValueMin = 'aria-valuemin';
2084
-
2085
- /**
2086
- * A global namespace for aria-valuemax.
2087
- * @type {string}
2088
- */
2089
- const ariaValueMax = 'aria-valuemax';
2090
-
2091
- /**
2092
- * Returns all color controls for `ColorPicker`.
2093
- *
2094
- * @param {CP.ColorPicker} self the `ColorPicker` instance
2095
- * @returns {HTMLElement | Element} color controls
2096
- */
2097
- function getColorControls(self) {
2098
- const { format, componentLabels } = self;
2099
- const {
2100
- hueLabel, alphaLabel, lightnessLabel, saturationLabel,
2101
- whitenessLabel, blacknessLabel,
2102
- } = componentLabels;
2103
-
2104
- const max1 = format === 'hsl' ? 360 : 100;
2105
- const max2 = format === 'hsl' ? 100 : 360;
2106
- const max3 = 100;
2107
-
2108
- let ctrl1Label = format === 'hsl'
2109
- ? `${hueLabel} & ${lightnessLabel}`
2110
- : `${lightnessLabel} & ${saturationLabel}`;
2111
-
2112
- ctrl1Label = format === 'hwb'
2113
- ? `${whitenessLabel} & ${blacknessLabel}`
2114
- : ctrl1Label;
2115
-
2116
- const ctrl2Label = format === 'hsl'
2117
- ? `${saturationLabel}`
2118
- : `${hueLabel}`;
2119
-
2120
- const colorControls = createElement({
2121
- tagName: 'div',
2122
- className: `color-controls ${format}`,
2123
- });
2124
-
2125
- const colorPointer = 'color-pointer';
2126
- const colorSlider = 'color-slider';
2127
-
2128
- const controls = [
2129
- {
2130
- i: 1,
2131
- c: colorPointer,
2132
- l: ctrl1Label,
2133
- min: 0,
2134
- max: max1,
2135
- },
2136
- {
2137
- i: 2,
2138
- c: colorSlider,
2139
- l: ctrl2Label,
2140
- min: 0,
2141
- max: max2,
2142
- },
2143
- {
2144
- i: 3,
2145
- c: colorSlider,
2146
- l: alphaLabel,
2147
- min: 0,
2148
- max: max3,
2149
- },
2150
- ];
2151
-
2152
- controls.forEach((template) => {
2153
- const {
2154
- i, c, l, min, max,
2155
- } = template;
2156
- const control = createElement({
2157
- tagName: 'div',
2158
- className: 'color-control',
2159
- });
2160
- setAttribute(control, 'role', 'presentation');
2161
-
2162
- control.append(
2163
- createElement({
2164
- tagName: 'div',
2165
- className: `visual-control visual-control${i}`,
2166
- }),
2167
- );
2168
-
2169
- const knob = createElement({
2170
- tagName: 'div',
2171
- className: `${c} knob`,
2172
- ariaLive: 'polite',
2173
- });
2174
-
2175
- setAttribute(knob, ariaLabel, l);
2176
- setAttribute(knob, 'role', 'slider');
2177
- setAttribute(knob, tabIndex, '0');
2178
- setAttribute(knob, ariaValueMin, `${min}`);
2179
- setAttribute(knob, ariaValueMax, `${max}`);
2180
- control.append(knob);
2181
- colorControls.append(control);
2182
- });
2183
-
2184
- return colorControls;
2185
- }
2186
-
2187
- /**
2188
- * Helps setting CSS variables to the color-menu.
2189
- * @param {HTMLElement} element
2190
- * @param {Record<string,any>} props
2191
- */
2192
- function setCSSProperties(element, props) {
2193
- ObjectKeys(props).forEach((prop) => {
2194
- element.style.setProperty(prop, props[prop]);
2195
- });
2196
- }
2197
-
2198
- /**
2199
- * Returns a color-defaults with given values and class.
2200
- * @param {CP.ColorPicker} self
2201
- * @param {CP.ColorPalette | string[]} colorsSource
2202
- * @param {string} menuClass
2203
- * @returns {HTMLElement | Element}
2204
- */
2205
- function getColorMenu(self, colorsSource, menuClass) {
2206
- const { input, format, componentLabels } = self;
2207
- const { defaultsLabel, presetsLabel } = componentLabels;
2208
- const isOptionsMenu = menuClass === 'color-options';
2209
- const isPalette = colorsSource instanceof ColorPalette;
2210
- const menuLabel = isOptionsMenu ? presetsLabel : defaultsLabel;
2211
- const colorsArray = isPalette ? colorsSource.colors : colorsSource;
2212
- const colorsCount = colorsArray.length;
2213
- const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
2214
- const fit = lightSteps || [9, 10].find((x) => colorsCount >= x * 2 && !(colorsCount % x)) || 5;
2215
- const isMultiLine = isOptionsMenu && colorsCount > fit;
2216
- let rowCountHover = 2;
2217
- rowCountHover = isMultiLine && colorsCount > fit * 2 ? 3 : rowCountHover;
2218
- rowCountHover = isMultiLine && colorsCount > fit * 3 ? 4 : rowCountHover;
2219
- rowCountHover = isMultiLine && colorsCount > fit * 4 ? 5 : rowCountHover;
2220
- const rowCount = rowCountHover - (colorsCount <= fit * 3 ? 1 : 2);
2221
- const isScrollable = isMultiLine && colorsCount > rowCount * fit;
2222
- let finalClass = menuClass;
2223
- finalClass += isScrollable ? ' scrollable' : '';
2224
- finalClass += isMultiLine ? ' multiline' : '';
2225
- const gap = isMultiLine ? '1px' : '0.25rem';
2226
- let optionSize = isMultiLine ? 1.75 : 2;
2227
- optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
2228
- const menuHeight = `${rowCount * optionSize}rem`;
2229
- const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
2230
- /** @type {HTMLUListElement} */
2231
- // @ts-ignore -- <UL> is an `HTMLElement`
2232
- const menu = createElement({
2233
- tagName: 'ul',
2234
- className: finalClass,
2235
- });
2236
- setAttribute(menu, 'role', 'listbox');
2237
- setAttribute(menu, ariaLabel, menuLabel);
2238
-
2239
- if (isScrollable) {
2240
- setCSSProperties(menu, {
2241
- '--grid-item-size': `${optionSize}rem`,
2242
- '--grid-fit': fit,
2243
- '--grid-gap': gap,
2244
- '--grid-height': menuHeight,
2245
- '--grid-hover-height': menuHeightHover,
2246
- });
2247
- }
2248
-
2249
- colorsArray.forEach((x) => {
2250
- let [value, label] = typeof x === 'string' ? x.trim().split(':') : [];
2251
- if (x instanceof Color) {
2252
- value = x.toHexString();
2253
- label = value;
2254
- }
2255
- const color = new Color(x instanceof Color ? x : value, format);
2256
- const isActive = color.toString() === getAttribute(input, 'value');
2257
- const active = isActive ? ' active' : '';
2258
-
2259
- const option = createElement({
2260
- tagName: 'li',
2261
- className: `color-option${active}`,
2262
- innerText: `${label || value}`,
2263
- });
2264
-
2265
- setAttribute(option, tabIndex, '0');
2266
- setAttribute(option, 'data-value', `${value}`);
2267
- setAttribute(option, 'role', 'option');
2268
- setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
2269
-
2270
- if (isOptionsMenu) {
2271
- setElementStyle(option, { backgroundColor: value });
2272
- }
2273
-
2274
- menu.append(option);
2275
- });
2276
- return menu;
2277
- }
2278
-
2279
- /**
2280
- * Generate HTML markup and update instance properties.
2281
- * @param {CP.ColorPicker} self
2282
- */
2283
- function setMarkup(self) {
2284
- const {
2285
- input, parent, format, id, componentLabels, colorKeywords, colorPresets,
2286
- } = self;
2287
- const colorValue = getAttribute(input, 'value') || '#fff';
2288
-
2289
- const {
2290
- toggleLabel, pickerLabel, formatLabel, hexLabel,
2291
- } = componentLabels;
2292
-
2293
- // update color
2294
- const color = nonColors.includes(colorValue) ? '#fff' : colorValue;
2295
- self.color = new Color(color, format);
2296
-
2297
- // set initial controls dimensions
2298
- const formatString = format === 'hex' ? hexLabel : toUpperCase(format);
2299
-
2300
- const pickerBtn = createElement({
2301
- id: `picker-btn-${id}`,
2302
- tagName: 'button',
2303
- className: 'picker-toggle btn-appearance',
2304
- });
2305
- setAttribute(pickerBtn, ariaExpanded, 'false');
2306
- setAttribute(pickerBtn, ariaHasPopup, 'true');
2307
- pickerBtn.append(createElement({
2308
- tagName: 'span',
2309
- className: vHidden,
2310
- innerText: `${pickerLabel}. ${formatLabel}: ${formatString}`,
2311
- }));
2312
-
2313
- const pickerDropdown = createElement({
2314
- tagName: 'div',
2315
- className: 'color-dropdown picker',
2316
- });
2317
- setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
2318
- setAttribute(pickerDropdown, 'role', 'group');
2319
-
2320
- const colorControls = getColorControls(self);
2321
- const colorForm = getColorForm(self);
2322
-
2323
- pickerDropdown.append(colorControls, colorForm);
2324
- input.before(pickerBtn);
2325
- parent.append(pickerDropdown);
2326
-
2327
- // set colour key menu template
2328
- if (colorKeywords || colorPresets) {
2329
- const presetsDropdown = createElement({
2330
- tagName: 'div',
2331
- className: 'color-dropdown scrollable menu',
2332
- });
2333
-
2334
- // color presets
2335
- if (colorPresets) {
2336
- presetsDropdown.append(getColorMenu(self, colorPresets, 'color-options'));
2337
- }
2338
-
2339
- // explicit defaults [reset, initial, inherit, transparent, currentColor]
2340
- // also custom defaults [default: #069, complementary: #930]
2341
- if (colorKeywords && colorKeywords.length) {
2342
- presetsDropdown.append(getColorMenu(self, colorKeywords, 'color-defaults'));
2343
- }
2344
-
2345
- const presetsBtn = createElement({
2346
- tagName: 'button',
2347
- className: 'menu-toggle btn-appearance',
2348
- });
2349
- setAttribute(presetsBtn, tabIndex, '-1');
2350
- setAttribute(presetsBtn, ariaExpanded, 'false');
2351
- setAttribute(presetsBtn, ariaHasPopup, 'true');
2352
-
2353
- const xmlns = encodeURI('http://www.w3.org/2000/svg');
2354
- const presetsIcon = createElementNS(xmlns, { tagName: 'svg' });
2355
- setAttribute(presetsIcon, 'xmlns', xmlns);
2356
- setAttribute(presetsIcon, 'viewBox', '0 0 512 512');
2357
- setAttribute(presetsIcon, ariaHidden, 'true');
2358
-
2359
- const path = createElementNS(xmlns, { tagName: 'path' });
2360
- setAttribute(path, 'd', 'M98,158l157,156L411,158l27,27L255,368L71,185L98,158z');
2361
- setAttribute(path, 'fill', '#fff');
2362
- presetsIcon.append(path);
2363
- presetsBtn.append(createElement({
2364
- tagName: 'span',
2365
- className: vHidden,
2366
- innerText: `${toggleLabel}`,
2367
- }), presetsIcon);
2368
-
2369
- parent.append(presetsBtn, presetsDropdown);
2370
- }
2371
-
2372
- // solve non-colors after settings save
2373
- if (colorKeywords && nonColors.includes(colorValue)) {
2374
- self.value = colorValue;
2375
- }
2376
- setAttribute(input, tabIndex, '-1');
2377
- }
2378
-
2379
- var version = "1.0.1";
2380
-
2381
- const Version = version;
2382
-
2383
- // ColorPicker GC
2384
- // ==============
2385
- const colorPickerString = 'color-picker';
2386
- const colorPickerSelector = `[data-function="${colorPickerString}"]`;
2387
- const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
2388
- const colorPickerDefaults = {
2389
- componentLabels: colorPickerLabels,
2390
- colorLabels: colorNames,
2391
- format: 'rgb',
2392
- colorPresets: false,
2393
- colorKeywords: false,
2394
- };
2395
-
2396
- // ColorPicker Static Methods
2397
- // ==========================
2398
-
2399
- /** @type {CP.GetInstance<ColorPicker, HTMLInputElement>} */
2400
- const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
2401
-
2402
- /** @type {CP.InitCallback<ColorPicker>} */
2403
- const initColorPicker = (element) => new ColorPicker(element);
2404
-
2405
- // ColorPicker Private Methods
2406
- // ===========================
2407
-
2408
- /**
2409
- * Add / remove `ColorPicker` main event listeners.
2410
- * @param {ColorPicker} self
2411
- * @param {boolean=} action
2412
- */
2413
- function toggleEvents(self, action) {
2414
- const fn = action ? addListener : removeListener;
2415
- const { input, pickerToggle, menuToggle } = self;
2416
-
2417
- fn(input, focusinEvent, self.showPicker);
2418
- fn(pickerToggle, mouseclickEvent, self.togglePicker);
2419
-
2420
- if (menuToggle) {
2421
- fn(menuToggle, mouseclickEvent, self.toggleMenu);
2422
- }
2423
- }
2424
-
2425
- /**
2426
- * Add / remove `ColorPicker` event listeners active only when open.
2427
- * @param {ColorPicker} self
2428
- * @param {boolean=} action
2429
- */
2430
- function toggleEventsOnShown(self, action) {
2431
- const fn = action ? addListener : removeListener;
2432
- const { input, colorMenu, parent } = self;
2433
- const doc = getDocument(input);
2434
- const win = doc.defaultView;
2435
-
2436
- fn(self.controls, pointerdownEvent, self.pointerDown);
2437
- self.controlKnobs.forEach((x) => fn(x, keydownEvent, self.handleKnobs));
2438
-
2439
- fn(win, scrollEvent, self.handleScroll);
2440
- fn(win, resizeEvent, self.update);
2441
-
2442
- [input, ...self.inputs].forEach((x) => fn(x, changeEvent, self.changeHandler));
2443
-
2444
- if (colorMenu) {
2445
- fn(colorMenu, mouseclickEvent, self.menuClickHandler);
2446
- fn(colorMenu, keydownEvent, self.menuKeyHandler);
2447
- }
2448
-
2449
- fn(doc, pointermoveEvent, self.pointerMove);
2450
- fn(doc, pointerupEvent, self.pointerUp);
2451
- fn(parent, focusoutEvent, self.handleFocusOut);
2452
- fn(doc, keyupEvent, self.handleDismiss);
2453
- }
2454
-
2455
- /**
2456
- * Triggers the `ColorPicker` original event.
2457
- * @param {ColorPicker} self
2458
- */
2459
- function firePickerChange(self) {
2460
- dispatchEvent(self.input, new CustomEvent('colorpicker.change'));
2461
- }
2462
-
2463
- /**
2464
- * Hides a visible dropdown.
2465
- * @param {HTMLElement} element
2466
- * @returns {void}
2467
- */
2468
- function removePosition(element) {
2469
- /* istanbul ignore else */
2470
- if (element) {
2471
- ['bottom', 'top'].forEach((x) => removeClass(element, x));
2472
- }
2473
- }
2474
-
2475
- /**
2476
- * Shows a `ColorPicker` dropdown and close the curent open dropdown.
2477
- * @param {ColorPicker} self
2478
- * @param {HTMLElement | Element} dropdown
2479
- */
2480
- function showDropdown(self, dropdown) {
2481
- const {
2482
- colorPicker, colorMenu, menuToggle, pickerToggle, parent,
2483
- } = self;
2484
- const isPicker = dropdown === colorPicker;
2485
- const openDropdown = isPicker ? colorMenu : colorPicker;
2486
- const activeBtn = isPicker ? menuToggle : pickerToggle;
2487
- const nextBtn = !isPicker ? menuToggle : pickerToggle;
2488
-
2489
- if (!hasClass(parent, 'open')) {
2490
- addClass(parent, 'open');
2491
- }
2492
- if (openDropdown) {
2493
- removeClass(openDropdown, 'show');
2494
- removePosition(openDropdown);
2495
- }
2496
- addClass(dropdown, 'bottom');
2497
- reflow(dropdown);
2498
- addClass(dropdown, 'show');
2499
-
2500
- if (isPicker) self.update();
2501
-
2502
- if (!self.isOpen) {
2503
- toggleEventsOnShown(self, true);
2504
- self.updateDropdownPosition();
2505
- self.isOpen = true;
2506
- setAttribute(self.input, tabIndex, '0');
2507
- if (menuToggle) {
2508
- setAttribute(menuToggle, tabIndex, '0');
2509
- }
2510
- }
2511
-
2512
- setAttribute(nextBtn, ariaExpanded, 'true');
2513
- if (activeBtn) {
2514
- setAttribute(activeBtn, ariaExpanded, 'false');
2515
- }
2516
- }
2517
-
2518
- /**
2519
- * Color Picker Web Component
2520
- * @see http://thednp.github.io/color-picker
2521
- */
2522
- class ColorPicker {
2523
- /**
2524
- * Returns a new `ColorPicker` instance. The target of this constructor
2525
- * must be an `HTMLInputElement`.
2526
- *
2527
- * @param {HTMLInputElement | string} target the target `<input>` element
2528
- * @param {CP.ColorPickerOptions=} config instance options
2529
- */
2530
- constructor(target, config) {
2531
- const self = this;
2532
- /** @type {HTMLInputElement} */
2533
- const input = querySelector(target);
2534
-
2535
- // invalidate
2536
- if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
2537
- self.input = input;
2538
-
2539
- const parent = closest(input, colorPickerParentSelector);
2540
- if (!parent) throw new TypeError('ColorPicker requires a specific markup to work.');
2541
-
2542
- /** @type {HTMLElement} */
2543
- self.parent = parent;
2544
-
2545
- /** @type {number} */
2546
- self.id = getUID(input, colorPickerString);
2547
-
2548
- // set initial state
2549
- /** @type {HTMLElement?} */
2550
- self.dragElement = null;
2551
- /** @type {boolean} */
2552
- self.isOpen = false;
2553
- /** @type {Record<string, number>} */
2554
- self.controlPositions = {
2555
- c1x: 0, c1y: 0, c2y: 0, c3y: 0,
2556
- };
2557
- /** @type {Record<string, string>} */
2558
- self.colorLabels = {};
2559
- /** @type {string[]=} */
2560
- self.colorKeywords = undefined;
2561
- /** @type {(ColorPalette | string[])=} */
2562
- self.colorPresets = undefined;
2563
-
2564
- // process options
2565
- const {
2566
- format, componentLabels, colorLabels, colorKeywords, colorPresets,
2567
- } = normalizeOptions(this.isCE ? parent : input, colorPickerDefaults, config || {});
2568
-
2569
- let translatedColorLabels = colorNames;
2570
- /* istanbul ignore else */
2571
- if (colorLabels instanceof Array && colorLabels.length === 17) {
2572
- translatedColorLabels = colorLabels;
2573
- } else if (colorLabels && colorLabels.split(',').length === 17) {
2574
- translatedColorLabels = colorLabels.split(',');
2575
- }
2576
-
2577
- // expose colour labels to all methods
2578
- colorNames.forEach((c, i) => {
2579
- self.colorLabels[c] = translatedColorLabels[i].trim();
2580
- });
2581
-
2582
- // update and expose component labels
2583
- const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
2584
- ? JSON.parse(componentLabels) : componentLabels;
2585
-
2586
- /** @type {Record<string, string>} */
2587
- self.componentLabels = ObjectAssign({ ...colorPickerLabels }, tempComponentLabels);
2588
-
2589
- /** @type {Color} */
2590
- self.color = new Color(input.value || '#fff', format);
2591
-
2592
- /** @type {CP.ColorFormats} */
2593
- self.format = format;
2594
-
2595
- // set colour defaults
2596
- if (colorKeywords instanceof Array && colorKeywords.length) {
2597
- self.colorKeywords = colorKeywords;
2598
- } else if (typeof colorKeywords === 'string' && colorKeywords.length) {
2599
- self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
2600
- }
2601
-
2602
- // set colour presets
2603
- if (colorPresets instanceof Array && colorPresets.length) {
2604
- self.colorPresets = colorPresets;
2605
- } else if (typeof colorPresets === 'string' && colorPresets.length) {
2606
- if (isValidJSON(colorPresets)) {
2607
- const { hue, hueSteps, lightSteps } = JSON.parse(colorPresets);
2608
- self.colorPresets = new ColorPalette(hue, hueSteps, lightSteps);
2609
- } else {
2610
- self.colorPresets = colorPresets.split(',').map((x) => x.trim());
2611
- }
2612
- }
2613
-
2614
- // bind events
2615
- self.showPicker = self.showPicker.bind(self);
2616
- self.togglePicker = self.togglePicker.bind(self);
2617
- self.toggleMenu = self.toggleMenu.bind(self);
2618
- self.menuClickHandler = self.menuClickHandler.bind(self);
2619
- self.menuKeyHandler = self.menuKeyHandler.bind(self);
2620
- self.pointerDown = self.pointerDown.bind(self);
2621
- self.pointerMove = self.pointerMove.bind(self);
2622
- self.pointerUp = self.pointerUp.bind(self);
2623
- self.update = self.update.bind(self);
2624
- self.handleScroll = self.handleScroll.bind(self);
2625
- self.handleFocusOut = self.handleFocusOut.bind(self);
2626
- self.changeHandler = self.changeHandler.bind(self);
2627
- self.handleDismiss = self.handleDismiss.bind(self);
2628
- self.handleKnobs = self.handleKnobs.bind(self);
2629
-
2630
- // generate markup
2631
- setMarkup(self);
2632
-
2633
- const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
2634
- // set main elements
2635
- /** @type {HTMLElement} */
2636
- self.pickerToggle = querySelector('.picker-toggle', parent);
2637
- /** @type {HTMLElement} */
2638
- self.menuToggle = querySelector('.menu-toggle', parent);
2639
- /** @type {HTMLElement} */
2640
- self.colorPicker = colorPicker;
2641
- /** @type {HTMLElement} */
2642
- self.colorMenu = colorMenu;
2643
- /** @type {HTMLInputElement[]} */
2644
- self.inputs = [...getElementsByClassName('color-input', parent)];
2645
- const [controls] = getElementsByClassName('color-controls', parent);
2646
- self.controls = controls;
2647
- /** @type {(HTMLElement | Element)[]} */
2648
- self.controlKnobs = [...getElementsByClassName('knob', controls)];
2649
- /** @type {(HTMLElement)[]} */
2650
- self.visuals = [...getElementsByClassName('visual-control', controls)];
2651
-
2652
- // update colour picker controls, inputs and visuals
2653
- self.update();
2654
-
2655
- // add main events listeners
2656
- toggleEvents(self, true);
2657
-
2658
- // set component data
2659
- Data.set(input, colorPickerString, self);
2660
- }
2661
-
2662
- /** Returns the current colour value */
2663
- get value() { return this.input.value; }
2664
-
2665
- /**
2666
- * Sets a new colour value.
2667
- * @param {string} v new colour value
2668
- */
2669
- set value(v) { this.input.value = v; }
2670
-
2671
- /** Check if the colour presets include any non-colour. */
2672
- get hasNonColor() {
2673
- return this.colorKeywords instanceof Array
2674
- && this.colorKeywords.some((x) => nonColors.includes(x));
2675
- }
2676
-
2677
- /** Check if the parent of the target is a `ColorPickerElement` instance. */
2678
- get isCE() { return this.parent.localName === colorPickerString; }
2679
-
2680
- /** Returns hexadecimal value of the current colour. */
2681
- get hex() { return this.color.toHex(true); }
2682
-
2683
- /** Returns the current colour value in {h,s,v,a} object format. */
2684
- get hsv() { return this.color.toHsv(); }
2685
-
2686
- /** Returns the current colour value in {h,s,l,a} object format. */
2687
- get hsl() { return this.color.toHsl(); }
2688
-
2689
- /** Returns the current colour value in {h,w,b,a} object format. */
2690
- get hwb() { return this.color.toHwb(); }
2691
-
2692
- /** Returns the current colour value in {r,g,b,a} object format. */
2693
- get rgb() { return this.color.toRgb(); }
2694
-
2695
- /** Returns the current colour brightness. */
2696
- get brightness() { return this.color.brightness; }
2697
-
2698
- /** Returns the current colour luminance. */
2699
- get luminance() { return this.color.luminance; }
2700
-
2701
- /** Checks if the current colour requires a light text colour. */
2702
- get isDark() {
2703
- const { color, brightness } = this;
2704
- return brightness < 120 && color.a > 0.33;
2705
- }
2706
-
2707
- /** Checks if the current input value is a valid colour. */
2708
- get isValid() {
2709
- const inputValue = this.input.value;
2710
- return inputValue !== '' && new Color(inputValue).isValid;
2711
- }
2712
-
2713
- /** Returns the colour appearance, usually the closest colour name for the current value. */
2714
- get appearance() {
2715
- const {
2716
- colorLabels, hsl, hsv, format,
2717
- } = this;
2718
-
2719
- const hue = roundPart(hsl.h * 360);
2720
- const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
2721
- const saturation = roundPart(saturationSource * 100);
2722
- const lightness = roundPart(hsl.l * 100);
2723
- const hsvl = hsv.v * 100;
2724
-
2725
- let colorName;
2726
-
2727
- // determine color appearance
2728
- /* istanbul ignore else */
2729
- if (lightness === 100 && saturation === 0) {
2730
- colorName = colorLabels.white;
2731
- } else if (lightness === 0) {
2732
- colorName = colorLabels.black;
2733
- } else if (saturation === 0) {
2734
- colorName = colorLabels.grey;
2735
- } else if (hue < 15 || hue >= 345) {
2736
- colorName = colorLabels.red;
2737
- } else if (hue >= 15 && hue < 45) {
2738
- colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
2739
- } else if (hue >= 45 && hue < 75) {
2740
- const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
2741
- const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
2742
- colorName = isGold ? colorLabels.gold : colorLabels.yellow;
2743
- colorName = isOlive ? colorLabels.olive : colorName;
2744
- } else if (hue >= 75 && hue < 155) {
2745
- colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
2746
- } else if (hue >= 155 && hue < 175) {
2747
- colorName = colorLabels.teal;
2748
- } else if (hue >= 175 && hue < 195) {
2749
- colorName = colorLabels.cyan;
2750
- } else if (hue >= 195 && hue < 255) {
2751
- colorName = colorLabels.blue;
2752
- } else if (hue >= 255 && hue < 270) {
2753
- colorName = colorLabels.violet;
2754
- } else if (hue >= 270 && hue < 295) {
2755
- colorName = colorLabels.magenta;
2756
- } else if (hue >= 295 && hue < 345) {
2757
- colorName = colorLabels.pink;
2758
- }
2759
- return colorName;
2760
- }
2761
-
2762
- /** Updates `ColorPicker` visuals. */
2763
- updateVisuals() {
2764
- const self = this;
2765
- const {
2766
- controlPositions, visuals,
2767
- } = self;
2768
- const [v1, v2, v3] = visuals;
2769
- const { offsetHeight } = v1;
2770
- const hue = controlPositions.c2y / offsetHeight;
2771
- const { r, g, b } = new Color({ h: hue, s: 1, l: 0.5 }).toRgb();
2772
- const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
2773
- const alpha = 1 - controlPositions.c3y / offsetHeight;
2774
- const roundA = roundPart((alpha * 100)) / 100;
2775
-
2776
- const fill = new Color({
2777
- h: hue, s: 1, l: 0.5, a: alpha,
2778
- }).toRgbString();
2779
- const hueGradient = `linear-gradient(
1
+ var ColorPicker=function(){"use strict";const Ut="aria-description",xt="aria-expanded",Me="aria-hidden",Pt="aria-selected",Q="aria-valuenow",tt="aria-valuetext",Te="change",Re="DOMContentLoaded",Oe="focusin",De="focusout",_t="keydown",Fe="keyup",St="click",Ie="pointerdown",zt="pointermove",Ke="pointerup",Ve="resize",Be="scroll",je="touchmove",j="ArrowDown",et="ArrowUp",ht="ArrowLeft",U="ArrowRight",qe="Enter",Ge="Escape",Wt="Space",Ue="transitionDuration",_e="transitionProperty",W="tabindex",ze=navigator.userAgentData,gt=ze,{userAgent:We}=navigator,ut=We,Jt=/iPhone|iPad|iPod|Android/i;gt?gt.brands.some(o=>Jt.test(o.brand)):Jt.test(ut);const Xt=/(iPhone|iPod|iPad)/;gt?gt.brands.some(o=>Xt.test(o.brand)):Xt.test(ut),ut&&ut.includes("Firefox");const{head:bt}=document;["webkitPerspective","perspective"].some(o=>o in bt.style);const Ct=(o,t,e,s)=>{const r=s||!1;o.addEventListener(t,e,r)},Lt=(o,t,e,s)=>{const r=s||!1;o.removeEventListener(t,e,r)},Je=(o,t,e,s)=>{const r=n=>{(n.target===o||n.currentTarget===o)&&(e.apply(o,[n]),Lt(o,t,r,s))};Ct(o,t,r,s)},Xe=()=>{};(()=>{let o=!1;try{const t=Object.defineProperty({},"passive",{get:()=>(o=!0,o)});Je(document,Re,Xe,t)}catch{}return o})(),["webkitTransform","transform"].some(o=>o in bt.style),["webkitAnimation","animation"].some(o=>o in bt.style),["webkitTransition","transition"].some(o=>o in bt.style);const pt=(o,t)=>o.getAttribute(t),m=(o,t,e)=>o.setAttribute(t,e),At=(o,t)=>o.removeAttribute(t),q=(o,...t)=>{o.classList.add(...t)},O=(o,...t)=>{o.classList.remove(...t)},M=(o,t)=>o.classList.contains(t),Ht=o=>o!=null&&typeof o=="object"||!1,J=o=>Ht(o)&&typeof o.nodeType=="number"&&[1,2,3,4,5,6,7,8,9,10,11].some(t=>o.nodeType===t)||!1,ot=o=>J(o)&&o.nodeType===1||!1,X=new Map,st={data:X,set:(o,t,e)=>{ot(o)&&(X.has(t)||X.set(t,new Map),X.get(t).set(o,e))},getAllFor:o=>X.get(o)||null,get:(o,t)=>{if(!ot(o)||!t)return null;const e=st.getAllFor(t);return o&&e&&e.get(o)||null},remove:(o,t)=>{const e=st.getAllFor(t);!e||!ot(o)||(e.delete(o),e.size===0&&X.delete(t))}},Ze=(o,t)=>st.get(o,t),B=o=>typeof o=="string"||!1,Ye=o=>Ht(o)&&o.constructor.name==="Window"||!1,Zt=o=>J(o)&&o.nodeType===9||!1,F=o=>Ye(o)?o.document:Zt(o)?o:J(o)?o.ownerDocument:window.document,I=(o,...t)=>Object.assign(o,...t),x=o=>{if(!o)return;if(B(o))return F().createElement(o);const{tagName:t}=o,e=x(t);if(!e)return;const s={...o};return delete s.tagName,I(e,s)},Nt=(o,t)=>{if(!o||!t)return;if(B(t))return F().createElementNS(o,t);const{tagName:e}=t,s=Nt(o,e);if(!s)return;const r={...t};return delete r.tagName,I(s,r)},Qe=(o,t)=>o.dispatchEvent(t),Et=(o,t)=>{const e=getComputedStyle(o),s=t.replace("webkit","Webkit").replace(/([A-Z])/g,"-$1").toLowerCase();return e.getPropertyValue(s)},to=o=>{const t=Et(o,_e),e=Et(o,Ue),s=e.includes("ms")?1:1e3,r=t&&t!=="none"?parseFloat(e)*s:0;return Number.isNaN(r)?0:r},_=(o,t)=>o.focus(t),Yt=o=>["true",!0].includes(o)?!0:["false",!1].includes(o)?!1:["null","",null,void 0].includes(o)?null:o!==""&&!Number.isNaN(+o)?+o:o,dt=o=>Object.entries(o),eo=o=>o.toLowerCase(),oo=(o,t,e,s)=>{const r={...e},n={...o.dataset},i={...t},a={},l="title";return dt(n).forEach(([c,h])=>{const b=s&&typeof c=="string"&&c.includes(s)?c.replace(s,"").replace(/[A-Z]/g,u=>eo(u)):c;a[b]=Yt(h)}),dt(r).forEach(([c,h])=>{r[c]=Yt(h)}),dt(t).forEach(([c,h])=>{c in r?i[c]=r[c]:c in a?i[c]=a[c]:i[c]=c===l?pt(o,l):h}),i},Qt=o=>Object.fromEntries(o),so=o=>o.offsetHeight,D=(o,t)=>{dt(t).forEach(([e,s])=>{if(s&&B(e)&&e.includes("--"))o.style.setProperty(e,s);else{const r={};r[e]=s,I(o.style,r)}})},ro=o=>Ht(o)&&o.constructor.name==="Map"||!1,te=o=>o.toUpperCase(),ft=(o,t)=>{const{width:e,height:s,top:r,right:n,bottom:i,left:a}=o.getBoundingClientRect();let l=1,c=1;if(t&&ot(o)){const{offsetWidth:h,offsetHeight:b}=o;l=h>0?Math.round(e)/h:1,c=b>0?Math.round(s)/b:1}return{width:e/l,height:s/c,top:r/c,right:n/l,bottom:i/c,left:a/l,x:a/l,y:r/c}},Mt=o=>F(o).documentElement;let ee=0,oe=0;const Z=new Map,se=(o,t)=>{let e=t?ee:oe;if(t){const s=se(o),r=Z.get(s)||new Map;Z.has(s)||Z.set(s,r),ro(r)&&!r.has(t)?(r.set(t,e),ee+=1):e=r.get(t)}else{const s=o.id||o;Z.has(s)?e=Z.get(s):(Z.set(s,e),oe+=1)}return e},no=o=>{var t;return o?Zt(o)?o.defaultView:J(o)?(t=o?.ownerDocument)==null?void 0:t.defaultView:o:window},Tt=o=>Array.isArray(o)||!1,re=(o,t)=>o?o.closest(t)||re(o.getRootNode().host,t):null,z=(o,t)=>ot(o)?o:(J(t)?t:F()).querySelector(o),rt=(o,t)=>(t&&J(t)?t:F()).getElementsByClassName(o),Rt=["transparent","currentColor","inherit","revert","initial"],v=o=>{const t=Math.floor(o);return o-t<.5?t:Math.round(o)},mt=[["aliceblue",{r:240,g:248,b:255}],["antiquewhite",{r:250,g:235,b:215}],["aqua",{r:0,g:255,b:255}],["aquamarine",{r:127,g:255,b:212}],["azure",{r:240,g:255,b:255}],["beige",{r:245,g:245,b:220}],["bisque",{r:255,g:228,b:196}],["black",{r:0,g:0,b:0}],["blanchedalmond",{r:255,g:235,b:205}],["blue",{r:0,g:0,b:255}],["blueviolet",{r:138,g:43,b:226}],["brown",{r:165,g:42,b:42}],["burlywood",{r:222,g:184,b:135}],["cadetblue",{r:95,g:158,b:160}],["chartreuse",{r:127,g:255,b:0}],["chocolate",{r:210,g:105,b:30}],["coral",{r:255,g:127,b:80}],["cornflowerblue",{r:100,g:149,b:237}],["cornsilk",{r:255,g:248,b:220}],["crimson",{r:220,g:20,b:60}],["cyan",{r:0,g:255,b:255}],["darkblue",{r:0,g:0,b:139}],["darkcyan",{r:0,g:139,b:139}],["darkgoldenrod",{r:184,g:134,b:11}],["darkgray",{r:169,g:169,b:169}],["darkgreen",{r:0,g:100,b:0}],["darkgrey",{r:169,g:169,b:169}],["darkkhaki",{r:189,g:183,b:107}],["darkmagenta",{r:139,g:0,b:139}],["darkolivegreen",{r:85,g:107,b:47}],["darkorange",{r:255,g:140,b:0}],["darkorchid",{r:153,g:50,b:204}],["darkred",{r:139,g:0,b:0}],["darksalmon",{r:233,g:150,b:122}],["darkseagreen",{r:143,g:188,b:143}],["darkslateblue",{r:72,g:61,b:139}],["darkslategray",{r:47,g:79,b:79}],["darkslategrey",{r:47,g:79,b:79}],["darkturquoise",{r:0,g:206,b:209}],["darkviolet",{r:148,g:0,b:211}],["deeppink",{r:255,g:20,b:147}],["deepskyblue",{r:0,g:191,b:255}],["dimgray",{r:105,g:105,b:105}],["dimgrey",{r:105,g:105,b:105}],["dodgerblue",{r:30,g:144,b:255}],["firebrick",{r:178,g:34,b:34}],["floralwhite",{r:255,g:250,b:240}],["forestgreen",{r:34,g:139,b:34}],["fuchsia",{r:255,g:0,b:255}],["gainsboro",{r:220,g:220,b:220}],["ghostwhite",{r:248,g:248,b:255}],["goldenrod",{r:218,g:165,b:32}],["gold",{r:255,g:215,b:0}],["gray",{r:128,g:128,b:128}],["green",{r:0,g:128,b:0}],["greenyellow",{r:173,g:255,b:47}],["grey",{r:128,g:128,b:128}],["honeydew",{r:240,g:255,b:240}],["hotpink",{r:255,g:105,b:180}],["indianred",{r:205,g:92,b:92}],["indigo",{r:75,g:0,b:130}],["ivory",{r:255,g:255,b:240}],["khaki",{r:240,g:230,b:140}],["lavenderblush",{r:255,g:240,b:245}],["lavender",{r:230,g:230,b:250}],["lawngreen",{r:124,g:252,b:0}],["lemonchiffon",{r:255,g:250,b:205}],["lightblue",{r:173,g:216,b:230}],["lightcoral",{r:240,g:128,b:128}],["lightcyan",{r:224,g:255,b:255}],["lightgoldenrodyellow",{r:250,g:250,b:210}],["lightgray",{r:211,g:211,b:211}],["lightgreen",{r:144,g:238,b:144}],["lightgrey",{r:211,g:211,b:211}],["lightpink",{r:255,g:182,b:193}],["lightsalmon",{r:255,g:160,b:122}],["lightseagreen",{r:32,g:178,b:170}],["lightskyblue",{r:135,g:206,b:250}],["lightslategray",{r:119,g:136,b:153}],["lightslategrey",{r:119,g:136,b:153}],["lightsteelblue",{r:176,g:196,b:222}],["lightyellow",{r:255,g:255,b:224}],["lime",{r:0,g:255,b:0}],["limegreen",{r:50,g:205,b:50}],["linen",{r:250,g:240,b:230}],["magenta",{r:255,g:0,b:255}],["maroon",{r:128,g:0,b:0}],["mediumaquamarine",{r:102,g:205,b:170}],["mediumblue",{r:0,g:0,b:205}],["mediumorchid",{r:186,g:85,b:211}],["mediumpurple",{r:147,g:112,b:219}],["mediumseagreen",{r:60,g:179,b:113}],["mediumslateblue",{r:123,g:104,b:238}],["mediumspringgreen",{r:0,g:250,b:154}],["mediumturquoise",{r:72,g:209,b:204}],["mediumvioletred",{r:199,g:21,b:133}],["midnightblue",{r:25,g:25,b:112}],["mintcream",{r:245,g:255,b:250}],["mistyrose",{r:255,g:228,b:225}],["moccasin",{r:255,g:228,b:181}],["navajowhite",{r:255,g:222,b:173}],["navy",{r:0,g:0,b:128}],["oldlace",{r:253,g:245,b:230}],["olive",{r:128,g:128,b:0}],["olivedrab",{r:107,g:142,b:35}],["orange",{r:255,g:165,b:0}],["orangered",{r:255,g:69,b:0}],["orchid",{r:218,g:112,b:214}],["palegoldenrod",{r:238,g:232,b:170}],["palegreen",{r:152,g:251,b:152}],["paleturquoise",{r:175,g:238,b:238}],["palevioletred",{r:219,g:112,b:147}],["papayawhip",{r:255,g:239,b:213}],["peachpuff",{r:255,g:218,b:185}],["peru",{r:205,g:133,b:63}],["pink",{r:255,g:192,b:203}],["plum",{r:221,g:160,b:221}],["powderblue",{r:176,g:224,b:230}],["purple",{r:128,g:0,b:128}],["rebeccapurple",{r:102,g:51,b:153}],["red",{r:255,g:0,b:0}],["rosybrown",{r:188,g:143,b:143}],["royalblue",{r:65,g:105,b:225}],["saddlebrown",{r:139,g:69,b:19}],["salmon",{r:250,g:128,b:114}],["sandybrown",{r:244,g:164,b:96}],["seagreen",{r:46,g:139,b:87}],["seashell",{r:255,g:245,b:238}],["sienna",{r:160,g:82,b:45}],["silver",{r:192,g:192,b:192}],["skyblue",{r:135,g:206,b:235}],["slateblue",{r:106,g:90,b:205}],["slategray",{r:112,g:128,b:144}],["slategrey",{r:112,g:128,b:144}],["snow",{r:255,g:250,b:250}],["springgreen",{r:0,g:255,b:127}],["steelblue",{r:70,g:130,b:180}],["tan",{r:210,g:180,b:140}],["teal",{r:0,g:128,b:128}],["thistle",{r:216,g:191,b:216}],["tomato",{r:255,g:99,b:71}],["turquoise",{r:64,g:224,b:208}],["violet",{r:238,g:130,b:238}],["wheat",{r:245,g:222,b:179}],["white",{r:255,g:255,b:255}],["whitesmoke",{r:245,g:245,b:245}],["yellow",{r:255,g:255,b:0}],["yellowgreen",{r:154,g:205,b:50}]],ne="deg|rad|grad|turn",ie="[-\\+]?\\d+%?",ae="[-\\+]?\\d*\\.\\d+%?",le=`[-\\+]?\\d*\\.?\\d+(?:${ne})?`,vt=`(?:${ae})|(?:${ie})`,Ot=`(?:${vt})|(?:${le}?)`,io="(?:[\\s|\\(\\s|\\s\\(\\s]+)?",ao="(?:[\\s|\\)\\s]+)?",ce="(?:[,|\\s]+)",lo="(?:[,|\\/\\s]*)?",nt=`${io}(${Ot})${ce}(${vt})${ce}(${vt})${lo}(${vt})?${ao}`,K={CSS_UNIT:new RegExp(Ot),ANGLES:ne,CSS_ANGLE:le,CSS_INTEGER:ie,CSS_NUMBER:ae,CSS_UNIT2:Ot,PERMISSIVE_MATCH:nt,hwb:new RegExp(`hwb${nt}`),rgb:new RegExp(`rgb(?:a)?${nt}`),hsl:new RegExp(`hsl(?:a)?${nt}`),hsv:new RegExp(`hsv(?:a)?${nt}`),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/},he=o=>Rt.includes(o),it=(o,t)=>o!==null&&typeof o=="object"&&Object.keys(t).every(e=>e in o),ge=o=>`${o}`.includes(".")&&parseFloat(o)===1,wt=o=>typeof o=="string"&&o.includes("%"),L=o=>!!K.CSS_UNIT.exec(`${o}`),ue=["rgb","hex","hsl","hsv","hwb"],be=o=>Rt.includes(o)||["#",...ue].some(t=>o.includes(t))?!1:mt.some(([t])=>o===t),T=(o,t)=>{let e=o;if(typeof o=="number"&&Math.min(o,0)===0&&Math.max(o,1)===1)return o;ge(o)&&(e="100%");const s=wt(e);return e=t===360?parseFloat(e):Math.min(t,Math.max(0,parseFloat(e))),s&&(e=e*t/100),Math.abs(e-t)<1e-6?1:(t===360?e=(e<0?e%t+t:e%t)/t:e=e%t/t,e)},Dt=o=>{let t=parseFloat(o);return(Number.isNaN(t)||t<0||t>1)&&(t=1),t},$t=o=>Math.min(1,Math.max(0,o)),G=o=>o.length===1?`0${o}`:String(o),pe=o=>{const[[,t]]=mt.filter(([e])=>e===o.toLowerCase());return t},A=o=>parseInt(o,16),Ft=o=>A(o)/255,de=o=>v(o*255).toString(16),fe=(o,t,e)=>{const s=Math.max(o,t,e),r=Math.min(o,t,e);let n=0,i=0;const a=(s+r)/2;if(s===r)i=0,n=0;else{const l=s-r;i=a>.5?l/(2-s-r):l/(s+r),s===o&&(n=(t-e)/l+(t<e?6:0)),s===t&&(n=(e-o)/l+2),s===e&&(n=(o-t)/l+4),n/=6}return{h:n,s:i,l:a}},kt=(o,t,e)=>{let s=e;return s<0&&(s+=1),s>1&&(s-=1),s<1/6?o+(t-o)*(6*s):s<1/2?t:s<2/3?o+(t-o)*(2/3-s)*6:o},at=(o,t,e)=>{let s=0,r=0,n=0;if(t===0)r=e,n=e,s=e;else if(e){const i=e<.5?e*(1+t):e+t-e*t,a=2*e-i;s=kt(a,i,o+1/3),r=kt(a,i,o),n=kt(a,i,o-1/3)}return{r:s,g:r,b:n}},me=(o,t,e)=>{let s=0,r=0;const n=Math.min(o,t,e),i=Math.max(o,t,e),a=1-i;if(i===n)return{h:0,w:n,b:a};o===n?(s=t-e,r=3):(s=t===n?e-o:o-t,r=t===n?5:1);const l=(r-s/(i-n))/6;return{h:l===1?0:l,w:n,b:a}},ve=(o,t,e)=>{if(t+e>=1){const i=t/(t+e);return{r:i,g:i,b:i}}let{r:s,g:r,b:n}=at(o,1,.5);return[s,r,n]=[s,r,n].map(i=>i*(1-t-e)+t),{r:s,g:r,b:n}},we=(o,t,e)=>{const s=Math.max(o,t,e),r=Math.min(o,t,e);let n=0;const i=s,a=s-r,l=s===0?0:a/s;return s===r?n=0:(o===s&&(n=(t-e)/a+(t<e?6:0)),t===s&&(n=(e-o)/a+2),e===s&&(n=(o-t)/a+4),n/=6),{h:n,s:l,v:i}},It=(o,t,e)=>{const s=o*6,r=t,n=e,i=Math.floor(s),a=s-i,l=n*(1-r),c=n*(1-a*r),h=n*(1-(1-a)*r),b=i%6,u=[n,c,l,l,h,n][b],d=[h,n,n,c,l,l][b],g=[l,l,h,n,n,c][b];return{r:u,g:d,b:g}},$e=(o,t,e,s)=>{const r=[G(v(o).toString(16)),G(v(t).toString(16)),G(v(e).toString(16))];return s&&r[0].charAt(0)===r[0].charAt(1)&&r[1].charAt(0)===r[1].charAt(1)&&r[2].charAt(0)===r[2].charAt(1)?r[0].charAt(0)+r[1].charAt(0)+r[2].charAt(0):r.join("")},Kt=(o,t,e,s,r)=>{const n=[G(v(o).toString(16)),G(v(t).toString(16)),G(v(e).toString(16)),G(de(s))];return r&&n[0].charAt(0)===n[0].charAt(1)&&n[1].charAt(0)===n[1].charAt(1)&&n[2].charAt(0)===n[2].charAt(1)&&n[3].charAt(0)===n[3].charAt(1)?n[0].charAt(0)+n[1].charAt(0)+n[2].charAt(0)+n[3].charAt(0):n.join("")},ke=o=>{const t=String(o).trim().toLowerCase();if(be(t))return Object.assign(pe(t),{a:1,format:"rgb",ok:!0});if(he(t))return{r:0,g:0,b:0,a:t==="transparent"?0:1,format:"rgb",ok:!0};let[,e,s,r,n]=K.rgb.exec(t)||[];return e&&s&&r?{r:e,g:s,b:r,a:n!==void 0?n:1,format:"rgb",ok:!0}:([,e,s,r,n]=K.hsl.exec(t)||[],e&&s&&r?{h:e,s,l:r,a:n!==void 0?n:1,format:"hsl",ok:!0}:([,e,s,r,n]=K.hsv.exec(t)||[],e&&s&&r?{h:e,s,v:r,a:n!==void 0?n:1,format:"hsv",ok:!0}:([,e,s,r,n]=K.hwb.exec(t)||[],e&&s&&r?{h:e,w:s,b:r,a:n!==void 0?n:1,format:"hwb",ok:!0}:([,e,s,r,n]=K.hex8.exec(t)||[],e&&s&&r&&n?{r:A(e),g:A(s),b:A(r),a:Ft(n),format:"hex",ok:!0}:([,e,s,r]=K.hex6.exec(t)||[],e&&s&&r?{r:A(e),g:A(s),b:A(r),a:1,format:"hex",ok:!0}:([,e,s,r,n]=K.hex4.exec(t)||[],e&&s&&r&&n?{r:A(e+e),g:A(s+s),b:A(r+r),a:Ft(n+n),format:"hex",ok:!0}:([,e,s,r]=K.hex3.exec(t)||[],e&&s&&r?{r:A(e+e),g:A(s+s),b:A(r+r),a:1,format:"hex",ok:!0}:{r:0,g:0,b:0,a:1,format:"rgb",ok:!o})))))))},ye=o=>{let t={r:0,g:0,b:0},e=o,s=1,r,n,i,a,l,c,h,b,u="rgb",d=!1;return(!e||typeof e=="string")&&(e=ke(e),d=e.ok),it(e,t)&&L(e.r)&&L(e.g)&&L(e.b)&&({r:h,g:b,b:l}=e,[h,b,l]=[h,b,l].map(g=>T(g,wt(g)?100:255)),t={r:h,g:b,b:l},u="format"in e?e.format:"rgb"),it(e,{h:0,s:0,v:0})&&L(e.h)&&L(e.s)&&L(e.v)&&({h:c,s:r,v:n}=e,c=T(c,360),r=T(r,100),n=T(n,100),t=It(c,r,n),u="hsv"),it(e,{h:0,s:0,l:0})&&L(e.h)&&L(e.s)&&L(e.l)&&({h:c,s:r,l:i}=e,c=T(c,360),r=T(r,100),i=T(i,100),t=at(c,r,i),u="hsl"),it(e,{h:0,w:0,b:0})&&L(e.h)&&L(e.w)&&L(e.b)&&({h:c,w:a,b:l}=e,c=T(c,360),a=T(a,100),l=T(l,100),t=ve(c,a,l),u="hwb"),L(e.a)&&(s=e.a,s=wt(s)||parseFloat(`${s}`)>1?T(s,100):s),{r:t.r,g:t.g,b:t.b,a:Dt(s),format:u,ok:d}},co="1.0.8";class k{static matchers=K;static isOnePointZero=ge;static isPercentage=wt;static isValidCSSUnit=L;static isNonColor=he;static isColorName=be;static isColorType=it;static pad2=G;static clamp01=$t;static bound01=T;static boundAlpha=Dt;static getRGBFromName=pe;static convertHexToDecimal=Ft;static convertDecimalToHex=de;static rgbToHsl=fe;static rgbToHex=$e;static rgbToHsv=we;static rgbToHwb=me;static rgbaToHex=Kt;static hslToRgb=It;static hsvToRgb=It;static hueToRgb=kt;static hwbToRgb=ve;static parseIntFromHex=A;static stringInputToObject=ke;static inputToRGB=ye;static roundPart=v;static webColors=mt;static nonColors=Rt;static version=co;r;g;b;a;format;ok;originalInput;constructor(t,e){const s=e&&ue.includes(e)?e:"",{r,g:n,b:i,a,ok:l,format:c}=ye(t);this.originalInput=t,this.r=r,this.g=n,this.b=i,this.a=a,this.ok=l,this.format=s||c}get isValid(){return this.ok}get isDark(){return this.brightness<120}get luminance(){const{r:t,g:e,b:s}=this;let r=0,n=0,i=0;return t<=.03928?r=t/12.92:r=((t+.055)/1.055)**2.4,e<=.03928?n=e/12.92:n=((e+.055)/1.055)**2.4,s<=.03928?i=s/12.92:i=((s+.055)/1.055)**2.4,.2126*r+.7152*n+.0722*i}get brightness(){const{r:t,g:e,b:s}=this.toRgb();return(t*299+e*587+s*114)/1e3}get name(){const{r:t,g:e,b:s}=this.toRgb(),[r]=mt.map(([n,i])=>{const a=(((i.r-t)*.3)**2+((i.g-e)*.6)**2+((i.b-s)*.1)**2)**.5;return[n,a]}).find(([,n],i,a)=>n===Math.min(...a.map(([,l])=>l)));return r}toRgb(){let{r:t,g:e,b:s,a:r}=this;return[t,e,s]=[t,e,s].map(n=>v(n*255*100)/100),r=v(r*100)/100,{r:t,g:e,b:s,a:r}}toRgbString(){const{r:t,g:e,b:s,a:r}=this.toRgb(),[n,i,a]=[t,e,s].map(v);return r===1?`rgb(${n}, ${i}, ${a})`:`rgba(${n}, ${i}, ${a}, ${r})`}toRgbCSS4String(){const{r:t,g:e,b:s,a:r}=this.toRgb(),[n,i,a]=[t,e,s].map(v),l=r===1?"":` / ${v(r*100)}%`;return`rgb(${n} ${i} ${a}${l})`}toHex(t){const{r:e,g:s,b:r,a:n}=this.toRgb();return n===1?$e(e,s,r,t):Kt(e,s,r,n,t)}toHexString(t){return`#${this.toHex(t)}`}toHex8(t){const{r:e,g:s,b:r,a:n}=this.toRgb();return Kt(e,s,r,n,t)}toHex8String(t){return`#${this.toHex8(t)}`}toHsv(){const{r:t,g:e,b:s,a:r}=this,{h:n,s:i,v:a}=we(t,e,s);return{h:n,s:i,v:a,a:r}}toHsl(){const{r:t,g:e,b:s,a:r}=this,{h:n,s:i,l:a}=fe(t,e,s);return{h:n,s:i,l:a,a:r}}toHslString(){let{h:t,s:e,l:s,a:r}=this.toHsl();return t=v(t*360),e=v(e*100),s=v(s*100),r=v(r*100)/100,r===1?`hsl(${t}, ${e}%, ${s}%)`:`hsla(${t}, ${e}%, ${s}%, ${r})`}toHslCSS4String(){let{h:t,s:e,l:s,a:r}=this.toHsl();t=v(t*360),e=v(e*100),s=v(s*100),r=v(r*100);const n=r<100?` / ${v(r)}%`:"";return`hsl(${t}deg ${e}% ${s}%${n})`}toHwb(){const{r:t,g:e,b:s,a:r}=this,{h:n,w:i,b:a}=me(t,e,s);return{h:n,w:i,b:a,a:r}}toHwbString(){let{h:t,w:e,b:s,a:r}=this.toHwb();t=v(t*360),e=v(e*100),s=v(s*100),r=v(r*100);const n=r<100?` / ${v(r)}%`:"";return`hwb(${t}deg ${e}% ${s}%${n})`}setAlpha(t){return typeof t!="number"?this:(this.a=Dt(t),this)}saturate(t){if(typeof t!="number")return this;const{h:e,s,l:r}=this.toHsl(),{r:n,g:i,b:a}=at(e,$t(s+t/100),r);return Object.assign(this,{r:n,g:i,b:a}),this}desaturate(t){return typeof t=="number"?this.saturate(-t):this}greyscale(){return this.saturate(-100)}lighten(t){if(typeof t!="number")return this;const{h:e,s,l:r}=this.toHsl(),{r:n,g:i,b:a}=at(e,s,$t(r+t/100));return Object.assign(this,{r:n,g:i,b:a}),this}darken(t){return typeof t=="number"?this.lighten(-t):this}spin(t){if(typeof t!="number")return this;const{h:e,s,l:r}=this.toHsl(),{r:n,g:i,b:a}=at($t((e*360+t)%360/360),s,r);return Object.assign(this,{r:n,g:i,b:a}),this}clone(){return new k(this)}toString(t){const{format:e}=this;return e==="hex"?this.toHexString(t):e==="hsl"?this.toHslString():e==="hwb"?this.toHwbString():this.toRgbString()}}class Vt{static Color=k;hue;hueSteps;lightSteps;saturation;colors;constructor(...t){let e=0,s=12,r=10,n=[.5],i=100;if(t.length===4)[e,s,r,i]=t;else if(t.length===3)[e,s,r]=t;else if(t.length===2&&([s,r]=t,[s,r].some(g=>g<1)))throw TypeError("ColorPalette: the two minimum arguments must be numbers higher than 0.");const a=[],l=360/s,c=k.roundPart((r-(r%2?1:0))/2),h=[.25,.2,.15,.11,.09,.075],b=[[1,2,3],[4,5],[6,7],[8,9],[10,11],[12,13]],u=b.find(g=>g.includes(r)),d=u?h[b.indexOf(u)]:100/(r+(r%2?0:1))/100;for(let g=1;g<c+1;g+=1)n=[...n,.5+d*g];for(let g=1;g<r-c;g+=1)n=[.5-d*g,...n];for(let g=0;g<s;g+=1){const p=(e+g*l)%360/360;n.forEach(f=>{const w=new k({h:p,s:1,l:f});a.push(i<100?w.saturate(i-100):w)})}this.hue=e,this.hueSteps=s,this.lightSteps=r,this.saturation=i,this.colors=a}}const Bt={pickerLabel:"Colour Picker",appearanceLabel:"Colour Appearance",valueLabel:"Colour Value",toggleLabel:"Select Colour",presetsLabel:"Colour Presets",defaultsLabel:"Colour Defaults",formatLabel:"Format",alphaLabel:"Alpha",hexLabel:"Hexadecimal",hueLabel:"Hue",whitenessLabel:"Whiteness",blacknessLabel:"Blackness",saturationLabel:"Saturation",lightnessLabel:"Lightness",redLabel:"Red",greenLabel:"Green",blueLabel:"Blue"},lt=["white","black","grey","red","orange","brown","gold","olive","yellow","lime","green","teal","cyan","blue","violet","magenta","pink"],xe=o=>{if(!B(o))return!1;try{JSON.parse(o)}catch{return!1}return!0},jt="v-hidden",ho=o=>{const{format:t,id:e,componentLabels:s}=o,r=x({tagName:"div",className:`color-form ${t}`});let n=["hex"];return t==="rgb"?n=["red","green","blue","alpha"]:t==="hsl"?n=["hue","saturation","lightness","alpha"]:t==="hwb"&&(n=["hue","whiteness","blackness","alpha"]),n.forEach(i=>{const[a]=t==="hex"?["#"]:te(i).split(""),l=`color_${t}_${i}_${e}`,c=s[`${i}Label`],h=x({tagName:"label"});m(h,"for",l),h.append(x({tagName:"span",ariaHidden:"true",innerText:`${a}:`}),x({tagName:"span",className:jt,innerText:c}));const b=x({tagName:"input",id:l,type:t==="hex"?"text":"number",value:i==="alpha"?"100":"0",className:`color-input ${i}`,autocomplete:"off",spellcheck:!1});let u="100",d="1";i!=="alpha"&&(t==="rgb"?(u="255",d="1"):i==="hue"&&(u="360",d="1")),I(b,{min:"0",max:u,step:d}),r.append(h,b)}),r},go=o=>{const{format:t,componentLabels:e}=o,{hueLabel:s,alphaLabel:r,lightnessLabel:n,saturationLabel:i,whitenessLabel:a,blacknessLabel:l}=e,c=t==="hsl"?360:100,h=t==="hsl"?100:360,b=100;let u=t==="hsl"?`${s} & ${n}`:`${n} & ${i}`;u=t==="hwb"?`${a} & ${l}`:u;const d=t==="hsl"?`${i}`:`${s}`,g=x({tagName:"div",className:`color-controls ${t}`}),p="color-pointer",f="color-slider";return[{i:1,c:p,l:u,min:0,max:c},{i:2,c:f,l:d,min:0,max:h},{i:3,c:f,l:r,min:0,max:b}].forEach($=>{const{i:P,c:H,l:S,min:C,max:R}=$,E=x({tagName:"div",className:"color-control",role:"presentation"});E.append(x({tagName:"div",className:`visual-control visual-control${P}`}));const N=x({tagName:"div",className:`${H} knob`,ariaLive:"polite",ariaLabel:S,role:"slider",tabIndex:0,ariaValueMin:`${C}`,ariaValueMax:`${R}`});E.append(N),g.append(E)}),g},Pe=(o,t,e)=>{const{input:s,format:r,componentLabels:n}=o,{defaultsLabel:i,presetsLabel:a}=n,l=e==="color-options",c=t instanceof Vt,h=l?a:i,b=c?t.colors:t,u=b.length,{lightSteps:d}=c?t:{lightSteps:null},g=d||[9,10].find(N=>u>=N*2&&!(u%N))||5,p=l&&u>g;let f=2;f=p&&u>g*2?3:f,f=p&&u>g*3?4:f,f=p&&u>g*4?5:f;const w=f-(u<=g*3?1:2),$=p&&u>w*g;let P=e;P+=$?" scrollable":"",P+=p?" multiline":"";const H=p?"1px":"0.25rem";let S=p?1.75:2;S=g>5&&p?1.5:S;const C=`${w*S}rem`,R=`calc(${f} * ${S}rem + ${f-1} * ${H})`,E=x({tagName:"ul",className:P,role:"listbox",ariaLabel:h});return $&&D(E,{"--grid-item-size":`${S}rem`,"--grid-fit":`${g}`,"--grid-gap":H,"--grid-height":C,"--grid-hover-height":R}),b.forEach(N=>{let[V,ct]=typeof N=="string"?N.trim().split(":"):[];N instanceof k&&(V=N.toHexString(),ct=V);const Ee=new k(N instanceof k?N:V,r).toString()===pt(s,"value"),Gt=x({tagName:"li",className:`color-option${Ee?" active":""}`,innerText:`${ct||V}`,tabIndex:0,role:"option",ariaSelected:Ee?"true":"false"});m(Gt,"data-value",`${V}`),l&&D(Gt,{backgroundColor:V}),E.append(Gt)}),E},uo=o=>{const{input:t,parent:e,format:s,id:r,componentLabels:n,colorKeywords:i,colorPresets:a}=o,l=pt(t,"value")||"#fff",{nonColors:c}=k,{toggleLabel:h,pickerLabel:b,formatLabel:u,hexLabel:d}=n,g=c.includes(l)?"#fff":l;o.color=new k(g,s);const p=s==="hex"?d:te(s),f=x({id:`picker-btn-${r}`,tagName:"button",type:"button",className:"picker-toggle btn-appearance",ariaExpanded:"false",ariaHasPopup:"true"});f.append(x({tagName:"span",className:jt,innerText:`${b}. ${u}: ${p}`}));const w=x({tagName:"div",className:"color-dropdown picker",role:"group",ariaLabelledBy:`picker-btn-${r}`}),$=go(o),P=ho(o);if(w.append($,P),t.before(f),e.append(w),i||a){const H=x({tagName:"div",className:"color-dropdown scrollable menu"});a&&H.append(Pe(o,a,"color-options")),i&&i.length&&H.append(Pe(o,i,"color-defaults"));const S=x({tagName:"button",type:"button",className:"menu-toggle btn-appearance",tabIndex:-1,ariaExpanded:"false",ariaHasPopup:"true"}),C=encodeURI("http://www.w3.org/2000/svg"),R=Nt(C,{tagName:"svg"});m(R,"xmlns",C),m(R,"viewBox","0 0 512 512"),m(R,Me,"true");const E=Nt(C,{tagName:"path"});m(E,"d","M98,158l157,156L411,158l27,27L255,368L71,185L98,158z"),m(E,"fill","#fff"),R.append(E),S.append(x({tagName:"span",className:jt,innerText:`${h}`}),R),e.append(S,H)}i&&c.includes(l)&&(o.value=l),m(t,W,"-1")},bo="2.0.0-alpha10",Y="color-picker",po=`[data-function="${Y}"]`,Se=`.${Y}`,fo={componentLabels:Bt,colorLabels:lt,format:"rgb",colorPresets:!1,colorKeywords:!1},{roundPart:y,nonColors:yt}=k,mo=o=>Ze(o,Y),vo=o=>new Ne(o),Ce=(o,t)=>{const e=t?Ct:Lt,{input:s,pickerToggle:r,menuToggle:n}=o;e(s,Oe,o.showPicker),e(r,St,o.togglePicker),n&&e(n,St,o.toggleMenu)},Le=(o,t)=>{const e=t?Ct:Lt,{input:s,colorMenu:r,parent:n}=o,i=F(s),a=no(i);e(o.controls,Ie,o.pointerDown),o.controlKnobs.forEach(l=>e(l,_t,o.handleKnobs)),e(a,Be,o.handleScroll),e(a,Ve,o.update),[s,...o.inputs].forEach(l=>e(l,Te,o.changeHandler)),r&&(e(r,St,o.menuClickHandler),e(r,_t,o.menuKeyHandler)),e(i,zt,o.pointerMove),e(i,Ke,o.pointerUp),e(n,De,o.handleFocusOut),e(i,Fe,o.handleDismiss)},Ae=o=>{Qe(o.input,new CustomEvent("colorpicker.change"))},He=o=>{o&&["bottom","top"].forEach(t=>O(o,t))},qt=(o,t)=>{const{colorPicker:e,colorMenu:s,menuToggle:r,pickerToggle:n,parent:i}=o,a=t===e,l=a?s:e,c=a?r:n,h=a?n:r;M(i,"open")||q(i,"open"),l&&(O(l,"show"),He(l)),q(t,"bottom"),so(t),q(t,"show"),a&&o.update(),o.isOpen||(Le(o,!0),o.updateDropdownPosition(),o.isOpen=!0,m(o.input,W,"0"),r&&m(r,W,"0")),m(h,xt,"true"),c&&m(c,xt,"false")};class Ne{static Color=k;static ColorPalette=Vt;static getInstance=mo;static init=vo;static selector=po;static roundPart=y;static setElementStyle=D;static setAttribute=m;static getBoundingClientRect=ft;static version=bo;static colorNames=lt;static colorPickerLabels=Bt;id;input;color;format="rgb";parent;dragElement;isOpen=!1;controlPositions;colorLabels=Qt(lt.map(t=>[t,t]));colorKeywords;colorPresets;componentLabels;pickerToggle;menuToggle;colorPicker;colorMenu;controls;inputs;controlKnobs;visuals;constructor(t,e){const s=z(t);if(typeof t>"u")throw new TypeError("ColorPicker target not specified.");if(B(t)&&!s)throw new TypeError(`ColorPicker target "${t}" cannot be found.`);this.input=s;const r=re(s,Se);if(!r)throw new TypeError("ColorPicker requires a specific markup to work.");this.parent=r,this.id=se(s,Y),this.dragElement=void 0,this.isOpen=!1,this.controlPositions={c1x:0,c1y:0,c2y:0,c3y:0},this.colorKeywords=!1,this.colorPresets=!1;const{format:n,componentLabels:i,colorLabels:a,colorKeywords:l,colorPresets:c}=oo(s,fo,e||{});let h=lt;Tt(a)&&a.length===17?h=a:B(a)&&a.split(",").length===17&&(h=a.split(",")),I(this.colorLabels,Qt(h.map((p,f)=>[lt[f],p])));const b=B(i)&&xe(i)?JSON.parse(i):i;if(this.componentLabels=I({...Bt},b),this.color=new k(s.value||"#fff",n),this.format=n,Tt(l)&&l.length?this.colorKeywords=l:B(l)&&l.length&&(this.colorKeywords=l.split(",").map(p=>p.trim())),Tt(c)&&c.length)this.colorPresets=c;else if(c&&xe(c)){const{hue:p,hueSteps:f,lightSteps:w,saturation:$}=JSON.parse(c);this.colorPresets=new Vt(p,f,w,$)}else B(c)&&(this.colorPresets=c.split(",").map(p=>p.trim()));uo(this);const[u,d]=rt("color-dropdown",r);this.pickerToggle=z(".picker-toggle",r),this.menuToggle=z(".menu-toggle",r),this.colorPicker=u,this.colorMenu=d,this.inputs=[...rt("color-input",r)];const[g]=rt("color-controls",r);this.controls=g,this.controlKnobs=[...rt("knob",g)],this.visuals=[...rt("visual-control",g)],this.update(),Ce(this,!0),st.set(s,Y,this)}get value(){return this.input.value}set value(t){this.input.value=t}get hasNonColor(){return this.colorKeywords instanceof Array&&this.colorKeywords.some(t=>yt.includes(t))}get hex(){return this.color.toHex(!0)}get hsv(){return this.color.toHsv()}get hsl(){return this.color.toHsl()}get hwb(){return this.color.toHwb()}get rgb(){return this.color.toRgb()}get brightness(){return this.color.brightness}get luminance(){return this.color.luminance}get isDark(){const{color:t,brightness:e}=this;return e<120&&t.a>.33}get isValid(){const t=this.input.value;return t!==""&&new k(t).isValid}get appearance(){const{colorLabels:t,hsl:e,hsv:s,format:r}=this,n=y(e.h*360),i=r==="hsl"?e.s:s.s,a=y(i*100),l=y(e.l*100),c=s.v*100;let h="black";if(l===100&&a===0)h=t.white;else if(l===0)h=t.black;else if(a===0)h=t.grey;else if(n<15||n>=345)h=t.red;else if(n>=15&&n<45)h=c>80&&a>80?t.orange:t.brown;else if(n>=45&&n<75){const b=n>46&&n<54&&c<80&&a>90,u=n>=54&&n<75&&c<80;h=b?t.gold:t.yellow,h=u?t.olive:h}else n>=75&&n<155?h=c<68?t.green:t.lime:n>=155&&n<175?h=t.teal:n>=175&&n<195?h=t.cyan:n>=195&&n<255?h=t.blue:n>=255&&n<270?h=t.violet:n>=270&&n<295?h=t.magenta:n>=295&&n<345&&(h=t.pink);return h}updateVisuals(){const{controlPositions:t,visuals:e}=this,[s,r,n]=e,{offsetHeight:i}=s,a=t.c2y/i,{r:l,g:c,b:h}=new k({h:a,s:1,l:.5}).toRgb(),b="linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)",u=1-t.c3y/i,d=y(u*100)/100,g=new k({h:a,s:1,l:.5,a:u}).toRgbString(),p=`linear-gradient(
2780
2
  rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
2781
3
  rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
2782
4
  rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
2783
- rgb(255,0,0) 100%)`;
2784
- setElementStyle(v1, {
2785
- background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
2786
- linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
2787
- ${whiteGrad}`,
2788
- });
2789
- setElementStyle(v2, { background: hueGradient });
2790
-
2791
- setElementStyle(v3, {
2792
- background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
2793
- });
2794
- }
2795
-
2796
- /**
2797
- * The `ColorPicker` *focusout* event listener when open.
2798
- * @param {FocusEvent} e
2799
- * @this {ColorPicker}
2800
- */
2801
- handleFocusOut({ relatedTarget }) {
2802
- if (relatedTarget && !this.parent.contains(relatedTarget)) {
2803
- this.hide(true);
2804
- }
2805
- }
2806
-
2807
- /**
2808
- * The `ColorPicker` *keyup* event listener when open.
2809
- * @param {KeyboardEvent} e
2810
- * @this {ColorPicker}
2811
- */
2812
- handleDismiss({ code }) {
2813
- const self = this;
2814
- if (self.isOpen && code === keyEscape) {
2815
- self.hide();
2816
- }
2817
- }
2818
-
2819
- /**
2820
- * The `ColorPicker` *scroll* event listener when open.
2821
- * @param {Event} e
2822
- * @this {ColorPicker}
2823
- */
2824
- handleScroll(e) {
2825
- const self = this;
2826
- const { activeElement } = getDocument(self.input);
2827
-
2828
- self.updateDropdownPosition();
2829
-
2830
- /* istanbul ignore next */
2831
- if (([pointermoveEvent, touchmoveEvent].includes(e.type) && self.dragElement)
2832
- || (activeElement && self.controlKnobs.includes(activeElement))) {
2833
- e.stopPropagation();
2834
- e.preventDefault();
2835
- }
2836
- }
2837
-
2838
- /**
2839
- * The `ColorPicker` keyboard event listener for menu navigation.
2840
- * @param {KeyboardEvent} e
2841
- * @this {ColorPicker}
2842
- */
2843
- menuKeyHandler(e) {
2844
- const { target, code } = e;
2845
- const { previousElementSibling, nextElementSibling, parentElement } = target;
2846
- const isColorOptionsMenu = parentElement && hasClass(parentElement, 'color-options');
2847
- const allSiblings = [...parentElement.children];
2848
- const columnsCount = isColorOptionsMenu
2849
- && getElementStyle(parentElement, 'grid-template-columns').split(' ').length;
2850
- const currentIndex = allSiblings.indexOf(target);
2851
- const previousElement = currentIndex > -1
2852
- && columnsCount && allSiblings[currentIndex - columnsCount];
2853
- const nextElement = currentIndex > -1
2854
- && columnsCount && allSiblings[currentIndex + columnsCount];
2855
-
2856
- if ([keyArrowDown, keyArrowUp, keySpace].includes(code)) {
2857
- // prevent scroll when navigating the menu via arrow keys / Space
2858
- e.preventDefault();
2859
- }
2860
- if (isColorOptionsMenu) {
2861
- if (previousElement && code === keyArrowUp) {
2862
- focus(previousElement);
2863
- } else if (nextElement && code === keyArrowDown) {
2864
- focus(nextElement);
2865
- } else if (previousElementSibling && code === keyArrowLeft) {
2866
- focus(previousElementSibling);
2867
- } else if (nextElementSibling && code === keyArrowRight) {
2868
- focus(nextElementSibling);
2869
- }
2870
- } else if (previousElementSibling && [keyArrowLeft, keyArrowUp].includes(code)) {
2871
- focus(previousElementSibling);
2872
- } else if (nextElementSibling && [keyArrowRight, keyArrowDown].includes(code)) {
2873
- focus(nextElementSibling);
2874
- }
2875
-
2876
- if ([keyEnter, keySpace].includes(code)) {
2877
- this.menuClickHandler({ target });
2878
- }
2879
- }
2880
-
2881
- /**
2882
- * The `ColorPicker` click event listener for the colour menu presets / defaults.
2883
- * @param {Event} e
2884
- * @this {ColorPicker}
2885
- */
2886
- menuClickHandler(e) {
2887
- const self = this;
2888
- const { target } = e;
2889
- const { colorMenu } = self;
2890
- const newOption = (getAttribute(target, 'data-value') || '').trim();
2891
- // invalidate for targets other than color options
2892
- if (!newOption.length) return;
2893
- const currentActive = querySelector('li.active', colorMenu);
2894
- let newColor = newOption;
2895
- newColor = nonColors.includes(newColor) ? 'white' : newColor;
2896
- newColor = newColor === 'transparent' ? 'rgba(0,0,0,0)' : newColor;
2897
-
2898
- const {
2899
- r, g, b, a,
2900
- } = new Color(newColor);
2901
-
2902
- ObjectAssign(self.color, {
2903
- r, g, b, a,
2904
- });
2905
-
2906
- self.update();
2907
-
2908
- /* istanbul ignore else */
2909
- if (currentActive !== target) {
2910
- /* istanbul ignore else */
2911
- if (currentActive) {
2912
- removeClass(currentActive, 'active');
2913
- removeAttribute(currentActive, ariaSelected);
2914
- }
2915
-
2916
- addClass(target, 'active');
2917
- setAttribute(target, ariaSelected, 'true');
2918
-
2919
- if (nonColors.includes(newOption)) {
2920
- self.value = newOption;
2921
- }
2922
- firePickerChange(self);
2923
- }
2924
- }
2925
-
2926
- /**
2927
- * The `ColorPicker` *touchstart* / *mousedown* events listener for control knobs.
2928
- * @param {PointerEvent} e
2929
- * @this {ColorPicker}
2930
- */
2931
- pointerDown(e) {
2932
- const self = this;
2933
- /** @type {*} */
2934
- const { target, pageX, pageY } = e;
2935
- const { colorMenu, visuals, controlKnobs } = self;
2936
- const [v1, v2, v3] = visuals;
2937
- const [c1, c2, c3] = controlKnobs;
2938
- /** @type {HTMLElement} */
2939
- const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
2940
- const visualRect = getBoundingClientRect(visual);
2941
- const html = getDocumentElement(v1);
2942
- const offsetX = pageX - html.scrollLeft - visualRect.left;
2943
- const offsetY = pageY - html.scrollTop - visualRect.top;
2944
-
2945
- /* istanbul ignore else */
2946
- if (target === v1 || target === c1) {
2947
- self.dragElement = visual;
2948
- self.changeControl1(offsetX, offsetY);
2949
- } else if (target === v2 || target === c2) {
2950
- self.dragElement = visual;
2951
- self.changeControl2(offsetY);
2952
- } else if (target === v3 || target === c3) {
2953
- self.dragElement = visual;
2954
- self.changeAlpha(offsetY);
2955
- }
2956
-
2957
- if (colorMenu) {
2958
- const currentActive = querySelector('li.active', colorMenu);
2959
- if (currentActive) {
2960
- removeClass(currentActive, 'active');
2961
- removeAttribute(currentActive, ariaSelected);
2962
- }
2963
- }
2964
- e.preventDefault();
2965
- }
2966
-
2967
- /**
2968
- * The `ColorPicker` *touchend* / *mouseup* events listener for control knobs.
2969
- * @param {PointerEvent} e
2970
- * @this {ColorPicker}
2971
- */
2972
- pointerUp({ target }) {
2973
- const self = this;
2974
- const { parent } = self;
2975
- const doc = getDocument(parent);
2976
- const currentOpen = querySelector(`${colorPickerParentSelector}.open`, doc) !== null;
2977
- const selection = doc.getSelection();
2978
-
2979
- if (!self.dragElement && !selection.toString().length
2980
- && !parent.contains(target)) {
2981
- self.hide(currentOpen);
2982
- }
2983
-
2984
- self.dragElement = null;
2985
- }
2986
-
2987
- /**
2988
- * The `ColorPicker` *touchmove* / *mousemove* events listener for control knobs.
2989
- * @param {PointerEvent} e
2990
- */
2991
- pointerMove(e) {
2992
- const self = this;
2993
- const { dragElement, visuals } = self;
2994
- const [v1, v2, v3] = visuals;
2995
- const { pageX, pageY } = e;
2996
-
2997
- if (!dragElement) return;
2998
-
2999
- const controlRect = getBoundingClientRect(dragElement);
3000
- const win = getDocumentElement(v1);
3001
- const offsetX = pageX - win.scrollLeft - controlRect.left;
3002
- const offsetY = pageY - win.scrollTop - controlRect.top;
3003
-
3004
- if (dragElement === v1) {
3005
- self.changeControl1(offsetX, offsetY);
3006
- }
3007
-
3008
- if (dragElement === v2) {
3009
- self.changeControl2(offsetY);
3010
- }
3011
-
3012
- if (dragElement === v3) {
3013
- self.changeAlpha(offsetY);
3014
- }
3015
- }
3016
-
3017
- /**
3018
- * The `ColorPicker` *keydown* event listener for control knobs.
3019
- * @param {KeyboardEvent} e
3020
- */
3021
- handleKnobs(e) {
3022
- const { target, code } = e;
3023
- const self = this;
3024
-
3025
- // only react to arrow buttons
3026
- if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
3027
- e.preventDefault();
3028
-
3029
- const { controlKnobs, visuals } = self;
3030
- const { offsetWidth, offsetHeight } = visuals[0];
3031
- const [c1, c2, c3] = controlKnobs;
3032
- const { activeElement } = getDocument(c1);
3033
- const currentKnob = controlKnobs.find((x) => x === activeElement);
3034
- const yRatio = offsetHeight / 360;
3035
-
3036
- /* istanbul ignore else */
3037
- if (currentKnob) {
3038
- let offsetX = 0;
3039
- let offsetY = 0;
3040
-
3041
- /* istanbul ignore else */
3042
- if (target === c1) {
3043
- const xRatio = offsetWidth / 100;
3044
-
3045
- /* istanbul ignore else */
3046
- if ([keyArrowLeft, keyArrowRight].includes(code)) {
3047
- self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
3048
- } else if ([keyArrowUp, keyArrowDown].includes(code)) {
3049
- self.controlPositions.c1y += code === keyArrowDown ? yRatio : -yRatio;
3050
- }
3051
-
3052
- offsetX = self.controlPositions.c1x;
3053
- offsetY = self.controlPositions.c1y;
3054
- self.changeControl1(offsetX, offsetY);
3055
- } else if (target === c2) {
3056
- self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
3057
- ? yRatio
3058
- : -yRatio;
3059
-
3060
- offsetY = self.controlPositions.c2y;
3061
- self.changeControl2(offsetY);
3062
- } else if (target === c3) {
3063
- self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
3064
- ? yRatio
3065
- : -yRatio;
3066
-
3067
- offsetY = self.controlPositions.c3y;
3068
- self.changeAlpha(offsetY);
3069
- }
3070
- self.handleScroll(e);
3071
- }
3072
- }
3073
-
3074
- /** The event listener of the colour form inputs. */
3075
- changeHandler() {
3076
- const self = this;
3077
- let colorSource;
3078
- const {
3079
- inputs, format, value: currentValue, input, controlPositions, visuals,
3080
- } = self;
3081
- /** @type {*} */
3082
- const { activeElement } = getDocument(input);
3083
- const { offsetHeight } = visuals[0];
3084
- const [i1,,, i4] = inputs;
3085
- const [v1, v2, v3, v4] = format === 'rgb'
3086
- ? inputs.map((i) => parseFloat(i.value) / (i === i4 ? 100 : 1))
3087
- : inputs.map((i) => parseFloat(i.value) / (i !== i1 ? 100 : 360));
3088
- const isNonColorValue = self.hasNonColor && nonColors.includes(currentValue);
3089
- const alpha = i4 ? v4 : (1 - controlPositions.c3y / offsetHeight);
3090
-
3091
- /* istanbul ignore else */
3092
- if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
3093
- if (activeElement === input) {
3094
- if (isNonColorValue) {
3095
- colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
3096
- } else {
3097
- colorSource = currentValue;
3098
- }
3099
- } else if (format === 'hex') {
3100
- colorSource = i1.value;
3101
- } else if (format === 'hsl') {
3102
- colorSource = {
3103
- h: v1, s: v2, l: v3, a: alpha,
3104
- };
3105
- } else if (format === 'hwb') {
3106
- colorSource = {
3107
- h: v1, w: v2, b: v3, a: alpha,
3108
- };
3109
- } else {
3110
- colorSource = {
3111
- r: v1, g: v2, b: v3, a: alpha,
3112
- };
3113
- }
3114
-
3115
- const {
3116
- r, g, b, a,
3117
- } = new Color(colorSource);
3118
-
3119
- ObjectAssign(self.color, {
3120
- r, g, b, a,
3121
- });
3122
- self.setControlPositions();
3123
- self.updateAppearance();
3124
- self.updateInputs();
3125
- self.updateControls();
3126
- self.updateVisuals();
3127
-
3128
- // set non-color keyword
3129
- if (activeElement === input && isNonColorValue) {
3130
- self.value = currentValue;
3131
- }
3132
- }
3133
- }
3134
-
3135
- /**
3136
- * Updates `ColorPicker` first control:
3137
- * * `lightness` and `saturation` for HEX/RGB;
3138
- * * `lightness` and `hue` for HSL.
3139
- *
3140
- * @param {number} X the X component of the offset
3141
- * @param {number} Y the Y component of the offset
3142
- */
3143
- changeControl1(X, Y) {
3144
- const self = this;
3145
- let [offsetX, offsetY] = [0, 0];
3146
- const { controlPositions, visuals } = self;
3147
- const { offsetHeight, offsetWidth } = visuals[0];
3148
-
3149
- if (X > offsetWidth) offsetX = offsetWidth;
3150
- else if (X >= 0) offsetX = X;
3151
-
3152
- if (Y > offsetHeight) offsetY = offsetHeight;
3153
- else if (Y >= 0) offsetY = Y;
3154
-
3155
- const hue = controlPositions.c2y / offsetHeight;
3156
-
3157
- const saturation = offsetX / offsetWidth;
3158
-
3159
- const lightness = 1 - offsetY / offsetHeight;
3160
- const alpha = 1 - controlPositions.c3y / offsetHeight;
3161
-
3162
- // new color
3163
- const {
3164
- r, g, b, a,
3165
- } = new Color({
3166
- h: hue, s: saturation, v: lightness, a: alpha,
3167
- });
3168
-
3169
- ObjectAssign(self.color, {
3170
- r, g, b, a,
3171
- });
3172
-
3173
- // new positions
3174
- self.controlPositions.c1x = offsetX;
3175
- self.controlPositions.c1y = offsetY;
3176
-
3177
- // update color picker
3178
- self.updateAppearance();
3179
- self.updateInputs();
3180
- self.updateControls();
3181
- self.updateVisuals();
3182
- }
3183
-
3184
- /**
3185
- * Updates `ColorPicker` second control:
3186
- * * `hue` for HEX/RGB/HWB;
3187
- * * `saturation` for HSL.
3188
- *
3189
- * @param {number} Y the Y offset
3190
- */
3191
- changeControl2(Y) {
3192
- const self = this;
3193
- const {
3194
- controlPositions, visuals,
3195
- } = self;
3196
- const { offsetHeight, offsetWidth } = visuals[0];
3197
-
3198
- let offsetY = 0;
3199
-
3200
- if (Y > offsetHeight) offsetY = offsetHeight;
3201
- else if (Y >= 0) offsetY = Y;
3202
-
3203
- const hue = offsetY / offsetHeight;
3204
- const saturation = controlPositions.c1x / offsetWidth;
3205
- const lightness = 1 - controlPositions.c1y / offsetHeight;
3206
- const alpha = 1 - controlPositions.c3y / offsetHeight;
3207
-
3208
- // new color
3209
- const {
3210
- r, g, b, a,
3211
- } = new Color({
3212
- h: hue, s: saturation, v: lightness, a: alpha,
3213
- });
3214
-
3215
- ObjectAssign(self.color, {
3216
- r, g, b, a,
3217
- });
3218
-
3219
- // new position
3220
- self.controlPositions.c2y = offsetY;
3221
- // update color picker
3222
- self.updateAppearance();
3223
- self.updateInputs();
3224
- self.updateControls();
3225
- self.updateVisuals();
3226
- }
3227
-
3228
- /**
3229
- * Updates `ColorPicker` last control,
3230
- * the `alpha` channel.
3231
- *
3232
- * @param {number} Y
3233
- */
3234
- changeAlpha(Y) {
3235
- const self = this;
3236
- const { visuals } = self;
3237
- const { offsetHeight } = visuals[0];
3238
- let offsetY = 0;
3239
-
3240
- if (Y > offsetHeight) offsetY = offsetHeight;
3241
- else if (Y >= 0) offsetY = Y;
3242
-
3243
- // update color alpha
3244
- const alpha = 1 - offsetY / offsetHeight;
3245
- self.color.setAlpha(alpha);
3246
- // update position
3247
- self.controlPositions.c3y = offsetY;
3248
- // update color picker
3249
- self.updateAppearance();
3250
- self.updateInputs();
3251
- self.updateControls();
3252
- self.updateVisuals();
3253
- }
3254
-
3255
- /**
3256
- * Updates `ColorPicker` control positions on:
3257
- * * initialization
3258
- * * window resize
3259
- */
3260
- update() {
3261
- const self = this;
3262
- self.updateDropdownPosition();
3263
- self.updateAppearance();
3264
- self.setControlPositions();
3265
- self.updateInputs(true);
3266
- self.updateControls();
3267
- self.updateVisuals();
3268
- }
3269
-
3270
- /** Updates the open dropdown position on *scroll* event. */
3271
- updateDropdownPosition() {
3272
- const self = this;
3273
- const { input, colorPicker, colorMenu } = self;
3274
- const elRect = getBoundingClientRect(input);
3275
- const { top, bottom } = elRect;
3276
- const { offsetHeight: elHeight } = input;
3277
- const windowHeight = getDocumentElement(input).clientHeight;
3278
- const isPicker = hasClass(colorPicker, 'show');
3279
- const dropdown = isPicker ? colorPicker : colorMenu;
3280
- if (!dropdown) return;
3281
- const { offsetHeight: dropHeight } = dropdown;
3282
- const distanceBottom = windowHeight - bottom;
3283
- const distanceTop = top;
3284
- const bottomExceed = top + dropHeight + elHeight > windowHeight; // show
3285
- const topExceed = top - dropHeight < 0; // show-top
3286
-
3287
- if ((hasClass(dropdown, 'bottom') || !topExceed) && distanceBottom < distanceTop && bottomExceed) {
3288
- removeClass(dropdown, 'bottom');
3289
- addClass(dropdown, 'top');
3290
- } else {
3291
- removeClass(dropdown, 'top');
3292
- addClass(dropdown, 'bottom');
3293
- }
3294
- }
3295
-
3296
- /** Updates control knobs' positions. */
3297
- setControlPositions() {
3298
- const self = this;
3299
- const {
3300
- visuals, color, hsv,
3301
- } = self;
3302
- const { offsetHeight, offsetWidth } = visuals[0];
3303
- const alpha = color.a;
3304
- const hue = hsv.h;
3305
-
3306
- const saturation = hsv.s;
3307
- const lightness = hsv.v;
3308
-
3309
- self.controlPositions.c1x = saturation * offsetWidth;
3310
- self.controlPositions.c1y = (1 - lightness) * offsetHeight;
3311
- self.controlPositions.c2y = hue * offsetHeight;
3312
- self.controlPositions.c3y = (1 - alpha) * offsetHeight;
3313
- }
3314
-
3315
- /** Update the visual appearance label and control knob labels. */
3316
- updateAppearance() {
3317
- const self = this;
3318
- const {
3319
- componentLabels, color, parent,
3320
- hsv, hex, format, controlKnobs,
3321
- } = self;
3322
- const {
3323
- appearanceLabel, hexLabel, valueLabel,
3324
- } = componentLabels;
3325
- let { r, g, b } = color.toRgb();
3326
- const [knob1, knob2, knob3] = controlKnobs;
3327
- const hue = roundPart(hsv.h * 360);
3328
- const alpha = color.a;
3329
- const saturation = roundPart(hsv.s * 100);
3330
- const lightness = roundPart(hsv.v * 100);
3331
- const colorName = self.appearance;
3332
-
3333
- let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
3334
-
3335
- if (format === 'hwb') {
3336
- const { hwb } = self;
3337
- const whiteness = roundPart(hwb.w * 100);
3338
- const blackness = roundPart(hwb.b * 100);
3339
- colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
3340
- setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
3341
- setAttribute(knob1, ariaValueNow, `${whiteness}`);
3342
- setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3343
- setAttribute(knob2, ariaValueText, `${hue}%`);
3344
- setAttribute(knob2, ariaValueNow, `${hue}`);
3345
- } else {
3346
- [r, g, b] = [r, g, b].map(roundPart);
3347
- colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
3348
- colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
3349
-
3350
- setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
3351
- setAttribute(knob1, ariaValueNow, `${lightness}`);
3352
- setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3353
- setAttribute(knob2, ariaValueText, `${hue}°`);
3354
- setAttribute(knob2, ariaValueNow, `${hue}`);
3355
- }
3356
-
3357
- const alphaValue = roundPart(alpha * 100);
3358
- setAttribute(knob3, ariaValueText, `${alphaValue}%`);
3359
- setAttribute(knob3, ariaValueNow, `${alphaValue}`);
3360
-
3361
- // update the input backgroundColor
3362
- const newColor = color.toString();
3363
- setElementStyle(self.input, { backgroundColor: newColor });
3364
-
3365
- // toggle dark/light classes will also style the placeholder
3366
- // dark sets color white, light sets color black
3367
- // isDark ? '#000' : '#fff'
3368
- if (!self.isDark) {
3369
- if (hasClass(parent, 'txt-dark')) removeClass(parent, 'txt-dark');
3370
- if (!hasClass(parent, 'txt-light')) addClass(parent, 'txt-light');
3371
- } else {
3372
- if (hasClass(parent, 'txt-light')) removeClass(parent, 'txt-light');
3373
- if (!hasClass(parent, 'txt-dark')) addClass(parent, 'txt-dark');
3374
- }
3375
- }
3376
-
3377
- /** Updates the control knobs actual positions. */
3378
- updateControls() {
3379
- const { controlKnobs, controlPositions } = this;
3380
- let {
3381
- c1x, c1y, c2y, c3y,
3382
- } = controlPositions;
3383
- const [control1, control2, control3] = controlKnobs;
3384
- // round control positions
3385
- [c1x, c1y, c2y, c3y] = [c1x, c1y, c2y, c3y].map(roundPart);
3386
-
3387
- setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
3388
- setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
3389
- setElementStyle(control3, { transform: `translate3d(0,${c3y - 4}px,0)` });
3390
- }
3391
-
3392
- /**
3393
- * Updates all color form inputs.
3394
- * @param {boolean=} isPrevented when `true`, the component original event is prevented
3395
- */
3396
- updateInputs(isPrevented) {
3397
- const self = this;
3398
- const {
3399
- value: oldColor, format, inputs, color, hsl,
3400
- } = self;
3401
- const [i1, i2, i3, i4] = inputs;
3402
- const alpha = roundPart(color.a * 100);
3403
- const hue = roundPart(hsl.h * 360);
3404
- let newColor;
3405
-
3406
- /* istanbul ignore else */
3407
- if (format === 'hex') {
3408
- newColor = self.color.toHexString(true);
3409
- i1.value = self.hex;
3410
- } else if (format === 'hsl') {
3411
- const lightness = roundPart(hsl.l * 100);
3412
- const saturation = roundPart(hsl.s * 100);
3413
- newColor = self.color.toHslString();
3414
- i1.value = `${hue}`;
3415
- i2.value = `${saturation}`;
3416
- i3.value = `${lightness}`;
3417
- i4.value = `${alpha}`;
3418
- } else if (format === 'hwb') {
3419
- const { w, b } = self.hwb;
3420
- const whiteness = roundPart(w * 100);
3421
- const blackness = roundPart(b * 100);
3422
-
3423
- newColor = self.color.toHwbString();
3424
- i1.value = `${hue}`;
3425
- i2.value = `${whiteness}`;
3426
- i3.value = `${blackness}`;
3427
- i4.value = `${alpha}`;
3428
- } else if (format === 'rgb') {
3429
- let { r, g, b } = self.rgb;
3430
- [r, g, b] = [r, g, b].map(roundPart);
3431
-
3432
- newColor = self.color.toRgbString();
3433
- i1.value = `${r}`;
3434
- i2.value = `${g}`;
3435
- i3.value = `${b}`;
3436
- i4.value = `${alpha}`;
3437
- }
3438
-
3439
- // update the color value
3440
- self.value = `${newColor}`;
3441
-
3442
- // don't trigger the custom event unless it's really changed
3443
- if (!isPrevented && newColor !== oldColor) {
3444
- firePickerChange(self);
3445
- }
3446
- }
3447
-
3448
- /**
3449
- * Toggle the `ColorPicker` dropdown visibility.
3450
- * @param {Event=} e
3451
- * @this {ColorPicker}
3452
- */
3453
- togglePicker(e) {
3454
- if (e) e.preventDefault();
3455
- const self = this;
3456
- const { colorPicker } = self;
3457
-
3458
- if (self.isOpen && hasClass(colorPicker, 'show')) {
3459
- self.hide(true);
3460
- } else {
3461
- showDropdown(self, colorPicker);
3462
- }
3463
- }
3464
-
3465
- /** Shows the `ColorPicker` dropdown. */
3466
- showPicker() {
3467
- const self = this;
3468
- const { colorPicker } = self;
3469
-
3470
- if (!['top', 'bottom'].some((c) => hasClass(colorPicker, c))) {
3471
- showDropdown(self, colorPicker);
3472
- }
3473
- }
3474
-
3475
- /**
3476
- * Toggles the visibility of the `ColorPicker` presets menu.
3477
- * @param {Event=} e
3478
- * @this {ColorPicker}
3479
- */
3480
- toggleMenu(e) {
3481
- if (e) e.preventDefault();
3482
- const self = this;
3483
- const { colorMenu } = self;
3484
-
3485
- if (self.isOpen && hasClass(colorMenu, 'show')) {
3486
- self.hide(true);
3487
- } else {
3488
- showDropdown(self, colorMenu);
3489
- }
3490
- }
3491
-
3492
- /**
3493
- * Hides the currently open `ColorPicker` dropdown.
3494
- * @param {boolean=} focusPrevented
3495
- */
3496
- hide(focusPrevented) {
3497
- const self = this;
3498
- if (self.isOpen) {
3499
- const {
3500
- pickerToggle, menuToggle, colorPicker, colorMenu, parent, input,
3501
- } = self;
3502
- const openPicker = hasClass(colorPicker, 'show');
3503
- const openDropdown = openPicker ? colorPicker : colorMenu;
3504
- const relatedBtn = openPicker ? pickerToggle : menuToggle;
3505
- const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
3506
-
3507
- self.value = self.color.toString(true);
3508
-
3509
- /* istanbul ignore else */
3510
- if (openDropdown) {
3511
- removeClass(openDropdown, 'show');
3512
- setAttribute(relatedBtn, ariaExpanded, 'false');
3513
- setTimeout(() => {
3514
- removePosition(openDropdown);
3515
- /* istanbul ignore else */
3516
- if (!querySelector('.show', parent)) {
3517
- removeClass(parent, 'open');
3518
- toggleEventsOnShown(self);
3519
- self.isOpen = false;
3520
- }
3521
- }, animationDuration);
3522
- }
3523
-
3524
- if (!focusPrevented) {
3525
- focus(pickerToggle);
3526
- }
3527
- setAttribute(input, tabIndex, '-1');
3528
- if (relatedBtn === menuToggle) {
3529
- setAttribute(menuToggle, tabIndex, '-1');
3530
- }
3531
- }
3532
- }
3533
-
3534
- /** Removes `ColorPicker` from target `<input>`. */
3535
- dispose() {
3536
- const self = this;
3537
- const { input, parent } = self;
3538
- self.hide(true);
3539
- toggleEvents(self);
3540
- [...parent.children].forEach((el) => {
3541
- if (el !== input) el.remove();
3542
- });
3543
-
3544
- removeAttribute(input, tabIndex);
3545
- setElementStyle(input, { backgroundColor: '' });
3546
-
3547
- ['txt-light', 'txt-dark'].forEach((c) => removeClass(parent, c));
3548
- Data.remove(input, colorPickerString);
3549
- }
3550
- }
3551
-
3552
- ObjectAssign(ColorPicker, {
3553
- Color,
3554
- ColorPalette,
3555
- Version,
3556
- getInstance: getColorPickerInstance,
3557
- init: initColorPicker,
3558
- selector: colorPickerSelector,
3559
- // utils important for render
3560
- roundPart,
3561
- setElementStyle,
3562
- setAttribute,
3563
- getBoundingClientRect,
3564
- });
3565
-
3566
- /**
3567
- * A single import is required to add the `CP` namespace to `src` sources.
3568
- * @typedef {import("../../types/index")}
3569
- */
3570
-
3571
- return ColorPicker;
3572
-
3573
- }));
5
+ rgb(255,0,0) 100%)`;D(s,{background:`linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${d}) 100%),
6
+ linear-gradient(to right, rgba(255,255,255,${d}) 0%, ${g} 100%),
7
+ ${b}`}),D(r,{background:p}),D(n,{background:`linear-gradient(rgba(${l},${c},${h},1) 0%,rgba(${l},${c},${h},0) 100%)`})}handleFocusOut=({relatedTarget:t})=>{t&&!this.parent.contains(t)&&this.hide(!0)};handleDismiss=({code:t})=>{this.isOpen&&t===Ge&&this.hide()};handleScroll=t=>{const{activeElement:e}=F(this.input);this.updateDropdownPosition(),([zt,je].includes(t.type)&&this.dragElement||e&&this.controlKnobs.includes(e))&&(t.stopPropagation(),t.preventDefault())};menuKeyHandler=t=>{const{target:e,code:s}=t,{previousElementSibling:r,nextElementSibling:n,parentElement:i}=e,a=i&&M(i,"color-options"),l=i?[...i.children]:[],c=a&&Et(i,"grid-template-columns").split(" ").length,h=l.indexOf(e),b=h>-1&&c&&l[h-c],u=h>-1&&c&&l[h+c];[j,et,Wt].includes(s)&&t.preventDefault(),a?b&&s===et?_(b):u&&s===j?_(u):r&&s===ht?_(r):n&&s===U&&_(n):r&&[ht,et].includes(s)?_(r):n&&[U,j].includes(s)&&_(n),[qe,Wt].includes(s)&&this.menuClickHandler(t)};menuClickHandler=t=>{const{target:e}=t,{colorMenu:s}=this,r=(pt(e,"data-value")||"").trim();if(!r.length)return;const n=z("li.active",s);let i=r;i=yt.includes(i)?"white":i,i=i==="transparent"?"rgba(0,0,0,0)":i;const{r:a,g:l,b:c,a:h}=new k(i);I(this.color,{r:a,g:l,b:c,a:h}),this.update(),n!==e&&(n&&(O(n,"active"),At(n,Pt)),q(e,"active"),m(e,Pt,"true"),yt.includes(r)&&(this.value=r),Ae(this))};pointerDown=t=>{if(t.button!==0)return;const{target:e,pageX:s,pageY:r}=t,{colorMenu:n,visuals:i,controlKnobs:a}=this,[l,c,h]=i,[b,u,d]=a,g=a.includes(e)?e.previousElementSibling:e,p=ft(g),f=Mt(l),w=s-f.scrollLeft-p.left,$=r-f.scrollTop-p.top;if(e===l||e===b?(this.dragElement=g,this.changeControl1(w,$)):e===c||e===u?(this.dragElement=g,this.changeControl2($)):(e===h||e===d)&&(this.dragElement=g,this.changeAlpha($)),n){const P=z("li.active",n);P&&(O(P,"active"),At(P,Pt))}t.preventDefault()};pointerUp=({target:t})=>{const{parent:e}=this,s=F(e),r=z(`${Se}.open`,s)!==null,n=s.getSelection();!this.dragElement&&(!n||!n.toString().length)&&!e.contains(t)&&this.hide(r),this.dragElement=void 0};pointerMove=t=>{const{dragElement:e,visuals:s}=this,[r,n,i]=s,{pageX:a,pageY:l}=t;if(!e)return;const c=ft(e),h=Mt(r),b=a-h.scrollLeft-c.left,u=l-h.scrollTop-c.top;e===r&&this.changeControl1(b,u),e===n&&this.changeControl2(u),e===i&&this.changeAlpha(u)};handleKnobs=t=>{const{target:e,code:s}=t;if(![et,j,ht,U].includes(s))return;t.preventDefault();const{controlKnobs:r,visuals:n}=this,{offsetWidth:i,offsetHeight:a}=n[0],[l,c,h]=r,{activeElement:b}=F(l),u=r.find(g=>g===b),d=a/360;if(u){let g=0,p=0;if(e===l){const f=i/100;[ht,U].includes(s)?this.controlPositions.c1x+=s===U?f:-f:[et,j].includes(s)&&(this.controlPositions.c1y+=s===j?d:-d),g=this.controlPositions.c1x,p=this.controlPositions.c1y,this.changeControl1(g,p)}else e===c?(this.controlPositions.c2y+=[j,U].includes(s)?d:-d,p=this.controlPositions.c2y,this.changeControl2(p)):e===h&&(this.controlPositions.c3y+=[j,U].includes(s)?d:-d,p=this.controlPositions.c3y,this.changeAlpha(p));this.handleScroll(t)}};changeHandler=()=>{let t;const{inputs:e,format:s,value:r,input:n,controlPositions:i,visuals:a}=this,{activeElement:l}=F(n),{offsetHeight:c}=a[0],[h,,,b]=e,[u,d,g,p]=s==="rgb"?e.map($=>parseFloat($.value)/($===b?100:1)):e.map($=>parseFloat($.value)/($!==h?100:360)),f=this.hasNonColor&&yt.includes(r),w=b?p:1-i.c3y/c;if(l===n||l&&e.includes(l)){l===n?f?t=r==="transparent"?"rgba(0,0,0,0)":"rgb(0,0,0)":t=r:s==="hex"?t=h.value:s==="hsl"?t={h:u,s:d,l:g,a:w}:s==="hwb"?t={h:u,w:d,b:g,a:w}:t={r:u,g:d,b:g,a:w};const{r:$,g:P,b:H,a:S}=new k(t);I(this.color,{r:$,g:P,b:H,a:S}),this.setControlPositions(),this.updateAppearance(),this.updateInputs(),this.updateControls(),this.updateVisuals(),l===n&&f&&(this.value=r)}};changeControl1(t,e){let[s,r]=[0,0];const{controlPositions:n,visuals:i}=this,{offsetHeight:a,offsetWidth:l}=i[0];t>l?s=l:t>=0&&(s=t),e>a?r=a:e>=0&&(r=e);const c=n.c2y/a,h=s/l,b=1-r/a,u=1-n.c3y/a,{r:d,g,b:p,a:f}=new k({h:c,s:h,v:b,a:u});I(this.color,{r:d,g,b:p,a:f}),this.controlPositions.c1x=s,this.controlPositions.c1y=r,this.updateAppearance(),this.updateInputs(),this.updateControls(),this.updateVisuals()}changeControl2(t){const{controlPositions:e,visuals:s}=this,{offsetHeight:r,offsetWidth:n}=s[0];let i=0;t>r?i=r:t>=0&&(i=t);const a=i/r,l=e.c1x/n,c=1-e.c1y/r,h=1-e.c3y/r,{r:b,g:u,b:d,a:g}=new k({h:a,s:l,v:c,a:h});I(this.color,{r:b,g:u,b:d,a:g}),this.controlPositions.c2y=i,this.updateAppearance(),this.updateInputs(),this.updateControls(),this.updateVisuals()}changeAlpha(t){const{visuals:e}=this,{offsetHeight:s}=e[0];let r=0;t>s?r=s:t>=0&&(r=t);const n=1-r/s;this.color.setAlpha(n),this.controlPositions.c3y=r,this.updateAppearance(),this.updateInputs(),this.updateControls(),this.updateVisuals()}update=()=>{this.updateDropdownPosition(),this.updateAppearance(),this.setControlPositions(),this.updateInputs(!0),this.updateControls(),this.updateVisuals()};updateDropdownPosition(){const{input:t,colorPicker:e,colorMenu:s}=this,r=ft(t),{top:n,bottom:i}=r,{offsetHeight:a}=t,l=Mt(t).clientHeight,h=M(e,"show")?e:s;if(!h)return;const{offsetHeight:b}=h,u=l-i,d=n,g=n+b+a>l,p=n-b<0;(M(h,"bottom")||!p)&&u<d&&g?(O(h,"bottom"),q(h,"top")):(O(h,"top"),q(h,"bottom"))}setControlPositions(){const{visuals:t,color:e,hsv:s}=this,{offsetHeight:r,offsetWidth:n}=t[0],i=e.a,a=s.h,l=s.s,c=s.v;this.controlPositions.c1x=l*n,this.controlPositions.c1y=(1-c)*r,this.controlPositions.c2y=a*r,this.controlPositions.c3y=(1-i)*r}updateAppearance(){const{componentLabels:t,color:e,parent:s,hsv:r,hex:n,format:i,controlKnobs:a}=this,{appearanceLabel:l,hexLabel:c,valueLabel:h}=t;let{r:b,g:u,b:d}=e.toRgb();const[g,p,f]=a,w=y(r.h*360),$=e.a,P=y(r.s*100),H=y(r.v*100),S=this.appearance;let C=`${c} ${n.split("").join(" ")}`;if(i==="hwb"){const{hwb:N}=this,V=y(N.w*100),ct=y(N.b*100);C=`HWB: ${w}°, ${V}%, ${ct}%`,m(g,tt,`${V}% & ${ct}%`),m(g,Q,`${V}`),m(p,Ut,`${h}: ${C}. ${l}: ${S}.`),m(p,tt,`${w}%`),m(p,Q,`${w}`)}else[b,u,d]=[b,u,d].map(y),C=i==="hsl"?`HSL: ${w}°, ${P}%, ${H}%`:C,C=i==="rgb"?`RGB: ${b}, ${u}, ${d}`:C,m(g,tt,`${H}% & ${P}%`),m(g,Q,`${H}`),m(p,Ut,`${h}: ${C}. ${l}: ${S}.`),m(p,tt,`${w}°`),m(p,Q,`${w}`);const R=y($*100);m(f,tt,`${R}%`),m(f,Q,`${R}`);const E=e.toString();D(this.input,{backgroundColor:E}),this.isDark?(M(s,"txt-light")&&O(s,"txt-light"),M(s,"txt-dark")||q(s,"txt-dark")):(M(s,"txt-dark")&&O(s,"txt-dark"),M(s,"txt-light")||q(s,"txt-light"))}updateControls(){const{controlKnobs:t,controlPositions:e}=this;let{c1x:s,c1y:r,c2y:n,c3y:i}=e;const[a,l,c]=t;[s,r,n,i]=[s,r,n,i].map(y),D(a,{transform:`translate3d(${s-4}px,${r-4}px,0)`}),D(l,{transform:`translate3d(0,${n-4}px,0)`}),D(c,{transform:`translate3d(0,${i-4}px,0)`})}updateInputs(t){const{value:e,format:s,inputs:r,color:n,hsl:i}=this,[a,l,c,h]=r,b=y(n.a*100),u=y(i.h*360);let d=n.toString();if(s==="hex")d=this.color.toHexString(!0),a.value=this.hex;else if(s==="hsl"){const g=y(i.l*100),p=y(i.s*100);d=this.color.toHslString(),a.value=`${u}`,l.value=`${p}`,c.value=`${g}`,h.value=`${b}`}else if(s==="hwb"){const{w:g,b:p}=this.hwb,f=y(g*100),w=y(p*100);d=this.color.toHwbString(),a.value=`${u}`,l.value=`${f}`,c.value=`${w}`,h.value=`${b}`}else if(s==="rgb"){let{r:g,g:p,b:f}=this.rgb;[g,p,f]=[g,p,f].map(y),d=this.color.toRgbString(),a.value=`${g}`,l.value=`${p}`,c.value=`${f}`,h.value=`${b}`}this.value=d,!t&&d!==e&&Ae(this)}togglePicker=t=>{t&&t.preventDefault();const{colorPicker:e}=this;this.isOpen&&M(e,"show")?this.hide(!0):qt(this,e)};showPicker=()=>{const{colorPicker:t}=this;["top","bottom"].some(e=>M(t,e))||qt(this,t)};toggleMenu=t=>{t&&t.preventDefault();const{colorMenu:e}=this;this.isOpen&&M(e,"show")?this.hide(!0):qt(this,e)};hide(t){if(this.isOpen){const{pickerToggle:e,menuToggle:s,colorPicker:r,colorMenu:n,parent:i,input:a}=this,l=M(r,"show"),c=l?r:n,h=l?e:s,b=c&&to(c);this.value=this.color.toString(!0),c&&(O(c,"show"),m(h,xt,"false"),setTimeout(()=>{He(c),z(".show",i)||(O(i,"open"),Le(this),this.isOpen=!1)},b)),t||_(e),m(a,W,"-1"),h===s&&m(s,W,"-1")}}dispose(){const{input:t,parent:e}=this;this.hide(!0),Ce(this),[...e.children].forEach(s=>{s!==t&&s.remove()}),At(t,W),D(t,{backgroundColor:""}),["txt-light","txt-dark"].forEach(s=>O(e,s)),st.remove(t,Y)}}return Ne}();
8
+ //# sourceMappingURL=color-picker.js.map