@thednp/color-picker 0.0.1-alpha2 → 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.
- package/README.md +32 -15
- package/dist/css/color-picker.css +38 -15
- package/dist/css/color-picker.min.css +2 -2
- package/dist/css/color-picker.rtl.css +38 -15
- package/dist/css/color-picker.rtl.min.css +2 -2
- package/dist/js/color-esm.js +1178 -0
- package/dist/js/color-esm.min.js +2 -0
- package/dist/js/color-palette-esm.js +1252 -0
- package/dist/js/color-palette-esm.min.js +2 -0
- package/dist/js/color-palette.js +1260 -0
- package/dist/js/color-palette.min.js +2 -0
- package/dist/js/color-picker-element-esm.js +433 -424
- package/dist/js/color-picker-element-esm.min.js +2 -2
- package/dist/js/color-picker-element.js +435 -426
- package/dist/js/color-picker-element.min.js +2 -2
- package/dist/js/color-picker-esm.js +745 -739
- package/dist/js/color-picker-esm.min.js +2 -2
- package/dist/js/color-picker.js +747 -741
- package/dist/js/color-picker.min.js +2 -2
- package/dist/js/color.js +1186 -0
- package/dist/js/color.min.js +2 -0
- package/package.json +19 -3
- package/src/js/color-palette.js +28 -12
- package/src/js/color-picker-element.js +8 -4
- package/src/js/color-picker.js +84 -172
- package/src/js/color.js +125 -131
- package/src/js/util/getColorControls.js +3 -3
- package/src/js/util/getColorForm.js +0 -1
- package/src/js/util/getColorMenu.js +31 -33
- package/src/js/util/roundPart.js +9 -0
- package/src/js/util/setCSSProperties.js +12 -0
- package/src/js/util/setMarkup.js +122 -0
- package/src/js/util/tabindex.js +3 -0
- package/src/js/util/version.js +6 -0
- package/src/scss/color-picker.scss +35 -16
- package/types/cp.d.ts +48 -20
- package/src/js/util/templates.js +0 -10
package/src/js/color.js
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
import
|
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';
|
8
|
+
import roundPart from './util/roundPart';
|
7
9
|
|
8
10
|
// Color supported formats
|
9
|
-
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', '
|
11
|
+
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
|
10
12
|
|
11
13
|
// Hue angles
|
12
14
|
const ANGLES = 'deg|rad|grad|turn';
|
@@ -28,10 +30,17 @@ const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
|
|
28
30
|
// Add angles to the mix
|
29
31
|
const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
|
30
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
|
+
|
31
40
|
// Actual matching.
|
32
41
|
// Parentheses and commas are optional, but not required.
|
33
42
|
// Whitespace can take the place of commas or opening paren
|
34
|
-
const PERMISSIVE_MATCH =
|
43
|
+
const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
|
35
44
|
|
36
45
|
const matchers = {
|
37
46
|
CSS_UNIT: new RegExp(CSS_UNIT2),
|
@@ -64,23 +73,22 @@ function isPercentage(n) {
|
|
64
73
|
return `${n}`.includes('%');
|
65
74
|
}
|
66
75
|
|
67
|
-
/**
|
68
|
-
* Check to see if string passed in is an angle
|
69
|
-
* @param {string} n testing string
|
70
|
-
* @returns {boolean} the query result
|
71
|
-
*/
|
72
|
-
function isAngle(n) {
|
73
|
-
return ANGLES.split('|').some((a) => `${n}`.includes(a));
|
74
|
-
}
|
75
|
-
|
76
76
|
/**
|
77
77
|
* Check to see if string passed is a web safe colour.
|
78
|
+
* @see https://stackoverflow.com/a/16994164
|
78
79
|
* @param {string} color a colour name, EG: *red*
|
79
80
|
* @returns {boolean} the query result
|
80
81
|
*/
|
81
82
|
function isColorName(color) {
|
82
|
-
|
83
|
-
|
83
|
+
if (nonColors.includes(color)
|
84
|
+
|| ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
|
85
|
+
|
86
|
+
return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
|
87
|
+
setElementStyle(documentHead, { color });
|
88
|
+
const computedColor = getElementStyle(documentHead, 'color');
|
89
|
+
setElementStyle(documentHead, { color: '' });
|
90
|
+
return computedColor !== c;
|
91
|
+
});
|
84
92
|
}
|
85
93
|
|
86
94
|
/**
|
@@ -101,15 +109,15 @@ function isValidCSSUnit(color) {
|
|
101
109
|
*/
|
102
110
|
function bound01(N, max) {
|
103
111
|
let n = N;
|
104
|
-
if (isOnePointZero(
|
105
|
-
|
106
|
-
n = max === 360 ? n : Math.min(max, Math.max(0, parseFloat(n)));
|
112
|
+
if (isOnePointZero(N)) n = '100%';
|
107
113
|
|
108
|
-
|
109
|
-
|
114
|
+
const processPercent = isPercentage(n);
|
115
|
+
n = max === 360
|
116
|
+
? parseFloat(n)
|
117
|
+
: Math.min(max, Math.max(0, parseFloat(n)));
|
110
118
|
|
111
119
|
// Automatically convert percentage into number
|
112
|
-
if (
|
120
|
+
if (processPercent) n = (n * max) / 100;
|
113
121
|
|
114
122
|
// Handle floating point rounding errors
|
115
123
|
if (Math.abs(n - max) < 0.000001) {
|
@@ -120,11 +128,11 @@ function bound01(N, max) {
|
|
120
128
|
// If n is a hue given in degrees,
|
121
129
|
// wrap around out-of-range values into [0, 360] range
|
122
130
|
// then convert into [0, 1].
|
123
|
-
n = (n < 0 ? (n % max) + max : n % max) /
|
131
|
+
n = (n < 0 ? (n % max) + max : n % max) / max;
|
124
132
|
} else {
|
125
133
|
// If n not a hue given in degrees
|
126
134
|
// Convert into [0, 1] range if it isn't already.
|
127
|
-
n = (n % max) /
|
135
|
+
n = (n % max) / max;
|
128
136
|
}
|
129
137
|
return n;
|
130
138
|
}
|
@@ -159,7 +167,6 @@ function clamp01(v) {
|
|
159
167
|
* @returns {string}
|
160
168
|
*/
|
161
169
|
function getRGBFromName(name) {
|
162
|
-
const documentHead = getDocumentHead();
|
163
170
|
setElementStyle(documentHead, { color: name });
|
164
171
|
const colorName = getElementStyle(documentHead, 'color');
|
165
172
|
setElementStyle(documentHead, { color: '' });
|
@@ -172,7 +179,7 @@ function getRGBFromName(name) {
|
|
172
179
|
* @returns {string} - the hexadecimal value
|
173
180
|
*/
|
174
181
|
function convertDecimalToHex(d) {
|
175
|
-
return
|
182
|
+
return roundPart(d * 255).toString(16);
|
176
183
|
}
|
177
184
|
|
178
185
|
/**
|
@@ -259,6 +266,36 @@ function hueToRgb(p, q, t) {
|
|
259
266
|
return p;
|
260
267
|
}
|
261
268
|
|
269
|
+
/**
|
270
|
+
* Converts an HSL colour value to RGB.
|
271
|
+
*
|
272
|
+
* @param {number} h Hue Angle [0, 1]
|
273
|
+
* @param {number} s Saturation [0, 1]
|
274
|
+
* @param {number} l Lightness Angle [0, 1]
|
275
|
+
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
276
|
+
*/
|
277
|
+
function hslToRgb(h, s, l) {
|
278
|
+
let r = 0;
|
279
|
+
let g = 0;
|
280
|
+
let b = 0;
|
281
|
+
|
282
|
+
if (s === 0) {
|
283
|
+
// achromatic
|
284
|
+
g = l;
|
285
|
+
b = l;
|
286
|
+
r = l;
|
287
|
+
} else {
|
288
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
289
|
+
const p = 2 * l - q;
|
290
|
+
r = hueToRgb(p, q, h + 1 / 3);
|
291
|
+
g = hueToRgb(p, q, h);
|
292
|
+
b = hueToRgb(p, q, h - 1 / 3);
|
293
|
+
}
|
294
|
+
[r, g, b] = [r, g, b].map((x) => x * 255);
|
295
|
+
|
296
|
+
return { r, g, b };
|
297
|
+
}
|
298
|
+
|
262
299
|
/**
|
263
300
|
* Returns an HWB colour object from an RGB colour object.
|
264
301
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
@@ -321,36 +358,6 @@ function hwbToRgb(H, W, B) {
|
|
321
358
|
return { r, g, b };
|
322
359
|
}
|
323
360
|
|
324
|
-
/**
|
325
|
-
* Converts an HSL colour value to RGB.
|
326
|
-
*
|
327
|
-
* @param {number} h Hue Angle [0, 1]
|
328
|
-
* @param {number} s Saturation [0, 1]
|
329
|
-
* @param {number} l Lightness Angle [0, 1]
|
330
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
331
|
-
*/
|
332
|
-
function hslToRgb(h, s, l) {
|
333
|
-
let r = 0;
|
334
|
-
let g = 0;
|
335
|
-
let b = 0;
|
336
|
-
|
337
|
-
if (s === 0) {
|
338
|
-
// achromatic
|
339
|
-
g = l;
|
340
|
-
b = l;
|
341
|
-
r = l;
|
342
|
-
} else {
|
343
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
344
|
-
const p = 2 * l - q;
|
345
|
-
r = hueToRgb(p, q, h + 1 / 3);
|
346
|
-
g = hueToRgb(p, q, h);
|
347
|
-
b = hueToRgb(p, q, h - 1 / 3);
|
348
|
-
}
|
349
|
-
[r, g, b] = [r, g, b].map((x) => x * 255);
|
350
|
-
|
351
|
-
return { r, g, b };
|
352
|
-
}
|
353
|
-
|
354
361
|
/**
|
355
362
|
* Converts an RGB colour value to HSV.
|
356
363
|
*
|
@@ -407,10 +414,11 @@ function hsvToRgb(H, S, V) {
|
|
407
414
|
const q = v * (1 - f * s);
|
408
415
|
const t = v * (1 - (1 - f) * s);
|
409
416
|
const mod = i % 6;
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
417
|
+
let r = [v, q, p, p, t, v][mod];
|
418
|
+
let g = [t, v, v, q, p, p][mod];
|
419
|
+
let b = [p, p, t, v, v, q][mod];
|
420
|
+
[r, g, b] = [r, g, b].map((n) => n * 255);
|
421
|
+
return { r, g, b };
|
414
422
|
}
|
415
423
|
|
416
424
|
/**
|
@@ -426,15 +434,15 @@ function hsvToRgb(H, S, V) {
|
|
426
434
|
*/
|
427
435
|
function rgbToHex(r, g, b, allow3Char) {
|
428
436
|
const hex = [
|
429
|
-
pad2(
|
430
|
-
pad2(
|
431
|
-
pad2(
|
437
|
+
pad2(roundPart(r).toString(16)),
|
438
|
+
pad2(roundPart(g).toString(16)),
|
439
|
+
pad2(roundPart(b).toString(16)),
|
432
440
|
];
|
433
441
|
|
434
442
|
// Return a 3 character hex if possible
|
435
443
|
if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
|
436
444
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
437
|
-
|
445
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)) {
|
438
446
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
|
439
447
|
}
|
440
448
|
|
@@ -453,48 +461,33 @@ function rgbToHex(r, g, b, allow3Char) {
|
|
453
461
|
*/
|
454
462
|
function rgbaToHex(r, g, b, a, allow4Char) {
|
455
463
|
const hex = [
|
456
|
-
pad2(
|
457
|
-
pad2(
|
458
|
-
pad2(
|
464
|
+
pad2(roundPart(r).toString(16)),
|
465
|
+
pad2(roundPart(g).toString(16)),
|
466
|
+
pad2(roundPart(b).toString(16)),
|
459
467
|
pad2(convertDecimalToHex(a)),
|
460
468
|
];
|
461
469
|
|
462
470
|
// Return a 4 character hex if possible
|
463
471
|
if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
|
464
472
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
465
|
-
|
466
|
-
|
473
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)
|
474
|
+
&& hex[3].charAt(0) === hex[3].charAt(1)) {
|
467
475
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
|
468
476
|
}
|
469
477
|
return hex.join('');
|
470
478
|
}
|
471
479
|
|
472
|
-
/**
|
473
|
-
* Returns a colour object corresponding to a given number.
|
474
|
-
* @param {number} color input number
|
475
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
476
|
-
*/
|
477
|
-
function numberInputToObject(color) {
|
478
|
-
/* eslint-disable no-bitwise */
|
479
|
-
return {
|
480
|
-
r: color >> 16,
|
481
|
-
g: (color & 0xff00) >> 8,
|
482
|
-
b: color & 0xff,
|
483
|
-
};
|
484
|
-
/* eslint-enable no-bitwise */
|
485
|
-
}
|
486
|
-
|
487
480
|
/**
|
488
481
|
* Permissive string parsing. Take in a number of formats, and output an object
|
489
482
|
* based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
|
490
483
|
* @param {string} input colour value in any format
|
491
|
-
* @returns {Record<string, (number | string)> | false} an object matching the RegExp
|
484
|
+
* @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
|
492
485
|
*/
|
493
486
|
function stringInputToObject(input) {
|
494
|
-
let color = input.trim()
|
487
|
+
let color = toLowerCase(input.trim());
|
495
488
|
if (color.length === 0) {
|
496
489
|
return {
|
497
|
-
r: 0, g: 0, b: 0, a:
|
490
|
+
r: 0, g: 0, b: 0, a: 1,
|
498
491
|
};
|
499
492
|
}
|
500
493
|
let named = false;
|
@@ -502,11 +495,9 @@ function stringInputToObject(input) {
|
|
502
495
|
color = getRGBFromName(color);
|
503
496
|
named = true;
|
504
497
|
} else if (nonColors.includes(color)) {
|
505
|
-
const
|
506
|
-
const rgb = isTransparent ? 0 : 255;
|
507
|
-
const a = isTransparent ? 0 : 1;
|
498
|
+
const a = color === 'transparent' ? 0 : 1;
|
508
499
|
return {
|
509
|
-
r:
|
500
|
+
r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
|
510
501
|
};
|
511
502
|
}
|
512
503
|
|
@@ -546,7 +537,6 @@ function stringInputToObject(input) {
|
|
546
537
|
g: parseIntFromHex(m2),
|
547
538
|
b: parseIntFromHex(m3),
|
548
539
|
a: convertHexToDecimal(m4),
|
549
|
-
// format: named ? 'rgb' : 'hex8',
|
550
540
|
format: named ? 'rgb' : 'hex',
|
551
541
|
};
|
552
542
|
}
|
@@ -610,6 +600,7 @@ function stringInputToObject(input) {
|
|
610
600
|
function inputToRGB(input) {
|
611
601
|
let rgb = { r: 0, g: 0, b: 0 };
|
612
602
|
let color = input;
|
603
|
+
/** @type {string | number} */
|
613
604
|
let a = 1;
|
614
605
|
let s = null;
|
615
606
|
let v = null;
|
@@ -617,8 +608,11 @@ function inputToRGB(input) {
|
|
617
608
|
let w = null;
|
618
609
|
let b = null;
|
619
610
|
let h = null;
|
611
|
+
let r = null;
|
612
|
+
let g = null;
|
620
613
|
let ok = false;
|
621
|
-
|
614
|
+
const inputFormat = typeof color === 'object' && color.format;
|
615
|
+
let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
|
622
616
|
|
623
617
|
if (typeof input === 'string') {
|
624
618
|
// @ts-ignore -- this now is converted to object
|
@@ -627,7 +621,10 @@ function inputToRGB(input) {
|
|
627
621
|
}
|
628
622
|
if (typeof color === 'object') {
|
629
623
|
if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
|
630
|
-
|
624
|
+
({ r, g, b } = color);
|
625
|
+
// RGB values now are all in [0, 255] range
|
626
|
+
[r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255) * 255);
|
627
|
+
rgb = { r, g, b };
|
631
628
|
ok = true;
|
632
629
|
format = 'rgb';
|
633
630
|
} else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
|
@@ -656,14 +653,17 @@ function inputToRGB(input) {
|
|
656
653
|
format = 'hwb';
|
657
654
|
}
|
658
655
|
if (isValidCSSUnit(color.a)) {
|
659
|
-
a = color.a;
|
660
|
-
a = isPercentage(`${a}`) ? bound01(a, 100) : a;
|
656
|
+
a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
|
657
|
+
a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
|
661
658
|
}
|
662
659
|
}
|
660
|
+
if (typeof color === 'undefined') {
|
661
|
+
ok = true;
|
662
|
+
}
|
663
663
|
|
664
664
|
return {
|
665
|
-
ok,
|
666
|
-
format
|
665
|
+
ok,
|
666
|
+
format,
|
667
667
|
r: Math.min(255, Math.max(rgb.r, 0)),
|
668
668
|
g: Math.min(255, Math.max(rgb.g, 0)),
|
669
669
|
b: Math.min(255, Math.max(rgb.b, 0)),
|
@@ -692,7 +692,8 @@ export default class Color {
|
|
692
692
|
color = inputToRGB(color);
|
693
693
|
}
|
694
694
|
if (typeof color === 'number') {
|
695
|
-
|
695
|
+
const len = `${color}`.length;
|
696
|
+
color = `#${(len === 2 ? '0' : '00')}${color}`;
|
696
697
|
}
|
697
698
|
const {
|
698
699
|
r, g, b, a, ok, format,
|
@@ -702,7 +703,7 @@ export default class Color {
|
|
702
703
|
const self = this;
|
703
704
|
|
704
705
|
/** @type {CP.ColorInput} */
|
705
|
-
self.originalInput =
|
706
|
+
self.originalInput = input;
|
706
707
|
/** @type {number} */
|
707
708
|
self.r = r;
|
708
709
|
/** @type {number} */
|
@@ -715,14 +716,6 @@ export default class Color {
|
|
715
716
|
self.ok = ok;
|
716
717
|
/** @type {CP.ColorFormats} */
|
717
718
|
self.format = configFormat || format;
|
718
|
-
|
719
|
-
// Don't let the range of [0,255] come back in [0,1].
|
720
|
-
// Potentially lose a little bit of precision here, but will fix issues where
|
721
|
-
// .5 gets interpreted as half of the total, instead of half of 1
|
722
|
-
// If it was supposed to be 128, this was already taken care of by `inputToRgb`
|
723
|
-
if (r < 1) self.r = Math.round(r);
|
724
|
-
if (g < 1) self.g = Math.round(g);
|
725
|
-
if (b < 1) self.b = Math.round(b);
|
726
719
|
}
|
727
720
|
|
728
721
|
/**
|
@@ -738,7 +731,7 @@ export default class Color {
|
|
738
731
|
* @returns {boolean} the query result
|
739
732
|
*/
|
740
733
|
get isDark() {
|
741
|
-
return this.brightness <
|
734
|
+
return this.brightness < 120;
|
742
735
|
}
|
743
736
|
|
744
737
|
/**
|
@@ -790,13 +783,9 @@ export default class Color {
|
|
790
783
|
const {
|
791
784
|
r, g, b, a,
|
792
785
|
} = this;
|
793
|
-
const [R, G, B] = [r, g, b].map((x) => Math.round(x));
|
794
786
|
|
795
787
|
return {
|
796
|
-
r:
|
797
|
-
g: G,
|
798
|
-
b: B,
|
799
|
-
a: Math.round(a * 100) / 100,
|
788
|
+
r, g, b, a: roundPart(a * 100) / 100,
|
800
789
|
};
|
801
790
|
}
|
802
791
|
|
@@ -810,10 +799,11 @@ export default class Color {
|
|
810
799
|
const {
|
811
800
|
r, g, b, a,
|
812
801
|
} = this.toRgb();
|
802
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
813
803
|
|
814
804
|
return a === 1
|
815
|
-
? `rgb(${
|
816
|
-
: `rgba(${
|
805
|
+
? `rgb(${R}, ${G}, ${B})`
|
806
|
+
: `rgba(${R}, ${G}, ${B}, ${a})`;
|
817
807
|
}
|
818
808
|
|
819
809
|
/**
|
@@ -826,9 +816,10 @@ export default class Color {
|
|
826
816
|
const {
|
827
817
|
r, g, b, a,
|
828
818
|
} = this.toRgb();
|
829
|
-
const
|
819
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
820
|
+
const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
|
830
821
|
|
831
|
-
return `rgb(${
|
822
|
+
return `rgb(${R} ${G} ${B}${A})`;
|
832
823
|
}
|
833
824
|
|
834
825
|
/**
|
@@ -921,10 +912,10 @@ export default class Color {
|
|
921
912
|
let {
|
922
913
|
h, s, l, a,
|
923
914
|
} = this.toHsl();
|
924
|
-
h =
|
925
|
-
s =
|
926
|
-
l =
|
927
|
-
a =
|
915
|
+
h = roundPart(h * 360);
|
916
|
+
s = roundPart(s * 100);
|
917
|
+
l = roundPart(l * 100);
|
918
|
+
a = roundPart(a * 100) / 100;
|
928
919
|
|
929
920
|
return a === 1
|
930
921
|
? `hsl(${h}, ${s}%, ${l}%)`
|
@@ -941,11 +932,11 @@ export default class Color {
|
|
941
932
|
let {
|
942
933
|
h, s, l, a,
|
943
934
|
} = this.toHsl();
|
944
|
-
h =
|
945
|
-
s =
|
946
|
-
l =
|
947
|
-
a =
|
948
|
-
const A = a < 100 ? ` / ${
|
935
|
+
h = roundPart(h * 360);
|
936
|
+
s = roundPart(s * 100);
|
937
|
+
l = roundPart(l * 100);
|
938
|
+
a = roundPart(a * 100);
|
939
|
+
const A = a < 100 ? ` / ${roundPart(a)}%` : '';
|
949
940
|
|
950
941
|
return `hsl(${h}deg ${s}% ${l}%${A})`;
|
951
942
|
}
|
@@ -972,11 +963,11 @@ export default class Color {
|
|
972
963
|
let {
|
973
964
|
h, w, b, a,
|
974
965
|
} = this.toHwb();
|
975
|
-
h =
|
976
|
-
w =
|
977
|
-
b =
|
978
|
-
a =
|
979
|
-
const A = a < 100 ? ` / ${
|
966
|
+
h = roundPart(h * 360);
|
967
|
+
w = roundPart(w * 100);
|
968
|
+
b = roundPart(b * 100);
|
969
|
+
a = roundPart(a * 100);
|
970
|
+
const A = a < 100 ? ` / ${roundPart(a)}%` : '';
|
980
971
|
|
981
972
|
return `hwb(${h}deg ${w}% ${b}%${A})`;
|
982
973
|
}
|
@@ -1102,6 +1093,7 @@ ObjectAssign(Color, {
|
|
1102
1093
|
isOnePointZero,
|
1103
1094
|
isPercentage,
|
1104
1095
|
isValidCSSUnit,
|
1096
|
+
isColorName,
|
1105
1097
|
pad2,
|
1106
1098
|
clamp01,
|
1107
1099
|
bound01,
|
@@ -1119,8 +1111,10 @@ ObjectAssign(Color, {
|
|
1119
1111
|
hueToRgb,
|
1120
1112
|
hwbToRgb,
|
1121
1113
|
parseIntFromHex,
|
1122
|
-
numberInputToObject,
|
1123
1114
|
stringInputToObject,
|
1124
1115
|
inputToRGB,
|
1116
|
+
roundPart,
|
1117
|
+
getElementStyle,
|
1118
|
+
setElementStyle,
|
1125
1119
|
ObjectAssign,
|
1126
1120
|
});
|
@@ -4,6 +4,8 @@ import ariaValueMax from 'shorter-js/src/strings/ariaValueMax';
|
|
4
4
|
import createElement from 'shorter-js/src/misc/createElement';
|
5
5
|
import setAttribute from 'shorter-js/src/attr/setAttribute';
|
6
6
|
|
7
|
+
import tabIndex from './tabindex';
|
8
|
+
|
7
9
|
/**
|
8
10
|
* Returns all color controls for `ColorPicker`.
|
9
11
|
*
|
@@ -69,10 +71,8 @@ export default function getColorControls(self) {
|
|
69
71
|
const {
|
70
72
|
i, c, l, min, max,
|
71
73
|
} = template;
|
72
|
-
// const hidden = i === 2 && format === 'hwb' ? ' v-hidden' : '';
|
73
74
|
const control = createElement({
|
74
75
|
tagName: 'div',
|
75
|
-
// className: `color-control${hidden}`,
|
76
76
|
className: 'color-control',
|
77
77
|
});
|
78
78
|
setAttribute(control, 'role', 'presentation');
|
@@ -92,7 +92,7 @@ export default function getColorControls(self) {
|
|
92
92
|
|
93
93
|
setAttribute(knob, ariaLabel, l);
|
94
94
|
setAttribute(knob, 'role', 'slider');
|
95
|
-
setAttribute(knob,
|
95
|
+
setAttribute(knob, tabIndex, '0');
|
96
96
|
setAttribute(knob, ariaValueMin, `${min}`);
|
97
97
|
setAttribute(knob, ariaValueMax, `${max}`);
|
98
98
|
control.append(knob);
|
@@ -5,6 +5,8 @@ import getAttribute from 'shorter-js/src/attr/getAttribute';
|
|
5
5
|
import createElement from 'shorter-js/src/misc/createElement';
|
6
6
|
import setElementStyle from 'shorter-js/src/misc/setElementStyle';
|
7
7
|
|
8
|
+
import setCSSProperties from './setCSSProperties';
|
9
|
+
import tabIndex from './tabindex';
|
8
10
|
import Color from '../color';
|
9
11
|
import ColorPalette from '../color-palette';
|
10
12
|
|
@@ -25,68 +27,64 @@ export default function getColorMenu(self, colorsSource, menuClass) {
|
|
25
27
|
colorsArray = colorsArray instanceof Array ? colorsArray : [];
|
26
28
|
const colorsCount = colorsArray.length;
|
27
29
|
const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
|
28
|
-
|
29
|
-
|| Math.max(...[5, 6, 7, 8, 9, 10].filter((x) => colorsCount > (x * 2) && !(colorsCount % x)));
|
30
|
-
fit = Number.isFinite(fit) ? fit : 5;
|
30
|
+
const fit = lightSteps || [9, 10].find((x) => colorsCount > x * 2 && !(colorsCount % x)) || 5;
|
31
31
|
const isMultiLine = isOptionsMenu && colorsCount > fit;
|
32
|
-
let rowCountHover =
|
33
|
-
rowCountHover = isMultiLine && colorsCount
|
34
|
-
rowCountHover = colorsCount >=
|
35
|
-
rowCountHover = colorsCount >=
|
36
|
-
|
37
|
-
const
|
38
|
-
const isScrollable = isMultiLine && colorsCount > rowCountHover * fit;
|
32
|
+
let rowCountHover = 2;
|
33
|
+
rowCountHover = isMultiLine && colorsCount >= fit * 2 ? 3 : rowCountHover;
|
34
|
+
rowCountHover = colorsCount >= fit * 3 ? 4 : rowCountHover;
|
35
|
+
rowCountHover = colorsCount >= fit * 4 ? 5 : rowCountHover;
|
36
|
+
const rowCount = rowCountHover - (colorsCount < fit * 3 ? 1 : 2);
|
37
|
+
const isScrollable = isMultiLine && colorsCount > rowCount * fit;
|
39
38
|
let finalClass = menuClass;
|
40
39
|
finalClass += isScrollable ? ' scrollable' : '';
|
41
40
|
finalClass += isMultiLine ? ' multiline' : '';
|
42
41
|
const gap = isMultiLine ? '1px' : '0.25rem';
|
43
42
|
let optionSize = isMultiLine ? 1.75 : 2;
|
44
|
-
optionSize =
|
43
|
+
optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
|
45
44
|
const menuHeight = `${(rowCount || 1) * optionSize}rem`;
|
46
45
|
const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
/** @type {HTMLUListElement} */
|
47
|
+
// @ts-ignore -- <UL> is an `HTMLElement`
|
50
48
|
const menu = createElement({
|
51
49
|
tagName: 'ul',
|
52
50
|
className: finalClass,
|
53
51
|
});
|
54
52
|
setAttribute(menu, 'role', 'listbox');
|
55
|
-
setAttribute(menu, ariaLabel,
|
53
|
+
setAttribute(menu, ariaLabel, menuLabel);
|
56
54
|
|
57
|
-
if (
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
};
|
66
|
-
setElementStyle(menu, menuStyle);
|
55
|
+
if (isScrollable) {
|
56
|
+
setCSSProperties(menu, {
|
57
|
+
'--grid-item-size': `${optionSize}rem`,
|
58
|
+
'--grid-fit': fit,
|
59
|
+
'--grid-gap': gap,
|
60
|
+
'--grid-height': menuHeight,
|
61
|
+
'--grid-hover-height': menuHeightHover,
|
62
|
+
});
|
67
63
|
}
|
68
64
|
|
69
65
|
colorsArray.forEach((x) => {
|
70
|
-
|
71
|
-
|
72
|
-
|
66
|
+
let [value, label] = typeof x === 'string' ? x.trim().split(':') : [];
|
67
|
+
if (x instanceof Color) {
|
68
|
+
value = x.toHexString();
|
69
|
+
label = value;
|
70
|
+
}
|
71
|
+
const color = new Color(x instanceof Color ? x : value, format);
|
72
|
+
const isActive = color.toString() === getAttribute(input, 'value');
|
73
73
|
const active = isActive ? ' active' : '';
|
74
74
|
|
75
75
|
const option = createElement({
|
76
76
|
tagName: 'li',
|
77
77
|
className: `color-option${active}`,
|
78
|
-
innerText: `${label ||
|
78
|
+
innerText: `${label || value}`,
|
79
79
|
});
|
80
80
|
|
81
|
-
setAttribute(option,
|
81
|
+
setAttribute(option, tabIndex, '0');
|
82
82
|
setAttribute(option, 'data-value', `${value}`);
|
83
83
|
setAttribute(option, 'role', 'option');
|
84
84
|
setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
|
85
85
|
|
86
86
|
if (isOptionsMenu) {
|
87
|
-
setElementStyle(option, {
|
88
|
-
width: `${optionSize}rem`, height: `${optionSize}rem`, backgroundColor: x,
|
89
|
-
});
|
87
|
+
setElementStyle(option, { backgroundColor: value });
|
90
88
|
}
|
91
89
|
|
92
90
|
menu.append(option);
|
@@ -0,0 +1,9 @@
|
|
1
|
+
/**
|
2
|
+
* Round colour components, for all formats except HEX.
|
3
|
+
* @param {number} v one of the colour components
|
4
|
+
* @returns {number} the rounded number
|
5
|
+
*/
|
6
|
+
export default function roundPart(v) {
|
7
|
+
const floor = Math.floor(v);
|
8
|
+
return v - floor < 0.5 ? floor : Math.round(v);
|
9
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import ObjectKeys from 'shorter-js/src/misc/ObjectKeys';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Helps setting CSS variables to the color-menu.
|
5
|
+
* @param {HTMLElement} element
|
6
|
+
* @param {Record<string,any>} props
|
7
|
+
*/
|
8
|
+
export default function setCSSProperties(element, props) {
|
9
|
+
ObjectKeys(props).forEach((prop) => {
|
10
|
+
element.style.setProperty(prop, props[prop]);
|
11
|
+
});
|
12
|
+
}
|