@thednp/color-picker 0.0.1 → 0.0.2-alpha3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,19 +728,19 @@ export default class ColorPicker {
839
728
  if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
840
729
  e.preventDefault();
841
730
 
842
- const { format, controlKnobs, visuals } = self;
731
+ const { controlKnobs, visuals } = self;
843
732
  const { offsetWidth, offsetHeight } = visuals[0];
844
733
  const [c1, c2, c3] = controlKnobs;
845
734
  const { activeElement } = getDocument(c1);
846
735
  const currentKnob = controlKnobs.find((x) => x === activeElement);
847
- const yRatio = offsetHeight / (format === 'hsl' ? 100 : 360);
736
+ const yRatio = offsetHeight / 360;
848
737
 
849
738
  if (currentKnob) {
850
739
  let offsetX = 0;
851
740
  let offsetY = 0;
852
741
 
853
742
  if (target === c1) {
854
- const xRatio = offsetWidth / (format === 'hsl' ? 360 : 100);
743
+ const xRatio = offsetWidth / 100;
855
744
 
856
745
  if ([keyArrowLeft, keyArrowRight].includes(code)) {
857
746
  self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
@@ -901,7 +790,7 @@ export default class ColorPicker {
901
790
  if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
902
791
  if (activeElement === input) {
903
792
  if (isNonColorValue) {
904
- colorSource = 'white';
793
+ colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
905
794
  } else {
906
795
  colorSource = currentValue;
907
796
  }
@@ -952,9 +841,7 @@ export default class ColorPicker {
952
841
  changeControl1(X, Y) {
953
842
  const self = this;
954
843
  let [offsetX, offsetY] = [0, 0];
955
- const {
956
- format, controlPositions, visuals,
957
- } = self;
844
+ const { controlPositions, visuals } = self;
958
845
  const { offsetHeight, offsetWidth } = visuals[0];
959
846
 
960
847
  if (X > offsetWidth) offsetX = offsetWidth;
@@ -963,29 +850,19 @@ export default class ColorPicker {
963
850
  if (Y > offsetHeight) offsetY = offsetHeight;
964
851
  else if (Y >= 0) offsetY = Y;
965
852
 
966
- const hue = format === 'hsl'
967
- ? offsetX / offsetWidth
968
- : controlPositions.c2y / offsetHeight;
853
+ const hue = controlPositions.c2y / offsetHeight;
969
854
 
970
- const saturation = format === 'hsl'
971
- ? 1 - controlPositions.c2y / offsetHeight
972
- : offsetX / offsetWidth;
855
+ const saturation = offsetX / offsetWidth;
973
856
 
974
857
  const lightness = 1 - offsetY / offsetHeight;
975
858
  const alpha = 1 - controlPositions.c3y / offsetHeight;
976
859
 
977
- const colorObject = format === 'hsl'
978
- ? {
979
- h: hue, s: saturation, l: lightness, a: alpha,
980
- }
981
- : {
982
- h: hue, s: saturation, v: lightness, a: alpha,
983
- };
984
-
985
860
  // new color
986
861
  const {
987
862
  r, g, b, a,
988
- } = new Color(colorObject);
863
+ } = new Color({
864
+ h: hue, s: saturation, v: lightness, a: alpha,
865
+ });
989
866
 
990
867
  ObjectAssign(self.color, {
991
868
  r, g, b, a,
@@ -1012,7 +889,7 @@ export default class ColorPicker {
1012
889
  changeControl2(Y) {
1013
890
  const self = this;
1014
891
  const {
1015
- format, controlPositions, visuals,
892
+ controlPositions, visuals,
1016
893
  } = self;
1017
894
  const { offsetHeight, offsetWidth } = visuals[0];
1018
895
 
@@ -1021,26 +898,17 @@ export default class ColorPicker {
1021
898
  if (Y > offsetHeight) offsetY = offsetHeight;
1022
899
  else if (Y >= 0) offsetY = Y;
1023
900
 
1024
- const hue = format === 'hsl'
1025
- ? controlPositions.c1x / offsetWidth
1026
- : offsetY / offsetHeight;
1027
- const saturation = format === 'hsl'
1028
- ? 1 - offsetY / offsetHeight
1029
- : controlPositions.c1x / offsetWidth;
901
+ const hue = offsetY / offsetHeight;
902
+ const saturation = controlPositions.c1x / offsetWidth;
1030
903
  const lightness = 1 - controlPositions.c1y / offsetHeight;
1031
904
  const alpha = 1 - controlPositions.c3y / offsetHeight;
1032
- const colorObject = format === 'hsl'
1033
- ? {
1034
- h: hue, s: saturation, l: lightness, a: alpha,
1035
- }
1036
- : {
1037
- h: hue, s: saturation, v: lightness, a: alpha,
1038
- };
1039
905
 
1040
906
  // new color
1041
907
  const {
1042
908
  r, g, b, a,
1043
- } = new Color(colorObject);
909
+ } = new Color({
910
+ h: hue, s: saturation, v: lightness, a: alpha,
911
+ });
1044
912
 
1045
913
  ObjectAssign(self.color, {
1046
914
  r, g, b, a,
@@ -1127,18 +995,18 @@ export default class ColorPicker {
1127
995
  setControlPositions() {
1128
996
  const self = this;
1129
997
  const {
1130
- format, visuals, color, hsl, hsv,
998
+ visuals, color, hsv,
1131
999
  } = self;
1132
1000
  const { offsetHeight, offsetWidth } = visuals[0];
1133
1001
  const alpha = color.a;
1134
- const hue = hsl.h;
1002
+ const hue = hsv.h;
1135
1003
 
1136
- const saturation = format !== 'hsl' ? hsv.s : hsl.s;
1137
- const lightness = format !== 'hsl' ? hsv.v : hsl.l;
1004
+ const saturation = hsv.s;
1005
+ const lightness = hsv.v;
1138
1006
 
1139
- self.controlPositions.c1x = format !== 'hsl' ? saturation * offsetWidth : hue * offsetWidth;
1007
+ self.controlPositions.c1x = saturation * offsetWidth;
1140
1008
  self.controlPositions.c1y = (1 - lightness) * offsetHeight;
1141
- self.controlPositions.c2y = format !== 'hsl' ? hue * offsetHeight : (1 - saturation) * offsetHeight;
1009
+ self.controlPositions.c2y = hue * offsetHeight;
1142
1010
  self.controlPositions.c3y = (1 - alpha) * offsetHeight;
1143
1011
  }
1144
1012
 
@@ -1146,78 +1014,40 @@ export default class ColorPicker {
1146
1014
  updateAppearance() {
1147
1015
  const self = this;
1148
1016
  const {
1149
- componentLabels, colorLabels, color, parent,
1150
- hsl, hsv, hex, format, controlKnobs,
1017
+ componentLabels, color, parent,
1018
+ hsv, hex, format, controlKnobs,
1151
1019
  } = self;
1152
1020
  const {
1153
1021
  appearanceLabel, hexLabel, valueLabel,
1154
1022
  } = componentLabels;
1155
- const { r, g, b } = color.toRgb();
1023
+ let { r, g, b } = color.toRgb();
1156
1024
  const [knob1, knob2, knob3] = controlKnobs;
1157
- const hue = roundPart(hsl.h * 360);
1025
+ const hue = roundPart(hsv.h * 360);
1158
1026
  const alpha = color.a;
1159
- const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
1160
- const saturation = roundPart(saturationSource * 100);
1161
- const lightness = roundPart(hsl.l * 100);
1162
- const hsvl = hsv.v * 100;
1163
- let colorName;
1164
-
1165
- // determine color appearance
1166
- if (lightness === 100 && saturation === 0) {
1167
- colorName = colorLabels.white;
1168
- } else if (lightness === 0) {
1169
- colorName = colorLabels.black;
1170
- } else if (saturation === 0) {
1171
- colorName = colorLabels.grey;
1172
- } else if (hue < 15 || hue >= 345) {
1173
- colorName = colorLabels.red;
1174
- } else if (hue >= 15 && hue < 45) {
1175
- colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
1176
- } else if (hue >= 45 && hue < 75) {
1177
- const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
1178
- const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
1179
- colorName = isGold ? colorLabels.gold : colorLabels.yellow;
1180
- colorName = isOlive ? colorLabels.olive : colorName;
1181
- } else if (hue >= 75 && hue < 155) {
1182
- colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
1183
- } else if (hue >= 155 && hue < 175) {
1184
- colorName = colorLabels.teal;
1185
- } else if (hue >= 175 && hue < 195) {
1186
- colorName = colorLabels.cyan;
1187
- } else if (hue >= 195 && hue < 255) {
1188
- colorName = colorLabels.blue;
1189
- } else if (hue >= 255 && hue < 270) {
1190
- colorName = colorLabels.violet;
1191
- } else if (hue >= 270 && hue < 295) {
1192
- colorName = colorLabels.magenta;
1193
- } else if (hue >= 295 && hue < 345) {
1194
- colorName = colorLabels.pink;
1195
- }
1027
+ const saturation = roundPart(hsv.s * 100);
1028
+ const lightness = roundPart(hsv.v * 100);
1029
+ const colorName = self.appearance;
1196
1030
 
1197
1031
  let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
1198
1032
 
1199
- if (format === 'hsl') {
1200
- colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
1201
- setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
1202
- setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
1203
- setAttribute(knob1, ariaValueNow, `${hue}`);
1204
- setAttribute(knob2, ariaValueText, `${saturation}%`);
1205
- setAttribute(knob2, ariaValueNow, `${saturation}`);
1206
- } else if (format === 'hwb') {
1033
+ if (format === 'hwb') {
1207
1034
  const { hwb } = self;
1208
1035
  const whiteness = roundPart(hwb.w * 100);
1209
1036
  const blackness = roundPart(hwb.b * 100);
1210
1037
  colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
1211
- setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
1212
1038
  setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
1213
1039
  setAttribute(knob1, ariaValueNow, `${whiteness}`);
1040
+ setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
1214
1041
  setAttribute(knob2, ariaValueText, `${hue}%`);
1215
1042
  setAttribute(knob2, ariaValueNow, `${hue}`);
1216
1043
  } else {
1044
+ [r, g, b] = [r, g, b].map(roundPart);
1045
+ colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
1217
1046
  colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
1218
- setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
1047
+
1219
1048
  setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
1220
1049
  setAttribute(knob1, ariaValueNow, `${lightness}`);
1050
+ setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
1221
1051
  setAttribute(knob2, ariaValueText, `${hue}°`);
1222
1052
  setAttribute(knob2, ariaValueNow, `${hue}`);
1223
1053
  }
@@ -1312,37 +1142,13 @@ export default class ColorPicker {
1312
1142
  }
1313
1143
  }
1314
1144
 
1315
- /**
1316
- * The `Space` & `Enter` keys specific event listener.
1317
- * Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
1318
- * @param {KeyboardEvent} e
1319
- * @this {ColorPicker}
1320
- */
1321
- keyToggle(e) {
1322
- const self = this;
1323
- const { menuToggle } = self;
1324
- const { activeElement } = getDocument(menuToggle);
1325
- const { code } = e;
1326
-
1327
- if ([keyEnter, keySpace].includes(code)) {
1328
- if ((menuToggle && activeElement === menuToggle) || !activeElement) {
1329
- e.preventDefault();
1330
- if (!activeElement) {
1331
- self.togglePicker(e);
1332
- } else {
1333
- self.toggleMenu();
1334
- }
1335
- }
1336
- }
1337
- }
1338
-
1339
1145
  /**
1340
1146
  * Toggle the `ColorPicker` dropdown visibility.
1341
- * @param {Event} e
1147
+ * @param {Event=} e
1342
1148
  * @this {ColorPicker}
1343
1149
  */
1344
1150
  togglePicker(e) {
1345
- e.preventDefault();
1151
+ if (e) e.preventDefault();
1346
1152
  const self = this;
1347
1153
  const { colorPicker } = self;
1348
1154
 
@@ -1363,8 +1169,13 @@ export default class ColorPicker {
1363
1169
  }
1364
1170
  }
1365
1171
 
1366
- /** Toggles the visibility of the `ColorPicker` presets menu. */
1367
- 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();
1368
1179
  const self = this;
1369
1180
  const { colorMenu } = self;
1370
1181
 
@@ -1390,6 +1201,10 @@ export default class ColorPicker {
1390
1201
  const relatedBtn = openPicker ? pickerToggle : menuToggle;
1391
1202
  const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
1392
1203
 
1204
+ // if (!self.isValid) {
1205
+ self.value = self.color.toString(true);
1206
+ // }
1207
+
1393
1208
  if (openDropdown) {
1394
1209
  removeClass(openDropdown, 'show');
1395
1210
  setAttribute(relatedBtn, ariaExpanded, 'false');
@@ -1403,9 +1218,6 @@ export default class ColorPicker {
1403
1218
  }, animationDuration);
1404
1219
  }
1405
1220
 
1406
- if (!self.isValid) {
1407
- self.value = self.color.toString();
1408
- }
1409
1221
  if (!focusPrevented) {
1410
1222
  focus(pickerToggle);
1411
1223
  }