@thednp/color-picker 1.0.0 → 2.0.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/.eslintrc.cjs +199 -0
  2. package/.lgtm.yml +9 -0
  3. package/.prettierrc.json +15 -0
  4. package/.stylelintrc.json +236 -0
  5. package/LICENSE +0 -0
  6. package/README.md +63 -80
  7. package/compile.js +48 -0
  8. package/cypress/downloads/downloads.htm +0 -0
  9. package/cypress/e2e/color-palette.cy.ts +128 -0
  10. package/cypress/e2e/color-picker.cy.ts +920 -0
  11. package/cypress/fixtures/colorNamesFrench.js +3 -0
  12. package/cypress/fixtures/componentLabelsFrench.js +21 -0
  13. package/cypress/fixtures/format.js +3 -0
  14. package/cypress/fixtures/getCEMarkup.js +29 -0
  15. package/cypress/fixtures/getMarkup.js +28 -0
  16. package/cypress/fixtures/getRandomInt.js +6 -0
  17. package/cypress/fixtures/sampleWebcolors.js +18 -0
  18. package/cypress/fixtures/testSample.js +8 -0
  19. package/cypress/plugins/esbuild-istanbul.ts +50 -0
  20. package/cypress/plugins/tsCompile.ts +34 -0
  21. package/cypress/support/commands.ts +0 -0
  22. package/cypress/support/e2e.ts +21 -0
  23. package/cypress/test.html +23 -0
  24. package/cypress.config.ts +29 -0
  25. package/dist/css/color-picker.css +15 -39
  26. package/dist/css/color-picker.min.css +2 -2
  27. package/dist/css/color-picker.rtl.css +15 -39
  28. package/dist/css/color-picker.rtl.min.css +2 -2
  29. package/dist/js/color-picker.cjs +8 -0
  30. package/dist/js/color-picker.cjs.map +1 -0
  31. package/dist/js/color-picker.d.ts +278 -0
  32. package/dist/js/color-picker.js +5 -3583
  33. package/dist/js/color-picker.js.map +1 -0
  34. package/dist/js/color-picker.mjs +2631 -0
  35. package/dist/js/color-picker.mjs.map +1 -0
  36. package/dts.config.ts +15 -0
  37. package/package.json +64 -74
  38. package/src/scss/_variables.scss +0 -1
  39. package/src/scss/color-picker.rtl.scss +4 -0
  40. package/src/scss/color-picker.scss +75 -39
  41. package/src/ts/colorPalette.ts +89 -0
  42. package/src/{js/color-picker.js → ts/index.ts} +492 -502
  43. package/src/ts/interface/colorPickerLabels.ts +20 -0
  44. package/src/ts/interface/colorPickerOptions.ts +11 -0
  45. package/src/ts/interface/paletteOptions.ts +6 -0
  46. package/src/ts/util/colorNames.ts +21 -0
  47. package/src/{js/util/colorPickerLabels.js → ts/util/colorPickerLabels.ts} +4 -2
  48. package/src/ts/util/getColorControls.ts +90 -0
  49. package/src/{js/util/getColorForm.js → ts/util/getColorForm.ts} +28 -18
  50. package/src/{js/util/getColorMenu.js → ts/util/getColorMenu.ts} +21 -30
  51. package/src/ts/util/isValidJSON.ts +19 -0
  52. package/src/{js/util/setMarkup.js → ts/util/setMarkup.ts} +57 -48
  53. package/src/{js/util/vHidden.js → ts/util/vHidden.ts} +0 -0
  54. package/tsconfig.json +29 -0
  55. package/vite.config.ts +34 -0
  56. package/dist/js/color-esm.js +0 -1167
  57. package/dist/js/color-esm.min.js +0 -2
  58. package/dist/js/color-palette-esm.js +0 -1238
  59. package/dist/js/color-palette-esm.min.js +0 -2
  60. package/dist/js/color-palette.js +0 -1246
  61. package/dist/js/color-palette.min.js +0 -2
  62. package/dist/js/color-picker-element-esm.js +0 -3739
  63. package/dist/js/color-picker-element-esm.min.js +0 -2
  64. package/dist/js/color-picker-element.js +0 -3747
  65. package/dist/js/color-picker-element.min.js +0 -2
  66. package/dist/js/color-picker-esm.js +0 -3578
  67. package/dist/js/color-picker-esm.min.js +0 -2
  68. package/dist/js/color-picker.min.js +0 -2
  69. package/dist/js/color.js +0 -1175
  70. package/dist/js/color.min.js +0 -2
  71. package/src/js/color-palette.js +0 -75
  72. package/src/js/color-picker-element.js +0 -110
  73. package/src/js/color.js +0 -1107
  74. package/src/js/index.js +0 -3
  75. package/src/js/util/colorNames.js +0 -6
  76. package/src/js/util/getColorControls.js +0 -103
  77. package/src/js/util/isValidJSON.js +0 -13
  78. package/src/js/util/nonColors.js +0 -5
  79. package/src/js/util/roundPart.js +0 -9
  80. package/src/js/util/setCSSProperties.js +0 -12
  81. package/src/js/util/tabindex.js +0 -3
  82. package/src/js/util/toggleCEAttr.js +0 -70
  83. package/src/js/util/version.js +0 -6
  84. package/src/js/version.js +0 -6
  85. package/types/cp.d.ts +0 -544
  86. package/types/index.d.ts +0 -48
  87. package/types/source/source.ts +0 -5
  88. package/types/source/types.d.ts +0 -92
