@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/README.md +3 -1
- package/dist/css/color-picker.css +3 -2
- package/dist/css/color-picker.min.css +2 -2
- package/dist/css/color-picker.rtl.css +3 -2
- package/dist/css/color-picker.rtl.min.css +2 -2
- package/dist/js/color-esm.js +1167 -0
- package/dist/js/color-esm.min.js +2 -0
- package/dist/js/color-palette-esm.js +1238 -0
- package/dist/js/color-palette-esm.min.js +2 -0
- package/dist/js/color-palette.js +1246 -0
- package/dist/js/color-palette.min.js +2 -0
- package/dist/js/color-picker-element-esm.js +567 -683
- package/dist/js/color-picker-element-esm.min.js +2 -2
- package/dist/js/color-picker-element.js +569 -685
- package/dist/js/color-picker-element.min.js +2 -2
- package/dist/js/color-picker-esm.js +782 -890
- package/dist/js/color-picker-esm.min.js +2 -2
- package/dist/js/color-picker.js +784 -892
- package/dist/js/color-picker.min.js +2 -2
- package/dist/js/color.js +1175 -0
- package/dist/js/color.min.js +2 -0
- package/package.json +22 -3
- package/src/js/color-palette.js +18 -14
- package/src/js/color-picker-element.js +47 -55
- package/src/js/color-picker.js +155 -329
- package/src/js/color.js +175 -193
- package/src/js/util/getColorMenu.js +12 -7
- package/src/js/util/setMarkup.js +122 -0
- package/src/js/util/version.js +6 -0
- package/src/scss/color-picker.scss +3 -7
- package/types/cp.d.ts +64 -32
- package/types/source/types.d.ts +1 -1
- package/src/js/util/templates.js +0 -10
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* ColorPickerElement v0.0.
|
2
|
+
* ColorPickerElement v0.0.2alpha2 (http://thednp.github.io/color-picker)
|
3
3
|
* Copyright 2022 © thednp
|
4
4
|
* Licensed under MIT (https://github.com/thednp/color-picker/blob/master/LICENSE)
|
5
5
|
*/
|
@@ -82,14 +82,9 @@ const setAttribute = (element, attribute, value) => element.setAttribute(attribu
|
|
82
82
|
const getAttribute = (element, attribute) => element.getAttribute(attribute);
|
83
83
|
|
84
84
|
/**
|
85
|
-
*
|
86
|
-
*
|
87
|
-
* @param {(Node | HTMLElement | Element | globalThis)=} node
|
88
|
-
* @returns {HTMLElement | HTMLHeadElement}
|
85
|
+
* A global namespace for `document.head`.
|
89
86
|
*/
|
90
|
-
|
91
|
-
return getDocument(node).head;
|
92
|
-
}
|
87
|
+
const { head: documentHead } = document;
|
93
88
|
|
94
89
|
/**
|
95
90
|
* Shortcut for `window.getComputedStyle(element).propertyName`
|
@@ -110,20 +105,21 @@ function getElementStyle(element, property) {
|
|
110
105
|
return property in computedStyle ? computedStyle[property] : '';
|
111
106
|
}
|
112
107
|
|
113
|
-
/**
|
114
|
-
* Shortcut for `Object.keys()` static method.
|
115
|
-
* @param {Record<string, any>} obj a target object
|
116
|
-
* @returns {string[]}
|
117
|
-
*/
|
118
|
-
const ObjectKeys = (obj) => Object.keys(obj);
|
119
|
-
|
120
108
|
/**
|
121
109
|
* Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
|
122
110
|
* @param {HTMLElement | Element} element target element
|
123
111
|
* @param {Partial<CSSStyleDeclaration>} styles attribute value
|
124
112
|
*/
|
125
113
|
// @ts-ignore
|
126
|
-
const setElementStyle = (element, styles) => ObjectAssign(element.style, styles);
|
114
|
+
const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Shortcut for `String.toLowerCase()`.
|
118
|
+
*
|
119
|
+
* @param {string} source input string
|
120
|
+
* @returns {string} lowercase output string
|
121
|
+
*/
|
122
|
+
const toLowerCase = (source) => source.toLowerCase();
|
127
123
|
|
128
124
|
/**
|
129
125
|
* A list of explicit default non-color values.
|
@@ -141,7 +137,7 @@ function roundPart(v) {
|
|
141
137
|
}
|
142
138
|
|
143
139
|
// Color supported formats
|
144
|
-
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', '
|
140
|
+
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
|
145
141
|
|
146
142
|
// Hue angles
|
147
143
|
const ANGLES = 'deg|rad|grad|turn';
|
@@ -163,10 +159,17 @@ const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
|
|
163
159
|
// Add angles to the mix
|
164
160
|
const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
|
165
161
|
|
162
|
+
// Start & end
|
163
|
+
const START_MATCH = '(?:[\\s|\\(\\s|\\s\\(\\s]+)?';
|
164
|
+
const END_MATCH = '(?:[\\s|\\)\\s]+)?';
|
165
|
+
// Components separation
|
166
|
+
const SEP = '(?:[,|\\s]+)';
|
167
|
+
const SEP2 = '(?:[,|\\/\\s]*)?';
|
168
|
+
|
166
169
|
// Actual matching.
|
167
170
|
// Parentheses and commas are optional, but not required.
|
168
171
|
// Whitespace can take the place of commas or opening paren
|
169
|
-
const PERMISSIVE_MATCH =
|
172
|
+
const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
|
170
173
|
|
171
174
|
const matchers = {
|
172
175
|
CSS_UNIT: new RegExp(CSS_UNIT2),
|
@@ -199,23 +202,24 @@ function isPercentage(n) {
|
|
199
202
|
return `${n}`.includes('%');
|
200
203
|
}
|
201
204
|
|
202
|
-
/**
|
203
|
-
* Check to see if string passed in is an angle
|
204
|
-
* @param {string} n testing string
|
205
|
-
* @returns {boolean} the query result
|
206
|
-
*/
|
207
|
-
function isAngle(n) {
|
208
|
-
return ANGLES.split('|').some((a) => `${n}`.includes(a));
|
209
|
-
}
|
210
|
-
|
211
205
|
/**
|
212
206
|
* Check to see if string passed is a web safe colour.
|
207
|
+
* @see https://stackoverflow.com/a/16994164
|
213
208
|
* @param {string} color a colour name, EG: *red*
|
214
209
|
* @returns {boolean} the query result
|
215
210
|
*/
|
216
211
|
function isColorName(color) {
|
217
|
-
|
218
|
-
|
212
|
+
if (nonColors.includes(color)
|
213
|
+
|| ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
|
214
|
+
|
215
|
+
if (['black', 'white'].includes(color)) return true;
|
216
|
+
|
217
|
+
return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
|
218
|
+
setElementStyle(documentHead, { color });
|
219
|
+
const computedColor = getElementStyle(documentHead, 'color');
|
220
|
+
setElementStyle(documentHead, { color: '' });
|
221
|
+
return computedColor !== c;
|
222
|
+
});
|
219
223
|
}
|
220
224
|
|
221
225
|
/**
|
@@ -236,15 +240,20 @@ function isValidCSSUnit(color) {
|
|
236
240
|
*/
|
237
241
|
function bound01(N, max) {
|
238
242
|
let n = N;
|
239
|
-
if (isOnePointZero(n)) n = '100%';
|
240
243
|
|
241
|
-
|
244
|
+
if (typeof N === 'number'
|
245
|
+
&& Math.min(N, 0) === 0 // round values to 6 decimals Math.round(N * (10 ** 6)) / 10 ** 6
|
246
|
+
&& Math.max(N, 1) === 1) return N;
|
247
|
+
|
248
|
+
if (isOnePointZero(N)) n = '100%';
|
242
249
|
|
243
|
-
|
244
|
-
|
250
|
+
const processPercent = isPercentage(n);
|
251
|
+
n = max === 360
|
252
|
+
? parseFloat(n)
|
253
|
+
: Math.min(max, Math.max(0, parseFloat(n)));
|
245
254
|
|
246
255
|
// Automatically convert percentage into number
|
247
|
-
if (
|
256
|
+
if (processPercent) n = (n * max) / 100;
|
248
257
|
|
249
258
|
// Handle floating point rounding errors
|
250
259
|
if (Math.abs(n - max) < 0.000001) {
|
@@ -255,11 +264,11 @@ function bound01(N, max) {
|
|
255
264
|
// If n is a hue given in degrees,
|
256
265
|
// wrap around out-of-range values into [0, 360] range
|
257
266
|
// then convert into [0, 1].
|
258
|
-
n = (n < 0 ? (n % max) + max : n % max) /
|
267
|
+
n = (n < 0 ? (n % max) + max : n % max) / max;
|
259
268
|
} else {
|
260
269
|
// If n not a hue given in degrees
|
261
270
|
// Convert into [0, 1] range if it isn't already.
|
262
|
-
n = (n % max) /
|
271
|
+
n = (n % max) / max;
|
263
272
|
}
|
264
273
|
return n;
|
265
274
|
}
|
@@ -294,7 +303,6 @@ function clamp01(v) {
|
|
294
303
|
* @returns {string}
|
295
304
|
*/
|
296
305
|
function getRGBFromName(name) {
|
297
|
-
const documentHead = getDocumentHead();
|
298
306
|
setElementStyle(documentHead, { color: name });
|
299
307
|
const colorName = getElementStyle(documentHead, 'color');
|
300
308
|
setElementStyle(documentHead, { color: '' });
|
@@ -340,15 +348,12 @@ function pad2(c) {
|
|
340
348
|
/**
|
341
349
|
* Converts an RGB colour value to HSL.
|
342
350
|
*
|
343
|
-
* @param {number}
|
344
|
-
* @param {number}
|
345
|
-
* @param {number}
|
351
|
+
* @param {number} r Red component [0, 1]
|
352
|
+
* @param {number} g Green component [0, 1]
|
353
|
+
* @param {number} b Blue component [0, 1]
|
346
354
|
* @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
|
347
355
|
*/
|
348
|
-
function rgbToHsl(
|
349
|
-
const r = R / 255;
|
350
|
-
const g = G / 255;
|
351
|
-
const b = B / 255;
|
356
|
+
function rgbToHsl(r, g, b) {
|
352
357
|
const max = Math.max(r, g, b);
|
353
358
|
const min = Math.min(r, g, b);
|
354
359
|
let h = 0;
|
@@ -360,17 +365,10 @@ function rgbToHsl(R, G, B) {
|
|
360
365
|
} else {
|
361
366
|
const d = max - min;
|
362
367
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
case g:
|
368
|
-
h = (b - r) / d + 2;
|
369
|
-
break;
|
370
|
-
case b:
|
371
|
-
h = (r - g) / d + 4;
|
372
|
-
break;
|
373
|
-
}
|
368
|
+
if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
|
369
|
+
if (max === g) h = (b - r) / d + 2;
|
370
|
+
if (max === b) h = (r - g) / d + 4;
|
371
|
+
|
374
372
|
h /= 6;
|
375
373
|
}
|
376
374
|
return { h, s, l };
|
@@ -393,21 +391,46 @@ function hueToRgb(p, q, t) {
|
|
393
391
|
return p;
|
394
392
|
}
|
395
393
|
|
394
|
+
/**
|
395
|
+
* Converts an HSL colour value to RGB.
|
396
|
+
*
|
397
|
+
* @param {number} h Hue Angle [0, 1]
|
398
|
+
* @param {number} s Saturation [0, 1]
|
399
|
+
* @param {number} l Lightness Angle [0, 1]
|
400
|
+
* @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
|
401
|
+
*/
|
402
|
+
function hslToRgb(h, s, l) {
|
403
|
+
let r = 0;
|
404
|
+
let g = 0;
|
405
|
+
let b = 0;
|
406
|
+
|
407
|
+
if (s === 0) {
|
408
|
+
// achromatic
|
409
|
+
g = l;
|
410
|
+
b = l;
|
411
|
+
r = l;
|
412
|
+
} else {
|
413
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
414
|
+
const p = 2 * l - q;
|
415
|
+
r = hueToRgb(p, q, h + 1 / 3);
|
416
|
+
g = hueToRgb(p, q, h);
|
417
|
+
b = hueToRgb(p, q, h - 1 / 3);
|
418
|
+
}
|
419
|
+
|
420
|
+
return { r, g, b };
|
421
|
+
}
|
422
|
+
|
396
423
|
/**
|
397
424
|
* Returns an HWB colour object from an RGB colour object.
|
398
425
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
399
426
|
* @link http://alvyray.com/Papers/CG/hwb2rgb.htm
|
400
427
|
*
|
401
|
-
* @param {number}
|
402
|
-
* @param {number}
|
403
|
-
* @param {number}
|
428
|
+
* @param {number} r Red component [0, 1]
|
429
|
+
* @param {number} g Green [0, 1]
|
430
|
+
* @param {number} b Blue [0, 1]
|
404
431
|
* @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
|
405
432
|
*/
|
406
|
-
function rgbToHwb(
|
407
|
-
const r = R / 255;
|
408
|
-
const g = G / 255;
|
409
|
-
const b = B / 255;
|
410
|
-
|
433
|
+
function rgbToHwb(r, g, b) {
|
411
434
|
let f = 0;
|
412
435
|
let i = 0;
|
413
436
|
const whiteness = Math.min(r, g, b);
|
@@ -437,50 +460,18 @@ function rgbToHwb(R, G, B) {
|
|
437
460
|
* @param {number} H Hue Angle [0, 1]
|
438
461
|
* @param {number} W Whiteness [0, 1]
|
439
462
|
* @param {number} B Blackness [0, 1]
|
440
|
-
* @return {CP.RGB} {r,g,b} object with [0,
|
463
|
+
* @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
|
441
464
|
*
|
442
465
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
443
466
|
* @link http://alvyray.com/Papers/CG/hwb2rgb.htm
|
444
467
|
*/
|
445
468
|
function hwbToRgb(H, W, B) {
|
446
469
|
if (W + B >= 1) {
|
447
|
-
const gray =
|
470
|
+
const gray = W / (W + B);
|
448
471
|
return { r: gray, g: gray, b: gray };
|
449
472
|
}
|
450
473
|
let { r, g, b } = hslToRgb(H, 1, 0.5);
|
451
|
-
[r, g, b] = [r, g, b]
|
452
|
-
.map((v) => (v / 255) * (1 - W - B) + W)
|
453
|
-
.map((v) => v * 255);
|
454
|
-
|
455
|
-
return { r, g, b };
|
456
|
-
}
|
457
|
-
|
458
|
-
/**
|
459
|
-
* Converts an HSL colour value to RGB.
|
460
|
-
*
|
461
|
-
* @param {number} h Hue Angle [0, 1]
|
462
|
-
* @param {number} s Saturation [0, 1]
|
463
|
-
* @param {number} l Lightness Angle [0, 1]
|
464
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
465
|
-
*/
|
466
|
-
function hslToRgb(h, s, l) {
|
467
|
-
let r = 0;
|
468
|
-
let g = 0;
|
469
|
-
let b = 0;
|
470
|
-
|
471
|
-
if (s === 0) {
|
472
|
-
// achromatic
|
473
|
-
g = l;
|
474
|
-
b = l;
|
475
|
-
r = l;
|
476
|
-
} else {
|
477
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
478
|
-
const p = 2 * l - q;
|
479
|
-
r = hueToRgb(p, q, h + 1 / 3);
|
480
|
-
g = hueToRgb(p, q, h);
|
481
|
-
b = hueToRgb(p, q, h - 1 / 3);
|
482
|
-
}
|
483
|
-
[r, g, b] = [r, g, b].map((x) => x * 255);
|
474
|
+
[r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
|
484
475
|
|
485
476
|
return { r, g, b };
|
486
477
|
}
|
@@ -488,15 +479,12 @@ function hslToRgb(h, s, l) {
|
|
488
479
|
/**
|
489
480
|
* Converts an RGB colour value to HSV.
|
490
481
|
*
|
491
|
-
* @param {number}
|
492
|
-
* @param {number}
|
493
|
-
* @param {number}
|
482
|
+
* @param {number} r Red component [0, 1]
|
483
|
+
* @param {number} g Green [0, 1]
|
484
|
+
* @param {number} b Blue [0, 1]
|
494
485
|
* @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
|
495
486
|
*/
|
496
|
-
function rgbToHsv(
|
497
|
-
const r = R / 255;
|
498
|
-
const g = G / 255;
|
499
|
-
const b = B / 255;
|
487
|
+
function rgbToHsv(r, g, b) {
|
500
488
|
const max = Math.max(r, g, b);
|
501
489
|
const min = Math.min(r, g, b);
|
502
490
|
let h = 0;
|
@@ -506,17 +494,10 @@ function rgbToHsv(R, G, B) {
|
|
506
494
|
if (max === min) {
|
507
495
|
h = 0; // achromatic
|
508
496
|
} else {
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
case g:
|
514
|
-
h = (b - r) / d + 2;
|
515
|
-
break;
|
516
|
-
case b:
|
517
|
-
h = (r - g) / d + 4;
|
518
|
-
break;
|
519
|
-
}
|
497
|
+
if (r === max) h = (g - b) / d + (g < b ? 6 : 0);
|
498
|
+
if (g === max) h = (b - r) / d + 2;
|
499
|
+
if (b === max) h = (r - g) / d + 4;
|
500
|
+
|
520
501
|
h /= 6;
|
521
502
|
}
|
522
503
|
return { h, s, v };
|
@@ -543,7 +524,7 @@ function hsvToRgb(H, S, V) {
|
|
543
524
|
const r = [v, q, p, p, t, v][mod];
|
544
525
|
const g = [t, v, v, q, p, p][mod];
|
545
526
|
const b = [p, p, t, v, v, q][mod];
|
546
|
-
return { r
|
527
|
+
return { r, g, b };
|
547
528
|
}
|
548
529
|
|
549
530
|
/**
|
@@ -567,7 +548,7 @@ function rgbToHex(r, g, b, allow3Char) {
|
|
567
548
|
// Return a 3 character hex if possible
|
568
549
|
if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
|
569
550
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
570
|
-
|
551
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)) {
|
571
552
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
|
572
553
|
}
|
573
554
|
|
@@ -595,51 +576,34 @@ function rgbaToHex(r, g, b, a, allow4Char) {
|
|
595
576
|
// Return a 4 character hex if possible
|
596
577
|
if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
|
597
578
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
598
|
-
|
599
|
-
|
579
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)
|
580
|
+
&& hex[3].charAt(0) === hex[3].charAt(1)) {
|
600
581
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
|
601
582
|
}
|
602
583
|
return hex.join('');
|
603
584
|
}
|
604
585
|
|
605
|
-
/**
|
606
|
-
* Returns a colour object corresponding to a given number.
|
607
|
-
* @param {number} color input number
|
608
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
609
|
-
*/
|
610
|
-
function numberInputToObject(color) {
|
611
|
-
/* eslint-disable no-bitwise */
|
612
|
-
return {
|
613
|
-
r: color >> 16,
|
614
|
-
g: (color & 0xff00) >> 8,
|
615
|
-
b: color & 0xff,
|
616
|
-
};
|
617
|
-
/* eslint-enable no-bitwise */
|
618
|
-
}
|
619
|
-
|
620
586
|
/**
|
621
587
|
* Permissive string parsing. Take in a number of formats, and output an object
|
622
588
|
* based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
|
623
589
|
* @param {string} input colour value in any format
|
624
|
-
* @returns {Record<string, (number | string)> | false} an object matching the RegExp
|
590
|
+
* @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
|
625
591
|
*/
|
626
592
|
function stringInputToObject(input) {
|
627
|
-
let color = input.trim()
|
593
|
+
let color = toLowerCase(input.trim());
|
594
|
+
|
628
595
|
if (color.length === 0) {
|
629
596
|
return {
|
630
|
-
r: 0, g: 0, b: 0, a:
|
597
|
+
r: 0, g: 0, b: 0, a: 1,
|
631
598
|
};
|
632
599
|
}
|
633
|
-
|
600
|
+
|
634
601
|
if (isColorName(color)) {
|
635
602
|
color = getRGBFromName(color);
|
636
|
-
named = true;
|
637
603
|
} else if (nonColors.includes(color)) {
|
638
|
-
const
|
639
|
-
const rgb = isTransparent ? 0 : 255;
|
640
|
-
const a = isTransparent ? 0 : 1;
|
604
|
+
const a = color === 'transparent' ? 0 : 1;
|
641
605
|
return {
|
642
|
-
r:
|
606
|
+
r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
|
643
607
|
};
|
644
608
|
}
|
645
609
|
|
@@ -654,24 +618,28 @@ function stringInputToObject(input) {
|
|
654
618
|
r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
|
655
619
|
};
|
656
620
|
}
|
621
|
+
|
657
622
|
[, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
|
658
623
|
if (m1 && m2 && m3/* && m4 */) {
|
659
624
|
return {
|
660
625
|
h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
|
661
626
|
};
|
662
627
|
}
|
628
|
+
|
663
629
|
[, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
|
664
630
|
if (m1 && m2 && m3/* && m4 */) {
|
665
631
|
return {
|
666
632
|
h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
|
667
633
|
};
|
668
634
|
}
|
635
|
+
|
669
636
|
[, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
|
670
637
|
if (m1 && m2 && m3) {
|
671
638
|
return {
|
672
639
|
h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
|
673
640
|
};
|
674
641
|
}
|
642
|
+
|
675
643
|
[, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
|
676
644
|
if (m1 && m2 && m3 && m4) {
|
677
645
|
return {
|
@@ -679,19 +647,20 @@ function stringInputToObject(input) {
|
|
679
647
|
g: parseIntFromHex(m2),
|
680
648
|
b: parseIntFromHex(m3),
|
681
649
|
a: convertHexToDecimal(m4),
|
682
|
-
|
683
|
-
format: named ? 'rgb' : 'hex',
|
650
|
+
format: 'hex',
|
684
651
|
};
|
685
652
|
}
|
653
|
+
|
686
654
|
[, m1, m2, m3] = matchers.hex6.exec(color) || [];
|
687
655
|
if (m1 && m2 && m3) {
|
688
656
|
return {
|
689
657
|
r: parseIntFromHex(m1),
|
690
658
|
g: parseIntFromHex(m2),
|
691
659
|
b: parseIntFromHex(m3),
|
692
|
-
format:
|
660
|
+
format: 'hex',
|
693
661
|
};
|
694
662
|
}
|
663
|
+
|
695
664
|
[, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
|
696
665
|
if (m1 && m2 && m3 && m4) {
|
697
666
|
return {
|
@@ -699,19 +668,20 @@ function stringInputToObject(input) {
|
|
699
668
|
g: parseIntFromHex(m2 + m2),
|
700
669
|
b: parseIntFromHex(m3 + m3),
|
701
670
|
a: convertHexToDecimal(m4 + m4),
|
702
|
-
|
703
|
-
format: named ? 'rgb' : 'hex',
|
671
|
+
format: 'hex',
|
704
672
|
};
|
705
673
|
}
|
674
|
+
|
706
675
|
[, m1, m2, m3] = matchers.hex3.exec(color) || [];
|
707
676
|
if (m1 && m2 && m3) {
|
708
677
|
return {
|
709
678
|
r: parseIntFromHex(m1 + m1),
|
710
679
|
g: parseIntFromHex(m2 + m2),
|
711
680
|
b: parseIntFromHex(m3 + m3),
|
712
|
-
format:
|
681
|
+
format: 'hex',
|
713
682
|
};
|
714
683
|
}
|
684
|
+
|
715
685
|
return false;
|
716
686
|
}
|
717
687
|
|
@@ -742,7 +712,9 @@ function stringInputToObject(input) {
|
|
742
712
|
*/
|
743
713
|
function inputToRGB(input) {
|
744
714
|
let rgb = { r: 0, g: 0, b: 0 };
|
715
|
+
/** @type {*} */
|
745
716
|
let color = input;
|
717
|
+
/** @type {string | number} */
|
746
718
|
let a = 1;
|
747
719
|
let s = null;
|
748
720
|
let v = null;
|
@@ -753,58 +725,67 @@ function inputToRGB(input) {
|
|
753
725
|
let r = null;
|
754
726
|
let g = null;
|
755
727
|
let ok = false;
|
756
|
-
|
728
|
+
const inputFormat = typeof color === 'object' && color.format;
|
729
|
+
let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
|
757
730
|
|
758
731
|
if (typeof input === 'string') {
|
759
|
-
// @ts-ignore -- this now is converted to object
|
760
732
|
color = stringInputToObject(input);
|
761
733
|
if (color) ok = true;
|
762
734
|
}
|
763
735
|
if (typeof color === 'object') {
|
764
736
|
if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
|
765
737
|
({ r, g, b } = color);
|
766
|
-
|
767
|
-
|
768
|
-
rgb = { r, g, b };
|
738
|
+
// RGB values now are all in [0, 1] range
|
739
|
+
[r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255));
|
740
|
+
rgb = { r, g, b };
|
769
741
|
ok = true;
|
770
|
-
format = 'rgb';
|
771
|
-
}
|
742
|
+
format = color.format || 'rgb';
|
743
|
+
}
|
744
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
|
772
745
|
({ h, s, v } = color);
|
773
|
-
h =
|
774
|
-
s =
|
775
|
-
v =
|
746
|
+
h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
|
747
|
+
s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
|
748
|
+
v = bound01(v, 100); // brightness can be `5%` or a [0, 1] value
|
776
749
|
rgb = hsvToRgb(h, s, v);
|
777
750
|
ok = true;
|
778
751
|
format = 'hsv';
|
779
|
-
}
|
752
|
+
}
|
753
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
|
780
754
|
({ h, s, l } = color);
|
781
|
-
h =
|
782
|
-
s =
|
783
|
-
l =
|
755
|
+
h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
|
756
|
+
s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
|
757
|
+
l = bound01(l, 100); // lightness can be `5%` or a [0, 1] value
|
784
758
|
rgb = hslToRgb(h, s, l);
|
785
759
|
ok = true;
|
786
760
|
format = 'hsl';
|
787
|
-
}
|
761
|
+
}
|
762
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
|
788
763
|
({ h, w, b } = color);
|
789
|
-
h =
|
790
|
-
w =
|
791
|
-
b =
|
764
|
+
h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
|
765
|
+
w = bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
|
766
|
+
b = bound01(b, 100); // blackness can be `5%` or a [0, 1] value
|
792
767
|
rgb = hwbToRgb(h, w, b);
|
793
768
|
ok = true;
|
794
769
|
format = 'hwb';
|
795
770
|
}
|
796
771
|
if (isValidCSSUnit(color.a)) {
|
797
|
-
a = color.a;
|
798
|
-
a = isPercentage(`${a}`) ? bound01(a, 100) : a;
|
772
|
+
a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
|
773
|
+
a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
|
799
774
|
}
|
800
775
|
}
|
776
|
+
if (typeof color === 'undefined') {
|
777
|
+
ok = true;
|
778
|
+
}
|
801
779
|
|
802
780
|
return {
|
803
|
-
ok,
|
804
|
-
format
|
805
|
-
r: Math.min(255, Math.max(rgb.r, 0)),
|
806
|
-
g: Math.min(255, Math.max(rgb.g, 0)),
|
807
|
-
b: Math.min(255, Math.max(rgb.b, 0)),
|
781
|
+
ok,
|
782
|
+
format,
|
783
|
+
// r: Math.min(255, Math.max(rgb.r, 0)),
|
784
|
+
// g: Math.min(255, Math.max(rgb.g, 0)),
|
785
|
+
// b: Math.min(255, Math.max(rgb.b, 0)),
|
786
|
+
r: rgb.r,
|
787
|
+
g: rgb.g,
|
788
|
+
b: rgb.b,
|
808
789
|
a: boundAlpha(a),
|
809
790
|
};
|
810
791
|
}
|
@@ -823,15 +804,13 @@ class Color {
|
|
823
804
|
constructor(input, config) {
|
824
805
|
let color = input;
|
825
806
|
const configFormat = config && COLOR_FORMAT.includes(config)
|
826
|
-
? config : '
|
807
|
+
? config : '';
|
827
808
|
|
828
|
-
// If input is already a `Color`,
|
809
|
+
// If input is already a `Color`, clone its values
|
829
810
|
if (color instanceof Color) {
|
830
811
|
color = inputToRGB(color);
|
831
812
|
}
|
832
|
-
|
833
|
-
color = numberInputToObject(color);
|
834
|
-
}
|
813
|
+
|
835
814
|
const {
|
836
815
|
r, g, b, a, ok, format,
|
837
816
|
} = inputToRGB(color);
|
@@ -840,7 +819,7 @@ class Color {
|
|
840
819
|
const self = this;
|
841
820
|
|
842
821
|
/** @type {CP.ColorInput} */
|
843
|
-
self.originalInput =
|
822
|
+
self.originalInput = input;
|
844
823
|
/** @type {number} */
|
845
824
|
self.r = r;
|
846
825
|
/** @type {number} */
|
@@ -881,24 +860,21 @@ class Color {
|
|
881
860
|
let R = 0;
|
882
861
|
let G = 0;
|
883
862
|
let B = 0;
|
884
|
-
const rp = r / 255;
|
885
|
-
const rg = g / 255;
|
886
|
-
const rb = b / 255;
|
887
863
|
|
888
|
-
if (
|
889
|
-
R =
|
864
|
+
if (r <= 0.03928) {
|
865
|
+
R = r / 12.92;
|
890
866
|
} else {
|
891
|
-
R = ((
|
867
|
+
R = ((r + 0.055) / 1.055) ** 2.4;
|
892
868
|
}
|
893
|
-
if (
|
894
|
-
G =
|
869
|
+
if (g <= 0.03928) {
|
870
|
+
G = g / 12.92;
|
895
871
|
} else {
|
896
|
-
G = ((
|
872
|
+
G = ((g + 0.055) / 1.055) ** 2.4;
|
897
873
|
}
|
898
|
-
if (
|
899
|
-
B =
|
874
|
+
if (b <= 0.03928) {
|
875
|
+
B = b / 12.92;
|
900
876
|
} else {
|
901
|
-
B = ((
|
877
|
+
B = ((b + 0.055) / 1.055) ** 2.4;
|
902
878
|
}
|
903
879
|
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
904
880
|
}
|
@@ -908,7 +884,7 @@ class Color {
|
|
908
884
|
* @returns {number} a number in the [0, 255] range
|
909
885
|
*/
|
910
886
|
get brightness() {
|
911
|
-
const { r, g, b } = this;
|
887
|
+
const { r, g, b } = this.toRgb();
|
912
888
|
return (r * 299 + g * 587 + b * 114) / 1000;
|
913
889
|
}
|
914
890
|
|
@@ -917,16 +893,14 @@ class Color {
|
|
917
893
|
* @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
|
918
894
|
*/
|
919
895
|
toRgb() {
|
920
|
-
|
896
|
+
let {
|
921
897
|
r, g, b, a,
|
922
898
|
} = this;
|
923
|
-
const [R, G, B] = [r, g, b].map((x) => roundPart(x));
|
924
899
|
|
900
|
+
[r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
|
901
|
+
a = roundPart(a * 100) / 100;
|
925
902
|
return {
|
926
|
-
r
|
927
|
-
g: G,
|
928
|
-
b: B,
|
929
|
-
a: roundPart(a * 100) / 100,
|
903
|
+
r, g, b, a,
|
930
904
|
};
|
931
905
|
}
|
932
906
|
|
@@ -940,10 +914,11 @@ class Color {
|
|
940
914
|
const {
|
941
915
|
r, g, b, a,
|
942
916
|
} = this.toRgb();
|
917
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
943
918
|
|
944
919
|
return a === 1
|
945
|
-
? `rgb(${
|
946
|
-
: `rgba(${
|
920
|
+
? `rgb(${R}, ${G}, ${B})`
|
921
|
+
: `rgba(${R}, ${G}, ${B}, ${a})`;
|
947
922
|
}
|
948
923
|
|
949
924
|
/**
|
@@ -956,9 +931,10 @@ class Color {
|
|
956
931
|
const {
|
957
932
|
r, g, b, a,
|
958
933
|
} = this.toRgb();
|
934
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
959
935
|
const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
|
960
936
|
|
961
|
-
return `rgb(${
|
937
|
+
return `rgb(${R} ${G} ${B}${A})`;
|
962
938
|
}
|
963
939
|
|
964
940
|
/**
|
@@ -1018,7 +994,7 @@ class Color {
|
|
1018
994
|
toHsv() {
|
1019
995
|
const {
|
1020
996
|
r, g, b, a,
|
1021
|
-
} = this
|
997
|
+
} = this;
|
1022
998
|
const { h, s, v } = rgbToHsv(r, g, b);
|
1023
999
|
|
1024
1000
|
return {
|
@@ -1033,7 +1009,7 @@ class Color {
|
|
1033
1009
|
toHsl() {
|
1034
1010
|
const {
|
1035
1011
|
r, g, b, a,
|
1036
|
-
} = this
|
1012
|
+
} = this;
|
1037
1013
|
const { h, s, l } = rgbToHsl(r, g, b);
|
1038
1014
|
|
1039
1015
|
return {
|
@@ -1118,6 +1094,7 @@ class Color {
|
|
1118
1094
|
*/
|
1119
1095
|
setAlpha(alpha) {
|
1120
1096
|
const self = this;
|
1097
|
+
if (typeof alpha !== 'number') return self;
|
1121
1098
|
self.a = boundAlpha(alpha);
|
1122
1099
|
return self;
|
1123
1100
|
}
|
@@ -1232,6 +1209,7 @@ ObjectAssign(Color, {
|
|
1232
1209
|
isOnePointZero,
|
1233
1210
|
isPercentage,
|
1234
1211
|
isValidCSSUnit,
|
1212
|
+
isColorName,
|
1235
1213
|
pad2,
|
1236
1214
|
clamp01,
|
1237
1215
|
bound01,
|
@@ -1249,10 +1227,11 @@ ObjectAssign(Color, {
|
|
1249
1227
|
hueToRgb,
|
1250
1228
|
hwbToRgb,
|
1251
1229
|
parseIntFromHex,
|
1252
|
-
numberInputToObject,
|
1253
1230
|
stringInputToObject,
|
1254
1231
|
inputToRGB,
|
1255
1232
|
roundPart,
|
1233
|
+
getElementStyle,
|
1234
|
+
setElementStyle,
|
1256
1235
|
ObjectAssign,
|
1257
1236
|
});
|
1258
1237
|
|
@@ -1382,24 +1361,6 @@ const ariaValueText = 'aria-valuetext';
|
|
1382
1361
|
*/
|
1383
1362
|
const ariaValueNow = 'aria-valuenow';
|
1384
1363
|
|
1385
|
-
/**
|
1386
|
-
* A global namespace for aria-haspopup.
|
1387
|
-
* @type {string}
|
1388
|
-
*/
|
1389
|
-
const ariaHasPopup = 'aria-haspopup';
|
1390
|
-
|
1391
|
-
/**
|
1392
|
-
* A global namespace for aria-hidden.
|
1393
|
-
* @type {string}
|
1394
|
-
*/
|
1395
|
-
const ariaHidden = 'aria-hidden';
|
1396
|
-
|
1397
|
-
/**
|
1398
|
-
* A global namespace for aria-labelledby.
|
1399
|
-
* @type {string}
|
1400
|
-
*/
|
1401
|
-
const ariaLabelledBy = 'aria-labelledby';
|
1402
|
-
|
1403
1364
|
/**
|
1404
1365
|
* A global namespace for `ArrowDown` key.
|
1405
1366
|
* @type {string} e.which = 40 equivalent
|
@@ -1526,37 +1487,6 @@ const resizeEvent = 'resize';
|
|
1526
1487
|
*/
|
1527
1488
|
const focusoutEvent = 'focusout';
|
1528
1489
|
|
1529
|
-
// @ts-ignore
|
1530
|
-
const { userAgentData: uaDATA } = navigator;
|
1531
|
-
|
1532
|
-
/**
|
1533
|
-
* A global namespace for `userAgentData` object.
|
1534
|
-
*/
|
1535
|
-
const userAgentData = uaDATA;
|
1536
|
-
|
1537
|
-
const { userAgent: userAgentString } = navigator;
|
1538
|
-
|
1539
|
-
/**
|
1540
|
-
* A global namespace for `navigator.userAgent` string.
|
1541
|
-
*/
|
1542
|
-
const userAgent = userAgentString;
|
1543
|
-
|
1544
|
-
const mobileBrands = /iPhone|iPad|iPod|Android/i;
|
1545
|
-
let isMobileCheck = false;
|
1546
|
-
|
1547
|
-
if (userAgentData) {
|
1548
|
-
isMobileCheck = userAgentData.brands
|
1549
|
-
.some((/** @type {Record<String, any>} */x) => mobileBrands.test(x.brand));
|
1550
|
-
} else {
|
1551
|
-
isMobileCheck = mobileBrands.test(userAgent);
|
1552
|
-
}
|
1553
|
-
|
1554
|
-
/**
|
1555
|
-
* A global `boolean` for mobile detection.
|
1556
|
-
* @type {boolean}
|
1557
|
-
*/
|
1558
|
-
const isMobile = isMobileCheck;
|
1559
|
-
|
1560
1490
|
/**
|
1561
1491
|
* Returns the `document.documentElement` or the `<html>` element.
|
1562
1492
|
*
|
@@ -1741,30 +1671,6 @@ function getElementsByClassName(selector, parent) {
|
|
1741
1671
|
return lookUp.getElementsByClassName(selector);
|
1742
1672
|
}
|
1743
1673
|
|
1744
|
-
/**
|
1745
|
-
* This is a shortie for `document.createElementNS` method
|
1746
|
-
* which allows you to create a new `HTMLElement` for a given `tagName`
|
1747
|
-
* or based on an object with specific non-readonly attributes:
|
1748
|
-
* `id`, `className`, `textContent`, `style`, etc.
|
1749
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
|
1750
|
-
*
|
1751
|
-
* @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
|
1752
|
-
* @param {Record<string, string> | string} param `tagName` or object
|
1753
|
-
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
1754
|
-
*/
|
1755
|
-
function createElementNS(namespace, param) {
|
1756
|
-
if (typeof param === 'string') {
|
1757
|
-
return getDocument().createElementNS(namespace, param);
|
1758
|
-
}
|
1759
|
-
|
1760
|
-
const { tagName } = param;
|
1761
|
-
const attr = { ...param };
|
1762
|
-
const newElement = createElementNS(namespace, tagName);
|
1763
|
-
delete attr.tagName;
|
1764
|
-
ObjectAssign(newElement, attr);
|
1765
|
-
return newElement;
|
1766
|
-
}
|
1767
|
-
|
1768
1674
|
/**
|
1769
1675
|
* Shortcut for the `Element.dispatchEvent(Event)` method.
|
1770
1676
|
*
|
@@ -1882,12 +1788,11 @@ function normalizeValue(value) {
|
|
1882
1788
|
}
|
1883
1789
|
|
1884
1790
|
/**
|
1885
|
-
* Shortcut for `
|
1886
|
-
*
|
1887
|
-
* @
|
1888
|
-
* @returns {string} lowercase output string
|
1791
|
+
* Shortcut for `Object.keys()` static method.
|
1792
|
+
* @param {Record<string, any>} obj a target object
|
1793
|
+
* @returns {string[]}
|
1889
1794
|
*/
|
1890
|
-
const
|
1795
|
+
const ObjectKeys = (obj) => Object.keys(obj);
|
1891
1796
|
|
1892
1797
|
/**
|
1893
1798
|
* Utility to normalize component options.
|
@@ -1992,6 +1897,77 @@ function removeClass(element, classNAME) {
|
|
1992
1897
|
*/
|
1993
1898
|
const removeAttribute = (element, attribute) => element.removeAttribute(attribute);
|
1994
1899
|
|
1900
|
+
/**
|
1901
|
+
* @class
|
1902
|
+
* Returns a color palette with a given set of parameters.
|
1903
|
+
* @example
|
1904
|
+
* new ColorPalette(0, 12, 10);
|
1905
|
+
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: Array<Color> }
|
1906
|
+
*/
|
1907
|
+
class ColorPalette {
|
1908
|
+
/**
|
1909
|
+
* The `hue` parameter is optional, which would be set to 0.
|
1910
|
+
* @param {number[]} args represeinting hue, hueSteps, lightSteps
|
1911
|
+
* * `args.hue` the starting Hue [0, 360]
|
1912
|
+
* * `args.hueSteps` Hue Steps Count [5, 24]
|
1913
|
+
* * `args.lightSteps` Lightness Steps Count [5, 12]
|
1914
|
+
*/
|
1915
|
+
constructor(...args) {
|
1916
|
+
let hue = 0;
|
1917
|
+
let hueSteps = 12;
|
1918
|
+
let lightSteps = 10;
|
1919
|
+
let lightnessArray = [0.5];
|
1920
|
+
|
1921
|
+
if (args.length === 3) {
|
1922
|
+
[hue, hueSteps, lightSteps] = args;
|
1923
|
+
} else if (args.length === 2) {
|
1924
|
+
[hueSteps, lightSteps] = args;
|
1925
|
+
if ([hueSteps, lightSteps].some((n) => n < 1)) {
|
1926
|
+
throw TypeError('ColorPalette: both arguments must be higher than 0.');
|
1927
|
+
}
|
1928
|
+
}
|
1929
|
+
|
1930
|
+
/** @type {*} */
|
1931
|
+
const colors = [];
|
1932
|
+
const hueStep = 360 / hueSteps;
|
1933
|
+
const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
|
1934
|
+
const steps1To13 = [0.25, 0.2, 0.15, 0.11, 0.09, 0.075];
|
1935
|
+
const lightSets = [[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]];
|
1936
|
+
const closestSet = lightSets.find((set) => set.includes(lightSteps));
|
1937
|
+
|
1938
|
+
// find a lightStep that won't go beyond black and white
|
1939
|
+
// something within the [10-90] range of lightness
|
1940
|
+
const lightStep = closestSet
|
1941
|
+
? steps1To13[lightSets.indexOf(closestSet)]
|
1942
|
+
: (100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100);
|
1943
|
+
|
1944
|
+
// light tints
|
1945
|
+
for (let i = 1; i < half + 1; i += 1) {
|
1946
|
+
lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
|
1947
|
+
}
|
1948
|
+
|
1949
|
+
// dark tints
|
1950
|
+
for (let i = 1; i < lightSteps - half; i += 1) {
|
1951
|
+
lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
|
1952
|
+
}
|
1953
|
+
|
1954
|
+
// feed `colors` Array
|
1955
|
+
for (let i = 0; i < hueSteps; i += 1) {
|
1956
|
+
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
1957
|
+
lightnessArray.forEach((l) => {
|
1958
|
+
colors.push(new Color({ h: currentHue, s: 1, l }));
|
1959
|
+
});
|
1960
|
+
}
|
1961
|
+
|
1962
|
+
this.hue = hue;
|
1963
|
+
this.hueSteps = hueSteps;
|
1964
|
+
this.lightSteps = lightSteps;
|
1965
|
+
this.colors = colors;
|
1966
|
+
}
|
1967
|
+
}
|
1968
|
+
|
1969
|
+
ObjectAssign(ColorPalette, { Color });
|
1970
|
+
|
1995
1971
|
/** @type {Record<string, string>} */
|
1996
1972
|
const colorPickerLabels = {
|
1997
1973
|
pickerLabel: 'Colour Picker',
|
@@ -2019,14 +1995,72 @@ const colorPickerLabels = {
|
|
2019
1995
|
*/
|
2020
1996
|
const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
|
2021
1997
|
|
1998
|
+
const tabIndex = 'tabindex';
|
1999
|
+
|
2022
2000
|
/**
|
2023
|
-
*
|
2024
|
-
*
|
2025
|
-
* @
|
2026
|
-
|
2001
|
+
* Check if a string is valid JSON string.
|
2002
|
+
* @param {string} str the string input
|
2003
|
+
* @returns {boolean} the query result
|
2004
|
+
*/
|
2005
|
+
function isValidJSON(str) {
|
2006
|
+
try {
|
2007
|
+
JSON.parse(str);
|
2008
|
+
} catch (e) {
|
2009
|
+
return false;
|
2010
|
+
}
|
2011
|
+
return true;
|
2012
|
+
}
|
2013
|
+
|
2014
|
+
/**
|
2015
|
+
* Shortcut for `String.toUpperCase()`.
|
2016
|
+
*
|
2017
|
+
* @param {string} source input string
|
2018
|
+
* @returns {string} uppercase output string
|
2027
2019
|
*/
|
2028
2020
|
const toUpperCase = (source) => source.toUpperCase();
|
2029
2021
|
|
2022
|
+
/**
|
2023
|
+
* A global namespace for aria-haspopup.
|
2024
|
+
* @type {string}
|
2025
|
+
*/
|
2026
|
+
const ariaHasPopup = 'aria-haspopup';
|
2027
|
+
|
2028
|
+
/**
|
2029
|
+
* A global namespace for aria-hidden.
|
2030
|
+
* @type {string}
|
2031
|
+
*/
|
2032
|
+
const ariaHidden = 'aria-hidden';
|
2033
|
+
|
2034
|
+
/**
|
2035
|
+
* A global namespace for aria-labelledby.
|
2036
|
+
* @type {string}
|
2037
|
+
*/
|
2038
|
+
const ariaLabelledBy = 'aria-labelledby';
|
2039
|
+
|
2040
|
+
/**
|
2041
|
+
* This is a shortie for `document.createElementNS` method
|
2042
|
+
* which allows you to create a new `HTMLElement` for a given `tagName`
|
2043
|
+
* or based on an object with specific non-readonly attributes:
|
2044
|
+
* `id`, `className`, `textContent`, `style`, etc.
|
2045
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
|
2046
|
+
*
|
2047
|
+
* @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
|
2048
|
+
* @param {Record<string, string> | string} param `tagName` or object
|
2049
|
+
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
2050
|
+
*/
|
2051
|
+
function createElementNS(namespace, param) {
|
2052
|
+
if (typeof param === 'string') {
|
2053
|
+
return getDocument().createElementNS(namespace, param);
|
2054
|
+
}
|
2055
|
+
|
2056
|
+
const { tagName } = param;
|
2057
|
+
const attr = { ...param };
|
2058
|
+
const newElement = createElementNS(namespace, tagName);
|
2059
|
+
delete attr.tagName;
|
2060
|
+
ObjectAssign(newElement, attr);
|
2061
|
+
return newElement;
|
2062
|
+
}
|
2063
|
+
|
2030
2064
|
const vHidden = 'v-hidden';
|
2031
2065
|
|
2032
2066
|
/**
|
@@ -2106,8 +2140,6 @@ const ariaValueMin = 'aria-valuemin';
|
|
2106
2140
|
*/
|
2107
2141
|
const ariaValueMax = 'aria-valuemax';
|
2108
2142
|
|
2109
|
-
const tabIndex = 'tabindex';
|
2110
|
-
|
2111
2143
|
/**
|
2112
2144
|
* Returns all color controls for `ColorPicker`.
|
2113
2145
|
*
|
@@ -2215,75 +2247,6 @@ function setCSSProperties(element, props) {
|
|
2215
2247
|
});
|
2216
2248
|
}
|
2217
2249
|
|
2218
|
-
/**
|
2219
|
-
* @class
|
2220
|
-
* Returns a color palette with a given set of parameters.
|
2221
|
-
* @example
|
2222
|
-
* new ColorPalette(0, 12, 10);
|
2223
|
-
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: array }
|
2224
|
-
*/
|
2225
|
-
class ColorPalette {
|
2226
|
-
/**
|
2227
|
-
* The `hue` parameter is optional, which would be set to 0.
|
2228
|
-
* @param {number[]} args represeinting hue, hueSteps, lightSteps
|
2229
|
-
* * `args.hue` the starting Hue [0, 360]
|
2230
|
-
* * `args.hueSteps` Hue Steps Count [5, 24]
|
2231
|
-
* * `args.lightSteps` Lightness Steps Count [5, 12]
|
2232
|
-
*/
|
2233
|
-
constructor(...args) {
|
2234
|
-
let hue = 0;
|
2235
|
-
let hueSteps = 12;
|
2236
|
-
let lightSteps = 10;
|
2237
|
-
let lightnessArray = [0.5];
|
2238
|
-
|
2239
|
-
if (args.length === 3) {
|
2240
|
-
[hue, hueSteps, lightSteps] = args;
|
2241
|
-
} else if (args.length === 2) {
|
2242
|
-
[hueSteps, lightSteps] = args;
|
2243
|
-
} else {
|
2244
|
-
throw TypeError('ColorPalette requires minimum 2 arguments');
|
2245
|
-
}
|
2246
|
-
|
2247
|
-
/** @type {string[]} */
|
2248
|
-
const colors = [];
|
2249
|
-
|
2250
|
-
const hueStep = 360 / hueSteps;
|
2251
|
-
const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
|
2252
|
-
const estimatedStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
|
2253
|
-
|
2254
|
-
let lightStep = 0.25;
|
2255
|
-
lightStep = [4, 5].includes(lightSteps) ? 0.2 : lightStep;
|
2256
|
-
lightStep = [6, 7].includes(lightSteps) ? 0.15 : lightStep;
|
2257
|
-
lightStep = [8, 9].includes(lightSteps) ? 0.11 : lightStep;
|
2258
|
-
lightStep = [10, 11].includes(lightSteps) ? 0.09 : lightStep;
|
2259
|
-
lightStep = [12, 13].includes(lightSteps) ? 0.075 : lightStep;
|
2260
|
-
lightStep = lightSteps > 13 ? estimatedStep : lightStep;
|
2261
|
-
|
2262
|
-
// light tints
|
2263
|
-
for (let i = 1; i < half + 1; i += 1) {
|
2264
|
-
lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
|
2265
|
-
}
|
2266
|
-
|
2267
|
-
// dark tints
|
2268
|
-
for (let i = 1; i < lightSteps - half; i += 1) {
|
2269
|
-
lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
|
2270
|
-
}
|
2271
|
-
|
2272
|
-
// feed `colors` Array
|
2273
|
-
for (let i = 0; i < hueSteps; i += 1) {
|
2274
|
-
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
2275
|
-
lightnessArray.forEach((l) => {
|
2276
|
-
colors.push(new Color({ h: currentHue, s: 1, l }).toHexString());
|
2277
|
-
});
|
2278
|
-
}
|
2279
|
-
|
2280
|
-
this.hue = hue;
|
2281
|
-
this.hueSteps = hueSteps;
|
2282
|
-
this.lightSteps = lightSteps;
|
2283
|
-
this.colors = colors;
|
2284
|
-
}
|
2285
|
-
}
|
2286
|
-
|
2287
2250
|
/**
|
2288
2251
|
* Returns a color-defaults with given values and class.
|
2289
2252
|
* @param {CP.ColorPicker} self
|
@@ -2317,7 +2280,8 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2317
2280
|
optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
|
2318
2281
|
const menuHeight = `${(rowCount || 1) * optionSize}rem`;
|
2319
2282
|
const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
|
2320
|
-
|
2283
|
+
/** @type {HTMLUListElement} */
|
2284
|
+
// @ts-ignore -- <UL> is an `HTMLElement`
|
2321
2285
|
const menu = createElement({
|
2322
2286
|
tagName: 'ul',
|
2323
2287
|
className: finalClass,
|
@@ -2325,7 +2289,7 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2325
2289
|
setAttribute(menu, 'role', 'listbox');
|
2326
2290
|
setAttribute(menu, ariaLabel, menuLabel);
|
2327
2291
|
|
2328
|
-
if (isScrollable) {
|
2292
|
+
if (isScrollable) {
|
2329
2293
|
setCSSProperties(menu, {
|
2330
2294
|
'--grid-item-size': `${optionSize}rem`,
|
2331
2295
|
'--grid-fit': fit,
|
@@ -2336,15 +2300,19 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2336
2300
|
}
|
2337
2301
|
|
2338
2302
|
colorsArray.forEach((x) => {
|
2339
|
-
|
2340
|
-
|
2341
|
-
|
2303
|
+
let [value, label] = typeof x === 'string' ? x.trim().split(':') : [];
|
2304
|
+
if (x instanceof Color) {
|
2305
|
+
value = x.toHexString();
|
2306
|
+
label = value;
|
2307
|
+
}
|
2308
|
+
const color = new Color(x instanceof Color ? x : value, format);
|
2309
|
+
const isActive = color.toString() === getAttribute(input, 'value');
|
2342
2310
|
const active = isActive ? ' active' : '';
|
2343
2311
|
|
2344
2312
|
const option = createElement({
|
2345
2313
|
tagName: 'li',
|
2346
2314
|
className: `color-option${active}`,
|
2347
|
-
innerText: `${label ||
|
2315
|
+
innerText: `${label || value}`,
|
2348
2316
|
});
|
2349
2317
|
|
2350
2318
|
setAttribute(option, tabIndex, '0');
|
@@ -2353,7 +2321,7 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2353
2321
|
setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
|
2354
2322
|
|
2355
2323
|
if (isOptionsMenu) {
|
2356
|
-
setElementStyle(option, { backgroundColor:
|
2324
|
+
setElementStyle(option, { backgroundColor: value });
|
2357
2325
|
}
|
2358
2326
|
|
2359
2327
|
menu.append(option);
|
@@ -2362,55 +2330,10 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2362
2330
|
}
|
2363
2331
|
|
2364
2332
|
/**
|
2365
|
-
|
2366
|
-
|
2367
|
-
|
2368
|
-
|
2369
|
-
function isValidJSON(str) {
|
2370
|
-
try {
|
2371
|
-
JSON.parse(str);
|
2372
|
-
} catch (e) {
|
2373
|
-
return false;
|
2374
|
-
}
|
2375
|
-
return true;
|
2376
|
-
}
|
2377
|
-
|
2378
|
-
var version = "0.0.1alpha3";
|
2379
|
-
|
2380
|
-
// @ts-ignore
|
2381
|
-
|
2382
|
-
const Version = version;
|
2383
|
-
|
2384
|
-
// ColorPicker GC
|
2385
|
-
// ==============
|
2386
|
-
const colorPickerString = 'color-picker';
|
2387
|
-
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2388
|
-
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2389
|
-
const colorPickerDefaults = {
|
2390
|
-
componentLabels: colorPickerLabels,
|
2391
|
-
colorLabels: colorNames,
|
2392
|
-
format: 'rgb',
|
2393
|
-
colorPresets: false,
|
2394
|
-
colorKeywords: false,
|
2395
|
-
};
|
2396
|
-
|
2397
|
-
// ColorPicker Static Methods
|
2398
|
-
// ==========================
|
2399
|
-
|
2400
|
-
/** @type {CP.GetInstance<ColorPicker>} */
|
2401
|
-
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2402
|
-
|
2403
|
-
/** @type {CP.InitCallback<ColorPicker>} */
|
2404
|
-
const initColorPicker = (element) => new ColorPicker(element);
|
2405
|
-
|
2406
|
-
// ColorPicker Private Methods
|
2407
|
-
// ===========================
|
2408
|
-
|
2409
|
-
/**
|
2410
|
-
* Generate HTML markup and update instance properties.
|
2411
|
-
* @param {ColorPicker} self
|
2412
|
-
*/
|
2413
|
-
function initCallback(self) {
|
2333
|
+
* Generate HTML markup and update instance properties.
|
2334
|
+
* @param {CP.ColorPicker} self
|
2335
|
+
*/
|
2336
|
+
function setMarkup(self) {
|
2414
2337
|
const {
|
2415
2338
|
input, parent, format, id, componentLabels, colorKeywords, colorPresets,
|
2416
2339
|
} = self;
|
@@ -2425,9 +2348,7 @@ function initCallback(self) {
|
|
2425
2348
|
self.color = new Color(color, format);
|
2426
2349
|
|
2427
2350
|
// set initial controls dimensions
|
2428
|
-
|
2429
|
-
const dropClass = isMobile ? ' mobile' : '';
|
2430
|
-
const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
|
2351
|
+
const formatString = format === 'hex' ? hexLabel : toUpperCase(format);
|
2431
2352
|
|
2432
2353
|
const pickerBtn = createElement({
|
2433
2354
|
id: `picker-btn-${id}`,
|
@@ -2444,7 +2365,7 @@ function initCallback(self) {
|
|
2444
2365
|
|
2445
2366
|
const pickerDropdown = createElement({
|
2446
2367
|
tagName: 'div',
|
2447
|
-
className:
|
2368
|
+
className: 'color-dropdown picker',
|
2448
2369
|
});
|
2449
2370
|
setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
|
2450
2371
|
setAttribute(pickerDropdown, 'role', 'group');
|
@@ -2460,7 +2381,7 @@ function initCallback(self) {
|
|
2460
2381
|
if (colorKeywords || colorPresets) {
|
2461
2382
|
const presetsDropdown = createElement({
|
2462
2383
|
tagName: 'div',
|
2463
|
-
className:
|
2384
|
+
className: 'color-dropdown scrollable menu',
|
2464
2385
|
});
|
2465
2386
|
|
2466
2387
|
// color presets
|
@@ -2510,6 +2431,37 @@ function initCallback(self) {
|
|
2510
2431
|
setAttribute(input, tabIndex, '-1');
|
2511
2432
|
}
|
2512
2433
|
|
2434
|
+
var version = "0.0.2alpha2";
|
2435
|
+
|
2436
|
+
// @ts-ignore
|
2437
|
+
|
2438
|
+
const Version = version;
|
2439
|
+
|
2440
|
+
// ColorPicker GC
|
2441
|
+
// ==============
|
2442
|
+
const colorPickerString = 'color-picker';
|
2443
|
+
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2444
|
+
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2445
|
+
const colorPickerDefaults = {
|
2446
|
+
componentLabels: colorPickerLabels,
|
2447
|
+
colorLabels: colorNames,
|
2448
|
+
format: 'rgb',
|
2449
|
+
colorPresets: false,
|
2450
|
+
colorKeywords: false,
|
2451
|
+
};
|
2452
|
+
|
2453
|
+
// ColorPicker Static Methods
|
2454
|
+
// ==========================
|
2455
|
+
|
2456
|
+
/** @type {CP.GetInstance<ColorPicker>} */
|
2457
|
+
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2458
|
+
|
2459
|
+
/** @type {CP.InitCallback<ColorPicker>} */
|
2460
|
+
const initColorPicker = (element) => new ColorPicker(element);
|
2461
|
+
|
2462
|
+
// ColorPicker Private Methods
|
2463
|
+
// ===========================
|
2464
|
+
|
2513
2465
|
/**
|
2514
2466
|
* Add / remove `ColorPicker` main event listeners.
|
2515
2467
|
* @param {ColorPicker} self
|
@@ -2522,8 +2474,6 @@ function toggleEvents(self, action) {
|
|
2522
2474
|
fn(input, focusinEvent, self.showPicker);
|
2523
2475
|
fn(pickerToggle, mouseclickEvent, self.togglePicker);
|
2524
2476
|
|
2525
|
-
fn(input, keydownEvent, self.keyToggle);
|
2526
|
-
|
2527
2477
|
if (menuToggle) {
|
2528
2478
|
fn(menuToggle, mouseclickEvent, self.toggleMenu);
|
2529
2479
|
}
|
@@ -2561,8 +2511,7 @@ function toggleEventsOnShown(self, action) {
|
|
2561
2511
|
fn(doc, pointerEvents.move, self.pointerMove);
|
2562
2512
|
fn(doc, pointerEvents.up, self.pointerUp);
|
2563
2513
|
fn(parent, focusoutEvent, self.handleFocusOut);
|
2564
|
-
|
2565
|
-
fn(win, keyupEvent, self.handleDismiss);
|
2514
|
+
fn(doc, keyupEvent, self.handleDismiss);
|
2566
2515
|
}
|
2567
2516
|
|
2568
2517
|
/**
|
@@ -2646,7 +2595,7 @@ class ColorPicker {
|
|
2646
2595
|
const input = querySelector(target);
|
2647
2596
|
|
2648
2597
|
// invalidate
|
2649
|
-
if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
|
2598
|
+
if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
|
2650
2599
|
self.input = input;
|
2651
2600
|
|
2652
2601
|
const parent = closest(input, colorPickerParentSelector);
|
@@ -2693,15 +2642,14 @@ class ColorPicker {
|
|
2693
2642
|
});
|
2694
2643
|
|
2695
2644
|
// update and expose component labels
|
2696
|
-
const
|
2697
|
-
|
2698
|
-
? JSON.parse(componentLabels) : componentLabels || {};
|
2645
|
+
const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
|
2646
|
+
? JSON.parse(componentLabels) : componentLabels;
|
2699
2647
|
|
2700
2648
|
/** @type {Record<string, string>} */
|
2701
|
-
self.componentLabels = ObjectAssign(
|
2649
|
+
self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
|
2702
2650
|
|
2703
2651
|
/** @type {Color} */
|
2704
|
-
self.color = new Color('
|
2652
|
+
self.color = new Color(input.value || '#fff', format);
|
2705
2653
|
|
2706
2654
|
/** @type {CP.ColorFormats} */
|
2707
2655
|
self.format = format;
|
@@ -2710,7 +2658,7 @@ class ColorPicker {
|
|
2710
2658
|
if (colorKeywords instanceof Array) {
|
2711
2659
|
self.colorKeywords = colorKeywords;
|
2712
2660
|
} else if (typeof colorKeywords === 'string' && colorKeywords.length) {
|
2713
|
-
self.colorKeywords = colorKeywords.split(',');
|
2661
|
+
self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
|
2714
2662
|
}
|
2715
2663
|
|
2716
2664
|
// set colour presets
|
@@ -2739,11 +2687,10 @@ class ColorPicker {
|
|
2739
2687
|
self.handleFocusOut = self.handleFocusOut.bind(self);
|
2740
2688
|
self.changeHandler = self.changeHandler.bind(self);
|
2741
2689
|
self.handleDismiss = self.handleDismiss.bind(self);
|
2742
|
-
self.keyToggle = self.keyToggle.bind(self);
|
2743
2690
|
self.handleKnobs = self.handleKnobs.bind(self);
|
2744
2691
|
|
2745
2692
|
// generate markup
|
2746
|
-
|
2693
|
+
setMarkup(self);
|
2747
2694
|
|
2748
2695
|
const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
|
2749
2696
|
// set main elements
|
@@ -2831,76 +2778,83 @@ class ColorPicker {
|
|
2831
2778
|
return inputValue !== '' && new Color(inputValue).isValid;
|
2832
2779
|
}
|
2833
2780
|
|
2781
|
+
/** Returns the colour appearance, usually the closest colour name for the current value. */
|
2782
|
+
get appearance() {
|
2783
|
+
const {
|
2784
|
+
colorLabels, hsl, hsv, format,
|
2785
|
+
} = this;
|
2786
|
+
|
2787
|
+
const hue = roundPart(hsl.h * 360);
|
2788
|
+
const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
|
2789
|
+
const saturation = roundPart(saturationSource * 100);
|
2790
|
+
const lightness = roundPart(hsl.l * 100);
|
2791
|
+
const hsvl = hsv.v * 100;
|
2792
|
+
|
2793
|
+
let colorName;
|
2794
|
+
|
2795
|
+
// determine color appearance
|
2796
|
+
if (lightness === 100 && saturation === 0) {
|
2797
|
+
colorName = colorLabels.white;
|
2798
|
+
} else if (lightness === 0) {
|
2799
|
+
colorName = colorLabels.black;
|
2800
|
+
} else if (saturation === 0) {
|
2801
|
+
colorName = colorLabels.grey;
|
2802
|
+
} else if (hue < 15 || hue >= 345) {
|
2803
|
+
colorName = colorLabels.red;
|
2804
|
+
} else if (hue >= 15 && hue < 45) {
|
2805
|
+
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
2806
|
+
} else if (hue >= 45 && hue < 75) {
|
2807
|
+
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
2808
|
+
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
2809
|
+
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
2810
|
+
colorName = isOlive ? colorLabels.olive : colorName;
|
2811
|
+
} else if (hue >= 75 && hue < 155) {
|
2812
|
+
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
2813
|
+
} else if (hue >= 155 && hue < 175) {
|
2814
|
+
colorName = colorLabels.teal;
|
2815
|
+
} else if (hue >= 175 && hue < 195) {
|
2816
|
+
colorName = colorLabels.cyan;
|
2817
|
+
} else if (hue >= 195 && hue < 255) {
|
2818
|
+
colorName = colorLabels.blue;
|
2819
|
+
} else if (hue >= 255 && hue < 270) {
|
2820
|
+
colorName = colorLabels.violet;
|
2821
|
+
} else if (hue >= 270 && hue < 295) {
|
2822
|
+
colorName = colorLabels.magenta;
|
2823
|
+
} else if (hue >= 295 && hue < 345) {
|
2824
|
+
colorName = colorLabels.pink;
|
2825
|
+
}
|
2826
|
+
return colorName;
|
2827
|
+
}
|
2828
|
+
|
2834
2829
|
/** Updates `ColorPicker` visuals. */
|
2835
2830
|
updateVisuals() {
|
2836
2831
|
const self = this;
|
2837
2832
|
const {
|
2838
|
-
|
2833
|
+
controlPositions, visuals,
|
2839
2834
|
} = self;
|
2840
2835
|
const [v1, v2, v3] = visuals;
|
2841
|
-
const {
|
2842
|
-
const hue =
|
2843
|
-
|
2844
|
-
: controlPositions.c2y / offsetHeight;
|
2845
|
-
// @ts-ignore - `hslToRgb` is assigned to `Color` as static method
|
2846
|
-
const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
|
2836
|
+
const { offsetHeight } = v1;
|
2837
|
+
const hue = controlPositions.c2y / offsetHeight;
|
2838
|
+
const { r, g, b } = new Color({ h: hue, s: 1, l: 0.5 }).toRgb();
|
2847
2839
|
const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
|
2848
2840
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
2849
2841
|
const roundA = roundPart((alpha * 100)) / 100;
|
2850
2842
|
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
|
2857
|
-
|
2858
|
-
|
2859
|
-
|
2860
|
-
|
2861
|
-
|
2862
|
-
|
2863
|
-
|
2864
|
-
|
2865
|
-
setElementStyle(v2, { background: hueGradient });
|
2866
|
-
} else {
|
2867
|
-
const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
|
2868
|
-
const fill0 = new Color({
|
2869
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2870
|
-
}).saturate(-saturation).toRgbString();
|
2871
|
-
const fill1 = new Color({
|
2872
|
-
r: 255, g: 255, b: 0, a: alpha,
|
2873
|
-
}).saturate(-saturation).toRgbString();
|
2874
|
-
const fill2 = new Color({
|
2875
|
-
r: 0, g: 255, b: 0, a: alpha,
|
2876
|
-
}).saturate(-saturation).toRgbString();
|
2877
|
-
const fill3 = new Color({
|
2878
|
-
r: 0, g: 255, b: 255, a: alpha,
|
2879
|
-
}).saturate(-saturation).toRgbString();
|
2880
|
-
const fill4 = new Color({
|
2881
|
-
r: 0, g: 0, b: 255, a: alpha,
|
2882
|
-
}).saturate(-saturation).toRgbString();
|
2883
|
-
const fill5 = new Color({
|
2884
|
-
r: 255, g: 0, b: 255, a: alpha,
|
2885
|
-
}).saturate(-saturation).toRgbString();
|
2886
|
-
const fill6 = new Color({
|
2887
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2888
|
-
}).saturate(-saturation).toRgbString();
|
2889
|
-
const fillGradient = `linear-gradient(to right,
|
2890
|
-
${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
|
2891
|
-
${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
|
2892
|
-
const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
|
2893
|
-
linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
|
2894
|
-
|
2895
|
-
setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
|
2896
|
-
const {
|
2897
|
-
r: gr, g: gg, b: gb,
|
2898
|
-
} = new Color({ r, g, b }).greyscale().toRgb();
|
2843
|
+
const fill = new Color({
|
2844
|
+
h: hue, s: 1, l: 0.5, a: alpha,
|
2845
|
+
}).toRgbString();
|
2846
|
+
const hueGradient = `linear-gradient(
|
2847
|
+
rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
|
2848
|
+
rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
|
2849
|
+
rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
|
2850
|
+
rgb(255,0,0) 100%)`;
|
2851
|
+
setElementStyle(v1, {
|
2852
|
+
background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
|
2853
|
+
linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
|
2854
|
+
${whiteGrad}`,
|
2855
|
+
});
|
2856
|
+
setElementStyle(v2, { background: hueGradient });
|
2899
2857
|
|
2900
|
-
setElementStyle(v2, {
|
2901
|
-
background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
|
2902
|
-
});
|
2903
|
-
}
|
2904
2858
|
setElementStyle(v3, {
|
2905
2859
|
background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
|
2906
2860
|
});
|
@@ -2939,7 +2893,7 @@ class ColorPicker {
|
|
2939
2893
|
const self = this;
|
2940
2894
|
const { activeElement } = getDocument(self.input);
|
2941
2895
|
|
2942
|
-
if ((
|
2896
|
+
if ((e.type === touchmoveEvent && self.dragElement)
|
2943
2897
|
|| (activeElement && self.controlKnobs.includes(activeElement))) {
|
2944
2898
|
e.stopPropagation();
|
2945
2899
|
e.preventDefault();
|
@@ -3050,13 +3004,13 @@ class ColorPicker {
|
|
3050
3004
|
const [v1, v2, v3] = visuals;
|
3051
3005
|
const [c1, c2, c3] = controlKnobs;
|
3052
3006
|
/** @type {HTMLElement} */
|
3053
|
-
const visual =
|
3054
|
-
? target : querySelector('.visual-control', target.parentElement);
|
3007
|
+
const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
|
3055
3008
|
const visualRect = getBoundingClientRect(visual);
|
3009
|
+
const html = getDocumentElement(v1);
|
3056
3010
|
const X = type === 'touchstart' ? touches[0].pageX : pageX;
|
3057
3011
|
const Y = type === 'touchstart' ? touches[0].pageY : pageY;
|
3058
|
-
const offsetX = X -
|
3059
|
-
const offsetY = Y -
|
3012
|
+
const offsetX = X - html.scrollLeft - visualRect.left;
|
3013
|
+
const offsetY = Y - html.scrollTop - visualRect.top;
|
3060
3014
|
|
3061
3015
|
if (target === v1 || target === c1) {
|
3062
3016
|
self.dragElement = visual;
|
@@ -3116,10 +3070,11 @@ class ColorPicker {
|
|
3116
3070
|
if (!dragElement) return;
|
3117
3071
|
|
3118
3072
|
const controlRect = getBoundingClientRect(dragElement);
|
3119
|
-
const
|
3120
|
-
const
|
3121
|
-
const
|
3122
|
-
const
|
3073
|
+
const win = getDocumentElement(v1);
|
3074
|
+
const X = type === touchmoveEvent ? touches[0].pageX : pageX;
|
3075
|
+
const Y = type === touchmoveEvent ? touches[0].pageY : pageY;
|
3076
|
+
const offsetX = X - win.scrollLeft - controlRect.left;
|
3077
|
+
const offsetY = Y - win.scrollTop - controlRect.top;
|
3123
3078
|
|
3124
3079
|
if (dragElement === v1) {
|
3125
3080
|
self.changeControl1(offsetX, offsetY);
|
@@ -3146,30 +3101,41 @@ class ColorPicker {
|
|
3146
3101
|
if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
|
3147
3102
|
e.preventDefault();
|
3148
3103
|
|
3149
|
-
const { controlKnobs } = self;
|
3104
|
+
const { controlKnobs, visuals } = self;
|
3105
|
+
const { offsetWidth, offsetHeight } = visuals[0];
|
3150
3106
|
const [c1, c2, c3] = controlKnobs;
|
3151
3107
|
const { activeElement } = getDocument(c1);
|
3152
3108
|
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
3109
|
+
const yRatio = offsetHeight / 360;
|
3153
3110
|
|
3154
3111
|
if (currentKnob) {
|
3155
3112
|
let offsetX = 0;
|
3156
3113
|
let offsetY = 0;
|
3114
|
+
|
3157
3115
|
if (target === c1) {
|
3116
|
+
const xRatio = offsetWidth / 100;
|
3117
|
+
|
3158
3118
|
if ([keyArrowLeft, keyArrowRight].includes(code)) {
|
3159
|
-
self.controlPositions.c1x += code === keyArrowRight ?
|
3119
|
+
self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
|
3160
3120
|
} else if ([keyArrowUp, keyArrowDown].includes(code)) {
|
3161
|
-
self.controlPositions.c1y += code === keyArrowDown ?
|
3121
|
+
self.controlPositions.c1y += code === keyArrowDown ? yRatio : -yRatio;
|
3162
3122
|
}
|
3163
3123
|
|
3164
3124
|
offsetX = self.controlPositions.c1x;
|
3165
3125
|
offsetY = self.controlPositions.c1y;
|
3166
3126
|
self.changeControl1(offsetX, offsetY);
|
3167
3127
|
} else if (target === c2) {
|
3168
|
-
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
3128
|
+
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
3129
|
+
? yRatio
|
3130
|
+
: -yRatio;
|
3131
|
+
|
3169
3132
|
offsetY = self.controlPositions.c2y;
|
3170
3133
|
self.changeControl2(offsetY);
|
3171
3134
|
} else if (target === c3) {
|
3172
|
-
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
3135
|
+
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
3136
|
+
? yRatio
|
3137
|
+
: -yRatio;
|
3138
|
+
|
3173
3139
|
offsetY = self.controlPositions.c3y;
|
3174
3140
|
self.changeAlpha(offsetY);
|
3175
3141
|
}
|
@@ -3197,7 +3163,7 @@ class ColorPicker {
|
|
3197
3163
|
if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
|
3198
3164
|
if (activeElement === input) {
|
3199
3165
|
if (isNonColorValue) {
|
3200
|
-
colorSource = '
|
3166
|
+
colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
|
3201
3167
|
} else {
|
3202
3168
|
colorSource = currentValue;
|
3203
3169
|
}
|
@@ -3248,9 +3214,7 @@ class ColorPicker {
|
|
3248
3214
|
changeControl1(X, Y) {
|
3249
3215
|
const self = this;
|
3250
3216
|
let [offsetX, offsetY] = [0, 0];
|
3251
|
-
const {
|
3252
|
-
format, controlPositions, visuals,
|
3253
|
-
} = self;
|
3217
|
+
const { controlPositions, visuals } = self;
|
3254
3218
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3255
3219
|
|
3256
3220
|
if (X > offsetWidth) offsetX = offsetWidth;
|
@@ -3259,29 +3223,19 @@ class ColorPicker {
|
|
3259
3223
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3260
3224
|
else if (Y >= 0) offsetY = Y;
|
3261
3225
|
|
3262
|
-
const hue =
|
3263
|
-
? offsetX / offsetWidth
|
3264
|
-
: controlPositions.c2y / offsetHeight;
|
3226
|
+
const hue = controlPositions.c2y / offsetHeight;
|
3265
3227
|
|
3266
|
-
const saturation =
|
3267
|
-
? 1 - controlPositions.c2y / offsetHeight
|
3268
|
-
: offsetX / offsetWidth;
|
3228
|
+
const saturation = offsetX / offsetWidth;
|
3269
3229
|
|
3270
3230
|
const lightness = 1 - offsetY / offsetHeight;
|
3271
3231
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3272
3232
|
|
3273
|
-
const colorObject = format === 'hsl'
|
3274
|
-
? {
|
3275
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3276
|
-
}
|
3277
|
-
: {
|
3278
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3279
|
-
};
|
3280
|
-
|
3281
3233
|
// new color
|
3282
3234
|
const {
|
3283
3235
|
r, g, b, a,
|
3284
|
-
} = new Color(
|
3236
|
+
} = new Color({
|
3237
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3238
|
+
});
|
3285
3239
|
|
3286
3240
|
ObjectAssign(self.color, {
|
3287
3241
|
r, g, b, a,
|
@@ -3308,7 +3262,7 @@ class ColorPicker {
|
|
3308
3262
|
changeControl2(Y) {
|
3309
3263
|
const self = this;
|
3310
3264
|
const {
|
3311
|
-
|
3265
|
+
controlPositions, visuals,
|
3312
3266
|
} = self;
|
3313
3267
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3314
3268
|
|
@@ -3317,26 +3271,17 @@ class ColorPicker {
|
|
3317
3271
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3318
3272
|
else if (Y >= 0) offsetY = Y;
|
3319
3273
|
|
3320
|
-
const hue =
|
3321
|
-
|
3322
|
-
: offsetY / offsetHeight;
|
3323
|
-
const saturation = format === 'hsl'
|
3324
|
-
? 1 - offsetY / offsetHeight
|
3325
|
-
: controlPositions.c1x / offsetWidth;
|
3274
|
+
const hue = offsetY / offsetHeight;
|
3275
|
+
const saturation = controlPositions.c1x / offsetWidth;
|
3326
3276
|
const lightness = 1 - controlPositions.c1y / offsetHeight;
|
3327
3277
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3328
|
-
const colorObject = format === 'hsl'
|
3329
|
-
? {
|
3330
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3331
|
-
}
|
3332
|
-
: {
|
3333
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3334
|
-
};
|
3335
3278
|
|
3336
3279
|
// new color
|
3337
3280
|
const {
|
3338
3281
|
r, g, b, a,
|
3339
|
-
} = new Color(
|
3282
|
+
} = new Color({
|
3283
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3284
|
+
});
|
3340
3285
|
|
3341
3286
|
ObjectAssign(self.color, {
|
3342
3287
|
r, g, b, a,
|
@@ -3423,18 +3368,18 @@ class ColorPicker {
|
|
3423
3368
|
setControlPositions() {
|
3424
3369
|
const self = this;
|
3425
3370
|
const {
|
3426
|
-
|
3371
|
+
visuals, color, hsv,
|
3427
3372
|
} = self;
|
3428
3373
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3429
3374
|
const alpha = color.a;
|
3430
|
-
const hue =
|
3375
|
+
const hue = hsv.h;
|
3431
3376
|
|
3432
|
-
const saturation =
|
3433
|
-
const lightness =
|
3377
|
+
const saturation = hsv.s;
|
3378
|
+
const lightness = hsv.v;
|
3434
3379
|
|
3435
|
-
self.controlPositions.c1x =
|
3380
|
+
self.controlPositions.c1x = saturation * offsetWidth;
|
3436
3381
|
self.controlPositions.c1y = (1 - lightness) * offsetHeight;
|
3437
|
-
self.controlPositions.c2y =
|
3382
|
+
self.controlPositions.c2y = hue * offsetHeight;
|
3438
3383
|
self.controlPositions.c3y = (1 - alpha) * offsetHeight;
|
3439
3384
|
}
|
3440
3385
|
|
@@ -3442,78 +3387,40 @@ class ColorPicker {
|
|
3442
3387
|
updateAppearance() {
|
3443
3388
|
const self = this;
|
3444
3389
|
const {
|
3445
|
-
componentLabels,
|
3446
|
-
|
3390
|
+
componentLabels, color, parent,
|
3391
|
+
hsv, hex, format, controlKnobs,
|
3447
3392
|
} = self;
|
3448
3393
|
const {
|
3449
3394
|
appearanceLabel, hexLabel, valueLabel,
|
3450
3395
|
} = componentLabels;
|
3451
|
-
|
3396
|
+
let { r, g, b } = color.toRgb();
|
3452
3397
|
const [knob1, knob2, knob3] = controlKnobs;
|
3453
|
-
const hue = roundPart(
|
3398
|
+
const hue = roundPart(hsv.h * 360);
|
3454
3399
|
const alpha = color.a;
|
3455
|
-
const
|
3456
|
-
const
|
3457
|
-
const
|
3458
|
-
const hsvl = hsv.v * 100;
|
3459
|
-
let colorName;
|
3460
|
-
|
3461
|
-
// determine color appearance
|
3462
|
-
if (lightness === 100 && saturation === 0) {
|
3463
|
-
colorName = colorLabels.white;
|
3464
|
-
} else if (lightness === 0) {
|
3465
|
-
colorName = colorLabels.black;
|
3466
|
-
} else if (saturation === 0) {
|
3467
|
-
colorName = colorLabels.grey;
|
3468
|
-
} else if (hue < 15 || hue >= 345) {
|
3469
|
-
colorName = colorLabels.red;
|
3470
|
-
} else if (hue >= 15 && hue < 45) {
|
3471
|
-
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
3472
|
-
} else if (hue >= 45 && hue < 75) {
|
3473
|
-
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
3474
|
-
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
3475
|
-
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
3476
|
-
colorName = isOlive ? colorLabels.olive : colorName;
|
3477
|
-
} else if (hue >= 75 && hue < 155) {
|
3478
|
-
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
3479
|
-
} else if (hue >= 155 && hue < 175) {
|
3480
|
-
colorName = colorLabels.teal;
|
3481
|
-
} else if (hue >= 175 && hue < 195) {
|
3482
|
-
colorName = colorLabels.cyan;
|
3483
|
-
} else if (hue >= 195 && hue < 255) {
|
3484
|
-
colorName = colorLabels.blue;
|
3485
|
-
} else if (hue >= 255 && hue < 270) {
|
3486
|
-
colorName = colorLabels.violet;
|
3487
|
-
} else if (hue >= 270 && hue < 295) {
|
3488
|
-
colorName = colorLabels.magenta;
|
3489
|
-
} else if (hue >= 295 && hue < 345) {
|
3490
|
-
colorName = colorLabels.pink;
|
3491
|
-
}
|
3400
|
+
const saturation = roundPart(hsv.s * 100);
|
3401
|
+
const lightness = roundPart(hsv.v * 100);
|
3402
|
+
const colorName = self.appearance;
|
3492
3403
|
|
3493
3404
|
let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
|
3494
3405
|
|
3495
|
-
if (format === '
|
3496
|
-
colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
|
3497
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3498
|
-
setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
|
3499
|
-
setAttribute(knob1, ariaValueNow, `${hue}`);
|
3500
|
-
setAttribute(knob2, ariaValueText, `${saturation}%`);
|
3501
|
-
setAttribute(knob2, ariaValueNow, `${saturation}`);
|
3502
|
-
} else if (format === 'hwb') {
|
3406
|
+
if (format === 'hwb') {
|
3503
3407
|
const { hwb } = self;
|
3504
3408
|
const whiteness = roundPart(hwb.w * 100);
|
3505
3409
|
const blackness = roundPart(hwb.b * 100);
|
3506
3410
|
colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
|
3507
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3508
3411
|
setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
|
3509
3412
|
setAttribute(knob1, ariaValueNow, `${whiteness}`);
|
3413
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3510
3414
|
setAttribute(knob2, ariaValueText, `${hue}%`);
|
3511
3415
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3512
3416
|
} else {
|
3417
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3418
|
+
colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
|
3513
3419
|
colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
|
3514
|
-
|
3420
|
+
|
3515
3421
|
setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
|
3516
3422
|
setAttribute(knob1, ariaValueNow, `${lightness}`);
|
3423
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3517
3424
|
setAttribute(knob2, ariaValueText, `${hue}°`);
|
3518
3425
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3519
3426
|
}
|
@@ -3541,10 +3448,12 @@ class ColorPicker {
|
|
3541
3448
|
/** Updates the control knobs actual positions. */
|
3542
3449
|
updateControls() {
|
3543
3450
|
const { controlKnobs, controlPositions } = this;
|
3544
|
-
|
3451
|
+
let {
|
3545
3452
|
c1x, c1y, c2y, c3y,
|
3546
3453
|
} = controlPositions;
|
3547
3454
|
const [control1, control2, control3] = controlKnobs;
|
3455
|
+
// round control positions
|
3456
|
+
[c1x, c1y, c2y, c3y] = [c1x, c1y, c2y, c3y].map(roundPart);
|
3548
3457
|
|
3549
3458
|
setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
|
3550
3459
|
setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
|
@@ -3587,7 +3496,8 @@ class ColorPicker {
|
|
3587
3496
|
i3.value = `${blackness}`;
|
3588
3497
|
i4.value = `${alpha}`;
|
3589
3498
|
} else if (format === 'rgb') {
|
3590
|
-
|
3499
|
+
let { r, g, b } = self.rgb;
|
3500
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3591
3501
|
|
3592
3502
|
newColor = self.color.toRgbString();
|
3593
3503
|
i1.value = `${r}`;
|
@@ -3605,37 +3515,13 @@ class ColorPicker {
|
|
3605
3515
|
}
|
3606
3516
|
}
|
3607
3517
|
|
3608
|
-
/**
|
3609
|
-
* The `Space` & `Enter` keys specific event listener.
|
3610
|
-
* Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
|
3611
|
-
* @param {KeyboardEvent} e
|
3612
|
-
* @this {ColorPicker}
|
3613
|
-
*/
|
3614
|
-
keyToggle(e) {
|
3615
|
-
const self = this;
|
3616
|
-
const { menuToggle } = self;
|
3617
|
-
const { activeElement } = getDocument(menuToggle);
|
3618
|
-
const { code } = e;
|
3619
|
-
|
3620
|
-
if ([keyEnter, keySpace].includes(code)) {
|
3621
|
-
if ((menuToggle && activeElement === menuToggle) || !activeElement) {
|
3622
|
-
e.preventDefault();
|
3623
|
-
if (!activeElement) {
|
3624
|
-
self.togglePicker(e);
|
3625
|
-
} else {
|
3626
|
-
self.toggleMenu();
|
3627
|
-
}
|
3628
|
-
}
|
3629
|
-
}
|
3630
|
-
}
|
3631
|
-
|
3632
3518
|
/**
|
3633
3519
|
* Toggle the `ColorPicker` dropdown visibility.
|
3634
|
-
* @param {Event} e
|
3520
|
+
* @param {Event=} e
|
3635
3521
|
* @this {ColorPicker}
|
3636
3522
|
*/
|
3637
3523
|
togglePicker(e) {
|
3638
|
-
e.preventDefault();
|
3524
|
+
if (e) e.preventDefault();
|
3639
3525
|
const self = this;
|
3640
3526
|
const { colorPicker } = self;
|
3641
3527
|
|
@@ -3656,8 +3542,13 @@ class ColorPicker {
|
|
3656
3542
|
}
|
3657
3543
|
}
|
3658
3544
|
|
3659
|
-
/**
|
3660
|
-
|
3545
|
+
/**
|
3546
|
+
* Toggles the visibility of the `ColorPicker` presets menu.
|
3547
|
+
* @param {Event=} e
|
3548
|
+
* @this {ColorPicker}
|
3549
|
+
*/
|
3550
|
+
toggleMenu(e) {
|
3551
|
+
if (e) e.preventDefault();
|
3661
3552
|
const self = this;
|
3662
3553
|
const { colorMenu } = self;
|
3663
3554
|
|
@@ -3683,6 +3574,10 @@ class ColorPicker {
|
|
3683
3574
|
const relatedBtn = openPicker ? pickerToggle : menuToggle;
|
3684
3575
|
const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
|
3685
3576
|
|
3577
|
+
// if (!self.isValid) {
|
3578
|
+
self.value = self.color.toString(true);
|
3579
|
+
// }
|
3580
|
+
|
3686
3581
|
if (openDropdown) {
|
3687
3582
|
removeClass(openDropdown, 'show');
|
3688
3583
|
setAttribute(relatedBtn, ariaExpanded, 'false');
|
@@ -3696,9 +3591,6 @@ class ColorPicker {
|
|
3696
3591
|
}, animationDuration);
|
3697
3592
|
}
|
3698
3593
|
|
3699
|
-
if (!self.isValid) {
|
3700
|
-
self.value = self.color.toString();
|
3701
|
-
}
|
3702
3594
|
if (!focusPrevented) {
|
3703
3595
|
focus(pickerToggle);
|
3704
3596
|
}
|
@@ -3747,93 +3639,85 @@ let CPID = 0;
|
|
3747
3639
|
* `ColorPickerElement` Web Component.
|
3748
3640
|
* @example
|
3749
3641
|
* <label for="UNIQUE_ID">Label</label>
|
3750
|
-
* <color-picker
|
3751
|
-
* <input id="UNIQUE_ID"
|
3642
|
+
* <color-picker>
|
3643
|
+
* <input id="UNIQUE_ID" value="red" format="hex" class="color-preview btn-appearance">
|
3752
3644
|
* </color-picker>
|
3645
|
+
* // or
|
3646
|
+
* <label for="UNIQUE_ID">Label</label>
|
3647
|
+
* <color-picker data-id="UNIQUE_ID" data-value="red" data-format="hex"></color-picker>
|
3753
3648
|
*/
|
3754
3649
|
class ColorPickerElement extends HTMLElement {
|
3755
3650
|
constructor() {
|
3756
3651
|
super();
|
3757
|
-
/** @type {boolean} */
|
3758
|
-
this.isDisconnected = true;
|
3759
3652
|
this.attachShadow({ mode: 'open' });
|
3760
3653
|
}
|
3761
3654
|
|
3762
3655
|
/**
|
3763
3656
|
* Returns the current color value.
|
3764
|
-
* @returns {string
|
3657
|
+
* @returns {string | undefined}
|
3765
3658
|
*/
|
3766
|
-
get value() { return this.input
|
3659
|
+
get value() { return this.input && this.input.value; }
|
3767
3660
|
|
3768
3661
|
connectedCallback() {
|
3769
|
-
if (this.
|
3770
|
-
if (this.isDisconnected) {
|
3771
|
-
this.isDisconnected = false;
|
3772
|
-
}
|
3773
|
-
return;
|
3774
|
-
}
|
3662
|
+
if (this.input) return;
|
3775
3663
|
|
3776
|
-
|
3664
|
+
let [input] = getElementsByTagName('input', this);
|
3665
|
+
const value = (input && getAttribute(input, 'value')) || getAttribute(this, 'data-value') || '#fff';
|
3666
|
+
const format = (input && getAttribute(input, 'format')) || getAttribute(this, 'data-format') || 'rgb';
|
3667
|
+
let id = (input && getAttribute(input, 'id')) || getAttribute(this, 'data-id');
|
3668
|
+
|
3669
|
+
if (!id) {
|
3670
|
+
id = `color-picker-${format}-${CPID}`;
|
3671
|
+
CPID += 1;
|
3672
|
+
}
|
3777
3673
|
|
3778
|
-
if (!
|
3779
|
-
|
3780
|
-
const value = getAttribute(this, 'data-value') || '#069';
|
3781
|
-
const format = getAttribute(this, 'data-format') || 'rgb';
|
3782
|
-
const newInput = createElement({
|
3674
|
+
if (!input) {
|
3675
|
+
input = createElement({
|
3783
3676
|
tagName: 'input',
|
3784
3677
|
type: 'text',
|
3785
3678
|
className: 'color-preview btn-appearance',
|
3786
3679
|
});
|
3787
|
-
let id = getAttribute(this, 'data-id');
|
3788
|
-
if (!id) {
|
3789
|
-
id = `color-picker-${format}-${CPID}`;
|
3790
|
-
CPID += 1;
|
3791
|
-
}
|
3792
3680
|
|
3793
|
-
|
3794
|
-
|
3795
|
-
setAttribute(
|
3796
|
-
setAttribute(
|
3797
|
-
setAttribute(
|
3798
|
-
|
3799
|
-
setAttribute(newInput, 'spellcheck', 'false');
|
3800
|
-
setAttribute(newInput, 'value', value);
|
3801
|
-
this.append(newInput);
|
3681
|
+
setAttribute(input, 'id', id);
|
3682
|
+
setAttribute(input, 'name', id);
|
3683
|
+
setAttribute(input, 'autocomplete', 'off');
|
3684
|
+
setAttribute(input, 'spellcheck', 'false');
|
3685
|
+
setAttribute(input, 'value', value);
|
3686
|
+
this.append(input);
|
3802
3687
|
}
|
3688
|
+
/** @type {HTMLInputElement} */
|
3689
|
+
// @ts-ignore - `HTMLInputElement` is `HTMLElement`
|
3690
|
+
this.input = input;
|
3803
3691
|
|
3804
|
-
|
3805
|
-
|
3806
|
-
if (input) {
|
3807
|
-
/** @type {HTMLInputElement} */
|
3808
|
-
// @ts-ignore - `HTMLInputElement` is `HTMLElement`
|
3809
|
-
this.input = input;
|
3810
|
-
|
3811
|
-
// @ts-ignore - `HTMLInputElement` is `HTMLElement`
|
3812
|
-
this.colorPicker = new ColorPicker(input);
|
3813
|
-
this.color = this.colorPicker.color;
|
3814
|
-
|
3815
|
-
if (this.shadowRoot) {
|
3816
|
-
this.shadowRoot.append(createElement('slot'));
|
3817
|
-
}
|
3692
|
+
// @ts-ignore - `HTMLInputElement` is `HTMLElement`
|
3693
|
+
this.colorPicker = new ColorPicker(input);
|
3818
3694
|
|
3819
|
-
|
3820
|
-
|
3695
|
+
// @ts-ignore - `shadowRoot` is defined in the constructor
|
3696
|
+
this.shadowRoot.append(createElement('slot'));
|
3821
3697
|
}
|
3822
3698
|
|
3699
|
+
/** @this {ColorPickerElement} */
|
3823
3700
|
disconnectedCallback() {
|
3824
|
-
|
3825
|
-
|
3701
|
+
const { input, colorPicker, shadowRoot } = this;
|
3702
|
+
if (colorPicker) colorPicker.dispose();
|
3703
|
+
if (input) input.remove();
|
3704
|
+
if (shadowRoot) shadowRoot.innerHTML = '';
|
3705
|
+
|
3706
|
+
ObjectAssign(this, {
|
3707
|
+
colorPicker: undefined,
|
3708
|
+
input: undefined,
|
3709
|
+
});
|
3826
3710
|
}
|
3827
3711
|
}
|
3828
3712
|
|
3829
3713
|
ObjectAssign(ColorPickerElement, {
|
3830
3714
|
Color,
|
3831
3715
|
ColorPicker,
|
3832
|
-
ColorPalette,
|
3833
|
-
getInstance:
|
3716
|
+
ColorPalette, // @ts-ignore
|
3717
|
+
getInstance: ColorPicker.getInstance,
|
3834
3718
|
Version,
|
3835
3719
|
});
|
3836
3720
|
|
3837
3721
|
customElements.define('color-picker', ColorPickerElement);
|
3838
3722
|
|
3839
|
-
export default
|
3723
|
+
export { ColorPickerElement as default };
|