@thednp/color-picker 0.0.1-alpha2 → 0.0.1-alpha3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -15
- package/dist/css/color-picker.css +37 -15
- package/dist/css/color-picker.min.css +2 -2
- package/dist/css/color-picker.rtl.css +37 -15
- package/dist/css/color-picker.rtl.min.css +2 -2
- package/dist/js/color-picker-element-esm.js +164 -135
- package/dist/js/color-picker-element-esm.min.js +2 -2
- package/dist/js/color-picker-element.js +164 -135
- package/dist/js/color-picker-element.min.js +2 -2
- package/dist/js/color-picker-esm.js +159 -133
- package/dist/js/color-picker-esm.min.js +2 -2
- package/dist/js/color-picker.js +159 -133
- package/dist/js/color-picker.min.js +2 -2
- package/package.json +1 -1
- package/src/js/color-palette.js +18 -9
- package/src/js/color-picker-element.js +7 -3
- package/src/js/color-picker.js +59 -48
- package/src/js/color.js +33 -34
- package/src/js/util/getColorControls.js +3 -3
- package/src/js/util/getColorForm.js +0 -1
- package/src/js/util/getColorMenu.js +21 -28
- package/src/js/util/roundPart.js +9 -0
- package/src/js/util/setCSSProperties.js +12 -0
- package/src/js/util/tabindex.js +3 -0
- package/src/scss/color-picker.scss +35 -12
- package/types/cp.d.ts +1 -3
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
|
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(
|
430
|
-
pad2(
|
431
|
-
pad2(
|
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(
|
457
|
-
pad2(
|
458
|
-
pad2(
|
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
|
-
|
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 <
|
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) =>
|
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:
|
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 ? '' : ` / ${
|
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 =
|
925
|
-
s =
|
926
|
-
l =
|
927
|
-
a =
|
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 =
|
945
|
-
s =
|
946
|
-
l =
|
947
|
-
a =
|
948
|
-
const A = a < 100 ? ` / ${
|
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 =
|
976
|
-
w =
|
977
|
-
b =
|
978
|
-
a =
|
979
|
-
const A = a < 100 ? ` / ${
|
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,
|
95
|
+
setAttribute(knob, tabIndex, '0');
|
96
96
|
setAttribute(knob, ariaValueMin, `${min}`);
|
97
97
|
setAttribute(knob, ariaValueMax, `${max}`);
|
98
98
|
control.append(knob);
|
@@ -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
|
-
|
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 =
|
33
|
-
rowCountHover = isMultiLine && colorsCount
|
34
|
-
rowCountHover = colorsCount >=
|
35
|
-
rowCountHover = colorsCount >=
|
36
|
-
|
37
|
-
const
|
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 =
|
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,
|
52
|
+
setAttribute(menu, ariaLabel, menuLabel);
|
56
53
|
|
57
|
-
if (
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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,
|
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
|
+
}
|
@@ -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: .
|
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
|
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
|