@thednp/color-picker 0.0.2-alpha5 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
  });