@thednp/color-picker 0.0.1 → 0.0.2-alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,13 +1,13 @@
1
1
  /*!
2
- * ColorPickerElement v0.0.1 (http://thednp.github.io/color-picker)
2
+ * ColorPickerElement v0.0.2alpha1 (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
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8
8
  typeof define === 'function' && define.amd ? define(factory) :
9
- (global = global || self, global.ColorPickerElement = factory());
10
- }(this, (function () { 'use strict';
9
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ColorPickerElement = factory());
10
+ })(this, (function () { 'use strict';
11
11
 
12
12
  /**
13
13
  * Returns the `document` or the `#document` element.
@@ -88,14 +88,9 @@
88
88
  const getAttribute = (element, attribute) => element.getAttribute(attribute);
89
89
 
90
90
  /**
91
- * Returns the `document.head` or the `<head>` element.
92
- *
93
- * @param {(Node | HTMLElement | Element | globalThis)=} node
94
- * @returns {HTMLElement | HTMLHeadElement}
91
+ * A global namespace for `document.head`.
95
92
  */
96
- function getDocumentHead(node) {
97
- return getDocument(node).head;
98
- }
93
+ const { head: documentHead } = document;
99
94
 
100
95
  /**
101
96
  * Shortcut for `window.getComputedStyle(element).propertyName`
@@ -116,20 +111,21 @@
116
111
  return property in computedStyle ? computedStyle[property] : '';
117
112
  }
118
113
 
119
- /**
120
- * Shortcut for `Object.keys()` static method.
121
- * @param {Record<string, any>} obj a target object
122
- * @returns {string[]}
123
- */
124
- const ObjectKeys = (obj) => Object.keys(obj);
125
-
126
114
  /**
127
115
  * Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
128
116
  * @param {HTMLElement | Element} element target element
129
117
  * @param {Partial<CSSStyleDeclaration>} styles attribute value
130
118
  */
131
119
  // @ts-ignore
132
- const setElementStyle = (element, styles) => ObjectAssign(element.style, styles);
120
+ const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
121
+
122
+ /**
123
+ * Shortcut for `String.toLowerCase()`.
124
+ *
125
+ * @param {string} source input string
126
+ * @returns {string} lowercase output string
127
+ */
128
+ const toLowerCase = (source) => source.toLowerCase();
133
129
 
134
130
  /**
135
131
  * A list of explicit default non-color values.
@@ -147,7 +143,7 @@
147
143
  }
148
144
 
149
145
  // Color supported formats
150
- const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
146
+ const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
151
147
 
152
148
  // Hue angles
153
149
  const ANGLES = 'deg|rad|grad|turn';
@@ -169,10 +165,17 @@
169
165
  // Add angles to the mix
170
166
  const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
171
167
 
168
+ // Start & end
169
+ const START_MATCH = '(?:[\\s|\\(\\s|\\s\\(\\s]+)?';
170
+ const END_MATCH = '(?:[\\s|\\)\\s]+)?';
171
+ // Components separation
172
+ const SEP = '(?:[,|\\s]+)';
173
+ const SEP2 = '(?:[,|\\/\\s]*)?';
174
+
172
175
  // Actual matching.
173
176
  // Parentheses and commas are optional, but not required.
174
177
  // Whitespace can take the place of commas or opening paren
175
- const PERMISSIVE_MATCH = `[\\s|\\(]+(${CSS_UNIT2})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s|\\/\\s]*(${CSS_UNIT})?\\s*\\)?`;
178
+ const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
176
179
 
177
180
  const matchers = {
178
181
  CSS_UNIT: new RegExp(CSS_UNIT2),
@@ -205,23 +208,22 @@
205
208
  return `${n}`.includes('%');
206
209
  }
207
210
 
208
- /**
209
- * Check to see if string passed in is an angle
210
- * @param {string} n testing string
211
- * @returns {boolean} the query result
212
- */
213
- function isAngle(n) {
214
- return ANGLES.split('|').some((a) => `${n}`.includes(a));
215
- }
216
-
217
211
  /**
218
212
  * Check to see if string passed is a web safe colour.
213
+ * @see https://stackoverflow.com/a/16994164
219
214
  * @param {string} color a colour name, EG: *red*
220
215
  * @returns {boolean} the query result
221
216
  */
222
217
  function isColorName(color) {
223
- return !['#', ...COLOR_FORMAT].some((s) => color.includes(s))
224
- && !/[0-9]/.test(color);
218
+ if (nonColors.includes(color)
219
+ || ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
220
+
221
+ return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
222
+ setElementStyle(documentHead, { color });
223
+ const computedColor = getElementStyle(documentHead, 'color');
224
+ setElementStyle(documentHead, { color: '' });
225
+ return computedColor !== c;
226
+ });
225
227
  }
