@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.
@@ -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 Color from './color';
74
- import ColorPalette from './color-palette';
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
- export const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
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
- // @ts-ignore -- this is `Window`
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 tempLabels = ObjectAssign({}, colorPickerLabels);
390
- const jsonLabels = componentLabels && isValidJSON(componentLabels)
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(tempLabels, jsonLabels);
276
+ self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
395
277
 
396
278
  /** @type {Color} */
397
- self.color = new Color('white', format);
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
- initCallback(self);
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
- format, controlPositions, visuals,
460
+ controlPositions, visuals,
532
461
  } = self;
533
462
  const [v1, v2, v3] = visuals;
534
- const { offsetWidth, offsetHeight } = v1;
535
- const hue = format === 'hsl'
536
- ? controlPositions.c1x / offsetWidth
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
- if (format !== 'hsl') {
545
- const fill = new Color({
546
- h: hue, s: 1, l: 0.5, a: alpha,
547
- }).toRgbString();
548
- const hueGradient = `linear-gradient(
549
- rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
550
- rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
551
- rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
552
- rgb(255,0,0) 100%)`;
553
- setElementStyle(v1, {
554
- background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
555
- linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
556
- ${whiteGrad}`,
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 ((isMobile && self.dragElement)
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 = hasClass(target, 'visual-control')
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 - window.pageXOffset - visualRect.left;
752
- const offsetY = Y - window.pageYOffset - visualRect.top;
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 X = type === 'touchmove' ? touches[0].pageX : pageX;
813
- const Y = type === 'touchmove' ? touches[0].pageY : pageY;
814
- const offsetX = X - window.pageXOffset - controlRect.left;
815
- const offsetY = Y - window.pageYOffset - controlRect.top;
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 ? +1 : -1;
746
+ self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
853
747
  } else if ([keyArrowUp, keyArrowDown].includes(code)) {
854
- self.controlPositions.c1y += code === keyArrowDown ? +1 : -1;
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) ? +1 : -1;
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) ? +1 : -1;
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 = 'white';
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 = format === 'hsl'
956
- ? offsetX / offsetWidth
957
- : controlPositions.c2y / offsetHeight;
853
+ const hue = controlPositions.c2y / offsetHeight;
958
854
 
959
- const saturation = format === 'hsl'
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(colorObject);
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
- format, controlPositions, visuals,
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 = format === 'hsl'
1014
- ? controlPositions.c1x / offsetWidth
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(colorObject);
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
- format, visuals, color, hsl, hsv,
998
+ visuals, color, hsv,
1120
999
  } = self;
1121
1000
  const { offsetHeight, offsetWidth } = visuals[0];
1122
1001
  const alpha = color.a;
1123
- const hue = hsl.h;
1002
+ const hue = hsv.h;
1124
1003
 
1125
- const saturation = format !== 'hsl' ? hsv.s : hsl.s;
1126
- const lightness = format !== 'hsl' ? hsv.v : hsl.l;
1004
+ const saturation = hsv.s;
1005
+ const lightness = hsv.v;
1127
1006
 
1128
- self.controlPositions.c1x = format !== 'hsl' ? saturation * offsetWidth : hue * offsetWidth;
1007
+ self.controlPositions.c1x = saturation * offsetWidth;
1129
1008
  self.controlPositions.c1y = (1 - lightness) * offsetHeight;
1130
- self.controlPositions.c2y = format !== 'hsl' ? hue * offsetHeight : (1 - saturation) * offsetHeight;
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, colorLabels, color, parent,
1139
- hsl, hsv, hex, format, controlKnobs,
1017
+ componentLabels, color, parent,
1018
+ hsv, hex, format, controlKnobs,
1140
1019
  } = self;
1141
1020
  const {
1142
1021
  appearanceLabel, hexLabel, valueLabel,
1143
1022
  } = componentLabels;
1144
- const { r, g, b } = color.toRgb();
1023
+ let { r, g, b } = color.toRgb();
1145
1024
  const [knob1, knob2, knob3] = controlKnobs;
1146
- const hue = roundPart(hsl.h * 360);
1025
+ const hue = roundPart(hsv.h * 360);
1147
1026
  const alpha = color.a;
1148
- const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
1149
- const saturation = roundPart(saturationSource * 100);
1150
- const lightness = roundPart(hsl.l * 100);
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 === 'hsl') {
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
- setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
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
- const {
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
- const { r, g, b } = self.rgb;
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
- /** Toggles the visibility of the `ColorPicker` presets menu. */
1353
- toggleMenu() {
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
  }