@bornova/colorpicker 1.0.1 → 1.1.0
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/dist/browser/colorpicker.js +232 -233
- package/dist/browser/colorpicker.min.js +2 -2
- package/dist/browser/colorpicker.min.js.map +1 -1
- package/dist/colorpicker.css +35 -21
- package/dist/esm/colorpicker.js +234 -233
- package/dist/types/colors.d.ts +42 -42
- package/dist/types/dom.d.ts +3 -3
- package/dist/types/index.d.ts +10 -9
- package/dist/types/picker.d.ts +11 -11
- package/dist/types/settings.d.ts +68 -71
- package/package.json +15 -10
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @bornova/colorpicker v1.0
|
|
1
|
+
/* @bornova/colorpicker v1.1.0 */
|
|
2
2
|
var ColorPicker = function() {
|
|
3
3
|
"use strict";
|
|
4
4
|
const settings = {
|
|
@@ -41,7 +41,12 @@ var ColorPicker = function() {
|
|
|
41
41
|
function getCtx() {
|
|
42
42
|
if (!ctx && typeof document !== "undefined") {
|
|
43
43
|
try {
|
|
44
|
-
|
|
44
|
+
const canvas = document.createElement("canvas");
|
|
45
|
+
canvas.width = 1;
|
|
46
|
+
canvas.height = 1;
|
|
47
|
+
ctx = canvas.getContext("2d", {
|
|
48
|
+
willReadFrequently: true
|
|
49
|
+
});
|
|
45
50
|
} catch {}
|
|
46
51
|
}
|
|
47
52
|
return ctx;
|
|
@@ -100,82 +105,71 @@ var ColorPicker = function() {
|
|
|
100
105
|
hue = 4 + (red - green) / chroma;
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
|
-
hue = Math.
|
|
108
|
+
hue = Math.round(hue * 60);
|
|
104
109
|
return {
|
|
105
|
-
h: hue
|
|
110
|
+
h: (hue + 360) % 360,
|
|
106
111
|
s: Math.round(saturation * 100),
|
|
107
112
|
v: Math.round(value * 100),
|
|
108
113
|
a: rgba.a
|
|
109
114
|
};
|
|
110
115
|
}
|
|
111
116
|
function strToRGBA(str) {
|
|
117
|
+
if (typeof str !== "string" || !str.trim()) {
|
|
118
|
+
return {
|
|
119
|
+
r: 0,
|
|
120
|
+
g: 0,
|
|
121
|
+
b: 0,
|
|
122
|
+
a: 1
|
|
123
|
+
};
|
|
124
|
+
}
|
|
112
125
|
const regex = /^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i;
|
|
113
126
|
const canvasCtx = getCtx();
|
|
114
|
-
let
|
|
127
|
+
let normalized = str.trim();
|
|
115
128
|
if (canvasCtx) {
|
|
116
129
|
canvasCtx.fillStyle = "#000";
|
|
117
130
|
canvasCtx.fillStyle = str;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
};
|
|
131
|
+
normalized = canvasCtx.fillStyle;
|
|
132
|
+
}
|
|
133
|
+
const match = regex.exec(normalized);
|
|
134
|
+
if (match) {
|
|
135
|
+
return {
|
|
136
|
+
r: Math.min(255, Math.max(0, Number(match[3]))),
|
|
137
|
+
g: Math.min(255, Math.max(0, Number(match[4]))),
|
|
138
|
+
b: Math.min(255, Math.max(0, Number(match[5]))),
|
|
139
|
+
a: match[6] === "" ? 1 : Math.min(1, Math.max(0, Number(Number(match[6]).toFixed(2))))
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (/^#?(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(normalized)) {
|
|
143
|
+
let hex = normalized.replace("#", "");
|
|
144
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
145
|
+
hex = hex.split("").map(c => c + c).join("");
|
|
134
146
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
return {
|
|
148
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
149
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
150
|
+
b: parseInt(hex.slice(4, 6), 16),
|
|
151
|
+
a: hex.length === 8 ? Number((parseInt(hex.slice(6, 8), 16) / 255).toFixed(2)) : 1
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (canvasCtx) {
|
|
155
|
+
try {
|
|
156
|
+
canvasCtx.clearRect(0, 0, 1, 1);
|
|
157
|
+
canvasCtx.fillRect(0, 0, 1, 1);
|
|
158
|
+
const [r, g, b, a] = canvasCtx.getImageData(0, 0, 1, 1).data;
|
|
159
|
+
return {
|
|
160
|
+
r: r,
|
|
161
|
+
g: g,
|
|
162
|
+
b: b,
|
|
163
|
+
a: Number((a / 255).toFixed(2))
|
|
143
164
|
};
|
|
144
|
-
}
|
|
145
|
-
const hex = str.replace("#", "");
|
|
146
|
-
if (hex.length === 3 || hex.length === 4) {
|
|
147
|
-
const r = parseInt(hex[0] + hex[0], 16);
|
|
148
|
-
const g = parseInt(hex[1] + hex[1], 16);
|
|
149
|
-
const b = parseInt(hex[2] + hex[2], 16);
|
|
150
|
-
const a = hex.length === 4 ? Number((parseInt(hex[3] + hex[3], 16) / 255).toFixed(2)) : 1;
|
|
151
|
-
rgba = {
|
|
152
|
-
r: r,
|
|
153
|
-
g: g,
|
|
154
|
-
b: b,
|
|
155
|
-
a: a
|
|
156
|
-
};
|
|
157
|
-
} else if (hex.length === 6 || hex.length === 8) {
|
|
158
|
-
const r = parseInt(hex.slice(0, 2), 16);
|
|
159
|
-
const g = parseInt(hex.slice(2, 4), 16);
|
|
160
|
-
const b = parseInt(hex.slice(4, 6), 16);
|
|
161
|
-
const a = hex.length === 8 ? Number((parseInt(hex.slice(6, 8), 16) / 255).toFixed(2)) : 1;
|
|
162
|
-
rgba = {
|
|
163
|
-
r: r,
|
|
164
|
-
g: g,
|
|
165
|
-
b: b,
|
|
166
|
-
a: a
|
|
167
|
-
};
|
|
168
|
-
} else {
|
|
169
|
-
rgba = {
|
|
170
|
-
r: 0,
|
|
171
|
-
g: 0,
|
|
172
|
-
b: 0,
|
|
173
|
-
a: 1
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
165
|
+
} catch {}
|
|
177
166
|
}
|
|
178
|
-
return
|
|
167
|
+
return {
|
|
168
|
+
r: 0,
|
|
169
|
+
g: 0,
|
|
170
|
+
b: 0,
|
|
171
|
+
a: 1
|
|
172
|
+
};
|
|
179
173
|
}
|
|
180
174
|
function RGBAToHex(rgba) {
|
|
181
175
|
const R = rgba.r.toString(16).padStart(2, "0");
|
|
@@ -199,7 +193,7 @@ var ColorPicker = function() {
|
|
|
199
193
|
function addListener(context, type, selector, fn) {
|
|
200
194
|
if (typeof selector === "string") {
|
|
201
195
|
context.addEventListener(type, event => {
|
|
202
|
-
if (event.target.matches(selector)) {
|
|
196
|
+
if (event.target instanceof Element && event.target.matches(selector)) {
|
|
203
197
|
fn.call(event.target, event);
|
|
204
198
|
}
|
|
205
199
|
});
|
|
@@ -225,11 +219,12 @@ var ColorPicker = function() {
|
|
|
225
219
|
v: 0,
|
|
226
220
|
a: 1
|
|
227
221
|
};
|
|
228
|
-
let container, picker, colorArea, colorMarker, colorPreview, colorValue, clearButton, closeButton, hueSlider, hueMarker, alphaSlider, alphaMarker, currentEl, currentFormat, oldColor,
|
|
222
|
+
let container, picker, colorArea, colorMarker, colorPreview, colorValue, clearButton, closeButton, hueSlider, hueMarker, alphaSlider, alphaMarker, currentEl, currentFormat, oldColor, initialFormatColor, colorAreaDims = {};
|
|
229
223
|
const instances = {};
|
|
230
224
|
let currentInstanceId = "";
|
|
231
225
|
let defaultInstance = {};
|
|
232
|
-
|
|
226
|
+
const boundSelectors = new Set;
|
|
227
|
+
const boundElements = new WeakSet;
|
|
233
228
|
const MARKER_MOVEMENTS = {
|
|
234
229
|
ArrowUp: [ 0, -1 ],
|
|
235
230
|
ArrowDown: [ 0, 1 ],
|
|
@@ -237,28 +232,28 @@ var ColorPicker = function() {
|
|
|
237
232
|
ArrowRight: [ 1, 0 ]
|
|
238
233
|
};
|
|
239
234
|
function configure(options) {
|
|
240
|
-
if (typeof options !== "object") {
|
|
235
|
+
if (!options || typeof options !== "object") {
|
|
241
236
|
return;
|
|
242
237
|
}
|
|
243
238
|
for (const key of Object.keys(options)) {
|
|
244
239
|
switch (key) {
|
|
245
240
|
case "el":
|
|
246
241
|
bindFields(options.el);
|
|
247
|
-
if (options.wrap !== false) {
|
|
242
|
+
if (options.wrap !== false && (options.wrap || settings.wrap)) {
|
|
248
243
|
wrapFields(options.el);
|
|
249
244
|
}
|
|
250
245
|
break;
|
|
251
246
|
|
|
252
247
|
case "parent":
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
container = undefined;
|
|
248
|
+
{
|
|
249
|
+
const newContainer = options.parent instanceof HTMLElement ? options.parent : typeof options.parent === "string" ? document.querySelector(options.parent) : null;
|
|
250
|
+
if (newContainer) {
|
|
251
|
+
newContainer.appendChild(picker);
|
|
252
|
+
settings.parent = options.parent;
|
|
253
|
+
container = newContainer === document.body ? undefined : newContainer;
|
|
259
254
|
}
|
|
255
|
+
break;
|
|
260
256
|
}
|
|
261
|
-
break;
|
|
262
257
|
|
|
263
258
|
case "themeMode":
|
|
264
259
|
settings.themeMode = options.themeMode;
|
|
@@ -267,14 +262,22 @@ var ColorPicker = function() {
|
|
|
267
262
|
}
|
|
268
263
|
|
|
269
264
|
case "theme":
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
265
|
+
{
|
|
266
|
+
if (options.theme) {
|
|
267
|
+
settings.theme = options.theme;
|
|
268
|
+
}
|
|
269
|
+
const isOpen = picker.classList.contains("clr-open");
|
|
270
|
+
const isLeft = picker.classList.contains("clr-left");
|
|
271
|
+
const isTop = picker.classList.contains("clr-top");
|
|
272
|
+
picker.className = `clr-picker clr-${settings.theme} clr-${settings.themeMode}`;
|
|
273
|
+
picker.classList.toggle("clr-open", isOpen);
|
|
274
|
+
picker.classList.toggle("clr-left", isLeft);
|
|
275
|
+
picker.classList.toggle("clr-top", isTop);
|
|
276
|
+
if (settings.inline) {
|
|
277
|
+
updatePickerPosition();
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
276
280
|
}
|
|
277
|
-
break;
|
|
278
281
|
|
|
279
282
|
case "rtl":
|
|
280
283
|
settings.rtl = !!options.rtl;
|
|
@@ -291,8 +294,9 @@ var ColorPicker = function() {
|
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
case "wrap":
|
|
294
|
-
|
|
295
|
-
|
|
297
|
+
settings.wrap = !!options.wrap;
|
|
298
|
+
if (!("el" in options) && options.wrap) {
|
|
299
|
+
wrapFields(settings.el);
|
|
296
300
|
}
|
|
297
301
|
break;
|
|
298
302
|
|
|
@@ -338,29 +342,16 @@ var ColorPicker = function() {
|
|
|
338
342
|
case "inline":
|
|
339
343
|
settings.inline = !!options.inline;
|
|
340
344
|
picker.setAttribute("data-inline", settings.inline);
|
|
341
|
-
if (settings.inline) {
|
|
342
|
-
const defaultColor = options.defaultColor || settings.defaultColor;
|
|
343
|
-
currentFormat = getColorFormatFromStr(defaultColor);
|
|
344
|
-
updatePickerPosition();
|
|
345
|
-
setColorFromStr(defaultColor);
|
|
346
|
-
}
|
|
347
345
|
break;
|
|
348
346
|
|
|
349
347
|
case "clearButton":
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
clearButton.innerHTML = settings.clearLabel;
|
|
354
|
-
}
|
|
355
|
-
const show = typeof options.clearButton === "object" ? options.clearButton.show : options.clearButton;
|
|
356
|
-
settings.clearButton = !!show;
|
|
357
|
-
clearButton.style.display = settings.clearButton ? "block" : "none";
|
|
358
|
-
break;
|
|
359
|
-
}
|
|
348
|
+
settings.clearButton = !!options.clearButton;
|
|
349
|
+
clearButton.style.display = settings.clearButton ? "block" : "none";
|
|
350
|
+
break;
|
|
360
351
|
|
|
361
352
|
case "clearLabel":
|
|
362
353
|
settings.clearLabel = options.clearLabel;
|
|
363
|
-
clearButton.
|
|
354
|
+
clearButton.textContent = settings.clearLabel;
|
|
364
355
|
break;
|
|
365
356
|
|
|
366
357
|
case "closeButton":
|
|
@@ -374,14 +365,14 @@ var ColorPicker = function() {
|
|
|
374
365
|
|
|
375
366
|
case "closeLabel":
|
|
376
367
|
settings.closeLabel = options.closeLabel;
|
|
377
|
-
closeButton.
|
|
368
|
+
closeButton.textContent = settings.closeLabel;
|
|
378
369
|
break;
|
|
379
370
|
|
|
380
371
|
case "a11y":
|
|
381
372
|
{
|
|
382
373
|
const labels = options.a11y;
|
|
383
374
|
let update = false;
|
|
384
|
-
if (typeof labels === "object") {
|
|
375
|
+
if (labels && typeof labels === "object") {
|
|
385
376
|
for (const label of Object.keys(labels)) {
|
|
386
377
|
if (labels[label] && settings.a11y[label]) {
|
|
387
378
|
settings.a11y[label] = labels[label];
|
|
@@ -392,14 +383,19 @@ var ColorPicker = function() {
|
|
|
392
383
|
if (update) {
|
|
393
384
|
const openLabel = getEl("clr-open-label");
|
|
394
385
|
const swatchLabel = getEl("clr-swatch-label");
|
|
395
|
-
|
|
396
|
-
|
|
386
|
+
const formatLegend = picker.querySelector(".clr-format legend");
|
|
387
|
+
openLabel.textContent = settings.a11y.open;
|
|
388
|
+
swatchLabel.textContent = settings.a11y.swatch;
|
|
389
|
+
if (formatLegend) {
|
|
390
|
+
formatLegend.textContent = settings.a11y.format;
|
|
391
|
+
}
|
|
397
392
|
closeButton.setAttribute("aria-label", settings.a11y.close);
|
|
398
393
|
clearButton.setAttribute("aria-label", settings.a11y.clear);
|
|
399
394
|
hueSlider.setAttribute("aria-label", settings.a11y.hueSlider);
|
|
400
395
|
alphaSlider.setAttribute("aria-label", settings.a11y.alphaSlider);
|
|
401
396
|
colorValue.setAttribute("aria-label", settings.a11y.input);
|
|
402
397
|
colorArea.setAttribute("aria-label", settings.a11y.instruction);
|
|
398
|
+
updateMarkerA11yLabel(currentColor.s, currentColor.v);
|
|
403
399
|
}
|
|
404
400
|
break;
|
|
405
401
|
}
|
|
@@ -408,43 +404,49 @@ var ColorPicker = function() {
|
|
|
408
404
|
settings[key] = options[key];
|
|
409
405
|
}
|
|
410
406
|
}
|
|
407
|
+
if (settings.formatToggle) {
|
|
408
|
+
settings.format = "auto";
|
|
409
|
+
}
|
|
410
|
+
if (settings.inline && ("inline" in options || "defaultColor" in options || "format" in options || "formatToggle" in options || "alpha" in options || "forceAlpha" in options)) {
|
|
411
|
+
const defaultColor = options.defaultColor || settings.defaultColor;
|
|
412
|
+
currentFormat = getColorFormatFromStr(defaultColor);
|
|
413
|
+
updatePickerPosition();
|
|
414
|
+
setColorFromStr(defaultColor);
|
|
415
|
+
}
|
|
411
416
|
}
|
|
412
417
|
function setVirtualInstance(selector, options) {
|
|
413
|
-
if (typeof selector === "string" && typeof options === "object") {
|
|
418
|
+
if (typeof selector === "string" && options && typeof options === "object") {
|
|
414
419
|
instances[selector] = options;
|
|
415
|
-
hasInstance = true;
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
function removeVirtualInstance(selector) {
|
|
419
423
|
delete instances[selector];
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
if (selector === currentInstanceId) {
|
|
423
|
-
resetVirtualInstance();
|
|
424
|
-
}
|
|
424
|
+
if (selector === currentInstanceId) {
|
|
425
|
+
resetVirtualInstance();
|
|
425
426
|
}
|
|
426
427
|
}
|
|
427
428
|
function attachVirtualInstance(element) {
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
429
|
+
if (currentInstanceId) {
|
|
430
|
+
resetVirtualInstance();
|
|
431
|
+
}
|
|
432
|
+
const unsupportedOptions = [ "el", "wrap", "rtl", "inline", "defaultColor", "a11y" ];
|
|
433
|
+
for (const selector of Object.keys(instances)) {
|
|
434
|
+
const options = instances[selector];
|
|
435
|
+
if (element.matches(selector)) {
|
|
436
|
+
currentInstanceId = selector;
|
|
437
|
+
defaultInstance = {};
|
|
438
|
+
const clonedOptions = {
|
|
439
|
+
...options
|
|
440
|
+
};
|
|
441
|
+
unsupportedOptions.forEach(option => delete clonedOptions[option]);
|
|
442
|
+
for (const option of Object.keys(clonedOptions)) {
|
|
443
|
+
defaultInstance[option] = Array.isArray(settings[option]) ? [ ...settings[option] ] : settings[option];
|
|
444
|
+
if (option === "formatToggle" && !("format" in clonedOptions)) {
|
|
445
|
+
defaultInstance.format = settings.format;
|
|
444
446
|
}
|
|
445
|
-
configure(clonedOptions);
|
|
446
|
-
break;
|
|
447
447
|
}
|
|
448
|
+
configure(clonedOptions);
|
|
449
|
+
break;
|
|
448
450
|
}
|
|
449
451
|
}
|
|
450
452
|
}
|
|
@@ -456,13 +458,17 @@ var ColorPicker = function() {
|
|
|
456
458
|
}
|
|
457
459
|
}
|
|
458
460
|
function bindFields(selector) {
|
|
459
|
-
const fields = selector instanceof HTMLElement ? [ selector ] : selector;
|
|
461
|
+
const fields = selector instanceof HTMLElement ? [ selector ] : selector instanceof NodeList ? [ ...selector ] : selector;
|
|
460
462
|
if (Array.isArray(fields)) {
|
|
461
463
|
fields.forEach(field => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
+
if (field instanceof HTMLElement && !boundElements.has(field)) {
|
|
465
|
+
boundElements.add(field);
|
|
466
|
+
addListener(field, "click", openPicker);
|
|
467
|
+
addListener(field, "input", updateColorPreview);
|
|
468
|
+
}
|
|
464
469
|
});
|
|
465
|
-
} else {
|
|
470
|
+
} else if (typeof fields === "string" && !boundSelectors.has(fields)) {
|
|
471
|
+
boundSelectors.add(fields);
|
|
466
472
|
addListener(document, "click", fields, openPicker);
|
|
467
473
|
addListener(document, "input", fields, updateColorPreview);
|
|
468
474
|
}
|
|
@@ -471,6 +477,9 @@ var ColorPicker = function() {
|
|
|
471
477
|
if (settings.inline) {
|
|
472
478
|
return;
|
|
473
479
|
}
|
|
480
|
+
if (currentEl && currentEl !== event.target) {
|
|
481
|
+
closePicker();
|
|
482
|
+
}
|
|
474
483
|
attachVirtualInstance(event.target);
|
|
475
484
|
currentEl = event.target;
|
|
476
485
|
oldColor = currentEl.value;
|
|
@@ -478,6 +487,7 @@ var ColorPicker = function() {
|
|
|
478
487
|
picker.classList.add("clr-open");
|
|
479
488
|
updatePickerPosition();
|
|
480
489
|
setColorFromStr(oldColor);
|
|
490
|
+
initialFormatColor = colorValue.value;
|
|
481
491
|
if (settings.focusInput || settings.selectInput) {
|
|
482
492
|
colorValue.focus({
|
|
483
493
|
preventScroll: true
|
|
@@ -493,7 +503,7 @@ var ColorPicker = function() {
|
|
|
493
503
|
if (settings.selectInput) {
|
|
494
504
|
colorValue.select();
|
|
495
505
|
}
|
|
496
|
-
if (
|
|
506
|
+
if (settings.swatchesOnly) {
|
|
497
507
|
const firstFocusable = getFocusableElements().shift();
|
|
498
508
|
if (firstFocusable) {
|
|
499
509
|
firstFocusable.focus();
|
|
@@ -503,32 +513,40 @@ var ColorPicker = function() {
|
|
|
503
513
|
bubbles: false
|
|
504
514
|
}));
|
|
505
515
|
}
|
|
506
|
-
function
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
top: false
|
|
514
|
-
};
|
|
515
|
-
let parentStyle, parentMarginTop, parentBorderTop;
|
|
516
|
-
let offset = {
|
|
517
|
-
x: 0,
|
|
518
|
-
y: 0
|
|
516
|
+
function updateColorAreaDims() {
|
|
517
|
+
const rect = colorArea.getBoundingClientRect();
|
|
518
|
+
colorAreaDims = {
|
|
519
|
+
width: rect.width || colorArea.offsetWidth,
|
|
520
|
+
height: rect.height || colorArea.offsetHeight,
|
|
521
|
+
x: rect.x + window.scrollX,
|
|
522
|
+
y: rect.y + window.scrollY
|
|
519
523
|
};
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
offset = parent.getBoundingClientRect();
|
|
525
|
-
offset.y += parentBorderTop + scrollY;
|
|
524
|
+
}
|
|
525
|
+
function updatePickerPosition() {
|
|
526
|
+
if (!picker || !currentEl && !settings.inline) {
|
|
527
|
+
return;
|
|
526
528
|
}
|
|
527
529
|
if (!settings.inline) {
|
|
530
|
+
const parent = container;
|
|
531
|
+
const scrollX = window.scrollX;
|
|
532
|
+
const scrollY = window.scrollY;
|
|
533
|
+
const pickerWidth = picker.offsetWidth;
|
|
534
|
+
const pickerHeight = picker.offsetHeight;
|
|
535
|
+
const reposition = {
|
|
536
|
+
left: false,
|
|
537
|
+
top: false
|
|
538
|
+
};
|
|
528
539
|
const coords = currentEl.getBoundingClientRect();
|
|
529
|
-
let left = coords.x;
|
|
540
|
+
let left = scrollX + coords.x;
|
|
530
541
|
let top = scrollY + coords.y + coords.height + settings.margin;
|
|
531
542
|
if (parent) {
|
|
543
|
+
const parentStyle = window.getComputedStyle(parent);
|
|
544
|
+
const parentMarginTop = parseFloat(parentStyle.marginTop);
|
|
545
|
+
const parentBorderTop = parseFloat(parentStyle.borderTopWidth);
|
|
546
|
+
const parentBorderLeft = parseFloat(parentStyle.borderLeftWidth);
|
|
547
|
+
const offset = parent.getBoundingClientRect();
|
|
548
|
+
offset.x += parentBorderLeft + scrollX;
|
|
549
|
+
offset.y += parentBorderTop + scrollY;
|
|
532
550
|
left -= offset.x;
|
|
533
551
|
top -= offset.y;
|
|
534
552
|
if (left + pickerWidth > parent.clientWidth) {
|
|
@@ -541,9 +559,10 @@ var ColorPicker = function() {
|
|
|
541
559
|
reposition.top = true;
|
|
542
560
|
}
|
|
543
561
|
}
|
|
562
|
+
left += parent.scrollLeft;
|
|
544
563
|
top += parent.scrollTop;
|
|
545
564
|
} else {
|
|
546
|
-
if (left + pickerWidth > document.documentElement.clientWidth) {
|
|
565
|
+
if (left - scrollX + pickerWidth > document.documentElement.clientWidth) {
|
|
547
566
|
left += coords.width - pickerWidth;
|
|
548
567
|
reposition.left = true;
|
|
549
568
|
}
|
|
@@ -558,23 +577,16 @@ var ColorPicker = function() {
|
|
|
558
577
|
picker.classList.toggle("clr-top", reposition.top);
|
|
559
578
|
picker.style.left = `${left}px`;
|
|
560
579
|
picker.style.top = `${top}px`;
|
|
561
|
-
offset.x += picker.offsetLeft;
|
|
562
|
-
offset.y += picker.offsetTop;
|
|
563
580
|
}
|
|
564
|
-
|
|
565
|
-
width: colorArea.offsetWidth,
|
|
566
|
-
height: colorArea.offsetHeight,
|
|
567
|
-
x: colorArea.offsetLeft + offset.x,
|
|
568
|
-
y: colorArea.offsetTop + offset.y
|
|
569
|
-
};
|
|
581
|
+
updateColorAreaDims();
|
|
570
582
|
}
|
|
571
583
|
function wrapFields(selector) {
|
|
572
|
-
const fields = selector instanceof HTMLElement ? [ selector ] : Array.isArray(selector) ? selector : [ ...document.querySelectorAll(selector) ];
|
|
584
|
+
const fields = selector instanceof HTMLElement ? [ selector ] : Array.isArray(selector) || selector instanceof NodeList ? [ ...selector ] : typeof selector === "string" ? [ ...document.querySelectorAll(selector) ] : [];
|
|
573
585
|
fields.forEach(wrapColorField);
|
|
574
586
|
}
|
|
575
587
|
function wrapColorField(field) {
|
|
576
|
-
const parentNode = field
|
|
577
|
-
if (!parentNode.classList.contains("clr-field")) {
|
|
588
|
+
const parentNode = field?.parentNode;
|
|
589
|
+
if (parentNode && parentNode.classList && !parentNode.classList.contains("clr-field")) {
|
|
578
590
|
const wrapper = document.createElement("div");
|
|
579
591
|
let classes = "clr-field";
|
|
580
592
|
if (settings.rtl || field.classList.contains("clr-rtl")) {
|
|
@@ -589,7 +601,7 @@ var ColorPicker = function() {
|
|
|
589
601
|
}
|
|
590
602
|
function updateColorPreview(event) {
|
|
591
603
|
const parent = event.target.parentNode;
|
|
592
|
-
if (parent.classList.contains("clr-field")) {
|
|
604
|
+
if (parent && parent.classList && parent.classList.contains("clr-field")) {
|
|
593
605
|
parent.style.color = event.target.value;
|
|
594
606
|
}
|
|
595
607
|
}
|
|
@@ -606,13 +618,14 @@ var ColorPicker = function() {
|
|
|
606
618
|
}
|
|
607
619
|
} else {
|
|
608
620
|
const value = colorValue.value;
|
|
609
|
-
if (value
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
pickColor(colorValue.value);
|
|
621
|
+
if (value !== initialFormatColor) {
|
|
622
|
+
if (value !== "") {
|
|
623
|
+
setColorFromStr(value);
|
|
624
|
+
}
|
|
625
|
+
pickColor(value === "" ? "" : colorValue.value);
|
|
614
626
|
}
|
|
615
627
|
}
|
|
628
|
+
currentEl = undefined;
|
|
616
629
|
setTimeout(() => {
|
|
617
630
|
if (oldColor !== prevEl.value) {
|
|
618
631
|
prevEl.dispatchEvent(new Event("change", {
|
|
@@ -621,9 +634,7 @@ var ColorPicker = function() {
|
|
|
621
634
|
}
|
|
622
635
|
});
|
|
623
636
|
picker.classList.remove("clr-open");
|
|
624
|
-
|
|
625
|
-
resetVirtualInstance();
|
|
626
|
-
}
|
|
637
|
+
resetVirtualInstance();
|
|
627
638
|
prevEl.dispatchEvent(new Event("close", {
|
|
628
639
|
bubbles: false
|
|
629
640
|
}));
|
|
@@ -632,7 +643,6 @@ var ColorPicker = function() {
|
|
|
632
643
|
preventScroll: true
|
|
633
644
|
});
|
|
634
645
|
}
|
|
635
|
-
currentEl = undefined;
|
|
636
646
|
}
|
|
637
647
|
}
|
|
638
648
|
function setColorFromStr(str) {
|
|
@@ -649,11 +659,12 @@ var ColorPicker = function() {
|
|
|
649
659
|
alphaMarker.style.left = `${hsva.a * 100}%`;
|
|
650
660
|
}
|
|
651
661
|
function getColorFormatFromStr(str) {
|
|
652
|
-
const fmt = str.slice(0, 3).toLowerCase();
|
|
662
|
+
const fmt = str.trim().slice(0, 3).toLowerCase();
|
|
653
663
|
return fmt === "rgb" || fmt === "hsl" ? fmt : "hex";
|
|
654
664
|
}
|
|
655
665
|
function pickColor(color) {
|
|
656
666
|
color = color ?? colorValue.value;
|
|
667
|
+
initialFormatColor = colorValue.value;
|
|
657
668
|
if (currentEl) {
|
|
658
669
|
currentEl.value = color;
|
|
659
670
|
currentEl.dispatchEvent(new Event("input", {
|
|
@@ -686,13 +697,7 @@ var ColorPicker = function() {
|
|
|
686
697
|
colorMarker.setAttribute("aria-label", settings.a11y.marker.replace("{s}", Number(saturation.toFixed(1))).replace("{v}", Number(value.toFixed(1))));
|
|
687
698
|
}
|
|
688
699
|
function moveMarker(event) {
|
|
689
|
-
|
|
690
|
-
let x = (touch?.pageX ?? event.pageX) - colorAreaDims.x;
|
|
691
|
-
let y = (touch?.pageY ?? event.pageY) - colorAreaDims.y;
|
|
692
|
-
if (container) {
|
|
693
|
-
y += container.scrollTop;
|
|
694
|
-
}
|
|
695
|
-
setMarkerPosition(x, y);
|
|
700
|
+
setMarkerPosition(event.pageX - colorAreaDims.x, event.pageY - colorAreaDims.y);
|
|
696
701
|
event.preventDefault();
|
|
697
702
|
event.stopPropagation();
|
|
698
703
|
}
|
|
@@ -707,7 +712,9 @@ var ColorPicker = function() {
|
|
|
707
712
|
colorMarker.style.left = `${x}px`;
|
|
708
713
|
colorMarker.style.top = `${y}px`;
|
|
709
714
|
setColorAtPosition(x, y);
|
|
710
|
-
colorMarker.focus(
|
|
715
|
+
colorMarker.focus({
|
|
716
|
+
preventScroll: true
|
|
717
|
+
});
|
|
711
718
|
}
|
|
712
719
|
function updateColor(rgba = {}, hsva = {}) {
|
|
713
720
|
let format = settings.format;
|
|
@@ -718,12 +725,6 @@ var ColorPicker = function() {
|
|
|
718
725
|
alphaMarker.parentNode.style.color = opaqueHex;
|
|
719
726
|
alphaMarker.style.color = hex;
|
|
720
727
|
colorPreview.style.color = hex;
|
|
721
|
-
colorArea.style.display = "none";
|
|
722
|
-
colorArea.offsetHeight;
|
|
723
|
-
colorArea.style.display = "";
|
|
724
|
-
alphaMarker.nextElementSibling.style.display = "none";
|
|
725
|
-
alphaMarker.nextElementSibling.offsetHeight;
|
|
726
|
-
alphaMarker.nextElementSibling.style.display = "";
|
|
727
728
|
if (format === "mixed") {
|
|
728
729
|
format = currentColor.a === 1 ? "hex" : "rgb";
|
|
729
730
|
} else if (format === "auto") {
|
|
@@ -742,7 +743,10 @@ var ColorPicker = function() {
|
|
|
742
743
|
colorValue.value = HSLAToStr(HSVAtoHSLA(currentColor));
|
|
743
744
|
break;
|
|
744
745
|
}
|
|
745
|
-
|
|
746
|
+
const formatRadio = picker.querySelector(`.clr-format [value="${format}"]`);
|
|
747
|
+
if (formatRadio) {
|
|
748
|
+
formatRadio.checked = true;
|
|
749
|
+
}
|
|
746
750
|
}
|
|
747
751
|
function setHue() {
|
|
748
752
|
const hue = Number(hueSlider.value);
|
|
@@ -761,14 +765,15 @@ var ColorPicker = function() {
|
|
|
761
765
|
pickColor();
|
|
762
766
|
}
|
|
763
767
|
function getFocusableElements() {
|
|
764
|
-
return [ ...picker.querySelectorAll("input, button") ].filter(node => !!node.offsetWidth);
|
|
768
|
+
return [ ...picker.querySelectorAll("input, button, [tabindex]") ].filter(node => !!node.offsetWidth && (node.type !== "radio" || node.checked));
|
|
765
769
|
}
|
|
766
770
|
function init() {
|
|
767
771
|
container = undefined;
|
|
768
772
|
picker = document.createElement("div");
|
|
769
773
|
picker.setAttribute("id", "clr-picker");
|
|
774
|
+
picker.setAttribute("role", "dialog");
|
|
770
775
|
picker.className = "clr-picker";
|
|
771
|
-
picker.innerHTML = `\n <input id="clr-color-value" name="clr-color-value" class="clr-color" type="text" value="" spellcheck="false"
|
|
776
|
+
picker.innerHTML = `\n <input id="clr-color-value" name="clr-color-value" class="clr-color" type="text" value="" spellcheck="false">\n <div id="clr-color-area" class="clr-gradient" role="application">\n <div id="clr-color-marker" class="clr-marker" tabindex="0"></div>\n </div>\n <div class="clr-hue">\n <input id="clr-hue-slider" name="clr-hue-slider" type="range" min="0" max="360" step="1">\n <div id="clr-hue-marker"></div>\n </div>\n <div class="clr-alpha">\n <input id="clr-alpha-slider" name="clr-alpha-slider" type="range" min="0" max="100" step="1">\n <div id="clr-alpha-marker"></div>\n <span></span>\n </div>\n <div id="clr-format" class="clr-format">\n <fieldset class="clr-segmented">\n <legend></legend>\n <input id="clr-f1" type="radio" name="clr-format" value="hex">\n <label for="clr-f1">Hex</label>\n <input id="clr-f2" type="radio" name="clr-format" value="rgb">\n <label for="clr-f2">RGB</label>\n <input id="clr-f3" type="radio" name="clr-format" value="hsl">\n <label for="clr-f3">HSL</label>\n </fieldset>\n </div>\n <div id="clr-swatches" class="clr-swatches"></div>\n <button type="button" id="clr-clear" class="clr-clear"></button>\n <div id="clr-color-preview" class="clr-preview">\n <button type="button" id="clr-close" class="clr-close"></button>\n </div>\n <span id="clr-open-label" hidden></span>\n <span id="clr-swatch-label" hidden></span>\n `;
|
|
772
777
|
document.body.appendChild(picker);
|
|
773
778
|
colorArea = getEl("clr-color-area");
|
|
774
779
|
colorMarker = getEl("clr-color-marker");
|
|
@@ -780,36 +785,36 @@ var ColorPicker = function() {
|
|
|
780
785
|
hueMarker = getEl("clr-hue-marker");
|
|
781
786
|
alphaSlider = getEl("clr-alpha-slider");
|
|
782
787
|
alphaMarker = getEl("clr-alpha-marker");
|
|
788
|
+
colorValue.setAttribute("aria-label", settings.a11y.input);
|
|
789
|
+
colorArea.setAttribute("aria-label", settings.a11y.instruction);
|
|
790
|
+
hueSlider.setAttribute("aria-label", settings.a11y.hueSlider);
|
|
791
|
+
alphaSlider.setAttribute("aria-label", settings.a11y.alphaSlider);
|
|
792
|
+
clearButton.setAttribute("aria-label", settings.a11y.clear);
|
|
793
|
+
clearButton.textContent = settings.clearLabel;
|
|
794
|
+
closeButton.setAttribute("aria-label", settings.a11y.close);
|
|
795
|
+
closeButton.textContent = settings.closeLabel;
|
|
796
|
+
picker.querySelector(".clr-format legend").textContent = settings.a11y.format;
|
|
797
|
+
getEl("clr-open-label").textContent = settings.a11y.open;
|
|
798
|
+
getEl("clr-swatch-label").textContent = settings.a11y.swatch;
|
|
783
799
|
bindFields(settings.el);
|
|
784
800
|
wrapFields(settings.el);
|
|
785
|
-
addListener(picker, "
|
|
786
|
-
picker.classList.remove("clr-keyboard-nav");
|
|
801
|
+
addListener(picker, "pointerdown", event => {
|
|
787
802
|
event.stopPropagation();
|
|
788
803
|
});
|
|
789
|
-
addListener(colorArea, "
|
|
790
|
-
|
|
804
|
+
addListener(colorArea, "pointerdown", () => {
|
|
805
|
+
updateColorAreaDims();
|
|
806
|
+
addListener(document, "pointermove", moveMarker);
|
|
791
807
|
});
|
|
792
808
|
addListener(colorArea, "contextmenu", event => {
|
|
793
809
|
event.preventDefault();
|
|
794
810
|
});
|
|
795
|
-
addListener(colorArea, "touchstart", () => {
|
|
796
|
-
document.addEventListener("touchmove", moveMarker, {
|
|
797
|
-
passive: false
|
|
798
|
-
});
|
|
799
|
-
});
|
|
800
|
-
addListener(colorMarker, "mousedown", () => {
|
|
801
|
-
addListener(document, "mousemove", moveMarker);
|
|
802
|
-
});
|
|
803
|
-
addListener(colorMarker, "touchstart", () => {
|
|
804
|
-
document.addEventListener("touchmove", moveMarker, {
|
|
805
|
-
passive: false
|
|
806
|
-
});
|
|
807
|
-
});
|
|
808
811
|
addListener(colorValue, "change", () => {
|
|
809
812
|
const value = colorValue.value;
|
|
810
813
|
if (currentEl || settings.inline) {
|
|
811
|
-
|
|
812
|
-
|
|
814
|
+
if (value !== "") {
|
|
815
|
+
setColorFromStr(value);
|
|
816
|
+
}
|
|
817
|
+
pickColor(value === "" ? "" : colorValue.value);
|
|
813
818
|
}
|
|
814
819
|
});
|
|
815
820
|
addListener(clearButton, "click", () => {
|
|
@@ -832,33 +837,29 @@ var ColorPicker = function() {
|
|
|
832
837
|
closePicker();
|
|
833
838
|
}
|
|
834
839
|
});
|
|
835
|
-
addListener(document, "
|
|
836
|
-
document.removeEventListener("
|
|
840
|
+
addListener(document, "pointerup", () => {
|
|
841
|
+
document.removeEventListener("pointermove", moveMarker);
|
|
837
842
|
});
|
|
838
|
-
addListener(document, "
|
|
839
|
-
document.removeEventListener("touchmove", moveMarker);
|
|
840
|
-
});
|
|
841
|
-
addListener(document, "mousedown", () => {
|
|
842
|
-
keyboardNav = false;
|
|
843
|
-
picker.classList.remove("clr-keyboard-nav");
|
|
843
|
+
addListener(document, "pointerdown", () => {
|
|
844
844
|
closePicker();
|
|
845
845
|
});
|
|
846
846
|
addListener(document, "keydown", event => {
|
|
847
847
|
const key = event.key;
|
|
848
848
|
const target = event.target;
|
|
849
849
|
const shiftKey = event.shiftKey;
|
|
850
|
-
const navKeys = [ "Tab", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight" ];
|
|
851
850
|
if (key === "Escape") {
|
|
852
|
-
|
|
851
|
+
if (currentEl) {
|
|
852
|
+
closePicker(true);
|
|
853
|
+
}
|
|
853
854
|
return;
|
|
854
855
|
} else if (key === "Enter" && target.tagName !== "BUTTON") {
|
|
855
|
-
|
|
856
|
+
if (currentEl) {
|
|
857
|
+
event.preventDefault();
|
|
858
|
+
closePicker();
|
|
859
|
+
}
|
|
856
860
|
return;
|
|
857
|
-
} else if (navKeys.includes(key)) {
|
|
858
|
-
keyboardNav = true;
|
|
859
|
-
picker.classList.add("clr-keyboard-nav");
|
|
860
861
|
}
|
|
861
|
-
if (key === "Tab" && target.matches(".clr-picker *")) {
|
|
862
|
+
if (key === "Tab" && target instanceof Element && target.matches(".clr-picker *")) {
|
|
862
863
|
const focusables = getFocusableElements();
|
|
863
864
|
if (focusables.length > 1) {
|
|
864
865
|
const firstFocusable = focusables[0];
|
|
@@ -876,10 +877,8 @@ var ColorPicker = function() {
|
|
|
876
877
|
}
|
|
877
878
|
});
|
|
878
879
|
addListener(document, "click", ".clr-field button", event => {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
882
|
-
event.target.nextElementSibling.dispatchEvent(new Event("click", {
|
|
880
|
+
resetVirtualInstance();
|
|
881
|
+
event.target.nextElementSibling?.dispatchEvent(new Event("click", {
|
|
883
882
|
bubbles: true
|
|
884
883
|
}));
|
|
885
884
|
});
|