@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
  */
@@ -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
  });