@thednp/color-picker 0.0.1-alpha2 → 0.0.1-alpha3

Sign up to get free protection for your applications and to get access to all the features.
package/src/js/color.js CHANGED
@@ -4,6 +4,7 @@ import setElementStyle from 'shorter-js/src/misc/setElementStyle';
4
4
  import ObjectAssign from 'shorter-js/src/misc/ObjectAssign';
5
5
 
6
6
  import nonColors from './util/nonColors';
7
+ import roundPart from './util/roundPart';
7
8
 
8
9
  // Color supported formats
9
10
  const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
@@ -172,7 +173,7 @@ function getRGBFromName(name) {
172
173
  * @returns {string} - the hexadecimal value
173
174
  */
174
175
  function convertDecimalToHex(d) {
175
- return Math.round(d * 255).toString(16);
176
+ return roundPart(d * 255).toString(16);
176
177
  }
177
178
 
178
179
  /**
@@ -426,9 +427,9 @@ function hsvToRgb(H, S, V) {
426
427
  */
427
428
  function rgbToHex(r, g, b, allow3Char) {
428
429
  const hex = [
429
- pad2(Math.round(r).toString(16)),
430
- pad2(Math.round(g).toString(16)),
431
- pad2(Math.round(b).toString(16)),
430
+ pad2(roundPart(r).toString(16)),
431
+ pad2(roundPart(g).toString(16)),
432
+ pad2(roundPart(b).toString(16)),
432
433
  ];
433
434
 
434
435
  // Return a 3 character hex if possible
@@ -453,9 +454,9 @@ function rgbToHex(r, g, b, allow3Char) {
453
454
  */
454
455
  function rgbaToHex(r, g, b, a, allow4Char) {
455
456
  const hex = [
456
- pad2(Math.round(r).toString(16)),
457
- pad2(Math.round(g).toString(16)),
458
- pad2(Math.round(b).toString(16)),
457
+ pad2(roundPart(r).toString(16)),
458
+ pad2(roundPart(g).toString(16)),
459
+ pad2(roundPart(b).toString(16)),
459
460
  pad2(convertDecimalToHex(a)),
460
461
  ];
461
462
 
@@ -617,6 +618,8 @@ function inputToRGB(input) {
617
618
  let w = null;
618
619
  let b = null;
619
620
  let h = null;
621
+ let r = null;
622
+ let g = null;
620
623
  let ok = false;
621
624
  let format = 'hex';
622
625
 
@@ -627,7 +630,10 @@ function inputToRGB(input) {
627
630
  }
628
631
  if (typeof color === 'object') {
629
632
  if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
630
- rgb = { r: color.r, g: color.g, b: color.b }; // RGB values in [0, 255] range
633
+ ({ r, g, b } = color);
634
+ [r, g, b] = [...[r, g, b]]
635
+ .map((n) => bound01(n, isPercentage(n) ? 100 : 255) * 255).map(roundPart);
636
+ rgb = { r, g, b }; // RGB values now are all in [0, 255] range
631
637
  ok = true;
632
638
  format = 'rgb';
633
639
  } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
@@ -715,14 +721,6 @@ export default class Color {
715
721
  self.ok = ok;
716
722
  /** @type {CP.ColorFormats} */
717
723
  self.format = configFormat || format;
718
-
719
- // Don't let the range of [0,255] come back in [0,1].
720
- // Potentially lose a little bit of precision here, but will fix issues where
721
- // .5 gets interpreted as half of the total, instead of half of 1
722
- // If it was supposed to be 128, this was already taken care of by `inputToRgb`
723
- if (r < 1) self.r = Math.round(r);
724
- if (g < 1) self.g = Math.round(g);
725
- if (b < 1) self.b = Math.round(b);
726
724
  }
727
725
 
728
726
  /**
@@ -738,7 +736,7 @@ export default class Color {
738
736
  * @returns {boolean} the query result
739
737
  */
740
738
  get isDark() {
741
- return this.brightness < 128;
739
+ return this.brightness < 120;
742
740
  }
743
741
 
744
742
  /**
@@ -790,13 +788,13 @@ export default class Color {
790
788
  const {
791
789
  r, g, b, a,
792
790
  } = this;
793
- const [R, G, B] = [r, g, b].map((x) => Math.round(x));
791
+ const [R, G, B] = [r, g, b].map((x) => roundPart(x));
794
792
 
795
793
  return {
796
794
  r: R,
797
795
  g: G,
798
796
  b: B,
799
- a: Math.round(a * 100) / 100,
797
+ a: roundPart(a * 100) / 100,
800
798
  };
801
799
  }
802
800
 
@@ -826,7 +824,7 @@ export default class Color {
826
824
  const {
827
825
  r, g, b, a,
828
826
  } = this.toRgb();
829
- const A = a === 1 ? '' : ` / ${Math.round(a * 100)}%`;
827
+ const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
830
828
 
831
829
  return `rgb(${r} ${g} ${b}${A})`;
832
830
  }
@@ -921,10 +919,10 @@ export default class Color {
921
919
  let {
922
920
  h, s, l, a,
923
921
  } = this.toHsl();
924
- h = Math.round(h * 360);
925
- s = Math.round(s * 100);
926
- l = Math.round(l * 100);
927
- a = Math.round(a * 100) / 100;
922
+ h = roundPart(h * 360);
923
+ s = roundPart(s * 100);
924
+ l = roundPart(l * 100);
925
+ a = roundPart(a * 100) / 100;
928
926
 
929
927
  return a === 1
930
928
  ? `hsl(${h}, ${s}%, ${l}%)`
@@ -941,11 +939,11 @@ export default class Color {
941
939
  let {
942
940
  h, s, l, a,
943
941
  } = this.toHsl();
944
- h = Math.round(h * 360);
945
- s = Math.round(s * 100);
946
- l = Math.round(l * 100);
947
- a = Math.round(a * 100);
948
- const A = a < 100 ? ` / ${Math.round(a)}%` : '';
942
+ h = roundPart(h * 360);
943
+ s = roundPart(s * 100);
944
+ l = roundPart(l * 100);
945
+ a = roundPart(a * 100);
946
+ const A = a < 100 ? ` / ${roundPart(a)}%` : '';
949
947
 
950
948
  return `hsl(${h}deg ${s}% ${l}%${A})`;
951
949
  }
@@ -972,11 +970,11 @@ export default class Color {
972
970
  let {
973
971
  h, w, b, a,
974
972
  } = this.toHwb();
975
- h = Math.round(h * 360);
976
- w = Math.round(w * 100);
977
- b = Math.round(b * 100);
978
- a = Math.round(a * 100);
979
- const A = a < 100 ? ` / ${Math.round(a)}%` : '';
973
+ h = roundPart(h * 360);
974
+ w = roundPart(w * 100);
975
+ b = roundPart(b * 100);
976
+ a = roundPart(a * 100);
977
+ const A = a < 100 ? ` / ${roundPart(a)}%` : '';
980
978
 
981
979
  return `hwb(${h}deg ${w}% ${b}%${A})`;
982
980
  }
@@ -1122,5 +1120,6 @@ ObjectAssign(Color, {
1122
1120
  numberInputToObject,
1123
1121
  stringInputToObject,
1124
1122
  inputToRGB,
1123
+ roundPart,
1125
1124
  ObjectAssign,
1126
1125
  });
@@ -4,6 +4,8 @@ import ariaValueMax from 'shorter-js/src/strings/ariaValueMax';
4
4
  import createElement from 'shorter-js/src/misc/createElement';
5
5
  import setAttribute from 'shorter-js/src/attr/setAttribute';
6
6
 
7
+ import tabIndex from './tabindex';
8
+
7
9
  /**
8
10
  * Returns all color controls for `ColorPicker`.
9
11
  *
@@ -69,10 +71,8 @@ export default function getColorControls(self) {
69
71
  const {
70
72
  i, c, l, min, max,
71
73
  } = template;
72
- // const hidden = i === 2 && format === 'hwb' ? ' v-hidden' : '';
73
74
  const control = createElement({
74
75
  tagName: 'div',
75
- // className: `color-control${hidden}`,
76
76
  className: 'color-control',
77
77
  });
78
78
  setAttribute(control, 'role', 'presentation');
@@ -92,7 +92,7 @@ export default function getColorControls(self) {
92
92
 
93
93
  setAttribute(knob, ariaLabel, l);
94
94
  setAttribute(knob, 'role', 'slider');
95
- setAttribute(knob, 'tabindex', '0');
95
+ setAttribute(knob, tabIndex, '0');
96
96
  setAttribute(knob, ariaValueMin, `${min}`);
97
97
  setAttribute(knob, ariaValueMax, `${max}`);
98
98
  control.append(knob);
@@ -59,7 +59,6 @@ export default function getColorForm(self) {
59
59
  max,
60
60
  step,
61
61
  });
62
- // }
63
62
  colorForm.append(cInputLabel, cInput);
64
63
  });
65
64
  return colorForm;
@@ -5,6 +5,8 @@ import getAttribute from 'shorter-js/src/attr/getAttribute';
5
5
  import createElement from 'shorter-js/src/misc/createElement';
6
6
  import setElementStyle from 'shorter-js/src/misc/setElementStyle';
7
7
 
8
+ import setCSSProperties from './setCSSProperties';
9
+ import tabIndex from './tabindex';
8
10
  import Color from '../color';
9
11
  import ColorPalette from '../color-palette';
10
12
 
@@ -25,45 +27,38 @@ export default function getColorMenu(self, colorsSource, menuClass) {
25
27
  colorsArray = colorsArray instanceof Array ? colorsArray : [];
26
28
  const colorsCount = colorsArray.length;
27
29
  const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
28
- let fit = lightSteps
29
- || Math.max(...[5, 6, 7, 8, 9, 10].filter((x) => colorsCount > (x * 2) && !(colorsCount % x)));
30
- fit = Number.isFinite(fit) ? fit : 5;
30
+ const fit = lightSteps || [9, 10].find((x) => colorsCount > x * 2 && !(colorsCount % x)) || 5;
31
31
  const isMultiLine = isOptionsMenu && colorsCount > fit;
32
- let rowCountHover = 1;
33
- rowCountHover = isMultiLine && colorsCount < 27 ? 2 : rowCountHover;
34
- rowCountHover = colorsCount >= 27 ? 3 : rowCountHover;
35
- rowCountHover = colorsCount >= 36 ? 4 : rowCountHover;
36
- rowCountHover = colorsCount >= 45 ? 5 : rowCountHover;
37
- const rowCount = rowCountHover - (colorsCount < 27 ? 1 : 2);
38
- const isScrollable = isMultiLine && colorsCount > rowCountHover * fit;
32
+ let rowCountHover = 2;
33
+ rowCountHover = isMultiLine && colorsCount >= fit * 2 ? 3 : rowCountHover;
34
+ rowCountHover = colorsCount >= fit * 3 ? 4 : rowCountHover;
35
+ rowCountHover = colorsCount >= fit * 4 ? 5 : rowCountHover;
36
+ const rowCount = rowCountHover - (colorsCount < fit * 3 ? 1 : 2);
37
+ const isScrollable = isMultiLine && colorsCount > rowCount * fit;
39
38
  let finalClass = menuClass;
40
39
  finalClass += isScrollable ? ' scrollable' : '';
41
40
  finalClass += isMultiLine ? ' multiline' : '';
42
41
  const gap = isMultiLine ? '1px' : '0.25rem';
43
42
  let optionSize = isMultiLine ? 1.75 : 2;
44
- optionSize = !(colorsCount % 10) && isMultiLine ? 1.5 : optionSize;
43
+ optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
45
44
  const menuHeight = `${(rowCount || 1) * optionSize}rem`;
46
45
  const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
47
- const gridTemplateColumns = `repeat(${fit}, ${optionSize}rem)`;
48
- const gridTemplateRows = `repeat(auto-fill, ${optionSize}rem)`;
49
46
 
50
47
  const menu = createElement({
51
48
  tagName: 'ul',
52
49
  className: finalClass,
53
50
  });
54
51
  setAttribute(menu, 'role', 'listbox');
55
- setAttribute(menu, ariaLabel, `${menuLabel}`);
52
+ setAttribute(menu, ariaLabel, menuLabel);
56
53
 
57
- if (isOptionsMenu) {
58
- if (isScrollable) {
59
- const styleText = 'this.style.height=';
60
- setAttribute(menu, 'onmouseout', `${styleText}'${menuHeight}'`);
61
- setAttribute(menu, 'onmouseover', `${styleText}'${menuHeightHover}'`);
62
- }
63
- const menuStyle = {
64
- height: isScrollable ? menuHeight : '', gridTemplateColumns, gridTemplateRows, gap,
65
- };
66
- setElementStyle(menu, menuStyle);
54
+ if (isScrollable) { // @ts-ignore
55
+ setCSSProperties(menu, {
56
+ '--grid-item-size': `${optionSize}rem`,
57
+ '--grid-fit': fit,
58
+ '--grid-gap': gap,
59
+ '--grid-height': menuHeight,
60
+ '--grid-hover-height': menuHeightHover,
61
+ });
67
62
  }
68
63
 
69
64
  colorsArray.forEach((x) => {
@@ -78,15 +73,13 @@ export default function getColorMenu(self, colorsSource, menuClass) {
78
73
  innerText: `${label || x}`,
79
74
  });
80
75
 
81
- setAttribute(option, 'tabindex', '0');
76
+ setAttribute(option, tabIndex, '0');
82
77
  setAttribute(option, 'data-value', `${value}`);
83
78
  setAttribute(option, 'role', 'option');
84
79
  setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
85
80
 
86
81
  if (isOptionsMenu) {
87
- setElementStyle(option, {
88
- width: `${optionSize}rem`, height: `${optionSize}rem`, backgroundColor: x,
89
- });
82
+ setElementStyle(option, { backgroundColor: x });
90
83
  }
91
84
 
92
85
  menu.append(option);
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Round colour components, for all formats except HEX.
3
+ * @param {number} v one of the colour components
4
+ * @returns {number} the rounded number
5
+ */
6
+ export default function roundPart(v) {
7
+ const floor = Math.floor(v);
8
+ return v - floor < 0.5 ? floor : Math.round(v);
9
+ }
@@ -0,0 +1,12 @@
1
+ import ObjectKeys from 'shorter-js/src/misc/ObjectKeys';
2
+
3
+ /**
4
+ * Helps setting CSS variables to the color-menu.
5
+ * @param {HTMLElement} element
6
+ * @param {Record<string,any>} props
7
+ */
8
+ export default function setCSSProperties(element, props) {
9
+ ObjectKeys(props).forEach((prop) => {
10
+ element.style.setProperty(prop, props[prop]);
11
+ });
12
+ }
@@ -0,0 +1,3 @@
1
+ const tabIndex = 'tabindex';
2
+
3
+ export default tabIndex;
@@ -223,16 +223,30 @@ color-picker:focus,
223
223
  padding: 0;
224
224
  margin: 0;
225
225
  list-style: none;
226
+ --grid-item-size: 2rem; // grid item width / height
227
+ --grid-fit: 5; // grid columns
228
+ --grid-gap: .25rem; // grid vertical / horizontal spacing
229
+ --grid-height: auto; // default height
230
+ --grid-hover-height: auto; // height on hover
231
+ grid-template-columns: repeat(var(--grid-fit), var(--grid-item-size));
232
+ grid-template-rows: repeat(auto-fill, var(--grid-item-size));
233
+ gap: var(--grid-gap);
226
234
  }
227
235
  .color-options.scrollable {
236
+ height: var(--grid-height);
228
237
  margin: 0 -.5rem 0 0; // 0.5rem is the scrollbar width
229
238
  overflow-y: scroll;
230
239
  transition: height .33s ease;
231
240
  }
241
+ .color-options.scrollable:hover {
242
+ height: var(--grid-hover-height);
243
+ }
244
+ .color-options + .color-defaults {
245
+ margin-top: .25rem;
246
+ }
232
247
  .multiline + .color-defaults {
233
248
  flex-direction: row;
234
249
  flex-wrap: wrap;
235
- margin-top: .25rem;
236
250
  .color-option {
237
251
  padding: .25rem .33rem; font-size: 12px;
238
252
  }
@@ -240,20 +254,30 @@ color-picker:focus,
240
254
  .color-options .color-option {
241
255
  // hide any text
242
256
  position: relative;
257
+ width: var(--grid-item-size);
258
+ height: var(--grid-item-size);
243
259
  overflow: hidden;
244
260
  text-indent: 2.1rem;
245
- background: #eee;
246
- opacity: .8;
247
- }
248
- .color-options .color-option:hover,
249
- .color-options .color-option:active,
250
- .color-options .color-option:focus {
251
- opacity: 1;
261
+ // background: #eee;
252
262
  }
253
263
  .color-options .color-option:active,
254
264
  .color-options .color-option:focus {
255
265
  outline: none;
256
266
  }
267
+ .color-options .color-option::before {
268
+ position: absolute;
269
+ top: 0;
270
+ right: 0;
271
+ bottom: 0;
272
+ left: 0;
273
+ }
274
+ .color-options .color-option:hover::before,
275
+ .color-options .color-option:active::before,
276
+ .color-options .color-option:focus::before {
277
+ content: "";
278
+ border: 3px solid rgba(255,255,255.3,.75);
279
+ mix-blend-mode: difference;
280
+ }
257
281
 
258
282
  .color-options .color-option:active,
259
283
  .color-options .color-option:focus {
@@ -261,7 +285,6 @@ color-picker:focus,
261
285
  }
262
286
 
263
287
  .color-options .color-option.active {
264
- opacity: 1;
265
288
  &:after {
266
289
  position: absolute;
267
290
  top: 50%;
@@ -269,7 +292,7 @@ color-picker:focus,
269
292
  width: 4px;
270
293
  height: 4px;
271
294
  margin: -2px 0 0 -2px;
272
- content: " ";
295
+ content: "";
273
296
  border-radius: 4px;
274
297
  }
275
298
  }
@@ -287,7 +310,7 @@ color-picker:focus,
287
310
  flex-wrap: wrap;
288
311
  align-items: center;
289
312
  // max-width: fit-content;
290
- padding: .5rem 0 0;
313
+ padding: .25rem 0 0;
291
314
  font: 12px sans-serif;
292
315
  }
293
316
 
@@ -374,7 +397,7 @@ color-picker:focus,
374
397
  background-color: #000;
375
398
  border: 1px solid #fff;
376
399
  outline: none;
377
- will-change: transform;
400
+ // will-change: transform;
378
401
  }
379
402
  .knob:hover {
380
403
  box-shadow: 0 0 0 6px rgba(255,255,255,.5);
package/types/cp.d.ts CHANGED
@@ -406,7 +406,7 @@ declare module "color-picker/src/js/color-picker" {
406
406
  /** Returns the current colour value */
407
407
  get value(): string;
408
408
  /** Check if the colour presets include any non-colour. */
409
- get includeNonColor(): boolean;
409
+ get hasNonColor(): boolean;
410
410
  /** Check if the parent of the target is a `ColorPickerElement` instance. */
411
411
  get isCE(): boolean;
412
412
  /** Returns hexadecimal value of the current colour. */
@@ -466,8 +466,6 @@ declare module "color-picker/src/js/color-picker" {
466
466
  * @param {boolean=} isPrevented when `true`, the component original event is prevented
467
467
  */
468
468
  updateInputs(isPrevented?: boolean | undefined): void;
469
- /** Shows the `ColorPicker` dropdown or the presets menu. */
470
- show(): void;
471
469
  /**
472
470
  * Hides the currently open `ColorPicker` dropdown.
473
471
  * @param {boolean=} focusPrevented