@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/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
|