226
228
 
227
229
  /**
@@ -242,15 +244,15 @@
242
244
  */
243
245
  function bound01(N, max) {
244
246
  let n = N;
245
- if (isOnePointZero(n)) n = '100%';
247
+ if (isOnePointZero(N)) n = '100%';
246
248
 
247
- n = max === 360 ? n : Math.min(max, Math.max(0, parseFloat(n)));
248
-
249
- // Handle hue angles
250
- if (isAngle(N)) n = N.replace(new RegExp(ANGLES), '');
249
+ const processPercent = isPercentage(n);
250
+ n = max === 360
251
+ ? parseFloat(n)
252
+ : Math.min(max, Math.max(0, parseFloat(n)));
251
253
 
252
254
  // Automatically convert percentage into number
253
- if (isPercentage(n)) n = parseInt(String(n * max), 10) / 100;
255
+ if (processPercent) n = (n * max) / 100;
254
256
 
255
257
  // Handle floating point rounding errors
256
258
  if (Math.abs(n - max) < 0.000001) {
@@ -261,11 +263,11 @@
261
263
  // If n is a hue given in degrees,
262
264
  // wrap around out-of-range values into [0, 360] range
263
265
  // then convert into [0, 1].
264
- n = (n < 0 ? (n % max) + max : n % max) / parseFloat(String(max));
266
+ n = (n < 0 ? (n % max) + max : n % max) / max;
265
267
  } else {
266
268
  // If n not a hue given in degrees
267
269
  // Convert into [0, 1] range if it isn't already.
268
- n = (n % max) / parseFloat(String(max));
270
+ n = (n % max) / max;
269
271
  }
270
272
  return n;
271
273
  }
@@ -300,7 +302,6 @@
300
302
  * @returns {string}
301
303
  */
302
304
  function getRGBFromName(name) {
303
- const documentHead = getDocumentHead();
304
305
  setElementStyle(documentHead, { color: name });
305
306
  const colorName = getElementStyle(documentHead, 'color');
306
307
  setElementStyle(documentHead, { color: '' });
@@ -399,6 +400,36 @@
399
400
  return p;
400
401
  }
401
402
 
403
+ /**
404
+ * Converts an HSL colour value to RGB.
405
+ *
406
+ * @param {number} h Hue Angle [0, 1]
407
+ * @param {number} s Saturation [0, 1]
408
+ * @param {number} l Lightness Angle [0, 1]
409
+ * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
410
+ */
411
+ function hslToRgb(h, s, l) {
412
+ let r = 0;
413
+ let g = 0;
414
+ let b = 0;
415
+
416
+ if (s === 0) {
417
+ // achromatic
418
+ g = l;
419
+ b = l;
420
+ r = l;
421
+ } else {
422
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
423
+ const p = 2 * l - q;
424
+ r = hueToRgb(p, q, h + 1 / 3);
425
+ g = hueToRgb(p, q, h);
426
+ b = hueToRgb(p, q, h - 1 / 3);
427
+ }
428
+ [r, g, b] = [r, g, b].map((x) => x * 255);
429
+
430
+ return { r, g, b };
431
+ }
432
+
402
433
  /**
403
434
  * Returns an HWB colour object from an RGB colour object.
404
435
  * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
@@ -461,36 +492,6 @@
461
492
  return { r, g, b };
462
493
  }
463
494
 
464
- /**
465
- * Converts an HSL colour value to RGB.
466
- *
467
- * @param {number} h Hue Angle [0, 1]
468
- * @param {number} s Saturation [0, 1]
469
- * @param {number} l Lightness Angle [0, 1]
470
- * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
471
- */
472
- function hslToRgb(h, s, l) {
473
- let r = 0;
474
- let g = 0;
475
- let b = 0;
476
-
477
- if (s === 0) {
478
- // achromatic
479
- g = l;
480
- b = l;
481
- r = l;
482
- } else {
483
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
484
- const p = 2 * l - q;
485
- r = hueToRgb(p, q, h + 1 / 3);
486
- g = hueToRgb(p, q, h);
487
- b = hueToRgb(p, q, h - 1 / 3);
488
- }
489
- [r, g, b] = [r, g, b].map((x) => x * 255);
490
-
491
- return { r, g, b };
492
- }
493
-
494
495
  /**
495
496
  * Converts an RGB colour value to HSV.
496
497
  *
@@ -546,10 +547,11 @@
546
547
  const q = v * (1 - f * s);
547
548
  const t = v * (1 - (1 - f) * s);
548
549
  const mod = i % 6;
549
- const r = [v, q, p, p, t, v][mod];
550
- const g = [t, v, v, q, p, p][mod];
551
- const b = [p, p, t, v, v, q][mod];
552
- return { r: r * 255, g: g * 255, b: b * 255 };
550
+ let r = [v, q, p, p, t, v][mod];
551
+ let g = [t, v, v, q, p, p][mod];
552
+ let b = [p, p, t, v, v, q][mod];
553
+ [r, g, b] = [r, g, b].map((n) => n * 255);
554
+ return { r, g, b };
553
555
  }
554
556
 
555
557
  /**
@@ -573,7 +575,7 @@
573
575
  // Return a 3 character hex if possible
574
576
  if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
575
577
  && hex[1].charAt(0) === hex[1].charAt(1)
576
- && hex[2].charAt(0) === hex[2].charAt(1)) {
578
+ && hex[2].charAt(0) === hex[2].charAt(1)) {
577
579
  return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
578
580
  }
579
581
 
@@ -601,39 +603,24 @@
601
603
  // Return a 4 character hex if possible
602
604
  if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
603
605
  && hex[1].charAt(0) === hex[1].charAt(1)
604
- && hex[2].charAt(0) === hex[2].charAt(1)
605
- && hex[3].charAt(0) === hex[3].charAt(1)) {
606
+ && hex[2].charAt(0) === hex[2].charAt(1)
607
+ && hex[3].charAt(0) === hex[3].charAt(1)) {
606
608
  return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
607
609
  }
608
610
  return hex.join('');
609
611
  }
610
612
 
611
- /**
612
- * Returns a colour object corresponding to a given number.
613
- * @param {number} color input number
614
- * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
615
- */
616
- function numberInputToObject(color) {
617
- /* eslint-disable no-bitwise */
618
- return {
619
- r: color >> 16,
620
- g: (color & 0xff00) >> 8,
621
- b: color & 0xff,
622
- };
623
- /* eslint-enable no-bitwise */
624
- }
625
-
626
613
  /**
627
614
  * Permissive string parsing. Take in a number of formats, and output an object
628
615
  * based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
629
616
  * @param {string} input colour value in any format
630
- * @returns {Record<string, (number | string)> | false} an object matching the RegExp
617
+ * @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
631
618
  */
