@fusion.dev/helix-headless 0.176.1 → 0.177.1
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 +1383 -766
- package/dist/area-chart/root/area-chart-root.d.ts +18 -1
- package/dist/area-chart/root/area-chart-root.js +2 -2
- package/dist/avatar/root/avatar-root.d.ts +1 -1
- package/dist/avatar/root/avatar-root.js +1 -1
- package/dist/bar-chart/root/bar-chart-root.d.ts +19 -1
- package/dist/bar-chart/root/bar-chart-root.js +2 -2
- package/dist/button/root/button-root.js +1 -0
- package/dist/canvas/node/canvas-node.js +1 -1
- package/dist/canvas/root/canvas-root.d.ts +5 -1
- package/dist/canvas/root/canvas-root.js +9 -1
- package/dist/chat/composer/chat-composer.d.ts +1 -1
- package/dist/chat/composer/chat-composer.js +1 -1
- package/dist/color-picker/root/color-picker-root.d.ts +10 -0
- package/dist/color-picker/root/color-picker-root.js +20 -13
- package/dist/combobox/root/combobox-root.d.ts +13 -0
- package/dist/combobox/root/combobox-root.js +16 -4
- package/dist/donut-chart/root/donut-chart-root.d.ts +37 -2
- package/dist/donut-chart/root/donut-chart-root.js +22 -8
- package/dist/element-events.generated.js +2 -0
- package/dist/file-input/root/file-input-root.d.ts +3 -2
- package/dist/file-input/root/file-input-root.js +5 -2
- package/dist/graph/root/graph-root.d.ts +28 -3
- package/dist/graph/root/graph-root.js +38 -3
- package/dist/heatmap/root/heatmap-root.d.ts +28 -5
- package/dist/heatmap/root/heatmap-root.js +7 -2
- package/dist/line-chart/root/line-chart-root.d.ts +18 -1
- package/dist/line-chart/root/line-chart-root.js +2 -2
- package/dist/loader/root/loader-root.d.ts +15 -0
- package/dist/loader/root/loader-root.js +28 -6
- package/dist/pin-input/root/pin-input-root.js +1 -0
- package/dist/rating/root/rating-root.d.ts +2 -1
- package/dist/rating/root/rating-root.js +8 -2
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +29 -6
- package/dist/scatter-chart/root/scatter-chart-root.js +18 -3
- package/dist/select/root/select-root.d.ts +4 -1
- package/dist/select/root/select-root.js +1 -0
- package/dist/sparkline/root/sparkline-root.d.ts +29 -4
- package/dist/sparkline/root/sparkline-root.js +8 -1
- package/dist/tag/group/tag-group.d.ts +4 -3
- package/dist/tag/group/tag-group.js +6 -5
- package/dist/tag/root/tag-root.js +2 -1
- package/dist/time-input/root/time-input-root.d.ts +7 -1
- package/dist/time-input/root/time-input-root.js +6 -3
- package/dist/tree/item/tree-item.d.ts +4 -4
- package/dist/tree/item/tree-item.js +5 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +688 -382
- package/types/vue.d.ts +101 -22
- package/types/words.d.ts +26 -2
|
@@ -83,6 +83,18 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
|
|
|
83
83
|
* there until readings arrive. The `empty` and `loading` slots take markup in
|
|
84
84
|
* their place, such as a link to add the first reading.
|
|
85
85
|
*
|
|
86
|
+
* **Keyboard.** The plot is one tab stop.
|
|
87
|
+
*
|
|
88
|
+
* | Key | What it does |
|
|
89
|
+
* |---|---|
|
|
90
|
+
* | Tab | Moves focus to the plot and shows the first reading. |
|
|
91
|
+
* | Right, Left | Moves to the next or the previous reading. On a right-to-left page, Left moves to the next. |
|
|
92
|
+
* | Down, Up | Moves among the series of the reading. With one series, moves to the next or the previous reading. |
|
|
93
|
+
* | Home, End | Moves to the first or the last reading. |
|
|
94
|
+
* | Page Down, Page Up | Moves a tenth of the readings on or back. |
|
|
95
|
+
* | Enter, Space | Presses the reading shown, which sends `helix-reading-press`. |
|
|
96
|
+
* | Escape | Puts the reading and its tooltip away. |
|
|
97
|
+
*
|
|
86
98
|
* @since 0.1.0
|
|
87
99
|
* @status stable
|
|
88
100
|
* @category charts
|
|
@@ -107,9 +119,10 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
|
|
|
107
119
|
* @csspart live - The readout: a polite live region that says the reading the keys moved to. Hide it visually.
|
|
108
120
|
* @csspart loading - The words in place of the drawing while `loading` is set and there are no readings: the `loading` slot, falling back to `loading-text`.
|
|
109
121
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
110
|
-
* @csspart plot - The drawing, which takes focus to be read by key.
|
|
122
|
+
* @csspart plot - The drawing, which takes focus to be read by key. With nothing to draw, a group named as the chart is, holding the `empty` or `loading` part.
|
|
111
123
|
* @csspart summary - The total and each part's share at the latest reading, written as a sentence for screen readers.
|
|
112
124
|
* @csspart svg - The drawing surface the bands sit on.
|
|
125
|
+
* @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`. Hide it visually.
|
|
113
126
|
* @csspart tick - One number on the axis, placed with `inset-block-start` at its level.
|
|
114
127
|
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
115
128
|
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
@@ -129,6 +142,10 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
|
|
|
129
142
|
* @csspart tooltip-value - A column's value inside the tooltip.
|
|
130
143
|
*
|
|
131
144
|
* @cssproperty --area-chart-axis - The width of the axis, in `ch`, set from the widest number on it. Written by the component on the axis.
|
|
145
|
+
*
|
|
146
|
+
* @cssstate active - While a reading is shown, under the pointer or the keys, with its tooltip open.
|
|
147
|
+
* @cssstate empty - While the `empty` part stands in the plot.
|
|
148
|
+
* @cssstate loading - While the `loading` part stands in the plot.
|
|
132
149
|
*/
|
|
133
150
|
export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base {
|
|
134
151
|
/**
|
|
@@ -96,7 +96,7 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartStat
|
|
|
96
96
|
const rows = this.sourceRows;
|
|
97
97
|
const stack = this.stack;
|
|
98
98
|
if (rows.length === 0 || stack.columns.length === 0) {
|
|
99
|
-
return
|
|
99
|
+
return this.renderEmpty();
|
|
100
100
|
}
|
|
101
101
|
const top = this.overlap
|
|
102
102
|
? Math.max(0, ...stack.layers.flat())
|
|
@@ -108,7 +108,7 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartStat
|
|
|
108
108
|
const slot = PLOT_WIDTH / rows.length;
|
|
109
109
|
const x = linear([0, Math.max(rows.length - 1, 1)], [slot / 2, PLOT_WIDTH - slot / 2]);
|
|
110
110
|
return html `${this.valueAxis ? this.renderAxis(y, domain) : nothing}${this.renderPlot(stack, rows.length, x, y, domain)}
|
|
111
|
-
${this.renderLabels(rows.map((row) => row.label))} ${this.renderSummary(stack, rows)}`;
|
|
111
|
+
${this.renderLabels(rows.map((row) => row.label))} ${this.renderSummary(stack, rows)}${this.renderTable()}`;
|
|
112
112
|
}
|
|
113
113
|
renderAxis(y, domain) {
|
|
114
114
|
const locale = wordsLocale(this);
|
|
@@ -32,7 +32,7 @@ export declare const initialsOf: (name: string) => string;
|
|
|
32
32
|
* @aliases Profile picture, Gravatar
|
|
33
33
|
* @documentation https://fusion.dev/helix/components/avatar
|
|
34
34
|
*
|
|
35
|
-
* @slot - Shown when there is no usable image, in place of the initials.
|
|
35
|
+
* @slot - Shown when there is no usable image, in place of the initials. Hidden from assistive technology when the avatar has a `name`, which the host announces.
|
|
36
36
|
*
|
|
37
37
|
* @event {Event} error - Relayed from the image when it fails.
|
|
38
38
|
* @event {Event} load - Relayed from the image once it has loaded.
|
|
@@ -103,7 +103,7 @@ let HelixHeadlessAvatar = class HelixHeadlessAvatar extends HelixElement {
|
|
|
103
103
|
return this.renderInitials();
|
|
104
104
|
}
|
|
105
105
|
renderInitials() {
|
|
106
|
-
return html `<slot>
|
|
106
|
+
return html `<slot aria-hidden=${this.name === '' ? nothing : 'true'}>
|
|
107
107
|
<span part="initials" aria-hidden="true">${this.initials ?? initialsOf(this.name)}</span>
|
|
108
108
|
</slot>`;
|
|
109
109
|
}
|
|
@@ -45,6 +45,19 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
|
|
|
45
45
|
* there until readings arrive. The `empty` and `loading` slots take markup in
|
|
46
46
|
* their place, such as a link to add the first reading.
|
|
47
47
|
*
|
|
48
|
+
* **Keyboard.** The plot is one tab stop.
|
|
49
|
+
*
|
|
50
|
+
* | Key | What it does |
|
|
51
|
+
* |---|---|
|
|
52
|
+
* | Tab | Moves focus to the plot and shows the first reading. |
|
|
53
|
+
* | Right, Left | Moves to the next or the previous reading. On a right-to-left page, Left moves to the next. |
|
|
54
|
+
* | Down, Up | Moves among the series of the reading. With one series, moves to the next or the previous reading. |
|
|
55
|
+
* | Down, Up under `orientation="horizontal"` | Moves to the next or the previous reading, and Right and Left move among the series. |
|
|
56
|
+
* | Home, End | Moves to the first or the last reading. |
|
|
57
|
+
* | Page Down, Page Up | Moves a tenth of the readings on or back. |
|
|
58
|
+
* | Enter, Space | Presses the reading shown, which sends `helix-reading-press`. |
|
|
59
|
+
* | Escape | Puts the reading and its tooltip away. |
|
|
60
|
+
*
|
|
48
61
|
* @since 0.1.0
|
|
49
62
|
* @status stable
|
|
50
63
|
* @category charts
|
|
@@ -73,9 +86,10 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
|
|
|
73
86
|
* @csspart live - The readout: a polite live region that says the reading the keys moved to. Hide it visually.
|
|
74
87
|
* @csspart loading - The words in place of the drawing while `loading` is set and there are no readings: the `loading` slot, falling back to `loading-text`.
|
|
75
88
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
76
|
-
* @csspart plot - The drawing, which takes focus to be read by key.
|
|
89
|
+
* @csspart plot - The drawing, which takes focus to be read by key. With nothing to draw, a group named as the chart is, holding the `empty` or `loading` part.
|
|
77
90
|
* @csspart summary - The chart's name and each series' highest reading, written as a sentence for screen readers. Hide it visually.
|
|
78
91
|
* @csspart svg - The drawing surface the bars and lines sit on.
|
|
92
|
+
* @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`. Hide it visually.
|
|
79
93
|
* @csspart tick - One number on an axis.
|
|
80
94
|
* @csspart tooltip - The tooltip bubble, forwarded from `Tooltip`.
|
|
81
95
|
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
@@ -99,6 +113,10 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
|
|
|
99
113
|
* @cssproperty --chart-bar-wash - The gradient for a bar, named per bar so the styled
|
|
100
114
|
* tier can use it as the `fill`. It is always drawn, and used only when
|
|
101
115
|
* `wash="gradient"` is set.
|
|
116
|
+
*
|
|
117
|
+
* @cssstate active - While a reading is shown, under the pointer or the keys, with its tooltip open.
|
|
118
|
+
* @cssstate empty - While the `empty` part stands in the plot.
|
|
119
|
+
* @cssstate loading - While the `loading` part stands in the plot.
|
|
102
120
|
*/
|
|
103
121
|
export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
|
|
104
122
|
/**
|
|
@@ -44,7 +44,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartStatus
|
|
|
44
44
|
const series = this.series;
|
|
45
45
|
const categories = series.length === 0 ? [] : this.sourceRows.map((row) => row.label);
|
|
46
46
|
if (categories.length === 0) {
|
|
47
|
-
return
|
|
47
|
+
return this.renderEmpty();
|
|
48
48
|
}
|
|
49
49
|
const scaled = this.scaled;
|
|
50
50
|
const own = (one) => scaled.has(seriesKey(one.name));
|
|
@@ -78,7 +78,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartStatus
|
|
|
78
78
|
: undefined;
|
|
79
79
|
return html `${z === undefined && !this.valueAxis
|
|
80
80
|
? nothing
|
|
81
|
-
: this.renderAxis(y, domain, 'primary', sideways, widest)}${this.renderPlot(series, categories, y, domain, floor, z)}${z === undefined ? nothing : this.renderAxis(z, other, 'secondary', sideways, widest, tint)}${this.renderLabels(categories)}${this.renderSummary(series)}`;
|
|
81
|
+
: this.renderAxis(y, domain, 'primary', sideways, widest)}${this.renderPlot(series, categories, y, domain, floor, z)}${z === undefined ? nothing : this.renderAxis(z, other, 'secondary', sideways, widest, tint)}${this.renderLabels(categories)}${this.renderSummary(series)}${this.renderTable()}`;
|
|
82
82
|
}
|
|
83
83
|
renderSummary(series) {
|
|
84
84
|
const rows = this.sourceRows;
|
|
@@ -77,7 +77,7 @@ let HelixHeadlessCanvasNode = class HelixHeadlessCanvasNode extends HelixElement
|
|
|
77
77
|
aria-describedby=${this.status || this.meaning || this.links ? 'about' : nothing}
|
|
78
78
|
aria-label=${this.label || nothing}
|
|
79
79
|
aria-labelledby=${this.label || this.group ? nothing : 'content'}
|
|
80
|
-
aria-keyshortcuts=${this.connectable ? 'Enter F2 Space Control+ArrowDown Control+ArrowUp Shift+ArrowRight Shift+ArrowDown Alt+ArrowRight Alt+ArrowDown C Delete' : nothing}
|
|
80
|
+
aria-keyshortcuts=${this.connectable ? 'Enter F2 Space Control+ArrowDown Control+ArrowUp Shift+ArrowRight Shift+ArrowDown Alt+ArrowRight Alt+ArrowDown C Delete Plus Minus' : nothing}
|
|
81
81
|
role=${this.group ? 'group' : 'article'}
|
|
82
82
|
tabindex=${this.current ? '0' : '-1'}
|
|
83
83
|
>
|
|
@@ -45,7 +45,11 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
45
45
|
* double click zooms out.
|
|
46
46
|
*
|
|
47
47
|
* Keyboard behavior:
|
|
48
|
-
* - One card holds the board's tab stop
|
|
48
|
+
* - One card holds the board's tab stop, and the board itself leaves the tab
|
|
49
|
+
* order while a card can hold it. Escape on a card goes back out to the
|
|
50
|
+
* board, where the arrow keys pan it.
|
|
51
|
+
* - Plus and minus zoom the board, on the board and on a card or a
|
|
52
|
+
* connection.
|
|
49
53
|
* - The arrow keys move through the cards in reading order. The card you land
|
|
50
54
|
* on scrolls into view, and its connections are announced with it.
|
|
51
55
|
* - The card you land on is selected. Control or Command with an arrow key,
|
|
@@ -566,6 +566,11 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
566
566
|
}
|
|
567
567
|
return;
|
|
568
568
|
}
|
|
569
|
+
if (plain && ['+', '=', '-', '_'].includes(event.key)) {
|
|
570
|
+
event.preventDefault();
|
|
571
|
+
this.zoom(event.key === '+' || event.key === '=' ? 1 : -1);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
569
574
|
if (event.key === ' ' && !this.linking) {
|
|
570
575
|
event.preventDefault();
|
|
571
576
|
this.toggle(key);
|
|
@@ -2143,6 +2148,9 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2143
2148
|
const stop = this.stop !== undefined && this.keyNamed(this.stop) === this.stop
|
|
2144
2149
|
? this.stop
|
|
2145
2150
|
: reading(all, this.edges, this.order ?? 'spatial')[0]?.id;
|
|
2151
|
+
const holds = stop !== undefined &&
|
|
2152
|
+
(stop.startsWith('edge:') || this.cards.some((card) => card.name === stop));
|
|
2153
|
+
this.partNamed('region')?.setAttribute('tabindex', holds ? '-1' : '0');
|
|
2146
2154
|
for (const card of this.cards) {
|
|
2147
2155
|
const x = finite(card.x) - origin.x;
|
|
2148
2156
|
const y = finite(card.y) - origin.y;
|
|
@@ -2255,7 +2263,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2255
2263
|
d=${drawn.d}
|
|
2256
2264
|
data-edge=${key}
|
|
2257
2265
|
fill="none"
|
|
2258
|
-
aria-keyshortcuts="Enter F2 Space Control+ArrowDown Control+ArrowUp C Delete"
|
|
2266
|
+
aria-keyshortcuts="Enter F2 Space Control+ArrowDown Control+ArrowUp C Delete Plus Minus"
|
|
2259
2267
|
role="button"
|
|
2260
2268
|
tabindex=${key === stop ? '0' : '-1'}
|
|
2261
2269
|
/>
|
|
@@ -49,7 +49,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
49
49
|
*
|
|
50
50
|
* @event {HelixSendEvent} helix-send - Fired when the user sends their turn, and cancelable.
|
|
51
51
|
* @event {HelixTypingEvent} helix-typing - The user started or stopped writing, with `typing` saying which, as the composer's `typing` does.
|
|
52
|
-
* @event {Event} input - The user changed the text. Read it from `value`, for a typing indicator or a saved draft.
|
|
52
|
+
* @event {Event} input - The user changed the text. Read it from `value`, for a typing indicator or a saved draft. Bubbles and is composed, as a native field's is.
|
|
53
53
|
*
|
|
54
54
|
* @csspart composer - The whole composer.
|
|
55
55
|
* @csspart end - The box around the `end` slot.
|
|
@@ -41,7 +41,7 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
41
41
|
event.stopPropagation();
|
|
42
42
|
this.value = event.target.value;
|
|
43
43
|
this.edited = true;
|
|
44
|
-
this.
|
|
44
|
+
this.announceInput();
|
|
45
45
|
this.markTyping(this.value !== '');
|
|
46
46
|
};
|
|
47
47
|
this.handleSend = () => {
|
|
@@ -633,6 +633,16 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
633
633
|
* is redrawn though the value it settles on is the one drawn before.
|
|
634
634
|
*/
|
|
635
635
|
protected renderFields(color: Color): TemplateResult;
|
|
636
|
+
/**
|
|
637
|
+
* A channel's name, after the picker's own where the picker has one.
|
|
638
|
+
*
|
|
639
|
+
* **Every channel the same way**, as React Aria's color area names its own:
|
|
640
|
+
* "Brand, Saturation" says which picker on a page with two. The square's
|
|
641
|
+
* axes and the wheel took the picker's name, and the hue and opacity ramps
|
|
642
|
+
* and the number fields did not, so two pickers on a page gave two sliders
|
|
643
|
+
* named "Hue", measured in Chromium's tree.
|
|
644
|
+
*/
|
|
645
|
+
protected channelName(channel: string): string;
|
|
636
646
|
/**
|
|
637
647
|
* One of the ramps under the square.
|
|
638
648
|
*
|
|
@@ -407,11 +407,15 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
407
407
|
}
|
|
408
408
|
renderFields(color) {
|
|
409
409
|
return html `<div part="fields">
|
|
410
|
-
${channelFields(this.value, color, this.format ?? 'hex', this.alpha).map((field) =>
|
|
411
|
-
|
|
410
|
+
${channelFields(this.value, color, this.format ?? 'hex', this.alpha).map((field) => {
|
|
411
|
+
const word = this.channelWord(field.channel);
|
|
412
|
+
const name = this.channelName(word);
|
|
413
|
+
return html `<label part="field">
|
|
414
|
+
<span part="field-label">${word}</span>
|
|
412
415
|
<input
|
|
413
416
|
part="field-input"
|
|
414
417
|
type="number"
|
|
418
|
+
aria-label=${name === word ? nothing : name}
|
|
415
419
|
inputmode=${field.step < 1 ? 'decimal' : 'numeric'}
|
|
416
420
|
min=${field.min}
|
|
417
421
|
max=${field.max}
|
|
@@ -421,14 +425,19 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
421
425
|
?readonly=${this.readOnly}
|
|
422
426
|
@input=${(event) => this.handleField(field.channel, event)}
|
|
423
427
|
@change=${(event) => {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
+
event.stopPropagation();
|
|
429
|
+
this.settle();
|
|
430
|
+
this.requestUpdate();
|
|
431
|
+
}}
|
|
428
432
|
/>
|
|
429
|
-
</label
|
|
433
|
+
</label>`;
|
|
434
|
+
})}
|
|
430
435
|
</div>`;
|
|
431
436
|
}
|
|
437
|
+
channelName(channel) {
|
|
438
|
+
const owner = this.getAttribute('aria-label')?.trim() || this.named;
|
|
439
|
+
return owner ? `${owner}, ${channel}` : channel;
|
|
440
|
+
}
|
|
432
441
|
renderChannel(channel, label, max, value, spoken = '') {
|
|
433
442
|
return html `<${this.tag('slider')}
|
|
434
443
|
part="channel"
|
|
@@ -439,7 +448,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
439
448
|
tooltips: channel-tooltips"
|
|
440
449
|
data-channel=${channel}
|
|
441
450
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
442
|
-
label=${label}
|
|
451
|
+
label=${this.channelName(label)}
|
|
443
452
|
max=${max}
|
|
444
453
|
min="0"
|
|
445
454
|
value-text=${spoken || nothing}
|
|
@@ -568,8 +577,6 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
568
577
|
const reversed = square('brightness');
|
|
569
578
|
const hue = hueName({ alpha: 100, brightness: 100, hue: color.hue, saturation: 100 }, this.colorWords, locale);
|
|
570
579
|
const hueSaid = (value) => new Intl.ListFormat(locale, { style: 'short', type: 'unit' }).format([String(value), hue]);
|
|
571
|
-
const owner = this.getAttribute('aria-label')?.trim() || this.named;
|
|
572
|
-
const axisName = (channel) => (owner ? `${owner}, ${channel}` : channel);
|
|
573
580
|
const area = html `<div
|
|
574
581
|
part="grid"
|
|
575
582
|
style="--color-picker-hue: ${color.hue}; --color-picker-saturation: ${color.saturation}%; --color-picker-brightness: ${color.brightness}%"
|
|
@@ -584,7 +591,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
584
591
|
type="range"
|
|
585
592
|
min="0"
|
|
586
593
|
max="100"
|
|
587
|
-
aria-label=${
|
|
594
|
+
aria-label=${this.channelName(this.saturationLabel)}
|
|
588
595
|
aria-describedby=${this.describedBy}
|
|
589
596
|
aria-valuetext=${both}
|
|
590
597
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
@@ -601,7 +608,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
601
608
|
max="100"
|
|
602
609
|
tabindex="-1"
|
|
603
610
|
style="writing-mode: vertical-lr; direction: rtl"
|
|
604
|
-
aria-label=${
|
|
611
|
+
aria-label=${this.channelName(this.brightnessLabel)}
|
|
605
612
|
aria-describedby=${this.describedBy}
|
|
606
613
|
aria-valuetext=${reversed}
|
|
607
614
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
@@ -630,7 +637,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPicke
|
|
|
630
637
|
min="0"
|
|
631
638
|
max="360"
|
|
632
639
|
dir="ltr"
|
|
633
|
-
aria-label=${
|
|
640
|
+
aria-label=${this.channelName(this.hueLabel)}
|
|
634
641
|
aria-describedby=${this.describedBy}
|
|
635
642
|
aria-valuetext=${hueSaid(color.hue)}
|
|
636
643
|
aria-invalid=${this.marked ? 'true' : nothing}
|
|
@@ -395,6 +395,8 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
395
395
|
* "France", and the form received two entries.
|
|
396
396
|
*/
|
|
397
397
|
private get values();
|
|
398
|
+
/** The values a value holds, read as `values` reads this element's own. */
|
|
399
|
+
private valuesIn;
|
|
398
400
|
/** The answers created here, which no option holds, kept so their commas read as theirs. */
|
|
399
401
|
private readonly created;
|
|
400
402
|
/**
|
|
@@ -577,6 +579,17 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
577
579
|
* `result-label` for one where a page still sets it.
|
|
578
580
|
*/
|
|
579
581
|
private countSaid;
|
|
582
|
+
/**
|
|
583
|
+
* Says the option a choice changed, for VoiceOver, which says nothing when
|
|
584
|
+
* `aria-selected` changes: `Beta, selected` for one taken, and its words
|
|
585
|
+
* alone for one let go, as the highlight says an option not chosen.
|
|
586
|
+
*
|
|
587
|
+
* **The option the choice changed, read from the values before and after.**
|
|
588
|
+
* With `multiple` the value is the whole list, and the option was looked up
|
|
589
|
+
* by it, so no option matched and no choice was ever said: measured with
|
|
590
|
+
* Beta chosen, Enter let it go and the live region kept `Beta, selected`.
|
|
591
|
+
*/
|
|
592
|
+
private sayChoice;
|
|
580
593
|
/**
|
|
581
594
|
* Takes what the list announced and stops it there.
|
|
582
595
|
*
|
|
@@ -188,12 +188,12 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
188
188
|
this.handleChange = (event) => {
|
|
189
189
|
event.stopPropagation();
|
|
190
190
|
const { value } = event.detail;
|
|
191
|
+
const before = this.values;
|
|
191
192
|
if (!this.requestValue(value)) {
|
|
192
193
|
return;
|
|
193
194
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.status.say(formatWords(this, 'selectedLabel', { name: chosen.text }));
|
|
195
|
+
if (onApple()) {
|
|
196
|
+
this.sayChoice(before, this.valuesIn(value));
|
|
197
197
|
}
|
|
198
198
|
this.emptied = false;
|
|
199
199
|
this.typed = '';
|
|
@@ -253,11 +253,14 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
253
253
|
return this.partNamed('listbox');
|
|
254
254
|
}
|
|
255
255
|
get values() {
|
|
256
|
+
return this.valuesIn(this.value);
|
|
257
|
+
}
|
|
258
|
+
valuesIn(value) {
|
|
256
259
|
const known = new Set(this.created);
|
|
257
260
|
for (const option of optionsAmong(this.knownChildren)) {
|
|
258
261
|
known.add(option.value);
|
|
259
262
|
}
|
|
260
|
-
return chosenValues(
|
|
263
|
+
return chosenValues(value, known, this.multiple);
|
|
261
264
|
}
|
|
262
265
|
get chosen() {
|
|
263
266
|
const options = optionsAmong(this.knownChildren);
|
|
@@ -460,6 +463,15 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
460
463
|
? countLabel(count, this.resultLabel, this.resultsLabel, wordsLocale(this))
|
|
461
464
|
: formatWords(this, 'resultsLabel', { count });
|
|
462
465
|
}
|
|
466
|
+
sayChoice(before, after) {
|
|
467
|
+
const taken = this.multiple ? after.find((one) => !before.includes(one)) : after[0];
|
|
468
|
+
const changed = taken ?? before.find((one) => !after.includes(one));
|
|
469
|
+
const option = this.listbox?.options.find((one) => one.value === changed);
|
|
470
|
+
if (!option) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
this.status.say(taken === undefined ? option.text : formatWords(this, 'selectedLabel', { name: option.text }));
|
|
474
|
+
}
|
|
463
475
|
removeLastToken() {
|
|
464
476
|
const last = this.values.at(-1);
|
|
465
477
|
if (last !== undefined) {
|
|
@@ -10,7 +10,7 @@ export interface DonutShare {
|
|
|
10
10
|
/** Where the slice starts, as a share of the whole. */
|
|
11
11
|
start: number;
|
|
12
12
|
}
|
|
13
|
-
declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
13
|
+
declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartStatusInterface);
|
|
14
14
|
/**
|
|
15
15
|
* @summary A chart that shows parts of a whole as a ring, read from a table.
|
|
16
16
|
*
|
|
@@ -34,6 +34,27 @@ declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args:
|
|
|
34
34
|
* follows, the `live` part says the slice in words, and Escape puts the
|
|
35
35
|
* tooltip away.
|
|
36
36
|
*
|
|
37
|
+
* **A tap shows the slice under it**, and keeps it until a tap lands
|
|
38
|
+
* elsewhere. `helix-reading-change` says each move of the slice shown, by
|
|
39
|
+
* pointer, tap or key, and `helix-reading-press` says a slice was clicked,
|
|
40
|
+
* tapped, or pressed with Enter or Space, for a page that opens what a slice
|
|
41
|
+
* stands for. `index` is the slice's row in the table.
|
|
42
|
+
*
|
|
43
|
+
* **A chart with nothing to draw says so.** A table with no readings shows
|
|
44
|
+
* `empty-text`, "No readings", in the plot, and `loading` shows `loading-text`
|
|
45
|
+
* there until readings arrive. The `empty` and `loading` slots take markup in
|
|
46
|
+
* their place.
|
|
47
|
+
*
|
|
48
|
+
* **Keyboard.** The plot is one tab stop.
|
|
49
|
+
*
|
|
50
|
+
* | Key | What it does |
|
|
51
|
+
* |---|---|
|
|
52
|
+
* | Tab | Moves focus to the plot and shows the first slice. |
|
|
53
|
+
* | Any arrow | Right and Down move to the next slice, Left and Up to the previous. On a right-to-left page, Left moves to the next. |
|
|
54
|
+
* | Home, End | Moves to the first or the last slice. |
|
|
55
|
+
* | Enter, Space | Presses the slice shown, which sends `helix-reading-press`. |
|
|
56
|
+
* | Escape | Puts the slice and its tooltip away. |
|
|
57
|
+
*
|
|
37
58
|
* @since 0.1.0
|
|
38
59
|
* @status stable
|
|
39
60
|
* @category charts
|
|
@@ -41,16 +62,24 @@ declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args:
|
|
|
41
62
|
* @documentation https://fusion.dev/helix/components/donut-chart
|
|
42
63
|
*
|
|
43
64
|
* @slot - Content in the middle of the ring, usually a total. It is read by screen readers, while the ring itself is hidden from them.
|
|
65
|
+
* @slot empty - Content shown in place of the ring when the table has no readings. Falls back to `empty-text`.
|
|
66
|
+
* @slot loading - Content shown in place of the ring while `loading` is set and there are no readings yet. Falls back to `loading-text`.
|
|
67
|
+
*
|
|
68
|
+
* @event {HelixReadingChangeEvent} helix-reading-change - The slice shown moved, or was put away: `index` is the slice's row, or `-1` for none, with its `label` and the `pointerType` that moved it.
|
|
69
|
+
* @event {HelixReadingPressEvent} helix-reading-press - A slice was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
|
|
44
70
|
*
|
|
45
71
|
* @csspart center - The middle of the ring, holding whatever is slotted.
|
|
72
|
+
* @csspart empty - The words in place of the ring when the table has no readings: the `empty` slot, falling back to `empty-text`. Not drawn while the table cannot be read yet, on the server and during hydration.
|
|
46
73
|
* @csspart live - The readout: a polite live region that says the slice the keys moved to. Hide it visually.
|
|
74
|
+
* @csspart loading - The words in place of the ring while `loading` is set and there are no readings: the `loading` slot, falling back to `loading-text`.
|
|
47
75
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
48
|
-
* @csspart plot - The drawing, which takes focus to be read by key.
|
|
76
|
+
* @csspart plot - The drawing, which takes focus to be read by key. With nothing to draw, a group named as the chart is, holding the `empty` or `loading` part.
|
|
49
77
|
* @csspart segment - One slice.
|
|
50
78
|
* @csspart segment-active - The slice under the pointer. Set alongside `segment`.
|
|
51
79
|
* @csspart segment-edge - One slice as a closed shape, used to draw its outline.
|
|
52
80
|
* @csspart slice-label - One slice's share, written on the slice under `slice-labels`. Placed with `left` and `top`, or `inset-inline-start` and `top` on a half ring, and carries its series color as `--chart-series`.
|
|
53
81
|
* @csspart summary - The chart's name and each slice's share, written as a sentence for screen readers. Hide it visually.
|
|
82
|
+
* @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`. Hide it visually.
|
|
54
83
|
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
55
84
|
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
56
85
|
* @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
|
|
@@ -70,6 +99,10 @@ declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args:
|
|
|
70
99
|
* @csspart track - The ring the slices are drawn on.
|
|
71
100
|
*
|
|
72
101
|
* @cssproperty --donut-chart-stroke - How thick the ring is drawn, derived from `hole`.
|
|
102
|
+
*
|
|
103
|
+
* @cssstate active - While a slice is shown, under the pointer or the keys, with its tooltip open.
|
|
104
|
+
* @cssstate empty - While the `empty` part stands in place of the ring.
|
|
105
|
+
* @cssstate loading - While the `loading` part stands in place of the ring.
|
|
73
106
|
*/
|
|
74
107
|
export declare class HelixHeadlessDonutChart extends HelixHeadlessDonutChart_base {
|
|
75
108
|
/**
|
|
@@ -204,6 +237,8 @@ export declare class HelixHeadlessDonutChart extends HelixHeadlessDonutChart_bas
|
|
|
204
237
|
* The shares, because they are what the ring shows and the table does not:
|
|
205
238
|
* the table holds the amounts, and the percentages are this chart's
|
|
206
239
|
* arithmetic. In the ring's order, which is the table's and the legend's.
|
|
240
|
+
* A row the ring leaves out, a negative amount or a slice a legend hid, is
|
|
241
|
+
* left out here too.
|
|
207
242
|
*/
|
|
208
243
|
protected renderSummary(shares: DonutShare[]): TemplateResult;
|
|
209
244
|
/**
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { CHART_PLOT, customElement, drawTooltip, HelixElement, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { CHART_PLOT, customElement, drawTooltip, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, MixinChartSource, MixinChartStatus, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing, svg } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
@@ -30,7 +30,7 @@ const THICKNESS = {
|
|
|
30
30
|
none: EDGE,
|
|
31
31
|
small: 26,
|
|
32
32
|
};
|
|
33
|
-
let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSource(HelixElement) {
|
|
33
|
+
let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartStatus(MixinChartSource(HelixElement)) {
|
|
34
34
|
constructor() {
|
|
35
35
|
super(...arguments);
|
|
36
36
|
this.sliceLabels = false;
|
|
@@ -39,6 +39,11 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
|
|
|
39
39
|
connectedCallback() {
|
|
40
40
|
super.connectedCallback();
|
|
41
41
|
this.pointer.axis = 'none';
|
|
42
|
+
this.pointer.onReading = (heard, detail) => {
|
|
43
|
+
const row = this.shares[detail.index]?.row ?? -1;
|
|
44
|
+
const told = { ...detail, index: row, label: this.sourceRows[row]?.label ?? '' };
|
|
45
|
+
this.dispatchEvent(heard === 'press' ? new HelixReadingPressEvent(told) : new HelixReadingChangeEvent(told));
|
|
46
|
+
};
|
|
42
47
|
}
|
|
43
48
|
get span() {
|
|
44
49
|
return this.sweep === 'half' ? 0.5 : 1;
|
|
@@ -81,28 +86,32 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
|
|
|
81
86
|
}
|
|
82
87
|
render() {
|
|
83
88
|
const shares = this.shares;
|
|
84
|
-
|
|
89
|
+
if (shares.length === 0 && (this.readingsKnown || this.loading)) {
|
|
90
|
+
return this.renderEmpty();
|
|
91
|
+
}
|
|
92
|
+
return html `${this.renderPlot(shares)}${shares.length === 0 ? nothing : this.renderSummary(shares)}${this.renderTable()}`;
|
|
85
93
|
}
|
|
86
94
|
renderSummary(shares) {
|
|
87
95
|
const rows = this.sourceRows;
|
|
88
96
|
const locale = wordsLocale(this);
|
|
89
97
|
const parts = shares
|
|
90
|
-
.filter((share) =>
|
|
98
|
+
.filter((share) => share.fraction > 0)
|
|
91
99
|
.map((share) => `${rows[share.row]?.label ?? ''} ${percentIn(locale).format(share.fraction)}`);
|
|
92
100
|
const named = this.named ? `${this.named}: ` : '';
|
|
93
101
|
const listed = new Intl.ListFormat(locale, { style: 'short', type: 'unit' }).format(parts);
|
|
94
102
|
return html `<p id="helix-chart-summary" part="summary">${named}${listed}.</p>`;
|
|
95
103
|
}
|
|
96
104
|
renderPlot(shares) {
|
|
105
|
+
const drawn = shares.length > 0;
|
|
97
106
|
return html `<div
|
|
98
|
-
aria-describedby=${this.named ? `helix-chart-summary${this.hole === 'none' ? '' : ' helix-chart-center'}` : nothing}
|
|
107
|
+
aria-describedby=${drawn && this.named ? `helix-chart-summary${this.hole === 'none' ? '' : ' helix-chart-center'}` : nothing}
|
|
99
108
|
aria-label=${this.named || nothing}
|
|
100
|
-
aria-labelledby=${this.named ?
|
|
109
|
+
aria-labelledby=${drawn && !this.named ? 'helix-chart-summary' : nothing}
|
|
101
110
|
id=${CHART_PLOT}
|
|
102
111
|
part="plot"
|
|
103
|
-
role
|
|
112
|
+
role=${drawn ? 'application' : this.named ? 'group' : nothing}
|
|
104
113
|
style="--donut-chart-stroke: ${this.thickness}"
|
|
105
|
-
tabindex
|
|
114
|
+
tabindex=${drawn ? '0' : nothing}
|
|
106
115
|
>
|
|
107
116
|
<svg
|
|
108
117
|
aria-hidden="true"
|
|
@@ -175,6 +184,11 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
|
|
|
175
184
|
this.pointer.set(index);
|
|
176
185
|
}
|
|
177
186
|
}}
|
|
187
|
+
@pointerup=${(event) => {
|
|
188
|
+
if (event.pointerType === 'touch') {
|
|
189
|
+
this.pointer.tapTo(index);
|
|
190
|
+
}
|
|
191
|
+
}}
|
|
178
192
|
part="segment${active ? ' segment-active' : ''}"
|
|
179
193
|
r=${this.radius}
|
|
180
194
|
stroke-dasharray="${length} ${GAP}"
|
|
@@ -108,6 +108,7 @@ registerElementEvents({
|
|
|
108
108
|
],
|
|
109
109
|
'helix-headless-date-picker': ['change', 'helix-before-change', 'helix-navigate', 'input'],
|
|
110
110
|
'helix-headless-dialog': ['close', 'helix-before-close', 'helix-before-open', 'toggle'],
|
|
111
|
+
'helix-headless-donut-chart': ['helix-reading-change', 'helix-reading-press'],
|
|
111
112
|
'helix-headless-drawer': ['close', 'helix-before-close', 'helix-before-open', 'toggle'],
|
|
112
113
|
'helix-headless-drilldown': ['helix-navigate', 'helix-select'],
|
|
113
114
|
'helix-headless-drilldown-item': ['helix-select'],
|
|
@@ -249,6 +250,7 @@ registerElementEvents({
|
|
|
249
250
|
'toggle',
|
|
250
251
|
],
|
|
251
252
|
'helix-headless-slider': ['change', 'helix-before-change', 'input'],
|
|
253
|
+
'helix-headless-sparkline': ['helix-reading-change', 'helix-reading-press'],
|
|
252
254
|
'helix-headless-speed-dial': [
|
|
253
255
|
'helix-before-close',
|
|
254
256
|
'helix-before-open',
|
|
@@ -360,8 +360,9 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
|
|
|
360
360
|
*
|
|
361
361
|
* Not through `commit`, and that is the point of the split: a reset is the
|
|
362
362
|
* form's decision and there is nothing here for a listener to refuse. What it
|
|
363
|
-
* owes is the report
|
|
364
|
-
* `
|
|
363
|
+
* owes is the report to the form, and no `input` or `change`: a native file
|
|
364
|
+
* input sends neither on `form.reset()`, measured in Chromium, where this
|
|
365
|
+
* sent both.
|
|
365
366
|
*/
|
|
366
367
|
formResetCallback(): void;
|
|
367
368
|
/**
|
|
@@ -224,10 +224,13 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
224
224
|
}
|
|
225
225
|
control.files = holding.files;
|
|
226
226
|
}
|
|
227
|
-
settle() {
|
|
227
|
+
settle(announce = true) {
|
|
228
228
|
this.committed = this.files;
|
|
229
229
|
this.sync();
|
|
230
230
|
this.requestUpdate();
|
|
231
|
+
if (!announce) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
231
234
|
this.announceInput();
|
|
232
235
|
this.announceChange();
|
|
233
236
|
}
|
|
@@ -265,7 +268,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
265
268
|
if (this.control) {
|
|
266
269
|
restoreValue(this.control, '');
|
|
267
270
|
}
|
|
268
|
-
this.settle();
|
|
271
|
+
this.settle(false);
|
|
269
272
|
}
|
|
270
273
|
renderMeta(file) {
|
|
271
274
|
return html `<${this.tag('tag')} part="size"
|