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

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.
package/src/js/color.js CHANGED
@@ -1,13 +1,14 @@
1
- import getDocumentHead from 'shorter-js/src/get/getDocumentHead';
1
+ import documentHead from 'shorter-js/src/blocks/documentHead';
2
2
  import getElementStyle from 'shorter-js/src/get/getElementStyle';
3
3
  import setElementStyle from 'shorter-js/src/misc/setElementStyle';
4
4
  import ObjectAssign from 'shorter-js/src/misc/ObjectAssign';
5
+ import toLowerCase from 'shorter-js/src/misc/toLowerCase';
5
6
 
6
7
  import nonColors from './util/nonColors';
7
8
  import roundPart from './util/roundPart';
8
9
 
9
10
  // Color supported formats
10
- const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
11
+ const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
11
12
 
12
13
  // Hue angles
13
14
  const ANGLES = 'deg|rad|grad|turn';
@@ -29,10 +30,17 @@ const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
29
30
  // Add angles to the mix
30
31
  const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
31
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
+
32
40
  // Actual matching.
33
41
  // Parentheses and commas are optional, but not required.
34
42
  // Whitespace can take the place of commas or opening paren
35
- const PERMISSIVE_MATCH = `[\\s|\\(]+(${CSS_UNIT2})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s|\\/\\s]*(${CSS_UNIT})?\\s*\\)?`;
43
+ const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
36
44
 
37
45
  const matchers = {
38
46
  CSS_UNIT: new RegExp(CSS_UNIT2),
@@ -65,23 +73,24 @@ function isPercentage(n) {
65
73
  return `${n}`.includes('%');
66
74
  }
67
75
 
68
- /**
69
- * Check to see if string passed in is an angle
70
- * @param {string} n testing string
71
- * @returns {boolean} the query result
72
- */
73
- function isAngle(n) {
74
- return ANGLES.split('|').some((a) => `${n}`.includes(a));
75
- }
76
-
77
76
  /**
78
77
  * Check to see if string passed is a web safe colour.
78
+ * @see https://stackoverflow.com/a/16994164
79
79
  * @param {string} color a colour name, EG: *red*
80
80
  * @returns {boolean} the query result
81
81
  */
82
82
  function isColorName(color) {
83
- return !['#', ...COLOR_FORMAT].some((s) => color.includes(s))
84
- && !/[0-9]/.test(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
+ });
85
94
  }
86
95
 
87
96
  /**
@@ -102,15 +111,20 @@ function isValidCSSUnit(color) {
102
111
  */