632
619
  function stringInputToObject(input) {
633
- let color = input.trim().toLowerCase();
620
+ let color = toLowerCase(input.trim());
634
621
  if (color.length === 0) {
635
622
  return {
636
- r: 0, g: 0, b: 0, a: 0,
623
+ r: 0, g: 0, b: 0, a: 1,
637
624
  };
638
625
  }
639
626
  let named = false;
@@ -641,11 +628,9 @@
641
628
  color = getRGBFromName(color);
642
629
  named = true;
643
630
  } else if (nonColors.includes(color)) {
644
- const isTransparent = color === 'transparent';
645
- const rgb = isTransparent ? 0 : 255;
646
- const a = isTransparent ? 0 : 1;
631
+ const a = color === 'transparent' ? 0 : 1;
647
632
  return {
648
- r: rgb, g: rgb, b: rgb, a, format: 'rgb',
633
+ r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
649
634
  };
650
635
  }
651
636
 
@@ -685,7 +670,6 @@
685
670
  g: parseIntFromHex(m2),
686
671
  b: parseIntFromHex(m3),
687
672
  a: convertHexToDecimal(m4),
688
- // format: named ? 'rgb' : 'hex8',
689
673
  format: named ? 'rgb' : 'hex',
690
674
  };
691
675
  }
@@ -749,6 +733,7 @@
749
733
  function inputToRGB(input) {
750
734
  let rgb = { r: 0, g: 0, b: 0 };
751
735
  let color = input;
736
+ /** @type {string | number} */
752
737
  let a = 1;
753
738
  let s = null;
754
739
  let v = null;
@@ -759,7 +744,8 @@
759
744
  let r = null;
760
745
  let g = null;
761
746
  let ok = false;
762
- let format = 'hex';
747
+ const inputFormat = typeof color === 'object' && color.format;
748
+ let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
763
749
 
764
750
  if (typeof input === 'string') {
765
751
  // @ts-ignore -- this now is converted to object
@@ -800,14 +786,17 @@
800
786
  format = 'hwb';
801
787
  }
802
788
  if (isValidCSSUnit(color.a)) {
803
- a = color.a;
804
- a = isPercentage(`${a}`) ? bound01(a, 100) : a;
789
+ a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
790
+ a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
805
791
  }
806
792
  }
793
+ if (typeof color === 'undefined') {
794
+ ok = true;
795
+ }
807
796
 
