@fusion.dev/helix-headless 0.154.0 → 0.155.0
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/custom-elements.json +1427 -88
- package/dist/accordion/item/accordion-item.d.ts +15 -0
- package/dist/accordion/item/accordion-item.js +12 -1
- package/dist/accordion/root/accordion-root.d.ts +2 -1
- package/dist/accordion/root/accordion-root.js +1 -0
- package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +36 -3
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +42 -2
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +15 -5
- package/dist/breadcrumbs/root/breadcrumbs-root.js +29 -4
- package/dist/button-group/root/button-group-root.d.ts +2 -0
- package/dist/button-group/root/button-group-root.js +1 -0
- package/dist/color-input/root/color-input-root.d.ts +9 -3
- package/dist/color-input/root/color-input-root.js +3 -1
- package/dist/color-picker/channels.d.ts +5 -3
- package/dist/color-picker/channels.js +79 -21
- package/dist/color-picker/root/color-picker-root.d.ts +58 -5
- package/dist/color-picker/root/color-picker-root.js +75 -13
- package/dist/date-input/root/date-input-root.d.ts +1 -0
- package/dist/date-input/root/date-input-root.js +2 -1
- package/dist/date-picker/root/date-picker-root.d.ts +43 -13
- package/dist/date-picker/root/date-picker-root.js +54 -31
- package/dist/element-events.generated.js +2 -1
- package/dist/navigation/root/navigation-root.d.ts +26 -0
- package/dist/navigation/root/navigation-root.js +58 -1
- package/dist/select/root/select-root.d.ts +29 -3
- package/dist/select/root/select-root.js +34 -9
- package/dist/slider/root/slider-root.d.ts +71 -9
- package/dist/slider/root/slider-root.js +129 -42
- package/dist/speed-dial/item/speed-dial-item.d.ts +29 -1
- package/dist/speed-dial/item/speed-dial-item.js +48 -7
- package/dist/speed-dial/root/speed-dial-root.d.ts +32 -6
- package/dist/speed-dial/root/speed-dial-root.js +50 -5
- package/dist/splitter/root/splitter-root.d.ts +22 -0
- package/dist/splitter/root/splitter-root.js +36 -7
- package/dist/steps/reach.d.ts +5 -1
- package/dist/steps/reach.js +2 -2
- package/dist/steps/root/steps-root.d.ts +37 -2
- package/dist/steps/root/steps-root.js +49 -7
- package/dist/steps/step/step.d.ts +6 -0
- package/dist/steps/step/step.js +4 -0
- package/dist/tabs/root/tabs-root.d.ts +37 -0
- package/dist/tabs/root/tabs-root.js +92 -2
- package/dist/tabs/tab/tab.d.ts +38 -2
- package/dist/tabs/tab/tab.js +45 -4
- package/dist/tag/group/tag-group.d.ts +29 -3
- package/dist/tag/group/tag-group.js +42 -11
- package/dist/toggle/root/toggle-root.d.ts +2 -0
- package/dist/toggle/root/toggle-root.js +1 -1
- package/dist/tree/item/tree-item.d.ts +10 -0
- package/dist/tree/item/tree-item.js +12 -0
- package/dist/tree/root/tree-root.d.ts +40 -1
- package/dist/tree/root/tree-root.js +120 -4
- package/dist/tree/selection.d.ts +6 -0
- package/dist/tree/selection.js +11 -0
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +984 -151
- package/types/vue.d.ts +221 -49
- package/types/words.d.ts +36 -1
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import { clamp, toHsl, toRgb } from '@fusion.dev/helix-core';
|
|
1
|
+
import { clamp, toHsl, toOklch, toRgb } from '@fusion.dev/helix-core';
|
|
2
|
+
const onStep = (value, step) => {
|
|
3
|
+
const decimals = String(step).split('.')[1]?.length ?? 0;
|
|
4
|
+
return Number((Math.round(value / step) * step).toFixed(decimals));
|
|
5
|
+
};
|
|
6
|
+
const FAMILY = {
|
|
7
|
+
hex: /^rgba?$/,
|
|
8
|
+
hsb: /^hsba?$/,
|
|
9
|
+
hsl: /^hsla?$/,
|
|
10
|
+
oklch: /^oklch$/,
|
|
11
|
+
rgb: /^rgba?$/,
|
|
12
|
+
};
|
|
13
|
+
const OKLCH_STEPS = [0.1, 0.001, 0.1];
|
|
2
14
|
const percent = (token) => {
|
|
3
15
|
if (token === undefined) {
|
|
4
16
|
return 100;
|
|
@@ -8,7 +20,8 @@ const percent = (token) => {
|
|
|
8
20
|
};
|
|
9
21
|
const readWritten = (value, format) => {
|
|
10
22
|
const text = value.trim().toLowerCase();
|
|
11
|
-
if (format
|
|
23
|
+
if ((format === 'hex' || format === 'rgb') &&
|
|
24
|
+
/^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/.test(text)) {
|
|
12
25
|
const digits = text.slice(1);
|
|
13
26
|
const width = digits.length <= 4 ? 1 : 2;
|
|
14
27
|
const byte = (index) => {
|
|
@@ -18,23 +31,46 @@ const readWritten = (value, format) => {
|
|
|
18
31
|
const count = digits.length / width;
|
|
19
32
|
return [byte(0), byte(1), byte(2), count === 4 ? Math.round((byte(3) / 255) * 100) : 100];
|
|
20
33
|
}
|
|
21
|
-
const call = /^(rgba?|hsla
|
|
22
|
-
if (!call?.[1] || call[2] === undefined) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
if (call[1].startsWith('hsl') !== (format === 'hsl')) {
|
|
34
|
+
const call = /^(rgba?|hsla?|hsba?|oklch)\(([^)]*)\)$/.exec(text);
|
|
35
|
+
if (!call?.[1] || call[2] === undefined || !FAMILY[format].test(call[1])) {
|
|
26
36
|
return null;
|
|
27
37
|
}
|
|
28
38
|
const [first, second, third, fourth] = call[2].replace(/[,/]/g, ' ').split(/\s+/).filter(Boolean);
|
|
29
|
-
const numbers = [first, second, third].map((one) => Number.parseFloat(one ?? ''));
|
|
39
|
+
const numbers = [first, second, third].map((one) => one === 'none' ? 0 : Number.parseFloat(one ?? ''));
|
|
30
40
|
if (numbers.some(Number.isNaN)) {
|
|
31
41
|
return null;
|
|
32
42
|
}
|
|
43
|
+
if (format === 'oklch') {
|
|
44
|
+
const [lightness = 0, chroma = 0, hue = 0] = numbers;
|
|
45
|
+
return [
|
|
46
|
+
onStep(first?.endsWith('%') ? lightness : lightness * 100, OKLCH_STEPS[0]),
|
|
47
|
+
onStep(second?.endsWith('%') ? (chroma / 100) * 0.4 : chroma, OKLCH_STEPS[1]),
|
|
48
|
+
onStep(hue, OKLCH_STEPS[2]),
|
|
49
|
+
Math.round(percent(fourth)),
|
|
50
|
+
];
|
|
51
|
+
}
|
|
33
52
|
return [...numbers, percent(fourth)].map((one) => Math.round(one));
|
|
34
53
|
};
|
|
35
54
|
export const channelFields = (value, color, format, alpha) => {
|
|
36
55
|
const written = readWritten(value, format);
|
|
37
56
|
const fromModel = () => {
|
|
57
|
+
if (format === 'hsb') {
|
|
58
|
+
return [
|
|
59
|
+
Math.round(color.hue),
|
|
60
|
+
Math.round(color.saturation),
|
|
61
|
+
Math.round(color.brightness),
|
|
62
|
+
Math.round(color.alpha),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
if (format === 'oklch') {
|
|
66
|
+
const { chroma, hue, lightness } = toOklch(color);
|
|
67
|
+
return [
|
|
68
|
+
onStep(lightness * 100, OKLCH_STEPS[0]),
|
|
69
|
+
onStep(chroma, OKLCH_STEPS[1]),
|
|
70
|
+
onStep(hue, OKLCH_STEPS[2]),
|
|
71
|
+
Math.round(color.alpha),
|
|
72
|
+
];
|
|
73
|
+
}
|
|
38
74
|
if (format === 'hsl') {
|
|
39
75
|
const { hue, lightness, saturation } = toHsl(color);
|
|
40
76
|
return [Math.round(hue), saturation, lightness, Math.round(color.alpha)];
|
|
@@ -43,18 +79,32 @@ export const channelFields = (value, color, format, alpha) => {
|
|
|
43
79
|
return [red, green, blue, Math.round(color.alpha)];
|
|
44
80
|
};
|
|
45
81
|
const [one = 0, two = 0, three = 0, four = 100] = written ?? fromModel();
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
{ channel: '
|
|
50
|
-
{ channel: '
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{ channel: '
|
|
55
|
-
{ channel: '
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
const byFormat = {
|
|
83
|
+
hex: [],
|
|
84
|
+
hsb: [
|
|
85
|
+
{ channel: 'hue', max: 360, min: 0, step: 1, value: one },
|
|
86
|
+
{ channel: 'saturation', max: 100, min: 0, step: 1, value: two },
|
|
87
|
+
{ channel: 'brightness', max: 100, min: 0, step: 1, value: three },
|
|
88
|
+
],
|
|
89
|
+
hsl: [
|
|
90
|
+
{ channel: 'hue', max: 360, min: 0, step: 1, value: one },
|
|
91
|
+
{ channel: 'saturation', max: 100, min: 0, step: 1, value: two },
|
|
92
|
+
{ channel: 'lightness', max: 100, min: 0, step: 1, value: three },
|
|
93
|
+
],
|
|
94
|
+
oklch: [
|
|
95
|
+
{ channel: 'lightness', max: 100, min: 0, step: OKLCH_STEPS[0], value: one },
|
|
96
|
+
{ channel: 'chroma', max: 0.4, min: 0, step: OKLCH_STEPS[1], value: two },
|
|
97
|
+
{ channel: 'hue', max: 360, min: 0, step: OKLCH_STEPS[2], value: three },
|
|
98
|
+
],
|
|
99
|
+
rgb: [],
|
|
100
|
+
};
|
|
101
|
+
const bytes = [
|
|
102
|
+
{ channel: 'red', max: 255, min: 0, step: 1, value: one },
|
|
103
|
+
{ channel: 'green', max: 255, min: 0, step: 1, value: two },
|
|
104
|
+
{ channel: 'blue', max: 255, min: 0, step: 1, value: three },
|
|
105
|
+
];
|
|
106
|
+
const fields = byFormat[format].length > 0 ? byFormat[format] : bytes;
|
|
107
|
+
return alpha ? [...fields, { channel: 'alpha', max: 100, min: 0, step: 1, value: four }] : fields;
|
|
58
108
|
};
|
|
59
109
|
export const writeChannel = (fields, format, alpha, channel, value) => {
|
|
60
110
|
if (Number.isNaN(value)) {
|
|
@@ -65,7 +115,7 @@ export const writeChannel = (fields, format, alpha, channel, value) => {
|
|
|
65
115
|
if (field === undefined) {
|
|
66
116
|
return null;
|
|
67
117
|
}
|
|
68
|
-
numbers.set(channel, clamp(
|
|
118
|
+
numbers.set(channel, clamp(onStep(value, field.step), field.min, field.max));
|
|
69
119
|
const get = (name, fallback = 0) => numbers.get(name) ?? fallback;
|
|
70
120
|
const opacity = get('alpha', 100);
|
|
71
121
|
const fraction = Number((opacity / 100).toFixed(2));
|
|
@@ -73,6 +123,14 @@ export const writeChannel = (fields, format, alpha, channel, value) => {
|
|
|
73
123
|
const body = `${get('hue')}, ${get('saturation')}%, ${get('lightness')}%`;
|
|
74
124
|
return alpha ? `hsla(${body}, ${fraction})` : `hsl(${body})`;
|
|
75
125
|
}
|
|
126
|
+
if (format === 'hsb') {
|
|
127
|
+
const body = `${get('hue')}, ${get('saturation')}%, ${get('brightness')}%`;
|
|
128
|
+
return alpha ? `hsba(${body}, ${fraction})` : `hsb(${body})`;
|
|
129
|
+
}
|
|
130
|
+
if (format === 'oklch') {
|
|
131
|
+
const body = `${get('lightness')}% ${get('chroma')} ${get('hue')}`;
|
|
132
|
+
return alpha ? `oklch(${body} / ${fraction})` : `oklch(${body})`;
|
|
133
|
+
}
|
|
76
134
|
if (format === 'rgb') {
|
|
77
135
|
const body = `${get('red')}, ${get('green')}, ${get('blue')}`;
|
|
78
136
|
return alpha ? `rgba(${body}, ${fraction})` : `rgb(${body})`;
|
|
@@ -32,6 +32,11 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
32
32
|
* is named by its color. The opacity ramp is heard as a percentage. Set
|
|
33
33
|
* `color-words` to speak the names in another language.
|
|
34
34
|
*
|
|
35
|
+
* `label` names the picker, which is then a group with that name, and the
|
|
36
|
+
* square's ramps say it before their own, as "Brand, Saturation". `hint`
|
|
37
|
+
* writes help under the picker, which the square, the ramps and the value
|
|
38
|
+
* field are described by. Inside Color Input the field carries both.
|
|
39
|
+
*
|
|
35
40
|
* The presets are a listbox with the current color selected, and one tab
|
|
36
41
|
* stop. The arrow keys move between them, Home and End go to the ends, and
|
|
37
42
|
* Enter, Space or a press chooses one.
|
|
@@ -43,6 +48,8 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
43
48
|
* @documentation https://fusion.dev/helix/components/color-picker
|
|
44
49
|
*
|
|
45
50
|
* @slot eyedropper-icon - The icon on the eyedropper button, usually a dropper.
|
|
51
|
+
* @slot hint - Help under the picker, for content richer than the `hint` text.
|
|
52
|
+
* @slot label - The name above the picker, for content richer than the `label` text.
|
|
46
53
|
*
|
|
47
54
|
* @event {Event} change - Fired after the value changes.
|
|
48
55
|
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before the color changes. Cancelable: call `preventDefault()` to keep the current color.
|
|
@@ -62,6 +69,8 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
62
69
|
* @csspart channel-marker-label - The value a mark stands at, when `marker-labels` is set.
|
|
63
70
|
* @csspart channel-markers - The row of marks along the track.
|
|
64
71
|
* @csspart channel-output - The current value, shown beside the track.
|
|
72
|
+
* @csspart channel-thumb - A ramp's thumb box, which holds no content in a picker.
|
|
73
|
+
* @csspart channel-thumbs - The layer over a ramp's track that holds its thumb box.
|
|
65
74
|
* @csspart channel-tooltip - A ramp's value in a bubble, where a ramp shows one.
|
|
66
75
|
* @csspart channel-tooltips - The layer over a ramp's track that holds its bubble.
|
|
67
76
|
* @csspart channels - The ramps beside the sample.
|
|
@@ -88,6 +97,8 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
88
97
|
* `--color-picker-brightness`, and this tier ships no CSS.
|
|
89
98
|
* @csspart grid-brightness - The range input for the square's vertical axis.
|
|
90
99
|
* @csspart grid-saturation - The range input for the square's horizontal axis.
|
|
100
|
+
* @csspart hint - The help under the picker, while `hint` is set or the `hint` slot has content.
|
|
101
|
+
* @csspart label - The name above the picker, while `label` is set or the `label` slot has content.
|
|
91
102
|
* @csspart sample - A chip of the current color. Pressing it copies the value.
|
|
92
103
|
* @csspart swatch - One preset, an option in the `swatches` listbox.
|
|
93
104
|
* @csspart swatches - The row of presets, a listbox.
|
|
@@ -100,7 +111,10 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
100
111
|
* @cssproperty --color-picker-saturation - The chosen saturation, 0 to 100, as a percentage.
|
|
101
112
|
* @cssproperty --color-picker-swatch - The color a chip shows: the current color on the sample, and the preset's color on each preset.
|
|
102
113
|
*
|
|
114
|
+
* @cssstate copied - The moment after the sample copies the value, while it says `copied-label`.
|
|
115
|
+
* @cssstate dragging - While a pointer drags the square's handle.
|
|
103
116
|
* @cssstate invalid - While `invalid` is set or `feedback` says what is wrong.
|
|
117
|
+
* @cssstate sampling - While the eyedropper is open over the screen.
|
|
104
118
|
*/
|
|
105
119
|
export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_base {
|
|
106
120
|
/**
|
|
@@ -142,14 +156,20 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
142
156
|
/**
|
|
143
157
|
* Whether the picker shows a number field for each channel of the color, as
|
|
144
158
|
* React Aria's ColorField does with `channel`: red, green and blue for hex
|
|
145
|
-
* and rgb, hue, saturation and lightness for hsl,
|
|
146
|
-
*
|
|
159
|
+
* and rgb, hue, saturation and lightness for hsl, hue, saturation and
|
|
160
|
+
* brightness for hsb, lightness, chroma and hue for oklch, and the opacity
|
|
161
|
+
* with `alpha`.
|
|
147
162
|
*
|
|
148
163
|
* For a reader who has the numbers from a brand guide, and for a keyboard,
|
|
149
164
|
* where typing 128 is quicker than holding an arrow key. The fields follow
|
|
150
165
|
* the syntax the format toggle sets.
|
|
151
166
|
*/
|
|
152
167
|
channelFields: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* What the chroma channel's field is called, in `channel-fields` with
|
|
170
|
+
* oklch.
|
|
171
|
+
*/
|
|
172
|
+
chromaLabel: string;
|
|
153
173
|
/**
|
|
154
174
|
* The words a color's name is spoken in, as JSON keyed by their English,
|
|
155
175
|
* such as `{"red": "rot", "vibrant": "kräftiges"}`.
|
|
@@ -179,10 +199,16 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
179
199
|
*/
|
|
180
200
|
eyedropperLabel: string;
|
|
181
201
|
/**
|
|
182
|
-
* The syntax the value is written in: `hex`, `rgb`,
|
|
202
|
+
* The syntax the value is written in: `hex`, `rgb`, `hsl`, `oklch`, or
|
|
203
|
+
* `hsb`.
|
|
183
204
|
*
|
|
184
205
|
* Only what is written. Everything is read whatever this says, so a picker on
|
|
185
|
-
* `hex` still takes an `rgb()` handed to it and stores the
|
|
206
|
+
* `hex` still takes an `rgb()` or an `oklch()` handed to it and stores the
|
|
207
|
+
* hex for it. `oklch` writes the sRGB color the picker holds, such as
|
|
208
|
+
* `oklch(62.8% 0.258 29.2)`; a color outside sRGB that is handed in is
|
|
209
|
+
* clipped to it. `hsb` writes the picker's own model, `hsb(0, 100%, 100%)`,
|
|
210
|
+
* as React Aria's and Zag's color strings do. It is not CSS, so the toggle
|
|
211
|
+
* walks the other four.
|
|
186
212
|
*/
|
|
187
213
|
format?: ColorFormat;
|
|
188
214
|
/**
|
|
@@ -193,13 +219,23 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
193
219
|
* What the green channel's field is called, in `channel-fields`.
|
|
194
220
|
*/
|
|
195
221
|
greenLabel: string;
|
|
222
|
+
/**
|
|
223
|
+
* Help under the picker, such as what the color is used for. The `hint`
|
|
224
|
+
* slot takes richer content.
|
|
225
|
+
*/
|
|
226
|
+
hint: string;
|
|
196
227
|
/**
|
|
197
228
|
* What the hue channel is called.
|
|
198
229
|
*/
|
|
199
230
|
hueLabel: string;
|
|
231
|
+
/**
|
|
232
|
+
* The name above the picker, such as "Brand color". The picker is then a
|
|
233
|
+
* group with this name. The `label` slot takes richer content.
|
|
234
|
+
*/
|
|
235
|
+
label: string;
|
|
200
236
|
/**
|
|
201
237
|
* What the lightness channel's field is called, in `channel-fields` with
|
|
202
|
-
* hsl.
|
|
238
|
+
* hsl and oklch.
|
|
203
239
|
*/
|
|
204
240
|
lightnessLabel: string;
|
|
205
241
|
/**
|
|
@@ -353,6 +389,23 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
353
389
|
private sampling;
|
|
354
390
|
/** The preset the reader was last on, which holds the row's one tab stop. */
|
|
355
391
|
private swatchAt;
|
|
392
|
+
/**
|
|
393
|
+
* The children in a slot, read off the children rather than heard from
|
|
394
|
+
* `slotchange`: a declarative shadow root fires that before anything
|
|
395
|
+
* listens, so a served picker kept its label hidden.
|
|
396
|
+
*/
|
|
397
|
+
private slottedIn;
|
|
398
|
+
private readonly handleNamingSlot;
|
|
399
|
+
/** The picker's own name, from `label` or the `label` slot's words. */
|
|
400
|
+
private get named();
|
|
401
|
+
/** Whether help is shown under the picker. */
|
|
402
|
+
private get hinted();
|
|
403
|
+
/** The ids a control in the picker is described by: the help, then the message. */
|
|
404
|
+
private get describedBy();
|
|
405
|
+
/** Whether a pointer is dragging the square's handle, said as `:state(dragging)`. */
|
|
406
|
+
private dragging;
|
|
407
|
+
/** Whether the eyedropper is open, said as `:state(sampling)`. */
|
|
408
|
+
private picking;
|
|
356
409
|
/**
|
|
357
410
|
* Whether the value on this element was produced here.
|
|
358
411
|
*
|
|
@@ -15,7 +15,7 @@ import { areaAt, hueAt } from '../wheel.js';
|
|
|
15
15
|
import '../../button/index.js';
|
|
16
16
|
import '../../button-group/index.js';
|
|
17
17
|
import '../../slider/index.js';
|
|
18
|
-
const FORMATS = ['hex', 'rgb', 'hsl'];
|
|
18
|
+
const FORMATS = ['hex', 'rgb', 'hsl', 'oklch'];
|
|
19
19
|
const START = { alpha: 100, brightness: 100, hue: 0, saturation: 100 };
|
|
20
20
|
let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPickerFeedback(MixinControllerValue(HelixFormElement)) {
|
|
21
21
|
constructor() {
|
|
@@ -26,13 +26,16 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
26
26
|
this.blueLabel = 'Blue';
|
|
27
27
|
this.brightnessLabel = 'Brightness';
|
|
28
28
|
this.channelFields = false;
|
|
29
|
+
this.chromaLabel = 'Chroma';
|
|
29
30
|
this.colorWords = {};
|
|
30
31
|
this.copiedLabel = 'Copied';
|
|
31
32
|
this.copyLabel = 'Copy {value}';
|
|
32
33
|
this.eyedropperLabel = 'Sample a color from the screen';
|
|
33
34
|
this.formatLabel = 'Syntax: {format}. Change it.';
|
|
34
35
|
this.greenLabel = 'Green';
|
|
36
|
+
this.hint = '';
|
|
35
37
|
this.hueLabel = 'Hue';
|
|
38
|
+
this.label = '';
|
|
36
39
|
this.lightnessLabel = 'Lightness';
|
|
37
40
|
this.noEyedropper = false;
|
|
38
41
|
this.noFormat = false;
|
|
@@ -50,6 +53,11 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
50
53
|
this.valueMissingMessage = 'Choose a color.';
|
|
51
54
|
this.wheel = false;
|
|
52
55
|
this.sampling = false;
|
|
56
|
+
this.handleNamingSlot = () => {
|
|
57
|
+
this.requestUpdate();
|
|
58
|
+
};
|
|
59
|
+
this.dragging = false;
|
|
60
|
+
this.picking = false;
|
|
53
61
|
this.owned = false;
|
|
54
62
|
this.announcesInput = true;
|
|
55
63
|
this.unsettled = false;
|
|
@@ -132,6 +140,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
132
140
|
return;
|
|
133
141
|
}
|
|
134
142
|
this.dispatchEvent(new HelixSampleStartEvent());
|
|
143
|
+
this.picking = true;
|
|
135
144
|
try {
|
|
136
145
|
const { sRGBHex } = await new Dropper().open();
|
|
137
146
|
const parsed = parseColor(sRGBHex);
|
|
@@ -142,6 +151,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
142
151
|
}
|
|
143
152
|
catch {
|
|
144
153
|
}
|
|
154
|
+
this.picking = false;
|
|
145
155
|
this.dispatchEvent(new HelixSampleEndEvent());
|
|
146
156
|
};
|
|
147
157
|
this.handleGridPointer = (event) => {
|
|
@@ -150,12 +160,14 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
150
160
|
}
|
|
151
161
|
const grid = event.currentTarget;
|
|
152
162
|
if (event.type === 'pointerup' || event.type === 'pointercancel') {
|
|
163
|
+
this.dragging = false;
|
|
153
164
|
this.settle();
|
|
154
165
|
return;
|
|
155
166
|
}
|
|
156
167
|
if (event.type === 'pointerdown') {
|
|
168
|
+
this.dragging = true;
|
|
157
169
|
capturePointer(grid, event.pointerId);
|
|
158
|
-
this.partNamed('grid-saturation')?.focus();
|
|
170
|
+
this.partNamed('grid-saturation')?.focus({ preventScroll: true });
|
|
159
171
|
}
|
|
160
172
|
else if (!grid.hasPointerCapture(event.pointerId)) {
|
|
161
173
|
return;
|
|
@@ -181,7 +193,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
181
193
|
}
|
|
182
194
|
if (event.type === 'pointerdown') {
|
|
183
195
|
capturePointer(wheel, event.pointerId);
|
|
184
|
-
this.partNamed('wheel-hue')?.focus();
|
|
196
|
+
this.partNamed('wheel-hue')?.focus({ preventScroll: true });
|
|
185
197
|
}
|
|
186
198
|
else if (!wheel.hasPointerCapture(event.pointerId)) {
|
|
187
199
|
return;
|
|
@@ -232,6 +244,23 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
232
244
|
};
|
|
233
245
|
}
|
|
234
246
|
static { this.words = 'helix-color-picker'; }
|
|
247
|
+
slottedIn(name) {
|
|
248
|
+
return this.knownChildren.filter((child) => child.slot === name);
|
|
249
|
+
}
|
|
250
|
+
get named() {
|
|
251
|
+
return (this.label ||
|
|
252
|
+
this.slottedIn('label')
|
|
253
|
+
.map((child) => child.textContent ?? '')
|
|
254
|
+
.join(' ')
|
|
255
|
+
.trim());
|
|
256
|
+
}
|
|
257
|
+
get hinted() {
|
|
258
|
+
return this.hint !== '' || this.slottedIn('hint').length > 0;
|
|
259
|
+
}
|
|
260
|
+
get describedBy() {
|
|
261
|
+
const ids = [this.hinted ? 'hint' : '', this.feedback === '' ? '' : 'feedback'].filter(Boolean);
|
|
262
|
+
return ids.length > 0 ? ids.join(' ') : nothing;
|
|
263
|
+
}
|
|
235
264
|
settle() {
|
|
236
265
|
if (this.unsettled) {
|
|
237
266
|
this.unsettled = false;
|
|
@@ -299,6 +328,8 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
299
328
|
return {
|
|
300
329
|
alpha: this.opacityLabel,
|
|
301
330
|
blue: this.blueLabel,
|
|
331
|
+
brightness: this.brightnessLabel,
|
|
332
|
+
chroma: this.chromaLabel,
|
|
302
333
|
green: this.greenLabel,
|
|
303
334
|
hue: this.hueLabel,
|
|
304
335
|
lightness: this.lightnessLabel,
|
|
@@ -327,10 +358,10 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
327
358
|
<input
|
|
328
359
|
part="field-input"
|
|
329
360
|
type="number"
|
|
330
|
-
inputmode
|
|
361
|
+
inputmode=${field.step < 1 ? 'decimal' : 'numeric'}
|
|
331
362
|
min=${field.min}
|
|
332
363
|
max=${field.max}
|
|
333
|
-
step
|
|
364
|
+
step=${field.step}
|
|
334
365
|
.value=${live(String(field.value))}
|
|
335
366
|
?disabled=${this.isDisabled}
|
|
336
367
|
?readonly=${this.readOnly}
|
|
@@ -350,7 +381,8 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
350
381
|
exportparts="control: channel-control, feedback: channel-feedback, field: channel-field,
|
|
351
382
|
hint: channel-hint, label: channel-label, marker: channel-marker,
|
|
352
383
|
marker-label: channel-marker-label, markers: channel-markers, output: channel-output,
|
|
353
|
-
|
|
384
|
+
thumb: channel-thumb, thumbs: channel-thumbs, tooltip: channel-tooltip,
|
|
385
|
+
tooltips: channel-tooltips"
|
|
354
386
|
data-channel=${channel}
|
|
355
387
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
356
388
|
label=${label}
|
|
@@ -391,10 +423,18 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
391
423
|
willUpdate(changed) {
|
|
392
424
|
super.willUpdate(changed);
|
|
393
425
|
const outside = [...(this.internals.labels ?? [])];
|
|
394
|
-
|
|
426
|
+
const named = this.named;
|
|
427
|
+
hostAria(this.internals, {
|
|
428
|
+
label: outside.length > 0 ? '' : named,
|
|
429
|
+
labelledBy: outside,
|
|
430
|
+
role: outside.length > 0 || named !== '' ? 'group' : '',
|
|
431
|
+
});
|
|
395
432
|
if (!this.hydrating) {
|
|
396
433
|
this.sampling = 'EyeDropper' in globalThis;
|
|
397
434
|
}
|
|
435
|
+
this.toggleState('copied', this.copier.copied);
|
|
436
|
+
this.toggleState('dragging', this.dragging);
|
|
437
|
+
this.toggleState('sampling', this.picking);
|
|
398
438
|
}
|
|
399
439
|
renderTools() {
|
|
400
440
|
return html `<${this.tag('button-group')} part="actions" label=${this.toolsLabel}>
|
|
@@ -471,7 +511,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
471
511
|
const reversed = square('brightness');
|
|
472
512
|
const hue = hueName({ alpha: 100, brightness: 100, hue: color.hue, saturation: 100 }, this.colorWords, locale);
|
|
473
513
|
const hueSaid = (value) => new Intl.ListFormat(locale, { style: 'short', type: 'unit' }).format([String(value), hue]);
|
|
474
|
-
const owner = this.getAttribute('aria-label')?.trim();
|
|
514
|
+
const owner = this.getAttribute('aria-label')?.trim() || this.named;
|
|
475
515
|
const axisName = (channel) => (owner ? `${owner}, ${channel}` : channel);
|
|
476
516
|
const area = html `<div
|
|
477
517
|
part="grid"
|
|
@@ -488,7 +528,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
488
528
|
min="0"
|
|
489
529
|
max="100"
|
|
490
530
|
aria-label=${axisName(this.saturationLabel)}
|
|
491
|
-
aria-describedby=${this.
|
|
531
|
+
aria-describedby=${this.describedBy}
|
|
492
532
|
aria-valuetext=${both}
|
|
493
533
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
494
534
|
.value=${String(color.saturation)}
|
|
@@ -505,7 +545,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
505
545
|
tabindex="-1"
|
|
506
546
|
style="writing-mode: vertical-lr; direction: rtl"
|
|
507
547
|
aria-label=${axisName(this.brightnessLabel)}
|
|
508
|
-
aria-describedby=${this.
|
|
548
|
+
aria-describedby=${this.describedBy}
|
|
509
549
|
aria-valuetext=${reversed}
|
|
510
550
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
511
551
|
.value=${String(color.brightness)}
|
|
@@ -514,7 +554,10 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
514
554
|
@change=${() => this.settle()}
|
|
515
555
|
/>
|
|
516
556
|
</div>`;
|
|
517
|
-
return html
|
|
557
|
+
return html `<div part="label" ?hidden=${this.named === ''}>
|
|
558
|
+
<slot name="label" @slotchange=${this.handleNamingSlot}>${this.label}</slot>
|
|
559
|
+
</div>
|
|
560
|
+
${this.wheel
|
|
518
561
|
? html `<div
|
|
519
562
|
part="wheel"
|
|
520
563
|
style="--color-picker-hue: ${color.hue}"
|
|
@@ -531,7 +574,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
531
574
|
max="360"
|
|
532
575
|
dir="ltr"
|
|
533
576
|
aria-label=${axisName(this.hueLabel)}
|
|
534
|
-
aria-describedby=${this.
|
|
577
|
+
aria-describedby=${this.describedBy}
|
|
535
578
|
aria-valuetext=${hueSaid(color.hue)}
|
|
536
579
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
537
580
|
.value=${String(color.hue)}
|
|
@@ -574,7 +617,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
574
617
|
part="value"
|
|
575
618
|
type="text"
|
|
576
619
|
aria-label=${this.valueLabel}
|
|
577
|
-
aria-describedby=${this.
|
|
620
|
+
aria-describedby=${this.describedBy}
|
|
578
621
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
579
622
|
autocomplete=${this.autocomplete || nothing}
|
|
580
623
|
spellcheck="false"
|
|
@@ -591,6 +634,9 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
591
634
|
</div>
|
|
592
635
|
|
|
593
636
|
${this.presets.length === 0 ? nothing : this.renderSwatches(painted)}
|
|
637
|
+
<div part="hint" id="hint" ?hidden=${!this.hinted}>
|
|
638
|
+
<slot name="hint" @slotchange=${this.handleNamingSlot}>${this.hint}</slot>
|
|
639
|
+
</div>
|
|
594
640
|
${this.renderPickerFeedback()}`;
|
|
595
641
|
}
|
|
596
642
|
};
|
|
@@ -615,6 +661,10 @@ __decorate([
|
|
|
615
661
|
__decorate([
|
|
616
662
|
property({ attribute: 'channel-fields', type: Boolean })
|
|
617
663
|
], HelixHeadlessColorPicker.prototype, "channelFields", void 0);
|
|
664
|
+
__decorate([
|
|
665
|
+
words,
|
|
666
|
+
property({ attribute: 'chroma-label' })
|
|
667
|
+
], HelixHeadlessColorPicker.prototype, "chromaLabel", void 0);
|
|
618
668
|
__decorate([
|
|
619
669
|
property({ attribute: 'color-words', converter: wordsConverter })
|
|
620
670
|
], HelixHeadlessColorPicker.prototype, "colorWords", void 0);
|
|
@@ -641,10 +691,16 @@ __decorate([
|
|
|
641
691
|
words,
|
|
642
692
|
property({ attribute: 'green-label' })
|
|
643
693
|
], HelixHeadlessColorPicker.prototype, "greenLabel", void 0);
|
|
694
|
+
__decorate([
|
|
695
|
+
property()
|
|
696
|
+
], HelixHeadlessColorPicker.prototype, "hint", void 0);
|
|
644
697
|
__decorate([
|
|
645
698
|
words,
|
|
646
699
|
property({ attribute: 'hue-label' })
|
|
647
700
|
], HelixHeadlessColorPicker.prototype, "hueLabel", void 0);
|
|
701
|
+
__decorate([
|
|
702
|
+
property()
|
|
703
|
+
], HelixHeadlessColorPicker.prototype, "label", void 0);
|
|
648
704
|
__decorate([
|
|
649
705
|
words,
|
|
650
706
|
property({ attribute: 'lightness-label' })
|
|
@@ -711,6 +767,12 @@ __decorate([
|
|
|
711
767
|
__decorate([
|
|
712
768
|
state()
|
|
713
769
|
], HelixHeadlessColorPicker.prototype, "swatchAt", void 0);
|
|
770
|
+
__decorate([
|
|
771
|
+
state()
|
|
772
|
+
], HelixHeadlessColorPicker.prototype, "dragging", void 0);
|
|
773
|
+
__decorate([
|
|
774
|
+
state()
|
|
775
|
+
], HelixHeadlessColorPicker.prototype, "picking", void 0);
|
|
714
776
|
HelixHeadlessColorPicker = __decorate([
|
|
715
777
|
customElement('helix-headless-color-picker')
|
|
716
778
|
], HelixHeadlessColorPicker);
|
|
@@ -72,6 +72,7 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
|
|
|
72
72
|
* @csspart picker-day-outside - A day from a neighboring month. Applied with `picker-day`.
|
|
73
73
|
* @csspart picker-day-selected - The chosen day, or either end of a chosen range. Applied with `picker-day`.
|
|
74
74
|
* @csspart picker-day-today - Today. Applied with `picker-day`.
|
|
75
|
+
* @csspart picker-day-weekend - A day of the weekend where the calendar's language is spoken, beside `picker-day`.
|
|
75
76
|
* @csspart picker-feedback - The calendar's own message, under it, while the calendar's `feedback` is set.
|
|
76
77
|
* @csspart picker-grid - The grid of days.
|
|
77
78
|
* @csspart picker-header - The month name and its controls.
|
|
@@ -218,7 +218,8 @@ let HelixHeadlessDateInput = class HelixHeadlessDateInput extends MixinSegmented
|
|
|
218
218
|
exportparts="cell: picker-cell, cell-range-end: picker-cell-range-end,
|
|
219
219
|
cell-range-middle: picker-cell-range-middle, cell-range-start: picker-cell-range-start,
|
|
220
220
|
day: picker-day, day-disabled: picker-day-disabled, day-outside: picker-day-outside,
|
|
221
|
-
day-selected: picker-day-selected, day-today: picker-day-today,
|
|
221
|
+
day-selected: picker-day-selected, day-today: picker-day-today, day-weekend: picker-day-weekend,
|
|
222
|
+
feedback: picker-feedback, grid: picker-grid,
|
|
222
223
|
header: picker-header, heading: picker-heading, heading-button: picker-heading-button,
|
|
223
224
|
month: picker-month, months: picker-months, next: picker-next,
|
|
224
225
|
next-mark: picker-next-mark, period: picker-period, period-current: picker-period-current,
|