@fusion.dev/helix-headless 0.142.0 → 0.143.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 +16710 -707
- package/dist/accordion/item/accordion-item.d.ts +38 -2
- package/dist/accordion/item/accordion-item.js +38 -6
- package/dist/accordion/root/accordion-root.d.ts +23 -1
- package/dist/accordion/root/accordion-root.js +39 -3
- package/dist/announcement/root/announcement-root.d.ts +11 -7
- package/dist/announcement/root/announcement-root.js +1 -1
- package/dist/app-header/root/app-header-root.d.ts +21 -0
- package/dist/app-header/root/app-header-root.js +31 -5
- package/dist/area-chart/root/area-chart-root.js +16 -5
- package/dist/avatar/group/avatar-group.d.ts +24 -3
- package/dist/avatar/group/avatar-group.js +34 -2
- package/dist/avatar/root/avatar-root.d.ts +3 -1
- package/dist/avatar/root/avatar-root.js +8 -1
- package/dist/badge/root/badge-root.d.ts +8 -0
- package/dist/bar-chart/root/bar-chart-root.d.ts +19 -3
- package/dist/bar-chart/root/bar-chart-root.js +43 -13
- package/dist/blockquote/root/blockquote-root.d.ts +12 -7
- package/dist/blockquote/root/blockquote-root.js +6 -4
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
- package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
- package/dist/button/root/button-root.d.ts +37 -0
- package/dist/button/root/button-root.js +44 -0
- package/dist/callout/root/callout-root.d.ts +14 -5
- package/dist/callout/root/callout-root.js +11 -3
- package/dist/canvas/root/canvas-root.d.ts +1 -0
- package/dist/canvas/root/canvas-root.js +10 -1
- package/dist/carousel/root/carousel-root.d.ts +47 -5
- package/dist/carousel/root/carousel-root.js +53 -21
- package/dist/chat/composer/chat-composer.d.ts +13 -11
- package/dist/chat/composer/chat-composer.js +6 -12
- package/dist/chat/message/chat-message.js +8 -1
- package/dist/chat/root/chat-root.d.ts +1 -0
- package/dist/chat/root/chat-root.js +2 -1
- package/dist/checkbox/root/checkbox-root.d.ts +7 -0
- package/dist/checkbox/root/checkbox-root.js +7 -3
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +65 -32
- package/dist/checkbox-group/root/checkbox-group-root.js +85 -37
- package/dist/code/root/code-root.d.ts +1 -1
- package/dist/code-block/root/code-block-root.d.ts +19 -0
- package/dist/code-block/root/code-block-root.js +21 -1
- package/dist/color-input/root/color-input-root.d.ts +26 -1
- package/dist/color-input/root/color-input-root.js +32 -3
- package/dist/color-picker/root/color-picker-root.d.ts +37 -7
- package/dist/color-picker/root/color-picker-root.js +83 -6
- package/dist/combobox/root/combobox-root.d.ts +90 -2
- package/dist/combobox/root/combobox-root.js +160 -19
- package/dist/command/root/command-root.d.ts +17 -6
- package/dist/command/root/command-root.js +12 -1
- package/dist/copy-button/root/copy-button-root.d.ts +21 -6
- package/dist/copy-button/root/copy-button-root.js +11 -1
- package/dist/data-table/root/data-table-root.d.ts +164 -10
- package/dist/data-table/root/data-table-root.js +488 -57
- package/dist/date-input/root/date-input-root.d.ts +53 -8
- package/dist/date-input/root/date-input-root.js +70 -18
- package/dist/date-picker/root/date-picker-root.d.ts +84 -17
- package/dist/date-picker/root/date-picker-root.js +125 -20
- package/dist/dialog/root/dialog-root.d.ts +38 -10
- package/dist/dialog/root/dialog-root.js +55 -2
- package/dist/donut-chart/root/donut-chart-root.d.ts +39 -15
- package/dist/donut-chart/root/donut-chart-root.js +30 -10
- package/dist/drawer/root/drawer-root.d.ts +32 -7
- package/dist/drawer/root/drawer-root.js +50 -5
- package/dist/drilldown/item/drilldown-item.d.ts +16 -13
- package/dist/drilldown/item/drilldown-item.js +6 -2
- package/dist/drilldown/root/drilldown-root.d.ts +21 -5
- package/dist/drilldown/root/drilldown-root.js +26 -10
- package/dist/element-events.generated.js +35 -9
- package/dist/file-input/root/file-input-root.d.ts +18 -1
- package/dist/file-input/root/file-input-root.js +34 -8
- package/dist/filter-input/menu/filter-menu.d.ts +9 -0
- package/dist/filter-input/menu/filter-menu.js +8 -0
- package/dist/filter-input/root/filter-input-root.d.ts +5 -2
- package/dist/filter-input/root/filter-input-root.js +22 -7
- package/dist/format-date/root/format-date-root.d.ts +11 -3
- package/dist/format-date/root/format-date-root.js +26 -5
- package/dist/graph/root/graph-root.d.ts +24 -3
- package/dist/graph/root/graph-root.js +66 -12
- package/dist/heatmap/root/buckets.d.ts +2 -1
- package/dist/heatmap/root/buckets.js +5 -1
- package/dist/heatmap/root/heatmap-root.d.ts +27 -6
- package/dist/heatmap/root/heatmap-root.js +23 -6
- package/dist/icon/root/icon-root.d.ts +24 -3
- package/dist/icon/root/icon-root.js +34 -1
- package/dist/icon-button/root/icon-button-root.d.ts +1 -1
- package/dist/image/root/image-root.d.ts +9 -2
- package/dist/image/root/image-root.js +8 -2
- package/dist/input/root/input-root.d.ts +51 -9
- package/dist/input/root/input-root.js +46 -5
- package/dist/kbd/root/kbd-root.d.ts +12 -4
- package/dist/kbd/root/kbd-root.js +10 -11
- package/dist/legend/root/legend-root.d.ts +7 -0
- package/dist/legend/root/legend-root.js +7 -2
- package/dist/lightbox/item/lightbox-item.d.ts +2 -1
- package/dist/lightbox/root/lightbox-root.d.ts +16 -5
- package/dist/lightbox/root/lightbox-root.js +24 -3
- package/dist/line-chart/root/line-chart-root.d.ts +19 -2
- package/dist/line-chart/root/line-chart-root.js +40 -13
- package/dist/list/item/list-item.d.ts +11 -5
- package/dist/list/item/list-item.js +5 -6
- package/dist/listbox/root/listbox-root.d.ts +111 -2
- package/dist/listbox/root/listbox-root.js +144 -16
- package/dist/map/root/map-root.d.ts +19 -4
- package/dist/map/root/map-root.js +36 -7
- package/dist/megamenu/root/megamenu-root.d.ts +10 -1
- package/dist/megamenu/root/megamenu-root.js +10 -1
- package/dist/menu/item/menu-item.d.ts +41 -12
- package/dist/menu/item/menu-item.js +46 -3
- package/dist/menu/root/menu-root.d.ts +32 -2
- package/dist/menu/root/menu-root.js +48 -8
- package/dist/meter/root/meter-root.d.ts +6 -0
- package/dist/meter/root/meter-root.js +18 -2
- package/dist/navigation/root/navigation-root.d.ts +10 -2
- package/dist/navigation/root/navigation-root.js +13 -2
- package/dist/notifications/group/notification-group.d.ts +1 -0
- package/dist/notifications/notification/notification.d.ts +12 -0
- package/dist/notifications/notification/notification.js +31 -2
- package/dist/notifications/root/notifications-root.d.ts +7 -0
- package/dist/notifications/root/notifications-root.js +17 -2
- package/dist/number-input/root/number-input-root.d.ts +62 -7
- package/dist/number-input/root/number-input-root.js +68 -16
- package/dist/object-inspector/root/object-inspector-root.d.ts +48 -5
- package/dist/object-inspector/root/object-inspector-root.js +91 -11
- package/dist/option/root/option-root.d.ts +4 -1
- package/dist/option/root/option-root.js +1 -0
- package/dist/outline/root/outline-root.d.ts +13 -2
- package/dist/outline/root/outline-root.js +4 -2
- package/dist/pagination/root/pagination-root.d.ts +19 -1
- package/dist/pagination/root/pagination-root.js +49 -9
- package/dist/pagination/size/pagination-size.d.ts +13 -3
- package/dist/pagination/size/pagination-size.js +7 -0
- package/dist/pin-input/root/pin-input-root.d.ts +16 -0
- package/dist/pin-input/root/pin-input-root.js +19 -5
- package/dist/popover/root/popover-root.d.ts +19 -2
- package/dist/popover/root/popover-root.js +22 -1
- package/dist/progress/root/progress-root.d.ts +16 -1
- package/dist/progress/root/progress-root.js +31 -7
- package/dist/qr-code/root/qr-code-root.d.ts +19 -0
- package/dist/qr-code/root/qr-code-root.js +29 -1
- package/dist/radio/group/radio-group.d.ts +41 -0
- package/dist/radio/group/radio-group.js +50 -14
- package/dist/radio/root/radio-root.d.ts +8 -5
- package/dist/radio/root/radio-root.js +1 -2
- package/dist/rating/root/rating-root.d.ts +24 -1
- package/dist/rating/root/rating-root.js +24 -7
- package/dist/scatter-chart/root/scatter-chart-root.js +15 -7
- package/dist/select/root/select-root.d.ts +69 -3
- package/dist/select/root/select-root.js +92 -8
- package/dist/skeleton/root/skeleton-root.d.ts +14 -3
- package/dist/skeleton/root/skeleton-root.js +17 -3
- package/dist/skip-link/root/skip-link-root.d.ts +10 -0
- package/dist/skip-link/root/skip-link-root.js +15 -1
- package/dist/slider/root/slider-root.d.ts +20 -2
- package/dist/slider/root/slider-root.js +41 -3
- package/dist/sparkline/root/sparkline-root.d.ts +9 -10
- package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
- package/dist/speed-dial/item/speed-dial-item.js +7 -2
- package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
- package/dist/speed-dial/root/speed-dial-root.js +19 -5
- package/dist/splitter/root/splitter-root.d.ts +28 -6
- package/dist/splitter/root/splitter-root.js +35 -1
- package/dist/stat/root/stat-root.d.ts +7 -1
- package/dist/stat/root/stat-root.js +14 -1
- package/dist/steps/root/steps-root.js +1 -1
- package/dist/switch/root/switch-root.d.ts +2 -0
- package/dist/switch/root/switch-root.js +1 -0
- package/dist/table/cell/table-cell.d.ts +24 -4
- package/dist/table/cell/table-cell.js +21 -1
- package/dist/table/column/table-column.d.ts +32 -6
- package/dist/table/column/table-column.js +36 -6
- package/dist/table/root/table-root.d.ts +94 -3
- package/dist/table/root/table-root.js +128 -5
- package/dist/table/row/table-row.d.ts +7 -0
- package/dist/table/row/table-row.js +9 -1
- package/dist/tabs/root/tabs-root.d.ts +6 -1
- package/dist/tabs/root/tabs-root.js +6 -2
- package/dist/tag/root/tag-root.d.ts +23 -1
- package/dist/tag/root/tag-root.js +16 -2
- package/dist/textarea/root/textarea-root.d.ts +4 -0
- package/dist/textarea/root/textarea-root.js +1 -1
- package/dist/time-input/root/time-input-root.d.ts +28 -2
- package/dist/time-input/root/time-input-root.js +46 -10
- package/dist/time-picker/root/time-picker-root.d.ts +101 -18
- package/dist/time-picker/root/time-picker-root.js +115 -16
- package/dist/toast/root/toast-root.d.ts +64 -1
- package/dist/toast/root/toast-root.js +147 -2
- package/dist/toggle/group/toggle-group.d.ts +38 -1
- package/dist/toggle/group/toggle-group.js +34 -8
- package/dist/toggle/root/toggle-root.d.ts +14 -10
- package/dist/toggle/root/toggle-root.js +3 -3
- package/dist/toolbar/root/toolbar-root.d.ts +55 -15
- package/dist/toolbar/root/toolbar-root.js +99 -16
- package/dist/tooltip/root/tooltip-root.d.ts +37 -4
- package/dist/tooltip/root/tooltip-root.js +48 -0
- package/dist/tour/root/tour-root.d.ts +34 -5
- package/dist/tour/root/tour-root.js +51 -14
- package/dist/tour/stop/tour-stop.d.ts +18 -1
- package/dist/tour/stop/tour-stop.js +25 -5
- package/dist/tree/item/tree-item.d.ts +21 -5
- package/dist/tree/item/tree-item.js +14 -4
- package/dist/tree/root/tree-root.d.ts +29 -5
- package/dist/tree/root/tree-root.js +64 -8
- package/dist/trend/root/trend-root.d.ts +23 -1
- package/dist/trend/root/trend-root.js +28 -7
- package/dist/video/root/keys.d.ts +5 -0
- package/dist/video/root/keys.js +13 -1
- package/dist/video/root/video-root.d.ts +19 -0
- package/dist/video/root/video-root.js +48 -4
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
- package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
- package/dist/video-playlist/root/video-playlist-root.js +6 -3
- package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
- package/dist/virtual-list/root/virtual-list-root.js +103 -21
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +4348 -965
- package/types/vue.d.ts +972 -232
|
@@ -11,39 +11,71 @@ import { HelixHeadlessRadio } from '../root/radio-root.js';
|
|
|
11
11
|
let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
|
+
this.focusable = true;
|
|
14
15
|
this.keys = new KeyboardNavigationController(this, { items: () => this.items });
|
|
15
16
|
this.cascade = new DisabledCascadeController(this, {
|
|
16
17
|
items: () => this.items,
|
|
17
18
|
});
|
|
18
19
|
this.handleSelection = (event) => {
|
|
19
20
|
event.stopPropagation();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
queueMicrotask(() => {
|
|
22
|
+
if (event.defaultPrevented) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const next = event.detail.value;
|
|
26
|
+
if (next === this.value) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.requestValue(next);
|
|
30
|
+
});
|
|
28
31
|
};
|
|
29
32
|
}
|
|
30
33
|
get items() {
|
|
31
|
-
return [...this.children].filter((child) => child instanceof HelixHeadlessRadio);
|
|
34
|
+
return [...(this.children ?? [])].filter((child) => child instanceof HelixHeadlessRadio);
|
|
35
|
+
}
|
|
36
|
+
get answer() {
|
|
37
|
+
return this.items.find((item) => item.value === this.value);
|
|
38
|
+
}
|
|
39
|
+
get answered() {
|
|
40
|
+
if (this.value === '') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const items = this.items;
|
|
44
|
+
return items.length === 0 || items.some((item) => item.value === this.value);
|
|
45
|
+
}
|
|
46
|
+
adopt() {
|
|
47
|
+
if (this.value !== '' || this.children === undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const declared = this.items.find((item) => item.defaultSelected);
|
|
51
|
+
if (declared) {
|
|
52
|
+
this.defaultValue = declared.value;
|
|
53
|
+
}
|
|
32
54
|
}
|
|
33
55
|
place() {
|
|
34
56
|
const items = this.items;
|
|
35
|
-
const answer =
|
|
57
|
+
const answer = this.answer;
|
|
36
58
|
for (const item of items) {
|
|
37
59
|
item.selected = item === answer;
|
|
38
60
|
}
|
|
39
|
-
const entry =
|
|
61
|
+
const entry = this.focusable ? this.entry() : undefined;
|
|
40
62
|
for (const item of items) {
|
|
41
63
|
item.focusable = item === entry;
|
|
42
64
|
}
|
|
43
65
|
}
|
|
66
|
+
entry() {
|
|
67
|
+
const answer = this.answer;
|
|
68
|
+
return answer && !answer.disabled ? answer : this.items.find((item) => !item.disabled);
|
|
69
|
+
}
|
|
70
|
+
get focusTarget() {
|
|
71
|
+
return (super.focusTarget ??
|
|
72
|
+
this.entry()?.shadowRoot?.querySelector('[part="item"]') ??
|
|
73
|
+
null);
|
|
74
|
+
}
|
|
44
75
|
sync() {
|
|
45
|
-
|
|
46
|
-
this.
|
|
76
|
+
const answered = this.answered;
|
|
77
|
+
this.internals.setFormValue(answered ? this.value : null);
|
|
78
|
+
this.reportRequired(!answered, 'Choose one to continue.', 'items');
|
|
47
79
|
}
|
|
48
80
|
connectedCallback() {
|
|
49
81
|
super.connectedCallback();
|
|
@@ -55,6 +87,7 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
|
|
|
55
87
|
}
|
|
56
88
|
willUpdate(changed) {
|
|
57
89
|
super.willUpdate(changed);
|
|
90
|
+
this.adopt();
|
|
58
91
|
hostAria(this.internals, { label: this.label, role: 'radiogroup' });
|
|
59
92
|
this.internals.ariaRequired = this.required ? 'true' : null;
|
|
60
93
|
}
|
|
@@ -67,11 +100,14 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
|
|
|
67
100
|
render() {
|
|
68
101
|
return html `${this.renderGroupLabel()}
|
|
69
102
|
<div part="items">
|
|
70
|
-
<slot @slotchange=${() => this.
|
|
103
|
+
<slot @slotchange=${() => this.requestUpdate()}></slot>
|
|
71
104
|
</div>
|
|
72
105
|
${this.renderHint()}${this.renderFeedback()}`;
|
|
73
106
|
}
|
|
74
107
|
};
|
|
108
|
+
__decorate([
|
|
109
|
+
property({ attribute: false })
|
|
110
|
+
], HelixHeadlessRadioGroup.prototype, "focusable", void 0);
|
|
75
111
|
__decorate([
|
|
76
112
|
property({ reflect: true })
|
|
77
113
|
], HelixHeadlessRadioGroup.prototype, "orientation", void 0);
|
|
@@ -73,11 +73,14 @@ export declare class HelixHeadlessRadio extends HelixHeadlessRadio_base {
|
|
|
73
73
|
get defaultSelected(): boolean;
|
|
74
74
|
set defaultSelected(next: boolean);
|
|
75
75
|
/**
|
|
76
|
-
* Whether the radio holds the group's tab stop
|
|
76
|
+
* Whether the radio holds the group's tab stop. Unset, the chosen radio
|
|
77
|
+
* holds it.
|
|
77
78
|
*
|
|
78
79
|
* The stop rides on the selection, and an unanswered group has no selection,
|
|
79
80
|
* so without this every choice is `tabindex="-1"` and the keyboard cannot
|
|
80
|
-
* enter the set at all.
|
|
81
|
+
* enter the set at all. The group writes `true` for the chosen radio, or for
|
|
82
|
+
* the first that can be chosen while nothing is, and `false` for every radio,
|
|
83
|
+
* the chosen one too, while a toolbar the group sits in holds the stop.
|
|
81
84
|
*
|
|
82
85
|
* **Written by the group, with no attribute, and it reaches the control
|
|
83
86
|
* rather than the host.** A `tabindex` on the host is two faults at once: it
|
|
@@ -88,15 +91,15 @@ export declare class HelixHeadlessRadio extends HelixHeadlessRadio_base {
|
|
|
88
91
|
* them an unchecked radio. `Toggle.Root` carries the same property for the
|
|
89
92
|
* same reason.
|
|
90
93
|
*/
|
|
91
|
-
focusable
|
|
94
|
+
focusable?: boolean;
|
|
92
95
|
/**
|
|
93
96
|
* Asks for the render that follows a hydration, so the stop stops being every
|
|
94
97
|
* choice's.
|
|
95
98
|
*
|
|
96
99
|
* `tabindex` reads `childrenKnown` and a hydrating render is one where that is
|
|
97
100
|
* false, which is what keeps the hydrating render the server's. Nothing else
|
|
98
|
-
*
|
|
99
|
-
*
|
|
101
|
+
* is sure to ask: a write from the group that leaves `focusable` as it was
|
|
102
|
+
* re-renders nothing.
|
|
100
103
|
*/
|
|
101
104
|
protected firstUpdated(changed: PropertyValues): void;
|
|
102
105
|
protected render(): TemplateResult;
|
|
@@ -11,7 +11,6 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends MixinSelectableItem(He
|
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.clicks = new HostClickController(this, () => this.partNamed('item'));
|
|
14
|
-
this.focusable = false;
|
|
15
14
|
}
|
|
16
15
|
get defaultSelected() {
|
|
17
16
|
return this.hasAttribute('selected');
|
|
@@ -30,7 +29,7 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends MixinSelectableItem(He
|
|
|
30
29
|
role="radio"
|
|
31
30
|
aria-checked=${this.selected ? 'true' : 'false'}
|
|
32
31
|
?disabled=${this.disabled}
|
|
33
|
-
tabindex=${this.
|
|
32
|
+
tabindex=${(this.focusable ?? this.selected) || !this.childrenKnown ? '0' : '-1'}
|
|
34
33
|
@click=${() => this.select()}
|
|
35
34
|
>
|
|
36
35
|
<span part="box"></span>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixFormElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, HelixFormElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
4
4
|
/**
|
|
@@ -21,6 +21,11 @@ declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion
|
|
|
21
21
|
* @aliases Stars, Score, Review score
|
|
22
22
|
* @documentation https://fusion.dev/helix/components/rating
|
|
23
23
|
*
|
|
24
|
+
* @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
|
|
25
|
+
* @slot hint - Help under the control, for content richer than the `hint` text.
|
|
26
|
+
* @slot label - The label, for content richer than the `label` text.
|
|
27
|
+
* @slot mark - The shape of a mark, copied into every position.
|
|
28
|
+
*
|
|
24
29
|
* @event {Event} change - The score was chosen. Sent together with `input`, because each choice commits the score.
|
|
25
30
|
* @event {HelixBeforeChangeEvent} helix-before-change - The score is about to change. Cancelable: cancel it to keep the current score.
|
|
26
31
|
* @event {Event} input - The score changed.
|
|
@@ -92,6 +97,14 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
|
|
|
92
97
|
* nonsense is the fault this pays off, so neither case is quietly ignored.
|
|
93
98
|
*/
|
|
94
99
|
min: number;
|
|
100
|
+
/**
|
|
101
|
+
* The name of each mark a reader can choose, with `{score}` and `{max}`
|
|
102
|
+
* filled in. Set it to translate "3 out of 5".
|
|
103
|
+
*
|
|
104
|
+
* Also what a read-only rating says for its score where `value-text` is
|
|
105
|
+
* empty.
|
|
106
|
+
*/
|
|
107
|
+
optionLabel: string;
|
|
95
108
|
/**
|
|
96
109
|
* Shows the score without letting users change it.
|
|
97
110
|
*
|
|
@@ -128,6 +141,7 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
|
|
|
128
141
|
* and each of its options by the score that option is.
|
|
129
142
|
*/
|
|
130
143
|
valueText: string;
|
|
144
|
+
protected readonly aria: AriaForwardController;
|
|
131
145
|
/**
|
|
132
146
|
* The shape a consumer slotted, if they slotted one.
|
|
133
147
|
*
|
|
@@ -255,6 +269,15 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
|
|
|
255
269
|
protected updated(changed: PropertyValues): void;
|
|
256
270
|
protected willUpdate(changed: PropertyValues): void;
|
|
257
271
|
protected firstUpdated(changed: PropertyValues): void;
|
|
272
|
+
/** The name of one score, as `optionLabel` writes it. */
|
|
273
|
+
private nameFor;
|
|
274
|
+
/**
|
|
275
|
+
* The option holding the tab stop: the chosen one, or for a score between
|
|
276
|
+
* steps, such as an average of 3.5 on whole marks, the step below it, and
|
|
277
|
+
* the first while nothing is chosen. Between steps no option matched, and
|
|
278
|
+
* the rating left the tab order.
|
|
279
|
+
*/
|
|
280
|
+
private stopAt;
|
|
258
281
|
protected render(): TemplateResult;
|
|
259
282
|
/**
|
|
260
283
|
* The value as a number, or `NaN` while the control is empty.
|
|
@@ -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 { valueAsNumber as asNumber, customElement, numberAsValue as fromNumber, HelixFormElement, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, valueAsNumber as asNumber, customElement, fillTemplate, numberAsValue as fromNumber, HelixFormElement, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
@@ -13,10 +13,14 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
13
13
|
this.announcesInput = false;
|
|
14
14
|
this.max = 5;
|
|
15
15
|
this.min = 0;
|
|
16
|
+
this.optionLabel = '{score} out of {max}';
|
|
16
17
|
this.readOnly = false;
|
|
17
18
|
this.showValue = false;
|
|
18
19
|
this.step = 1;
|
|
19
20
|
this.valueText = '';
|
|
21
|
+
this.aria = new AriaForwardController(this, {
|
|
22
|
+
target: () => this.partNamed('marks'),
|
|
23
|
+
});
|
|
20
24
|
this.markSource = undefined;
|
|
21
25
|
this.markRead = false;
|
|
22
26
|
this.previewing = undefined;
|
|
@@ -190,11 +194,23 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
190
194
|
this.finishHydration();
|
|
191
195
|
this.sync();
|
|
192
196
|
}
|
|
197
|
+
nameFor(score) {
|
|
198
|
+
return fillTemplate(this.optionLabel, { max: this.max, score });
|
|
199
|
+
}
|
|
200
|
+
stopAt(answered, first) {
|
|
201
|
+
if (!answered) {
|
|
202
|
+
return first;
|
|
203
|
+
}
|
|
204
|
+
const below = this.steps.filter((score) => score <= this.score);
|
|
205
|
+
return below.at(-1) ?? first;
|
|
206
|
+
}
|
|
193
207
|
render() {
|
|
194
208
|
const showing = this.previewing ?? this.score;
|
|
195
|
-
const said = this.valueText ||
|
|
209
|
+
const said = this.valueText || this.nameFor(this.score);
|
|
196
210
|
const count = Math.max(0, Math.round(this.max));
|
|
197
211
|
const first = this.steps[0];
|
|
212
|
+
const answered = this.value !== '' && Number.isFinite(Number.parseFloat(this.value));
|
|
213
|
+
const stop = this.stopAt(answered, first);
|
|
198
214
|
const mark = (layer, index) => html `<span part="mark">${this.markFor(layer, index)}</span>`;
|
|
199
215
|
const marksIn = (layer) => Array.from({ length: count }, (_, index) => mark(layer, index));
|
|
200
216
|
return html `${this.renderGroupLabel()}
|
|
@@ -240,14 +256,12 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
240
256
|
part="option"
|
|
241
257
|
role="radio"
|
|
242
258
|
type="button"
|
|
243
|
-
aria-checked=${this.score === score ? 'true' : 'false'}
|
|
244
|
-
aria-label=${
|
|
259
|
+
aria-checked=${answered && this.score === score ? 'true' : 'false'}
|
|
260
|
+
aria-label=${this.nameFor(score)}
|
|
245
261
|
data-score=${String(score)}
|
|
246
262
|
style=${`--rating-step: ${index + 1}`}
|
|
247
263
|
?disabled=${this.isDisabled}
|
|
248
|
-
tabindex=${
|
|
249
|
-
? '0'
|
|
250
|
-
: '-1'}
|
|
264
|
+
tabindex=${score === stop ? '0' : '-1'}
|
|
251
265
|
@click=${this.handleOptionClick}
|
|
252
266
|
@pointerenter=${this.handleOptionEnter}
|
|
253
267
|
></button>`)}
|
|
@@ -275,6 +289,9 @@ __decorate([
|
|
|
275
289
|
__decorate([
|
|
276
290
|
property({ type: Number })
|
|
277
291
|
], HelixHeadlessRating.prototype, "min", void 0);
|
|
292
|
+
__decorate([
|
|
293
|
+
property({ attribute: 'option-label' })
|
|
294
|
+
], HelixHeadlessRating.prototype, "optionLabel", void 0);
|
|
278
295
|
__decorate([
|
|
279
296
|
property({ attribute: 'readonly', reflect: true, type: Boolean })
|
|
280
297
|
], HelixHeadlessRating.prototype, "readOnly", void 0);
|
|
@@ -9,11 +9,19 @@ import { html, nothing, svg } from 'lit';
|
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
11
11
|
const REACH = 24;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const COEFFICIENTS = new Map();
|
|
13
|
+
const coefficientIn = (locale) => {
|
|
14
|
+
let found = COEFFICIENTS.get(locale);
|
|
15
|
+
if (!found) {
|
|
16
|
+
found = new Intl.NumberFormat(locale || undefined, {
|
|
17
|
+
maximumFractionDigits: 2,
|
|
18
|
+
minimumFractionDigits: 2,
|
|
19
|
+
signDisplay: 'exceptZero',
|
|
20
|
+
});
|
|
21
|
+
COEFFICIENTS.set(locale, found);
|
|
22
|
+
}
|
|
23
|
+
return found;
|
|
24
|
+
};
|
|
17
25
|
const nice = ([low, high]) => {
|
|
18
26
|
if (low === high) {
|
|
19
27
|
return [low - 1, high + 1];
|
|
@@ -130,7 +138,7 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
|
|
|
130
138
|
<div id=${CHART_PLOT} part="plot"></div>
|
|
131
139
|
</div>`;
|
|
132
140
|
}
|
|
133
|
-
const format = formatNumbers(
|
|
141
|
+
const format = formatNumbers(this.locale);
|
|
134
142
|
const along = linear(field.x, [0, PLOT_WIDTH]);
|
|
135
143
|
const up = linear(field.y, [PLOT_HEIGHT, 0]);
|
|
136
144
|
return html `<div dir="ltr" part="plane">
|
|
@@ -177,7 +185,7 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
|
|
|
177
185
|
const clauses = field.yNames.map((name, depth) => {
|
|
178
186
|
const pairs = field.pairs[depth] ?? [];
|
|
179
187
|
const r = correlation(pairs);
|
|
180
|
-
const strength = r === undefined ? '' : `, ${this.correlationLabel} ${
|
|
188
|
+
const strength = r === undefined ? '' : `, ${this.correlationLabel} ${coefficientIn(this.locale).format(r)}`;
|
|
181
189
|
return `${name} / ${field.xName}: ${pairs.length} ${this.pointsLabel}${strength}`;
|
|
182
190
|
});
|
|
183
191
|
const named = this.named ? `${this.named}. ` : '';
|
|
@@ -17,6 +17,14 @@ declare const HelixHeadlessSelect_base: (new (...args: any[]) => import("@fusion
|
|
|
17
17
|
* `Select` owns the value, passes it down to the listbox, and takes changes
|
|
18
18
|
* back, so the value lives in one place.
|
|
19
19
|
*
|
|
20
|
+
* In the open list the arrow keys, Home, End, PageUp, PageDown and typed
|
|
21
|
+
* letters move the highlight, and Enter, Space or a click chooses it and
|
|
22
|
+
* closes the list. Tab closes the list and moves on. On the closed trigger,
|
|
23
|
+
* ArrowDown, Enter and Space open the list on the chosen option, ArrowUp opens
|
|
24
|
+
* it on the chosen or last option, Home and End open it on the first or last,
|
|
25
|
+
* and typed letters choose the option whose words start with them, as a native
|
|
26
|
+
* `<select>` does.
|
|
27
|
+
*
|
|
20
28
|
* @since 0.1.0
|
|
21
29
|
* @status stable
|
|
22
30
|
* @category forms
|
|
@@ -25,6 +33,7 @@ declare const HelixHeadlessSelect_base: (new (...args: any[]) => import("@fusion
|
|
|
25
33
|
*
|
|
26
34
|
* @slot - The options, and any groups of them.
|
|
27
35
|
* @slot end - Content after the chosen value, such as a count, a unit, or a clear button.
|
|
36
|
+
* @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
|
|
28
37
|
* @slot hint - The hint, for content richer than plain text.
|
|
29
38
|
* @slot indicator - The icon on the trigger that shows it opens, usually a caret.
|
|
30
39
|
* @slot label - The label, for content richer than plain text.
|
|
@@ -34,8 +43,9 @@ declare const HelixHeadlessSelect_base: (new (...args: any[]) => import("@fusion
|
|
|
34
43
|
* @event {HelixBeforeChangeEvent} helix-before-change - Relayed from the listbox when the value is about to change. Cancelable: canceling it stops the change and leaves the decision to you.
|
|
35
44
|
* @event {HelixBeforeCloseEvent} helix-before-close - Fired before the list closes. Cancelable: canceling it keeps the list open and leaves the decision to you.
|
|
36
45
|
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before the list opens. Cancelable: canceling it keeps the list closed and leaves the decision to you.
|
|
46
|
+
* @event {HelixFilterEvent} helix-filter - Fired as the user types in the filter field of a `filterable` select, with the text the list is filtered by. Cancelable: cancel it to leave the list unfiltered and filter the options yourself.
|
|
37
47
|
* @event {Event} input - Fired after the value changes.
|
|
38
|
-
* @event {
|
|
48
|
+
* @event {ToggleEvent} toggle - Fired after the list opens or closes, with `newState` and `oldState`.
|
|
39
49
|
*
|
|
40
50
|
* @csspart feedback - The message that says what is wrong with the value.
|
|
41
51
|
* @csspart hint - Help under the trigger.
|
|
@@ -158,10 +168,64 @@ export declare class HelixHeadlessSelect extends HelixHeadlessSelect_base {
|
|
|
158
168
|
* the commit creates the text node it needs.
|
|
159
169
|
*/
|
|
160
170
|
private get display();
|
|
171
|
+
/** Where the highlight goes when the list opens, set by the key that opened it. */
|
|
172
|
+
private opening;
|
|
173
|
+
/**
|
|
174
|
+
* The values of the options seen on the last update, so an option that
|
|
175
|
+
* leaves can be told apart from one that has not arrived yet.
|
|
176
|
+
*/
|
|
177
|
+
private seen;
|
|
178
|
+
/**
|
|
179
|
+
* Hears an option's words change, which no slot reports, and the host's
|
|
180
|
+
* `aria-labelledby` change, which `triggerNames` reads.
|
|
181
|
+
*
|
|
182
|
+
* The trigger shows the chosen option's words, read at render time. A slot
|
|
183
|
+
* reports an option arriving or leaving and says nothing when the text of
|
|
184
|
+
* one already there changes, so a relabeled option left the old words on the
|
|
185
|
+
* trigger. `label` and `value` are attributes that change the words too.
|
|
186
|
+
*
|
|
187
|
+
* Made on connection, there being no `MutationObserver` on a server.
|
|
188
|
+
*/
|
|
189
|
+
private watchers;
|
|
190
|
+
connectedCallback(): void;
|
|
191
|
+
disconnectedCallback(): void;
|
|
192
|
+
/** Opens the list with the highlight where the opening key says. */
|
|
193
|
+
private openAt;
|
|
161
194
|
/** Closes the list and puts focus back where it came from. */
|
|
162
195
|
private close;
|
|
163
196
|
private readonly handleChange;
|
|
197
|
+
/**
|
|
198
|
+
* The keys of the closed trigger.
|
|
199
|
+
*
|
|
200
|
+
* Letters choose without opening, the way a native `<select>` and React
|
|
201
|
+
* Aria's Select do, so a reader who knows the answer types it. A Space in the
|
|
202
|
+
* middle of those letters is one of them. With `multiple`, a letter opens the
|
|
203
|
+
* list on the match instead, because adding to a set is a choice the reader
|
|
204
|
+
* makes in the list.
|
|
205
|
+
*/
|
|
164
206
|
private readonly handleTriggerKeydown;
|
|
207
|
+
/**
|
|
208
|
+
* Tab closes the list and lets focus move on, as a native `<select>` does.
|
|
209
|
+
*
|
|
210
|
+
* Measured before this: Tab moved focus to the next control and left the
|
|
211
|
+
* panel open over the page, because the panel closes on a press outside or
|
|
212
|
+
* on Escape and nothing closed it when focus left.
|
|
213
|
+
*/
|
|
214
|
+
private readonly handleListKeydown;
|
|
215
|
+
/**
|
|
216
|
+
* Takes `<helix-option selected>` as the default value, and an option that
|
|
217
|
+
* has left out of the value.
|
|
218
|
+
*
|
|
219
|
+
* The first is what a native `<select>` does with `<option selected>`, and
|
|
220
|
+
* what `Option.defaultSelected` says: a form reset returns to it. A
|
|
221
|
+
* `default-value` on the select wins.
|
|
222
|
+
*
|
|
223
|
+
* The second keeps the trigger and the form in agreement. Measured before
|
|
224
|
+
* this: removing the chosen option made the trigger say "Choose" while the
|
|
225
|
+
* form still submitted the removed value. Only an option seen before is
|
|
226
|
+
* dropped, so a value set ahead of options that load later is kept.
|
|
227
|
+
*/
|
|
228
|
+
private reconcileOptions;
|
|
165
229
|
/** Reports the answer and the verdict on it. */
|
|
166
230
|
private sync;
|
|
167
231
|
protected willUpdate(changed: PropertyValues): void;
|
|
@@ -198,8 +262,10 @@ export declare class HelixHeadlessSelect extends HelixHeadlessSelect_base {
|
|
|
198
262
|
* native `<select>` says both, and this names the trigger by both, the label
|
|
199
263
|
* and then the value, in that order.
|
|
200
264
|
*
|
|
201
|
-
* A consumer's own `aria-labelledby` on the host wins,
|
|
202
|
-
*
|
|
265
|
+
* A consumer's own `aria-labelledby` on the host wins, and so does a
|
|
266
|
+
* `<label>` that names the host where there is no `label`. Both are carried
|
|
267
|
+
* to the trigger by `AriaForwardController`, which reads the value after
|
|
268
|
+
* them, so nothing is written here to fight it.
|
|
203
269
|
*/
|
|
204
270
|
private get triggerNames();
|
|
205
271
|
protected render(): TemplateResult;
|
|
@@ -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 { AriaForwardController, customElement, HelixFormElement, hostAria, MixinControllerOpen, MixinControllerValue, MixinField, PopoverController, reportValues, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, HelixFormElement, hostAria, MixinControllerOpen, MixinControllerValue, MixinField, PopoverController, reportValues, typesCharacter, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretDown } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
@@ -16,6 +16,10 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
16
16
|
constructor() {
|
|
17
17
|
super(...arguments);
|
|
18
18
|
this.aria = new AriaForwardController(this, {
|
|
19
|
+
labelledByAfter: () => {
|
|
20
|
+
const value = this.partNamed('value');
|
|
21
|
+
return value ? [value] : [];
|
|
22
|
+
},
|
|
19
23
|
target: () => this.partNamed('trigger'),
|
|
20
24
|
});
|
|
21
25
|
this.emptyText = 'No matches';
|
|
@@ -36,6 +40,9 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
36
40
|
placement: 'bottom-start',
|
|
37
41
|
surface: () => this.partNamed('surface'),
|
|
38
42
|
});
|
|
43
|
+
this.opening = 'selected';
|
|
44
|
+
this.seen = new Set();
|
|
45
|
+
this.watchers = [];
|
|
39
46
|
this.handleChange = (event) => {
|
|
40
47
|
const next = event.detail.value;
|
|
41
48
|
if (!this.requestValue(next)) {
|
|
@@ -48,9 +55,39 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
48
55
|
event.stopPropagation();
|
|
49
56
|
};
|
|
50
57
|
this.handleTriggerKeydown = (event) => {
|
|
51
|
-
|
|
58
|
+
const listbox = this.listbox;
|
|
59
|
+
if (typesCharacter(event) && (event.key !== ' ' || listbox?.typing)) {
|
|
52
60
|
event.preventDefault();
|
|
53
|
-
|
|
61
|
+
const match = listbox?.typeaheadTo(event.key);
|
|
62
|
+
if (match === undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (this.multiple) {
|
|
66
|
+
this.opening = 'selected';
|
|
67
|
+
this.requestOpen(true);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.requestValue(match);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const opens = {
|
|
75
|
+
' ': 'selected',
|
|
76
|
+
ArrowDown: 'selected',
|
|
77
|
+
ArrowUp: 'last',
|
|
78
|
+
End: 'last',
|
|
79
|
+
Enter: 'selected',
|
|
80
|
+
Home: 'first',
|
|
81
|
+
};
|
|
82
|
+
const where = opens[event.key];
|
|
83
|
+
if (where !== undefined) {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
this.openAt(where === 'last' && event.key === 'ArrowUp' && this.value !== '' ? 'selected' : where);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
this.handleListKeydown = (event) => {
|
|
89
|
+
if (event.key === 'Tab' && this.open) {
|
|
90
|
+
this.requestOpen(false);
|
|
54
91
|
}
|
|
55
92
|
};
|
|
56
93
|
}
|
|
@@ -58,10 +95,7 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
58
95
|
return this.listbox?.selectedIndex ?? -1;
|
|
59
96
|
}
|
|
60
97
|
set selectedIndex(at) {
|
|
61
|
-
|
|
62
|
-
if (listbox) {
|
|
63
|
-
listbox.selectedIndex = at;
|
|
64
|
-
}
|
|
98
|
+
this.value = this.listbox?.options[at]?.value ?? '';
|
|
65
99
|
}
|
|
66
100
|
get type() {
|
|
67
101
|
return this.multiple ? 'select-multiple' : 'select-one';
|
|
@@ -79,6 +113,30 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
79
113
|
}
|
|
80
114
|
return chosen.size > 0 && !this.childrenKnown ? nothing : this.placeholder;
|
|
81
115
|
}
|
|
116
|
+
connectedCallback() {
|
|
117
|
+
super.connectedCallback();
|
|
118
|
+
const words = new MutationObserver(() => this.requestUpdate());
|
|
119
|
+
words.observe(this, {
|
|
120
|
+
attributeFilter: ['label', 'value'],
|
|
121
|
+
characterData: true,
|
|
122
|
+
childList: true,
|
|
123
|
+
subtree: true,
|
|
124
|
+
});
|
|
125
|
+
const naming = new MutationObserver(() => this.requestUpdate());
|
|
126
|
+
naming.observe(this, { attributeFilter: ['aria-labelledby'] });
|
|
127
|
+
this.watchers = [words, naming];
|
|
128
|
+
}
|
|
129
|
+
disconnectedCallback() {
|
|
130
|
+
super.disconnectedCallback();
|
|
131
|
+
for (const watcher of this.watchers) {
|
|
132
|
+
watcher.disconnect();
|
|
133
|
+
}
|
|
134
|
+
this.watchers = [];
|
|
135
|
+
}
|
|
136
|
+
openAt(where) {
|
|
137
|
+
this.opening = where;
|
|
138
|
+
this.requestOpen(true);
|
|
139
|
+
}
|
|
82
140
|
close() {
|
|
83
141
|
if (!this.open) {
|
|
84
142
|
return;
|
|
@@ -86,6 +144,24 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
86
144
|
this.requestOpen(false);
|
|
87
145
|
this.partNamed('trigger')?.focus();
|
|
88
146
|
}
|
|
147
|
+
reconcileOptions() {
|
|
148
|
+
const options = optionsAmong(this.knownChildren);
|
|
149
|
+
if (this.defaultValue === '' && !this.hasAttribute('default-value')) {
|
|
150
|
+
const declared = options
|
|
151
|
+
.filter((option) => option.defaultSelected)
|
|
152
|
+
.map((option) => option.value);
|
|
153
|
+
if (declared.length > 0) {
|
|
154
|
+
this.defaultValue = this.multiple ? declared.join(',') : (declared.at(-1) ?? '');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const present = new Set(options.map((option) => option.value));
|
|
158
|
+
const held = this.value === '' ? [] : this.value.split(',');
|
|
159
|
+
const gone = held.filter((value) => this.seen.has(value) && !present.has(value));
|
|
160
|
+
this.seen = present;
|
|
161
|
+
if (gone.length > 0) {
|
|
162
|
+
this.value = held.filter((value) => !gone.includes(value)).join(',');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
89
165
|
sync() {
|
|
90
166
|
if (this.multiple) {
|
|
91
167
|
reportValues(this.internals, this.name, this.value === '' ? [] : this.value.split(','));
|
|
@@ -98,6 +174,9 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
98
174
|
willUpdate(changed) {
|
|
99
175
|
super.willUpdate(changed);
|
|
100
176
|
hostAria(this.internals, { label: this.label });
|
|
177
|
+
if (this.childrenKnown) {
|
|
178
|
+
this.reconcileOptions();
|
|
179
|
+
}
|
|
101
180
|
}
|
|
102
181
|
firstUpdated(changed) {
|
|
103
182
|
super.firstUpdated(changed);
|
|
@@ -109,6 +188,8 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
109
188
|
this.linkHost();
|
|
110
189
|
if (this.open) {
|
|
111
190
|
if (changed.has('open')) {
|
|
191
|
+
this.listbox?.highlight(this.opening);
|
|
192
|
+
this.opening = 'selected';
|
|
112
193
|
this.listbox?.focus();
|
|
113
194
|
}
|
|
114
195
|
}
|
|
@@ -129,7 +210,9 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
129
210
|
.value=${this.value}
|
|
130
211
|
?multiple=${this.multiple}
|
|
131
212
|
?filterable=${this.filterable}
|
|
213
|
+
manual
|
|
132
214
|
aria-label=${this.label || nothing}
|
|
215
|
+
@keydown=${this.handleListKeydown}
|
|
133
216
|
@helix-before-change=${this.handleChange}
|
|
134
217
|
@change=${this.stopInner}
|
|
135
218
|
@input=${this.stopInner}
|
|
@@ -147,7 +230,8 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
147
230
|
</slot>`;
|
|
148
231
|
}
|
|
149
232
|
get triggerNames() {
|
|
150
|
-
|
|
233
|
+
const outside = (this.internals.labels?.length ?? 0) > 0 && !this.labeled;
|
|
234
|
+
if (this.hasAttribute('aria-labelledby') || outside) {
|
|
151
235
|
return nothing;
|
|
152
236
|
}
|
|
153
237
|
return this.labeled ? `${this.labelId} value` : 'value';
|
|
@@ -4,9 +4,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
4
4
|
* @summary A placeholder shown in place of content that is still loading.
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* A skeleton is hidden from screen readers unless it has a `label`. An empty
|
|
8
|
+
* placeholder has nothing to say, and a page of them would be a page of
|
|
9
|
+
* unnamed shapes. Tell a screen reader that a region is loading on the region
|
|
10
|
+
* itself: set `aria-busy="true"` on it while it loads, and put a `Loader` or a
|
|
11
|
+
* status message in it. Set `label` on one skeleton to have it read out by
|
|
12
|
+
* name, such as "Loading profile", when a reader reaches it. The role and the
|
|
13
|
+
* name are set through `ElementInternals`, which writes no attributes.
|
|
10
14
|
*
|
|
11
15
|
* There is no `shape` property. Whether a placeholder is a line, a block, or a
|
|
12
16
|
* circle is presentation, which belongs to the styled tier.
|
|
@@ -20,6 +24,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
20
24
|
* @csspart skeleton - The placeholder shape.
|
|
21
25
|
*/
|
|
22
26
|
export declare class HelixHeadlessSkeleton extends HelixElement {
|
|
27
|
+
/**
|
|
28
|
+
* What is loading, read out as the placeholder's name, such as "Loading
|
|
29
|
+
* profile".
|
|
30
|
+
*
|
|
31
|
+
* Absent hides the placeholder from screen readers.
|
|
32
|
+
*/
|
|
33
|
+
label: string;
|
|
23
34
|
protected willUpdate(changed: PropertyValues): void;
|
|
24
35
|
protected render(): TemplateResult;
|
|
25
36
|
}
|