808
797
  return {
809
- ok, // @ts-ignore
810
- format: color.format || format,
798
+ ok,
799
+ format,
811
800
  r: Math.min(255, Math.max(rgb.r, 0)),
812
801
  g: Math.min(255, Math.max(rgb.g, 0)),
813
802
  b: Math.min(255, Math.max(rgb.b, 0)),
@@ -836,7 +825,8 @@
836
825
  color = inputToRGB(color);
837
826
  }
838
827
  if (typeof color === 'number') {
839
- color = numberInputToObject(color);
828
+ const len = `${color}`.length;
829
+ color = `#${(len === 2 ? '0' : '00')}${color}`;
840
830
  }
841
831
  const {
842
832
  r, g, b, a, ok, format,
@@ -846,7 +836,7 @@
846
836
  const self = this;
847
837
 
848
838
  /** @type {CP.ColorInput} */
849
- self.originalInput = color;
839
+ self.originalInput = input;
850
840
  /** @type {number} */
851
841
  self.r = r;
852
842
  /** @type {number} */
@@ -1236,6 +1226,7 @@
1236
1226
  isOnePointZero,
1237
1227
  isPercentage,
1238
1228
  isValidCSSUnit,
1229
+ isColorName,
1239
1230
  pad2,
1240
1231
  clamp01,
1241
1232
  bound01,
@@ -1253,10 +1244,11 @@
1253
1244
  hueToRgb,
1254
1245
  hwbToRgb,
1255
1246
  parseIntFromHex,
1256
- numberInputToObject,
1257
1247
  stringInputToObject,
1258
1248
  inputToRGB,
1259
1249
  roundPart,
1250
+ getElementStyle,
1251
+ setElementStyle,
1260
1252
  ObjectAssign,
1261
1253
  });
1262
1254
 
@@ -1386,24 +1378,6 @@
1386
1378
  */
1387
1379
  const ariaValueNow = 'aria-valuenow';
1388
1380
 
1389
- /**
1390
- * A global namespace for aria-haspopup.
1391
- * @type {string}
1392
- */
1393
- const ariaHasPopup = 'aria-haspopup';
1394
-
1395
- /**
1396
- * A global namespace for aria-hidden.
1397
- * @type {string}
1398
- */
1399
- const ariaHidden = 'aria-hidden';
1400
-
1401
- /**
1402
- * A global namespace for aria-labelledby.
1403
- * @type {string}
1404
- */
1405
- const ariaLabelledBy = 'aria-labelledby';
1406
-
1407
1381
  /**
1408
1382
  * A global namespace for `ArrowDown` key.
1409
1383
  * @type {string} e.which = 40 equivalent
@@ -1530,37 +1504,6 @@
1530
1504
  */
1531
1505
  const focusoutEvent = 'focusout';
1532
1506
 
1533
- // @ts-ignore
1534
- const { userAgentData: uaDATA } = navigator;
1535
-
1536
- /**
1537
- * A global namespace for `userAgentData` object.
1538
- */
1539
- const userAgentData = uaDATA;
1540
-
1541
- const { userAgent: userAgentString } = navigator;
1542
-
1543
- /**
1544
- * A global namespace for `navigator.userAgent` string.
1545
- */
1546
- const userAgent = userAgentString;
1547
-
1548
- const mobileBrands = /iPhone|iPad|iPod|Android/i;
1549
- let isMobileCheck = false;
1550
-
1551
- if (userAgentData) {
1552
- isMobileCheck = userAgentData.brands
1553
- .some((/** @type {Record<String, any>} */x) => mobileBrands.test(x.brand));
1554
- } else {
1555
- isMobileCheck = mobileBrands.test(userAgent);
1556
- }
1557
-
1558
- /**
1559
- * A global `boolean` for mobile detection.
1560
- * @type {boolean}
1561
- */
1562
- const isMobile = isMobileCheck;
1563
-
1564
1507
  /**
1565
1508
  * Returns the `document.documentElement` or the `<html>` element.
1566
1509
  *
@@ -1745,30 +1688,6 @@
1745
1688
  return lookUp.getElementsByClassName(selector);
1746
1689
  }
1747
1690
 
1748
- /**
1749
- * This is a shortie for `document.createElementNS` method
1750
- * which allows you to create a new `HTMLElement` for a given `tagName`
1751
- * or based on an object with specific non-readonly attributes:
1752
- * `id`, `className`, `textContent`, `style`, etc.
1753
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
1754
- *
1755
- * @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
1756
- * @param {Record<string, string> | string} param `tagName` or object
1757
- * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
1758
- */
1759
- function createElementNS(namespace, param) {
1760
- if (typeof param === 'string') {
1761
- return getDocument().createElementNS(namespace, param);
1762
- }
1763
-
1764
- const { tagName } = param;
1765
- const attr = { ...param };
1766
- const newElement = createElementNS(namespace, tagName);
1767
- delete attr.tagName;
1768
- ObjectAssign(newElement, attr);
1769
- return newElement;
1770
- }
1771
-
1772
1691
  /**
1773
1692
  * Shortcut for the `Element.dispatchEvent(Event)` method.
1774
1693
  *
@@ -1886,12 +1805,11 @@
1886
1805
  }
1887
1806
 
1888
1807
  /**
1889
- * Shortcut for `String.toLowerCase()`.
1890
- *
1891
- * @param {string} source input string
1892
- * @returns {string} lowercase output string
1808
+ * Shortcut for `Object.keys()` static method.
1809
+ * @param {Record<string, any>} obj a target object
1810
+ * @returns {string[]}
1893
1811
  */
