@thednp/color-picker 0.0.1-alpha2 → 0.0.1-alpha3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * ColorPickerElement v0.0.1alpha2 (http://thednp.github.io/color-picker)
2
+ * ColorPickerElement v0.0.1alpha3 (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
  */
@@ -110,19 +110,36 @@ function getElementStyle(element, property) {
110
110
  return property in computedStyle ? computedStyle[property] : '';
111
111
  }
112
112
 
113
+ /**
114
+ * Shortcut for `Object.keys()` static method.
115
+ * @param {Record<string, any>} obj a target object
116
+ * @returns {string[]}
117
+ */
118
+ const ObjectKeys = (obj) => Object.keys(obj);
119
+
113
120
  /**
114
121
  * Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
115
122
  * @param {HTMLElement | Element} element target element
116
123
  * @param {Partial<CSSStyleDeclaration>} styles attribute value
117
124
  */
118
125
  // @ts-ignore
119
- const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
126
+ const setElementStyle = (element, styles) => ObjectAssign(element.style, styles);
120
127
 
121
128
  /**
122
129
  * A list of explicit default non-color values.
123
130
  */
124
131
  const nonColors = ['transparent', 'currentColor', 'inherit', 'revert', 'initial'];
125
132
 
133
+ /**
134
+ * Round colour components, for all formats except HEX.
135
+ * @param {number} v one of the colour components
136
+ * @returns {number} the rounded number
137
+ */
138
+ function roundPart(v) {
139
+ const floor = Math.floor(v);
140
+ return v - floor < 0.5 ? floor : Math.round(v);
141
+ }
142
+
126
143
  // Color supported formats
127
144
  const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
128
145
 
@@ -290,7 +307,7 @@ function getRGBFromName(name) {
290
307
  * @returns {string} - the hexadecimal value
291
308
  */
292
309
  function convertDecimalToHex(d) {
293
- return Math.round(d * 255).toString(16);
310
+ return roundPart(d * 255).toString(16);
294
311
  }
295
312
 
296
313
  /**
@@ -542,9 +559,9 @@ function hsvToRgb(H, S, V) {
542
559
  */
543
560
  function rgbToHex(r, g, b, allow3Char) {
544
561
  const hex = [
545
- pad2(Math.round(r).toString(16)),
546
- pad2(Math.round(g).toString(16)),
547
- pad2(Math.round(b).toString(16)),
562
+ pad2(roundPart(r).toString(16)),
563
+ pad2(roundPart(g).toString(16)),
564
+ pad2(roundPart(b).toString(16)),
548
565
  ];
549
566
 
550
567
  // Return a 3 character hex if possible
@@ -569,9 +586,9 @@ function rgbToHex(r, g, b, allow3Char) {
569
586
  */
570
587
  function rgbaToHex(r, g, b, a, allow4Char) {
571
588
  const hex = [
572
- pad2(Math.round(r).toString(16)),
573
- pad2(Math.round(g).toString(16)),
574
- pad2(Math.round(b).toString(16)),
589
+ pad2(roundPart(r).toString(16)),
590
+ pad2(roundPart(g).toString(16)),
591
+ pad2(roundPart(b).toString(16)),
575
592
  pad2(convertDecimalToHex(a)),
576
593
  ];
577
594
 
@@ -733,6 +750,8 @@ function inputToRGB(input) {
733
750
  let w = null;
734
751
  let b = null;
735
752
  let h = null;
753
+ let r = null;
754
+ let g = null;
736
755
  let ok = false;
737
756
  let format = 'hex';
738
757
 
@@ -743,7 +762,10 @@ function inputToRGB(input) {
743
762
  }
744
763
  if (typeof color === 'object') {
745
764
  if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
746
- rgb = { r: color.r, g: color.g, b: color.b }; // RGB values in [0, 255] range
765
+ ({ r, g, b } = color);
766
+ [r, g, b] = [...[r, g, b]]
767
+ .map((n) => bound01(n, isPercentage(n) ? 100 : 255) * 255).map(roundPart);
768
+ rgb = { r, g, b }; // RGB values now are all in [0, 255] range
747
769
  ok = true;
748
770
  format = 'rgb';
749
771
  } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
@@ -831,14 +853,6 @@ class Color {
831
853
  self.ok = ok;
832
854
  /** @type {CP.ColorFormats} */
833
855
  self.format = configFormat || format;
834
-
835
- // Don't let the range of [0,255] come back in [0,1].
836
- // Potentially lose a little bit of precision here, but will fix issues where
837
- // .5 gets interpreted as half of the total, instead of half of 1
838
- // If it was supposed to be 128, this was already taken care of by `inputToRgb`
839
- if (r < 1) self.r = Math.round(r);
840
- if (g < 1) self.g = Math.round(g);
841
- if (b < 1) self.b = Math.round(b);
842
856
  }
843
857
 
844
858
  /**
@@ -854,7 +868,7 @@ class Color {
854
868
  * @returns {boolean} the query result
855
869
  */
856
870
  get isDark() {
857
- return this.brightness < 128;
871
+ return this.brightness < 120;
858
872
  }
859
873
 
860
874
  /**
@@ -906,13 +920,13 @@ class Color {
906
920
  const {
907
921
  r, g, b, a,
908
922
  } = this;
909
- const [R, G, B] = [r, g, b].map((x) => Math.round(x));
923
+ const [R, G, B] = [r, g, b].map((x) => roundPart(x));
910
924
 
911
925
  return {
912
926
  r: R,
913
927
  g: G,
914
928
  b: B,
915
- a: Math.round(a * 100) / 100,
929
+ a: roundPart(a * 100) / 100,
916
930
  };
917
931
  }
918
932
 
@@ -942,7 +956,7 @@ class Color {
942
956
  const {
943
957
  r, g, b, a,
944
958
  } = this.toRgb();
945
- const A = a === 1 ? '' : ` / ${Math.round(a * 100)}%`;
959
+ const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
946
960
 
947
961
  return `rgb(${r} ${g} ${b}${A})`;
948
962
  }
@@ -1037,10 +1051,10 @@ class Color {
1037
1051
  let {
1038
1052
  h, s, l, a,
1039
1053
  } = this.toHsl();
1040
- h = Math.round(h * 360);
1041
- s = Math.round(s * 100);
1042
- l = Math.round(l * 100);
1043
- a = Math.round(a * 100) / 100;
1054
+ h = roundPart(h * 360);
1055
+ s = roundPart(s * 100);
1056
+ l = roundPart(l * 100);
1057
+ a = roundPart(a * 100) / 100;
1044
1058
 
1045
1059
  return a === 1
1046
1060
  ? `hsl(${h}, ${s}%, ${l}%)`
@@ -1057,11 +1071,11 @@ class Color {
1057
1071
  let {
1058
1072
  h, s, l, a,
1059
1073
  } = this.toHsl();
1060
- h = Math.round(h * 360);
1061
- s = Math.round(s * 100);
1062
- l = Math.round(l * 100);
1063
- a = Math.round(a * 100);
1064
- const A = a < 100 ? ` / ${Math.round(a)}%` : '';
1074
+ h = roundPart(h * 360);
1075
+ s = roundPart(s * 100);
1076
+ l = roundPart(l * 100);
1077
+ a = roundPart(a * 100);
1078
+ const A = a < 100 ? ` / ${roundPart(a)}%` : '';
1065
1079
 
1066
1080
  return `hsl(${h}deg ${s}% ${l}%${A})`;
1067
1081
  }
@@ -1088,11 +1102,11 @@ class Color {
1088
1102
  let {
1089
1103
  h, w, b, a,
1090
1104
  } = this.toHwb();
1091
- h = Math.round(h * 360);
1092
- w = Math.round(w * 100);
1093
- b = Math.round(b * 100);
1094
- a = Math.round(a * 100);
1095
- const A = a < 100 ? ` / ${Math.round(a)}%` : '';
1105
+ h = roundPart(h * 360);
1106
+ w = roundPart(w * 100);
1107
+ b = roundPart(b * 100);
1108
+ a = roundPart(a * 100);
1109
+ const A = a < 100 ? ` / ${roundPart(a)}%` : '';
1096
1110
 
1097
1111
  return `hwb(${h}deg ${w}% ${b}%${A})`;
1098
1112
  }
@@ -1238,6 +1252,7 @@ ObjectAssign(Color, {
1238
1252
  numberInputToObject,
1239
1253
  stringInputToObject,
1240
1254
  inputToRGB,
1255
+ roundPart,
1241
1256
  ObjectAssign,
1242
1257
  });
1243
1258
 
@@ -1866,13 +1881,6 @@ function normalizeValue(value) {
1866
1881
  return value;
1867
1882
  }
1868
1883
 
1869
- /**
1870
- * Shortcut for `Object.keys()` static method.
1871
- * @param {Record<string, any>} obj a target object
1872
- * @returns {string[]}
1873
- */
1874
- const ObjectKeys = (obj) => Object.keys(obj);
1875
-
1876
1884
  /**
1877
1885
  * Shortcut for `String.toLowerCase()`.
1878
1886
  *
@@ -2075,7 +2083,6 @@ function getColorForm(self) {
2075
2083
  max,
2076
2084
  step,
2077
2085
  });
2078
- // }
2079
2086
  colorForm.append(cInputLabel, cInput);
2080
2087
  });
2081
2088
  return colorForm;
@@ -2099,6 +2106,8 @@ const ariaValueMin = 'aria-valuemin';
2099
2106
  */
2100
2107
  const ariaValueMax = 'aria-valuemax';
2101
2108
 
2109
+ const tabIndex = 'tabindex';
2110
+
2102
2111
  /**
2103
2112
  * Returns all color controls for `ColorPicker`.
2104
2113
  *
@@ -2164,10 +2173,8 @@ function getColorControls(self) {
2164
2173
  const {
2165
2174
  i, c, l, min, max,
2166
2175
  } = template;
2167
- // const hidden = i === 2 && format === 'hwb' ? ' v-hidden' : '';
2168
2176
  const control = createElement({
2169
2177
  tagName: 'div',
2170
- // className: `color-control${hidden}`,
2171
2178
  className: 'color-control',
2172
2179
  });
2173
2180
  setAttribute(control, 'role', 'presentation');
@@ -2187,7 +2194,7 @@ function getColorControls(self) {
2187
2194
 
2188
2195
  setAttribute(knob, ariaLabel, l);
2189
2196
  setAttribute(knob, 'role', 'slider');
2190
- setAttribute(knob, 'tabindex', '0');
2197
+ setAttribute(knob, tabIndex, '0');
2191
2198
  setAttribute(knob, ariaValueMin, `${min}`);
2192
2199
  setAttribute(knob, ariaValueMax, `${max}`);
2193
2200
  control.append(knob);
@@ -2197,6 +2204,17 @@ function getColorControls(self) {
2197
2204
  return colorControls;
2198
2205
  }
2199
2206
 
2207
+ /**
2208
+ * Helps setting CSS variables to the color-menu.
2209
+ * @param {HTMLElement} element
2210
+ * @param {Record<string,any>} props
2211
+ */
2212
+ function setCSSProperties(element, props) {
2213
+ ObjectKeys(props).forEach((prop) => {
2214
+ element.style.setProperty(prop, props[prop]);
2215
+ });
2216
+ }
2217
+
2200
2218
  /**
2201
2219
  * @class
2202
2220
  * Returns a color palette with a given set of parameters.
@@ -2209,8 +2227,8 @@ class ColorPalette {
2209
2227
  * The `hue` parameter is optional, which would be set to 0.
2210
2228
  * @param {number[]} args represeinting hue, hueSteps, lightSteps
2211
2229
  * * `args.hue` the starting Hue [0, 360]
2212
- * * `args.hueSteps` Hue Steps Count [5, 13]
2213
- * * `args.lightSteps` Lightness Steps Count [8, 10]
2230
+ * * `args.hueSteps` Hue Steps Count [5, 24]
2231
+ * * `args.lightSteps` Lightness Steps Count [5, 12]
2214
2232
  */
2215
2233
  constructor(...args) {
2216
2234
  let hue = 0;
@@ -2223,24 +2241,32 @@ class ColorPalette {
2223
2241
  } else if (args.length === 2) {
2224
2242
  [hueSteps, lightSteps] = args;
2225
2243
  } else {
2226
- throw TypeError('The ColorPalette requires minimum 2 arguments');
2244
+ throw TypeError('ColorPalette requires minimum 2 arguments');
2227
2245
  }
2228
2246
 
2229
2247
  /** @type {string[]} */
2230
2248
  const colors = [];
2231
2249
 
2232
2250
  const hueStep = 360 / hueSteps;
2233
- const lightStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
2234
- const half = Math.round((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
2251
+ const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
2252
+ const estimatedStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
2253
+
2254
+ let lightStep = 0.25;
2255
+ lightStep = [4, 5].includes(lightSteps) ? 0.2 : lightStep;
2256
+ lightStep = [6, 7].includes(lightSteps) ? 0.15 : lightStep;
2257
+ lightStep = [8, 9].includes(lightSteps) ? 0.11 : lightStep;
2258
+ lightStep = [10, 11].includes(lightSteps) ? 0.09 : lightStep;
2259
+ lightStep = [12, 13].includes(lightSteps) ? 0.075 : lightStep;
2260
+ lightStep = lightSteps > 13 ? estimatedStep : lightStep;
2235
2261
 
2236
2262
  // light tints
2237
- for (let i = 0; i < half; i += 1) {
2238
- lightnessArray = [...lightnessArray, (0.5 + lightStep * (i + 1))];
2263
+ for (let i = 1; i < half + 1; i += 1) {
2264
+ lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
2239
2265
  }
2240
2266
 
2241
2267
  // dark tints
2242
- for (let i = 0; i < lightSteps - half - 1; i += 1) {
2243
- lightnessArray = [(0.5 - lightStep * (i + 1)), ...lightnessArray];
2268
+ for (let i = 1; i < lightSteps - half; i += 1) {
2269
+ lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
2244
2270
  }
2245
2271
 
2246
2272
  // feed `colors` Array
@@ -2275,45 +2301,38 @@ function getColorMenu(self, colorsSource, menuClass) {
2275
2301
  colorsArray = colorsArray instanceof Array ? colorsArray : [];
2276
2302
  const colorsCount = colorsArray.length;
2277
2303
  const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
2278
- let fit = lightSteps
2279
- || Math.max(...[5, 6, 7, 8, 9, 10].filter((x) => colorsCount > (x * 2) && !(colorsCount % x)));
2280
- fit = Number.isFinite(fit) ? fit : 5;
2304
+ const fit = lightSteps || [9, 10].find((x) => colorsCount > x * 2 && !(colorsCount % x)) || 5;
2281
2305
  const isMultiLine = isOptionsMenu && colorsCount > fit;
2282
- let rowCountHover = 1;
2283
- rowCountHover = isMultiLine && colorsCount < 27 ? 2 : rowCountHover;
2284
- rowCountHover = colorsCount >= 27 ? 3 : rowCountHover;
2285
- rowCountHover = colorsCount >= 36 ? 4 : rowCountHover;
2286
- rowCountHover = colorsCount >= 45 ? 5 : rowCountHover;
2287
- const rowCount = rowCountHover - (colorsCount < 27 ? 1 : 2);
2288
- const isScrollable = isMultiLine && colorsCount > rowCountHover * fit;
2306
+ let rowCountHover = 2;
2307
+ rowCountHover = isMultiLine && colorsCount >= fit * 2 ? 3 : rowCountHover;
2308
+ rowCountHover = colorsCount >= fit * 3 ? 4 : rowCountHover;
2309
+ rowCountHover = colorsCount >= fit * 4 ? 5 : rowCountHover;
2310
+ const rowCount = rowCountHover - (colorsCount < fit * 3 ? 1 : 2);
2311
+ const isScrollable = isMultiLine && colorsCount > rowCount * fit;
2289
2312
  let finalClass = menuClass;
2290
2313
  finalClass += isScrollable ? ' scrollable' : '';
2291
2314
  finalClass += isMultiLine ? ' multiline' : '';
2292
2315
  const gap = isMultiLine ? '1px' : '0.25rem';
2293
2316
  let optionSize = isMultiLine ? 1.75 : 2;
2294
- optionSize = !(colorsCount % 10) && isMultiLine ? 1.5 : optionSize;
2317
+ optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
2295
2318
  const menuHeight = `${(rowCount || 1) * optionSize}rem`;
2296
2319
  const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
2297
- const gridTemplateColumns = `repeat(${fit}, ${optionSize}rem)`;
2298
- const gridTemplateRows = `repeat(auto-fill, ${optionSize}rem)`;
2299
2320
 
2300
2321
  const menu = createElement({
2301
2322
  tagName: 'ul',
2302
2323
  className: finalClass,
2303
2324
  });
2304
2325
  setAttribute(menu, 'role', 'listbox');
2305
- setAttribute(menu, ariaLabel, `${menuLabel}`);
2306
-
2307
- if (isOptionsMenu) {
2308
- if (isScrollable) {
2309
- const styleText = 'this.style.height=';
2310
- setAttribute(menu, 'onmouseout', `${styleText}'${menuHeight}'`);
2311
- setAttribute(menu, 'onmouseover', `${styleText}'${menuHeightHover}'`);
2312
- }
2313
- const menuStyle = {
2314
- height: isScrollable ? menuHeight : '', gridTemplateColumns, gridTemplateRows, gap,
2315
- };
2316
- setElementStyle(menu, menuStyle);
2326
+ setAttribute(menu, ariaLabel, menuLabel);
2327
+
2328
+ if (isScrollable) { // @ts-ignore
2329
+ setCSSProperties(menu, {
2330
+ '--grid-item-size': `${optionSize}rem`,
2331
+ '--grid-fit': fit,
2332
+ '--grid-gap': gap,
2333
+ '--grid-height': menuHeight,
2334
+ '--grid-hover-height': menuHeightHover,
2335
+ });
2317
2336
  }
2318
2337
 
2319
2338
  colorsArray.forEach((x) => {
@@ -2328,15 +2347,13 @@ function getColorMenu(self, colorsSource, menuClass) {
2328
2347
  innerText: `${label || x}`,
2329
2348
  });
2330
2349
 
2331
- setAttribute(option, 'tabindex', '0');
2350
+ setAttribute(option, tabIndex, '0');
2332
2351
  setAttribute(option, 'data-value', `${value}`);
2333
2352
  setAttribute(option, 'role', 'option');
2334
2353
  setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
2335
2354
 
2336
2355
  if (isOptionsMenu) {
2337
- setElementStyle(option, {
2338
- width: `${optionSize}rem`, height: `${optionSize}rem`, backgroundColor: x,
2339
- });
2356
+ setElementStyle(option, { backgroundColor: x });
2340
2357
  }
2341
2358
 
2342
2359
  menu.append(option);
@@ -2358,7 +2375,7 @@ function isValidJSON(str) {
2358
2375
  return true;
2359
2376
  }
2360
2377
 
2361
- var version = "0.0.1alpha2";
2378
+ var version = "0.0.1alpha3";
2362
2379
 
2363
2380
  // @ts-ignore
2364
2381
 
@@ -2373,8 +2390,8 @@ const colorPickerDefaults = {
2373
2390
  componentLabels: colorPickerLabels,
2374
2391
  colorLabels: colorNames,
2375
2392
  format: 'rgb',
2376
- colorPresets: undefined,
2377
- colorKeywords: nonColors,
2393
+ colorPresets: false,
2394
+ colorKeywords: false,
2378
2395
  };
2379
2396
 
2380
2397
  // ColorPicker Static Methods
@@ -2463,7 +2480,7 @@ function initCallback(self) {
2463
2480
  tagName: 'button',
2464
2481
  className: 'menu-toggle btn-appearance',
2465
2482
  });
2466
- setAttribute(presetsBtn, 'tabindex', '-1');
2483
+ setAttribute(presetsBtn, tabIndex, '-1');
2467
2484
  setAttribute(presetsBtn, ariaExpanded, 'false');
2468
2485
  setAttribute(presetsBtn, ariaHasPopup, 'true');
2469
2486
 
@@ -2490,7 +2507,7 @@ function initCallback(self) {
2490
2507
  if (colorKeywords && nonColors.includes(colorValue)) {
2491
2508
  self.value = colorValue;
2492
2509
  }
2493
- setAttribute(input, 'tabindex', '-1');
2510
+ setAttribute(input, tabIndex, '-1');
2494
2511
  }
2495
2512
 
2496
2513
  /**
@@ -2591,8 +2608,19 @@ function showDropdown(self, dropdown) {
2591
2608
  addClass(dropdown, 'bottom');
2592
2609
  reflow(dropdown);
2593
2610
  addClass(dropdown, 'show');
2611
+
2594
2612
  if (isPicker) self.update();
2595
- self.show();
2613
+
2614
+ if (!self.isOpen) {
2615
+ toggleEventsOnShown(self, true);
2616
+ self.updateDropdownPosition();
2617
+ self.isOpen = true;
2618
+ setAttribute(self.input, tabIndex, '0');
2619
+ if (menuToggle) {
2620
+ setAttribute(menuToggle, tabIndex, '0');
2621
+ }
2622
+ }
2623
+
2596
2624
  setAttribute(nextBtn, ariaExpanded, 'true');
2597
2625
  if (activeBtn) {
2598
2626
  setAttribute(activeBtn, ariaExpanded, 'false');
@@ -2762,7 +2790,7 @@ class ColorPicker {
2762
2790
  set value(v) { this.input.value = v; }
2763
2791
 
2764
2792
  /** Check if the colour presets include any non-colour. */
2765
- get includeNonColor() {
2793
+ get hasNonColor() {
2766
2794
  return this.colorKeywords instanceof Array
2767
2795
  && this.colorKeywords.some((x) => nonColors.includes(x));
2768
2796
  }
@@ -2818,7 +2846,7 @@ class ColorPicker {
2818
2846
  const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
2819
2847
  const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
2820
2848
  const alpha = 1 - controlPositions.c3y / offsetHeight;
2821
- const roundA = Math.round((alpha * 100)) / 100;
2849
+ const roundA = roundPart((alpha * 100)) / 100;
2822
2850
 
2823
2851
  if (format !== 'hsl') {
2824
2852
  const fill = new Color({
@@ -2836,7 +2864,7 @@ class ColorPicker {
2836
2864
  });
2837
2865
  setElementStyle(v2, { background: hueGradient });
2838
2866
  } else {
2839
- const saturation = Math.round((controlPositions.c2y / offsetHeight) * 100);
2867
+ const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
2840
2868
  const fill0 = new Color({
2841
2869
  r: 255, g: 0, b: 0, a: alpha,
2842
2870
  }).saturate(-saturation).toRgbString();
@@ -2991,12 +3019,12 @@ class ColorPicker {
2991
3019
 
2992
3020
  self.update();
2993
3021
 
2994
- if (currentActive) {
2995
- removeClass(currentActive, 'active');
2996
- removeAttribute(currentActive, ariaSelected);
2997
- }
2998
-
2999
3022
  if (currentActive !== target) {
3023
+ if (currentActive) {
3024
+ removeClass(currentActive, 'active');
3025
+ removeAttribute(currentActive, ariaSelected);
3026
+ }
3027
+
3000
3028
  addClass(target, 'active');
3001
3029
  setAttribute(target, ariaSelected, 'true');
3002
3030
 
@@ -3163,7 +3191,7 @@ class ColorPicker {
3163
3191
  const [v1, v2, v3, v4] = format === 'rgb'
3164
3192
  ? inputs.map((i) => parseFloat(i.value) / (i === i4 ? 100 : 1))
3165
3193
  : inputs.map((i) => parseFloat(i.value) / (i !== i1 ? 100 : 360));
3166
- const isNonColorValue = self.includeNonColor && nonColors.includes(currentValue);
3194
+ const isNonColorValue = self.hasNonColor && nonColors.includes(currentValue);
3167
3195
  const alpha = i4 ? v4 : (1 - controlPositions.c3y / offsetHeight);
3168
3196
 
3169
3197
  if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
@@ -3422,11 +3450,11 @@ class ColorPicker {
3422
3450
  } = componentLabels;
3423
3451
  const { r, g, b } = color.toRgb();
3424
3452
  const [knob1, knob2, knob3] = controlKnobs;
3425
- const hue = Math.round(hsl.h * 360);
3453
+ const hue = roundPart(hsl.h * 360);
3426
3454
  const alpha = color.a;
3427
3455
  const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
3428
- const saturation = Math.round(saturationSource * 100);
3429
- const lightness = Math.round(hsl.l * 100);
3456
+ const saturation = roundPart(saturationSource * 100);
3457
+ const lightness = roundPart(hsl.l * 100);
3430
3458
  const hsvl = hsv.v * 100;
3431
3459
  let colorName;
3432
3460
 
@@ -3473,8 +3501,8 @@ class ColorPicker {
3473
3501
  setAttribute(knob2, ariaValueNow, `${saturation}`);
3474
3502
  } else if (format === 'hwb') {
3475
3503
  const { hwb } = self;
3476
- const whiteness = Math.round(hwb.w * 100);
3477
- const blackness = Math.round(hwb.b * 100);
3504
+ const whiteness = roundPart(hwb.w * 100);
3505
+ const blackness = roundPart(hwb.b * 100);
3478
3506
  colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
3479
3507
  setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3480
3508
  setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
@@ -3490,7 +3518,7 @@ class ColorPicker {
3490
3518
  setAttribute(knob2, ariaValueNow, `${hue}`);
3491
3519
  }
3492
3520
 
3493
- const alphaValue = Math.round(alpha * 100);
3521
+ const alphaValue = roundPart(alpha * 100);
3494
3522
  setAttribute(knob3, ariaValueText, `${alphaValue}%`);
3495
3523
  setAttribute(knob3, ariaValueNow, `${alphaValue}`);
3496
3524
 
@@ -3513,10 +3541,14 @@ class ColorPicker {
3513
3541
  /** Updates the control knobs actual positions. */
3514
3542
  updateControls() {
3515
3543
  const { controlKnobs, controlPositions } = this;
3544
+ const {
3545
+ c1x, c1y, c2y, c3y,
3546
+ } = controlPositions;
3516
3547
  const [control1, control2, control3] = controlKnobs;
3517
- setElementStyle(control1, { transform: `translate3d(${controlPositions.c1x - 4}px,${controlPositions.c1y - 4}px,0)` });
3518
- setElementStyle(control2, { transform: `translate3d(0,${controlPositions.c2y - 4}px,0)` });
3519
- setElementStyle(control3, { transform: `translate3d(0,${controlPositions.c3y - 4}px,0)` });
3548
+
3549
+ setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
3550
+ setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
3551
+ setElementStyle(control3, { transform: `translate3d(0,${c3y - 4}px,0)` });
3520
3552
  }
3521
3553
 
3522
3554
  /**
@@ -3529,16 +3561,16 @@ class ColorPicker {
3529
3561
  value: oldColor, format, inputs, color, hsl,
3530
3562
  } = self;
3531
3563
  const [i1, i2, i3, i4] = inputs;
3532
- const alpha = Math.round(color.a * 100);
3533
- const hue = Math.round(hsl.h * 360);
3564
+ const alpha = roundPart(color.a * 100);
3565
+ const hue = roundPart(hsl.h * 360);
3534
3566
  let newColor;
3535
3567
 
3536
3568
  if (format === 'hex') {
3537
3569
  newColor = self.color.toHexString(true);
3538
3570
  i1.value = self.hex;
3539
3571
  } else if (format === 'hsl') {
3540
- const lightness = Math.round(hsl.l * 100);
3541
- const saturation = Math.round(hsl.s * 100);
3572
+ const lightness = roundPart(hsl.l * 100);
3573
+ const saturation = roundPart(hsl.s * 100);
3542
3574
  newColor = self.color.toHslString();
3543
3575
  i1.value = `${hue}`;
3544
3576
  i2.value = `${saturation}`;
@@ -3546,8 +3578,8 @@ class ColorPicker {
3546
3578
  i4.value = `${alpha}`;
3547
3579
  } else if (format === 'hwb') {
3548
3580
  const { w, b } = self.hwb;
3549
- const whiteness = Math.round(w * 100);
3550
- const blackness = Math.round(b * 100);
3581
+ const whiteness = roundPart(w * 100);
3582
+ const blackness = roundPart(b * 100);
3551
3583
 
3552
3584
  newColor = self.color.toHwbString();
3553
3585
  i1.value = `${hue}`;
@@ -3619,7 +3651,7 @@ class ColorPicker {
3619
3651
  const self = this;
3620
3652
  const { colorPicker } = self;
3621
3653
 
3622
- if (!hasClass(colorPicker, 'show')) {
3654
+ if (!['top', 'bottom'].some((c) => hasClass(colorPicker, c))) {
3623
3655
  showDropdown(self, colorPicker);
3624
3656
  }
3625
3657
  }
@@ -3636,21 +3668,6 @@ class ColorPicker {
3636
3668
  }
3637
3669
  }
3638
3670
 
3639
- /** Shows the `ColorPicker` dropdown or the presets menu. */
3640
- show() {
3641
- const self = this;
3642
- const { menuToggle } = self;
3643
- if (!self.isOpen) {
3644
- toggleEventsOnShown(self, true);
3645
- self.updateDropdownPosition();
3646
- self.isOpen = true;
3647
- setAttribute(self.input, 'tabindex', '0');
3648
- if (menuToggle) {
3649
- setAttribute(menuToggle, 'tabindex', '0');
3650
- }
3651
- }
3652
- }
3653
-
3654
3671
  /**
3655
3672
  * Hides the currently open `ColorPicker` dropdown.
3656
3673
  * @param {boolean=} focusPrevented
@@ -3685,9 +3702,9 @@ class ColorPicker {
3685
3702
  if (!focusPrevented) {
3686
3703
  focus(pickerToggle);
3687
3704
  }
3688
- setAttribute(input, 'tabindex', '-1');
3705
+ setAttribute(input, tabIndex, '-1');
3689
3706
  if (menuToggle) {
3690
- setAttribute(menuToggle, 'tabindex', '-1');
3707
+ setAttribute(menuToggle, tabIndex, '-1');
3691
3708
  }
3692
3709
  }
3693
3710
  }
@@ -3701,7 +3718,10 @@ class ColorPicker {
3701
3718
  [...parent.children].forEach((el) => {
3702
3719
  if (el !== input) el.remove();
3703
3720
  });
3721
+
3722
+ removeAttribute(input, tabIndex);
3704
3723
  setElementStyle(input, { backgroundColor: '' });
3724
+
3705
3725
  ['txt-light', 'txt-dark'].forEach((c) => removeClass(parent, c));
3706
3726
  Data.remove(input, colorPickerString);
3707
3727
  }
@@ -3709,10 +3729,16 @@ class ColorPicker {
3709
3729
 
3710
3730
  ObjectAssign(ColorPicker, {
3711
3731
  Color,
3732
+ ColorPalette,
3712
3733
  Version,
3713
3734
  getInstance: getColorPickerInstance,
3714
3735
  init: initColorPicker,
3715
3736
  selector: colorPickerSelector,
3737
+ // utils important for render
3738
+ roundPart,
3739
+ setElementStyle,
3740
+ setAttribute,
3741
+ getBoundingClientRect,
3716
3742
  });
3717
3743
 
3718
3744
  let CPID = 0;
@@ -3720,8 +3746,9 @@ let CPID = 0;
3720
3746
  /**
3721
3747
  * `ColorPickerElement` Web Component.
3722
3748
  * @example
3723
- * <color-picker>
3724
- * <input type="text">
3749
+ * <label for="UNIQUE_ID">Label</label>
3750
+ * <color-picker data-format="hex" data-value="#075">
3751
+ * <input id="UNIQUE_ID" type="text" class="color-preview btn-appearance">
3725
3752
  * </color-picker>
3726
3753
  */
3727
3754
  class ColorPickerElement extends HTMLElement {
@@ -3802,6 +3829,8 @@ class ColorPickerElement extends HTMLElement {
3802
3829
  ObjectAssign(ColorPickerElement, {
3803
3830
  Color,
3804
3831
  ColorPicker,
3832
+ ColorPalette,
3833
+ getInstance: getColorPickerInstance,
3805
3834
  Version,
3806
3835
  });
3807
3836