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