@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
  */
@@ -21,25 +21,6 @@
21
21
  return window.document;
22
22
  }
23
23
 
24
- /**
25
- * A global array of possible `ParentNode`.
26
- */
27
- const parentNodes = [Document, Element, HTMLElement];
28
-
29
- /**
30
- * Shortcut for `HTMLElement.getElementsByTagName` method. Some `Node` elements
31
- * like `ShadowRoot` do not support `getElementsByTagName`.
32
- *
33
- * @param {string} selector the tag name
34
- * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
35
- * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
36
- */
37
- function getElementsByTagName(selector, parent) {
38
- const lookUp = parent && parentNodes
39
- .some((x) => parent instanceof x) ? parent : getDocument();
40
- return lookUp.getElementsByTagName(selector);
41
- }
42
-
43
24
  /**
44
25
  * Shortcut for `Object.assign()` static method.
45
26
  * @param {Record<string, any>} obj a target object
@@ -775,7 +756,7 @@
775
756
  format = 'hwb';
776
757
  }
777
758
  if (isValidCSSUnit(color.a)) {
778
- a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
759
+ a = color.a;
779
760
  a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
780
761
  }
781
762
  }
@@ -786,9 +767,6 @@
786
767
  return {
787
768
  ok,
788
769
  format,
789
- // r: Math.min(255, Math.max(rgb.r, 0)),
790
- // g: Math.min(255, Math.max(rgb.g, 0)),
791
- // b: Math.min(255, Math.max(rgb.b, 0)),
792
770
  r: rgb.r,
793
771
  g: rgb.g,
794
772
  b: rgb.b,
@@ -1247,27 +1225,26 @@
1247
1225
  /**
1248
1226
  * The global event listener.
1249
1227
  *
1250
- * @this {Element | HTMLElement | Window | Document}
1251
- * @param {Event} e
1252
- * @returns {void}
1228
+ * @type {EventListener}
1229
+ * @this {EventTarget}
1253
1230
  */
