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

Sign up to get free protection for your applications and to get access to all the features.
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
  });