103
112
  function bound01(N, max) {
104
113
  let n = N;
105
- if (isOnePointZero(n)) n = '100%';
106
114
 
107
- n = max === 360 ? n : Math.min(max, Math.max(0, parseFloat(n)));
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%';
108
120
 
109
- // Handle hue angles
110
- if (isAngle(N)) n = N.replace(new RegExp(ANGLES), '');
121
+ const processPercent = isPercentage(n);
122
+ n = max === 360
123
+ ? parseFloat(n)
124
+ : Math.min(max, Math.max(0, parseFloat(n)));
111
125
 
112
126
  // Automatically convert percentage into number
113
- if (isPercentage(n)) n = parseInt(String(n * max), 10) / 100;
127
+ if (processPercent) n = (n * max) / 100;
114
128
 
115
129
  // Handle floating point rounding errors
116
130
  if (Math.abs(n - max) < 0.000001) {
@@ -121,11 +135,11 @@ function bound01(N, max) {
121
135
  // If n is a hue given in degrees,
122
136
  // wrap around out-of-range values into [0, 360] range
123
137
  // then convert into [0, 1].
124
- n = (n < 0 ? (n % max) + max : n % max) / parseFloat(String(max));
138
+ n = (n < 0 ? (n % max) + max : n % max) / max;
125
139
  } else {
126
140
  // If n not a hue given in degrees
127
141
  // Convert into [0, 1] range if it isn't already.
128
- n = (n % max) / parseFloat(String(max));
142
+ n = (n % max) / max;
129
143
  }
130
144
  return n;
131
145
  }
@@ -160,7 +174,6 @@ function clamp01(v) {
160
174
  * @returns {string}
161
175
  */
162
176
  function getRGBFromName(name) {
163
- const documentHead = getDocumentHead();
164
177
  setElementStyle(documentHead, { color: name });
165
178
  const colorName = getElementStyle(documentHead, 'color');
166
179
  setElementStyle(documentHead, { color: '' });
@@ -206,15 +219,12 @@ function pad2(c) {
206
219
  /**
207
220
  * Converts an RGB colour value to HSL.
208
221
  *
209
- * @param {number} R Red component [0, 255]
210
- * @param {number} G Green component [0, 255]
211
- * @param {number} B Blue component [0, 255]
222
+ * @param {number} r Red component [0, 1]
223
+ * @param {number} g Green component [0, 1]
224
+ * @param {number} b Blue component [0, 1]
212
225
  * @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
213
226
  */
214
- function rgbToHsl(R, G, B) {
215
- const r = R / 255;
216
- const g = G / 255;
217
- const b = B / 255;
227
+ function rgbToHsl(r, g, b) {
218
228
  const max = Math.max(r, g, b);
219
229
  const min = Math.min(r, g, b);
220
230
  let h = 0;
@@ -226,18 +236,10 @@ function rgbToHsl(R, G, B) {
226
236
  } else {
227
237
  const d = max - min;
228
238
  s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
229
- switch (max) {
230
- case r:
231
- h = (g - b) / d + (g < b ? 6 : 0);
232
- break;
233
- case g:
234
- h = (b - r) / d + 2;
235
- break;
236
- case b:
237
- h = (r - g) / d + 4;
238
- break;
239
- default:
240
- }
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
+
241
243
  h /= 6;
242
244
  }
243
245
  return { h, s, l };
@@ -260,21 +262,46 @@ function hueToRgb(p, q, t) {
260
262
  return p;
261
263
  }
262
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
+
263
294
  /**
264
295
  * Returns an HWB colour object from an RGB colour object.
265
296
  * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
266
297
  * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
267
298
  *
268
- * @param {number} R Red component [0, 255]
269
- * @param {number} G Green [0, 255]
270
- * @param {number} B Blue [0, 255]
299
+ * @param {number} r Red component [0, 1]
300
+ * @param {number} g Green [0, 1]
301
+ * @param {number} b Blue [0, 1]
271
302
  * @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
272
303
  */
273
- function rgbToHwb(R, G, B) {
274
- const r = R / 255;
275
- const g = G / 255;
276
- const b = B / 255;
277
-
304
+ function rgbToHwb(r, g, b) {
278
305
  let f = 0;
279
306
  let i = 0;
280
307
  const whiteness = Math.min(r, g, b);
@@ -304,50 +331,18 @@ function rgbToHwb(R, G, B) {
304
331
  * @param {number} H Hue Angle [0, 1]
305
332
  * @param {number} W Whiteness [0, 1]
306
333
  * @param {number} B Blackness [0, 1]
307
- * @return {CP.RGB} {r,g,b} object with [0, 255] ranged values
334
+ * @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
308
335
  *
309
336
  * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
310
337
  * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
311
338
  */
312
339
  function hwbToRgb(H, W, B) {
313
340
  if (W + B >= 1) {
314
- const gray = (W / (W + B)) * 255;
341
+ const gray = W / (W + B);
315
342
  return { r: gray, g: gray, b: gray };
316
343
  }
317
344
  let { r, g, b } = hslToRgb(H, 1, 0.5);
318
- [r, g, b] = [r, g, b]
319
- .map((v) => (v / 255) * (1 - W - B) + W)
320
- .map((v) => v * 255);
321
-
322
- return { r, g, b };
323
- }
324
-
325
- /**
326
- * Converts an HSL colour value to RGB.
327
- *
328
- * @param {number} h Hue Angle [0, 1]
329
- * @param {number} s Saturation [0, 1]
330
- * @param {number} l Lightness Angle [0, 1]
331
- * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
332
- */
333
- function hslToRgb(h, s, l) {
334
- let r = 0;
335
- let g = 0;
336
- let b = 0;
337
-
338
- if (s === 0) {
339
- // achromatic
340
- g = l;
341
- b = l;
342
- r = l;
343
- } else {
344
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
345
- const p = 2 * l - q;
346
- r = hueToRgb(p, q, h + 1 / 3);
347
- g = hueToRgb(p, q, h);
348
- b = hueToRgb(p, q, h - 1 / 3);
349
- }
350
- [r, g, b] = [r, g, b].map((x) => x * 255);
345
+ [r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
351
346
 
352
347
  return { r, g, b };
353
348
  }
@@ -355,15 +350,12 @@ function hslToRgb(h, s, l) {
355
350
  /**
356
351
  * Converts an RGB colour value to HSV.
357
352
  *
358
- * @param {number} R Red component [0, 255]
359
- * @param {number} G Green [0, 255]
360
- * @param {number} B Blue [0, 255]
353
+ * @param {number} r Red component [0, 1]
354
+ * @param {number} g Green [0, 1]
355
+ * @param {number} b Blue [0, 1]
361
356
  * @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
362
357
  */
363
- function rgbToHsv(R, G, B) {
364
- const r = R / 255;
365
- const g = G / 255;
366
- const b = B / 255;
358
+ function rgbToHsv(r, g, b) {
367
359
  const max = Math.max(r, g, b);
368
360
  const min = Math.min(r, g, b);
369
361
  let h = 0;
@@ -373,18 +365,10 @@ function rgbToHsv(R, G, B) {
373
365
  if (max === min) {
374
366
  h = 0; // achromatic
375
367
  } else {
376
- switch (max) {
377
- case r:
378
- h = (g - b) / d + (g < b ? 6 : 0);
379
- break;
380
- case g:
381
- h = (b - r) / d + 2;
382
- break;
383
- case b:
384
- h = (r - g) / d + 4;
385
- break;
386
- default:
387
- }
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
+
388
372
  h /= 6;
389
373
  }
390
374
  return { h, s, v };
@@ -411,7 +395,7 @@ function hsvToRgb(H, S, V) {
411
395
  const r = [v, q, p, p, t, v][mod];
412
396
  const g = [t, v, v, q, p, p][mod];
413
397
  const b = [p, p, t, v, v, q][mod];
414
- return { r: r * 255, g: g * 255, b: b * 255 };
398
+ return { r, g, b };
415
399
  }
416
400
 
417
401
  /**
@@ -435,7 +419,7 @@ function rgbToHex(r, g, b, allow3Char) {
435
419
  // Return a 3 character hex if possible
436
420
  if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
437
421
  && hex[1].charAt(0) === hex[1].charAt(1)
438
- && hex[2].charAt(0) === hex[2].charAt(1)) {
422
+ && hex[2].charAt(0) === hex[2].charAt(1)) {
439
423
  return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
440
424
  }
441
425
 
@@ -463,51 +447,34 @@ function rgbaToHex(r, g, b, a, allow4Char) {
463
447
  // Return a 4 character hex if possible
464
448
  if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
465
449
  && hex[1].charAt(0) === hex[1].charAt(1)
466
- && hex[2].charAt(0) === hex[2].charAt(1)
467
- && hex[3].charAt(0) === hex[3].charAt(1)) {
450
+ && hex[2].charAt(0) === hex[2].charAt(1)
451
+ && hex[3].charAt(0) === hex[3].charAt(1)) {
468
452
  return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
469
453
  }
470
454
  return hex.join('');
471
455
  }
472
456
 
473
- /**
474
- * Returns a colour object corresponding to a given number.
475
- * @param {number} color input number
476
- * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
477
- */
478
- function numberInputToObject(color) {
479
- /* eslint-disable no-bitwise */
480
- return {
481
- r: color >> 16,
482
- g: (color & 0xff00) >> 8,
483
- b: color & 0xff,
484
- };
485
- /* eslint-enable no-bitwise */
486
- }
487
-
488
457
  /**
489
458
  * Permissive string parsing. Take in a number of formats, and output an object
490
459
  * based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
491
460
  * @param {string} input colour value in any format
492
- * @returns {Record<string, (number | string)> | false} an object matching the RegExp
461
+ * @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
493
462
  */
494
463
  function stringInputToObject(input) {
495
- let color = input.trim().toLowerCase();
464
+ let color = toLowerCase(input.trim());
465
+
496
466
  if (color.length === 0) {
497
467
  return {
498
- r: 0, g: 0, b: 0, a: 0,
468
+ r: 0, g: 0, b: 0, a: 1,
499
469
  };
500
470
  }
501
- let named = false;
471
+
502
472
  if (isColorName(color)) {
503
473
  color = getRGBFromName(color);
504
- named = true;
505
474
  } else if (nonColors.includes(color)) {
506
- const isTransparent = color === 'transparent';
507
- const rgb = isTransparent ? 0 : 255;
508
- const a = isTransparent ? 0 : 1;
475
+ const a = color === 'transparent' ? 0 : 1;
509
476
  return {
510
- r: rgb, g: rgb, b: rgb, a, format: 'rgb',
477
+ r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
511
478
  };
512
479
  }
513
480
 
@@ -522,24 +489,28 @@ function stringInputToObject(input) {
522
489
  r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
523
490
  };
524
491
  }
492
+
525
493
  [, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
526
494
  if (m1 && m2 && m3/* && m4 */) {
527
495
  return {
528
496
  h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
529
497
  };
530
498
  }
499
+
531
500
  [, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
532
501
  if (m1 && m2 && m3/* && m4 */) {
533
502
  return {
534
503
  h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
535
504
  };
536
505
  }
506
+
537
507
  [, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
538
508
  if (m1 && m2 && m3) {
539
509
  return {
540
510
  h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
541
511
  };
542
512
  }
513
+
543
514
  [, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
544
515
  if (m1 && m2 && m3 && m4) {
545
516
  return {
@@ -547,19 +518,20 @@ function stringInputToObject(input) {
547
518
  g: parseIntFromHex(m2),
548
519
  b: parseIntFromHex(m3),
549
520
  a: convertHexToDecimal(m4),
550
- // format: named ? 'rgb' : 'hex8',
551
- format: named ? 'rgb' : 'hex',
521
+ format: 'hex',
552
522
  };
553
523
  }
524
+
554
525
  [, m1, m2, m3] = matchers.hex6.exec(color) || [];
555
526
  if (m1 && m2 && m3) {
556
527
  return {
557
528
  r: parseIntFromHex(m1),
558
529
  g: parseIntFromHex(m2),
559
530
  b: parseIntFromHex(m3),
560
- format: named ? 'rgb' : 'hex',
531
+ format: 'hex',
561
532
  };
562
533
  }
534
+
563
535
  [, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
564
536
  if (m1 && m2 && m3 && m4) {
565
537
  return {
@@ -567,19 +539,20 @@ function stringInputToObject(input) {
567
539
  g: parseIntFromHex(m2 + m2),
568
540
  b: parseIntFromHex(m3 + m3),
569
541
  a: convertHexToDecimal(m4 + m4),
570
- // format: named ? 'rgb' : 'hex8',
571
- format: named ? 'rgb' : 'hex',
542
+ format: 'hex',
572
543
  };
573
544
  }
545
+
574
546
  [, m1, m2, m3] = matchers.hex3.exec(color) || [];
575
547
  if (m1 && m2 && m3) {
576
548
  return {
577
549
  r: parseIntFromHex(m1 + m1),
578
550
  g: parseIntFromHex(m2 + m2),
579
551
  b: parseIntFromHex(m3 + m3),
580
- format: named ? 'rgb' : 'hex',
552
+ format: 'hex',
581
553
  };
582
554
  }
555
+
583
556
  return false;
584
557
  }
585
558
 
@@ -610,7 +583,9 @@ function stringInputToObject(input) {
610
583
  */
611
584
  function inputToRGB(input) {
612
585
  let rgb = { r: 0, g: 0, b: 0 };
586
+ /** @type {*} */
613
587
  let color = input;
588
+ /** @type {string | number} */
614
589
  let a = 1;
615
590
  let s = null;
616
591
  let v = null;
@@ -621,58 +596,67 @@ function inputToRGB(input) {
621
596
  let r = null;
622
597
  let g = null;
623
598
  let ok = false;
624
- let format = 'hex';
599
+ const inputFormat = typeof color === 'object' && color.format;
600
+ let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
625
601
 
626
602
  if (typeof input === 'string') {
627
- // @ts-ignore -- this now is converted to object
628
603
  color = stringInputToObject(input);
629
604
  if (color) ok = true;
630
605
  }
631
606
  if (typeof color === 'object') {
632
607
  if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
633
608
  ({ r, g, b } = color);
634
- [r, g, b] = [...[r, g, b]]
635
- .map((n) => bound01(n, isPercentage(n) ? 100 : 255) * 255).map(roundPart);
636
- rgb = { r, g, b }; // RGB values now are all in [0, 255] range
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 };
637
612
  ok = true;
638
- format = 'rgb';
639
- } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
613
+ format = color.format || 'rgb';
614
+ }
615
+ if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
640
616
  ({ h, s, v } = color);
641
- h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
642
- s = typeof s === 'number' ? s : bound01(s, 100); // saturation can be `5%` or a [0, 1] value
643
- v = typeof v === 'number' ? v : bound01(v, 100); // brightness can be `5%` or a [0, 1] value
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
644
620
  rgb = hsvToRgb(h, s, v);
645
621
  ok = true;
646
622
  format = 'hsv';
647
- } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
623
+ }
624
+ if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
648
625
  ({ h, s, l } = color);
649
- h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
650
- s = typeof s === 'number' ? s : bound01(s, 100); // saturation can be `5%` or a [0, 1] value
651
- l = typeof l === 'number' ? l : bound01(l, 100); // lightness can be `5%` or a [0, 1] value
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
652
629
  rgb = hslToRgb(h, s, l);
653
630
  ok = true;
654
631
  format = 'hsl';
655
- } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
632
+ }
633
+ if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
656
634
  ({ h, w, b } = color);
657
- h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
658
- w = typeof w === 'number' ? w : bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
659
- b = typeof b === 'number' ? b : bound01(b, 100); // blackness can be `5%` or a [0, 1] value
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
660
638
  rgb = hwbToRgb(h, w, b);
661
639
  ok = true;
662
640
  format = 'hwb';
663
641
  }
664
642
  if (isValidCSSUnit(color.a)) {
665
- a = color.a;
666
- a = isPercentage(`${a}`) ? bound01(a, 100) : a;
643
+ a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
644
+ a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
667
645
  }
668
646
  }
647
+ if (typeof color === 'undefined') {
648
+ ok = true;
649
+ }
669
650
 
670
651
  return {
671
- ok, // @ts-ignore
672
- format: color.format || format,
673
- r: Math.min(255, Math.max(rgb.r, 0)),
674
- g: Math.min(255, Math.max(rgb.g, 0)),
675
- b: Math.min(255, Math.max(rgb.b, 0)),
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,
676
660
  a: boundAlpha(a),
677
661
  };
678
662
  }
@@ -691,15 +675,13 @@ export default class Color {
691
675
  constructor(input, config) {
692
676
  let color = input;
693
677
  const configFormat = config && COLOR_FORMAT.includes(config)
694
- ? config : 'rgb';
678
+ ? config : '';
695
679
 
696
- // If input is already a `Color`, return itself
680
+ // If input is already a `Color`, clone its values
697
681
  if (color instanceof Color) {
698
682
  color = inputToRGB(color);
699
683
  }
700
- if (typeof color === 'number') {
701
- color = numberInputToObject(color);
702
- }
684
+
703
685
  const {
704
686
  r, g, b, a, ok, format,
705
687
  } = inputToRGB(color);
@@ -708,7 +690,7 @@ export default class Color {
708
690
  const self = this;
709
691
 
710
692
  /** @type {CP.ColorInput} */
711
- self.originalInput = color;
693
+ self.originalInput = input;
712
694
  /** @type {number} */
713
695
  self.r = r;
714
696
  /** @type {number} */
@@ -749,24 +731,21 @@ export default class Color {
749
731
  let R = 0;
750
732
  let G = 0;
751
733
  let B = 0;
752
- const rp = r / 255;
753
- const rg = g / 255;
754
- const rb = b / 255;
755
734
 
756
- if (rp <= 0.03928) {
757
- R = rp / 12.92;
735
+ if (r <= 0.03928) {
736
+ R = r / 12.92;
758
737
  } else {
759
- R = ((rp + 0.055) / 1.055) ** 2.4;
738
+ R = ((r + 0.055) / 1.055) ** 2.4;
760
739
  }
761
- if (rg <= 0.03928) {
762
- G = rg / 12.92;
740
+ if (g <= 0.03928) {
741
+ G = g / 12.92;
763
742
  } else {
764
- G = ((rg + 0.055) / 1.055) ** 2.4;
743
+ G = ((g + 0.055) / 1.055) ** 2.4;
765
744
  }
766
- if (rb <= 0.03928) {
767
- B = rb / 12.92;
745
+ if (b <= 0.03928) {
746
+ B = b / 12.92;
768
747
  } else {
769
- B = ((rb + 0.055) / 1.055) ** 2.4;
748
+ B = ((b + 0.055) / 1.055) ** 2.4;
770
749
  }
771
750
  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
772
751
  }
@@ -776,7 +755,7 @@ export default class Color {
776
755
  * @returns {number} a number in the [0, 255] range
777
756
  */
778
757
  get brightness() {
779
- const { r, g, b } = this;
758
+ const { r, g, b } = this.toRgb();
780
759
  return (r * 299 + g * 587 + b * 114) / 1000;
781
760
  }
782
761
 
@@ -785,16 +764,14 @@ export default class Color {
785
764
  * @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
786
765
  */
787
766
  toRgb() {
788
- const {
767
+ let {
789
768
  r, g, b, a,
790
769
  } = this;
791
- const [R, G, B] = [r, g, b].map((x) => roundPart(x));
792
770
 
771
+ [r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
772
+ a = roundPart(a * 100) / 100;
793
773
  return {
794
- r: R,
795
- g: G,
796
- b: B,
797
- a: roundPart(a * 100) / 100,
774
+ r, g, b, a,
798
775
  };
799
776
  }
800
777
 
@@ -808,10 +785,11 @@ export default class Color {
808
785
  const {
809
786
  r, g, b, a,
810
787
  } = this.toRgb();
788
+ const [R, G, B] = [r, g, b].map(roundPart);
811
789
 
812
790
  return a === 1
813
- ? `rgb(${r}, ${g}, ${b})`
814
- : `rgba(${r}, ${g}, ${b}, ${a})`;
791
+ ? `rgb(${R}, ${G}, ${B})`
792
+ : `rgba(${R}, ${G}, ${B}, ${a})`;
815
793
  }
816
794
 
817
795
  /**
@@ -824,9 +802,10 @@ export default class Color {
824
802
  const {
825
803
  r, g, b, a,
826
804
  } = this.toRgb();
805
+ const [R, G, B] = [r, g, b].map(roundPart);
827
806
  const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
828
807
 
829
- return `rgb(${r} ${g} ${b}${A})`;
808
+ return `rgb(${R} ${G} ${B}${A})`;
830
809
  }
831
810
 
832
811
  /**
@@ -886,7 +865,7 @@ export default class Color {
886
865
  toHsv() {
887
866
  const {
888
867
  r, g, b, a,
889
- } = this.toRgb();
868
+ } = this;
890
869
  const { h, s, v } = rgbToHsv(r, g, b);
891
870
 
892
871
  return {
@@ -901,7 +880,7 @@ export default class Color {
901
880
  toHsl() {
902
881
  const {
903
882
  r, g, b, a,
904
- } = this.toRgb();
883
+ } = this;
905
884
  const { h, s, l } = rgbToHsl(r, g, b);
906
885
 
907
886
  return {
@@ -986,6 +965,7 @@ export default class Color {
986
965
  */
987
966
  setAlpha(alpha) {
988
967
  const self = this;
968
+ if (typeof alpha !== 'number') return self;
989
969
  self.a = boundAlpha(alpha);
990
970
  return self;
991
971
  }
@@ -1100,6 +1080,7 @@ ObjectAssign(Color, {
1100
1080
  isOnePointZero,
1101
1081
  isPercentage,
1102
1082
  isValidCSSUnit,
1083
+ isColorName,
1103
1084
  pad2,
1104
1085
  clamp01,
1105
1086
  bound01,
@@ -1117,9 +1098,10 @@ ObjectAssign(Color, {
1117
1098
  hueToRgb,
1118
1099
  hwbToRgb,
1119
1100
  parseIntFromHex,
1120
- numberInputToObject,
1121
1101
  stringInputToObject,
1122
1102
  inputToRGB,
1123
1103
  roundPart,
1104
+ getElementStyle,
1105
+ setElementStyle,
1124
1106
  ObjectAssign,
1125
1107
  });