@fusion.dev/helix-headless 0.149.1 → 0.151.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 +1211 -110
- package/dist/area-chart/root/area-chart-root.d.ts +36 -0
- package/dist/area-chart/root/area-chart-root.js +17 -2
- package/dist/bar-chart/root/bar-chart-root.d.ts +28 -4
- package/dist/bar-chart/root/bar-chart-root.js +17 -4
- package/dist/button/root/button-root.d.ts +5 -0
- package/dist/button/root/button-root.js +3 -0
- package/dist/callout/root/callout-root.d.ts +20 -0
- package/dist/callout/root/callout-root.js +19 -4
- package/dist/chat/composer/chat-composer.d.ts +30 -0
- package/dist/chat/composer/chat-composer.js +47 -2
- package/dist/chat/root/chat-root.d.ts +10 -0
- package/dist/chat/root/chat-root.js +14 -0
- package/dist/color-input/root/color-input-root.d.ts +1 -0
- package/dist/color-picker/root/color-picker-root.js +1 -0
- package/dist/combobox/root/combobox-root.d.ts +12 -1
- package/dist/combobox/root/combobox-root.js +8 -0
- package/dist/copy-button/root/copy-button-root.d.ts +4 -3
- package/dist/copy-button/root/copy-button-root.js +5 -1
- package/dist/data-table/root/data-table-root.d.ts +34 -2
- package/dist/data-table/root/data-table-root.js +63 -2
- package/dist/date-input/root/date-input-root.d.ts +1 -0
- package/dist/divider/root/divider-root.d.ts +8 -6
- package/dist/divider/root/divider-root.js +10 -2
- package/dist/element-events.generated.js +5 -1
- package/dist/filter-input/chip/filter-chip.d.ts +2 -1
- package/dist/filter-input/root/filter-input-root.d.ts +5 -0
- package/dist/filter-input/root/filter-input-root.js +7 -0
- package/dist/kbd/root/kbd-root.d.ts +7 -1
- package/dist/kbd/root/kbd-root.js +8 -4
- package/dist/line-chart/root/line-chart-root.d.ts +36 -0
- package/dist/line-chart/root/line-chart-root.js +15 -2
- package/dist/number-input/root/number-input-root.d.ts +6 -0
- package/dist/number-input/root/number-input-root.js +14 -3
- package/dist/option/root/option-root.d.ts +17 -8
- package/dist/option/root/option-root.js +18 -4
- package/dist/radio/group/radio-group.d.ts +8 -1
- package/dist/radio/group/radio-group.js +15 -1
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +18 -0
- package/dist/scatter-chart/root/scatter-chart-root.js +38 -15
- package/dist/slider/root/slider-root.d.ts +8 -0
- package/dist/slider/root/slider-root.js +6 -3
- package/dist/table/column/table-column.js +3 -2
- package/dist/table/grid-controller.d.ts +14 -2
- package/dist/table/grid-controller.js +27 -1
- package/dist/table/root/table-root.d.ts +30 -4
- package/dist/table/root/table-root.js +45 -7
- package/dist/table/row/table-row.d.ts +12 -2
- package/dist/table/row/table-row.js +8 -2
- package/dist/tag/group/tag-group.d.ts +22 -1
- package/dist/tag/group/tag-group.js +63 -3
- package/dist/tag/root/tag-root.d.ts +9 -0
- package/dist/tag/root/tag-root.js +5 -1
- package/dist/time-input/root/time-input-root.d.ts +1 -0
- package/dist/toggle/group/toggle-group.d.ts +6 -0
- package/dist/toggle/group/toggle-group.js +16 -1
- package/dist/toggle/root/toggle-root.d.ts +21 -3
- package/dist/toggle/root/toggle-root.js +22 -5
- package/dist/video/root/video-root.d.ts +6 -0
- package/dist/video/root/video-root.js +5 -1
- package/dist/video-playlist/root/video-playlist-root.d.ts +4 -0
- package/dist/video-playlist/root/video-playlist-root.js +4 -1
- package/dist/virtual-list/root/virtual-list-root.d.ts +23 -1
- package/dist/virtual-list/root/virtual-list-root.js +21 -2
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +808 -156
- package/types/vue.d.ts +223 -59
- package/types/words.d.ts +4 -2
|
@@ -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 { byRow, CHART_PLOT, commaList, customElement, drawTooltip, extent, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, ticks, unbroken, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { byRow, CHART_PLOT, commaList, customElement, drawTooltip, drawValueAxis, extent, formatNumbers, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, ticks, unbroken, 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';
|
|
@@ -14,9 +14,16 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
|
|
|
14
14
|
this.area = false;
|
|
15
15
|
this.dashed = '';
|
|
16
16
|
this.markers = false;
|
|
17
|
+
this.valueAxis = false;
|
|
17
18
|
this.zero = false;
|
|
18
19
|
}
|
|
19
20
|
static { this.words = 'helix-line-chart'; }
|
|
21
|
+
connectedCallback() {
|
|
22
|
+
super.connectedCallback();
|
|
23
|
+
this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
|
|
24
|
+
? new HelixReadingPressEvent(detail)
|
|
25
|
+
: new HelixReadingChangeEvent(detail));
|
|
26
|
+
}
|
|
20
27
|
get rulesValue() {
|
|
21
28
|
return this.gridlines !== undefined && this.gridlines !== 'category';
|
|
22
29
|
}
|
|
@@ -58,7 +65,10 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
|
|
|
58
65
|
const y = linear(domain, [PLOT_HEIGHT, 0]);
|
|
59
66
|
const slot = PLOT_WIDTH / readings.length;
|
|
60
67
|
const x = linear([0, Math.max(readings.length - 1, 1)], [slot / 2, PLOT_WIDTH - slot / 2]);
|
|
61
|
-
return html `${this.renderPlot(series, x, y, domain)} ${this.renderLabels(readings)}${this.renderSummary(series)}`;
|
|
68
|
+
return html `${this.valueAxis ? this.renderAxis(y, domain) : nothing}${this.renderPlot(series, x, y, domain)} ${this.renderLabels(readings)}${this.renderSummary(series)}`;
|
|
69
|
+
}
|
|
70
|
+
renderAxis(y, domain) {
|
|
71
|
+
return drawValueAxis(domain, (value) => (y(value) / PLOT_HEIGHT) * 100, formatNumbers(wordsLocale(this)), (widest) => `--line-chart-axis: ${widest}ch`);
|
|
62
72
|
}
|
|
63
73
|
renderSummary(series) {
|
|
64
74
|
const rows = this.sourceRows;
|
|
@@ -254,6 +264,9 @@ __decorate([
|
|
|
254
264
|
__decorate([
|
|
255
265
|
property({ reflect: true })
|
|
256
266
|
], HelixHeadlessLineChart.prototype, "terminus", void 0);
|
|
267
|
+
__decorate([
|
|
268
|
+
property({ attribute: 'value-axis', reflect: true, type: Boolean })
|
|
269
|
+
], HelixHeadlessLineChart.prototype, "valueAxis", void 0);
|
|
257
270
|
__decorate([
|
|
258
271
|
property({ reflect: true, type: Boolean })
|
|
259
272
|
], HelixHeadlessLineChart.prototype, "zero", void 0);
|
|
@@ -381,6 +381,8 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
|
|
|
381
381
|
* saving on `change` kept 150 while the form submitted 100.
|
|
382
382
|
*/
|
|
383
383
|
private readonly handleChange;
|
|
384
|
+
/** The value Enter settled and announced, until the field's `change` or the next keystroke. */
|
|
385
|
+
private enterCommit;
|
|
384
386
|
/**
|
|
385
387
|
* The four keys a spinner answers, which `Input` does not bind.
|
|
386
388
|
*
|
|
@@ -423,6 +425,10 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
|
|
|
423
425
|
*
|
|
424
426
|
* On `keypress`, where the platform runs its own implicit submission, so a
|
|
425
427
|
* consumer who prevents the `keydown` for Enter stops this too.
|
|
428
|
+
*
|
|
429
|
+
* **Settled first**, as blur settles it. The typed text is read as a number,
|
|
430
|
+
* `change` announces it, and the form value is written before the form is
|
|
431
|
+
* submitted, so a field showing `$2,500.50` submits 2500.5.
|
|
426
432
|
*/
|
|
427
433
|
private readonly handleKeypress;
|
|
428
434
|
/** @internal A single line of text counts against implicit submission. */
|
|
@@ -96,6 +96,7 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
96
96
|
};
|
|
97
97
|
this.handleInput = (event) => {
|
|
98
98
|
event.stopPropagation();
|
|
99
|
+
this.enterCommit = undefined;
|
|
99
100
|
const control = event.target;
|
|
100
101
|
this.requestValue(control.value);
|
|
101
102
|
restoreValue(control, this.shown);
|
|
@@ -109,7 +110,11 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
109
110
|
this.handleChange = (event) => {
|
|
110
111
|
event.stopPropagation();
|
|
111
112
|
this.settle();
|
|
112
|
-
this.
|
|
113
|
+
const announced = this.enterCommit === this.value;
|
|
114
|
+
this.enterCommit = undefined;
|
|
115
|
+
if (!announced) {
|
|
116
|
+
this.announceChange();
|
|
117
|
+
}
|
|
113
118
|
};
|
|
114
119
|
this.handleKeydown = (event) => {
|
|
115
120
|
if (event.key === 'ArrowUp') {
|
|
@@ -142,9 +147,15 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
142
147
|
event.preventDefault();
|
|
143
148
|
};
|
|
144
149
|
this.handleKeypress = (event) => {
|
|
145
|
-
if (event.key
|
|
146
|
-
|
|
150
|
+
if (event.key !== 'Enter' || event.defaultPrevented) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (this.settle()) {
|
|
154
|
+
this.sync();
|
|
155
|
+
this.enterCommit = this.value;
|
|
156
|
+
this.announceChange();
|
|
147
157
|
}
|
|
158
|
+
submitImplicitly(this.form);
|
|
148
159
|
};
|
|
149
160
|
}
|
|
150
161
|
static { this.words = 'helix-number-input'; }
|
|
@@ -44,7 +44,9 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
|
|
|
44
44
|
* @documentation https://fusion.dev/helix/components/option
|
|
45
45
|
*
|
|
46
46
|
* @slot - The option's label.
|
|
47
|
+
* @slot description - A second line under the label, for content richer than the `description` text.
|
|
47
48
|
* @slot end - Content after the label, such as a shortcut or a count.
|
|
49
|
+
* @slot indicator - An icon shown while the option is chosen, in place of the check the styled option draws.
|
|
48
50
|
* @slot start - Content before the label, such as an icon or an avatar.
|
|
49
51
|
*
|
|
50
52
|
* @event {HelixSelectEvent} helix-select - The option was chosen. Cancelable: canceling it leaves the selection as it was, and keeps the browser from following an option's `href`.
|
|
@@ -57,6 +59,8 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
|
|
|
57
59
|
* @csspart text - The label and the description under it, as one column.
|
|
58
60
|
*
|
|
59
61
|
* @cssstate active - While the keyboard is on the option, which is the option `aria-activedescendant` names.
|
|
62
|
+
* @cssstate disabled - While the option cannot be chosen.
|
|
63
|
+
* @cssstate has-indicator - An icon is slotted into `indicator`.
|
|
60
64
|
* @cssstate selected - While the option is chosen.
|
|
61
65
|
*/
|
|
62
66
|
export declare class HelixHeadlessOption extends HelixElement {
|
|
@@ -136,14 +140,10 @@ export declare class HelixHeadlessOption extends HelixElement {
|
|
|
136
140
|
* ends. Matching still reads the label, so what somebody types is matched
|
|
137
141
|
* against the name rather than against prose about it.
|
|
138
142
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* filled, so every option without a caption would carry an empty line. The
|
|
144
|
-
* detection that would avoid it does not exist, `:host(:has())` being invalid
|
|
145
|
-
* and the server having no children to inspect. A property is known at render
|
|
146
|
-
* time, so the component can decline to draw the box at all.
|
|
143
|
+
* For a caption richer than a string, slot it into `description`. The
|
|
144
|
+
* property is that slot's fallback inside the `description` part. With no
|
|
145
|
+
* property the slot is drawn bare, which takes no box while it is empty, so
|
|
146
|
+
* an option without a caption carries no empty line.
|
|
147
147
|
*
|
|
148
148
|
* It does not reflect, per the rule that payload state stays off the host: a
|
|
149
149
|
* string is not a fact a selector needs.
|
|
@@ -198,6 +198,15 @@ export declare class HelixHeadlessOption extends HelixElement {
|
|
|
198
198
|
* Written by the listbox.
|
|
199
199
|
*/
|
|
200
200
|
selected: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Whether the option says it is selected while the keyboard is on it.
|
|
203
|
+
*
|
|
204
|
+
* Written by a control whose list holds suggestions, such as `FilterInput`:
|
|
205
|
+
* nothing there stays chosen, so the row the keyboard is on is the
|
|
206
|
+
* selection, as the APG combobox's list with selection that follows focus
|
|
207
|
+
* says. No attribute: it is coordination between elements.
|
|
208
|
+
*/
|
|
209
|
+
selectionFollowsActive: boolean;
|
|
201
210
|
/**
|
|
202
211
|
* Where the link opens, such as `_blank`, for an option with an `href`.
|
|
203
212
|
*/
|
|
@@ -4,8 +4,8 @@ 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 { customElement, followLink, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { html
|
|
7
|
+
import { customElement, followLink, HelixElement, HelixSelectEvent, slotFilled, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
export const optionsAmong = (elements) => {
|
|
11
11
|
const isOption = (element) => 'text' in element && 'value' in element && 'selected' in element;
|
|
@@ -23,6 +23,7 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
|
|
|
23
23
|
this.label = '';
|
|
24
24
|
this.rel = '';
|
|
25
25
|
this.selected = false;
|
|
26
|
+
this.selectionFollowsActive = false;
|
|
26
27
|
this.target = '';
|
|
27
28
|
this.textValue = '';
|
|
28
29
|
this.value = '';
|
|
@@ -59,10 +60,14 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
|
|
|
59
60
|
super.willUpdate(changed);
|
|
60
61
|
this.toggleState('selected', this.selected);
|
|
61
62
|
this.toggleState('active', this.active);
|
|
63
|
+
this.toggleState('disabled', this.disabled);
|
|
62
64
|
}
|
|
63
65
|
render() {
|
|
64
66
|
this.internals.role = 'option';
|
|
65
|
-
this.internals.ariaSelected =
|
|
67
|
+
this.internals.ariaSelected =
|
|
68
|
+
this.href === ''
|
|
69
|
+
? String(this.selected || (this.selectionFollowsActive && this.active))
|
|
70
|
+
: null;
|
|
66
71
|
this.internals.ariaDisabled = this.disabled ? 'true' : null;
|
|
67
72
|
this.internals.ariaDescription =
|
|
68
73
|
this.alternate !== '' && this.alternateLabel !== '' ? this.alternateLabel : null;
|
|
@@ -73,9 +78,15 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
|
|
|
73
78
|
<slot name="start"></slot>
|
|
74
79
|
<span part="text">
|
|
75
80
|
<span part="label"><slot>${this.label}</slot></span>
|
|
76
|
-
${this.description
|
|
81
|
+
${this.description
|
|
82
|
+
? html `<span part="description"><slot name="description">${this.description}</slot></span>`
|
|
83
|
+
: html `<slot name="description"></slot>`}
|
|
77
84
|
</span>
|
|
78
85
|
<slot name="end"></slot>
|
|
86
|
+
<slot
|
|
87
|
+
name="indicator"
|
|
88
|
+
@slotchange=${(event) => this.toggleState('has-indicator', slotFilled(event.target))}
|
|
89
|
+
></slot>
|
|
79
90
|
</div>`;
|
|
80
91
|
}
|
|
81
92
|
};
|
|
@@ -109,6 +120,9 @@ __decorate([
|
|
|
109
120
|
__decorate([
|
|
110
121
|
property({ type: Boolean })
|
|
111
122
|
], HelixHeadlessOption.prototype, "selected", void 0);
|
|
123
|
+
__decorate([
|
|
124
|
+
property({ attribute: false })
|
|
125
|
+
], HelixHeadlessOption.prototype, "selectionFollowsActive", void 0);
|
|
112
126
|
__decorate([
|
|
113
127
|
property()
|
|
114
128
|
], HelixHeadlessOption.prototype, "target", void 0);
|
|
@@ -114,8 +114,15 @@ export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_bas
|
|
|
114
114
|
valueMissingMessage: string;
|
|
115
115
|
protected readonly keys: KeyboardNavigationController;
|
|
116
116
|
protected readonly cascade: DisabledCascadeController;
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* The radios, in light-DOM order. Client only: the server has no tree.
|
|
119
|
+
*
|
|
120
|
+
* Descendants, as `Checkbox.Group` reads its boxes, so a radio inside a
|
|
121
|
+
* layout wrapper of yours is one of the group's. A radio held by a group
|
|
122
|
+
* nested inside this one is that group's.
|
|
123
|
+
*/
|
|
118
124
|
get items(): HelixHeadlessRadio[];
|
|
125
|
+
private observer;
|
|
119
126
|
/**
|
|
120
127
|
* Announces before it applies, so a listener can refuse the change.
|
|
121
128
|
*
|
|
@@ -42,7 +42,10 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
|
|
|
42
42
|
}
|
|
43
43
|
static { this.words = 'helix-radio-group'; }
|
|
44
44
|
get items() {
|
|
45
|
-
|
|
45
|
+
if (this.children === undefined) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
return [...this.querySelectorAll('*')].filter((element) => element instanceof HelixHeadlessRadio && nearestGroup(element) === this);
|
|
46
49
|
}
|
|
47
50
|
get answer() {
|
|
48
51
|
return this.items.find((item) => item.value === this.value);
|
|
@@ -91,10 +94,14 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
|
|
|
91
94
|
connectedCallback() {
|
|
92
95
|
super.connectedCallback();
|
|
93
96
|
this.addEventListener('helix-select', this.handleSelection);
|
|
97
|
+
this.observer = new MutationObserver(() => this.requestUpdate());
|
|
98
|
+
this.observer.observe(this, { childList: true, subtree: true });
|
|
94
99
|
}
|
|
95
100
|
disconnectedCallback() {
|
|
96
101
|
super.disconnectedCallback();
|
|
97
102
|
this.removeEventListener('helix-select', this.handleSelection);
|
|
103
|
+
this.observer?.disconnect();
|
|
104
|
+
this.observer = undefined;
|
|
98
105
|
}
|
|
99
106
|
willUpdate(changed) {
|
|
100
107
|
super.willUpdate(changed);
|
|
@@ -134,3 +141,10 @@ HelixHeadlessRadioGroup = __decorate([
|
|
|
134
141
|
customElement('helix-headless-radio-group')
|
|
135
142
|
], HelixHeadlessRadioGroup);
|
|
136
143
|
export { HelixHeadlessRadioGroup };
|
|
144
|
+
function nearestGroup(radio) {
|
|
145
|
+
let parent = radio.parentElement;
|
|
146
|
+
while (parent && !(parent instanceof HelixHeadlessRadioGroup)) {
|
|
147
|
+
parent = parent.parentElement;
|
|
148
|
+
}
|
|
149
|
+
return parent;
|
|
150
|
+
}
|
|
@@ -66,12 +66,20 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
|
|
|
66
66
|
* reads in its own direction. The summary and the tooltip bubble follow the
|
|
67
67
|
* page's direction.
|
|
68
68
|
*
|
|
69
|
+
* **A tap shows the reading under it**, and keeps it until a tap lands
|
|
70
|
+
* elsewhere. `helix-reading-change` says each move of the reading, by pointer,
|
|
71
|
+
* tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
|
|
72
|
+
* pressed with Enter or Space, for a page that opens what a reading stands for.
|
|
73
|
+
*
|
|
69
74
|
* @since 0.1.0
|
|
70
75
|
* @status stable
|
|
71
76
|
* @category charts
|
|
72
77
|
* @aliases Scatter Plot, XY Chart, Correlation Chart, Dot Plot
|
|
73
78
|
* @documentation https://fusion.dev/helix/components/scatter-chart
|
|
74
79
|
*
|
|
80
|
+
* @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
|
|
81
|
+
* @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
|
|
82
|
+
*
|
|
75
83
|
* @csspart gridline - One line across the plot, drawn when `gridlines` is set.
|
|
76
84
|
* @csspart live - The readout: a polite live region that says the row the keys moved to. Hide it visually.
|
|
77
85
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
@@ -238,6 +246,16 @@ export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart
|
|
|
238
246
|
* rest on a point with.
|
|
239
247
|
*/
|
|
240
248
|
private readonly handleMove;
|
|
249
|
+
/**
|
|
250
|
+
* The point a tap lands nearest, within twice a pointer's reach, a finger
|
|
251
|
+
* being broader than a cursor. It stays shown until a tap lands elsewhere.
|
|
252
|
+
*/
|
|
253
|
+
private readonly handleTap;
|
|
254
|
+
/**
|
|
255
|
+
* The drawn point nearest a pointer, within `reach` pixels, `undefined` for
|
|
256
|
+
* none in reach, and `null` with no plot to measure.
|
|
257
|
+
*/
|
|
258
|
+
private pointNear;
|
|
241
259
|
private readonly handleLeave;
|
|
242
260
|
}
|
|
243
261
|
declare global {
|
|
@@ -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, extent, formatNumbers, formatWords, HelixElement, isRtl, linear, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, isRtl, linear, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, words, 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';
|
|
@@ -47,28 +47,27 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
|
|
|
47
47
|
if (event.pointerType !== 'mouse' && event.pointerType !== 'pen') {
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
if (
|
|
50
|
+
const found = this.pointNear(event, REACH);
|
|
51
|
+
if (found === null) {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
const across = event.clientX - box.left;
|
|
55
|
-
const up = event.clientY - box.top;
|
|
56
|
-
let found;
|
|
57
|
-
let best = REACH ** 2;
|
|
58
|
-
for (const point of this.drawn) {
|
|
59
|
-
const distance = ((point.x / PLOT_WIDTH) * box.width - across) ** 2 +
|
|
60
|
-
((point.y / PLOT_HEIGHT) * box.height - up) ** 2;
|
|
61
|
-
if (distance <= best) {
|
|
62
|
-
best = distance;
|
|
63
|
-
found = point;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
54
|
if (found !== this.nearest) {
|
|
67
55
|
this.nearest = found;
|
|
68
56
|
this.pointer.set(found?.row ?? -1);
|
|
69
57
|
this.requestUpdate();
|
|
70
58
|
}
|
|
71
59
|
};
|
|
60
|
+
this.handleTap = (event) => {
|
|
61
|
+
if (event.pointerType !== 'touch') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const found = this.pointNear(event, REACH * 2);
|
|
65
|
+
if (found) {
|
|
66
|
+
this.nearest = found;
|
|
67
|
+
this.pointer.tapTo(found.row);
|
|
68
|
+
this.requestUpdate();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
72
71
|
this.handleLeave = () => {
|
|
73
72
|
this.nearest = undefined;
|
|
74
73
|
};
|
|
@@ -77,13 +76,18 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
|
|
|
77
76
|
connectedCallback() {
|
|
78
77
|
super.connectedCallback();
|
|
79
78
|
this.pointer.axis = 'none';
|
|
79
|
+
this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
|
|
80
|
+
? new HelixReadingPressEvent(detail)
|
|
81
|
+
: new HelixReadingChangeEvent(detail));
|
|
80
82
|
this.addEventListener('pointermove', this.handleMove);
|
|
81
83
|
this.addEventListener('pointerleave', this.handleLeave);
|
|
84
|
+
this.addEventListener('pointerup', this.handleTap);
|
|
82
85
|
}
|
|
83
86
|
disconnectedCallback() {
|
|
84
87
|
super.disconnectedCallback();
|
|
85
88
|
this.removeEventListener('pointermove', this.handleMove);
|
|
86
89
|
this.removeEventListener('pointerleave', this.handleLeave);
|
|
90
|
+
this.removeEventListener('pointerup', this.handleTap);
|
|
87
91
|
}
|
|
88
92
|
seriesOf(cell) {
|
|
89
93
|
const depth = this.measures.ups.indexOf(cell);
|
|
@@ -253,6 +257,25 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
|
|
|
253
257
|
y: `${point.y}%`,
|
|
254
258
|
}, (cell) => this.seriesOf(cell), this.pointer);
|
|
255
259
|
}
|
|
260
|
+
pointNear(event, reach) {
|
|
261
|
+
const box = this.partNamed('plot')?.getBoundingClientRect();
|
|
262
|
+
if (!box || box.width === 0 || box.height === 0) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const across = event.clientX - box.left;
|
|
266
|
+
const up = event.clientY - box.top;
|
|
267
|
+
let found;
|
|
268
|
+
let best = reach ** 2;
|
|
269
|
+
for (const point of this.drawn) {
|
|
270
|
+
const distance = ((point.x / PLOT_WIDTH) * box.width - across) ** 2 +
|
|
271
|
+
((point.y / PLOT_HEIGHT) * box.height - up) ** 2;
|
|
272
|
+
if (distance <= best) {
|
|
273
|
+
best = distance;
|
|
274
|
+
found = point;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return found;
|
|
278
|
+
}
|
|
256
279
|
};
|
|
257
280
|
__decorate([
|
|
258
281
|
words,
|
|
@@ -290,6 +290,14 @@ export declare class HelixHeadlessSlider extends HelixHeadlessSlider_base {
|
|
|
290
290
|
* from the origin to the thumb as `--slider-from` and `--slider-to`.
|
|
291
291
|
*/
|
|
292
292
|
private get fillStyle();
|
|
293
|
+
/**
|
|
294
|
+
* The range's axis, written on it, so a vertical slider is vertical to the
|
|
295
|
+
* accessibility tree with no stylesheet: Chromium reads a range's
|
|
296
|
+
* orientation off its writing mode and ignores `aria-orientation` there.
|
|
297
|
+
* `direction: rtl` puts the maximum at the top, which `vertical-lr` alone
|
|
298
|
+
* puts at the foot.
|
|
299
|
+
*/
|
|
300
|
+
private get axis();
|
|
293
301
|
/**
|
|
294
302
|
* A value snapped to the step, counted from `min` the way a range counts it.
|
|
295
303
|
*
|
|
@@ -112,10 +112,13 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
112
112
|
get fillStyle() {
|
|
113
113
|
const fraction = this.fraction;
|
|
114
114
|
if (!this.origin) {
|
|
115
|
-
return `--slider-fraction: ${fraction}`;
|
|
115
|
+
return `--slider-fraction: ${fraction}${this.axis}`;
|
|
116
116
|
}
|
|
117
117
|
const at = { center: 0.5, end: 1, start: 0 }[this.origin] ?? 0;
|
|
118
|
-
return `--slider-fraction: ${fraction}; --slider-from: ${Math.min(at, fraction)}; --slider-to: ${Math.max(at, fraction)}`;
|
|
118
|
+
return `--slider-fraction: ${fraction}; --slider-from: ${Math.min(at, fraction)}; --slider-to: ${Math.max(at, fraction)}${this.axis}`;
|
|
119
|
+
}
|
|
120
|
+
get axis() {
|
|
121
|
+
return this.orientation === 'vertical' ? '; writing-mode: vertical-lr; direction: rtl' : '';
|
|
119
122
|
}
|
|
120
123
|
snapped(at) {
|
|
121
124
|
if (!(this.step > 0)) {
|
|
@@ -208,7 +211,7 @@ let HelixHeadlessSlider = class HelixHeadlessSlider extends MixinControllerValue
|
|
|
208
211
|
id=${which === 'start' ? 'control' : 'control-end'}
|
|
209
212
|
data-thumb=${which}
|
|
210
213
|
type="range"
|
|
211
|
-
style="--slider-from: ${this.along(start)}; --slider-to: ${this.along(end)}"
|
|
214
|
+
style="--slider-from: ${this.along(start)}; --slider-to: ${this.along(end)}${this.axis}"
|
|
212
215
|
min=${this.min}
|
|
213
216
|
max=${this.max}
|
|
214
217
|
step=${this.step}
|
|
@@ -174,6 +174,7 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
174
174
|
const named = { name, named: name ? 'yes' : 'no' };
|
|
175
175
|
const grid = this.gridCell;
|
|
176
176
|
const control = grid ? '-1' : nothing;
|
|
177
|
+
const width = Math.min(4096, Math.max(HelixHeadlessTableColumn_1.NARROWEST, this.drawn));
|
|
177
178
|
return html `<span
|
|
178
179
|
part="column"
|
|
179
180
|
role=${grid ? 'columnheader' : nothing}
|
|
@@ -197,8 +198,8 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
197
198
|
aria-orientation="vertical"
|
|
198
199
|
aria-valuemin=${HelixHeadlessTableColumn_1.NARROWEST}
|
|
199
200
|
aria-valuemax="4096"
|
|
200
|
-
aria-valuenow=${
|
|
201
|
-
aria-valuetext=${formatWords(this, 'widthLabel', { width
|
|
201
|
+
aria-valuenow=${width}
|
|
202
|
+
aria-valuetext=${formatWords(this, 'widthLabel', { width })}
|
|
202
203
|
@keydown=${this.handleResizeKey}
|
|
203
204
|
@pointerdown=${this.handleResizeStart}
|
|
204
205
|
@pointermove=${this.handleResizeMove}
|
|
@@ -24,6 +24,16 @@ export interface TableGridOptions {
|
|
|
24
24
|
part: (name: string) => HTMLElement | null;
|
|
25
25
|
/** The rows in the order they are drawn. */
|
|
26
26
|
rows: () => Element[];
|
|
27
|
+
/**
|
|
28
|
+
* The words each row is found by when a reader types, in the order the rows
|
|
29
|
+
* are drawn.
|
|
30
|
+
*/
|
|
31
|
+
rowWords: () => string[];
|
|
32
|
+
/**
|
|
33
|
+
* Selects every row from the row last chosen, or from row `from` where none
|
|
34
|
+
* was, to row `to`, and says whether the table selects at all.
|
|
35
|
+
*/
|
|
36
|
+
selectRange: (to: number, from: number) => boolean;
|
|
27
37
|
/** Selects every row for Control or Command with A, and says whether it did. */
|
|
28
38
|
selectAll: () => boolean;
|
|
29
39
|
/** Whether the table is a grid now. */
|
|
@@ -31,8 +41,8 @@ export interface TableGridOptions {
|
|
|
31
41
|
}
|
|
32
42
|
/**
|
|
33
43
|
* The keyboard of a `grid` table: one tab stop among its cells, the arrow
|
|
34
|
-
* keys, Home, End and the Page keys between them,
|
|
35
|
-
*
|
|
44
|
+
* keys, Home, End and the Page keys between them, Enter or F2 into a cell's
|
|
45
|
+
* controls, typeahead over the rows, and Shift ranges of rows.
|
|
36
46
|
*
|
|
37
47
|
* **Tab leaves the grid.** The controls in a cell are the consumer's, so their
|
|
38
48
|
* tab order is left as written: Tab on a cell, or past the last control of a
|
|
@@ -59,6 +69,8 @@ export declare class TableGridController implements ReactiveController {
|
|
|
59
69
|
private pointing;
|
|
60
70
|
/** The cells given a `tabindex`, so a table that stops being a grid takes it back. */
|
|
61
71
|
private readonly given;
|
|
72
|
+
/** The letters a reader has typed at the rows. */
|
|
73
|
+
private readonly typeahead;
|
|
62
74
|
constructor(host: ReactiveControllerHost & HTMLElement, options: TableGridOptions);
|
|
63
75
|
hostConnected(): void;
|
|
64
76
|
hostDisconnected(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepActiveElement, deepContains, focusableElements, gridMove, inlineStep, pageRows, scrollingAncestor, scrollRoom, selectsAll, whenHydrated, } from '@fusion.dev/helix-core';
|
|
1
|
+
import { deepActiveElement, deepContains, focusableElements, gridMove, inlineStep, pageRows, scrollingAncestor, scrollRoom, selectsAll, Typeahead, typesCharacter, whenHydrated, } from '@fusion.dev/helix-core';
|
|
2
2
|
const HEADS = { grip: 'grip-head', select: 'select-all' };
|
|
3
3
|
const shown = (element) => {
|
|
4
4
|
const hidden = element.getAttribute('hidden');
|
|
@@ -13,6 +13,7 @@ export class TableGridController {
|
|
|
13
13
|
this.focusWanted = false;
|
|
14
14
|
this.pointing = false;
|
|
15
15
|
this.given = new Set();
|
|
16
|
+
this.typeahead = new Typeahead();
|
|
16
17
|
this.handleKeydown = (event) => {
|
|
17
18
|
if (!this.options.on() || event.defaultPrevented || event.altKey) {
|
|
18
19
|
return;
|
|
@@ -35,6 +36,25 @@ export class TableGridController {
|
|
|
35
36
|
}
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
39
|
+
if (typesCharacter(event) && (event.key !== ' ' || this.typeahead.typing())) {
|
|
40
|
+
const found = this.typeahead.match(event.key, this.options.rowWords(), place.row);
|
|
41
|
+
if (found >= 0) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
if (found !== place.row) {
|
|
44
|
+
this.moveTo({
|
|
45
|
+
column: Math.max(0, Math.min(place.column, this.widthOf(found) - 1)),
|
|
46
|
+
row: found,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (event.key === ' ' && event.shiftKey && place.row >= 0) {
|
|
53
|
+
if (this.options.selectRange(place.row, place.row)) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
38
58
|
if (event.key === 'Enter' || event.key === ' ' || event.key === 'F2') {
|
|
39
59
|
if (event.key !== 'F2' && this.options.activate(place, event)) {
|
|
40
60
|
event.preventDefault();
|
|
@@ -57,6 +77,12 @@ export class TableGridController {
|
|
|
57
77
|
}
|
|
58
78
|
event.preventDefault();
|
|
59
79
|
to.column = Math.max(0, Math.min(to.column, this.widthOf(to.row) - 1));
|
|
80
|
+
if (event.shiftKey &&
|
|
81
|
+
(event.key === 'ArrowUp' || event.key === 'ArrowDown') &&
|
|
82
|
+
place.row >= 0 &&
|
|
83
|
+
to.row >= 0) {
|
|
84
|
+
this.options.selectRange(to.row, place.row);
|
|
85
|
+
}
|
|
60
86
|
if (to.row !== place.row || to.column !== place.column) {
|
|
61
87
|
this.moveTo(to);
|
|
62
88
|
}
|
|
@@ -76,7 +76,7 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
76
76
|
* through cell by cell, the headings included, as React Aria's table is. The
|
|
77
77
|
* table is then a `grid` and each value a `gridcell`, and the table gives
|
|
78
78
|
* every cell a `tabindex` so focus can rest on it: set `grid` only where the
|
|
79
|
-
* cells may carry one.
|
|
79
|
+
* cells may carry one.
|
|
80
80
|
*
|
|
81
81
|
* | Key | What it does |
|
|
82
82
|
* |---|---|
|
|
@@ -90,7 +90,10 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
90
90
|
* | Enter, Space on a sortable heading | Sorts by the column. |
|
|
91
91
|
* | Space on a row, Enter or Space on its checkbox cell | Selects the row, in a `selectable` table. |
|
|
92
92
|
* | Enter or Space on a grip cell | Picks the row up, or drops it, in a `reorderable` table. Up, Down, Home, and End move a row picked up, and Escape puts it back. |
|
|
93
|
+
* | Shift with Down or Up | Selects the rows it moves across, in a `selectable` table. |
|
|
94
|
+
* | Shift with Space | Selects every row from the one last chosen, in a `selectable` table. |
|
|
93
95
|
* | Control or Command with A | Selects every row, in a `selectable` table. |
|
|
96
|
+
* | A letter or a word | Moves to the next row whose first cell starts with what you typed. |
|
|
94
97
|
* | Tab | Leaves the table. |
|
|
95
98
|
*
|
|
96
99
|
* The CSS custom properties listed for this component are written by it and
|
|
@@ -105,7 +108,9 @@ declare const HelixHeadlessTable_base: (new (...args: any[]) => import("@fusion.
|
|
|
105
108
|
* @documentation https://fusion.dev/helix/components/table
|
|
106
109
|
*
|
|
107
110
|
* @slot - The table's rows.
|
|
111
|
+
* @slot empty - Content shown when there are no rows and none are loading, such as a picture and a link. Falls back to `empty-text`, which the live region still says.
|
|
108
112
|
* @slot heading - Words that name the table, shown above it, such as a heading.
|
|
113
|
+
* @slot loading - Content shown while the rows load, such as a spinner. Falls back to `loading-text`, which the live region still says.
|
|
109
114
|
*
|
|
110
115
|
* @event {HelixBeforeReorderEvent} helix-before-reorder - Fired when a row is dropped at a new place, in a `reorderable` table, with every row's key in the new order. Cancelable: cancel it to keep the row where it is.
|
|
111
116
|
* @event {HelixReorderEvent} helix-reorder - Fired after `helix-before-reorder` when nobody canceled it. Move your rows to the order in `keys`. Cancelable: cancel it to say the move did not happen.
|
|
@@ -144,6 +149,8 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
144
149
|
* `registerWords('de', { 'helix-table': { selectAllLabel: 'Alle Zeilen auswählen' } })`.
|
|
145
150
|
*/
|
|
146
151
|
static words: string;
|
|
152
|
+
/** The empty and loading lines are the `empty` and `loading` slots. */
|
|
153
|
+
protected statusSlots: boolean;
|
|
147
154
|
/**
|
|
148
155
|
* The word for an ascending sort, in a `sorted-label` that writes `{order}`.
|
|
149
156
|
*
|
|
@@ -383,9 +390,25 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
383
390
|
private readonly handleToggle;
|
|
384
391
|
/** Every row's key, in the order the rows are written. */
|
|
385
392
|
private get everyKey();
|
|
386
|
-
/** The
|
|
393
|
+
/** The keys of the rows that can be chosen: every row that is not `disabled`. */
|
|
394
|
+
private get enabledKeys();
|
|
395
|
+
/** Whether the row of that key is `disabled`. */
|
|
396
|
+
private isDisabled;
|
|
397
|
+
/**
|
|
398
|
+
* The checkbox in the heading: every row that can be chosen, or none of
|
|
399
|
+
* them where each already is. A `disabled` row keeps what it had.
|
|
400
|
+
*/
|
|
387
401
|
private chooseAll;
|
|
388
|
-
/**
|
|
402
|
+
/** The key of the row a Shift range runs from: the one last chosen. */
|
|
403
|
+
private anchorKey;
|
|
404
|
+
/**
|
|
405
|
+
* Selects every row from the one last chosen to the drawn row `to`, keeping
|
|
406
|
+
* what was already selected. With no row to run from, the range is the row
|
|
407
|
+
* the reader is on, `from`, and it becomes the one to run from. Says whether
|
|
408
|
+
* the table selects at all.
|
|
409
|
+
*/
|
|
410
|
+
private selectRange;
|
|
411
|
+
/** One row's checkbox, ticked or not. It becomes the row a range runs from. */
|
|
389
412
|
private chooseRow;
|
|
390
413
|
/**
|
|
391
414
|
* Enter, Space or F2 on a cell of a `grid` table, and whether it did
|
|
@@ -498,7 +521,10 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
498
521
|
* assignment has not happened.
|
|
499
522
|
*/
|
|
500
523
|
static shadowRootOptions: ShadowRootInit;
|
|
501
|
-
/**
|
|
524
|
+
/**
|
|
525
|
+
* The rows: every child that is not a column, a heading, or the content of
|
|
526
|
+
* the `empty` or `loading` slot, which is what the body slot is assigned.
|
|
527
|
+
*/
|
|
502
528
|
protected get slotted(): Element[];
|
|
503
529
|
private assign;
|
|
504
530
|
/**
|