1894
- const toLowerCase = (source) => source.toLowerCase();
1812
+ const ObjectKeys = (obj) => Object.keys(obj);
1895
1813
 
1896
1814
  /**
1897
1815
  * Utility to normalize component options.
@@ -1996,6 +1914,80 @@
1996
1914
  */
1997
1915
  const removeAttribute = (element, attribute) => element.removeAttribute(attribute);
1998
1916
 
1917
+ /**
1918
+ * @class
1919
+ * Returns a color palette with a given set of parameters.
1920
+ * @example
1921
+ * new ColorPalette(0, 12, 10);
1922
+ * // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: Array<Color> }
1923
+ */
1924
+ class ColorPalette {
1925
+ /**
1926
+ * The `hue` parameter is optional, which would be set to 0.
1927
+ * @param {number[]} args represeinting hue, hueSteps, lightSteps
1928
+ * * `args.hue` the starting Hue [0, 360]
1929
+ * * `args.hueSteps` Hue Steps Count [5, 24]
1930
+ * * `args.lightSteps` Lightness Steps Count [5, 12]
1931
+ */
1932
+ constructor(...args) {
1933
+ let hue = 0;
1934
+ let hueSteps = 12;
1935
+ let lightSteps = 10;
1936
+ let lightnessArray = [0.5];
1937
+
1938
+ if (args.length === 3) {
1939
+ [hue, hueSteps, lightSteps] = args;
1940
+ } else if (args.length === 2) {
1941
+ [hueSteps, lightSteps] = args;
1942
+ if ([hueSteps, lightSteps].some((n) => n < 1)) {
1943
+ throw TypeError('ColorPalette: when 2 arguments used, both must be larger than 0.');
1944
+ }
1945
+ } else {
1946
+ throw TypeError('ColorPalette requires minimum 2 arguments');
1947
+ }
1948
+
1949
+ /** @type {Color[]} */
1950
+ const colors = [];
1951
+
1952
+ const hueStep = 360 / hueSteps;
1953
+ const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
1954
+ const estimatedStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
1955
+
1956
+ let lightStep = 0.25;
1957
+ lightStep = [4, 5].includes(lightSteps) ? 0.2 : lightStep;
1958
+ lightStep = [6, 7].includes(lightSteps) ? 0.15 : lightStep;
1959
+ lightStep = [8, 9].includes(lightSteps) ? 0.11 : lightStep;
1960
+ lightStep = [10, 11].includes(lightSteps) ? 0.09 : lightStep;
1961
+ lightStep = [12, 13].includes(lightSteps) ? 0.075 : lightStep;
1962
+ lightStep = lightSteps > 13 ? estimatedStep : lightStep;
1963
+
1964
+ // light tints
1965
+ for (let i = 1; i < half + 1; i += 1) {
1966
+ lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
1967
+ }
1968
+
1969
+ // dark tints
1970
+ for (let i = 1; i < lightSteps - half; i += 1) {
1971
+ lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
1972
+ }
1973
+
1974
+ // feed `colors` Array
1975
+ for (let i = 0; i < hueSteps; i += 1) {
1976
+ const currentHue = ((hue + i * hueStep) % 360) / 360;
1977
+ lightnessArray.forEach((l) => {
1978
+ colors.push(new Color({ h: currentHue, s: 1, l }));
1979
+ });
1980
+ }
1981
+
1982
+ this.hue = hue;
1983
+ this.hueSteps = hueSteps;
1984
+ this.lightSteps = lightSteps;
1985
+ this.colors = colors;
1986
+ }
1987
+ }
1988
+
1989
+ ObjectAssign(ColorPalette, { Color });
1990
+
1999
1991
  /** @type {Record<string, string>} */
