@thednp/color-picker 0.0.2-alpha5 → 1.0.1

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 (37) hide show
  1. package/README.md +21 -19
  2. package/dist/css/color-picker.css +82 -46
  3. package/dist/css/color-picker.min.css +2 -2
  4. package/dist/css/color-picker.rtl.css +82 -46
  5. package/dist/css/color-picker.rtl.min.css +2 -2
  6. package/dist/js/color-esm.js +2 -5
  7. package/dist/js/color-esm.min.js +1 -1
  8. package/dist/js/color-palette-esm.js +2 -5
  9. package/dist/js/color-palette-esm.min.js +1 -1
  10. package/dist/js/color-palette.js +2 -5
  11. package/dist/js/color-palette.min.js +1 -1
  12. package/dist/js/color-picker-element-esm.js +184 -189
  13. package/dist/js/color-picker-element-esm.min.js +2 -2
  14. package/dist/js/color-picker-element.js +184 -189
  15. package/dist/js/color-picker-element.min.js +2 -2
  16. package/dist/js/color-picker-esm.js +77 -134
  17. package/dist/js/color-picker-esm.min.js +2 -2
  18. package/dist/js/color-picker.js +77 -134
  19. package/dist/js/color-picker.min.js +2 -2
  20. package/dist/js/color.js +2 -5
  21. package/dist/js/color.min.js +1 -1
  22. package/package.json +20 -19
  23. package/src/js/color-picker-element.js +45 -37
  24. package/src/js/color-picker.js +46 -60
  25. package/src/js/color.js +1 -4
  26. package/src/js/index.js +5 -0
  27. package/src/js/util/getColorMenu.js +7 -8
  28. package/src/js/util/setMarkup.js +4 -7
  29. package/src/js/util/toggleCEAttr.js +70 -0
  30. package/src/js/util/version.js +0 -1
  31. package/src/js/version.js +0 -1
  32. package/src/scss/_variables.scss +7 -0
  33. package/src/scss/color-picker.scss +86 -45
  34. package/types/cp.d.ts +31 -17
  35. package/types/index.d.ts +0 -4
  36. package/types/source/source.ts +0 -1
  37. package/types/source/types.d.ts +8 -6
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * ColorPickerElement v0.0.2alpha5 (http://thednp.github.io/color-picker)
2
+ * ColorPickerElement v1.0.1 (http://thednp.github.io/color-picker)
3
3
  * Copyright 2022 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/color-picker/blob/master/LICENSE)
5
5
  */
@@ -15,25 +15,6 @@ function getDocument(node) {
15
15
  return window.document;
16
16
  }
17
17
 
18
- /**
19
- * A global array of possible `ParentNode`.
20
- */
21
- const parentNodes = [Document, Element, HTMLElement];
22
-
23
- /**
24
- * Shortcut for `HTMLElement.getElementsByTagName` method. Some `Node` elements
25
- * like `ShadowRoot` do not support `getElementsByTagName`.
26
- *
27
- * @param {string} selector the tag name
28
- * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
29
- * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
30
- */
31
- function getElementsByTagName(selector, parent) {
32
- const lookUp = parent && parentNodes
33
- .some((x) => parent instanceof x) ? parent : getDocument();
34
- return lookUp.getElementsByTagName(selector);
35
- }
36
-
37
18
  /**
38
19
  * Shortcut for `Object.assign()` static method.
39
20
  * @param {Record<string, any>} obj a target object
@@ -769,7 +750,7 @@ function inputToRGB(input) {
769
750
  format = 'hwb';
770
751
  }
771
752
  if (isValidCSSUnit(color.a)) {
772
- a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
753
+ a = color.a;
773
754
  a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
774
755
  }
775
756
  }
@@ -780,9 +761,6 @@ function inputToRGB(input) {
780
761
  return {
781
762
  ok,
782
763
  format,
783
- // r: Math.min(255, Math.max(rgb.r, 0)),
784
- // g: Math.min(255, Math.max(rgb.g, 0)),
785
- // b: Math.min(255, Math.max(rgb.b, 0)),
786
764
  r: rgb.r,
787
765
  g: rgb.g,
788
766
  b: rgb.b,
@@ -1241,27 +1219,26 @@ const EventRegistry = {};
1241
1219
  /**
1242
1220
  * The global event listener.
1243
1221
  *
1244
- * @this {Element | HTMLElement | Window | Document}
1245
- * @param {Event} e
1246
- * @returns {void}
1222
+ * @type {EventListener}
1223
+ * @this {EventTarget}
1247
1224
  */
1248
1225
  function globalListener(e) {
1249
1226
  const that = this;
1250
1227
  const { type } = e;
1251
- const oneEvMap = EventRegistry[type] ? [...EventRegistry[type]] : [];
1252
1228
 
1253
- oneEvMap.forEach((elementsMap) => {
1229
+ [...EventRegistry[type]].forEach((elementsMap) => {
1254
1230
  const [element, listenersMap] = elementsMap;
1255
- [...listenersMap].forEach((listenerMap) => {
1256
- if (element === that) {
1231
+ /* istanbul ignore else */
1232
+ if (element === that) {
1233
+ [...listenersMap].forEach((listenerMap) => {
1257
1234
  const [listener, options] = listenerMap;
1258
1235
  listener.apply(element, [e]);
1259
1236
 
1260
1237
  if (options && options.once) {
1261
1238
  removeListener(element, type, listener, options);
1262
1239
  }
1263
- }
1264
- });
1240
+ });
1241
+ }
1265
1242
  });
1266
1243
  }
1267
1244
 
@@ -1269,10 +1246,7 @@ function globalListener(e) {
1269
1246
  * Register a new listener with its options and attach the `globalListener`
1270
1247
  * to the target if this is the first listener.
1271
1248
  *
1272
- * @param {Element | HTMLElement | Window | Document} element
1273
- * @param {string} eventType
1274
- * @param {EventListenerObject['handleEvent']} listener
1275
- * @param {AddEventListenerOptions=} options
1249
+ * @type {Listener.ListenerAction<EventTarget>}
1276
1250
  */
1277
1251
  const addListener = (element, eventType, listener, options) => {
1278
1252
  // get element listeners first
@@ -1290,9 +1264,7 @@ const addListener = (element, eventType, listener, options) => {
1290
1264
  const { size } = oneElementMap;
1291
1265
 
1292
1266
  // register listener with its options
1293
- if (oneElementMap) {
1294
- oneElementMap.set(listener, options);
1295
- }
1267
+ oneElementMap.set(listener, options);
1296
1268
 
1297
1269
  // add listener last
1298
1270
  if (!size) {
@@ -1304,10 +1276,7 @@ const addListener = (element, eventType, listener, options) => {
1304
1276
  * Remove a listener from registry and detach the `globalListener`
1305
1277
  * if no listeners are found in the registry.
1306
1278
  *
1307
- * @param {Element | HTMLElement | Window | Document} element
1308
- * @param {string} eventType
1309
- * @param {EventListenerObject['handleEvent']} listener
1310
- * @param {AddEventListenerOptions=} options
1279
+ * @type {Listener.ListenerAction<EventTarget>}
1311
1280
  */
1312
1281
  const removeListener = (element, eventType, listener, options) => {
1313
1282
  // get listener first
@@ -1326,6 +1295,7 @@ const removeListener = (element, eventType, listener, options) => {
1326
1295
  if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType];
1327
1296
 
1328
1297
  // remove listener last
1298
+ /* istanbul ignore else */
1329
1299
  if (!oneElementMap || !oneElementMap.size) {
1330
1300
  element.removeEventListener(eventType, globalListener, eventOptions);
1331
1301
  }
@@ -1427,12 +1397,6 @@ const keydownEvent = 'keydown';
1427
1397
  */
1428
1398
  const changeEvent = 'change';
1429
1399
 
1430
- /**
1431
- * A global namespace for `touchstart` event.
1432
- * @type {string}
1433
- */
1434
- const touchstartEvent = 'touchstart';
1435
-
1436
1400
  /**
1437
1401
  * A global namespace for `touchmove` event.
1438
1402
  * @type {string}
@@ -1440,28 +1404,22 @@ const touchstartEvent = 'touchstart';
1440
1404
  const touchmoveEvent = 'touchmove';
1441
1405
 
1442
1406
  /**
1443
- * A global namespace for `touchend` event.
1407
+ * A global namespace for `pointerdown` event.
1444
1408
  * @type {string}
1445
1409
  */
1446
- const touchendEvent = 'touchend';
1410
+ const pointerdownEvent = 'pointerdown';
1447
1411
 
1448
1412
  /**
1449
- * A global namespace for `mousedown` event.
1413
+ * A global namespace for `pointermove` event.
1450
1414
  * @type {string}
1451
1415
  */
1452
- const mousedownEvent = 'mousedown';
1416
+ const pointermoveEvent = 'pointermove';
1453
1417
 
1454
1418
  /**
1455
- * A global namespace for `mousemove` event.
1419
+ * A global namespace for `pointerup` event.
1456
1420
  * @type {string}
1457
1421
  */
1458
- const mousemoveEvent = 'mousemove';
1459
-
1460
- /**
1461
- * A global namespace for `mouseup` event.
1462
- * @type {string}
1463
- */
1464
- const mouseupEvent = 'mouseup';
1422
+ const pointerupEvent = 'pointerup';
1465
1423
 
1466
1424
  /**
1467
1425
  * A global namespace for `scroll` event.
@@ -1497,27 +1455,6 @@ function getDocumentElement(node) {
1497
1455
  return getDocument(node).documentElement;
1498
1456
  }
1499
1457
 
1500
- /**
1501
- * Returns the `Window` object of a target node.
1502
- * @see https://github.com/floating-ui/floating-ui
1503
- *
1504
- * @param {(Node | HTMLElement | Element | Window)=} node target node
1505
- * @returns {globalThis}
1506
- */
1507
- function getWindow(node) {
1508
- if (node == null) {
1509
- return window;
1510
- }
1511
-
1512
- if (!(node instanceof Window)) {
1513
- const { ownerDocument } = node;
1514
- return ownerDocument ? ownerDocument.defaultView || window : window;
1515
- }
1516
-
1517
- // @ts-ignore
1518
- return node;
1519
- }
1520
-
1521
1458
  let elementUID = 0;
1522
1459
  let elementMapUID = 0;
1523
1460
  const elementIDMap = new Map();
@@ -1617,6 +1554,11 @@ function getElementTransitionDuration(element) {
1617
1554
  return !Number.isNaN(duration) ? duration : 0;
1618
1555
  }
1619
1556
 
1557
+ /**
1558
+ * A global array of possible `ParentNode`.
1559
+ */
1560
+ const parentNodes = [Document, Element, HTMLElement];
1561
+
1620
1562
  /**
1621
1563
  * A global array with `Element` | `HTMLElement`.
1622
1564
  */
@@ -2260,17 +2202,16 @@ function getColorMenu(self, colorsSource, menuClass) {
2260
2202
  const isOptionsMenu = menuClass === 'color-options';
2261
2203
  const isPalette = colorsSource instanceof ColorPalette;
2262
2204
  const menuLabel = isOptionsMenu ? presetsLabel : defaultsLabel;
2263
- let colorsArray = isPalette ? colorsSource.colors : colorsSource;
2264
- colorsArray = colorsArray instanceof Array ? colorsArray : [];
2205
+ const colorsArray = isPalette ? colorsSource.colors : colorsSource;
2265
2206
  const colorsCount = colorsArray.length;
2266
2207
  const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
2267
- const fit = lightSteps || [9, 10].find((x) => colorsCount > x * 2 && !(colorsCount % x)) || 5;
2208
+ const fit = lightSteps || [9, 10].find((x) => colorsCount >= x * 2 && !(colorsCount % x)) || 5;
2268
2209
  const isMultiLine = isOptionsMenu && colorsCount > fit;
2269
2210
  let rowCountHover = 2;
2270
- rowCountHover = isMultiLine && colorsCount >= fit * 2 ? 3 : rowCountHover;
2271
- rowCountHover = colorsCount >= fit * 3 ? 4 : rowCountHover;
2272
- rowCountHover = colorsCount >= fit * 4 ? 5 : rowCountHover;
2273
- const rowCount = rowCountHover - (colorsCount < fit * 3 ? 1 : 2);
2211
+ rowCountHover = isMultiLine && colorsCount > fit * 2 ? 3 : rowCountHover;
2212
+ rowCountHover = isMultiLine && colorsCount > fit * 3 ? 4 : rowCountHover;
2213
+ rowCountHover = isMultiLine && colorsCount > fit * 4 ? 5 : rowCountHover;
2214
+ const rowCount = rowCountHover - (colorsCount <= fit * 3 ? 1 : 2);
2274
2215
  const isScrollable = isMultiLine && colorsCount > rowCount * fit;
2275
2216
  let finalClass = menuClass;
2276
2217
  finalClass += isScrollable ? ' scrollable' : '';
@@ -2278,7 +2219,7 @@ function getColorMenu(self, colorsSource, menuClass) {
2278
2219
  const gap = isMultiLine ? '1px' : '0.25rem';
2279
2220
  let optionSize = isMultiLine ? 1.75 : 2;
2280
2221
  optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
2281
- const menuHeight = `${(rowCount || 1) * optionSize}rem`;
2222
+ const menuHeight = `${rowCount * optionSize}rem`;
2282
2223
  const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
2283
2224
  /** @type {HTMLUListElement} */
2284
2225
  // @ts-ignore -- <UL> is an `HTMLElement`
@@ -2385,16 +2326,14 @@ function setMarkup(self) {
2385
2326
  });
2386
2327
 
2387
2328
  // color presets
2388
- if ((colorPresets instanceof Array && colorPresets.length)
2389
- || (colorPresets instanceof ColorPalette && colorPresets.colors)) {
2390
- const presetsMenu = getColorMenu(self, colorPresets, 'color-options');
2391
- presetsDropdown.append(presetsMenu);
2329
+ if (colorPresets) {
2330
+ presetsDropdown.append(getColorMenu(self, colorPresets, 'color-options'));
2392
2331
  }
2393
2332
 
2394
2333
  // explicit defaults [reset, initial, inherit, transparent, currentColor]
2334
+ // also custom defaults [default: #069, complementary: #930]
2395
2335
  if (colorKeywords && colorKeywords.length) {
2396
- const keywordsMenu = getColorMenu(self, colorKeywords, 'color-defaults');
2397
- presetsDropdown.append(keywordsMenu);
2336
+ presetsDropdown.append(getColorMenu(self, colorKeywords, 'color-defaults'));
2398
2337
  }
2399
2338
 
2400
2339
  const presetsBtn = createElement({
@@ -2431,9 +2370,7 @@ function setMarkup(self) {
2431
2370
  setAttribute(input, tabIndex, '-1');
2432
2371
  }
2433
2372
 
2434
- var version = "0.0.2alpha5";
2435
-
2436
- // @ts-ignore
2373
+ var version = "1.0.1";
2437
2374
 
2438
2375
  const Version = version;
2439
2376
 
@@ -2453,7 +2390,7 @@ const colorPickerDefaults = {
2453
2390
  // ColorPicker Static Methods
2454
2391
  // ==========================
2455
2392
 
2456
- /** @type {CP.GetInstance<ColorPicker>} */
2393
+ /** @type {CP.GetInstance<ColorPicker, HTMLInputElement>} */
2457
2394
  const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
2458
2395
 
2459
2396
  /** @type {CP.InitCallback<ColorPicker>} */
@@ -2488,17 +2425,12 @@ function toggleEventsOnShown(self, action) {
2488
2425
  const fn = action ? addListener : removeListener;
2489
2426
  const { input, colorMenu, parent } = self;
2490
2427
  const doc = getDocument(input);
2491
- const win = getWindow(input);
2492
- const pointerEvents = `on${touchstartEvent}` in doc
2493
- ? { down: touchstartEvent, move: touchmoveEvent, up: touchendEvent }
2494
- : { down: mousedownEvent, move: mousemoveEvent, up: mouseupEvent };
2428
+ const win = doc.defaultView;
2495
2429
 
2496
- fn(self.controls, pointerEvents.down, self.pointerDown);
2430
+ fn(self.controls, pointerdownEvent, self.pointerDown);
2497
2431
  self.controlKnobs.forEach((x) => fn(x, keydownEvent, self.handleKnobs));
2498
2432
 
2499
- // @ts-ignore -- this is `Window`
2500
2433
  fn(win, scrollEvent, self.handleScroll);
2501
- // @ts-ignore -- this is `Window`
2502
2434
  fn(win, resizeEvent, self.update);
2503
2435
 
2504
2436
  [input, ...self.inputs].forEach((x) => fn(x, changeEvent, self.changeHandler));
@@ -2508,8 +2440,8 @@ function toggleEventsOnShown(self, action) {
2508
2440
  fn(colorMenu, keydownEvent, self.menuKeyHandler);
2509
2441
  }
2510
2442
 
2511
- fn(doc, pointerEvents.move, self.pointerMove);
2512
- fn(doc, pointerEvents.up, self.pointerUp);
2443
+ fn(doc, pointermoveEvent, self.pointerMove);
2444
+ fn(doc, pointerupEvent, self.pointerUp);
2513
2445
  fn(parent, focusoutEvent, self.handleFocusOut);
2514
2446
  fn(doc, keyupEvent, self.handleDismiss);
2515
2447
  }
@@ -2528,6 +2460,7 @@ function firePickerChange(self) {
2528
2460
  * @returns {void}
2529
2461
  */
2530
2462
  function removePosition(element) {
2463
+ /* istanbul ignore else */
2531
2464
  if (element) {
2532
2465
  ['bottom', 'top'].forEach((x) => removeClass(element, x));
2533
2466
  }
@@ -2591,7 +2524,6 @@ class ColorPicker {
2591
2524
  constructor(target, config) {
2592
2525
  const self = this;
2593
2526
  /** @type {HTMLInputElement} */
2594
- // @ts-ignore
2595
2527
  const input = querySelector(target);
2596
2528
 
2597
2529
  // invalidate
@@ -2602,7 +2534,6 @@ class ColorPicker {
2602
2534
  if (!parent) throw new TypeError('ColorPicker requires a specific markup to work.');
2603
2535
 
2604
2536
  /** @type {HTMLElement} */
2605
- // @ts-ignore
2606
2537
  self.parent = parent;
2607
2538
 
2608
2539
  /** @type {number} */
@@ -2630,6 +2561,7 @@ class ColorPicker {
2630
2561
  } = normalizeOptions(this.isCE ? parent : input, colorPickerDefaults, config || {});
2631
2562
 
2632
2563
  let translatedColorLabels = colorNames;
2564
+ /* istanbul ignore else */
2633
2565
  if (colorLabels instanceof Array && colorLabels.length === 17) {
2634
2566
  translatedColorLabels = colorLabels;
2635
2567
  } else if (colorLabels && colorLabels.split(',').length === 17) {
@@ -2646,7 +2578,7 @@ class ColorPicker {
2646
2578
  ? JSON.parse(componentLabels) : componentLabels;
2647
2579
 
2648
2580
  /** @type {Record<string, string>} */
2649
- self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
2581
+ self.componentLabels = ObjectAssign({ ...colorPickerLabels }, tempComponentLabels);
2650
2582
 
2651
2583
  /** @type {Color} */
2652
2584
  self.color = new Color(input.value || '#fff', format);
@@ -2655,14 +2587,14 @@ class ColorPicker {
2655
2587
  self.format = format;
2656
2588
 
2657
2589
  // set colour defaults
2658
- if (colorKeywords instanceof Array) {
2590
+ if (colorKeywords instanceof Array && colorKeywords.length) {
2659
2591
  self.colorKeywords = colorKeywords;
2660
2592
  } else if (typeof colorKeywords === 'string' && colorKeywords.length) {
2661
2593
  self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
2662
2594
  }
2663
2595
 
2664
2596
  // set colour presets
2665
- if (colorPresets instanceof Array) {
2597
+ if (colorPresets instanceof Array && colorPresets.length) {
2666
2598
  self.colorPresets = colorPresets;
2667
2599
  } else if (typeof colorPresets === 'string' && colorPresets.length) {
2668
2600
  if (isValidJSON(colorPresets)) {
@@ -2695,26 +2627,20 @@ class ColorPicker {
2695
2627
  const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
2696
2628
  // set main elements
2697
2629
  /** @type {HTMLElement} */
2698
- // @ts-ignore
2699
2630
  self.pickerToggle = querySelector('.picker-toggle', parent);
2700
2631
  /** @type {HTMLElement} */
2701
- // @ts-ignore
2702
2632
  self.menuToggle = querySelector('.menu-toggle', parent);
2703
2633
  /** @type {HTMLElement} */
2704
- // @ts-ignore
2705
2634
  self.colorPicker = colorPicker;
2706
2635
  /** @type {HTMLElement} */
2707
- // @ts-ignore
2708
2636
  self.colorMenu = colorMenu;
2709
2637
  /** @type {HTMLInputElement[]} */
2710
- // @ts-ignore
2711
2638
  self.inputs = [...getElementsByClassName('color-input', parent)];
2712
2639
  const [controls] = getElementsByClassName('color-controls', parent);
2713
2640
  self.controls = controls;
2714
2641
  /** @type {(HTMLElement | Element)[]} */
2715
2642
  self.controlKnobs = [...getElementsByClassName('knob', controls)];
2716
2643
  /** @type {(HTMLElement)[]} */
2717
- // @ts-ignore
2718
2644
  self.visuals = [...getElementsByClassName('visual-control', controls)];
2719
2645
 
2720
2646
  // update colour picker controls, inputs and visuals
@@ -2793,6 +2719,7 @@ class ColorPicker {
2793
2719
  let colorName;
2794
2720
 
2795
2721
  // determine color appearance
2722
+ /* istanbul ignore else */
2796
2723
  if (lightness === 100 && saturation === 0) {
2797
2724
  colorName = colorLabels.white;
2798
2725
  } else if (lightness === 0) {
@@ -2866,7 +2793,6 @@ class ColorPicker {
2866
2793
  * @this {ColorPicker}
2867
2794
  */
2868
2795
  handleFocusOut({ relatedTarget }) {
2869
- // @ts-ignore
2870
2796
  if (relatedTarget && !this.parent.contains(relatedTarget)) {
2871
2797
  this.hide(true);
2872
2798
  }
@@ -2893,13 +2819,14 @@ class ColorPicker {
2893
2819
  const self = this;
2894
2820
  const { activeElement } = getDocument(self.input);
2895
2821
 
2896
- if ((e.type === touchmoveEvent && self.dragElement)
2822
+ self.updateDropdownPosition();
2823
+
2824
+ /* istanbul ignore next */
2825
+ if (([pointermoveEvent, touchmoveEvent].includes(e.type) && self.dragElement)
2897
2826
  || (activeElement && self.controlKnobs.includes(activeElement))) {
2898
2827
  e.stopPropagation();
2899
2828
  e.preventDefault();
2900
2829
  }
2901
-
2902
- self.updateDropdownPosition();
2903
2830
  }
2904
2831
 
2905
2832
  /**
@@ -2909,7 +2836,6 @@ class ColorPicker {
2909
2836
  */
2910
2837
  menuKeyHandler(e) {
2911
2838
  const { target, code } = e;
2912
- // @ts-ignore
2913
2839
  const { previousElementSibling, nextElementSibling, parentElement } = target;
2914
2840
  const isColorOptionsMenu = parentElement && hasClass(parentElement, 'color-options');
2915
2841
  const allSiblings = [...parentElement.children];
@@ -2948,20 +2874,20 @@ class ColorPicker {
2948
2874
 
2949
2875
  /**
2950
2876
  * The `ColorPicker` click event listener for the colour menu presets / defaults.
2951
- * @param {Partial<Event>} e
2877
+ * @param {Event} e
2952
2878
  * @this {ColorPicker}
2953
2879
  */
2954
2880
  menuClickHandler(e) {
2955
2881
  const self = this;
2956
- /** @type {*} */
2957
2882
  const { target } = e;
2958
2883
  const { colorMenu } = self;
2959
2884
  const newOption = (getAttribute(target, 'data-value') || '').trim();
2960
2885
  // invalidate for targets other than color options
2961
2886
  if (!newOption.length) return;
2962
2887
  const currentActive = querySelector('li.active', colorMenu);
2963
- let newColor = nonColors.includes(newOption) ? 'white' : newOption;
2964
- newColor = newOption === 'transparent' ? 'rgba(0,0,0,0)' : newOption;
2888
+ let newColor = newOption;
2889
+ newColor = nonColors.includes(newColor) ? 'white' : newColor;
2890
+ newColor = newColor === 'transparent' ? 'rgba(0,0,0,0)' : newColor;
2965
2891
 
2966
2892
  const {
2967
2893
  r, g, b, a,
@@ -2973,7 +2899,9 @@ class ColorPicker {
2973
2899
 
2974
2900
  self.update();
2975
2901
 
2902
+ /* istanbul ignore else */
2976
2903
  if (currentActive !== target) {
2904
+ /* istanbul ignore else */
2977
2905
  if (currentActive) {
2978
2906
  removeClass(currentActive, 'active');
2979
2907
  removeAttribute(currentActive, ariaSelected);
@@ -2991,15 +2919,13 @@ class ColorPicker {
2991
2919
 
2992
2920
  /**
2993
2921
  * The `ColorPicker` *touchstart* / *mousedown* events listener for control knobs.
2994
- * @param {TouchEvent} e
2922
+ * @param {PointerEvent} e
2995
2923
  * @this {ColorPicker}
2996
2924
  */
2997
2925
  pointerDown(e) {
2998
2926
  const self = this;
2999
2927
  /** @type {*} */
3000
- const {
3001
- type, target, touches, pageX, pageY,
3002
- } = e;
2928
+ const { target, pageX, pageY } = e;
3003
2929
  const { colorMenu, visuals, controlKnobs } = self;
3004
2930
  const [v1, v2, v3] = visuals;
3005
2931
  const [c1, c2, c3] = controlKnobs;
@@ -3007,11 +2933,10 @@ class ColorPicker {
3007
2933
  const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
3008
2934
  const visualRect = getBoundingClientRect(visual);
3009
2935
  const html = getDocumentElement(v1);
3010
- const X = type === 'touchstart' ? touches[0].pageX : pageX;
3011
- const Y = type === 'touchstart' ? touches[0].pageY : pageY;
3012
- const offsetX = X - html.scrollLeft - visualRect.left;
3013
- const offsetY = Y - html.scrollTop - visualRect.top;
2936
+ const offsetX = pageX - html.scrollLeft - visualRect.left;
2937
+ const offsetY = pageY - html.scrollTop - visualRect.top;
3014
2938
 
2939
+ /* istanbul ignore else */
3015
2940
  if (target === v1 || target === c1) {
3016
2941
  self.dragElement = visual;
3017
2942
  self.changeControl1(offsetX, offsetY);
@@ -3035,7 +2960,7 @@ class ColorPicker {
3035
2960
 
3036
2961
  /**
3037
2962
  * The `ColorPicker` *touchend* / *mouseup* events listener for control knobs.
3038
- * @param {TouchEvent} e
2963
+ * @param {PointerEvent} e
3039
2964
  * @this {ColorPicker}
3040
2965
  */
3041
2966
  pointerUp({ target }) {
@@ -3044,9 +2969,8 @@ class ColorPicker {
3044
2969
  const doc = getDocument(parent);
3045
2970
  const currentOpen = querySelector(`${colorPickerParentSelector}.open`, doc) !== null;
3046
2971
  const selection = doc.getSelection();
3047
- // @ts-ignore
2972
+
3048
2973
  if (!self.dragElement && !selection.toString().length
3049
- // @ts-ignore
3050
2974
  && !parent.contains(target)) {
3051
2975
  self.hide(currentOpen);
3052
2976
  }
@@ -3056,25 +2980,20 @@ class ColorPicker {
3056
2980
 
3057
2981
  /**
3058
2982
  * The `ColorPicker` *touchmove* / *mousemove* events listener for control knobs.
3059
- * @param {TouchEvent} e
2983
+ * @param {PointerEvent} e
3060
2984
  */
3061
2985
  pointerMove(e) {
3062
2986
  const self = this;
3063
2987
  const { dragElement, visuals } = self;
3064
2988
  const [v1, v2, v3] = visuals;
3065
- const {
3066
- // @ts-ignore
3067
- type, touches, pageX, pageY,
3068
- } = e;
2989
+ const { pageX, pageY } = e;
3069
2990
 
3070
2991
  if (!dragElement) return;
3071
2992
 
3072
2993
  const controlRect = getBoundingClientRect(dragElement);
3073
2994
  const win = getDocumentElement(v1);
3074
- const X = type === touchmoveEvent ? touches[0].pageX : pageX;
3075
- const Y = type === touchmoveEvent ? touches[0].pageY : pageY;
3076
- const offsetX = X - win.scrollLeft - controlRect.left;
3077
- const offsetY = Y - win.scrollTop - controlRect.top;
2995
+ const offsetX = pageX - win.scrollLeft - controlRect.left;
2996
+ const offsetY = pageY - win.scrollTop - controlRect.top;
3078
2997
 
3079
2998
  if (dragElement === v1) {
3080
2999
  self.changeControl1(offsetX, offsetY);
@@ -3108,13 +3027,16 @@ class ColorPicker {
3108
3027
  const currentKnob = controlKnobs.find((x) => x === activeElement);
3109
3028
  const yRatio = offsetHeight / 360;
3110
3029
 
3030
+ /* istanbul ignore else */
3111
3031
  if (currentKnob) {
3112
3032
  let offsetX = 0;
3113
3033
  let offsetY = 0;
3114
3034
 
3035
+ /* istanbul ignore else */
3115
3036
  if (target === c1) {
3116
3037
  const xRatio = offsetWidth / 100;
3117
3038
 
3039
+ /* istanbul ignore else */
3118
3040
  if ([keyArrowLeft, keyArrowRight].includes(code)) {
3119
3041
  self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
3120
3042
  } else if ([keyArrowUp, keyArrowDown].includes(code)) {
@@ -3160,6 +3082,7 @@ class ColorPicker {
3160
3082
  const isNonColorValue = self.hasNonColor && nonColors.includes(currentValue);
3161
3083
  const alpha = i4 ? v4 : (1 - controlPositions.c3y / offsetHeight);
3162
3084
 
3085
+ /* istanbul ignore else */
3163
3086
  if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
3164
3087
  if (activeElement === input) {
3165
3088
  if (isNonColorValue) {
@@ -3474,6 +3397,7 @@ class ColorPicker {
3474
3397
  const hue = roundPart(hsl.h * 360);
3475
3398
  let newColor;
3476
3399
 
3400
+ /* istanbul ignore else */
3477
3401
  if (format === 'hex') {
3478
3402
  newColor = self.color.toHexString(true);
3479
3403
  i1.value = self.hex;
@@ -3574,15 +3498,15 @@ class ColorPicker {
3574
3498
  const relatedBtn = openPicker ? pickerToggle : menuToggle;
3575
3499
  const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
3576
3500
 
3577
- // if (!self.isValid) {
3578
3501
  self.value = self.color.toString(true);
3579
- // }
3580
3502
 
3503
+ /* istanbul ignore else */
3581
3504
  if (openDropdown) {
3582
3505
  removeClass(openDropdown, 'show');
3583
3506
  setAttribute(relatedBtn, ariaExpanded, 'false');
3584
3507
  setTimeout(() => {
3585
3508
  removePosition(openDropdown);
3509
+ /* istanbul ignore else */
3586
3510
  if (!querySelector('.show', parent)) {
3587
3511
  removeClass(parent, 'open');
3588
3512
  toggleEventsOnShown(self);
@@ -3595,7 +3519,7 @@ class ColorPicker {
3595
3519
  focus(pickerToggle);
3596
3520
  }
3597
3521
  setAttribute(input, tabIndex, '-1');
3598
- if (menuToggle) {
3522
+ if (relatedBtn === menuToggle) {
3599
3523
  setAttribute(menuToggle, tabIndex, '-1');
3600
3524
  }
3601
3525
  }
@@ -3633,14 +3557,76 @@ ObjectAssign(ColorPicker, {
3633
3557
  getBoundingClientRect,
3634
3558
  });
3635
3559
 
3560
+ /**
3561
+ * A small utility to toggle `ColorPickerElement` attributes
3562
+ * when `connectedCallback` or `disconnectedCallback` methods
3563
+ * are called and helps the instance keep its value and settings instact.
3564
+ *
3565
+ * @param {CP.ColorPickerElement} self ColorPickerElement instance
3566
+ * @param {Function=} callback when `true`, attributes are added
3567
+ *
3568
+ * @example
3569
+ * const attributes = [
3570
+ * // essentials
3571
+ * 'value', 'format',
3572
+ * // presets menus
3573
+ * 'color-presets', 'color-keywords',
3574
+ * // labels
3575
+ * 'color-labels', 'component-labels',
3576
+ * ];
3577
+ */
3578
+ function toggleCEAttr(self, callback) {
3579
+ if (callback) {
3580
+ const { input, colorPicker } = self;
3581
+
3582
+ const {
3583
+ value, format, colorPresets, colorKeywords, componentLabels, colorLabels,
3584
+ } = colorPicker;
3585
+
3586
+ const { id, placeholder } = input;
3587
+
3588
+ setAttribute(self, 'data-id', id);
3589
+ setAttribute(self, 'data-value', value);
3590
+ setAttribute(self, 'data-format', format);
3591
+ setAttribute(self, 'data-placeholder', placeholder);
3592
+
3593
+ if (ObjectKeys(colorPickerLabels).some((l) => colorPickerLabels[l] !== componentLabels[l])) {
3594
+ setAttribute(self, 'data-component-labels', JSON.stringify(componentLabels));
3595
+ }
3596
+ if (!colorNames.every((c) => c === colorLabels[c])) {
3597
+ setAttribute(self, 'data-color-labels', colorNames.map((n) => colorLabels[n]).join(','));
3598
+ }
3599
+ if (colorPresets instanceof ColorPalette) {
3600
+ const { hue, hueSteps, lightSteps } = colorPresets;
3601
+ setAttribute(self, 'data-color-presets', JSON.stringify({ hue, hueSteps, lightSteps }));
3602
+ }
3603
+ if (Array.isArray(colorPresets) && colorPresets.length) {
3604
+ setAttribute(self, 'data-color-presets', colorPresets.join(','));
3605
+ }
3606
+ if (colorKeywords) {
3607
+ setAttribute(self, 'data-color-keywords', colorKeywords.join(','));
3608
+ }
3609
+ setTimeout(callback, 0);
3610
+ } else {
3611
+ // keep id
3612
+ // removeAttribute(self, 'data-id');
3613
+ removeAttribute(self, 'data-value');
3614
+ removeAttribute(self, 'data-format');
3615
+ removeAttribute(self, 'data-placeholder');
3616
+ removeAttribute(self, 'data-component-labels');
3617
+ removeAttribute(self, 'data-color-labels');
3618
+ removeAttribute(self, 'data-color-presets');
3619
+ removeAttribute(self, 'data-color-keywords');
3620
+ }
3621
+ }
3622
+
3636
3623
  let CPID = 0;
3637
3624
 
3638
3625
  /**
3639
3626
  * `ColorPickerElement` Web Component.
3640
3627
  * @example
3641
3628
  * <label for="UNIQUE_ID">Label</label>
3642
- * <color-picker>
3643
- * <input id="UNIQUE_ID" value="red" format="hex" class="color-preview btn-appearance">
3629
+ * <color-picker data-id="UNIQUE_ID" data-value="red" data-format="hex">
3644
3630
  * </color-picker>
3645
3631
  * // or
3646
3632
  * <label for="UNIQUE_ID">Label</label>
@@ -3659,61 +3645,70 @@ class ColorPickerElement extends HTMLElement {
3659
3645
  get value() { return this.input && this.input.value; }
3660
3646
 
3661
3647
  connectedCallback() {
3662
- if (this.input) return;
3648
+ const self = this;
3649
+ if (self.input) return;
3663
3650
 
3664
- let [input] = getElementsByTagName('input', this);
3665
- const value = (input && getAttribute(input, 'value')) || getAttribute(this, 'data-value') || '#fff';
3666
- const format = (input && getAttribute(input, 'format')) || getAttribute(this, 'data-format') || 'rgb';
3667
- let id = (input && getAttribute(input, 'id')) || getAttribute(this, 'data-id');
3651
+ let id = getAttribute(self, 'data-id');
3652
+ const value = getAttribute(self, 'data-value') || '#fff';
3653
+ const format = getAttribute(self, 'data-format') || 'rgb';
3654
+ const placeholder = getAttribute(self, 'data-placeholder') || '';
3668
3655
 
3669
3656
  if (!id) {
3670
3657
  id = `color-picker-${format}-${CPID}`;
3671
3658
  CPID += 1;
3672
3659
  }
3673
3660
 
3674
- if (!input) {
3675
- input = createElement({
3676
- tagName: 'input',
3677
- type: 'text',
3678
- className: 'color-preview btn-appearance',
3679
- });
3661
+ const input = createElement({
3662
+ tagName: 'input',
3663
+ type: 'text',
3664
+ className: 'color-preview btn-appearance',
3665
+ });
3666
+
3667
+ setAttribute(input, 'id', id);
3668
+ setAttribute(input, 'name', id);
3669
+ setAttribute(input, 'autocomplete', 'off');
3670
+ setAttribute(input, 'spellcheck', 'false');
3671
+ setAttribute(input, 'value', value);
3672
+ setAttribute(input, 'placeholder', placeholder);
3673
+ self.append(input);
3680
3674
 
3681
- setAttribute(input, 'id', id);
3682
- setAttribute(input, 'name', id);
3683
- setAttribute(input, 'autocomplete', 'off');
3684
- setAttribute(input, 'spellcheck', 'false');
3685
- setAttribute(input, 'value', value);
3686
- this.append(input);
3687
- }
3688
3675
  /** @type {HTMLInputElement} */
3689
- // @ts-ignore - `HTMLInputElement` is `HTMLElement`
3690
- this.input = input;
3676
+ self.input = input;
3691
3677
 
3692
- // @ts-ignore - `HTMLInputElement` is `HTMLElement`
3693
- this.colorPicker = new ColorPicker(input);
3678
+ self.colorPicker = new ColorPicker(input);
3694
3679
 
3695
- // @ts-ignore - `shadowRoot` is defined in the constructor
3696
- this.shadowRoot.append(createElement('slot'));
3680
+ self.shadowRoot.append(createElement('slot'));
3681
+
3682
+ // remove Attributes
3683
+ toggleCEAttr(self);
3697
3684
  }
3698
3685
 
3699
3686
  /** @this {ColorPickerElement} */
3700
3687
  disconnectedCallback() {
3701
- const { input, colorPicker, shadowRoot } = this;
3702
- if (colorPicker) colorPicker.dispose();
3703
- if (input) input.remove();
3704
- if (shadowRoot) shadowRoot.innerHTML = '';
3705
-
3706
- ObjectAssign(this, {
3707
- colorPicker: undefined,
3708
- input: undefined,
3709
- });
3688
+ const self = this;
3689
+ const { input, colorPicker, shadowRoot } = self;
3690
+
3691
+ const callback = () => {
3692
+ // remove markup
3693
+ input.remove();
3694
+ colorPicker.dispose();
3695
+ shadowRoot.innerHTML = '';
3696
+
3697
+ ObjectAssign(self, {
3698
+ colorPicker: undefined,
3699
+ input: undefined,
3700
+ });
3701
+ };
3702
+
3703
+ // re-add Attributes
3704
+ toggleCEAttr(self, callback);
3710
3705
  }
3711
3706
  }
3712
3707
 
3713
3708
  ObjectAssign(ColorPickerElement, {
3714
3709
  Color,
3715
3710
  ColorPicker,
3716
- ColorPalette, // @ts-ignore
3711
+ ColorPalette,
3717
3712
  getInstance: ColorPicker.getInstance,
3718
3713
  Version,
3719
3714
  });