1254
1231
  function globalListener(e) {
1255
1232
  const that = this;
1256
1233
  const { type } = e;
1257
- const oneEvMap = EventRegistry[type] ? [...EventRegistry[type]] : [];
1258
1234
 
1259
- oneEvMap.forEach((elementsMap) => {
1235
+ [...EventRegistry[type]].forEach((elementsMap) => {
1260
1236
  const [element, listenersMap] = elementsMap;
1261
- [...listenersMap].forEach((listenerMap) => {
1262
- if (element === that) {
1237
+ /* istanbul ignore else */
1238
+ if (element === that) {
1239
+ [...listenersMap].forEach((listenerMap) => {
1263
1240
  const [listener, options] = listenerMap;
1264
1241
  listener.apply(element, [e]);
1265
1242
 
1266
1243
  if (options && options.once) {
1267
1244
  removeListener(element, type, listener, options);
1268
1245
  }
1269
- }
1270
- });
1246
+ });
1247
+ }
1271
1248
  });
1272
1249
  }
1273
1250
 
@@ -1275,10 +1252,7 @@
1275
1252
  * Register a new listener with its options and attach the `globalListener`
1276
1253
  * to the target if this is the first listener.
1277
1254
  *
1278
- * @param {Element | HTMLElement | Window | Document} element
1279
- * @param {string} eventType
1280
- * @param {EventListenerObject['handleEvent']} listener
1281
- * @param {AddEventListenerOptions=} options
1255
+ * @type {Listener.ListenerAction<EventTarget>}
1282
1256
  */
1283
1257
  const addListener = (element, eventType, listener, options) => {
1284
1258
  // get element listeners first
@@ -1296,9 +1270,7 @@
1296
1270
  const { size } = oneElementMap;
1297
1271
 
1298
1272
  // register listener with its options
1299
- if (oneElementMap) {
1300
- oneElementMap.set(listener, options);
1301
- }
1273
+ oneElementMap.set(listener, options);
1302
1274
 
1303
1275
  // add listener last
1304
1276
  if (!size) {
@@ -1310,10 +1282,7 @@
1310
1282
  * Remove a listener from registry and detach the `globalListener`
1311
1283
  * if no listeners are found in the registry.
1312
1284
  *
1313
- * @param {Element | HTMLElement | Window | Document} element
1314
- * @param {string} eventType
1315
- * @param {EventListenerObject['handleEvent']} listener
1316
- * @param {AddEventListenerOptions=} options
1285
+ * @type {Listener.ListenerAction<EventTarget>}
1317
1286
  */
1318
1287
  const removeListener = (element, eventType, listener, options) => {
1319
1288
  // get listener first
@@ -1332,6 +1301,7 @@
1332
1301
  if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType];
1333
1302
 
1334
1303
  // remove listener last
1304
+ /* istanbul ignore else */
1335
1305
  if (!oneElementMap || !oneElementMap.size) {
1336
1306
  element.removeEventListener(eventType, globalListener, eventOptions);
1337
1307
  }
@@ -1433,12 +1403,6 @@
1433
1403
  */
1434
1404
  const changeEvent = 'change';
1435
1405
 
1436
- /**
1437
- * A global namespace for `touchstart` event.
1438
- * @type {string}
1439
- */
1440
- const touchstartEvent = 'touchstart';
1441
-
1442
1406
  /**
1443
1407
  * A global namespace for `touchmove` event.
1444
1408
  * @type {string}
@@ -1446,28 +1410,22 @@
1446
1410
  const touchmoveEvent = 'touchmove';
1447
1411
 
1448
1412
  /**
1449
- * A global namespace for `touchend` event.
1413
+ * A global namespace for `pointerdown` event.
1450
1414
  * @type {string}
1451
1415
  */
1452
- const touchendEvent = 'touchend';
1416
+ const pointerdownEvent = 'pointerdown';
1453
1417
 
1454
1418
  /**
1455
- * A global namespace for `mousedown` event.
1419
+ * A global namespace for `pointermove` event.
1456
1420
  * @type {string}
1457
1421
  */
1458
- const mousedownEvent = 'mousedown';
1422
+ const pointermoveEvent = 'pointermove';
1459
1423
 
1460
1424
  /**
1461
- * A global namespace for `mousemove` event.
1425
+ * A global namespace for `pointerup` event.
1462
1426
  * @type {string}
1463
1427
  */
1464
- const mousemoveEvent = 'mousemove';
1465
-
1466
- /**
1467
- * A global namespace for `mouseup` event.
1468
- * @type {string}
1469
- */
1470
- const mouseupEvent = 'mouseup';
1428
+ const pointerupEvent = 'pointerup';
1471
1429
 
1472
1430
  /**
1473
1431
  * A global namespace for `scroll` event.
@@ -1503,27 +1461,6 @@
1503
1461
  return getDocument(node).documentElement;
1504
1462
  }
1505
1463
 
1506
- /**
1507
- * Returns the `Window` object of a target node.
1508
- * @see https://github.com/floating-ui/floating-ui
1509
- *
1510
- * @param {(Node | HTMLElement | Element | Window)=} node target node
1511
- * @returns {globalThis}
1512
- */
1513
- function getWindow(node) {
1514
- if (node == null) {
1515
- return window;
1516
- }
1517
-
1518
- if (!(node instanceof Window)) {
1519
- const { ownerDocument } = node;
1520
- return ownerDocument ? ownerDocument.defaultView || window : window;
1521
- }
1522
-
1523
- // @ts-ignore
1524
- return node;
1525
- }
1526
-
1527
1464
  let elementUID = 0;
1528
1465
  let elementMapUID = 0;
1529
1466
  const elementIDMap = new Map();
@@ -1623,6 +1560,11 @@
1623
1560
  return !Number.isNaN(duration) ? duration : 0;
1624
1561
  }
1625
1562
 
1563
+ /**
1564
+ * A global array of possible `ParentNode`.
1565
+ */
1566
+ const parentNodes = [Document, Element, HTMLElement];
1567
+
1626
1568
  /**
1627
1569
  * A global array with `Element` | `HTMLElement`.
1628
1570
  */
@@ -2266,17 +2208,16 @@
2266
2208
  const isOptionsMenu = menuClass === 'color-options';
2267
2209
  const isPalette = colorsSource instanceof ColorPalette;
2268
2210
  const menuLabel = isOptionsMenu ? presetsLabel : defaultsLabel;
2269
- let colorsArray = isPalette ? colorsSource.colors : colorsSource;
2270
- colorsArray = colorsArray instanceof Array ? colorsArray : [];
2211
+ const colorsArray = isPalette ? colorsSource.colors : colorsSource;
2271
2212
  const colorsCount = colorsArray.length;
2272
2213
  const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
2273
- const fit = lightSteps || [9, 10].find((x) => colorsCount > x * 2 && !(colorsCount % x)) || 5;
2214
+ const fit = lightSteps || [9, 10].find((x) => colorsCount >= x * 2 && !(colorsCount % x)) || 5;
2274
2215
  const isMultiLine = isOptionsMenu && colorsCount > fit;
2275
2216
  let rowCountHover = 2;
2276
- rowCountHover = isMultiLine && colorsCount >= fit * 2 ? 3 : rowCountHover;
2277
- rowCountHover = colorsCount >= fit * 3 ? 4 : rowCountHover;
2278
- rowCountHover = colorsCount >= fit * 4 ? 5 : rowCountHover;
2279
- const rowCount = rowCountHover - (colorsCount < fit * 3 ? 1 : 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);
2280
2221
  const isScrollable = isMultiLine && colorsCount > rowCount * fit;
2281
2222
  let finalClass = menuClass;
2282
2223
  finalClass += isScrollable ? ' scrollable' : '';
@@ -2284,7 +2225,7 @@
2284
2225
  const gap = isMultiLine ? '1px' : '0.25rem';
2285
2226
  let optionSize = isMultiLine ? 1.75 : 2;
2286
2227
  optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
2287
- const menuHeight = `${(rowCount || 1) * optionSize}rem`;
2228
+ const menuHeight = `${rowCount * optionSize}rem`;
2288
2229
  const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
2289
2230
  /** @type {HTMLUListElement} */
2290
2231
  // @ts-ignore -- <UL> is an `HTMLElement`
@@ -2391,16 +2332,14 @@
2391
2332
  });
2392
2333
 
2393
2334
  // color presets
2394
- if ((colorPresets instanceof Array && colorPresets.length)
2395
- || (colorPresets instanceof ColorPalette && colorPresets.colors)) {
2396
- const presetsMenu = getColorMenu(self, colorPresets, 'color-options');
2397
- presetsDropdown.append(presetsMenu);
2335
+ if (colorPresets) {
2336
+ presetsDropdown.append(getColorMenu(self, colorPresets, 'color-options'));
2398
2337
  }
2399
2338
 
2400
2339
  // explicit defaults [reset, initial, inherit, transparent, currentColor]
2340
+ // also custom defaults [default: #069, complementary: #930]
2401
2341
  if (colorKeywords && colorKeywords.length) {
2402
- const keywordsMenu = getColorMenu(self, colorKeywords, 'color-defaults');
2403
- presetsDropdown.append(keywordsMenu);
2342
+ presetsDropdown.append(getColorMenu(self, colorKeywords, 'color-defaults'));
2404
2343
  }
2405
2344
 
2406
2345
  const presetsBtn = createElement({
@@ -2437,9 +2376,7 @@
2437
2376
  setAttribute(input, tabIndex, '-1');
2438
2377
  }
2439
2378
 
2440
- var version = "0.0.2alpha5";
2441
-
2442
- // @ts-ignore
2379
+ var version = "1.0.1";
2443
2380
 
2444
2381
  const Version = version;
2445
2382
 
@@ -2459,7 +2396,7 @@
2459
2396
  // ColorPicker Static Methods
2460
2397
  // ==========================
2461
2398
 
2462
- /** @type {CP.GetInstance<ColorPicker>} */
2399
+ /** @type {CP.GetInstance<ColorPicker, HTMLInputElement>} */
2463
2400
  const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
2464
2401
 
2465
2402
  /** @type {CP.InitCallback<ColorPicker>} */
@@ -2494,17 +2431,12 @@
2494
2431
  const fn = action ? addListener : removeListener;
2495
2432
  const { input, colorMenu, parent } = self;
2496
2433
  const doc = getDocument(input);
2497
- const win = getWindow(input);
2498
- const pointerEvents = `on${touchstartEvent}` in doc
2499
- ? { down: touchstartEvent, move: touchmoveEvent, up: touchendEvent }
2500
- : { down: mousedownEvent, move: mousemoveEvent, up: mouseupEvent };
2434
+ const win = doc.defaultView;
2501
2435
 
2502
- fn(self.controls, pointerEvents.down, self.pointerDown);
2436
+ fn(self.controls, pointerdownEvent, self.pointerDown);
2503
2437
  self.controlKnobs.forEach((x) => fn(x, keydownEvent, self.handleKnobs));
2504
2438
 
2505
- // @ts-ignore -- this is `Window`
2506
2439
  fn(win, scrollEvent, self.handleScroll);
2507
- // @ts-ignore -- this is `Window`
2508
2440
  fn(win, resizeEvent, self.update);
2509
2441
 
2510
2442
  [input, ...self.inputs].forEach((x) => fn(x, changeEvent, self.changeHandler));
@@ -2514,8 +2446,8 @@
2514
2446
  fn(colorMenu, keydownEvent, self.menuKeyHandler);
2515
2447
  }
2516
2448
 
2517
- fn(doc, pointerEvents.move, self.pointerMove);
2518
- fn(doc, pointerEvents.up, self.pointerUp);
2449
+ fn(doc, pointermoveEvent, self.pointerMove);
2450
+ fn(doc, pointerupEvent, self.pointerUp);
2519
2451
  fn(parent, focusoutEvent, self.handleFocusOut);
2520
2452
  fn(doc, keyupEvent, self.handleDismiss);
2521
2453
  }
@@ -2534,6 +2466,7 @@
2534
2466
  * @returns {void}
2535
2467
  */
2536
2468
  function removePosition(element) {
2469
+ /* istanbul ignore else */
2537
2470
  if (element) {
2538
2471
  ['bottom', 'top'].forEach((x) => removeClass(element, x));
2539
2472
  }
@@ -2597,7 +2530,6 @@
2597
2530
  constructor(target, config) {
2598
2531
  const self = this;
2599
2532
  /** @type {HTMLInputElement} */
2600
- // @ts-ignore
2601
2533
  const input = querySelector(target);
2602
2534
 
2603
2535
  // invalidate
@@ -2608,7 +2540,6 @@
2608
2540
  if (!parent) throw new TypeError('ColorPicker requires a specific markup to work.');
2609
2541
 
2610
2542
  /** @type {HTMLElement} */
2611
- // @ts-ignore
2612
2543
  self.parent = parent;
2613
2544
 
2614
2545
  /** @type {number} */
@@ -2636,6 +2567,7 @@
2636
2567
  } = normalizeOptions(this.isCE ? parent : input, colorPickerDefaults, config || {});
2637
2568
 
2638
2569
  let translatedColorLabels = colorNames;
2570
+ /* istanbul ignore else */
2639
2571
  if (colorLabels instanceof Array && colorLabels.length === 17) {
2640
2572
  translatedColorLabels = colorLabels;
2641
2573
  } else if (colorLabels && colorLabels.split(',').length === 17) {
@@ -2652,7 +2584,7 @@
2652
2584
  ? JSON.parse(componentLabels) : componentLabels;
2653
2585
 
2654
2586
  /** @type {Record<string, string>} */
2655
- self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
2587
+ self.componentLabels = ObjectAssign({ ...colorPickerLabels }, tempComponentLabels);
2656
2588
 
2657
2589
  /** @type {Color} */
2658
2590
  self.color = new Color(input.value || '#fff', format);
@@ -2661,14 +2593,14 @@
2661
2593
  self.format = format;
2662
2594
 
2663
2595
  // set colour defaults
2664
- if (colorKeywords instanceof Array) {
2596
+ if (colorKeywords instanceof Array && colorKeywords.length) {
2665
2597
  self.colorKeywords = colorKeywords;
2666
2598
  } else if (typeof colorKeywords === 'string' && colorKeywords.length) {
2667
2599
  self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
2668
2600
  }
2669
2601
 
2670
2602
  // set colour presets
2671
- if (colorPresets instanceof Array) {
2603
+ if (colorPresets instanceof Array && colorPresets.length) {
2672
2604
  self.colorPresets = colorPresets;
2673
2605
  } else if (typeof colorPresets === 'string' && colorPresets.length) {
2674
2606
  if (isValidJSON(colorPresets)) {
@@ -2701,26 +2633,20 @@
2701
2633
  const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
2702
2634
  // set main elements
2703
2635
  /** @type {HTMLElement} */
2704
- // @ts-ignore
2705
2636
  self.pickerToggle = querySelector('.picker-toggle', parent);
2706
2637
  /** @type {HTMLElement} */
2707
- // @ts-ignore
2708
2638
  self.menuToggle = querySelector('.menu-toggle', parent);
2709
2639
  /** @type {HTMLElement} */
2710
- // @ts-ignore
2711
2640
  self.colorPicker = colorPicker;
2712
2641
  /** @type {HTMLElement} */
2713
- // @ts-ignore
2714
2642
  self.colorMenu = colorMenu;
2715
2643
  /** @type {HTMLInputElement[]} */
2716
- // @ts-ignore
2717
2644
  self.inputs = [...getElementsByClassName('color-input', parent)];
2718
2645
  const [controls] = getElementsByClassName('color-controls', parent);
2719
2646
  self.controls = controls;
2720
2647
  /** @type {(HTMLElement | Element)[]} */
2721
2648
  self.controlKnobs = [...getElementsByClassName('knob', controls)];
2722
2649
  /** @type {(HTMLElement)[]} */
2723
- // @ts-ignore
2724
2650
  self.visuals = [...getElementsByClassName('visual-control', controls)];
2725
2651
 
2726
2652
  // update colour picker controls, inputs and visuals
@@ -2799,6 +2725,7 @@
2799
2725
  let colorName;
2800
2726
 
2801
2727
  // determine color appearance
2728
+ /* istanbul ignore else */
2802
2729
  if (lightness === 100 && saturation === 0) {
2803
2730
  colorName = colorLabels.white;
2804
2731
  } else if (lightness === 0) {
@@ -2872,7 +2799,6 @@
2872
2799
  * @this {ColorPicker}
2873
2800
  */
2874
2801
  handleFocusOut({ relatedTarget }) {
2875
- // @ts-ignore
2876
2802
  if (relatedTarget && !this.parent.contains(relatedTarget)) {
2877
2803
  this.hide(true);
2878
2804
  }
@@ -2899,13 +2825,14 @@
2899
2825
  const self = this;
2900
2826
  const { activeElement } = getDocument(self.input);
2901
2827
 
2902
- if ((e.type === touchmoveEvent && self.dragElement)
2828
+ self.updateDropdownPosition();
2829
+
2830
+ /* istanbul ignore next */
2831
+ if (([pointermoveEvent, touchmoveEvent].includes(e.type) && self.dragElement)
2903
2832
  || (activeElement && self.controlKnobs.includes(activeElement))) {
2904
2833
  e.stopPropagation();
2905
2834
  e.preventDefault();
2906
2835
  }
2907
-
2908
- self.updateDropdownPosition();
2909
2836
  }
2910
2837
 
2911
2838
  /**
@@ -2915,7 +2842,6 @@
2915
2842
  */
2916
2843
  menuKeyHandler(e) {
2917
2844
  const { target, code } = e;
2918
- // @ts-ignore
2919
2845
  const { previousElementSibling, nextElementSibling, parentElement } = target;
2920
2846
  const isColorOptionsMenu = parentElement && hasClass(parentElement, 'color-options');
2921
2847
  const allSiblings = [...parentElement.children];
@@ -2954,20 +2880,20 @@
2954
2880
 
2955
2881
  /**
2956
2882
  * The `ColorPicker` click event listener for the colour menu presets / defaults.
2957
- * @param {Partial<Event>} e
2883
+ * @param {Event} e
2958
2884
  * @this {ColorPicker}
2959
2885
  */
2960
2886
  menuClickHandler(e) {
2961
2887
  const self = this;
2962
- /** @type {*} */
2963
2888
  const { target } = e;
2964
2889
  const { colorMenu } = self;
2965
2890
  const newOption = (getAttribute(target, 'data-value') || '').trim();
2966
2891
  // invalidate for targets other than color options
2967
2892
  if (!newOption.length) return;
2968
2893
  const currentActive = querySelector('li.active', colorMenu);
2969
- let newColor = nonColors.includes(newOption) ? 'white' : newOption;
2970
- newColor = newOption === 'transparent' ? 'rgba(0,0,0,0)' : newOption;
2894
+ let newColor = newOption;
2895
+ newColor = nonColors.includes(newColor) ? 'white' : newColor;
2896
+ newColor = newColor === 'transparent' ? 'rgba(0,0,0,0)' : newColor;
2971
2897
 
2972
2898
  const {
2973
2899
  r, g, b, a,
@@ -2979,7 +2905,9 @@
2979
2905
 
2980
2906
  self.update();
2981
2907
 
2908
+ /* istanbul ignore else */
2982
2909
  if (currentActive !== target) {
2910
+ /* istanbul ignore else */
2983
2911
  if (currentActive) {
2984
2912
  removeClass(currentActive, 'active');
2985
2913
  removeAttribute(currentActive, ariaSelected);
@@ -2997,15 +2925,13 @@
2997
2925
 
2998
2926
  /**
2999
2927
  * The `ColorPicker` *touchstart* / *mousedown* events listener for control knobs.
3000
- * @param {TouchEvent} e
2928
+ * @param {PointerEvent} e
3001
2929
  * @this {ColorPicker}
3002
2930
  */
3003
2931
  pointerDown(e) {
3004
2932
  const self = this;
3005
2933
  /** @type {*} */
3006
- const {
3007
- type, target, touches, pageX, pageY,
3008
- } = e;
2934
+ const { target, pageX, pageY } = e;
3009
2935
  const { colorMenu, visuals, controlKnobs } = self;
3010
2936
  const [v1, v2, v3] = visuals;
3011
2937
  const [c1, c2, c3] = controlKnobs;
@@ -3013,11 +2939,10 @@
3013
2939
  const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
3014
2940
  const visualRect = getBoundingClientRect(visual);
3015
2941
  const html = getDocumentElement(v1);
3016
- const X = type === 'touchstart' ? touches[0].pageX : pageX;
3017
- const Y = type === 'touchstart' ? touches[0].pageY : pageY;
3018
- const offsetX = X - html.scrollLeft - visualRect.left;
3019
- const offsetY = Y - html.scrollTop - visualRect.top;
2942
+ const offsetX = pageX - html.scrollLeft - visualRect.left;
2943
+ const offsetY = pageY - html.scrollTop - visualRect.top;
3020
2944
 
2945
+ /* istanbul ignore else */
3021
2946
  if (target === v1 || target === c1) {
3022
2947
  self.dragElement = visual;
3023
2948
  self.changeControl1(offsetX, offsetY);
@@ -3041,7 +2966,7 @@
3041
2966
 
3042
2967
  /**
3043
2968
  * The `ColorPicker` *touchend* / *mouseup* events listener for control knobs.
3044
- * @param {TouchEvent} e
2969
+ * @param {PointerEvent} e
3045
2970
  * @this {ColorPicker}
3046
2971
  */
3047
2972
  pointerUp({ target }) {
@@ -3050,9 +2975,8 @@
3050
2975
  const doc = getDocument(parent);
3051
2976
  const currentOpen = querySelector(`${colorPickerParentSelector}.open`, doc) !== null;
3052
2977
  const selection = doc.getSelection();
3053
- // @ts-ignore
2978
+
3054
2979
  if (!self.dragElement && !selection.toString().length
3055
- // @ts-ignore
3056
2980
  && !parent.contains(target)) {
3057
2981
  self.hide(currentOpen);
3058
2982
  }
@@ -3062,25 +2986,20 @@
3062
2986
 
3063
2987
  /**
3064
2988
  * The `ColorPicker` *touchmove* / *mousemove* events listener for control knobs.
3065
- * @param {TouchEvent} e
2989
+ * @param {PointerEvent} e
3066
2990
  */
3067
2991
  pointerMove(e) {
3068
2992
  const self = this;
3069
2993
  const { dragElement, visuals } = self;
3070
2994
  const [v1, v2, v3] = visuals;
3071
- const {
3072
- // @ts-ignore
3073
- type, touches, pageX, pageY,
3074
- } = e;
2995
+ const { pageX, pageY } = e;
3075
2996
 
3076
2997
  if (!dragElement) return;
3077
2998
 
3078
2999
  const controlRect = getBoundingClientRect(dragElement);
3079
3000
  const win = getDocumentElement(v1);
3080
- const X = type === touchmoveEvent ? touches[0].pageX : pageX;
3081
- const Y = type === touchmoveEvent ? touches[0].pageY : pageY;
3082
- const offsetX = X - win.scrollLeft - controlRect.left;
3083
- const offsetY = Y - win.scrollTop - controlRect.top;
3001
+ const offsetX = pageX - win.scrollLeft - controlRect.left;
3002
+ const offsetY = pageY - win.scrollTop - controlRect.top;
3084
3003
 
3085
3004
  if (dragElement === v1) {
3086
3005
  self.changeControl1(offsetX, offsetY);
@@ -3114,13 +3033,16 @@
3114
3033
  const currentKnob = controlKnobs.find((x) => x === activeElement);
3115
3034
  const yRatio = offsetHeight / 360;
3116
3035
 
3036
+ /* istanbul ignore else */
3117
3037
  if (currentKnob) {
3118
3038
  let offsetX = 0;
3119
3039
  let offsetY = 0;
3120
3040
 
3041
+ /* istanbul ignore else */
3121
3042
  if (target === c1) {
3122
3043
  const xRatio = offsetWidth / 100;
3123
3044
 
3045
+ /* istanbul ignore else */
3124
3046
  if ([keyArrowLeft, keyArrowRight].includes(code)) {
3125
3047
  self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
3126
3048
  } else if ([keyArrowUp, keyArrowDown].includes(code)) {
@@ -3166,6 +3088,7 @@
3166
3088
  const isNonColorValue = self.hasNonColor && nonColors.includes(currentValue);
3167
3089
  const alpha = i4 ? v4 : (1 - controlPositions.c3y / offsetHeight);
3168
3090
 
3091
+ /* istanbul ignore else */
3169
3092
  if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
3170
3093
  if (activeElement === input) {
3171
3094
  if (isNonColorValue) {
@@ -3480,6 +3403,7 @@
3480
3403
  const hue = roundPart(hsl.h * 360);
3481
3404
  let newColor;
3482
3405
 
3406
+ /* istanbul ignore else */
3483
3407
  if (format === 'hex') {
3484
3408
  newColor = self.color.toHexString(true);
3485
3409
  i1.value = self.hex;
@@ -3580,15 +3504,15 @@
3580
3504
  const relatedBtn = openPicker ? pickerToggle : menuToggle;
3581
3505
  const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
3582
3506
 
3583
- // if (!self.isValid) {
3584
3507
  self.value = self.color.toString(true);
3585
- // }
3586
3508
 
3509
+ /* istanbul ignore else */
3587
3510
  if (openDropdown) {
3588
3511
  removeClass(openDropdown, 'show');
3589
3512
  setAttribute(relatedBtn, ariaExpanded, 'false');
3590
3513
  setTimeout(() => {
3591
3514
  removePosition(openDropdown);
3515
+ /* istanbul ignore else */
3592
3516
  if (!querySelector('.show', parent)) {
3593
3517
  removeClass(parent, 'open');
3594
3518
  toggleEventsOnShown(self);
@@ -3601,7 +3525,7 @@
3601
3525
  focus(pickerToggle);
3602
3526
  }
3603
3527
  setAttribute(input, tabIndex, '-1');
3604
- if (menuToggle) {
3528
+ if (relatedBtn === menuToggle) {
3605
3529
  setAttribute(menuToggle, tabIndex, '-1');
3606
3530
  }
3607
3531
  }
@@ -3639,14 +3563,76 @@
3639
3563
  getBoundingClientRect,
3640
3564
  });
3641
3565
 
3566
+ /**
3567
+ * A small utility to toggle `ColorPickerElement` attributes
3568
+ * when `connectedCallback` or `disconnectedCallback` methods
3569
+ * are called and helps the instance keep its value and settings instact.
3570
+ *
3571
+ * @param {CP.ColorPickerElement} self ColorPickerElement instance
3572
+ * @param {Function=} callback when `true`, attributes are added
3573
+ *
3574
+ * @example
3575
+ * const attributes = [
3576
+ * // essentials
3577
+ * 'value', 'format',
3578
+ * // presets menus
3579
+ * 'color-presets', 'color-keywords',
3580
+ * // labels
3581
+ * 'color-labels', 'component-labels',
3582
+ * ];
3583
+ */
3584
+ function toggleCEAttr(self, callback) {
3585
+ if (callback) {
3586
+ const { input, colorPicker } = self;
3587
+
3588
+ const {
3589
+ value, format, colorPresets, colorKeywords, componentLabels, colorLabels,
3590
+ } = colorPicker;
3591
+
3592
+ const { id, placeholder } = input;
3593
+
3594
+ setAttribute(self, 'data-id', id);
3595
+ setAttribute(self, 'data-value', value);
3596
+ setAttribute(self, 'data-format', format);
3597
+ setAttribute(self, 'data-placeholder', placeholder);
3598
+
3599
+ if (ObjectKeys(colorPickerLabels).some((l) => colorPickerLabels[l] !== componentLabels[l])) {
3600
+ setAttribute(self, 'data-component-labels', JSON.stringify(componentLabels));
3601
+ }
3602
+ if (!colorNames.every((c) => c === colorLabels[c])) {
3603
+ setAttribute(self, 'data-color-labels', colorNames.map((n) => colorLabels[n]).join(','));
3604
+ }
3605
+ if (colorPresets instanceof ColorPalette) {
3606
+ const { hue, hueSteps, lightSteps } = colorPresets;
3607
+ setAttribute(self, 'data-color-presets', JSON.stringify({ hue, hueSteps, lightSteps }));
3608
+ }
3609
+ if (Array.isArray(colorPresets) && colorPresets.length) {
3610
+ setAttribute(self, 'data-color-presets', colorPresets.join(','));
3611
+ }
3612
+ if (colorKeywords) {
3613
+ setAttribute(self, 'data-color-keywords', colorKeywords.join(','));
3614
+ }
3615
+ setTimeout(callback, 0);
3616
+ } else {
3617
+ // keep id
3618
+ // removeAttribute(self, 'data-id');
3619
+ removeAttribute(self, 'data-value');
3620
+ removeAttribute(self, 'data-format');
3621
+ removeAttribute(self, 'data-placeholder');
3622
+ removeAttribute(self, 'data-component-labels');
3623
+ removeAttribute(self, 'data-color-labels');
3624
+ removeAttribute(self, 'data-color-presets');
3625
+ removeAttribute(self, 'data-color-keywords');
3626
+ }
3627
+ }
3628
+
3642
3629
  let CPID = 0;
3643
3630
 
3644
3631
  /**
3645
3632
  * `ColorPickerElement` Web Component.
3646
3633
  * @example
3647
3634
  * <label for="UNIQUE_ID">Label</label>
3648
- * <color-picker>
3649
- * <input id="UNIQUE_ID" value="red" format="hex" class="color-preview btn-appearance">
3635
+ * <color-picker data-id="UNIQUE_ID" data-value="red" data-format="hex">
3650
3636
  * </color-picker>
3651
3637
  * // or
3652
3638
  * <label for="UNIQUE_ID">Label</label>
@@ -3665,61 +3651,70 @@
3665
3651
  get value() { return this.input && this.input.value; }
3666
3652
 
3667
3653
  connectedCallback() {
3668
- if (this.input) return;
3654
+ const self = this;
3655
+ if (self.input) return;
3669
3656
 
3670
- let [input] = getElementsByTagName('input', this);
3671
- const value = (input && getAttribute(input, 'value')) || getAttribute(this, 'data-value') || '#fff';
3672
- const format = (input && getAttribute(input, 'format')) || getAttribute(this, 'data-format') || 'rgb';
3673
- let id = (input && getAttribute(input, 'id')) || getAttribute(this, 'data-id');
3657
+ let id = getAttribute(self, 'data-id');
3658
+ const value = getAttribute(self, 'data-value') || '#fff';
3659
+ const format = getAttribute(self, 'data-format') || 'rgb';
3660
+ const placeholder = getAttribute(self, 'data-placeholder') || '';
3674
3661
 
3675
3662
  if (!id) {
3676
3663
  id = `color-picker-${format}-${CPID}`;
3677
3664
  CPID += 1;
3678
3665
  }
3679
3666
 
3680
- if (!input) {
3681
- input = createElement({
3682
- tagName: 'input',
3683
- type: 'text',
3684
- className: 'color-preview btn-appearance',
3685
- });
3667
+ const input = createElement({
3668
+ tagName: 'input',
3669
+ type: 'text',
3670
+ className: 'color-preview btn-appearance',
3671
+ });
3672
+
3673
+ setAttribute(input, 'id', id);
3674
+ setAttribute(input, 'name', id);
3675
+ setAttribute(input, 'autocomplete', 'off');
3676
+ setAttribute(input, 'spellcheck', 'false');
3677
+ setAttribute(input, 'value', value);
3678
+ setAttribute(input, 'placeholder', placeholder);
3679
+ self.append(input);
3686
3680
 
3687
- setAttribute(input, 'id', id);
3688
- setAttribute(input, 'name', id);
3689
- setAttribute(input, 'autocomplete', 'off');
3690
- setAttribute(input, 'spellcheck', 'false');
3691
- setAttribute(input, 'value', value);
3692
- this.append(input);
3693
- }
3694
3681
  /** @type {HTMLInputElement} */
3695
- // @ts-ignore - `HTMLInputElement` is `HTMLElement`
3696
- this.input = input;
3682
+ self.input = input;
3697
3683
 
3698
- // @ts-ignore - `HTMLInputElement` is `HTMLElement`
3699
- this.colorPicker = new ColorPicker(input);
3684
+ self.colorPicker = new ColorPicker(input);
3700
3685
 
3701
- // @ts-ignore - `shadowRoot` is defined in the constructor
3702
- this.shadowRoot.append(createElement('slot'));
3686
+ self.shadowRoot.append(createElement('slot'));
3687
+
3688
+ // remove Attributes
3689
+ toggleCEAttr(self);
3703
3690
  }
3704
3691
 
3705
3692
  /** @this {ColorPickerElement} */
3706
3693
  disconnectedCallback() {
3707
- const { input, colorPicker, shadowRoot } = this;
3708
- if (colorPicker) colorPicker.dispose();
3709
- if (input) input.remove();
3710
- if (shadowRoot) shadowRoot.innerHTML = '';
3711
-
3712
- ObjectAssign(this, {
3713
- colorPicker: undefined,
3714
- input: undefined,
3715
- });
3694
+ const self = this;
3695
+ const { input, colorPicker, shadowRoot } = self;
3696
+
3697
+ const callback = () => {
3698
+ // remove markup
3699
+ input.remove();
3700
+ colorPicker.dispose();
3701
+ shadowRoot.innerHTML = '';
3702
+
3703
+ ObjectAssign(self, {
3704
+ colorPicker: undefined,
3705
+ input: undefined,
3706
+ });
3707
+ };
3708
+
3709
+ // re-add Attributes
3710
+ toggleCEAttr(self, callback);
3716
3711
  }
3717
3712
  }
3718
3713
 
3719
3714
  ObjectAssign(ColorPickerElement, {
3720
3715
  Color,
3721
3716
  ColorPicker,
3722
- ColorPalette, // @ts-ignore
3717
+ ColorPalette,
3723
3718
  getInstance: ColorPicker.getInstance,
3724
3719
  Version,
3725
3720
  });