@fusion.dev/helix-headless 0.142.1 → 0.144.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 +25381 -1202
- 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.d.ts +12 -4
- package/dist/area-chart/root/area-chart-root.js +39 -11
- 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 +17 -1
- package/dist/avatar/root/avatar-root.js +31 -10
- package/dist/badge/root/badge-root.d.ts +12 -0
- package/dist/badge/root/badge-root.js +1 -1
- package/dist/bar-chart/root/bar-chart-root.d.ts +30 -5
- package/dist/bar-chart/root/bar-chart-root.js +58 -21
- 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 +84 -1
- package/dist/button/root/button-root.js +81 -3
- package/dist/callout/root/callout-root.d.ts +32 -6
- package/dist/callout/root/callout-root.js +26 -7
- package/dist/canvas/root/canvas-root.d.ts +1 -0
- package/dist/canvas/root/canvas-root.js +10 -1
- package/dist/card/root/card-root.d.ts +30 -1
- package/dist/card/root/card-root.js +38 -5
- 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 +9 -3
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +116 -33
- package/dist/checkbox-group/root/checkbox-group-root.js +141 -44
- package/dist/code/root/code-root.d.ts +1 -1
- package/dist/code-block/lines.d.ts +13 -0
- package/dist/code-block/lines.js +2 -0
- package/dist/code-block/root/code-block-root.d.ts +49 -0
- package/dist/code-block/root/code-block-root.js +44 -3
- package/dist/color-input/root/color-input-root.d.ts +60 -2
- package/dist/color-input/root/color-input-root.js +55 -7
- package/dist/color-picker/channels.d.ts +26 -0
- package/dist/color-picker/channels.js +84 -0
- package/dist/color-picker/root/color-picker-root.d.ts +118 -12
- package/dist/color-picker/root/color-picker-root.js +183 -13
- package/dist/combobox/root/combobox-root.d.ts +126 -3
- package/dist/combobox/root/combobox-root.js +224 -21
- package/dist/command/root/command-root.d.ts +47 -10
- package/dist/command/root/command-root.js +45 -4
- package/dist/copy-button/from.d.ts +22 -0
- package/dist/copy-button/from.js +28 -0
- package/dist/copy-button/root/copy-button-root.d.ts +48 -7
- package/dist/copy-button/root/copy-button-root.js +54 -8
- 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 +123 -9
- package/dist/date-input/root/date-input-root.js +147 -45
- package/dist/date-picker/root/date-picker-root.d.ts +229 -18
- package/dist/date-picker/root/date-picker-root.js +342 -46
- package/dist/dialog/description.d.ts +9 -0
- package/dist/dialog/description.js +14 -0
- package/dist/dialog/root/dialog-root.d.ts +101 -10
- package/dist/dialog/root/dialog-root.js +83 -4
- package/dist/donut-chart/root/donut-chart-root.d.ts +61 -16
- package/dist/donut-chart/root/donut-chart-root.js +48 -13
- package/dist/drawer/root/drawer-root.d.ts +93 -7
- package/dist/drawer/root/drawer-root.js +126 -5
- package/dist/drawer/swipe-start.d.ts +9 -0
- package/dist/drawer/swipe-start.js +12 -0
- 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 +42 -13
- package/dist/empty/root/empty-root.d.ts +1 -1
- package/dist/file-input/root/file-input-root.d.ts +65 -1
- package/dist/file-input/root/file-input-root.js +76 -12
- package/dist/file-input/screen.d.ts +35 -0
- package/dist/file-input/screen.js +49 -0
- 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 +43 -6
- package/dist/filter-input/root/filter-input-root.js +89 -10
- package/dist/filter-input/suggest.d.ts +17 -0
- package/dist/filter-input/suggest.js +23 -0
- package/dist/format-date/options.d.ts +33 -0
- package/dist/format-date/options.js +42 -0
- package/dist/format-date/root/format-date-root.d.ts +62 -3
- package/dist/format-date/root/format-date-root.js +78 -37
- 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 +47 -9
- package/dist/heatmap/root/heatmap-root.js +46 -18
- package/dist/icon/index.d.ts +2 -0
- package/dist/icon/index.js +2 -0
- package/dist/icon/registry.d.ts +37 -0
- package/dist/icon/registry.js +83 -0
- package/dist/icon/root/icon-root.d.ts +53 -6
- package/dist/icon/root/icon-root.js +72 -4
- package/dist/icon/root/index.d.ts +2 -0
- package/dist/icon/root/index.js +2 -0
- package/dist/icon/set.d.ts +1 -0
- package/dist/icon/set.js +3 -0
- package/dist/icon-button/root/icon-button-root.d.ts +1 -1
- package/dist/image/root/image-root.d.ts +23 -2
- package/dist/image/root/image-root.js +23 -4
- package/dist/input/root/input-root.d.ts +62 -9
- package/dist/input/root/input-root.js +61 -5
- package/dist/kbd/root/kbd-root.d.ts +26 -11
- package/dist/kbd/root/kbd-root.js +35 -38
- package/dist/kbd/shortcut.d.ts +18 -0
- package/dist/kbd/shortcut.js +35 -0
- package/dist/legend/hidden-series.d.ts +8 -0
- package/dist/legend/hidden-series.js +13 -0
- package/dist/legend/root/legend-root.d.ts +63 -0
- package/dist/legend/root/legend-root.js +61 -7
- package/dist/lightbox/item/lightbox-item.d.ts +17 -1
- package/dist/lightbox/item/lightbox-item.js +10 -1
- package/dist/lightbox/root/lightbox-root.d.ts +77 -6
- package/dist/lightbox/root/lightbox-root.js +116 -5
- package/dist/line-chart/root/line-chart-root.d.ts +29 -4
- package/dist/line-chart/root/line-chart-root.js +58 -17
- package/dist/list/item/list-item.d.ts +11 -5
- package/dist/list/item/list-item.js +5 -6
- package/dist/listbox/range.d.ts +17 -0
- package/dist/listbox/range.js +5 -0
- package/dist/listbox/root/listbox-root.d.ts +184 -4
- package/dist/listbox/root/listbox-root.js +229 -23
- package/dist/loader/busy.d.ts +17 -0
- package/dist/loader/busy.js +24 -0
- package/dist/loader/root/loader-root.d.ts +37 -0
- package/dist/loader/root/loader-root.js +52 -3
- 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 +13 -0
- package/dist/meter/root/meter-root.js +24 -5
- package/dist/navigation/root/navigation-root.d.ts +10 -2
- package/dist/navigation/root/navigation-root.js +13 -2
- package/dist/notifications/feed-keys.d.ts +10 -0
- package/dist/notifications/feed-keys.js +14 -0
- package/dist/notifications/group/notification-group.d.ts +1 -0
- package/dist/notifications/notification/notification.d.ts +29 -0
- package/dist/notifications/notification/notification.js +51 -3
- package/dist/notifications/root/notifications-root.d.ts +49 -0
- package/dist/notifications/root/notifications-root.js +76 -2
- package/dist/number-input/root/number-input-root.d.ts +109 -7
- package/dist/number-input/root/number-input-root.js +109 -21
- package/dist/object-inspector/root/object-inspector-root.d.ts +49 -5
- package/dist/object-inspector/root/object-inspector-root.js +92 -12
- package/dist/option/root/option-root.d.ts +58 -6
- package/dist/option/root/option-root.js +35 -5
- 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/format.d.ts +7 -0
- package/dist/pin-input/format.js +16 -0
- package/dist/pin-input/root/pin-input-root.d.ts +37 -0
- package/dist/pin-input/root/pin-input-root.js +48 -7
- package/dist/popover/root/popover-root.d.ts +78 -3
- package/dist/popover/root/popover-root.js +74 -5
- package/dist/progress/root/progress-root.d.ts +23 -1
- package/dist/progress/root/progress-root.js +40 -10
- package/dist/qr-code/export.d.ts +31 -0
- package/dist/qr-code/export.js +10 -0
- package/dist/qr-code/root/qr-code-root.d.ts +45 -0
- package/dist/qr-code/root/qr-code-root.js +78 -3
- package/dist/radio/group/radio-group.d.ts +64 -0
- package/dist/radio/group/radio-group.js +66 -12
- 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 +49 -1
- package/dist/rating/root/rating-root.js +69 -10
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
- package/dist/scatter-chart/root/scatter-chart-root.js +40 -14
- package/dist/select/root/select-root.d.ts +93 -3
- package/dist/select/root/select-root.js +111 -13
- package/dist/skeleton/root/skeleton-root.d.ts +39 -3
- package/dist/skeleton/root/skeleton-root.js +33 -4
- 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 +59 -2
- package/dist/slider/root/slider-root.js +82 -8
- 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 +9 -1
- package/dist/stat/root/stat-root.js +15 -2
- package/dist/steps/root/steps-root.js +1 -1
- package/dist/steps/step/step.d.ts +1 -0
- package/dist/steps/step/step.js +1 -1
- package/dist/switch/root/switch-root.d.ts +14 -2
- package/dist/switch/root/switch-root.js +7 -1
- 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/group/tag-group.d.ts +107 -0
- package/dist/tag/group/tag-group.js +157 -0
- package/dist/tag/index.d.ts +1 -0
- package/dist/tag/index.js +1 -0
- package/dist/tag/root/tag-root.d.ts +83 -1
- package/dist/tag/root/tag-root.js +87 -17
- package/dist/tag/selection.d.ts +10 -0
- package/dist/tag/selection.js +12 -0
- package/dist/textarea/grow.d.ts +22 -0
- package/dist/textarea/grow.js +20 -0
- package/dist/textarea/root/textarea-root.d.ts +13 -0
- package/dist/textarea/root/textarea-root.js +10 -2
- package/dist/time-input/root/time-input-root.d.ts +71 -6
- package/dist/time-input/root/time-input-root.js +110 -44
- package/dist/time-picker/root/time-picker-root.d.ts +155 -26
- package/dist/time-picker/root/time-picker-root.js +192 -63
- package/dist/toast/hotkey.d.ts +22 -0
- package/dist/toast/hotkey.js +23 -0
- package/dist/toast/item/toast-item.d.ts +16 -1
- package/dist/toast/item/toast-item.js +41 -13
- package/dist/toast/root/toast-root.d.ts +105 -8
- package/dist/toast/root/toast-root.js +209 -4
- package/dist/toggle/group/toggle-group.d.ts +95 -16
- package/dist/toggle/group/toggle-group.js +99 -13
- package/dist/toggle/root/toggle-root.d.ts +26 -11
- package/dist/toggle/root/toggle-root.js +14 -6
- package/dist/toolbar/root/toolbar-root.d.ts +55 -15
- package/dist/toolbar/root/toolbar-root.js +99 -16
- package/dist/tooltip/focus-target.d.ts +6 -0
- package/dist/tooltip/focus-target.js +9 -0
- package/dist/tooltip/root/tooltip-root.d.ts +73 -15
- package/dist/tooltip/root/tooltip-root.js +96 -7
- package/dist/tour/naming.d.ts +30 -0
- package/dist/tour/naming.js +10 -0
- package/dist/tour/root/tour-root.d.ts +67 -3
- package/dist/tour/root/tour-root.js +105 -17
- package/dist/tour/stop/tour-stop.d.ts +65 -3
- package/dist/tour/stop/tour-stop.js +61 -11
- package/dist/tour/wait.d.ts +11 -0
- package/dist/tour/wait.js +19 -0
- 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 +36 -1
- package/dist/trend/root/trend-root.js +41 -15
- 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 +9140 -1149
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +2089 -267
|
@@ -4,28 +4,45 @@ 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, DisabledCascadeController, HelixFormElement, hostAria, IndicatorController, KeyboardNavigationController, MixinControllerValue, MixinField, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { commaList, customElement, DisabledCascadeController, HelixFormElement, hostAria, IndicatorController, KeyboardNavigationController, MixinControllerValue, MixinField, rovingStop, toggleValue, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessToggle } from '../root/toggle-root.js';
|
|
11
11
|
let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
|
+
this.lastFocused = undefined;
|
|
15
|
+
this.noteFocus = (event) => {
|
|
16
|
+
const item = this.items.find((one) => one === event.target);
|
|
17
|
+
if (this.multiple && item && item !== this.lastFocused) {
|
|
18
|
+
this.lastFocused = item;
|
|
19
|
+
this.sync();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
this.focusable = true;
|
|
14
23
|
this.indicator = new IndicatorController(this, {
|
|
15
|
-
active: () => this.items.find((item) => item.value === this.value),
|
|
24
|
+
active: () => this.multiple ? undefined : this.items.find((item) => item.value === this.value),
|
|
16
25
|
indicator: () => this.partNamed('indicator'),
|
|
17
26
|
observe: () => this.items,
|
|
18
27
|
surface: () => this.partNamed('track'),
|
|
19
28
|
});
|
|
20
|
-
this.keys = new KeyboardNavigationController(this, {
|
|
29
|
+
this.keys = new KeyboardNavigationController(this, {
|
|
30
|
+
items: () => this.items,
|
|
31
|
+
selects: () => !this.multiple,
|
|
32
|
+
});
|
|
21
33
|
this.cascade = new DisabledCascadeController(this, {
|
|
22
34
|
items: () => this.items,
|
|
23
35
|
});
|
|
24
36
|
this.handleSelection = (event) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
queueMicrotask(() => {
|
|
38
|
+
if (event.defaultPrevented) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const value = event.detail.value;
|
|
42
|
+
this.requestValue(this.multiple
|
|
43
|
+
? toggleValue(this.values, value, this.items.map((item) => item.value)).join(',')
|
|
44
|
+
: value);
|
|
45
|
+
});
|
|
29
46
|
};
|
|
30
47
|
}
|
|
31
48
|
adopt() {
|
|
@@ -38,23 +55,59 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
38
55
|
}
|
|
39
56
|
}
|
|
40
57
|
sync() {
|
|
41
|
-
const active = this.items.find((item) => item.value === this.value);
|
|
42
|
-
const
|
|
58
|
+
const active = this.multiple ? undefined : this.items.find((item) => item.value === this.value);
|
|
59
|
+
const pressed = new Set(this.values);
|
|
60
|
+
const entry = this.focusable ? this.entry() : undefined;
|
|
43
61
|
for (const item of this.items) {
|
|
44
62
|
item.selected = item === active;
|
|
63
|
+
item.pressed = this.multiple ? pressed.has(item.value) : undefined;
|
|
45
64
|
item.focusable = item === entry;
|
|
46
65
|
}
|
|
47
66
|
}
|
|
67
|
+
entry() {
|
|
68
|
+
const items = this.items;
|
|
69
|
+
if (this.multiple) {
|
|
70
|
+
return rovingStop(items, this.lastFocused, {
|
|
71
|
+
disabled: (item) => item.disabled,
|
|
72
|
+
selected: (item) => item.pressed === true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const active = items.find((item) => item.value === this.value);
|
|
76
|
+
return active && !active.disabled ? active : items.find((item) => !item.disabled);
|
|
77
|
+
}
|
|
78
|
+
get multiple() {
|
|
79
|
+
return this.selectionMode === 'multiple';
|
|
80
|
+
}
|
|
81
|
+
get focusTarget() {
|
|
82
|
+
return (super.focusTarget ??
|
|
83
|
+
this.entry()?.shadowRoot?.querySelector('[part="item"]') ??
|
|
84
|
+
null);
|
|
85
|
+
}
|
|
48
86
|
get items() {
|
|
49
|
-
return [...this.children].filter((child) => child instanceof HelixHeadlessToggle);
|
|
87
|
+
return [...(this.children ?? [])].filter((child) => child instanceof HelixHeadlessToggle);
|
|
88
|
+
}
|
|
89
|
+
get answered() {
|
|
90
|
+
if (this.value === '') {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (this.multiple) {
|
|
94
|
+
return this.children === undefined || this.values.length > 0;
|
|
95
|
+
}
|
|
96
|
+
if (this.children === undefined) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const items = this.items;
|
|
100
|
+
return items.length === 0 || items.some((item) => item.value === this.value);
|
|
50
101
|
}
|
|
51
102
|
connectedCallback() {
|
|
52
103
|
super.connectedCallback();
|
|
53
104
|
this.addEventListener('helix-select', this.handleSelection);
|
|
105
|
+
this.addEventListener('focusin', this.noteFocus);
|
|
54
106
|
}
|
|
55
107
|
disconnectedCallback() {
|
|
56
108
|
super.disconnectedCallback();
|
|
57
109
|
this.removeEventListener('helix-select', this.handleSelection);
|
|
110
|
+
this.removeEventListener('focusin', this.noteFocus);
|
|
58
111
|
}
|
|
59
112
|
firstUpdated(changed) {
|
|
60
113
|
super.firstUpdated(changed);
|
|
@@ -70,11 +123,31 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
70
123
|
willUpdate(changed) {
|
|
71
124
|
super.willUpdate(changed);
|
|
72
125
|
this.adopt();
|
|
73
|
-
hostAria(this.internals, { label: this.label, role: 'radiogroup' });
|
|
74
|
-
this.internals.
|
|
126
|
+
hostAria(this.internals, { label: this.label, role: this.multiple ? 'toolbar' : 'radiogroup' });
|
|
127
|
+
this.internals.ariaOrientation = this.orientation ?? null;
|
|
128
|
+
this.internals.setFormValue(this.formValue());
|
|
129
|
+
}
|
|
130
|
+
formValue() {
|
|
131
|
+
if (!this.answered) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (!this.multiple || this.name === '') {
|
|
135
|
+
return this.value;
|
|
136
|
+
}
|
|
137
|
+
const data = new FormData();
|
|
138
|
+
for (const value of this.values) {
|
|
139
|
+
data.append(this.name, value);
|
|
140
|
+
}
|
|
141
|
+
return data;
|
|
75
142
|
}
|
|
76
143
|
validate() {
|
|
77
|
-
this.reportRequired(this.
|
|
144
|
+
this.reportRequired(!this.answered, 'Choose one to continue.', 'track');
|
|
145
|
+
}
|
|
146
|
+
get values() {
|
|
147
|
+
return this.multiple ? commaList(this.value) : this.value === '' ? [] : [this.value];
|
|
148
|
+
}
|
|
149
|
+
set values(next) {
|
|
150
|
+
this.value = next.join(',');
|
|
78
151
|
}
|
|
79
152
|
render() {
|
|
80
153
|
const inside = this.labelPlacement === 'start';
|
|
@@ -90,6 +163,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
90
163
|
this.adopt();
|
|
91
164
|
this.sync();
|
|
92
165
|
this.indicator.place();
|
|
166
|
+
this.requestUpdate();
|
|
93
167
|
}}
|
|
94
168
|
></slot>
|
|
95
169
|
</div>
|
|
@@ -98,9 +172,21 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
98
172
|
${this.renderHint()}${this.renderFeedback()}`;
|
|
99
173
|
}
|
|
100
174
|
};
|
|
175
|
+
__decorate([
|
|
176
|
+
property({ attribute: false })
|
|
177
|
+
], HelixHeadlessToggleGroup.prototype, "focusable", void 0);
|
|
101
178
|
__decorate([
|
|
102
179
|
property({ attribute: 'label-placement', reflect: true })
|
|
103
180
|
], HelixHeadlessToggleGroup.prototype, "labelPlacement", void 0);
|
|
181
|
+
__decorate([
|
|
182
|
+
property({ reflect: true })
|
|
183
|
+
], HelixHeadlessToggleGroup.prototype, "orientation", void 0);
|
|
184
|
+
__decorate([
|
|
185
|
+
property({ attribute: 'selection-mode', reflect: true })
|
|
186
|
+
], HelixHeadlessToggleGroup.prototype, "selectionMode", void 0);
|
|
187
|
+
__decorate([
|
|
188
|
+
property({ attribute: false })
|
|
189
|
+
], HelixHeadlessToggleGroup.prototype, "values", null);
|
|
104
190
|
HelixHeadlessToggleGroup = __decorate([
|
|
105
191
|
customElement('helix-headless-toggle-group')
|
|
106
192
|
], HelixHeadlessToggleGroup);
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, HostClickController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessToggle_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinSelectableItemInterface) & typeof HelixElement;
|
|
4
4
|
/**
|
|
5
5
|
* @summary One option in a toggle group.
|
|
6
6
|
*
|
|
7
7
|
* @description
|
|
8
|
-
* Renders a `<button role="radio">` for use as a form control.
|
|
8
|
+
* Renders a `<button role="radio">` for use as a form control. In a group with
|
|
9
|
+
* `selection-mode="multiple"` it is a `<button>` that says whether it is
|
|
10
|
+
* pressed with `aria-pressed`.
|
|
9
11
|
*
|
|
10
12
|
* Do not use it for navigation. For navigation, use links marked with
|
|
11
13
|
* `aria-current` and styled by the `data-navigation-item` recipe in the base
|
|
@@ -23,9 +25,12 @@ declare const HelixHeadlessToggle_base: (new (...args: any[]) => import("@fusion
|
|
|
23
25
|
*
|
|
24
26
|
* @csspart item - The radio button.
|
|
25
27
|
*
|
|
28
|
+
* @cssstate pressed - The item is pressed, in a group where several can be.
|
|
26
29
|
* @cssstate selected - The item is chosen.
|
|
27
30
|
*/
|
|
28
31
|
export declare class HelixHeadlessToggle extends HelixHeadlessToggle_base {
|
|
32
|
+
/** A click on the element itself, `click()` included, chooses it, as on `Radio`. */
|
|
33
|
+
protected readonly clicks: HostClickController;
|
|
29
34
|
/**
|
|
30
35
|
* Whether the markup declares the item chosen. A form reset returns to it.
|
|
31
36
|
*
|
|
@@ -56,23 +61,33 @@ export declare class HelixHeadlessToggle extends HelixHeadlessToggle_base {
|
|
|
56
61
|
get defaultSelected(): boolean;
|
|
57
62
|
set defaultSelected(next: boolean);
|
|
58
63
|
/**
|
|
59
|
-
* Whether the item holds the group's tab stop
|
|
64
|
+
* Whether the item holds the group's tab stop. Unset, the chosen item holds
|
|
65
|
+
* it.
|
|
60
66
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
67
|
+
* Written by the group on every item: `true` for the chosen item, or for the
|
|
68
|
+
* first item that can be chosen while there is no selection to carry the
|
|
69
|
+
* stop, and `false` for every item, the chosen one too, while a toolbar the
|
|
70
|
+
* group sits in holds the stop. No attribute: it is coordination between
|
|
71
|
+
* elements, and `tabindex` on a host the consumer rendered is the change a
|
|
72
|
+
* React consumer's hydration reports and does not patch up.
|
|
66
73
|
*/
|
|
67
|
-
focusable
|
|
74
|
+
focusable?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the item is pressed, in a group where several can be, and
|
|
77
|
+
* `undefined` in a group where one is chosen.
|
|
78
|
+
*
|
|
79
|
+
* Written by the group, like `selected`, so no attribute.
|
|
80
|
+
*/
|
|
81
|
+
pressed?: boolean | undefined;
|
|
82
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
68
83
|
/**
|
|
69
84
|
* Asks for the render that follows a hydration, so the stop stops being every
|
|
70
85
|
* choice's.
|
|
71
86
|
*
|
|
72
87
|
* `tabindex` reads `childrenKnown` and a hydrating render is one where that is
|
|
73
88
|
* false, which is what keeps the hydrating render the server's. Nothing else
|
|
74
|
-
*
|
|
75
|
-
*
|
|
89
|
+
* is sure to ask: a write from the group that leaves `focusable` as it was
|
|
90
|
+
* re-renders nothing.
|
|
76
91
|
*/
|
|
77
92
|
protected firstUpdated(changed: PropertyValues): void;
|
|
78
93
|
protected render(): TemplateResult;
|
|
@@ -4,13 +4,13 @@ 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, HelixElement, MixinSelectableItem } from '@fusion.dev/helix-core';
|
|
8
|
-
import { html } from 'lit';
|
|
7
|
+
import { customElement, HelixElement, HostClickController, MixinSelectableItem, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(HelixElement) {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
|
-
this.
|
|
13
|
+
this.clicks = new HostClickController(this, () => this.partNamed('item'));
|
|
14
14
|
}
|
|
15
15
|
get defaultSelected() {
|
|
16
16
|
return this.hasAttribute('selected');
|
|
@@ -18,6 +18,10 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
|
|
|
18
18
|
set defaultSelected(next) {
|
|
19
19
|
this.toggleAttribute('selected', next);
|
|
20
20
|
}
|
|
21
|
+
willUpdate(changed) {
|
|
22
|
+
super.willUpdate(changed);
|
|
23
|
+
this.toggleState('pressed', this.pressed === true);
|
|
24
|
+
}
|
|
21
25
|
firstUpdated(changed) {
|
|
22
26
|
super.firstUpdated(changed);
|
|
23
27
|
this.finishHydration();
|
|
@@ -26,10 +30,11 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
|
|
|
26
30
|
return html `<button
|
|
27
31
|
part="item"
|
|
28
32
|
type="button"
|
|
29
|
-
role
|
|
30
|
-
aria-checked=${this.selected ? 'true' : 'false'}
|
|
33
|
+
role=${this.pressed === undefined ? 'radio' : nothing}
|
|
34
|
+
aria-checked=${this.pressed === undefined ? (this.selected ? 'true' : 'false') : nothing}
|
|
35
|
+
aria-pressed=${this.pressed === undefined ? nothing : String(this.pressed)}
|
|
31
36
|
?disabled=${this.disabled}
|
|
32
|
-
tabindex=${this.
|
|
37
|
+
tabindex=${(this.focusable ?? this.selected) || !this.childrenKnown ? '0' : '-1'}
|
|
33
38
|
@click=${() => this.select()}
|
|
34
39
|
>
|
|
35
40
|
<slot></slot>
|
|
@@ -39,6 +44,9 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
|
|
|
39
44
|
__decorate([
|
|
40
45
|
property({ attribute: false })
|
|
41
46
|
], HelixHeadlessToggle.prototype, "focusable", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
property({ attribute: false })
|
|
49
|
+
], HelixHeadlessToggle.prototype, "pressed", void 0);
|
|
42
50
|
HelixHeadlessToggle = __decorate([
|
|
43
51
|
customElement('helix-headless-toggle')
|
|
44
52
|
], HelixHeadlessToggle);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary A group of controls that share one tab stop.
|
|
@@ -9,7 +9,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
9
9
|
*
|
|
10
10
|
* The toolbar is one tab stop, and the arrow keys move between its controls, so
|
|
11
11
|
* three toolbars take three presses of Tab to pass. Any element the browser can
|
|
12
|
-
* focus counts as a control, including your own elements.
|
|
12
|
+
* focus counts as a control, including your own elements. A child that takes no
|
|
13
|
+
* focus, such as a divider, is skipped. A menu is reached at its trigger.
|
|
14
|
+
*
|
|
15
|
+
* A nested composite, such as a toggle group, is one stop on the toolbar's
|
|
16
|
+
* axis: the toolbar's arrows move past it, and the arrows across the toolbar
|
|
17
|
+
* move inside it. A composite with a `focusable` property, as Toggle Group
|
|
18
|
+
* has, gives up its own tab stop while the toolbar's is elsewhere, so Tab
|
|
19
|
+
* stops once in the toolbar. A composite without one keeps its own tab stop.
|
|
13
20
|
*
|
|
14
21
|
* @since 0.1.0
|
|
15
22
|
* @status stable
|
|
@@ -24,27 +31,50 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
24
31
|
export declare class HelixHeadlessToolbar extends HelixElement {
|
|
25
32
|
/**
|
|
26
33
|
* The toolbar's name, read by screen readers.
|
|
34
|
+
*
|
|
35
|
+
* An `aria-label` or `aria-labelledby` on the host names the toolbar too,
|
|
36
|
+
* and wins over this.
|
|
27
37
|
*/
|
|
28
38
|
label: string;
|
|
39
|
+
/** Carries a host `aria-label` and `aria-labelledby` to the toolbar. */
|
|
40
|
+
protected readonly aria: AriaForwardController;
|
|
41
|
+
/**
|
|
42
|
+
* Whether the stop has been written onto controls this package does not
|
|
43
|
+
* know, which happens on the first focus. From then on, every move of the
|
|
44
|
+
* stop has to write them too, or the one at `0` can be removed or disabled
|
|
45
|
+
* and leave every other one at `-1`.
|
|
46
|
+
*/
|
|
47
|
+
private wroteForeign;
|
|
29
48
|
/**
|
|
30
49
|
* The direction the arrow keys move focus in.
|
|
31
50
|
*/
|
|
32
51
|
orientation?: 'horizontal' | 'vertical';
|
|
33
52
|
private watcher;
|
|
34
53
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* Every child that is not `disabled` and not `aria-hidden`, which is
|
|
38
|
-
* deliberately a test on the element rather than a list of tag names: a
|
|
39
|
-
* toolbar holds anything, including elements this package has never heard of.
|
|
40
|
-
* Client only, there being no tree to walk on the server.
|
|
54
|
+
* The children that take part in the toolbar, in document order, each with
|
|
55
|
+
* the elements in it that take focus.
|
|
41
56
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
57
|
+
* A test on the element rather than a list of tag names: a toolbar holds
|
|
58
|
+
* anything, including elements this package has never heard of. A child
|
|
59
|
+
* that takes no focus and holds nothing that does is skipped. Client only,
|
|
60
|
+
* there being no tree to walk on the server.
|
|
46
61
|
*/
|
|
47
62
|
private get controls();
|
|
63
|
+
/** The elements in a child's light DOM that take focus. */
|
|
64
|
+
private within;
|
|
65
|
+
/**
|
|
66
|
+
* The stops of a child that takes focus itself: the child, or the members of
|
|
67
|
+
* a composite that owns its tab stop, such as a toggle group, which the
|
|
68
|
+
* toolbar enters at a member and moves the stop of through `focusable`.
|
|
69
|
+
*/
|
|
70
|
+
private inside;
|
|
71
|
+
/**
|
|
72
|
+
* Where focus lands on arriving at a control. A nested composite is entered
|
|
73
|
+
* at the stop it holds, which for a toggle group is the chosen toggle.
|
|
74
|
+
*/
|
|
75
|
+
private entry;
|
|
76
|
+
/** The control a press or a focus came from, by its path. */
|
|
77
|
+
private origin;
|
|
48
78
|
/**
|
|
49
79
|
* Moves the single tab stop to one control.
|
|
50
80
|
*
|
|
@@ -74,9 +104,10 @@ export declare class HelixHeadlessToolbar extends HelixElement {
|
|
|
74
104
|
* already at `-1`, leaving a toolbar the keyboard cannot reach at all. A Save
|
|
75
105
|
* button disabling itself once the form is clean is enough to do it.
|
|
76
106
|
*
|
|
77
|
-
* `foreign` stays false, the same as the first claim
|
|
78
|
-
*
|
|
79
|
-
*
|
|
107
|
+
* `foreign` stays false, the same as the first claim, until a focus has
|
|
108
|
+
* written `tabindex="-1"` onto foreign controls. From then on `setTabStop`
|
|
109
|
+
* writes them as well, so removing or disabling the native button that held
|
|
110
|
+
* the stop hands it to the first one left.
|
|
80
111
|
*/
|
|
81
112
|
private readonly restoreTabStop;
|
|
82
113
|
/**
|
|
@@ -86,6 +117,15 @@ export declare class HelixHeadlessToolbar extends HelixElement {
|
|
|
86
117
|
* so a reflected default cannot appear on the host after hydration.
|
|
87
118
|
*/
|
|
88
119
|
private get axis();
|
|
120
|
+
/**
|
|
121
|
+
* The toolbar's own keys, from inside a nested composite.
|
|
122
|
+
*
|
|
123
|
+
* In capture, ahead of the composite, which answers every arrow itself and
|
|
124
|
+
* would otherwise hold the reader inside it. Only the keys along the
|
|
125
|
+
* toolbar's axis and Home and End are taken, so the arrows across it still
|
|
126
|
+
* move inside the composite.
|
|
127
|
+
*/
|
|
128
|
+
private readonly handleCompositeKeydown;
|
|
89
129
|
private readonly handleKeydown;
|
|
90
130
|
connectedCallback(): void;
|
|
91
131
|
disconnectedCallback(): void;
|
|
@@ -4,28 +4,63 @@ 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, HelixElement, inlineStep, keyClaimed } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, HelixElement, inlineStep, keyClaimed, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
const KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End']);
|
|
11
11
|
const ownsTabStop = (node) => 'focusable' in node;
|
|
12
|
+
const NATIVE = 'a[href], button, input, select, textarea, [tabindex], [contenteditable=""], [contenteditable="true"]';
|
|
13
|
+
const TABBABLE = NATIVE.replace('[tabindex]', '[tabindex]:not([tabindex="-1"])');
|
|
14
|
+
const takesFocus = (node) => {
|
|
15
|
+
if (!(node instanceof HTMLElement) ||
|
|
16
|
+
node.hasAttribute('disabled') ||
|
|
17
|
+
node.getAttribute('aria-hidden') === 'true') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return (node.matches(NATIVE) ||
|
|
21
|
+
ownsTabStop(node) ||
|
|
22
|
+
node.shadowRoot?.delegatesFocus === true ||
|
|
23
|
+
node.shadowRoot?.querySelector(TABBABLE) != null);
|
|
24
|
+
};
|
|
12
25
|
let HelixHeadlessToolbar = class HelixHeadlessToolbar extends HelixElement {
|
|
13
26
|
constructor() {
|
|
14
27
|
super(...arguments);
|
|
15
28
|
this.label = 'Toolbar';
|
|
29
|
+
this.aria = new AriaForwardController(this, {
|
|
30
|
+
label: true,
|
|
31
|
+
target: () => this.partNamed('toolbar'),
|
|
32
|
+
});
|
|
33
|
+
this.wroteForeign = false;
|
|
16
34
|
this.handleFocusIn = (event) => {
|
|
17
|
-
const control = this.
|
|
35
|
+
const control = this.origin(event);
|
|
18
36
|
if (control) {
|
|
19
37
|
this.setTabStop(control);
|
|
20
38
|
}
|
|
21
39
|
};
|
|
22
40
|
this.restoreTabStop = () => {
|
|
23
41
|
const controls = this.controls;
|
|
24
|
-
const held = controls.some((control) =>
|
|
42
|
+
const held = controls.some((control) => {
|
|
43
|
+
const composite = control.stops.length !== 1;
|
|
44
|
+
const element = composite ? control.child : control.stops[0];
|
|
45
|
+
if (ownsTabStop(element)) {
|
|
46
|
+
return element.focusable;
|
|
47
|
+
}
|
|
48
|
+
return composite || element.tabIndex === 0;
|
|
49
|
+
});
|
|
25
50
|
if (controls.length > 0 && !held) {
|
|
26
51
|
this.setTabStop(controls[0], false);
|
|
27
52
|
}
|
|
28
53
|
};
|
|
54
|
+
this.handleCompositeKeydown = (event) => {
|
|
55
|
+
const control = this.origin(event);
|
|
56
|
+
if (!control || control.stops.length === 1) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.handleKeydown(event);
|
|
60
|
+
if (event.defaultPrevented) {
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
29
64
|
this.handleKeydown = (event) => {
|
|
30
65
|
if (!KEYS.has(event.key) || keyClaimed(event)) {
|
|
31
66
|
return;
|
|
@@ -34,7 +69,8 @@ let HelixHeadlessToolbar = class HelixHeadlessToolbar extends HelixElement {
|
|
|
34
69
|
const forward = this.axis === 'horizontal' ? along === 1 : event.key === 'ArrowDown';
|
|
35
70
|
const back = this.axis === 'horizontal' ? along === -1 : event.key === 'ArrowUp';
|
|
36
71
|
const controls = this.controls;
|
|
37
|
-
const
|
|
72
|
+
const origin = this.origin(event);
|
|
73
|
+
const current = origin ? controls.findIndex((control) => control.child === origin.child) : -1;
|
|
38
74
|
if (current === -1) {
|
|
39
75
|
return;
|
|
40
76
|
}
|
|
@@ -57,30 +93,75 @@ let HelixHeadlessToolbar = class HelixHeadlessToolbar extends HelixElement {
|
|
|
57
93
|
event.preventDefault();
|
|
58
94
|
const target = controls[next];
|
|
59
95
|
this.setTabStop(target);
|
|
60
|
-
target
|
|
96
|
+
const element = target ? this.entry(target) : undefined;
|
|
97
|
+
if (element && 'focusItem' in element && typeof element.focusItem === 'function') {
|
|
98
|
+
element.focusItem();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
element?.focus();
|
|
102
|
+
}
|
|
61
103
|
};
|
|
62
104
|
}
|
|
63
105
|
get controls() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
106
|
+
const controls = [];
|
|
107
|
+
for (const child of this.children) {
|
|
108
|
+
if (!(child instanceof HTMLElement) ||
|
|
109
|
+
child.hasAttribute('disabled') ||
|
|
110
|
+
child.getAttribute('aria-hidden') === 'true') {
|
|
111
|
+
continue;
|
|
67
112
|
}
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
const stops = takesFocus(child) ? this.inside(child) : this.within(child);
|
|
114
|
+
if (stops.length > 0) {
|
|
115
|
+
controls.push({ child, stops });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return controls;
|
|
119
|
+
}
|
|
120
|
+
within(child) {
|
|
121
|
+
return [...child.querySelectorAll('*')].filter(takesFocus);
|
|
122
|
+
}
|
|
123
|
+
inside(child) {
|
|
124
|
+
if (!ownsTabStop(child)) {
|
|
125
|
+
return [child];
|
|
126
|
+
}
|
|
127
|
+
const members = this.within(child);
|
|
128
|
+
return members.length > 1 ? members : [child];
|
|
129
|
+
}
|
|
130
|
+
entry(control) {
|
|
131
|
+
const { stops } = control;
|
|
132
|
+
if (stops.length === 1) {
|
|
133
|
+
return stops[0];
|
|
134
|
+
}
|
|
135
|
+
return (stops.find((stop) => (ownsTabStop(stop) && stop.focusable) ||
|
|
136
|
+
stop.selected === true) ?? stops[0]);
|
|
137
|
+
}
|
|
138
|
+
origin(event) {
|
|
139
|
+
const path = event.composedPath();
|
|
140
|
+
return this.controls.find((control) => control.stops.some((stop) => path.includes(stop)));
|
|
70
141
|
}
|
|
71
142
|
setTabStop(active, foreign = true) {
|
|
143
|
+
if (foreign) {
|
|
144
|
+
this.wroteForeign = true;
|
|
145
|
+
}
|
|
72
146
|
for (const control of this.controls) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
147
|
+
if (control.stops.length !== 1) {
|
|
148
|
+
if (ownsTabStop(control.child)) {
|
|
149
|
+
control.child.focusable = control.child === active?.child;
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const element = control.stops[0];
|
|
154
|
+
const stop = control.child === active?.child;
|
|
155
|
+
if (ownsTabStop(element)) {
|
|
156
|
+
element.focusable = stop;
|
|
76
157
|
}
|
|
77
|
-
else if (foreign) {
|
|
78
|
-
|
|
158
|
+
else if (foreign || this.wroteForeign) {
|
|
159
|
+
element.tabIndex = stop ? 0 : -1;
|
|
79
160
|
}
|
|
80
161
|
}
|
|
81
162
|
}
|
|
82
163
|
async claimTabStop() {
|
|
83
|
-
const pending = [...new Set(this.
|
|
164
|
+
const pending = [...new Set([...this.children].map((child) => child.localName))].filter((name) => name.includes('-'));
|
|
84
165
|
await Promise.all(pending.map((name) => customElements.whenDefined(name)));
|
|
85
166
|
this.setTabStop(this.controls[0], false);
|
|
86
167
|
}
|
|
@@ -89,6 +170,7 @@ let HelixHeadlessToolbar = class HelixHeadlessToolbar extends HelixElement {
|
|
|
89
170
|
}
|
|
90
171
|
connectedCallback() {
|
|
91
172
|
super.connectedCallback();
|
|
173
|
+
this.addEventListener('keydown', this.handleCompositeKeydown, true);
|
|
92
174
|
this.addEventListener('keydown', this.handleKeydown);
|
|
93
175
|
this.addEventListener('focusin', this.handleFocusIn);
|
|
94
176
|
this.watcher = new MutationObserver(this.restoreTabStop);
|
|
@@ -100,6 +182,7 @@ let HelixHeadlessToolbar = class HelixHeadlessToolbar extends HelixElement {
|
|
|
100
182
|
}
|
|
101
183
|
disconnectedCallback() {
|
|
102
184
|
super.disconnectedCallback();
|
|
185
|
+
this.removeEventListener('keydown', this.handleCompositeKeydown, true);
|
|
103
186
|
this.removeEventListener('keydown', this.handleKeydown);
|
|
104
187
|
this.removeEventListener('focusin', this.handleFocusIn);
|
|
105
188
|
this.watcher?.disconnect();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The element focus lands on when a trigger is focused: the trigger itself,
|
|
3
|
+
* or, for a component that delegates focus, the control inside its shadow root,
|
|
4
|
+
* followed down through any component nested there.
|
|
5
|
+
*/
|
|
6
|
+
export declare const focusTarget: (trigger: Element) => Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const CONTROL = 'a[href], button, input, select, textarea, [contenteditable], [tabindex]:not([tabindex="-1"])';
|
|
2
|
+
export const focusTarget = (trigger) => {
|
|
3
|
+
const root = trigger.shadowRoot;
|
|
4
|
+
if (!root?.delegatesFocus) {
|
|
5
|
+
return trigger;
|
|
6
|
+
}
|
|
7
|
+
const inner = root.querySelector(CONTROL);
|
|
8
|
+
return inner ? focusTarget(inner) : trigger;
|
|
9
|
+
};
|