package/src/js/color.js DELETED
@@ -1,1107 +0,0 @@
1
- import documentHead from 'shorter-js/src/blocks/documentHead';
2
- import getElementStyle from 'shorter-js/src/get/getElementStyle';
3
- import setElementStyle from 'shorter-js/src/misc/setElementStyle';
4
- import ObjectAssign from 'shorter-js/src/misc/ObjectAssign';
5
- import toLowerCase from 'shorter-js/src/misc/toLowerCase';
6
-
7
- import nonColors from './util/nonColors';
8
- import roundPart from './util/roundPart';
9
-
10
- // Color supported formats
11
- const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
12
-
13
- // Hue angles
14
- const ANGLES = 'deg|rad|grad|turn';
15
-
16
- // <http://www.w3.org/TR/css3-values/#integers>
17
- const CSS_INTEGER = '[-\\+]?\\d+%?';
18
-
19
- // Include CSS3 Module
20
- // <http://www.w3.org/TR/css3-values/#number-value>
21
- const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
22
-
23
- // Include CSS4 Module Hue degrees unit
24
- // <https://www.w3.org/TR/css3-values/#angle-value>
25
- const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
26
-
27
- // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
28
- const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
29
-
30
- // Add angles to the mix
31
- const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
32
-
33
- // Start & end
34
- const START_MATCH = '(?:[\\s|\\(\\s|\\s\\(\\s]+)?';
35
- const END_MATCH = '(?:[\\s|\\)\\s]+)?';
36
- // Components separation
37
- const SEP = '(?:[,|\\s]+)';
38
- const SEP2 = '(?:[,|\\/\\s]*)?';
39
-
40
- // Actual matching.
41
- // Parentheses and commas are optional, but not required.
42
- // Whitespace can take the place of commas or opening paren
43
- const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
44
-
45
- const matchers = {
46
- CSS_UNIT: new RegExp(CSS_UNIT2),
47
- hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
48
- rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
49
- hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
50
- hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
51
- hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
52
- hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
53
- hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
54
- hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
55
- };
56
-
57
- /**
58
- * Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
59
- * <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
60
- * @param {string} n testing number
61
- * @returns {boolean} the query result
62
- */
63
- function isOnePointZero(n) {
64
- return `${n}`.includes('.') && parseFloat(n) === 1;
65
- }
66
-
67
- /**
68
- * Check to see if string passed in is a percentage
69
- * @param {string} n testing number
70
- * @returns {boolean} the query result
71
- */
72
- function isPercentage(n) {
73
- return `${n}`.includes('%');
74
- }
75
-
76
- /**
77
- * Check to see if string passed is a web safe colour.
78
- * @see https://stackoverflow.com/a/16994164
79
- * @param {string} color a colour name, EG: *red*
80
- * @returns {boolean} the query result
81
- */
82
- function isColorName(color) {
83
- if (nonColors.includes(color)
84
- || ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
85
-
86
- if (['black', 'white'].includes(color)) return true;
87
-
88
- return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
89
- setElementStyle(documentHead, { color });
90
- const computedColor = getElementStyle(documentHead, 'color');
91
- setElementStyle(documentHead, { color: '' });
92
- return computedColor !== c;
93
- });
94
- }
95
-
96
- /**
97
- * Check to see if it looks like a CSS unit
98
- * (see `matchers` above for definition).
99
- * @param {string | number} color testing value
100
- * @returns {boolean} the query result
101
- */
102
- function isValidCSSUnit(color) {
103
- return Boolean(matchers.CSS_UNIT.exec(String(color)));
104
- }
105
-
106
- /**
107
- * Take input from [0, n] and return it as [0, 1]
108
- * @param {*} N the input number
109
- * @param {number} max the number maximum value
110
- * @returns {number} the number in [0, 1] value range
111
- */
112
- function bound01(N, max) {
113
- let n = N;
114
-
115
- if (typeof N === 'number'
116
- && Math.min(N, 0) === 0 // round values to 6 decimals Math.round(N * (10 ** 6)) / 10 ** 6
117
- && Math.max(N, 1) === 1) return N;
118
-
119
- if (isOnePointZero(N)) n = '100%';
120
-
121
- const processPercent = isPercentage(n);
122
- n = max === 360
123
- ? parseFloat(n)
124
- : Math.min(max, Math.max(0, parseFloat(n)));
125
-
126
- // Automatically convert percentage into number
127
- if (processPercent) n = (n * max) / 100;
128
-
129
- // Handle floating point rounding errors
130
- if (Math.abs(n - max) < 0.000001) {
131
- return 1;
132
- }
133
- // Convert into [0, 1] range if it isn't already
134
- if (max === 360) {
135
- // If n is a hue given in degrees,
136
- // wrap around out-of-range values into [0, 360] range
137
- // then convert into [0, 1].
138
- n = (n < 0 ? (n % max) + max : n % max) / max;
139
- } else {
140
- // If n not a hue given in degrees
141
- // Convert into [0, 1] range if it isn't already.
142
- n = (n % max) / max;
143
- }
144
- return n;
145
- }
146
-
147
- /**
148
- * Return a valid alpha value [0,1] with all invalid values being set to 1.
149
- * @param {string | number} a transparency value
150
- * @returns {number} a transparency value in the [0, 1] range
151
- */
152
- function boundAlpha(a) {
153
- let na = parseFloat(`${a}`);
154
-
155
- if (Number.isNaN(na) || na < 0 || na > 1) {
156
- na = 1;
157
- }
158
-
159
- return na;
160
- }
161
-
162
- /**
163
- * Force a number between 0 and 1.
164
- * @param {number} v the float number
165
- * @returns {number} - the resulting number
166
- */
167
- function clamp01(v) {
168
- return Math.min(1, Math.max(0, v));
169
- }
170
-
171
- /**
172
- * Returns the hexadecimal value of a web safe colour.
173
- * @param {string} name
174
- * @returns {string}
175
- */
176
- function getRGBFromName(name) {
177
- setElementStyle(documentHead, { color: name });
178
- const colorName = getElementStyle(documentHead, 'color');
179
- setElementStyle(documentHead, { color: '' });
180
- return colorName;
181
- }
182
-
183
- /**
184
- * Converts a decimal value to hexadecimal.
185
- * @param {number} d the input number
186
- * @returns {string} - the hexadecimal value
187
- */
188
- function convertDecimalToHex(d) {
189
- return roundPart(d * 255).toString(16);
190
- }
191
-
192
- /**
193
- * Converts a hexadecimal value to decimal.
194
- * @param {string} h hexadecimal value
195
- * @returns {number} number in decimal format
196
- */
197
- function convertHexToDecimal(h) {
198
- return parseIntFromHex(h) / 255;
199
- }
200
-
201
- /**
202
- * Converts a base-16 hexadecimal value into a base-10 integer.
203
- * @param {string} val
204
- * @returns {number}
205
- */
206
- function parseIntFromHex(val) {
207
- return parseInt(val, 16);
208
- }
209
-
210
- /**
211
- * Force a hexadecimal value to have 2 characters.
212
- * @param {string} c string with [0-9A-F] ranged values
213
- * @returns {string} 0 => 00, a => 0a
214
- */
215
- function pad2(c) {
216
- return c.length === 1 ? `0${c}` : String(c);
217
- }
218
-
219
- /**
220
- * Converts an RGB colour value to HSL.
221
- *
222
- * @param {number} r Red component [0, 1]
223
- * @param {number} g Green component [0, 1]
224
- * @param {number} b Blue component [0, 1]
225
- * @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
226
- */
227
- function rgbToHsl(r, g, b) {
228
- const max = Math.max(r, g, b);
229
- const min = Math.min(r, g, b);
230
- let h = 0;
231
- let s = 0;
232
- const l = (max + min) / 2;
233
- if (max === min) {
234
- s = 0;
235
- h = 0; // achromatic
236
- } else {
237
- const d = max - min;
238
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
239
- if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
240
- if (max === g) h = (b - r) / d + 2;
241
- if (max === b) h = (r - g) / d + 4;
242
-
243
- h /= 6;
244
- }
245
- return { h, s, l };
246
- }
247
-
248
- /**
249
- * Returns a normalized RGB component value.
250
- * @param {number} p
251
- * @param {number} q
252
- * @param {number} t
253
- * @returns {number}
254
- */
255
- function hueToRgb(p, q, t) {
256
- let T = t;
257
- if (T < 0) T += 1;
258
- if (T > 1) T -= 1;
259
- if (T < 1 / 6) return p + (q - p) * (6 * T);
260
- if (T < 1 / 2) return q;
261
- if (T < 2 / 3) return p + (q - p) * (2 / 3 - T) * 6;
262
- return p;
263
- }
264
-
265
- /**
266
- * Converts an HSL colour value to RGB.
267
- *
268
- * @param {number} h Hue Angle [0, 1]
269
- * @param {number} s Saturation [0, 1]
270
- * @param {number} l Lightness Angle [0, 1]
271
- * @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
272
- */
273
- function hslToRgb(h, s, l) {
274
- let r = 0;
275
- let g = 0;
276
- let b = 0;
277
-
278
- if (s === 0) {
279
- // achromatic
280
- g = l;
281
- b = l;
282
- r = l;
283
- } else {
284
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
285
- const p = 2 * l - q;
286
- r = hueToRgb(p, q, h + 1 / 3);
287
- g = hueToRgb(p, q, h);
288
- b = hueToRgb(p, q, h - 1 / 3);
289
- }
290
-
291
- return { r, g, b };
292
- }
293
-
294
- /**
295
- * Returns an HWB colour object from an RGB colour object.
296
- * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
297
- * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
298
- *
299
- * @param {number} r Red component [0, 1]
300
- * @param {number} g Green [0, 1]
301
- * @param {number} b Blue [0, 1]
302
- * @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
303
- */
304
- function rgbToHwb(r, g, b) {
305
- let f = 0;
306
- let i = 0;
307
- const whiteness = Math.min(r, g, b);
308
- const max = Math.max(r, g, b);
309
- const black = 1 - max;
310
-
311
- if (max === whiteness) return { h: 0, w: whiteness, b: black };
312
- if (r === whiteness) {
313
- f = g - b;
314
- i = 3;
315
- } else {
316
- f = g === whiteness ? b - r : r - g;
317
- i = g === whiteness ? 5 : 1;
318
- }
319
-
320
- const h = (i - f / (max - whiteness)) / 6;
321
- return {
322
- h: h === 1 ? 0 : h,
323
- w: whiteness,
324
- b: black,
325
- };
326
- }
327
-
328
- /**
329
- * Returns an RGB colour object from an HWB colour.
330
- *
331
- * @param {number} H Hue Angle [0, 1]
332
- * @param {number} W Whiteness [0, 1]
333
- * @param {number} B Blackness [0, 1]
334
- * @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
335
- *
336
- * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
337
- * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
338
- */
339
- function hwbToRgb(H, W, B) {
340
- if (W + B >= 1) {
341
- const gray = W / (W + B);
342
- return { r: gray, g: gray, b: gray };
343
- }
344
- let { r, g, b } = hslToRgb(H, 1, 0.5);
345
- [r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
346
-
347
- return { r, g, b };
348
- }
349
-
350
- /**
351
- * Converts an RGB colour value to HSV.
352
- *
353
- * @param {number} r Red component [0, 1]
354
- * @param {number} g Green [0, 1]
355
- * @param {number} b Blue [0, 1]
356
- * @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
357
- */
358
- function rgbToHsv(r, g, b) {
359
- const max = Math.max(r, g, b);
360
- const min = Math.min(r, g, b);
361
- let h = 0;
362
- const v = max;
363
- const d = max - min;
364
- const s = max === 0 ? 0 : d / max;
365
- if (max === min) {
366
- h = 0; // achromatic
367
- } else {
368
- if (r === max) h = (g - b) / d + (g < b ? 6 : 0);
369
- if (g === max) h = (b - r) / d + 2;
370
- if (b === max) h = (r - g) / d + 4;
371
-
372
- h /= 6;
373
- }
374
- return { h, s, v };
375
- }
376
-
377
- /**
378
- * Converts an HSV colour value to RGB.
379
- *
380
- * @param {number} H Hue Angle [0, 1]
381
- * @param {number} S Saturation [0, 1]
382
- * @param {number} V Brightness Angle [0, 1]
383
- * @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
384
- */
385
- function hsvToRgb(H, S, V) {
386
- const h = H * 6;
387
- const s = S;
388
- const v = V;
389
- const i = Math.floor(h);
390
- const f = h - i;
391
- const p = v * (1 - s);
392
- const q = v * (1 - f * s);
393
- const t = v * (1 - (1 - f) * s);
394
- const mod = i % 6;
395
- const r = [v, q, p, p, t, v][mod];
396
- const g = [t, v, v, q, p, p][mod];
397
- const b = [p, p, t, v, v, q][mod];
398
- return { r, g, b };
399
- }
400
-
401
- /**
402
- * Converts an RGB colour to hex
403
- *
404
- * Assumes r, g, and b are contained in the set [0, 255]
405
- * Returns a 3 or 6 character hex
406
- * @param {number} r Red component [0, 255]
407
- * @param {number} g Green [0, 255]
408
- * @param {number} b Blue [0, 255]
409
- * @param {boolean=} allow3Char
410
- * @returns {string}
411
- */
412
- function rgbToHex(r, g, b, allow3Char) {
413
- const hex = [
414
- pad2(roundPart(r).toString(16)),
415
- pad2(roundPart(g).toString(16)),
416
- pad2(roundPart(b).toString(16)),
417
- ];
418
-
419
- // Return a 3 character hex if possible
420
- if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
421
- && hex[1].charAt(0) === hex[1].charAt(1)
422
- && hex[2].charAt(0) === hex[2].charAt(1)) {
423
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
424
- }
425
-
426
- return hex.join('');
427
- }
428
-
429
- /**
430
- * Converts an RGBA color plus alpha transparency to hex8.
431
- *
432
- * @param {number} r Red component [0, 255]
433
- * @param {number} g Green [0, 255]
434
- * @param {number} b Blue [0, 255]
435
- * @param {number} a Alpha transparency [0, 1]
436
- * @param {boolean=} allow4Char when *true* it will also find hex shorthand
437
- * @returns {string} a hexadecimal value with alpha transparency
438
- */
439
- function rgbaToHex(r, g, b, a, allow4Char) {
440
- const hex = [
441
- pad2(roundPart(r).toString(16)),
442
- pad2(roundPart(g).toString(16)),
443
- pad2(roundPart(b).toString(16)),
444
- pad2(convertDecimalToHex(a)),
445
- ];
446
-
447
- // Return a 4 character hex if possible
448
- if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
449
- && hex[1].charAt(0) === hex[1].charAt(1)
450
- && hex[2].charAt(0) === hex[2].charAt(1)
451
- && hex[3].charAt(0) === hex[3].charAt(1)) {
452
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
453
- }
454
- return hex.join('');
455
- }
456
-
457
- /**
458
- * Permissive string parsing. Take in a number of formats, and output an object
459
- * based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
460
- * @param {string} input colour value in any format
461
- * @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
462
- */
463
- function stringInputToObject(input) {
464
- let color = toLowerCase(input.trim());
465
-
466
- if (color.length === 0) {
467
- return {
468
- r: 0, g: 0, b: 0, a: 1,
469
- };
470
- }
471
-
472
- if (isColorName(color)) {
473
- color = getRGBFromName(color);
474
- } else if (nonColors.includes(color)) {
475
- const a = color === 'transparent' ? 0 : 1;
476
- return {
477
- r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
478
- };
479
- }
480
-
481
- // Try to match string input using regular expressions.
482
- // Keep most of the number bounding out of this function,
483
- // don't worry about [0,1] or [0,100] or [0,360]
484
- // Just return an object and let the conversion functions handle that.
485
- // This way the result will be the same whether Color is initialized with string or object.
486
- let [, m1, m2, m3, m4] = matchers.rgb.exec(color) || [];
487
- if (m1 && m2 && m3/* && m4 */) {
488
- return {
489
- r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
490
- };
491
- }
492
-
493
- [, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
494
- if (m1 && m2 && m3/* && m4 */) {
495
- return {
496
- h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
497
- };
498
- }
499
-
500
- [, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
501
- if (m1 && m2 && m3/* && m4 */) {
502
- return {
503
- h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
504
- };
505
- }
506
-
507
- [, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
508
- if (m1 && m2 && m3) {
509
- return {
510
- h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
511
- };
512
- }
513
-
514
- [, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
515
- if (m1 && m2 && m3 && m4) {
516
- return {
517
- r: parseIntFromHex(m1),
518
- g: parseIntFromHex(m2),
519
- b: parseIntFromHex(m3),
520
- a: convertHexToDecimal(m4),
521
- format: 'hex',
522
- };
523
- }
524
-
525
- [, m1, m2, m3] = matchers.hex6.exec(color) || [];
526
- if (m1 && m2 && m3) {
527
- return {
528
- r: parseIntFromHex(m1),
529
- g: parseIntFromHex(m2),
530
- b: parseIntFromHex(m3),
531
- format: 'hex',
532
- };
533
- }
534
-
535
- [, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
536
- if (m1 && m2 && m3 && m4) {
537
- return {
538
- r: parseIntFromHex(m1 + m1),
539
- g: parseIntFromHex(m2 + m2),
540
- b: parseIntFromHex(m3 + m3),
541
- a: convertHexToDecimal(m4 + m4),
542
- format: 'hex',
543
- };
544
- }
545
-
546
- [, m1, m2, m3] = matchers.hex3.exec(color) || [];
547
- if (m1 && m2 && m3) {
548
- return {
549
- r: parseIntFromHex(m1 + m1),
550
- g: parseIntFromHex(m2 + m2),
551
- b: parseIntFromHex(m3 + m3),
552
- format: 'hex',
553
- };
554
- }
555
-
556
- return false;
557
- }
558
-
559
- /**
560
- * Given a string or object, convert that input to RGB
561
- *
562
- * Possible string inputs:
563
- * ```
564
- * "red"
565
- * "#f00" or "f00"
566
- * "#ff0000" or "ff0000"
567
- * "#ff000000" or "ff000000" // CSS4 Module
568
- * "rgb 255 0 0" or "rgb (255, 0, 0)"
569
- * "rgb 1.0 0 0" or "rgb (1, 0, 0)"
570
- * "rgba(255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
571
- * "rgba(1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
572
- * "rgb(255 0 0 / 10%)" or "rgb 255 0 0 0.1" // CSS4 Module
573
- * "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
574
- * "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
575
- * "hsl(0deg 100% 50% / 50%)" or "hsl 0 100 50 50" // CSS4 Module
576
- * "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
577
- * "hsva(0, 100%, 100%, 0.1)" or "hsva 0 100% 100% 0.1"
578
- * "hsv(0deg 100% 100% / 10%)" or "hsv 0 100 100 0.1" // CSS4 Module
579
- * "hwb(0deg, 100%, 100%, 100%)" or "hwb 0 100% 100% 0.1" // CSS4 Module
580
- * ```
581
- * @param {string | Record<string, any>} input
582
- * @returns {CP.ColorObject}
583
- */
584
- function inputToRGB(input) {
585
- let rgb = { r: 0, g: 0, b: 0 };
586
- /** @type {*} */
587
- let color = input;
588
- /** @type {string | number} */
589
- let a = 1;
590
- let s = null;
591
- let v = null;
592
- let l = null;
593
- let w = null;
594
- let b = null;
595
- let h = null;
596
- let r = null;
597
- let g = null;
598
- let ok = false;
599
- const inputFormat = typeof color === 'object' && color.format;
600
- let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
601
-
602
- if (typeof input === 'string') {
603
- color = stringInputToObject(input);
604
- if (color) ok = true;
605
- }
606
- if (typeof color === 'object') {
607
- if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
608
- ({ r, g, b } = color);
609
- // RGB values now are all in [0, 1] range
610
- [r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255));
611
- rgb = { r, g, b };
612
- ok = true;
613
- format = color.format || 'rgb';
614
- }
615
- if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
616
- ({ h, s, v } = color);
617
- h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
618
- s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
619
- v = bound01(v, 100); // brightness can be `5%` or a [0, 1] value
620
- rgb = hsvToRgb(h, s, v);
621
- ok = true;
622
- format = 'hsv';
623
- }
624
- if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
625
- ({ h, s, l } = color);
626
- h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
627
- s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
628
- l = bound01(l, 100); // lightness can be `5%` or a [0, 1] value
629
- rgb = hslToRgb(h, s, l);
630
- ok = true;
631
- format = 'hsl';
632
- }
633
- if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
634
- ({ h, w, b } = color);
635
- h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
636
- w = bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
637
- b = bound01(b, 100); // blackness can be `5%` or a [0, 1] value
638
- rgb = hwbToRgb(h, w, b);
639
- ok = true;
640
- format = 'hwb';
641
- }
642
- if (isValidCSSUnit(color.a)) {
643
- a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
644
- a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
645
- }
646
- }
647
- if (typeof color === 'undefined') {
648
- ok = true;
649
- }
650
-
651
- return {
652
- ok,
653
- format,
654
- // r: Math.min(255, Math.max(rgb.r, 0)),
655
- // g: Math.min(255, Math.max(rgb.g, 0)),
656
- // b: Math.min(255, Math.max(rgb.b, 0)),
657
- r: rgb.r,
658
- g: rgb.g,
659
- b: rgb.b,
660
- a: boundAlpha(a),
661
- };
662
- }
663
-
664
- /**
665
- * @class
666
- * Returns a new `Color` instance.
667
- * @see https://github.com/bgrins/TinyColor
668
- */
669
- export default class Color {
670
- /**
671
- * @constructor
672
- * @param {CP.ColorInput} input the given colour value
673
- * @param {CP.ColorFormats=} config the given format
674
- */
675
- constructor(input, config) {
676
- let color = input;
677
- const configFormat = config && COLOR_FORMAT.includes(config)
678
- ? config : '';
679
-
680
- // If input is already a `Color`, clone its values
681
- if (color instanceof Color) {
682
- color = inputToRGB(color);
683
- }
684
-
685
- const {
686
- r, g, b, a, ok, format,
687
- } = inputToRGB(color);
688
-
689
- // bind
690
- const self = this;
691
-
692
- /** @type {CP.ColorInput} */
693
- self.originalInput = input;
694
- /** @type {number} */
695
- self.r = r;
696
- /** @type {number} */
697
- self.g = g;
698
- /** @type {number} */
699
- self.b = b;
700
- /** @type {number} */
701
- self.a = a;
702
- /** @type {boolean} */
703
- self.ok = ok;
704
- /** @type {CP.ColorFormats} */
705
- self.format = configFormat || format;
706
- }
707
-
708
- /**
709
- * Checks if the current input value is a valid colour.
710
- * @returns {boolean} the query result
711
- */
712
- get isValid() {
713
- return this.ok;
714
- }
715
-
716
- /**
717
- * Checks if the current colour requires a light text colour.
718
- * @returns {boolean} the query result
719
- */
720
- get isDark() {
721
- return this.brightness < 120;
722
- }
723
-
724
- /**
725
- * Returns the perceived luminance of a colour.
726
- * @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
727
- * @returns {number} a number in the [0, 1] range
728
- */
729
- get luminance() {
730
- const { r, g, b } = this;
731
- let R = 0;
732
- let G = 0;
733
- let B = 0;
734
-
735
- if (r <= 0.03928) {
736
- R = r / 12.92;
737
- } else {
738
- R = ((r + 0.055) / 1.055) ** 2.4;
739
- }
740
- if (g <= 0.03928) {
741
- G = g / 12.92;
742
- } else {
743
- G = ((g + 0.055) / 1.055) ** 2.4;
744
- }
745
- if (b <= 0.03928) {
746
- B = b / 12.92;
747
- } else {
748
- B = ((b + 0.055) / 1.055) ** 2.4;
749
- }
750
- return 0.2126 * R + 0.7152 * G + 0.0722 * B;
751
- }
752
-
753
- /**
754
- * Returns the perceived brightness of the colour.
755
- * @returns {number} a number in the [0, 255] range
756
- */
757
- get brightness() {
758
- const { r, g, b } = this.toRgb();
759
- return (r * 299 + g * 587 + b * 114) / 1000;
760
- }
761
-
762
- /**
763
- * Returns the colour as an RGBA object.
764
- * @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
765
- */
766
- toRgb() {
767
- let {
768
- r, g, b, a,
769
- } = this;
770
-
771
- [r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
772
- a = roundPart(a * 100) / 100;
773
- return {
774
- r, g, b, a,
775
- };
776
- }
777
-
778
- /**
779
- * Returns the RGBA values concatenated into a CSS3 Module string format.
780
- * * rgb(255,255,255)
781
- * * rgba(255,255,255,0.5)
782
- * @returns {string} the CSS valid colour in RGB/RGBA format
783
- */
784
- toRgbString() {
785
- const {
786
- r, g, b, a,
787
- } = this.toRgb();
788
- const [R, G, B] = [r, g, b].map(roundPart);
789
-
790
- return a === 1
791
- ? `rgb(${R}, ${G}, ${B})`
792
- : `rgba(${R}, ${G}, ${B}, ${a})`;
793
- }
794
-
795
- /**
796
- * Returns the RGBA values concatenated into a CSS4 Module string format.
797
- * * rgb(255 255 255)
798
- * * rgb(255 255 255 / 50%)
799
- * @returns {string} the CSS valid colour in CSS4 RGB format
800
- */
801
- toRgbCSS4String() {
802
- const {
803
- r, g, b, a,
804
- } = this.toRgb();
805
- const [R, G, B] = [r, g, b].map(roundPart);
806
- const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
807
-
808
- return `rgb(${R} ${G} ${B}${A})`;
809
- }
810
-
811
- /**
812
- * Returns the hexadecimal value of the colour. When the parameter is *true*
813
- * it will find a 3 characters shorthand of the decimal value.
814
- *
815
- * @param {boolean=} allow3Char when `true` returns shorthand HEX
816
- * @returns {string} the hexadecimal colour format
817
- */
818
- toHex(allow3Char) {
819
- const {
820
- r, g, b, a,
821
- } = this.toRgb();
822
-
823
- return a === 1
824
- ? rgbToHex(r, g, b, allow3Char)
825
- : rgbaToHex(r, g, b, a, allow3Char);
826
- }
827
-
828
- /**
829
- * Returns the CSS valid hexadecimal vaue of the colour. When the parameter is *true*
830
- * it will find a 3 characters shorthand of the value.
831
- *
832
- * @param {boolean=} allow3Char when `true` returns shorthand HEX
833
- * @returns {string} the CSS valid colour in hexadecimal format
834
- */
835
- toHexString(allow3Char) {
836
- return `#${this.toHex(allow3Char)}`;
837
- }
838
-
839
- /**
840
- * Returns the HEX8 value of the colour.
841
- * @param {boolean=} allow4Char when `true` returns shorthand HEX
842
- * @returns {string} the CSS valid colour in hexadecimal format
843
- */
844
- toHex8(allow4Char) {
845
- const {
846
- r, g, b, a,
847
- } = this.toRgb();
848
-
849
- return rgbaToHex(r, g, b, a, allow4Char);
850
- }
851
-
852
- /**
853
- * Returns the HEX8 value of the colour.
854
- * @param {boolean=} allow4Char when `true` returns shorthand HEX
855
- * @returns {string} the CSS valid colour in hexadecimal format
856
- */
857
- toHex8String(allow4Char) {
858
- return `#${this.toHex8(allow4Char)}`;
859
- }
860
-
861
- /**
862
- * Returns the colour as a HSVA object.
863
- * @returns {CP.HSVA} the `{h,s,v,a}` object with [0, 1] ranged values
864
- */
865
- toHsv() {
866
- const {
867
- r, g, b, a,
868
- } = this;
869
- const { h, s, v } = rgbToHsv(r, g, b);
870
-
871
- return {
872
- h, s, v, a,
873
- };
874
- }
875
-
876
- /**
877
- * Returns the colour as an HSLA object.
878
- * @returns {CP.HSLA} the `{h,s,l,a}` object with [0, 1] ranged values
879
- */
880
- toHsl() {
881
- const {
882
- r, g, b, a,
883
- } = this;
884
- const { h, s, l } = rgbToHsl(r, g, b);
885
-
886
- return {
887
- h, s, l, a,
888
- };
889
- }
890
-
891
- /**
892
- * Returns the HSLA values concatenated into a CSS3 Module format string.
893
- * * `hsl(150, 100%, 50%)`
894
- * * `hsla(150, 100%, 50%, 0.5)`
895
- * @returns {string} the CSS valid colour in HSL/HSLA format
896
- */
897
- toHslString() {
898
- let {
899
- h, s, l, a,
900
- } = this.toHsl();
901
- h = roundPart(h * 360);
902
- s = roundPart(s * 100);
903
- l = roundPart(l * 100);
904
- a = roundPart(a * 100) / 100;
905
-
906
- return a === 1
907
- ? `hsl(${h}, ${s}%, ${l}%)`
908
- : `hsla(${h}, ${s}%, ${l}%, ${a})`;
909
- }
910
-
911
- /**
912
- * Returns the HSLA values concatenated into a CSS4 Module format string.
913
- * * `hsl(150deg 100% 50%)`
914
- * * `hsl(150deg 100% 50% / 50%)`
915
- * @returns {string} the CSS valid colour in CSS4 HSL format
916
- */
917
- toHslCSS4String() {
918
- let {
919
- h, s, l, a,
920
- } = this.toHsl();
921
- h = roundPart(h * 360);
922
- s = roundPart(s * 100);
923
- l = roundPart(l * 100);
924
- a = roundPart(a * 100);
925
- const A = a < 100 ? ` / ${roundPart(a)}%` : '';
926
-
927
- return `hsl(${h}deg ${s}% ${l}%${A})`;
928
- }
929
-
930
- /**
931
- * Returns the colour as an HWBA object.
932
- * @returns {CP.HWBA} the `{h,w,b,a}` object with [0, 1] ranged values
933
- */
934
- toHwb() {
935
- const {
936
- r, g, b, a,
937
- } = this;
938
- const { h, w, b: bl } = rgbToHwb(r, g, b);
939
- return {
940
- h, w, b: bl, a,
941
- };
942
- }
943
-
944
- /**
945
- * Returns the HWBA values concatenated into a string.
946
- * @returns {string} the CSS valid colour in HWB format
947
- */
948
- toHwbString() {
949
- let {
950
- h, w, b, a,
951
- } = this.toHwb();
952
- h = roundPart(h * 360);
953
- w = roundPart(w * 100);
954
- b = roundPart(b * 100);
955
- a = roundPart(a * 100);
956
- const A = a < 100 ? ` / ${roundPart(a)}%` : '';
957
-
958
- return `hwb(${h}deg ${w}% ${b}%${A})`;
959
- }
960
-
961
- /**
962
- * Sets the alpha value of the current colour.
963
- * @param {number} alpha a new alpha value in the [0, 1] range.
964
- * @returns {Color} the `Color` instance
965
- */
966
- setAlpha(alpha) {
967
- const self = this;
968
- if (typeof alpha !== 'number') return self;
969
- self.a = boundAlpha(alpha);
970
- return self;
971
- }
972
-
973
- /**
974
- * Saturate the colour with a given amount.
975
- * @param {number=} amount a value in the [0, 100] range
976
- * @returns {Color} the `Color` instance
977
- */
978
- saturate(amount) {
979
- const self = this;
980
- if (typeof amount !== 'number') return self;
981
- const { h, s, l } = self.toHsl();
982
- const { r, g, b } = hslToRgb(h, clamp01(s + amount / 100), l);
983
-
984
- ObjectAssign(self, { r, g, b });
985
- return self;
986
- }
987
-
988
- /**
989
- * Desaturate the colour with a given amount.
990
- * @param {number=} amount a value in the [0, 100] range
991
- * @returns {Color} the `Color` instance
992
- */
993
- desaturate(amount) {
994
- return typeof amount === 'number' ? this.saturate(-amount) : this;
995
- }
996
-
997
- /**
998
- * Completely desaturates a colour into greyscale.
999
- * Same as calling `desaturate(100)`
1000
- * @returns {Color} the `Color` instance
1001
- */
1002
- greyscale() {
1003
- return this.saturate(-100);
1004
- }
1005
-
1006
- /**
1007
- * Increase the colour lightness with a given amount.
1008
- * @param {number=} amount a value in the [0, 100] range
1009
- * @returns {Color} the `Color` instance
1010
- */
1011
- lighten(amount) {
1012
- const self = this;
1013
- if (typeof amount !== 'number') return self;
1014
-
1015
- const { h, s, l } = self.toHsl();
1016
- const { r, g, b } = hslToRgb(h, s, clamp01(l + amount / 100));
1017
-
1018
- ObjectAssign(self, { r, g, b });
1019
- return self;
1020
- }
1021
-
1022
- /**
1023
- * Decrease the colour lightness with a given amount.
1024
- * @param {number=} amount a value in the [0, 100] range
1025
- * @returns {Color} the `Color` instance
1026
- */
1027
- darken(amount) {
1028
- return typeof amount === 'number' ? this.lighten(-amount) : this;
1029
- }
1030
-
1031
- /**
1032
- * Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
1033
- * Values outside of this range will be wrapped into this range.
1034
- *
1035
- * @param {number=} amount a value in the [0, 100] range
1036
- * @returns {Color} the `Color` instance
1037
- */
1038
- spin(amount) {
1039
- const self = this;
1040
- if (typeof amount !== 'number') return self;
1041
-
1042
- const { h, s, l } = self.toHsl();
1043
- const { r, g, b } = hslToRgb(clamp01(((h * 360 + amount) % 360) / 360), s, l);
1044
-
1045
- ObjectAssign(self, { r, g, b });
1046
- return self;
1047
- }
1048
-
1049
- /** Returns a clone of the current `Color` instance. */
1050
- clone() {
1051
- return new Color(this);
1052
- }
1053
-
1054
- /**
1055
- * Returns the colour value in CSS valid string format.
1056
- * @param {boolean=} allowShort when *true*, HEX values can be shorthand
1057
- * @returns {string} the CSS valid colour in the configured format
1058
- */
1059
- toString(allowShort) {
1060
- const self = this;
1061
- const { format } = self;
1062
-
1063
- if (format === 'hex') return self.toHexString(allowShort);
1064
- if (format === 'hsl') return self.toHslString();
1065
- if (format === 'hwb') return self.toHwbString();
1066
-
1067
- return self.toRgbString();
1068
- }
1069
- }
1070
-
1071
- ObjectAssign(Color, {
1072
- ANGLES,
1073
- CSS_ANGLE,
1074
- CSS_INTEGER,
1075
- CSS_NUMBER,
1076
- CSS_UNIT,
1077
- CSS_UNIT2,
1078
- PERMISSIVE_MATCH,
1079
- matchers,
1080
- isOnePointZero,
1081
- isPercentage,
1082
- isValidCSSUnit,
1083
- isColorName,
1084
- pad2,
1085
- clamp01,
1086
- bound01,
1087
- boundAlpha,
1088
- getRGBFromName,
1089
- convertHexToDecimal,
1090
- convertDecimalToHex,
1091
- rgbToHsl,
1092
- rgbToHex,
1093
- rgbToHsv,
1094
- rgbToHwb,
1095
- rgbaToHex,
1096
- hslToRgb,
1097
- hsvToRgb,
1098
- hueToRgb,
1099
- hwbToRgb,
1100
- parseIntFromHex,
1101
- stringInputToObject,
1102
- inputToRGB,
1103
- roundPart,
1104
- getElementStyle,
1105
- setElementStyle,
1106
- ObjectAssign,
1107
- });