2000
1992
  const colorPickerLabels = {
2001
1993
  pickerLabel: 'Colour Picker',
@@ -2023,6 +2015,22 @@
2023
2015
  */
2024
2016
  const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
2025
2017
 
2018
+ const tabIndex = 'tabindex';
2019
+
2020
+ /**
2021
+ * Check if a string is valid JSON string.
2022
+ * @param {string} str the string input
2023
+ * @returns {boolean} the query result
2024
+ */
2025
+ function isValidJSON(str) {
2026
+ try {
2027
+ JSON.parse(str);
2028
+ } catch (e) {
2029
+ return false;
2030
+ }
2031
+ return true;
2032
+ }
2033
+
2026
2034
  /**
2027
2035
  * Shortcut for `String.toUpperCase()`.
2028
2036
  *
@@ -2031,6 +2039,48 @@
2031
2039
  */
2032
2040
  const toUpperCase = (source) => source.toUpperCase();
2033
2041
 
2042
+ /**
2043
+ * A global namespace for aria-haspopup.
2044
+ * @type {string}
2045
+ */
2046
+ const ariaHasPopup = 'aria-haspopup';
2047
+
2048
+ /**
2049
+ * A global namespace for aria-hidden.
2050
+ * @type {string}
2051
+ */
2052
+ const ariaHidden = 'aria-hidden';
2053
+
2054
+ /**
2055
+ * A global namespace for aria-labelledby.
2056
+ * @type {string}
2057
+ */
2058
+ const ariaLabelledBy = 'aria-labelledby';
2059
+
2060
+ /**
2061
+ * This is a shortie for `document.createElementNS` method
2062
+ * which allows you to create a new `HTMLElement` for a given `tagName`
2063
+ * or based on an object with specific non-readonly attributes:
2064
+ * `id`, `className`, `textContent`, `style`, etc.
2065
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
2066
+ *
2067
+ * @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
2068
+ * @param {Record<string, string> | string} param `tagName` or object
2069
+ * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
2070
+ */
2071
+ function createElementNS(namespace, param) {
2072
+ if (typeof param === 'string') {
2073
+ return getDocument().createElementNS(namespace, param);
2074
+ }
2075
+
2076
+ const { tagName } = param;
2077
+ const attr = { ...param };
2078
+ const newElement = createElementNS(namespace, tagName);
2079
+ delete attr.tagName;
2080
+ ObjectAssign(newElement, attr);
2081
+ return newElement;
2082
+ }
2083
+
2034
2084
  const vHidden = 'v-hidden';
2035
2085
 
2036
2086
  /**
@@ -2110,8 +2160,6 @@
2110
2160
  */
2111
2161
  const ariaValueMax = 'aria-valuemax';
2112
2162
 
2113
- const tabIndex = 'tabindex';
2114
-
2115
2163
  /**
2116
2164
  * Returns all color controls for `ColorPicker`.
2117
2165
  *
@@ -2219,75 +2267,6 @@
2219
2267
  });
2220
2268
  }
2221
2269
 
2222
- /**
2223
- * @class
2224
- * Returns a color palette with a given set of parameters.
2225
- * @example
2226
- * new ColorPalette(0, 12, 10);
2227
- * // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: array }
2228
- */
2229
- class ColorPalette {
2230
- /**
2231
- * The `hue` parameter is optional, which would be set to 0.
2232
- * @param {number[]} args represeinting hue, hueSteps, lightSteps
2233
- * * `args.hue` the starting Hue [0, 360]
2234
- * * `args.hueSteps` Hue Steps Count [5, 24]
2235
- * * `args.lightSteps` Lightness Steps Count [5, 12]
2236
- */
2237
- constructor(...args) {
2238
- let hue = 0;
2239
- let hueSteps = 12;
2240
- let lightSteps = 10;
2241
- let lightnessArray = [0.5];
2242
-
2243
- if (args.length === 3) {
2244
- [hue, hueSteps, lightSteps] = args;
2245
- } else if (args.length === 2) {
2246
- [hueSteps, lightSteps] = args;
2247
- } else {
2248
- throw TypeError('ColorPalette requires minimum 2 arguments');
2249
- }
2250
-
2251
- /** @type {string[]} */
2252
- const colors = [];
2253
-
2254
- const hueStep = 360 / hueSteps;
2255
- const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
2256
- const estimatedStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
2257
-
2258
- let lightStep = 0.25;
2259
- lightStep = [4, 5].includes(lightSteps) ? 0.2 : lightStep;
2260
- lightStep = [6, 7].includes(lightSteps) ? 0.15 : lightStep;
2261
- lightStep = [8, 9].includes(lightSteps) ? 0.11 : lightStep;
2262
- lightStep = [10, 11].includes(lightSteps) ? 0.09 : lightStep;
2263
- lightStep = [12, 13].includes(lightSteps) ? 0.075 : lightStep;
2264
- lightStep = lightSteps > 13 ? estimatedStep : lightStep;
2265
-
2266
- // light tints
2267
- for (let i = 1; i < half + 1; i += 1) {
2268
- lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
2269
- }
2270
-
2271
- // dark tints
2272
- for (let i = 1; i < lightSteps - half; i += 1) {
2273
- lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
2274
- }
2275
-
2276
- // feed `colors` Array
2277
- for (let i = 0; i < hueSteps; i += 1) {
2278
- const currentHue = ((hue + i * hueStep) % 360) / 360;
2279
- lightnessArray.forEach((l) => {
2280
- colors.push(new Color({ h: currentHue, s: 1, l }).toHexString());
2281
- });
2282
- }
2283
-
2284
- this.hue = hue;
2285
- this.hueSteps = hueSteps;
2286
- this.lightSteps = lightSteps;
2287
- this.colors = colors;
2288
- }
2289
- }
2290
-
2291
2270
  /**
2292
2271
  * Returns a color-defaults with given values and class.
2293
2272
  * @param {CP.ColorPicker} self
@@ -2321,7 +2300,8 @@
2321
2300
  optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
2322
2301
  const menuHeight = `${(rowCount || 1) * optionSize}rem`;
2323
2302
  const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
2324
-
2303
+ /** @type {HTMLUListElement} */
2304
+ // @ts-ignore -- <UL> is an `HTMLElement`
2325
2305
  const menu = createElement({
2326
2306
  tagName: 'ul',
2327
2307
  className: finalClass,
@@ -2329,7 +2309,7 @@
2329
2309
  setAttribute(menu, 'role', 'listbox');
2330
2310
  setAttribute(menu, ariaLabel, menuLabel);
2331
2311
 
2332
- if (isScrollable) { // @ts-ignore
2312
+ if (isScrollable) {
2333
2313
  setCSSProperties(menu, {
2334
2314
  '--grid-item-size': `${optionSize}rem`,
2335
2315
  '--grid-fit': fit,
@@ -2340,15 +2320,19 @@
2340
2320
  }
2341
2321
 
2342
2322
  colorsArray.forEach((x) => {
2343
- const [value, label] = x.trim().split(':');
2344
- const xRealColor = new Color(value, format).toString();
2345
- const isActive = xRealColor === getAttribute(input, 'value');
2323
+ let [value, label] = typeof x === 'string' ? x.trim().split(':') : [];
2324
+ if (x instanceof Color) {
2325
+ value = x.toHexString();
2326
+ label = value;
2327
+ }
2328
+ const color = new Color(x instanceof Color ? x : value, format);
2329
+ const isActive = color.toString() === getAttribute(input, 'value');
2346
2330
  const active = isActive ? ' active' : '';
2347
2331
 
2348
2332
  const option = createElement({
2349
2333
  tagName: 'li',
2350
2334
  className: `color-option${active}`,
2351
- innerText: `${label || x}`,
2335
+ innerText: `${label || value}`,
2352
2336
  });
2353
2337
 
2354
2338
  setAttribute(option, tabIndex, '0');
@@ -2357,7 +2341,7 @@
2357
2341
  setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
2358
2342
 
2359
2343
  if (isOptionsMenu) {
2360
- setElementStyle(option, { backgroundColor: x });
2344
+ setElementStyle(option, { backgroundColor: value });
2361
2345
  }
2362
2346
 
2363
2347
  menu.append(option);
@@ -2366,55 +2350,10 @@
2366
2350
  }
2367
2351
 
2368
2352
  /**
2369
- * Check if a string is valid JSON string.
2370
- * @param {string} str the string input
2371
- * @returns {boolean} the query result
2372
- */
2373
- function isValidJSON(str) {
2374
- try {
2375
- JSON.parse(str);
2376
- } catch (e) {
2377
- return false;
2378
- }
2379
- return true;
2380
- }
2381
-
2382
- var version = "0.0.1";
2383
-
2384
- // @ts-ignore
2385
-
2386
- const Version = version;
2387
-
2388
- // ColorPicker GC
2389
- // ==============
2390
- const colorPickerString = 'color-picker';
2391
- const colorPickerSelector = `[data-function="${colorPickerString}"]`;
2392
- const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
2393
- const colorPickerDefaults = {
2394
- componentLabels: colorPickerLabels,
2395
- colorLabels: colorNames,
2396
- format: 'rgb',
2397
- colorPresets: false,
2398
- colorKeywords: false,
2399
- };
2400
-
2401
- // ColorPicker Static Methods
2402
- // ==========================
2403
-
2404
- /** @type {CP.GetInstance<ColorPicker>} */
2405
- const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
2406
-
2407
- /** @type {CP.InitCallback<ColorPicker>} */
2408
- const initColorPicker = (element) => new ColorPicker(element);
2409
-
2410
- // ColorPicker Private Methods
2411
- // ===========================
2412
-
2413
- /**
2414
- * Generate HTML markup and update instance properties.
2415
- * @param {ColorPicker} self
2416
- */
2417
- function initCallback(self) {
2353
+ * Generate HTML markup and update instance properties.
2354
+ * @param {CP.ColorPicker} self
2355
+ */
2356
+ function setMarkup(self) {
2418
2357
  const {
2419
2358
  input, parent, format, id, componentLabels, colorKeywords, colorPresets,
2420
2359
  } = self;
@@ -2429,9 +2368,7 @@
2429
2368
  self.color = new Color(color, format);
2430
2369
 
2431
2370
  // set initial controls dimensions
2432
- // make the controls smaller on mobile
2433
- const dropClass = isMobile ? ' mobile' : '';
2434
- const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
2371
+ const formatString = format === 'hex' ? hexLabel : toUpperCase(format);
2435
2372
 
2436
2373
  const pickerBtn = createElement({
2437
2374
  id: `picker-btn-${id}`,
@@ -2448,7 +2385,7 @@
2448
2385
 
2449
2386
  const pickerDropdown = createElement({
2450
2387
  tagName: 'div',
2451
- className: `color-dropdown picker${dropClass}`,
2388
+ className: 'color-dropdown picker',
2452
2389
  });
2453
2390
  setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
2454
2391
  setAttribute(pickerDropdown, 'role', 'group');
@@ -2464,7 +2401,7 @@
2464
2401
  if (colorKeywords || colorPresets) {
2465
2402
  const presetsDropdown = createElement({
2466
2403
  tagName: 'div',
2467
- className: `color-dropdown scrollable menu${dropClass}`,
2404
+ className: 'color-dropdown scrollable menu',
2468
2405
  });
2469
2406
 
2470
2407
  // color presets
@@ -2514,6 +2451,37 @@
2514
2451
  setAttribute(input, tabIndex, '-1');
2515
2452
  }
2516
2453
 
2454
+ var version = "0.0.2alpha1";
2455
+
2456
+ // @ts-ignore
2457
+
2458
+ const Version = version;
2459
+
2460
+ // ColorPicker GC
2461
+ // ==============
2462
+ const colorPickerString = 'color-picker';
2463
+ const colorPickerSelector = `[data-function="${colorPickerString}"]`;
2464
+ const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
2465
+ const colorPickerDefaults = {
2466
+ componentLabels: colorPickerLabels,
2467
+ colorLabels: colorNames,
2468
+ format: 'rgb',
2469
+ colorPresets: false,
2470
+ colorKeywords: false,
2471
+ };
2472
+
2473
+ // ColorPicker Static Methods
2474
+ // ==========================
2475
+
2476
+ /** @type {CP.GetInstance<ColorPicker>} */
2477
+ const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
2478
+
2479
+ /** @type {CP.InitCallback<ColorPicker>} */
2480
+ const initColorPicker = (element) => new ColorPicker(element);
2481
+
2482
+ // ColorPicker Private Methods
2483
+ // ===========================
2484
+
2517
2485
  /**
2518
2486
  * Add / remove `ColorPicker` main event listeners.
2519
2487
  * @param {ColorPicker} self
@@ -2747,7 +2715,7 @@
2747
2715
  self.handleKnobs = self.handleKnobs.bind(self);
2748
2716
 
2749
2717
  // generate markup
2750
- initCallback(self);
2718
+ setMarkup(self);
2751
2719
 
2752
2720
  const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
2753
2721
  // set main elements
@@ -2943,7 +2911,7 @@
2943
2911
  const self = this;
2944
2912
  const { activeElement } = getDocument(self.input);
2945
2913
 
2946
- if ((isMobile && self.dragElement)
2914
+ if ((e.type === touchmoveEvent && self.dragElement)
2947
2915
  || (activeElement && self.controlKnobs.includes(activeElement))) {
2948
2916
  e.stopPropagation();
2949
2917
  e.preventDefault();
@@ -3856,4 +3824,4 @@
3856
3824
 
3857
3825
  return ColorPickerElement;
3858
3826
 
3859
- })));
3827
+ }));