@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
package/src/js/color-picker.js
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
import { addListener, removeListener } from 'event-listener.js';
|
2
2
|
|
3
3
|
import ariaDescription from 'shorter-js/src/strings/ariaDescription';
|
4
|
-
// import ariaLabel from 'shorter-js/src/strings/ariaLabel';
|
5
4
|
import ariaSelected from 'shorter-js/src/strings/ariaSelected';
|
6
5
|
import ariaExpanded from 'shorter-js/src/strings/ariaExpanded';
|
7
6
|
import ariaValueText from 'shorter-js/src/strings/ariaValueText';
|
8
7
|
import ariaValueNow from 'shorter-js/src/strings/ariaValueNow';
|
9
|
-
import ariaHasPopup from 'shorter-js/src/strings/ariaHasPopup';
|
10
|
-
import ariaHidden from 'shorter-js/src/strings/ariaHidden';
|
11
|
-
import ariaLabelledBy from 'shorter-js/src/strings/ariaLabelledBy';
|
12
8
|
import keyArrowDown from 'shorter-js/src/strings/keyArrowDown';
|
13
9
|
import keyArrowUp from 'shorter-js/src/strings/keyArrowUp';
|
14
10
|
import keyArrowLeft from 'shorter-js/src/strings/keyArrowLeft';
|
@@ -31,7 +27,6 @@ import keyupEvent from 'shorter-js/src/strings/keyupEvent';
|
|
31
27
|
import resizeEvent from 'shorter-js/src/strings/resizeEvent';
|
32
28
|
import focusoutEvent from 'shorter-js/src/strings/focusoutEvent';
|
33
29
|
|
34
|
-
import isMobile from 'shorter-js/src/boolean/isMobile';
|
35
30
|
import getDocument from 'shorter-js/src/get/getDocument';
|
36
31
|
import getDocumentElement from 'shorter-js/src/get/getDocumentElement';
|
37
32
|
import getWindow from 'shorter-js/src/get/getWindow';
|
@@ -42,8 +37,6 @@ import getElementTransitionDuration from 'shorter-js/src/get/getElementTransitio
|
|
42
37
|
import querySelector from 'shorter-js/src/selectors/querySelector';
|
43
38
|
import closest from 'shorter-js/src/selectors/closest';
|
44
39
|
import getElementsByClassName from 'shorter-js/src/selectors/getElementsByClassName';
|
45
|
-
import createElement from 'shorter-js/src/misc/createElement';
|
46
|
-
import createElementNS from 'shorter-js/src/misc/createElementNS';
|
47
40
|
import dispatchEvent from 'shorter-js/src/misc/dispatchEvent';
|
48
41
|
import ObjectAssign from 'shorter-js/src/misc/ObjectAssign';
|
49
42
|
import Data, { getInstance } from 'shorter-js/src/misc/data';
|
@@ -60,19 +53,16 @@ import removeAttribute from 'shorter-js/src/attr/removeAttribute';
|
|
60
53
|
|
61
54
|
// ColorPicker Util
|
62
55
|
// ================
|
56
|
+
import Color from './color';
|
57
|
+
import ColorPalette from './color-palette';
|
63
58
|
import colorPickerLabels from './util/colorPickerLabels';
|
64
59
|
import colorNames from './util/colorNames';
|
65
60
|
import nonColors from './util/nonColors';
|
66
|
-
import getColorForm from './util/getColorForm';
|
67
|
-
import getColorControls from './util/getColorControls';
|
68
|
-
import getColorMenu from './util/getColorMenu';
|
69
|
-
import vHidden from './util/vHidden';
|
70
61
|
import tabIndex from './util/tabindex';
|
71
62
|
import isValidJSON from './util/isValidJSON';
|
72
63
|
import roundPart from './util/roundPart';
|
73
|
-
import
|
74
|
-
import
|
75
|
-
import Version from './version';
|
64
|
+
import setMarkup from './util/setMarkup';
|
65
|
+
import Version from './util/version';
|
76
66
|
|
77
67
|
// ColorPicker GC
|
78
68
|
// ==============
|
@@ -91,7 +81,7 @@ const colorPickerDefaults = {
|
|
91
81
|
// ==========================
|
92
82
|
|
93
83
|
/** @type {CP.GetInstance<ColorPicker>} */
|
94
|
-
|
84
|
+
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
95
85
|
|
96
86
|
/** @type {CP.InitCallback<ColorPicker>} */
|
97
87
|
const initColorPicker = (element) => new ColorPicker(element);
|
@@ -99,110 +89,6 @@ const initColorPicker = (element) => new ColorPicker(element);
|
|
99
89
|
// ColorPicker Private Methods
|
100
90
|
// ===========================
|
101
91
|
|
102
|
-
/**
|
103
|
-
* Generate HTML markup and update instance properties.
|
104
|
-
* @param {ColorPicker} self
|
105
|
-
*/
|
106
|
-
function initCallback(self) {
|
107
|
-
const {
|
108
|
-
input, parent, format, id, componentLabels, colorKeywords, colorPresets,
|
109
|
-
} = self;
|
110
|
-
const colorValue = getAttribute(input, 'value') || '#fff';
|
111
|
-
|
112
|
-
const {
|
113
|
-
toggleLabel, pickerLabel, formatLabel, hexLabel,
|
114
|
-
} = componentLabels;
|
115
|
-
|
116
|
-
// update color
|
117
|
-
const color = nonColors.includes(colorValue) ? '#fff' : colorValue;
|
118
|
-
self.color = new Color(color, format);
|
119
|
-
|
120
|
-
// set initial controls dimensions
|
121
|
-
// make the controls smaller on mobile
|
122
|
-
const dropClass = isMobile ? ' mobile' : '';
|
123
|
-
const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
|
124
|
-
|
125
|
-
const pickerBtn = createElement({
|
126
|
-
id: `picker-btn-${id}`,
|
127
|
-
tagName: 'button',
|
128
|
-
className: 'picker-toggle btn-appearance',
|
129
|
-
});
|
130
|
-
setAttribute(pickerBtn, ariaExpanded, 'false');
|
131
|
-
setAttribute(pickerBtn, ariaHasPopup, 'true');
|
132
|
-
pickerBtn.append(createElement({
|
133
|
-
tagName: 'span',
|
134
|
-
className: vHidden,
|
135
|
-
innerText: `${pickerLabel}. ${formatLabel}: ${formatString}`,
|
136
|
-
}));
|
137
|
-
|
138
|
-
const pickerDropdown = createElement({
|
139
|
-
tagName: 'div',
|
140
|
-
className: `color-dropdown picker${dropClass}`,
|
141
|
-
});
|
142
|
-
setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
|
143
|
-
setAttribute(pickerDropdown, 'role', 'group');
|
144
|
-
|
145
|
-
const colorControls = getColorControls(self);
|
146
|
-
const colorForm = getColorForm(self);
|
147
|
-
|
148
|
-
pickerDropdown.append(colorControls, colorForm);
|
149
|
-
input.before(pickerBtn);
|
150
|
-
parent.append(pickerDropdown);
|
151
|
-
|
152
|
-
// set colour key menu template
|
153
|
-
if (colorKeywords || colorPresets) {
|
154
|
-
const presetsDropdown = createElement({
|
155
|
-
tagName: 'div',
|
156
|
-
className: `color-dropdown scrollable menu${dropClass}`,
|
157
|
-
});
|
158
|
-
|
159
|
-
// color presets
|
160
|
-
if ((colorPresets instanceof Array && colorPresets.length)
|
161
|
-
|| (colorPresets instanceof ColorPalette && colorPresets.colors)) {
|
162
|
-
const presetsMenu = getColorMenu(self, colorPresets, 'color-options');
|
163
|
-
presetsDropdown.append(presetsMenu);
|
164
|
-
}
|
165
|
-
|
166
|
-
// explicit defaults [reset, initial, inherit, transparent, currentColor]
|
167
|
-
if (colorKeywords && colorKeywords.length) {
|
168
|
-
const keywordsMenu = getColorMenu(self, colorKeywords, 'color-defaults');
|
169
|
-
presetsDropdown.append(keywordsMenu);
|
170
|
-
}
|
171
|
-
|
172
|
-
const presetsBtn = createElement({
|
173
|
-
tagName: 'button',
|
174
|
-
className: 'menu-toggle btn-appearance',
|
175
|
-
});
|
176
|
-
setAttribute(presetsBtn, tabIndex, '-1');
|
177
|
-
setAttribute(presetsBtn, ariaExpanded, 'false');
|
178
|
-
setAttribute(presetsBtn, ariaHasPopup, 'true');
|
179
|
-
|
180
|
-
const xmlns = encodeURI('http://www.w3.org/2000/svg');
|
181
|
-
const presetsIcon = createElementNS(xmlns, { tagName: 'svg' });
|
182
|
-
setAttribute(presetsIcon, 'xmlns', xmlns);
|
183
|
-
setAttribute(presetsIcon, 'viewBox', '0 0 512 512');
|
184
|
-
setAttribute(presetsIcon, ariaHidden, 'true');
|
185
|
-
|
186
|
-
const path = createElementNS(xmlns, { tagName: 'path' });
|
187
|
-
setAttribute(path, 'd', 'M98,158l157,156L411,158l27,27L255,368L71,185L98,158z');
|
188
|
-
setAttribute(path, 'fill', '#fff');
|
189
|
-
presetsIcon.append(path);
|
190
|
-
presetsBtn.append(createElement({
|
191
|
-
tagName: 'span',
|
192
|
-
className: vHidden,
|
193
|
-
innerText: `${toggleLabel}`,
|
194
|
-
}), presetsIcon);
|
195
|
-
|
196
|
-
parent.append(presetsBtn, presetsDropdown);
|
197
|
-
}
|
198
|
-
|
199
|
-
// solve non-colors after settings save
|
200
|
-
if (colorKeywords && nonColors.includes(colorValue)) {
|
201
|
-
self.value = colorValue;
|
202
|
-
}
|
203
|
-
setAttribute(input, tabIndex, '-1');
|
204
|
-
}
|
205
|
-
|
206
92
|
/**
|
207
93
|
* Add / remove `ColorPicker` main event listeners.
|
208
94
|
* @param {ColorPicker} self
|
@@ -215,8 +101,6 @@ function toggleEvents(self, action) {
|
|
215
101
|
fn(input, focusinEvent, self.showPicker);
|
216
102
|
fn(pickerToggle, mouseclickEvent, self.togglePicker);
|
217
103
|
|
218
|
-
fn(input, keydownEvent, self.keyToggle);
|
219
|
-
|
220
104
|
if (menuToggle) {
|
221
105
|
fn(menuToggle, mouseclickEvent, self.toggleMenu);
|
222
106
|
}
|
@@ -254,8 +138,7 @@ function toggleEventsOnShown(self, action) {
|
|
254
138
|
fn(doc, pointerEvents.move, self.pointerMove);
|
255
139
|
fn(doc, pointerEvents.up, self.pointerUp);
|
256
140
|
fn(parent, focusoutEvent, self.handleFocusOut);
|
257
|
-
|
258
|
-
fn(win, keyupEvent, self.handleDismiss);
|
141
|
+
fn(doc, keyupEvent, self.handleDismiss);
|
259
142
|
}
|
260
143
|
|
261
144
|
/**
|
@@ -339,7 +222,7 @@ export default class ColorPicker {
|
|
339
222
|
const input = querySelector(target);
|
340
223
|
|
341
224
|
// invalidate
|
342
|
-
if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
|
225
|
+
if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
|
343
226
|
self.input = input;
|
344
227
|
|
345
228
|
const parent = closest(input, colorPickerParentSelector);
|
@@ -386,15 +269,14 @@ export default class ColorPicker {
|
|
386
269
|
});
|
387
270
|
|
388
271
|
// update and expose component labels
|
389
|
-
const
|
390
|
-
|
391
|
-
? JSON.parse(componentLabels) : componentLabels || {};
|
272
|
+
const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
|
273
|
+
? JSON.parse(componentLabels) : componentLabels;
|
392
274
|
|
393
275
|
/** @type {Record<string, string>} */
|
394
|
-
self.componentLabels = ObjectAssign(
|
276
|
+
self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
|
395
277
|
|
396
278
|
/** @type {Color} */
|
397
|
-
self.color = new Color('
|
279
|
+
self.color = new Color(input.value || '#fff', format);
|
398
280
|
|
399
281
|
/** @type {CP.ColorFormats} */
|
400
282
|
self.format = format;
|
@@ -403,7 +285,7 @@ export default class ColorPicker {
|
|
403
285
|
if (colorKeywords instanceof Array) {
|
404
286
|
self.colorKeywords = colorKeywords;
|
405
287
|
} else if (typeof colorKeywords === 'string' && colorKeywords.length) {
|
406
|
-
self.colorKeywords = colorKeywords.split(',');
|
288
|
+
self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
|
407
289
|
}
|
408
290
|
|
409
291
|
// set colour presets
|
@@ -432,11 +314,10 @@ export default class ColorPicker {
|
|
432
314
|
self.handleFocusOut = self.handleFocusOut.bind(self);
|
433
315
|
self.changeHandler = self.changeHandler.bind(self);
|
434
316
|
self.handleDismiss = self.handleDismiss.bind(self);
|
435
|
-
self.keyToggle = self.keyToggle.bind(self);
|
436
317
|
self.handleKnobs = self.handleKnobs.bind(self);
|
437
318
|
|
438
319
|
// generate markup
|
439
|
-
|
320
|
+
setMarkup(self);
|
440
321
|
|
441
322
|
const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
|
442
323
|
// set main elements
|
@@ -524,76 +405,83 @@ export default class ColorPicker {
|
|
524
405
|
return inputValue !== '' && new Color(inputValue).isValid;
|
525
406
|
}
|
526
407
|
|
408
|
+
/** Returns the colour appearance, usually the closest colour name for the current value. */
|
409
|
+
get appearance() {
|
410
|
+
const {
|
411
|
+
colorLabels, hsl, hsv, format,
|
412
|
+
} = this;
|
413
|
+
|
414
|
+
const hue = roundPart(hsl.h * 360);
|
415
|
+
const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
|
416
|
+
const saturation = roundPart(saturationSource * 100);
|
417
|
+
const lightness = roundPart(hsl.l * 100);
|
418
|
+
const hsvl = hsv.v * 100;
|
419
|
+
|
420
|
+
let colorName;
|
421
|
+
|
422
|
+
// determine color appearance
|
423
|
+
if (lightness === 100 && saturation === 0) {
|
424
|
+
colorName = colorLabels.white;
|
425
|
+
} else if (lightness === 0) {
|
426
|
+
colorName = colorLabels.black;
|
427
|
+
} else if (saturation === 0) {
|
428
|
+
colorName = colorLabels.grey;
|
429
|
+
} else if (hue < 15 || hue >= 345) {
|
430
|
+
colorName = colorLabels.red;
|
431
|
+
} else if (hue >= 15 && hue < 45) {
|
432
|
+
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
433
|
+
} else if (hue >= 45 && hue < 75) {
|
434
|
+
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
435
|
+
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
436
|
+
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
437
|
+
colorName = isOlive ? colorLabels.olive : colorName;
|
438
|
+
} else if (hue >= 75 && hue < 155) {
|
439
|
+
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
440
|
+
} else if (hue >= 155 && hue < 175) {
|
441
|
+
colorName = colorLabels.teal;
|
442
|
+
} else if (hue >= 175 && hue < 195) {
|
443
|
+
colorName = colorLabels.cyan;
|
444
|
+
} else if (hue >= 195 && hue < 255) {
|
445
|
+
colorName = colorLabels.blue;
|
446
|
+
} else if (hue >= 255 && hue < 270) {
|
447
|
+
colorName = colorLabels.violet;
|
448
|
+
} else if (hue >= 270 && hue < 295) {
|
449
|
+
colorName = colorLabels.magenta;
|
450
|
+
} else if (hue >= 295 && hue < 345) {
|
451
|
+
colorName = colorLabels.pink;
|
452
|
+
}
|
453
|
+
return colorName;
|
454
|
+
}
|
455
|
+
|
527
456
|
/** Updates `ColorPicker` visuals. */
|
528
457
|
updateVisuals() {
|
529
458
|
const self = this;
|
530
459
|
const {
|
531
|
-
|
460
|
+
controlPositions, visuals,
|
532
461
|
} = self;
|
533
462
|
const [v1, v2, v3] = visuals;
|
534
|
-
const {
|
535
|
-
const hue =
|
536
|
-
|
537
|
-
: controlPositions.c2y / offsetHeight;
|
538
|
-
// @ts-ignore - `hslToRgb` is assigned to `Color` as static method
|
539
|
-
const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
|
463
|
+
const { offsetHeight } = v1;
|
464
|
+
const hue = controlPositions.c2y / offsetHeight;
|
465
|
+
const { r, g, b } = new Color({ h: hue, s: 1, l: 0.5 }).toRgb();
|
540
466
|
const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
|
541
467
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
542
468
|
const roundA = roundPart((alpha * 100)) / 100;
|
543
469
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
setElementStyle(v2, { background: hueGradient });
|
559
|
-
} else {
|
560
|
-
const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
|
561
|
-
const fill0 = new Color({
|
562
|
-
r: 255, g: 0, b: 0, a: alpha,
|
563
|
-
}).saturate(-saturation).toRgbString();
|
564
|
-
const fill1 = new Color({
|
565
|
-
r: 255, g: 255, b: 0, a: alpha,
|
566
|
-
}).saturate(-saturation).toRgbString();
|
567
|
-
const fill2 = new Color({
|
568
|
-
r: 0, g: 255, b: 0, a: alpha,
|
569
|
-
}).saturate(-saturation).toRgbString();
|
570
|
-
const fill3 = new Color({
|
571
|
-
r: 0, g: 255, b: 255, a: alpha,
|
572
|
-
}).saturate(-saturation).toRgbString();
|
573
|
-
const fill4 = new Color({
|
574
|
-
r: 0, g: 0, b: 255, a: alpha,
|
575
|
-
}).saturate(-saturation).toRgbString();
|
576
|
-
const fill5 = new Color({
|
577
|
-
r: 255, g: 0, b: 255, a: alpha,
|
578
|
-
}).saturate(-saturation).toRgbString();
|
579
|
-
const fill6 = new Color({
|
580
|
-
r: 255, g: 0, b: 0, a: alpha,
|
581
|
-
}).saturate(-saturation).toRgbString();
|
582
|
-
const fillGradient = `linear-gradient(to right,
|
583
|
-
${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
|
584
|
-
${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
|
585
|
-
const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
|
586
|
-
linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
|
587
|
-
|
588
|
-
setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
|
589
|
-
const {
|
590
|
-
r: gr, g: gg, b: gb,
|
591
|
-
} = new Color({ r, g, b }).greyscale().toRgb();
|
470
|
+
const fill = new Color({
|
471
|
+
h: hue, s: 1, l: 0.5, a: alpha,
|
472
|
+
}).toRgbString();
|
473
|
+
const hueGradient = `linear-gradient(
|
474
|
+
rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
|
475
|
+
rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
|
476
|
+
rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
|
477
|
+
rgb(255,0,0) 100%)`;
|
478
|
+
setElementStyle(v1, {
|
479
|
+
background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
|
480
|
+
linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
|
481
|
+
${whiteGrad}`,
|
482
|
+
});
|
483
|
+
setElementStyle(v2, { background: hueGradient });
|
592
484
|
|
593
|
-
setElementStyle(v2, {
|
594
|
-
background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
|
595
|
-
});
|
596
|
-
}
|
597
485
|
setElementStyle(v3, {
|
598
486
|
background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
|
599
487
|
});
|
@@ -632,7 +520,7 @@ export default class ColorPicker {
|
|
632
520
|
const self = this;
|
633
521
|
const { activeElement } = getDocument(self.input);
|
634
522
|
|
635
|
-
if ((
|
523
|
+
if ((e.type === touchmoveEvent && self.dragElement)
|
636
524
|
|| (activeElement && self.controlKnobs.includes(activeElement))) {
|
637
525
|
e.stopPropagation();
|
638
526
|
e.preventDefault();
|
@@ -743,13 +631,13 @@ export default class ColorPicker {
|
|
743
631
|
const [v1, v2, v3] = visuals;
|
744
632
|
const [c1, c2, c3] = controlKnobs;
|
745
633
|
/** @type {HTMLElement} */
|
746
|
-
const visual =
|
747
|
-
? target : querySelector('.visual-control', target.parentElement);
|
634
|
+
const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
|
748
635
|
const visualRect = getBoundingClientRect(visual);
|
636
|
+
const html = getDocumentElement(v1);
|
749
637
|
const X = type === 'touchstart' ? touches[0].pageX : pageX;
|
750
638
|
const Y = type === 'touchstart' ? touches[0].pageY : pageY;
|
751
|
-
const offsetX = X -
|
752
|
-
const offsetY = Y -
|
639
|
+
const offsetX = X - html.scrollLeft - visualRect.left;
|
640
|
+
const offsetY = Y - html.scrollTop - visualRect.top;
|
753
641
|
|
754
642
|
if (target === v1 || target === c1) {
|
755
643
|
self.dragElement = visual;
|
@@ -809,10 +697,11 @@ export default class ColorPicker {
|
|
809
697
|
if (!dragElement) return;
|
810
698
|
|
811
699
|
const controlRect = getBoundingClientRect(dragElement);
|
812
|
-
const
|
813
|
-
const
|
814
|
-
const
|
815
|
-
const
|
700
|
+
const win = getDocumentElement(v1);
|
701
|
+
const X = type === touchmoveEvent ? touches[0].pageX : pageX;
|
702
|
+
const Y = type === touchmoveEvent ? touches[0].pageY : pageY;
|
703
|
+
const offsetX = X - win.scrollLeft - controlRect.left;
|
704
|
+
const offsetY = Y - win.scrollTop - controlRect.top;
|
816
705
|
|
817
706
|
if (dragElement === v1) {
|
818
707
|
self.changeControl1(offsetX, offsetY);
|
@@ -839,30 +728,41 @@ export default class ColorPicker {
|
|
839
728
|
if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
|
840
729
|
e.preventDefault();
|
841
730
|
|
842
|
-
const { controlKnobs } = self;
|
731
|
+
const { controlKnobs, visuals } = self;
|
732
|
+
const { offsetWidth, offsetHeight } = visuals[0];
|
843
733
|
const [c1, c2, c3] = controlKnobs;
|
844
734
|
const { activeElement } = getDocument(c1);
|
845
735
|
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
736
|
+
const yRatio = offsetHeight / 360;
|
846
737
|
|
847
738
|
if (currentKnob) {
|
848
739
|
let offsetX = 0;
|
849
740
|
let offsetY = 0;
|
741
|
+
|
850
742
|
if (target === c1) {
|
743
|
+
const xRatio = offsetWidth / 100;
|
744
|
+
|
851
745
|
if ([keyArrowLeft, keyArrowRight].includes(code)) {
|
852
|
-
self.controlPositions.c1x += code === keyArrowRight ?
|
746
|
+
self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
|
853
747
|
} else if ([keyArrowUp, keyArrowDown].includes(code)) {
|
854
|
-
self.controlPositions.c1y += code === keyArrowDown ?
|
748
|
+
self.controlPositions.c1y += code === keyArrowDown ? yRatio : -yRatio;
|
855
749
|
}
|
856
750
|
|
857
751
|
offsetX = self.controlPositions.c1x;
|
858
752
|
offsetY = self.controlPositions.c1y;
|
859
753
|
self.changeControl1(offsetX, offsetY);
|
860
754
|
} else if (target === c2) {
|
861
|
-
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
755
|
+
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
756
|
+
? yRatio
|
757
|
+
: -yRatio;
|
758
|
+
|
862
759
|
offsetY = self.controlPositions.c2y;
|
863
760
|
self.changeControl2(offsetY);
|
864
761
|
} else if (target === c3) {
|
865
|
-
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
762
|
+
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
763
|
+
? yRatio
|
764
|
+
: -yRatio;
|
765
|
+
|
866
766
|
offsetY = self.controlPositions.c3y;
|
867
767
|
self.changeAlpha(offsetY);
|
868
768
|
}
|
@@ -890,7 +790,7 @@ export default class ColorPicker {
|
|
890
790
|
if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
|
891
791
|
if (activeElement === input) {
|
892
792
|
if (isNonColorValue) {
|
893
|
-
colorSource = '
|
793
|
+
colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
|
894
794
|
} else {
|
895
795
|
colorSource = currentValue;
|
896
796
|
}
|
@@ -941,9 +841,7 @@ export default class ColorPicker {
|
|
941
841
|
changeControl1(X, Y) {
|
942
842
|
const self = this;
|
943
843
|
let [offsetX, offsetY] = [0, 0];
|
944
|
-
const {
|
945
|
-
format, controlPositions, visuals,
|
946
|
-
} = self;
|
844
|
+
const { controlPositions, visuals } = self;
|
947
845
|
const { offsetHeight, offsetWidth } = visuals[0];
|
948
846
|
|
949
847
|
if (X > offsetWidth) offsetX = offsetWidth;
|
@@ -952,29 +850,19 @@ export default class ColorPicker {
|
|
952
850
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
953
851
|
else if (Y >= 0) offsetY = Y;
|
954
852
|
|
955
|
-
const hue =
|
956
|
-
? offsetX / offsetWidth
|
957
|
-
: controlPositions.c2y / offsetHeight;
|
853
|
+
const hue = controlPositions.c2y / offsetHeight;
|
958
854
|
|
959
|
-
const saturation =
|
960
|
-
? 1 - controlPositions.c2y / offsetHeight
|
961
|
-
: offsetX / offsetWidth;
|
855
|
+
const saturation = offsetX / offsetWidth;
|
962
856
|
|
963
857
|
const lightness = 1 - offsetY / offsetHeight;
|
964
858
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
965
859
|
|
966
|
-
const colorObject = format === 'hsl'
|
967
|
-
? {
|
968
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
969
|
-
}
|
970
|
-
: {
|
971
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
972
|
-
};
|
973
|
-
|
974
860
|
// new color
|
975
861
|
const {
|
976
862
|
r, g, b, a,
|
977
|
-
} = new Color(
|
863
|
+
} = new Color({
|
864
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
865
|
+
});
|
978
866
|
|
979
867
|
ObjectAssign(self.color, {
|
980
868
|
r, g, b, a,
|
@@ -1001,7 +889,7 @@ export default class ColorPicker {
|
|
1001
889
|
changeControl2(Y) {
|
1002
890
|
const self = this;
|
1003
891
|
const {
|
1004
|
-
|
892
|
+
controlPositions, visuals,
|
1005
893
|
} = self;
|
1006
894
|
const { offsetHeight, offsetWidth } = visuals[0];
|
1007
895
|
|
@@ -1010,26 +898,17 @@ export default class ColorPicker {
|
|
1010
898
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
1011
899
|
else if (Y >= 0) offsetY = Y;
|
1012
900
|
|
1013
|
-
const hue =
|
1014
|
-
|
1015
|
-
: offsetY / offsetHeight;
|
1016
|
-
const saturation = format === 'hsl'
|
1017
|
-
? 1 - offsetY / offsetHeight
|
1018
|
-
: controlPositions.c1x / offsetWidth;
|
901
|
+
const hue = offsetY / offsetHeight;
|
902
|
+
const saturation = controlPositions.c1x / offsetWidth;
|
1019
903
|
const lightness = 1 - controlPositions.c1y / offsetHeight;
|
1020
904
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
1021
|
-
const colorObject = format === 'hsl'
|
1022
|
-
? {
|
1023
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
1024
|
-
}
|
1025
|
-
: {
|
1026
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
1027
|
-
};
|
1028
905
|
|
1029
906
|
// new color
|
1030
907
|
const {
|
1031
908
|
r, g, b, a,
|
1032
|
-
} = new Color(
|
909
|
+
} = new Color({
|
910
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
911
|
+
});
|
1033
912
|
|
1034
913
|
ObjectAssign(self.color, {
|
1035
914
|
r, g, b, a,
|
@@ -1116,18 +995,18 @@ export default class ColorPicker {
|
|
1116
995
|
setControlPositions() {
|
1117
996
|
const self = this;
|
1118
997
|
const {
|
1119
|
-
|
998
|
+
visuals, color, hsv,
|
1120
999
|
} = self;
|
1121
1000
|
const { offsetHeight, offsetWidth } = visuals[0];
|
1122
1001
|
const alpha = color.a;
|
1123
|
-
const hue =
|
1002
|
+
const hue = hsv.h;
|
1124
1003
|
|
1125
|
-
const saturation =
|
1126
|
-
const lightness =
|
1004
|
+
const saturation = hsv.s;
|
1005
|
+
const lightness = hsv.v;
|
1127
1006
|
|
1128
|
-
self.controlPositions.c1x =
|
1007
|
+
self.controlPositions.c1x = saturation * offsetWidth;
|
1129
1008
|
self.controlPositions.c1y = (1 - lightness) * offsetHeight;
|
1130
|
-
self.controlPositions.c2y =
|
1009
|
+
self.controlPositions.c2y = hue * offsetHeight;
|
1131
1010
|
self.controlPositions.c3y = (1 - alpha) * offsetHeight;
|
1132
1011
|
}
|
1133
1012
|
|
@@ -1135,78 +1014,40 @@ export default class ColorPicker {
|
|
1135
1014
|
updateAppearance() {
|
1136
1015
|
const self = this;
|
1137
1016
|
const {
|
1138
|
-
componentLabels,
|
1139
|
-
|
1017
|
+
componentLabels, color, parent,
|
1018
|
+
hsv, hex, format, controlKnobs,
|
1140
1019
|
} = self;
|
1141
1020
|
const {
|
1142
1021
|
appearanceLabel, hexLabel, valueLabel,
|
1143
1022
|
} = componentLabels;
|
1144
|
-
|
1023
|
+
let { r, g, b } = color.toRgb();
|
1145
1024
|
const [knob1, knob2, knob3] = controlKnobs;
|
1146
|
-
const hue = roundPart(
|
1025
|
+
const hue = roundPart(hsv.h * 360);
|
1147
1026
|
const alpha = color.a;
|
1148
|
-
const
|
1149
|
-
const
|
1150
|
-
const
|
1151
|
-
const hsvl = hsv.v * 100;
|
1152
|
-
let colorName;
|
1153
|
-
|
1154
|
-
// determine color appearance
|
1155
|
-
if (lightness === 100 && saturation === 0) {
|
1156
|
-
colorName = colorLabels.white;
|
1157
|
-
} else if (lightness === 0) {
|
1158
|
-
colorName = colorLabels.black;
|
1159
|
-
} else if (saturation === 0) {
|
1160
|
-
colorName = colorLabels.grey;
|
1161
|
-
} else if (hue < 15 || hue >= 345) {
|
1162
|
-
colorName = colorLabels.red;
|
1163
|
-
} else if (hue >= 15 && hue < 45) {
|
1164
|
-
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
1165
|
-
} else if (hue >= 45 && hue < 75) {
|
1166
|
-
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
1167
|
-
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
1168
|
-
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
1169
|
-
colorName = isOlive ? colorLabels.olive : colorName;
|
1170
|
-
} else if (hue >= 75 && hue < 155) {
|
1171
|
-
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
1172
|
-
} else if (hue >= 155 && hue < 175) {
|
1173
|
-
colorName = colorLabels.teal;
|
1174
|
-
} else if (hue >= 175 && hue < 195) {
|
1175
|
-
colorName = colorLabels.cyan;
|
1176
|
-
} else if (hue >= 195 && hue < 255) {
|
1177
|
-
colorName = colorLabels.blue;
|
1178
|
-
} else if (hue >= 255 && hue < 270) {
|
1179
|
-
colorName = colorLabels.violet;
|
1180
|
-
} else if (hue >= 270 && hue < 295) {
|
1181
|
-
colorName = colorLabels.magenta;
|
1182
|
-
} else if (hue >= 295 && hue < 345) {
|
1183
|
-
colorName = colorLabels.pink;
|
1184
|
-
}
|
1027
|
+
const saturation = roundPart(hsv.s * 100);
|
1028
|
+
const lightness = roundPart(hsv.v * 100);
|
1029
|
+
const colorName = self.appearance;
|
1185
1030
|
|
1186
1031
|
let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
|
1187
1032
|
|
1188
|
-
if (format === '
|
1189
|
-
colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
|
1190
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1191
|
-
setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
|
1192
|
-
setAttribute(knob1, ariaValueNow, `${hue}`);
|
1193
|
-
setAttribute(knob2, ariaValueText, `${saturation}%`);
|
1194
|
-
setAttribute(knob2, ariaValueNow, `${saturation}`);
|
1195
|
-
} else if (format === 'hwb') {
|
1033
|
+
if (format === 'hwb') {
|
1196
1034
|
const { hwb } = self;
|
1197
1035
|
const whiteness = roundPart(hwb.w * 100);
|
1198
1036
|
const blackness = roundPart(hwb.b * 100);
|
1199
1037
|
colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
|
1200
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1201
1038
|
setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
|
1202
1039
|
setAttribute(knob1, ariaValueNow, `${whiteness}`);
|
1040
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1203
1041
|
setAttribute(knob2, ariaValueText, `${hue}%`);
|
1204
1042
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
1205
1043
|
} else {
|
1044
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
1045
|
+
colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
|
1206
1046
|
colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
|
1207
|
-
|
1047
|
+
|
1208
1048
|
setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
|
1209
1049
|
setAttribute(knob1, ariaValueNow, `${lightness}`);
|
1050
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1210
1051
|
setAttribute(knob2, ariaValueText, `${hue}°`);
|
1211
1052
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
1212
1053
|
}
|
@@ -1234,10 +1075,12 @@ export default class ColorPicker {
|
|
1234
1075
|
/** Updates the control knobs actual positions. */
|
1235
1076
|
updateControls() {
|
1236
1077
|
const { controlKnobs, controlPositions } = this;
|
1237
|
-
|
1078
|
+
let {
|
1238
1079
|
c1x, c1y, c2y, c3y,
|
1239
1080
|
} = controlPositions;
|
1240
1081
|
const [control1, control2, control3] = controlKnobs;
|
1082
|
+
// round control positions
|
1083
|
+
[c1x, c1y, c2y, c3y] = [c1x, c1y, c2y, c3y].map(roundPart);
|
1241
1084
|
|
1242
1085
|
setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
|
1243
1086
|
setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
|
@@ -1280,7 +1123,8 @@ export default class ColorPicker {
|
|
1280
1123
|
i3.value = `${blackness}`;
|
1281
1124
|
i4.value = `${alpha}`;
|
1282
1125
|
} else if (format === 'rgb') {
|
1283
|
-
|
1126
|
+
let { r, g, b } = self.rgb;
|
1127
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
1284
1128
|
|
1285
1129
|
newColor = self.color.toRgbString();
|
1286
1130
|
i1.value = `${r}`;
|
@@ -1298,37 +1142,13 @@ export default class ColorPicker {
|
|
1298
1142
|
}
|
1299
1143
|
}
|
1300
1144
|
|
1301
|
-
/**
|
1302
|
-
* The `Space` & `Enter` keys specific event listener.
|
1303
|
-
* Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
|
1304
|
-
* @param {KeyboardEvent} e
|
1305
|
-
* @this {ColorPicker}
|
1306
|
-
*/
|
1307
|
-
keyToggle(e) {
|
1308
|
-
const self = this;
|
1309
|
-
const { menuToggle } = self;
|
1310
|
-
const { activeElement } = getDocument(menuToggle);
|
1311
|
-
const { code } = e;
|
1312
|
-
|
1313
|
-
if ([keyEnter, keySpace].includes(code)) {
|
1314
|
-
if ((menuToggle && activeElement === menuToggle) || !activeElement) {
|
1315
|
-
e.preventDefault();
|
1316
|
-
if (!activeElement) {
|
1317
|
-
self.togglePicker(e);
|
1318
|
-
} else {
|
1319
|
-
self.toggleMenu();
|
1320
|
-
}
|
1321
|
-
}
|
1322
|
-
}
|
1323
|
-
}
|
1324
|
-
|
1325
1145
|
/**
|
1326
1146
|
* Toggle the `ColorPicker` dropdown visibility.
|
1327
|
-
* @param {Event} e
|
1147
|
+
* @param {Event=} e
|
1328
1148
|
* @this {ColorPicker}
|
1329
1149
|
*/
|
1330
1150
|
togglePicker(e) {
|
1331
|
-
e.preventDefault();
|
1151
|
+
if (e) e.preventDefault();
|
1332
1152
|
const self = this;
|
1333
1153
|
const { colorPicker } = self;
|
1334
1154
|
|
@@ -1349,8 +1169,13 @@ export default class ColorPicker {
|
|
1349
1169
|
}
|
1350
1170
|
}
|
1351
1171
|
|
1352
|
-
/**
|
1353
|
-
|
1172
|
+
/**
|
1173
|
+
* Toggles the visibility of the `ColorPicker` presets menu.
|
1174
|
+
* @param {Event=} e
|
1175
|
+
* @this {ColorPicker}
|
1176
|
+
*/
|
1177
|
+
toggleMenu(e) {
|
1178
|
+
if (e) e.preventDefault();
|
1354
1179
|
const self = this;
|
1355
1180
|
const { colorMenu } = self;
|
1356
1181
|
|
@@ -1376,6 +1201,10 @@ export default class ColorPicker {
|
|
1376
1201
|
const relatedBtn = openPicker ? pickerToggle : menuToggle;
|
1377
1202
|
const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
|
1378
1203
|
|
1204
|
+
// if (!self.isValid) {
|
1205
|
+
self.value = self.color.toString(true);
|
1206
|
+
// }
|
1207
|
+
|
1379
1208
|
if (openDropdown) {
|
1380
1209
|
removeClass(openDropdown, 'show');
|
1381
1210
|
setAttribute(relatedBtn, ariaExpanded, 'false');
|
@@ -1389,9 +1218,6 @@ export default class ColorPicker {
|
|
1389
1218
|
}, animationDuration);
|
1390
1219
|
}
|
1391
1220
|
|
1392
|
-
if (!self.isValid) {
|
1393
|
-
self.value = self.color.toString();
|
1394
|
-
}
|
1395
1221
|
if (!focusPrevented) {
|
1396
1222
|
focus(pickerToggle);
|
1397
1223
|
}
|