@fusion.dev/helix-headless 0.144.0 → 0.146.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 +15195 -4240
- package/dist/accordion/item/accordion-item.d.ts +62 -12
- package/dist/accordion/item/accordion-item.js +41 -10
- package/dist/accordion/root/accordion-root.d.ts +14 -5
- package/dist/accordion/root/accordion-root.js +7 -1
- package/dist/announcement/root/announcement-root.d.ts +24 -3
- package/dist/announcement/root/announcement-root.js +39 -2
- package/dist/app-header/root/app-header-root.d.ts +12 -0
- package/dist/app-header/root/app-header-root.js +13 -1
- package/dist/app-header/sectioning.d.ts +9 -0
- package/dist/app-header/sectioning.js +27 -0
- package/dist/avatar/group/avatar-group.d.ts +20 -14
- package/dist/avatar/group/avatar-group.js +17 -19
- package/dist/badge/root/badge-root.d.ts +3 -1
- package/dist/badge/root/badge-root.js +2 -2
- package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
- package/dist/bar-chart/root/bar-chart-root.js +52 -15
- package/dist/bar-chart/stack.d.ts +15 -0
- package/dist/bar-chart/stack.js +19 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +57 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +73 -3
- package/dist/button/root/button-root.d.ts +26 -0
- package/dist/button/root/button-root.js +38 -18
- package/dist/button-group/root/button-group-root.d.ts +2 -1
- package/dist/button-group/root/button-group-root.js +5 -1
- package/dist/canvas/node/canvas-node.d.ts +4 -3
- package/dist/canvas/node/canvas-node.js +3 -2
- package/dist/canvas/root/canvas-root.d.ts +38 -3
- package/dist/canvas/root/canvas-root.js +123 -42
- package/dist/card/root/card-root.d.ts +24 -3
- package/dist/card/root/card-root.js +20 -8
- package/dist/carousel/root/carousel-root.d.ts +133 -4
- package/dist/carousel/root/carousel-root.js +174 -24
- package/dist/carousel/stops.d.ts +20 -0
- package/dist/carousel/stops.js +30 -0
- package/dist/chat/composer/chat-composer.d.ts +38 -6
- package/dist/chat/composer/chat-composer.js +42 -9
- package/dist/chat/message/chat-message.d.ts +32 -1
- package/dist/chat/message/chat-message.js +27 -10
- package/dist/chat/naming.d.ts +26 -0
- package/dist/chat/naming.js +14 -0
- package/dist/chat/root/chat-root.d.ts +58 -0
- package/dist/chat/root/chat-root.js +43 -3
- package/dist/checkbox/root/checkbox-root.d.ts +20 -0
- package/dist/checkbox/root/checkbox-root.js +14 -1
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
- package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
- package/dist/checkbox-group/select-all.d.ts +27 -0
- package/dist/checkbox-group/select-all.js +12 -0
- package/dist/code-block/highlight.d.ts +15 -0
- package/dist/code-block/highlight.js +4 -0
- package/dist/code-block/root/code-block-root.d.ts +57 -2
- package/dist/code-block/root/code-block-root.js +63 -8
- package/dist/color-input/root/color-input-root.d.ts +3 -0
- package/dist/color-input/root/color-input-root.js +6 -3
- package/dist/color-picker/root/color-picker-root.d.ts +33 -4
- package/dist/color-picker/root/color-picker-root.js +80 -21
- package/dist/color-picker/swatches.d.ts +12 -0
- package/dist/color-picker/swatches.js +26 -0
- package/dist/combobox/root/combobox-root.d.ts +43 -0
- package/dist/combobox/root/combobox-root.js +79 -15
- package/dist/command/root/command-root.d.ts +16 -1
- package/dist/command/root/command-root.js +22 -4
- package/dist/copy-button/root/copy-button-root.d.ts +26 -1
- package/dist/copy-button/root/copy-button-root.js +12 -5
- package/dist/data-table/index.d.ts +1 -1
- package/dist/data-table/index.js +1 -1
- package/dist/data-table/keys.d.ts +24 -0
- package/dist/data-table/keys.js +24 -0
- package/dist/data-table/root/data-table-root.d.ts +170 -13
- package/dist/data-table/root/data-table-root.js +294 -52
- package/dist/data-table/tree.d.ts +55 -0
- package/dist/data-table/tree.js +54 -0
- package/dist/date-input/root/date-input-root.d.ts +3 -1
- package/dist/date-input/root/date-input-root.js +18 -7
- package/dist/date-picker/root/date-picker-root.js +12 -6
- package/dist/description-list/item/description-list-item.d.ts +17 -2
- package/dist/description-list/item/description-list-item.js +15 -2
- package/dist/description-list/root/description-list-root.d.ts +27 -1
- package/dist/description-list/root/description-list-root.js +26 -1
- package/dist/dialog/description.js +15 -1
- package/dist/dialog/root/dialog-root.d.ts +8 -2
- package/dist/dialog/root/dialog-root.js +16 -1
- package/dist/divider/root/divider-root.d.ts +3 -0
- package/dist/divider/root/divider-root.js +2 -1
- package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
- package/dist/donut-chart/root/donut-chart-root.js +48 -1
- package/dist/drawer/reveal.d.ts +8 -0
- package/dist/drawer/reveal.js +1 -0
- package/dist/drawer/root/drawer-root.d.ts +59 -0
- package/dist/drawer/root/drawer-root.js +63 -4
- package/dist/drilldown/item/drilldown-item.d.ts +11 -6
- package/dist/drilldown/item/drilldown-item.js +8 -5
- package/dist/drilldown/root/drilldown-root.d.ts +29 -0
- package/dist/drilldown/root/drilldown-root.js +28 -5
- package/dist/element-events.generated.js +83 -12
- package/dist/empty/root/empty-root.d.ts +3 -1
- package/dist/empty/root/empty-root.js +3 -2
- package/dist/file-input/root/file-input-root.d.ts +69 -6
- package/dist/file-input/root/file-input-root.js +82 -19
- package/dist/file-input/screen.d.ts +2 -1
- package/dist/filter-input/menu/filter-menu.d.ts +5 -0
- package/dist/filter-input/menu/filter-menu.js +16 -4
- package/dist/filter-input/root/filter-input-root.d.ts +30 -6
- package/dist/filter-input/root/filter-input-root.js +1 -1
- package/dist/filter-input/suggest.d.ts +28 -3
- package/dist/filter-input/suggest.js +18 -8
- package/dist/format-date/root/format-date-root.d.ts +9 -16
- package/dist/format-date/root/format-date-root.js +10 -27
- package/dist/heatmap/root/heatmap-root.d.ts +24 -13
- package/dist/heatmap/root/heatmap-root.js +34 -12
- package/dist/icon/root/icon-root.d.ts +12 -1
- package/dist/icon/root/icon-root.js +21 -10
- package/dist/icon/settle.d.ts +7 -0
- package/dist/icon/settle.js +10 -0
- package/dist/icon-button/root/icon-button-root.d.ts +2 -0
- package/dist/image/root/image-root.d.ts +5 -1
- package/dist/image/root/image-root.js +10 -2
- package/dist/input/root/input-root.d.ts +13 -3
- package/dist/input/root/input-root.js +12 -3
- package/dist/kbd/root/kbd-root.d.ts +35 -1
- package/dist/kbd/root/kbd-root.js +23 -4
- package/dist/legend/root/legend-root.d.ts +55 -8
- package/dist/legend/root/legend-root.js +55 -13
- package/dist/lightbox/root/lightbox-root.d.ts +49 -6
- package/dist/lightbox/root/lightbox-root.js +86 -19
- package/dist/lightbox/zoom-controller.d.ts +59 -0
- package/dist/lightbox/zoom-controller.js +128 -0
- package/dist/lightbox/zoom.d.ts +22 -0
- package/dist/lightbox/zoom.js +16 -0
- package/dist/line-chart/root/line-chart-root.d.ts +22 -1
- package/dist/line-chart/root/line-chart-root.js +21 -1
- package/dist/list/item/list-item.d.ts +70 -1
- package/dist/list/item/list-item.js +77 -6
- package/dist/list/numbering.d.ts +14 -0
- package/dist/list/numbering.js +8 -0
- package/dist/list/root/list-root.d.ts +155 -9
- package/dist/list/root/list-root.js +246 -4
- package/dist/list/selection.d.ts +49 -0
- package/dist/list/selection.js +30 -0
- package/dist/listbox/root/listbox-root.d.ts +37 -1
- package/dist/listbox/root/listbox-root.js +49 -4
- package/dist/loader/busy.d.ts +10 -0
- package/dist/loader/busy.js +11 -0
- package/dist/loader/root/loader-root.d.ts +9 -2
- package/dist/loader/root/loader-root.js +23 -3
- package/dist/map/root/map-root.d.ts +6 -2
- package/dist/map/root/map-root.js +5 -2
- package/dist/megamenu/root/megamenu-root.d.ts +62 -0
- package/dist/megamenu/root/megamenu-root.js +90 -5
- package/dist/menu/index.d.ts +1 -0
- package/dist/menu/index.js +1 -0
- package/dist/menu/item/menu-item.d.ts +87 -11
- package/dist/menu/item/menu-item.js +78 -22
- package/dist/menu/root/menu-root.d.ts +39 -1
- package/dist/menu/root/menu-root.js +65 -8
- package/dist/menu/section/menu-section.d.ts +55 -0
- package/dist/menu/section/menu-section.js +60 -0
- package/dist/meter/root/meter-root.d.ts +20 -1
- package/dist/meter/root/meter-root.js +11 -15
- package/dist/navigation/root/navigation-root.d.ts +18 -2
- package/dist/navigation/root/navigation-root.js +29 -3
- package/dist/notifications/root/notifications-root.d.ts +25 -0
- package/dist/notifications/root/notifications-root.js +33 -5
- package/dist/number-input/root/number-input-root.d.ts +17 -8
- package/dist/number-input/root/number-input-root.js +2 -7
- package/dist/object-inspector/root/object-inspector-root.d.ts +161 -32
- package/dist/object-inspector/root/object-inspector-root.js +264 -123
- package/dist/object-inspector/rows.d.ts +75 -0
- package/dist/object-inspector/rows.js +119 -0
- package/dist/option/root/option-root.d.ts +5 -3
- package/dist/outline/levels.d.ts +31 -0
- package/dist/outline/levels.js +23 -0
- package/dist/outline/root/outline-root.d.ts +86 -3
- package/dist/outline/root/outline-root.js +95 -12
- package/dist/pagination/size/pagination-size.d.ts +12 -2
- package/dist/pagination/size/pagination-size.js +20 -5
- package/dist/pin-input/code.d.ts +10 -0
- package/dist/pin-input/code.js +13 -0
- package/dist/pin-input/root/pin-input-root.d.ts +25 -1
- package/dist/pin-input/root/pin-input-root.js +10 -11
- package/dist/popover/hide-outside.d.ts +14 -0
- package/dist/popover/hide-outside.js +41 -0
- package/dist/popover/root/popover-root.d.ts +57 -7
- package/dist/popover/root/popover-root.js +58 -3
- package/dist/progress/root/progress-root.d.ts +20 -0
- package/dist/progress/root/progress-root.js +15 -15
- package/dist/qr-code/export.d.ts +26 -1
- package/dist/qr-code/export.js +14 -0
- package/dist/qr-code/root/qr-code-root.d.ts +19 -5
- package/dist/qr-code/root/qr-code-root.js +58 -9
- package/dist/quote/marks.d.ts +8 -0
- package/dist/quote/marks.js +5 -0
- package/dist/quote/root/quote-root.d.ts +14 -2
- package/dist/quote/root/quote-root.js +23 -1
- package/dist/radio/group/radio-group.d.ts +4 -10
- package/dist/radio/group/radio-group.js +5 -10
- package/dist/radio/root/radio-root.d.ts +6 -2
- package/dist/radio/root/radio-root.js +6 -1
- package/dist/rating/root/rating-root.d.ts +10 -5
- package/dist/rating/root/rating-root.js +32 -8
- package/dist/rating/steps.d.ts +7 -0
- package/dist/rating/steps.js +9 -0
- package/dist/scatter-chart/fit.d.ts +15 -0
- package/dist/scatter-chart/fit.js +35 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
- package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
- package/dist/scroll-area/root/scroll-area-root.d.ts +84 -0
- package/dist/scroll-area/root/scroll-area-root.js +106 -7
- package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
- package/dist/scroll-area/scrollbar-controller.js +172 -0
- package/dist/scroll-area/scrollbar.d.ts +92 -0
- package/dist/scroll-area/scrollbar.js +33 -0
- package/dist/select/root/select-root.d.ts +15 -0
- package/dist/select/root/select-root.js +18 -6
- package/dist/skeleton/root/skeleton-root.d.ts +3 -0
- package/dist/skeleton/root/skeleton-root.js +4 -2
- package/dist/skip-link/root/skip-link-root.js +2 -2
- package/dist/slider/root/slider-root.d.ts +41 -4
- package/dist/slider/root/slider-root.js +23 -3
- package/dist/sparkline/readings.d.ts +18 -0
- package/dist/sparkline/readings.js +19 -0
- package/dist/sparkline/root/sparkline-root.d.ts +78 -7
- package/dist/sparkline/root/sparkline-root.js +118 -23
- package/dist/speed-dial/root/speed-dial-root.js +2 -2
- package/dist/splitter/root/splitter-root.d.ts +20 -12
- package/dist/splitter/root/splitter-root.js +226 -203
- package/dist/splitter/snap.d.ts +9 -0
- package/dist/splitter/snap.js +12 -0
- package/dist/stat/root/stat-root.d.ts +14 -5
- package/dist/stat/root/stat-root.js +21 -5
- package/dist/steps/reach.d.ts +20 -0
- package/dist/steps/reach.js +15 -0
- package/dist/steps/root/steps-root.d.ts +76 -0
- package/dist/steps/root/steps-root.js +93 -1
- package/dist/steps/step/step.d.ts +45 -2
- package/dist/steps/step/step.js +33 -6
- package/dist/switch/root/switch-root.d.ts +10 -0
- package/dist/table/column/table-column.d.ts +11 -10
- package/dist/table/column/table-column.js +8 -19
- package/dist/table/root/table-root.d.ts +126 -4
- package/dist/table/root/table-root.js +156 -6
- package/dist/table/row/table-row.d.ts +47 -1
- package/dist/table/row/table-row.js +39 -2
- package/dist/tabs/panel/tab-panel.d.ts +22 -5
- package/dist/tabs/panel/tab-panel.js +34 -6
- package/dist/tabs/root/tabs-root.d.ts +37 -1
- package/dist/tabs/root/tabs-root.js +22 -4
- package/dist/tabs/tab/tab.d.ts +41 -4
- package/dist/tabs/tab/tab.js +49 -3
- package/dist/tag/group/tag-group.d.ts +31 -4
- package/dist/tag/group/tag-group.js +46 -7
- package/dist/tag/root/tag-root.js +9 -2
- package/dist/textarea/root/textarea-root.d.ts +28 -1
- package/dist/textarea/root/textarea-root.js +40 -1
- package/dist/time-input/root/time-input-root.d.ts +5 -1
- package/dist/time-input/root/time-input-root.js +16 -8
- package/dist/time-picker/root/time-picker-root.d.ts +18 -4
- package/dist/time-picker/root/time-picker-root.js +7 -10
- package/dist/time-picker/wheel-name.d.ts +9 -0
- package/dist/time-picker/wheel-name.js +15 -0
- package/dist/timeline/group/timeline-group.d.ts +46 -0
- package/dist/timeline/group/timeline-group.js +37 -0
- package/dist/timeline/index.d.ts +1 -0
- package/dist/timeline/index.js +1 -0
- package/dist/timeline/root/timeline-root.d.ts +10 -3
- package/dist/toast/item/toast-item.d.ts +24 -18
- package/dist/toast/item/toast-item.js +36 -40
- package/dist/toast/root/toast-root.d.ts +6 -0
- package/dist/toast/root/toast-root.js +16 -1
- package/dist/toggle/group/toggle-group.d.ts +24 -3
- package/dist/toggle/group/toggle-group.js +34 -5
- package/dist/toolbar/root/toolbar-root.d.ts +13 -0
- package/dist/toolbar/root/toolbar-root.js +15 -5
- package/dist/tooltip/root/tooltip-root.d.ts +13 -2
- package/dist/tooltip/root/tooltip-root.js +9 -1
- package/dist/tour/back.d.ts +10 -0
- package/dist/tour/back.js +8 -0
- package/dist/tour/root/tour-root.d.ts +30 -2
- package/dist/tour/root/tour-root.js +34 -8
- package/dist/tree/item/tree-item.d.ts +61 -4
- package/dist/tree/item/tree-item.js +92 -20
- package/dist/tree/root/tree-root.d.ts +58 -0
- package/dist/tree/root/tree-root.js +80 -16
- package/dist/tree/selection.d.ts +24 -0
- package/dist/tree/selection.js +13 -0
- package/dist/trend/root/trend-root.d.ts +43 -18
- package/dist/trend/root/trend-root.js +31 -6
- package/dist/video/media-events.d.ts +44 -0
- package/dist/video/media-events.js +35 -0
- package/dist/video/root/video-root.d.ts +117 -18
- package/dist/video/root/video-root.js +93 -47
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +3 -1
- package/dist/video-playlist/order.d.ts +41 -0
- package/dist/video-playlist/order.js +34 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +77 -3
- package/dist/video-playlist/root/video-playlist-root.js +60 -16
- package/dist/virtual-list/controls.d.ts +4 -0
- package/dist/virtual-list/controls.js +30 -0
- package/dist/virtual-list/fill.d.ts +22 -0
- package/dist/virtual-list/fill.js +23 -0
- package/dist/virtual-list/keys.d.ts +15 -0
- package/dist/virtual-list/keys.js +24 -0
- package/dist/virtual-list/plan.d.ts +49 -0
- package/dist/virtual-list/plan.js +16 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +259 -26
- package/dist/virtual-list/root/virtual-list-root.js +333 -49
- package/lint.json +1 -1
- package/package.json +8 -8
- package/types/jsx.d.ts +8113 -1394
- package/types/svelte.d.ts +2 -0
- package/types/vue.d.ts +1678 -353
- package/dist/data-table/root/order.d.ts +0 -141
- package/dist/data-table/root/order.js +0 -76
- package/dist/listbox/range.d.ts +0 -17
- package/dist/listbox/range.js +0 -5
- package/dist/tag/selection.d.ts +0 -10
- package/dist/tag/selection.js +0 -12
|
@@ -8,6 +8,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
8
8
|
* still there when the person comes back, and the content of every panel is in
|
|
9
9
|
* the server-rendered markup for crawlers, whichever tab is open.
|
|
10
10
|
*
|
|
11
|
+
* The host is the `tabpanel` while its tab is selected, through
|
|
12
|
+
* `ElementInternals`, so the tab's `aria-controls` can reach it from the tab's
|
|
13
|
+
* shadow root. The box inside is a tab stop only while the panel holds nothing
|
|
14
|
+
* focusable, as the ARIA tabs pattern and React Aria's tab panel do: Tab from
|
|
15
|
+
* the tab list lands on the panel's first link or control where there is one,
|
|
16
|
+
* and on the panel itself where there is none.
|
|
17
|
+
*
|
|
11
18
|
* @since 0.1.0
|
|
12
19
|
* @status stable
|
|
13
20
|
* @aliases TabPanel
|
|
@@ -15,7 +22,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
15
22
|
*
|
|
16
23
|
* @slot - The panel's content.
|
|
17
24
|
*
|
|
18
|
-
* @csspart panel - The panel container.
|
|
25
|
+
* @csspart panel - The panel container, which takes the focus while it holds nothing focusable.
|
|
19
26
|
*
|
|
20
27
|
* @cssstate selected - While its tab is the one selected.
|
|
21
28
|
*/
|
|
@@ -28,10 +35,9 @@ export declare class HelixHeadlessTabPanel extends HelixElement {
|
|
|
28
35
|
* writes: the tab already says it.
|
|
29
36
|
*
|
|
30
37
|
* A string and not a reference: an IDREF resolves only in the tree it is
|
|
31
|
-
* written in, and
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* anyway.
|
|
38
|
+
* written in, and the tab's control is inside the tab's shadow root, which no
|
|
39
|
+
* reference from the light DOM can reach. `aria-label` on a `tabpanel` is how
|
|
40
|
+
* the pattern names one anyway.
|
|
35
41
|
*/
|
|
36
42
|
label: string;
|
|
37
43
|
/**
|
|
@@ -42,6 +48,17 @@ export declare class HelixHeadlessTabPanel extends HelixElement {
|
|
|
42
48
|
* Pairs the panel with the tab of the same value.
|
|
43
49
|
*/
|
|
44
50
|
value: string;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the content holds a tab stop of its own, so the panel needs none.
|
|
53
|
+
* False on a server, which serves the panel as a stop: it cannot look.
|
|
54
|
+
*/
|
|
55
|
+
private holdsStop;
|
|
56
|
+
/** Re-reads the content when it changes in a way that adds or removes a stop. */
|
|
57
|
+
private readonly watcher;
|
|
58
|
+
private readonly readStops;
|
|
59
|
+
connectedCallback(): void;
|
|
60
|
+
disconnectedCallback(): void;
|
|
61
|
+
protected updated(changed: PropertyValues): void;
|
|
45
62
|
/**
|
|
46
63
|
* Mirrors the chosen state where a stylesheet can select on it.
|
|
47
64
|
*
|
|
@@ -4,29 +4,54 @@ 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 } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, focusableElements, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessTabPanel = class HelixHeadlessTabPanel extends HelixElement {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.label = '';
|
|
14
14
|
this.selected = false;
|
|
15
15
|
this.value = '';
|
|
16
|
+
this.holdsStop = false;
|
|
17
|
+
this.watcher = typeof MutationObserver === 'undefined'
|
|
18
|
+
? undefined
|
|
19
|
+
: new MutationObserver(() => this.readStops());
|
|
20
|
+
this.readStops = () => {
|
|
21
|
+
this.holdsStop = this.selected && focusableElements(this).length > 0;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
connectedCallback() {
|
|
25
|
+
super.connectedCallback();
|
|
26
|
+
this.watcher?.observe(this, {
|
|
27
|
+
attributeFilter: ['contenteditable', 'disabled', 'hidden', 'href', 'tabindex'],
|
|
28
|
+
attributes: true,
|
|
29
|
+
childList: true,
|
|
30
|
+
subtree: true,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
disconnectedCallback() {
|
|
34
|
+
super.disconnectedCallback();
|
|
35
|
+
this.watcher?.disconnect();
|
|
36
|
+
}
|
|
37
|
+
updated(changed) {
|
|
38
|
+
super.updated(changed);
|
|
39
|
+
if (changed.has('selected')) {
|
|
40
|
+
this.readStops();
|
|
41
|
+
}
|
|
16
42
|
}
|
|
17
43
|
willUpdate(changed) {
|
|
18
44
|
super.willUpdate(changed);
|
|
19
45
|
this.toggleState('selected', this.selected);
|
|
46
|
+
hostAria(this.internals, { label: this.label, role: this.selected ? 'tabpanel' : '' });
|
|
20
47
|
}
|
|
21
48
|
render() {
|
|
22
49
|
return html `<div
|
|
23
50
|
part="panel"
|
|
24
|
-
role="tabpanel"
|
|
25
|
-
aria-label=${this.label || nothing}
|
|
26
51
|
?hidden=${!this.selected}
|
|
27
|
-
tabindex
|
|
52
|
+
tabindex=${this.holdsStop ? nothing : '0'}
|
|
28
53
|
>
|
|
29
|
-
<slot></slot>
|
|
54
|
+
<slot @slotchange=${this.readStops}></slot>
|
|
30
55
|
</div>`;
|
|
31
56
|
}
|
|
32
57
|
};
|
|
@@ -39,6 +64,9 @@ __decorate([
|
|
|
39
64
|
__decorate([
|
|
40
65
|
property()
|
|
41
66
|
], HelixHeadlessTabPanel.prototype, "value", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
state()
|
|
69
|
+
], HelixHeadlessTabPanel.prototype, "holdsStop", void 0);
|
|
42
70
|
HelixHeadlessTabPanel = __decorate([
|
|
43
71
|
customElement('helix-headless-tab-panel')
|
|
44
72
|
], HelixHeadlessTabPanel);
|
|
@@ -16,6 +16,29 @@ declare const HelixHeadlessTabs_base: (new (...args: any[]) => import("@fusion.d
|
|
|
16
16
|
* panel is cheap. Set `manual` when showing a panel is expensive, so focus can
|
|
17
17
|
* move without selecting.
|
|
18
18
|
*
|
|
19
|
+
* `orientation="vertical"` sets the tabs in a column beside the panels, and the
|
|
20
|
+
* tab list says `aria-orientation="vertical"`. The arrow keys of both axes move
|
|
21
|
+
* between tabs either way.
|
|
22
|
+
*
|
|
23
|
+
* A tab with an `href` is a link, for tabs that are pages of a site. Pressing
|
|
24
|
+
* it follows the link, and the arrow keys move focus without choosing, so
|
|
25
|
+
* arrowing past a tab never leaves the page. Mark the tab of the page you are
|
|
26
|
+
* on with `selected`, or set `active` to its value.
|
|
27
|
+
*
|
|
28
|
+
* Each tab says which panel it controls through `aria-controls`, set as an
|
|
29
|
+
* element reference to the panel's host, where the engine has reflection.
|
|
30
|
+
*
|
|
31
|
+
* Keys, on the tab list: Left and Right Arrow move between tabs, and so do Up
|
|
32
|
+
* and Down; Home and End go to the first and the last. The horizontal arrows
|
|
33
|
+
* follow the reading direction, so on a right-to-left page Left moves on.
|
|
34
|
+
* Disabled tabs are passed over. Tab leaves the list for the selected panel,
|
|
35
|
+
* which is a tab stop only while it holds nothing focusable.
|
|
36
|
+
*
|
|
37
|
+
* The `indicator` part moves to the selected tab and carries its place and
|
|
38
|
+
* size, so a mark of your own can be drawn there: style the part, or slot an
|
|
39
|
+
* element into `indicator`, which sits inside the part and reads the same
|
|
40
|
+
* properties.
|
|
41
|
+
*
|
|
19
42
|
* @since 0.1.0
|
|
20
43
|
* @status stable
|
|
21
44
|
* @category navigation
|
|
@@ -23,13 +46,17 @@ declare const HelixHeadlessTabs_base: (new (...args: any[]) => import("@fusion.d
|
|
|
23
46
|
* @documentation https://fusion.dev/helix/components/tabs
|
|
24
47
|
*
|
|
25
48
|
* @slot - The tabs, as direct children of this element.
|
|
49
|
+
* @slot indicator - A mark of your own, drawn inside the `indicator` part, where `--indicator-x`, `--indicator-y`, `--indicator-inline-size` and `--indicator-block-size` reach it.
|
|
26
50
|
* @slot panels - The panels, as direct children of this element. Panels inside a wrapper are not paired with tabs, because pairing reads only the direct children.
|
|
27
51
|
*
|
|
28
52
|
* @event {HelixActivateEvent} helix-activate - Fired after the active tab changes, so the element already holds the new value.
|
|
29
53
|
* @event {HelixBeforeActivateEvent} helix-before-activate - Fired before the active tab changes, and cancelable. Canceling it is the only way to block a change.
|
|
30
54
|
* @event {HelixSelectEvent} helix-select - Fired when a tab is chosen. The tab sends it and the root handles it, so you can listen on the root. Cancelable: canceling it stops the change and leaves the decision to you.
|
|
31
55
|
*
|
|
32
|
-
* @csspart indicator - The marker that moves to the selected tab.
|
|
56
|
+
* @csspart indicator - The marker that moves to the selected tab. It carries the selected tab's
|
|
57
|
+
* place and size as `--indicator-x`, `--indicator-y`, `--indicator-inline-size` and
|
|
58
|
+
* `--indicator-block-size`, and `data-placed` once it has been placed, for a mark you draw
|
|
59
|
+
* and animate yourself.
|
|
33
60
|
* @csspart panels - The panel container.
|
|
34
61
|
* @csspart tabs - The tab list.
|
|
35
62
|
*/
|
|
@@ -54,6 +81,13 @@ export declare class HelixHeadlessTabs extends HelixHeadlessTabs_base {
|
|
|
54
81
|
* panels on the way.
|
|
55
82
|
*/
|
|
56
83
|
manual: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* The direction the tabs run. `vertical` sets them in a column beside the
|
|
86
|
+
* panels and tells a screen reader the list is vertical.
|
|
87
|
+
*
|
|
88
|
+
* Optional, so the resting row writes no attribute onto the host.
|
|
89
|
+
*/
|
|
90
|
+
orientation?: 'horizontal' | 'vertical';
|
|
57
91
|
/**
|
|
58
92
|
* How the row of tabs is drawn.
|
|
59
93
|
*
|
|
@@ -80,6 +114,8 @@ export declare class HelixHeadlessTabs extends HelixHeadlessTabs_base {
|
|
|
80
114
|
*/
|
|
81
115
|
variant?: 'segmented' | 'underlined';
|
|
82
116
|
private get tabs();
|
|
117
|
+
/** Whether any tab is a link, so arriving on one chooses nothing. */
|
|
118
|
+
private get linked();
|
|
83
119
|
private get panels();
|
|
84
120
|
private readonly handleSelection;
|
|
85
121
|
/** Pushes the current value down to whichever tab and panel own it. */
|
|
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { AriaForwardController, customElement, HelixElement, IndicatorController, KeyboardNavigationController, MixinControllerActive, whenHydrated, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { html } from 'lit';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessTabPanel } from '../panel/tab-panel.js';
|
|
11
11
|
import { HelixHeadlessTab } from '../tab/tab.js';
|
|
@@ -15,7 +15,7 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
|
|
|
15
15
|
this.keys = new KeyboardNavigationController(this, {
|
|
16
16
|
items: () => this.tabs,
|
|
17
17
|
origin: 'focus',
|
|
18
|
-
selects: () => !this.manual,
|
|
18
|
+
selects: () => !this.manual && !this.linked,
|
|
19
19
|
});
|
|
20
20
|
this.indicator = new IndicatorController(this, {
|
|
21
21
|
active: () => this.tabs.find((tab) => tab.selected),
|
|
@@ -43,6 +43,9 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
|
|
|
43
43
|
get tabs() {
|
|
44
44
|
return [...this.children].filter((child) => child instanceof HelixHeadlessTab);
|
|
45
45
|
}
|
|
46
|
+
get linked() {
|
|
47
|
+
return this.tabs.some((tab) => (tab.href ?? '') !== '');
|
|
48
|
+
}
|
|
46
49
|
get panels() {
|
|
47
50
|
return [...this.children].filter((child) => child instanceof HelixHeadlessTabPanel);
|
|
48
51
|
}
|
|
@@ -57,6 +60,13 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
|
|
|
57
60
|
});
|
|
58
61
|
}
|
|
59
62
|
const names = new Map(tabs.map((tab) => [tab.value, (tab.textContent ?? '').trim()]));
|
|
63
|
+
const panels = new Map(this.panels.map((panel) => [panel.value, panel]));
|
|
64
|
+
for (const tab of tabs) {
|
|
65
|
+
const panel = panels.get(tab.value);
|
|
66
|
+
whenHydrated(tab, () => {
|
|
67
|
+
tab.controls = panel;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
60
70
|
for (const panel of this.panels) {
|
|
61
71
|
const selected = panel.value === value;
|
|
62
72
|
const label = names.get(panel.value) ?? '';
|
|
@@ -88,9 +98,14 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
|
|
|
88
98
|
this.indicator.place();
|
|
89
99
|
}
|
|
90
100
|
render() {
|
|
91
|
-
return html `<div
|
|
101
|
+
return html `<div
|
|
102
|
+
part="tabs"
|
|
103
|
+
role="tablist"
|
|
104
|
+
aria-label=${this.label}
|
|
105
|
+
aria-orientation=${this.orientation === 'vertical' ? 'vertical' : nothing}
|
|
106
|
+
>
|
|
92
107
|
<slot @slotchange=${this.handleSlotChange}></slot>
|
|
93
|
-
<span part="indicator" aria-hidden="true"></span>
|
|
108
|
+
<span part="indicator" aria-hidden="true"><slot name="indicator"></slot></span>
|
|
94
109
|
</div>
|
|
95
110
|
<div part="panels"><slot name="panels" @slotchange=${this.handleSlotChange}></slot></div>`;
|
|
96
111
|
}
|
|
@@ -101,6 +116,9 @@ __decorate([
|
|
|
101
116
|
__decorate([
|
|
102
117
|
property({ reflect: true, type: Boolean })
|
|
103
118
|
], HelixHeadlessTabs.prototype, "manual", void 0);
|
|
119
|
+
__decorate([
|
|
120
|
+
property({ reflect: true })
|
|
121
|
+
], HelixHeadlessTabs.prototype, "orientation", void 0);
|
|
104
122
|
__decorate([
|
|
105
123
|
property({ reflect: true })
|
|
106
124
|
], HelixHeadlessTabs.prototype, "variant", void 0);
|
package/dist/tabs/tab/tab.d.ts
CHANGED
|
@@ -7,10 +7,10 @@ declare const HelixHeadlessTab_base: (new (...args: any[]) => import("@fusion.de
|
|
|
7
7
|
* @description
|
|
8
8
|
* A tab is an element, because it manages selection and its own tab stop.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* With an `href`, the tab is a link: `role="tab"` on an `<a>`, which a press,
|
|
11
|
+
* Enter and the browser's own link gestures follow. `helix-select` is sent
|
|
12
|
+
* first, and canceling it keeps the browser from following, so a client-side
|
|
13
|
+
* router follows it instead.
|
|
14
14
|
*
|
|
15
15
|
* @since 0.1.0
|
|
16
16
|
* @status stable
|
|
@@ -27,6 +27,43 @@ declare const HelixHeadlessTab_base: (new (...args: any[]) => import("@fusion.de
|
|
|
27
27
|
*/
|
|
28
28
|
export declare class HelixHeadlessTab extends HelixHeadlessTab_base {
|
|
29
29
|
protected itemPart: string;
|
|
30
|
+
/**
|
|
31
|
+
* @internal The panel this tab shows, written by the tab set. A property,
|
|
32
|
+
* because it is an element, and it reaches the tab's `aria-controls` as an
|
|
33
|
+
* element reference: the panel is in the light DOM, which a reference from
|
|
34
|
+
* inside this shadow root can reach.
|
|
35
|
+
*/
|
|
36
|
+
controls: Element | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* The URL the tab links to. Setting it renders the tab as a link, for a set
|
|
39
|
+
* of tabs that are pages of a site.
|
|
40
|
+
*/
|
|
41
|
+
href?: string;
|
|
42
|
+
/**
|
|
43
|
+
* The link's `rel`, for a tab with an `href`.
|
|
44
|
+
*/
|
|
45
|
+
rel?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Where the link opens, such as `_blank`, for a tab with an `href`.
|
|
48
|
+
*/
|
|
49
|
+
target?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Moves focus to the tab's control, the button or the link inside it, so a
|
|
52
|
+
* script can focus the tab the way it focuses a native one.
|
|
53
|
+
*/
|
|
54
|
+
focus(options?: FocusOptions): void;
|
|
55
|
+
/**
|
|
56
|
+
* Chooses the tab before the browser follows its link, and keeps the browser
|
|
57
|
+
* from following where the choice was refused. A press with a modifier, or
|
|
58
|
+
* with a button other than the main one, opens the link where the browser
|
|
59
|
+
* opens it and chooses nothing here.
|
|
60
|
+
*/
|
|
61
|
+
private readonly handleLinkClick;
|
|
62
|
+
/**
|
|
63
|
+
* Points the control's `aria-controls` at the panel, where the engine can.
|
|
64
|
+
* The control is this tab's own, so the reference is always its to clear.
|
|
65
|
+
*/
|
|
66
|
+
protected updated(changed: PropertyValues): void;
|
|
30
67
|
/**
|
|
31
68
|
* Asks for the render that follows a hydration, so the stop stops being every
|
|
32
69
|
* tab's.
|
package/dist/tabs/tab/tab.js
CHANGED
|
@@ -4,31 +4,77 @@ 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, MixinSelectableItem, plainClick, reflectElements, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
9
10
|
let HelixHeadlessTab = class HelixHeadlessTab extends MixinSelectableItem(HelixElement) {
|
|
10
11
|
constructor() {
|
|
11
12
|
super(...arguments);
|
|
12
13
|
this.itemPart = 'tab';
|
|
14
|
+
this.controls = undefined;
|
|
15
|
+
this.handleLinkClick = (event) => {
|
|
16
|
+
if (!plainClick(event)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!this.select()) {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
focus(options) {
|
|
25
|
+
this.partNamed('tab')?.focus(options);
|
|
26
|
+
}
|
|
27
|
+
updated(changed) {
|
|
28
|
+
super.updated(changed);
|
|
29
|
+
const control = this.renderRoot.querySelector('[part="tab"]');
|
|
30
|
+
reflectElements(control, 'ariaControlsElements', this.controls ? [this.controls] : [], true);
|
|
13
31
|
}
|
|
14
32
|
firstUpdated(changed) {
|
|
15
33
|
super.firstUpdated(changed);
|
|
16
34
|
this.finishHydration();
|
|
17
35
|
}
|
|
18
36
|
render() {
|
|
37
|
+
const tabindex = this.selected || !this.childrenKnown ? '0' : '-1';
|
|
38
|
+
if ((this.href ?? '') !== '') {
|
|
39
|
+
return html `<a
|
|
40
|
+
part="tab"
|
|
41
|
+
role="tab"
|
|
42
|
+
href=${this.disabled ? nothing : (this.href ?? nothing)}
|
|
43
|
+
target=${this.target || nothing}
|
|
44
|
+
rel=${this.rel || nothing}
|
|
45
|
+
aria-selected=${this.selected ? 'true' : 'false'}
|
|
46
|
+
aria-disabled=${this.disabled ? 'true' : nothing}
|
|
47
|
+
tabindex=${this.disabled ? nothing : tabindex}
|
|
48
|
+
@click=${this.handleLinkClick}
|
|
49
|
+
>
|
|
50
|
+
<slot></slot>
|
|
51
|
+
</a>`;
|
|
52
|
+
}
|
|
19
53
|
return html `<button
|
|
20
54
|
part="tab"
|
|
21
55
|
role="tab"
|
|
22
56
|
type="button"
|
|
23
57
|
aria-selected=${this.selected ? 'true' : 'false'}
|
|
24
58
|
?disabled=${this.disabled}
|
|
25
|
-
tabindex=${
|
|
59
|
+
tabindex=${tabindex}
|
|
26
60
|
@click=${() => this.select()}
|
|
27
61
|
>
|
|
28
62
|
<slot></slot>
|
|
29
63
|
</button>`;
|
|
30
64
|
}
|
|
31
65
|
};
|
|
66
|
+
__decorate([
|
|
67
|
+
property({ attribute: false })
|
|
68
|
+
], HelixHeadlessTab.prototype, "controls", void 0);
|
|
69
|
+
__decorate([
|
|
70
|
+
property()
|
|
71
|
+
], HelixHeadlessTab.prototype, "href", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
property()
|
|
74
|
+
], HelixHeadlessTab.prototype, "rel", void 0);
|
|
75
|
+
__decorate([
|
|
76
|
+
property()
|
|
77
|
+
], HelixHeadlessTab.prototype, "target", void 0);
|
|
32
78
|
HelixHeadlessTab = __decorate([
|
|
33
79
|
customElement('helix-headless-tab')
|
|
34
80
|
], HelixHeadlessTab);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, KeyboardNavigationController, type SelectionMode } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import { HelixHeadlessTag } from '../root/tag-root.js';
|
|
4
|
-
|
|
4
|
+
/** How many tags a group lets a reader select: core's `SelectionMode`. */
|
|
5
|
+
export type TagSelectionMode = SelectionMode;
|
|
5
6
|
/**
|
|
6
7
|
* @summary A set of tags that the arrow keys move through, and that can be removed and selected from the keyboard.
|
|
7
8
|
*
|
|
@@ -17,9 +18,16 @@ import { type TagSelectionMode } from '../selection.js';
|
|
|
17
18
|
* `single` selects one at a time and `multiple` any number. `value` holds the
|
|
18
19
|
* selected tags' values, separated by commas, and a change asks with
|
|
19
20
|
* `helix-before-change` and tells with `change`. Enter on a tag with an
|
|
20
|
-
* `href` follows its link. A `disabled` tag is passed over.
|
|
21
|
+
* `href` follows its link. A `disabled` tag is passed over. A selected tag
|
|
22
|
+
* that is removed leaves `value`, which then sends `change`.
|
|
21
23
|
*
|
|
22
|
-
* Name the set with `label`.
|
|
24
|
+
* Name the set with `label`. Each tag is named by its words alone, without
|
|
25
|
+
* its remove button.
|
|
26
|
+
*
|
|
27
|
+
* **Removing the last tag keeps focus in the group.** With no tags left the
|
|
28
|
+
* set is a `group`, and focus moves to its `empty` part,
|
|
29
|
+
* which holds the `empty` slot: put words there such as "No filters" for a
|
|
30
|
+
* reader to hear.
|
|
23
31
|
*
|
|
24
32
|
* @since 0.1.0
|
|
25
33
|
* @status stable
|
|
@@ -27,10 +35,13 @@ import { type TagSelectionMode } from '../selection.js';
|
|
|
27
35
|
* @documentation https://fusion.dev/helix/components/tag
|
|
28
36
|
*
|
|
29
37
|
* @slot - The tags, in the order they are read.
|
|
38
|
+
* @slot empty - Shown in place of the tags while there are none, such as "No filters".
|
|
30
39
|
*
|
|
31
40
|
* @event {Event} change - Fired once a press has changed which tags are selected.
|
|
32
41
|
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press changes which tags are selected, with the `value` it would leave. Cancelable.
|
|
33
42
|
* @event {HelixDismissEvent} helix-dismiss - A tag's remove button was pressed, or Delete or Backspace on the focused tag. Bubbles from that tag, which is its `target`; remove the tag in response. Cancelable.
|
|
43
|
+
*
|
|
44
|
+
* @csspart empty - Holds the `empty` slot while the group has no tags, rendered only then. Takes focus when the last tag is removed.
|
|
34
45
|
*/
|
|
35
46
|
export declare class HelixHeadlessTagGroup extends HelixElement {
|
|
36
47
|
/**
|
|
@@ -93,9 +104,25 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
|
|
|
93
104
|
protected press(tag: HelixHeadlessTag): void;
|
|
94
105
|
private readonly handleKeydown;
|
|
95
106
|
private readonly handleClick;
|
|
107
|
+
/**
|
|
108
|
+
* What happens once a tag has asked to be removed and the page has had its
|
|
109
|
+
* turn: a selected tag that left takes its value out of `value`, and focus
|
|
110
|
+
* that the last tag took with it comes to the group's `empty` part.
|
|
111
|
+
*
|
|
112
|
+
* Asked after the event and again a frame later, as `MixinDismissible`
|
|
113
|
+
* asks, because a framework removes the tag on its next render, after the
|
|
114
|
+
* handler. A canceled dismissal changes nothing.
|
|
115
|
+
*/
|
|
116
|
+
private readonly handleDismiss;
|
|
117
|
+
private readonly handleSlotchange;
|
|
96
118
|
private readonly handleFocusin;
|
|
97
119
|
connectedCallback(): void;
|
|
98
120
|
disconnectedCallback(): void;
|
|
121
|
+
/**
|
|
122
|
+
* Whether the group is known to hold no tags, which a server and a
|
|
123
|
+
* hydrating render cannot know: there it holds whatever it was given.
|
|
124
|
+
*/
|
|
125
|
+
private get empty();
|
|
99
126
|
protected willUpdate(changed: PropertyValues<this>): void;
|
|
100
127
|
protected updated(changed: PropertyValues<this>): void;
|
|
101
128
|
protected render(): TemplateResult;
|
|
@@ -4,11 +4,10 @@ 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 { commaList, customElement, HelixBeforeChangeEvent, HelixElement, hostAria, KeyboardNavigationController, requestChange, rovingStop, slottedWords, whenHydrated, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { html } from 'lit';
|
|
7
|
+
import { commaList, customElement, deepActiveElement, deepContains, HelixBeforeChangeEvent, HelixElement, hostAria, KeyboardNavigationController, pressedValue, prunedValue, requestChange, rovingStop, slottedWords, typeaheadBy, whenHydrated, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessTag } from '../root/tag-root.js';
|
|
11
|
-
import { pressedValue } from '../selection.js';
|
|
12
11
|
let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
|
|
13
12
|
constructor() {
|
|
14
13
|
super(...arguments);
|
|
@@ -18,7 +17,7 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
|
|
|
18
17
|
items: () => this.reachable.map((tag) => this.navigable(tag)),
|
|
19
18
|
origin: 'focus',
|
|
20
19
|
selects: () => false,
|
|
21
|
-
typeahead: (item) => item.words,
|
|
20
|
+
typeahead: typeaheadBy((item) => item.words),
|
|
22
21
|
wrap: false,
|
|
23
22
|
});
|
|
24
23
|
this.handleKeydown = (event) => {
|
|
@@ -53,6 +52,37 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
|
|
|
53
52
|
this.press(tag);
|
|
54
53
|
}
|
|
55
54
|
};
|
|
55
|
+
this.handleDismiss = (event) => {
|
|
56
|
+
const tag = this.tagOf(event);
|
|
57
|
+
if (!tag) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const had = deepContains(this, deepActiveElement());
|
|
61
|
+
const settle = async () => {
|
|
62
|
+
if (event.defaultPrevented || tag.isConnected) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const left = this.tags.map((one) => this.valueFor(one));
|
|
66
|
+
const pruned = prunedValue(this.value, this.valueFor(tag), left);
|
|
67
|
+
if (pruned !== this.value) {
|
|
68
|
+
this.value = pruned;
|
|
69
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
70
|
+
}
|
|
71
|
+
const active = document.activeElement;
|
|
72
|
+
if (!had || this.tags.length > 0 || (active !== null && active !== document.body)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.requestUpdate();
|
|
76
|
+
await this.updateComplete;
|
|
77
|
+
this.renderRoot.querySelector('[part~="empty"]')?.focus();
|
|
78
|
+
};
|
|
79
|
+
queueMicrotask(() => void settle());
|
|
80
|
+
requestAnimationFrame(() => void settle());
|
|
81
|
+
};
|
|
82
|
+
this.handleSlotchange = () => {
|
|
83
|
+
this.sync();
|
|
84
|
+
this.requestUpdate();
|
|
85
|
+
};
|
|
56
86
|
this.handleFocusin = (event) => {
|
|
57
87
|
const tag = this.tagOf(event);
|
|
58
88
|
if (tag && tag !== this.last) {
|
|
@@ -122,24 +152,33 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
|
|
|
122
152
|
this.addEventListener('keydown', this.handleKeydown);
|
|
123
153
|
this.addEventListener('click', this.handleClick);
|
|
124
154
|
this.addEventListener('focusin', this.handleFocusin);
|
|
155
|
+
this.addEventListener('helix-dismiss', this.handleDismiss);
|
|
125
156
|
}
|
|
126
157
|
disconnectedCallback() {
|
|
127
158
|
super.disconnectedCallback();
|
|
128
159
|
this.removeEventListener('keydown', this.handleKeydown);
|
|
129
160
|
this.removeEventListener('click', this.handleClick);
|
|
130
161
|
this.removeEventListener('focusin', this.handleFocusin);
|
|
162
|
+
this.removeEventListener('helix-dismiss', this.handleDismiss);
|
|
163
|
+
}
|
|
164
|
+
get empty() {
|
|
165
|
+
return this.childrenKnown && this.tags.length === 0;
|
|
131
166
|
}
|
|
132
167
|
willUpdate(changed) {
|
|
133
168
|
super.willUpdate(changed);
|
|
134
|
-
|
|
135
|
-
this.internals
|
|
169
|
+
const empty = this.empty;
|
|
170
|
+
hostAria(this.internals, { label: this.label, role: empty ? 'group' : 'grid' });
|
|
171
|
+
this.internals.ariaMultiSelectable = this.selectionMode === 'multiple' && !empty ? 'true' : null;
|
|
136
172
|
}
|
|
137
173
|
updated(changed) {
|
|
138
174
|
super.updated(changed);
|
|
139
175
|
this.sync();
|
|
140
176
|
}
|
|
141
177
|
render() {
|
|
142
|
-
|
|
178
|
+
const empty = this.empty
|
|
179
|
+
? html `<div part="empty" tabindex="-1"><slot name="empty"></slot></div>`
|
|
180
|
+
: nothing;
|
|
181
|
+
return html `<slot @slotchange=${this.handleSlotchange}></slot>${empty}`;
|
|
143
182
|
}
|
|
144
183
|
};
|
|
145
184
|
__decorate([
|
|
@@ -36,7 +36,10 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
|
|
|
36
36
|
}
|
|
37
37
|
willUpdate(changed) {
|
|
38
38
|
super.willUpdate(changed);
|
|
39
|
-
hostAria(this.internals, {
|
|
39
|
+
hostAria(this.internals, {
|
|
40
|
+
label: this.grouped ? this.words : '',
|
|
41
|
+
role: this.grouped ? 'row' : '',
|
|
42
|
+
});
|
|
40
43
|
this.internals.ariaSelected = this.selected === undefined ? null : String(this.selected);
|
|
41
44
|
this.internals.ariaDisabled = this.disabled ? 'true' : null;
|
|
42
45
|
this.toggleState('selected', this.selected === true);
|
|
@@ -58,7 +61,11 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
|
|
|
58
61
|
render() {
|
|
59
62
|
const content = html `<slot name="start" part="start"></slot>${this.renderLabel()}${this.dismissible ? this.renderDismiss() : nothing}`;
|
|
60
63
|
return this.grouped
|
|
61
|
-
? html `<span
|
|
64
|
+
? html `<span
|
|
65
|
+
aria-label=${this.words || nothing}
|
|
66
|
+
part="cell"
|
|
67
|
+
role="gridcell"
|
|
68
|
+
tabindex=${this.focusable ? '0' : '-1'}
|
|
62
69
|
>${content}</span
|
|
63
70
|
>`
|
|
64
71
|
: content;
|