@fusion.dev/helix-headless 0.0.0-dev
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/LICENSE +21 -0
- package/custom-elements.json +75202 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +2 -0
- package/dist/accordion/item/accordion-item.d.ts +141 -0
- package/dist/accordion/item/accordion-item.js +214 -0
- package/dist/accordion/root/accordion-root.d.ts +57 -0
- package/dist/accordion/root/accordion-root.js +96 -0
- package/dist/announcement/index.d.ts +1 -0
- package/dist/announcement/index.js +1 -0
- package/dist/announcement/root/announcement-root.d.ts +106 -0
- package/dist/announcement/root/announcement-root.js +144 -0
- package/dist/app-header/index.d.ts +1 -0
- package/dist/app-header/index.js +1 -0
- package/dist/app-header/root/app-header-root.d.ts +113 -0
- package/dist/app-header/root/app-header-root.js +147 -0
- package/dist/avatar/group/avatar-group.d.ts +116 -0
- package/dist/avatar/group/avatar-group.js +136 -0
- package/dist/avatar/index.d.ts +2 -0
- package/dist/avatar/index.js +2 -0
- package/dist/avatar/root/avatar-root.d.ts +71 -0
- package/dist/avatar/root/avatar-root.js +147 -0
- package/dist/badge/index.d.ts +1 -0
- package/dist/badge/index.js +1 -0
- package/dist/badge/root/badge-root.d.ts +61 -0
- package/dist/badge/root/badge-root.js +121 -0
- package/dist/bar-chart/index.d.ts +1 -0
- package/dist/bar-chart/index.js +1 -0
- package/dist/bar-chart/root/bar-chart-root.d.ts +266 -0
- package/dist/bar-chart/root/bar-chart-root.js +515 -0
- package/dist/blockquote/index.d.ts +1 -0
- package/dist/blockquote/index.js +1 -0
- package/dist/blockquote/root/blockquote-root.d.ts +51 -0
- package/dist/blockquote/root/blockquote-root.js +84 -0
- package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +104 -0
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +137 -0
- package/dist/breadcrumbs/index.d.ts +2 -0
- package/dist/breadcrumbs/index.js +2 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +77 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.js +94 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/button/root/button-root.d.ts +251 -0
- package/dist/button/root/button-root.js +401 -0
- package/dist/button-group/index.d.ts +1 -0
- package/dist/button-group/index.js +1 -0
- package/dist/button-group/root/button-group-root.d.ts +62 -0
- package/dist/button-group/root/button-group-root.js +62 -0
- package/dist/callout/index.d.ts +1 -0
- package/dist/callout/index.js +1 -0
- package/dist/callout/root/callout-root.d.ts +72 -0
- package/dist/callout/root/callout-root.js +90 -0
- package/dist/card/index.d.ts +1 -0
- package/dist/card/index.js +1 -0
- package/dist/card/root/card-root.d.ts +54 -0
- package/dist/card/root/card-root.js +71 -0
- package/dist/carousel/index.d.ts +1 -0
- package/dist/carousel/index.js +1 -0
- package/dist/carousel/root/carousel-root.d.ts +399 -0
- package/dist/carousel/root/carousel-root.js +723 -0
- package/dist/chat/composer/chat-composer.d.ts +160 -0
- package/dist/chat/composer/chat-composer.js +273 -0
- package/dist/chat/index.d.ts +3 -0
- package/dist/chat/index.js +3 -0
- package/dist/chat/message/chat-message.d.ts +170 -0
- package/dist/chat/message/chat-message.js +271 -0
- package/dist/chat/root/chat-root.d.ts +216 -0
- package/dist/chat/root/chat-root.js +389 -0
- package/dist/checkbox/index.d.ts +1 -0
- package/dist/checkbox/index.js +1 -0
- package/dist/checkbox/root/checkbox-root.d.ts +108 -0
- package/dist/checkbox/root/checkbox-root.js +207 -0
- package/dist/checkbox-group/index.d.ts +1 -0
- package/dist/checkbox-group/index.js +1 -0
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +155 -0
- package/dist/checkbox-group/root/checkbox-group-root.js +293 -0
- package/dist/code/index.d.ts +1 -0
- package/dist/code/index.js +1 -0
- package/dist/code/root/code-root.d.ts +36 -0
- package/dist/code/root/code-root.js +43 -0
- package/dist/code-block/index.d.ts +1 -0
- package/dist/code-block/index.js +1 -0
- package/dist/code-block/root/code-block-root.d.ts +112 -0
- package/dist/code-block/root/code-block-root.js +155 -0
- package/dist/color-input/index.d.ts +1 -0
- package/dist/color-input/index.js +1 -0
- package/dist/color-input/root/color-input-root.d.ts +227 -0
- package/dist/color-input/root/color-input-root.js +456 -0
- package/dist/color-picker/color.d.ts +74 -0
- package/dist/color-picker/color.js +238 -0
- package/dist/color-picker/index.d.ts +2 -0
- package/dist/color-picker/index.js +2 -0
- package/dist/color-picker/root/color-picker-root.d.ts +303 -0
- package/dist/color-picker/root/color-picker-root.js +576 -0
- package/dist/combobox/index.d.ts +1 -0
- package/dist/combobox/index.js +1 -0
- package/dist/combobox/root/combobox-root.d.ts +396 -0
- package/dist/combobox/root/combobox-root.js +808 -0
- package/dist/command/index.d.ts +1 -0
- package/dist/command/index.js +1 -0
- package/dist/command/root/command-root.d.ts +166 -0
- package/dist/command/root/command-root.js +353 -0
- package/dist/copy-button/index.d.ts +1 -0
- package/dist/copy-button/index.js +1 -0
- package/dist/copy-button/root/copy-button-root.d.ts +154 -0
- package/dist/copy-button/root/copy-button-root.js +225 -0
- package/dist/data-table/index.d.ts +2 -0
- package/dist/data-table/index.js +2 -0
- package/dist/data-table/root/data-table-root.d.ts +392 -0
- package/dist/data-table/root/data-table-root.js +729 -0
- package/dist/data-table/root/order.d.ts +116 -0
- package/dist/data-table/root/order.js +175 -0
- package/dist/date-input/index.d.ts +1 -0
- package/dist/date-input/index.js +1 -0
- package/dist/date-input/root/date-input-root.d.ts +361 -0
- package/dist/date-input/root/date-input-root.js +791 -0
- package/dist/date-picker/index.d.ts +1 -0
- package/dist/date-picker/index.js +1 -0
- package/dist/date-picker/root/date-picker-root.d.ts +361 -0
- package/dist/date-picker/root/date-picker-root.js +837 -0
- package/dist/dialog/index.d.ts +1 -0
- package/dist/dialog/index.js +1 -0
- package/dist/dialog/root/dialog-root.d.ts +185 -0
- package/dist/dialog/root/dialog-root.js +319 -0
- package/dist/divider/index.d.ts +1 -0
- package/dist/divider/index.js +1 -0
- package/dist/divider/root/divider-root.d.ts +68 -0
- package/dist/divider/root/divider-root.js +116 -0
- package/dist/donut-chart/index.d.ts +1 -0
- package/dist/donut-chart/index.js +1 -0
- package/dist/donut-chart/root/donut-chart-root.d.ts +209 -0
- package/dist/donut-chart/root/donut-chart-root.js +395 -0
- package/dist/drawer/index.d.ts +1 -0
- package/dist/drawer/index.js +1 -0
- package/dist/drawer/root/drawer-root.d.ts +144 -0
- package/dist/drawer/root/drawer-root.js +222 -0
- package/dist/drilldown/index.d.ts +2 -0
- package/dist/drilldown/index.js +2 -0
- package/dist/drilldown/item/drilldown-item.d.ts +87 -0
- package/dist/drilldown/item/drilldown-item.js +152 -0
- package/dist/drilldown/root/drilldown-root.d.ts +115 -0
- package/dist/drilldown/root/drilldown-root.js +263 -0
- package/dist/element-events.generated.d.ts +1 -0
- package/dist/element-events.generated.js +125 -0
- package/dist/empty/index.d.ts +1 -0
- package/dist/empty/index.js +1 -0
- package/dist/empty/root/empty-root.d.ts +47 -0
- package/dist/empty/root/empty-root.js +68 -0
- package/dist/file-input/index.d.ts +1 -0
- package/dist/file-input/index.js +1 -0
- package/dist/file-input/root/file-input-root.d.ts +196 -0
- package/dist/file-input/root/file-input-root.js +488 -0
- package/dist/format-date/index.d.ts +1 -0
- package/dist/format-date/index.js +1 -0
- package/dist/format-date/root/format-date-root.d.ts +151 -0
- package/dist/format-date/root/format-date-root.js +266 -0
- package/dist/heatmap/index.d.ts +2 -0
- package/dist/heatmap/index.js +2 -0
- package/dist/heatmap/root/buckets.d.ts +62 -0
- package/dist/heatmap/root/buckets.js +85 -0
- package/dist/heatmap/root/heatmap-root.d.ts +330 -0
- package/dist/heatmap/root/heatmap-root.js +526 -0
- package/dist/icon/index.d.ts +1 -0
- package/dist/icon/index.js +1 -0
- package/dist/icon/root/icon-root.d.ts +53 -0
- package/dist/icon/root/icon-root.js +93 -0
- package/dist/icon-button/index.d.ts +1 -0
- package/dist/icon-button/index.js +1 -0
- package/dist/icon-button/root/icon-button-root.d.ts +40 -0
- package/dist/icon-button/root/icon-button-root.js +46 -0
- package/dist/image/index.d.ts +1 -0
- package/dist/image/index.js +1 -0
- package/dist/image/root/image-root.d.ts +213 -0
- package/dist/image/root/image-root.js +335 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +99 -0
- package/dist/input/index.d.ts +1 -0
- package/dist/input/index.js +1 -0
- package/dist/input/root/input-root.d.ts +281 -0
- package/dist/input/root/input-root.js +472 -0
- package/dist/kbd/index.d.ts +1 -0
- package/dist/kbd/index.js +1 -0
- package/dist/kbd/root/kbd-root.d.ts +98 -0
- package/dist/kbd/root/kbd-root.js +230 -0
- package/dist/legend/index.d.ts +1 -0
- package/dist/legend/index.js +1 -0
- package/dist/legend/root/legend-root.d.ts +83 -0
- package/dist/legend/root/legend-root.js +125 -0
- package/dist/line-chart/index.d.ts +1 -0
- package/dist/line-chart/index.js +1 -0
- package/dist/line-chart/root/line-chart-root.d.ts +238 -0
- package/dist/line-chart/root/line-chart-root.js +365 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/list/index.js +2 -0
- package/dist/list/item/list-item.d.ts +95 -0
- package/dist/list/item/list-item.js +148 -0
- package/dist/list/root/list-root.d.ts +72 -0
- package/dist/list/root/list-root.js +61 -0
- package/dist/listbox/index.d.ts +1 -0
- package/dist/listbox/index.js +1 -0
- package/dist/listbox/root/listbox-root.d.ts +357 -0
- package/dist/listbox/root/listbox-root.js +890 -0
- package/dist/loader/index.d.ts +1 -0
- package/dist/loader/index.js +1 -0
- package/dist/loader/root/loader-root.d.ts +52 -0
- package/dist/loader/root/loader-root.js +74 -0
- package/dist/megamenu/index.d.ts +1 -0
- package/dist/megamenu/index.js +1 -0
- package/dist/megamenu/root/megamenu-root.d.ts +254 -0
- package/dist/megamenu/root/megamenu-root.js +526 -0
- package/dist/menu/index.d.ts +2 -0
- package/dist/menu/index.js +2 -0
- package/dist/menu/item/menu-item.d.ts +231 -0
- package/dist/menu/item/menu-item.js +405 -0
- package/dist/menu/root/menu-root.d.ts +158 -0
- package/dist/menu/root/menu-root.js +422 -0
- package/dist/meter/index.d.ts +1 -0
- package/dist/meter/index.js +1 -0
- package/dist/meter/root/meter-root.d.ts +234 -0
- package/dist/meter/root/meter-root.js +316 -0
- package/dist/navigation/index.d.ts +1 -0
- package/dist/navigation/index.js +1 -0
- package/dist/navigation/root/navigation-root.d.ts +55 -0
- package/dist/navigation/root/navigation-root.js +130 -0
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.js +2 -0
- package/dist/notifications/notification/notification.d.ts +181 -0
- package/dist/notifications/notification/notification.js +305 -0
- package/dist/notifications/root/notifications-root.d.ts +168 -0
- package/dist/notifications/root/notifications-root.js +291 -0
- package/dist/number-input/index.d.ts +1 -0
- package/dist/number-input/index.js +1 -0
- package/dist/number-input/root/number-input-root.d.ts +282 -0
- package/dist/number-input/root/number-input-root.js +592 -0
- package/dist/option/group/option-group.d.ts +38 -0
- package/dist/option/group/option-group.js +58 -0
- package/dist/option/index.d.ts +2 -0
- package/dist/option/index.js +2 -0
- package/dist/option/root/option-root.d.ts +194 -0
- package/dist/option/root/option-root.js +247 -0
- package/dist/outline/index.d.ts +1 -0
- package/dist/outline/index.js +1 -0
- package/dist/outline/root/outline-root.d.ts +237 -0
- package/dist/outline/root/outline-root.js +519 -0
- package/dist/pagination/index.d.ts +3 -0
- package/dist/pagination/index.js +3 -0
- package/dist/pagination/root/pagination-root.d.ts +390 -0
- package/dist/pagination/root/pagination-root.js +695 -0
- package/dist/pagination/size/pagination-size.d.ts +127 -0
- package/dist/pagination/size/pagination-size.js +232 -0
- package/dist/pagination/summary/pagination-summary.d.ts +90 -0
- package/dist/pagination/summary/pagination-summary.js +145 -0
- package/dist/pin-input/index.d.ts +1 -0
- package/dist/pin-input/index.js +1 -0
- package/dist/pin-input/root/pin-input-root.d.ts +292 -0
- package/dist/pin-input/root/pin-input-root.js +553 -0
- package/dist/popover/index.d.ts +1 -0
- package/dist/popover/index.js +1 -0
- package/dist/popover/root/popover-root.d.ts +108 -0
- package/dist/popover/root/popover-root.js +231 -0
- package/dist/progress/index.d.ts +1 -0
- package/dist/progress/index.js +1 -0
- package/dist/progress/root/progress-root.d.ts +125 -0
- package/dist/progress/root/progress-root.js +212 -0
- package/dist/qr-code/index.d.ts +1 -0
- package/dist/qr-code/index.js +1 -0
- package/dist/qr-code/root/qr-code-root.d.ts +93 -0
- package/dist/qr-code/root/qr-code-root.js +233 -0
- package/dist/quote/index.d.ts +1 -0
- package/dist/quote/index.js +1 -0
- package/dist/quote/root/quote-root.d.ts +36 -0
- package/dist/quote/root/quote-root.js +50 -0
- package/dist/radio/group/radio-group.d.ts +84 -0
- package/dist/radio/group/radio-group.js +170 -0
- package/dist/radio/index.d.ts +2 -0
- package/dist/radio/index.js +2 -0
- package/dist/radio/root/radio-root.d.ts +102 -0
- package/dist/radio/root/radio-root.js +151 -0
- package/dist/rating/index.d.ts +1 -0
- package/dist/rating/index.js +1 -0
- package/dist/rating/root/rating-root.d.ts +242 -0
- package/dist/rating/root/rating-root.js +484 -0
- package/dist/scroll-area/index.d.ts +1 -0
- package/dist/scroll-area/index.js +1 -0
- package/dist/scroll-area/root/scroll-area-root.d.ts +96 -0
- package/dist/scroll-area/root/scroll-area-root.js +215 -0
- package/dist/select/index.d.ts +1 -0
- package/dist/select/index.js +1 -0
- package/dist/select/root/select-root.d.ts +170 -0
- package/dist/select/root/select-root.js +419 -0
- package/dist/skeleton/index.d.ts +1 -0
- package/dist/skeleton/index.js +1 -0
- package/dist/skeleton/root/skeleton-root.d.ts +31 -0
- package/dist/skeleton/root/skeleton-root.js +42 -0
- package/dist/slider/index.d.ts +1 -0
- package/dist/slider/index.js +1 -0
- package/dist/slider/root/slider-root.d.ts +235 -0
- package/dist/slider/root/slider-root.js +488 -0
- package/dist/sparkline/index.d.ts +1 -0
- package/dist/sparkline/index.js +1 -0
- package/dist/sparkline/root/sparkline-root.d.ts +147 -0
- package/dist/sparkline/root/sparkline-root.js +278 -0
- package/dist/speed-dial/index.d.ts +2 -0
- package/dist/speed-dial/index.js +2 -0
- package/dist/speed-dial/item/speed-dial-item.d.ts +71 -0
- package/dist/speed-dial/item/speed-dial-item.js +113 -0
- package/dist/speed-dial/root/speed-dial-root.d.ts +122 -0
- package/dist/speed-dial/root/speed-dial-root.js +251 -0
- package/dist/splitter/index.d.ts +1 -0
- package/dist/splitter/index.js +1 -0
- package/dist/splitter/root/splitter-root.d.ts +491 -0
- package/dist/splitter/root/splitter-root.js +1030 -0
- package/dist/stat/group/stat-group.d.ts +30 -0
- package/dist/stat/group/stat-group.js +47 -0
- package/dist/stat/index.d.ts +2 -0
- package/dist/stat/index.js +2 -0
- package/dist/stat/root/stat-root.d.ts +62 -0
- package/dist/stat/root/stat-root.js +51 -0
- package/dist/steps/index.d.ts +2 -0
- package/dist/steps/index.js +2 -0
- package/dist/steps/root/steps-root.d.ts +110 -0
- package/dist/steps/root/steps-root.js +130 -0
- package/dist/steps/step/step.d.ts +139 -0
- package/dist/steps/step/step.js +215 -0
- package/dist/switch/index.d.ts +1 -0
- package/dist/switch/index.js +1 -0
- package/dist/switch/root/switch-root.d.ts +74 -0
- package/dist/switch/root/switch-root.js +153 -0
- package/dist/table/cell/table-cell.d.ts +34 -0
- package/dist/table/cell/table-cell.js +52 -0
- package/dist/table/column/table-column.d.ts +165 -0
- package/dist/table/column/table-column.js +222 -0
- package/dist/table/index.d.ts +4 -0
- package/dist/table/index.js +4 -0
- package/dist/table/root/table-root.d.ts +98 -0
- package/dist/table/root/table-root.js +161 -0
- package/dist/table/row/table-row.d.ts +27 -0
- package/dist/table/row/table-row.js +42 -0
- package/dist/tabs/index.d.ts +3 -0
- package/dist/tabs/index.js +3 -0
- package/dist/tabs/panel/tab-panel.d.ts +67 -0
- package/dist/tabs/panel/tab-panel.js +98 -0
- package/dist/tabs/root/tabs-root.d.ts +123 -0
- package/dist/tabs/root/tabs-root.js +217 -0
- package/dist/tabs/tab/tab.d.ts +64 -0
- package/dist/tabs/tab/tab.js +116 -0
- package/dist/tag/index.d.ts +1 -0
- package/dist/tag/index.js +1 -0
- package/dist/tag/root/tag-root.d.ts +41 -0
- package/dist/tag/root/tag-root.js +67 -0
- package/dist/textarea/index.d.ts +1 -0
- package/dist/textarea/index.js +1 -0
- package/dist/textarea/root/textarea-root.d.ts +215 -0
- package/dist/textarea/root/textarea-root.js +380 -0
- package/dist/time-input/index.d.ts +1 -0
- package/dist/time-input/index.js +1 -0
- package/dist/time-input/root/time-input-root.d.ts +322 -0
- package/dist/time-input/root/time-input-root.js +661 -0
- package/dist/time-picker/index.d.ts +1 -0
- package/dist/time-picker/index.js +1 -0
- package/dist/time-picker/root/time-picker-root.d.ts +380 -0
- package/dist/time-picker/root/time-picker-root.js +669 -0
- package/dist/timeline/index.d.ts +2 -0
- package/dist/timeline/index.js +2 -0
- package/dist/timeline/item/timeline-item.d.ts +81 -0
- package/dist/timeline/item/timeline-item.js +128 -0
- package/dist/timeline/root/timeline-root.d.ts +52 -0
- package/dist/timeline/root/timeline-root.js +72 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +2 -0
- package/dist/toast/item/toast-item.d.ts +183 -0
- package/dist/toast/item/toast-item.js +345 -0
- package/dist/toast/root/toast-root.d.ts +72 -0
- package/dist/toast/root/toast-root.js +87 -0
- package/dist/toggle/group/toggle-group.d.ts +140 -0
- package/dist/toggle/group/toggle-group.js +278 -0
- package/dist/toggle/index.d.ts +2 -0
- package/dist/toggle/index.js +2 -0
- package/dist/toggle/root/toggle-root.d.ts +99 -0
- package/dist/toggle/root/toggle-root.js +146 -0
- package/dist/toolbar/index.d.ts +1 -0
- package/dist/toolbar/index.js +1 -0
- package/dist/toolbar/root/toolbar-root.d.ts +105 -0
- package/dist/toolbar/root/toolbar-root.js +260 -0
- package/dist/tooltip/index.d.ts +1 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/tooltip/root/tooltip-root.d.ts +388 -0
- package/dist/tooltip/root/tooltip-root.js +707 -0
- package/dist/tour/index.d.ts +2 -0
- package/dist/tour/index.js +2 -0
- package/dist/tour/root/tour-root.d.ts +161 -0
- package/dist/tour/root/tour-root.js +351 -0
- package/dist/tour/stop/tour-stop.d.ts +131 -0
- package/dist/tour/stop/tour-stop.js +249 -0
- package/dist/tree/index.d.ts +2 -0
- package/dist/tree/index.js +2 -0
- package/dist/tree/item/tree-item.d.ts +208 -0
- package/dist/tree/item/tree-item.js +381 -0
- package/dist/tree/root/tree-root.d.ts +88 -0
- package/dist/tree/root/tree-root.js +287 -0
- package/dist/trend/index.d.ts +1 -0
- package/dist/trend/index.js +1 -0
- package/dist/trend/root/trend-root.d.ts +67 -0
- package/dist/trend/root/trend-root.js +106 -0
- package/dist/video/index.d.ts +1 -0
- package/dist/video/index.js +1 -0
- package/dist/video/root/video-root.d.ts +711 -0
- package/dist/video/root/video-root.js +1821 -0
- package/dist/video-playlist/index.d.ts +3 -0
- package/dist/video-playlist/index.js +3 -0
- package/dist/video-playlist/item/video-playlist-item.d.ts +101 -0
- package/dist/video-playlist/item/video-playlist-item.js +162 -0
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +121 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +237 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +108 -0
- package/dist/video-playlist/root/video-playlist-root.js +218 -0
- package/dist/virtual-list/index.d.ts +1 -0
- package/dist/virtual-list/index.js +1 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +121 -0
- package/dist/virtual-list/root/virtual-list-root.js +221 -0
- package/jsx.d.ts +49767 -0
- package/package.json +77 -0
- package/svelte.d.ts +129 -0
- package/vue.d.ts +9242 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* @summary One row in a menu: a command, a choice, or a way into a submenu.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Three shapes, one element, because a reader moving down a menu with the
|
|
8
|
+
* arrow keys does not care which kind each row is and neither should the
|
|
9
|
+
* markup. What it is follows from what it was given: something slotted into
|
|
10
|
+
* `submenu` makes it a way in, `checked` makes it a choice, and neither makes
|
|
11
|
+
* it a command.
|
|
12
|
+
*
|
|
13
|
+
* **The role follows the shape.** A row that can be ticked is
|
|
14
|
+
* `menuitemcheckbox` and carries `aria-checked`, because a reader is told "one
|
|
15
|
+
* of these is on" rather than "here is a button". A row that opens a page keeps
|
|
16
|
+
* `menuitem` and says `aria-haspopup`, since the page it opens is a menu.
|
|
17
|
+
*
|
|
18
|
+
* Whether it is on the page showing is set by the menu as a property, never as
|
|
19
|
+
* an attribute: an attribute written onto a consumer's element after it
|
|
20
|
+
* upgrades is a hydration mismatch waiting to happen.
|
|
21
|
+
*
|
|
22
|
+
* @since 0.1.0
|
|
23
|
+
* @status experimental
|
|
24
|
+
* @aliases Menu option, Menu row
|
|
25
|
+
* @documentation https://helix.fusion.dev/components/menu
|
|
26
|
+
*
|
|
27
|
+
* @slot - The label.
|
|
28
|
+
* @slot start - Before the label. A mark about the row.
|
|
29
|
+
* @slot submenu - Rows this one opens, which makes it a way in.
|
|
30
|
+
*
|
|
31
|
+
* @event {HelixSelectEvent} helix-select - Fired when the row is chosen. Cancelable: refusing it takes the decision and nothing moves.
|
|
32
|
+
*
|
|
33
|
+
* @csspart item - The row, which is an `<a>` where `href` is set.
|
|
34
|
+
* @csspart mark - The trailing mark: a tick when checked, a caret when it opens.
|
|
35
|
+
* a page. One column, because a row is never both.
|
|
36
|
+
* @csspart start - The leading column, which is a slot and is always there.
|
|
37
|
+
*/
|
|
38
|
+
export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
39
|
+
/**
|
|
40
|
+
* What this row stands for, carried in the event when it is chosen.
|
|
41
|
+
*/
|
|
42
|
+
value: string;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the row is one of a set of choices.
|
|
45
|
+
*
|
|
46
|
+
* Separate from `checked`, and it has to be. A row that is off is still a
|
|
47
|
+
* choice, and `checked` alone cannot say so: an unticked row and a row that
|
|
48
|
+
* was never tickable look identical from here. Announcing only the ticked one
|
|
49
|
+
* as a checkbox tells a reader its siblings are commands, which is the one
|
|
50
|
+
* thing they are not.
|
|
51
|
+
*
|
|
52
|
+
* `checked` implies it, so a row that is on never has to say both.
|
|
53
|
+
*/
|
|
54
|
+
checkable: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the row is ticked.
|
|
57
|
+
*/
|
|
58
|
+
checked: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the row can be chosen.
|
|
61
|
+
*/
|
|
62
|
+
disabled: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Where the row goes, which makes it a real link.
|
|
65
|
+
*
|
|
66
|
+
* **The row itself becomes an `<a>`, and the role goes with it.** A
|
|
67
|
+
* `menuitem` cannot contain a link: measured in Chromium, an anchor inside
|
|
68
|
+
* one is exposed as a child and the row loses its accessible name entirely.
|
|
69
|
+
* So the anchor *is* the row, carrying `menuitem` and the `tabindex` the menu
|
|
70
|
+
* roves, which is the same manoeuvre `Tree.Item` makes and the reason `href`
|
|
71
|
+
* changes the markup rather than only adding an attribute.
|
|
72
|
+
*
|
|
73
|
+
* **`Option` cannot have this and the reason is worth knowing**, since a
|
|
74
|
+
* command palette is the obvious place to want it. A listbox has to keep
|
|
75
|
+
* focus somewhere else, in the text field for a combobox, so it says which
|
|
76
|
+
* option is current with `aria-activedescendant`. That reference can only
|
|
77
|
+
* point at the option's host, measured: assigning
|
|
78
|
+
* `ariaActiveDescendantElement` an element inside a sibling shadow root reads
|
|
79
|
+
* back `null`. So `role="option"` is pinned to the host, and an anchor under
|
|
80
|
+
* it hits the naming failure above. A menu has no such constraint because
|
|
81
|
+
* focus really moves to the row.
|
|
82
|
+
*/
|
|
83
|
+
href: string;
|
|
84
|
+
/**
|
|
85
|
+
* Where the link opens, for the rows that should leave the page.
|
|
86
|
+
*/
|
|
87
|
+
target: string;
|
|
88
|
+
/**
|
|
89
|
+
* The relationship, which `target="_blank"` should always set.
|
|
90
|
+
*/
|
|
91
|
+
rel: string;
|
|
92
|
+
/**
|
|
93
|
+
* Whether the link saves the file rather than opening it.
|
|
94
|
+
*
|
|
95
|
+
* Only meaningful beside an `href`, and only for a file served from this
|
|
96
|
+
* origin: the attribute is ignored cross-origin, which is the browser's rule
|
|
97
|
+
* about who may name a file on somebody's disk rather than a gap here.
|
|
98
|
+
*/
|
|
99
|
+
download: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Whether this row is on the page currently showing.
|
|
102
|
+
*
|
|
103
|
+
* Set by the menu, and a property rather than an attribute for that reason.
|
|
104
|
+
* Public because the menu has to reach it, and not something a consumer
|
|
105
|
+
* writes: which page is open is the menu's business.
|
|
106
|
+
*/
|
|
107
|
+
onPage: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Whether this row's submenu is the page showing.
|
|
110
|
+
*
|
|
111
|
+
* Separate from `onPage`, and it has to be. A row that opened a page is not
|
|
112
|
+
* itself on that page, so both are false for it, and rendering nothing on
|
|
113
|
+
* that basis takes its `slot[name="submenu"]` with it: the rows inside have
|
|
114
|
+
* nowhere to be assigned and are never laid out. The element that holds the
|
|
115
|
+
* page has to keep the slot even when it shows no row of its own.
|
|
116
|
+
*/
|
|
117
|
+
showingSubmenu: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* @internal Read by a parent to identify its children across the light DOM,
|
|
120
|
+
* which is cheaper than `instanceof` and is why this is public rather than
|
|
121
|
+
* protected. Not a consumer's to read or set.
|
|
122
|
+
*/
|
|
123
|
+
readonly isMenuItem = true;
|
|
124
|
+
/**
|
|
125
|
+
* Whether anything was slotted into `submenu`.
|
|
126
|
+
*
|
|
127
|
+
* Client only, and the `?? []` is what makes that true rather than merely
|
|
128
|
+
* documented. Lit's element shim carries attributes but not a tree, so
|
|
129
|
+
* `children` is undefined on the server, and this getter is read during
|
|
130
|
+
* `render`: spreading it threw, and a menu row could not be served at all.
|
|
131
|
+
* Found by writing this component's SSR spec, which is the only place it
|
|
132
|
+
* could have been found, because every other path runs in a browser.
|
|
133
|
+
*
|
|
134
|
+
* False on the server is also the right answer rather than a safe one: the
|
|
135
|
+
* server cannot know whether a submenu was slotted, and a row that served a
|
|
136
|
+
* `aria-haspopup` it might not have would be a promise made to a screen
|
|
137
|
+
* reader on the strength of a guess.
|
|
138
|
+
*/
|
|
139
|
+
get hasSubmenu(): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Whether anything is slotted into `submenu`. Read after first render.
|
|
142
|
+
*
|
|
143
|
+
* State rather than a getter over `children`, which is what it was: read
|
|
144
|
+
* during `render()` with nothing to re-read it, a submenu arriving after the
|
|
145
|
+
* row's first render produced no caret and no `aria-haspopup` while selecting
|
|
146
|
+
* the row still drilled in. A framework filling a submenu on demand hit that
|
|
147
|
+
* every time. `Tree.Item` backs `branch` the same way for the same reason.
|
|
148
|
+
*
|
|
149
|
+
* Kept in step by `readSubmenu`, which is bound in the template rather than
|
|
150
|
+
* once at first render. See the note there: bound once, a single drill-in left
|
|
151
|
+
* it false for good.
|
|
152
|
+
*
|
|
153
|
+
* False on the server is the right answer rather than a safe one: the server
|
|
154
|
+
* cannot know whether a submenu was slotted, and a row that served an
|
|
155
|
+
* `aria-haspopup` it might not have would be a promise made to a screen
|
|
156
|
+
* reader on the strength of a guess.
|
|
157
|
+
*/
|
|
158
|
+
private submenu;
|
|
159
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
160
|
+
/**
|
|
161
|
+
* Re-reads whether anything is in the submenu, from whichever slot is current.
|
|
162
|
+
*
|
|
163
|
+
* **Bound in the template, and it has to be.** It was `addEventListener` on
|
|
164
|
+
* the slot found at `firstUpdated`, which is a different element from the one
|
|
165
|
+
* showing a moment later: this row renders a `slot[name="submenu"]` in two
|
|
166
|
+
* template branches, one for the row and one for the page it opens, and moving
|
|
167
|
+
* between them makes Lit build a new slot and throw the old one away. The
|
|
168
|
+
* listener stayed on the discarded slot, and the discarded slot fired
|
|
169
|
+
* `slotchange` on its way out with nothing assigned to it any more. So one
|
|
170
|
+
* drill-in set `submenu` false and nothing was left listening to set it back:
|
|
171
|
+
* every caret in the menu vanished and every row stopped opening. Reported
|
|
172
|
+
* from a recording of the sandbox's Environment menu.
|
|
173
|
+
*
|
|
174
|
+
* The live slot is asked rather than `event.target`, so it does not matter
|
|
175
|
+
* which of the two fired or in what order.
|
|
176
|
+
*/
|
|
177
|
+
private readonly readSubmenu;
|
|
178
|
+
/**
|
|
179
|
+
* Moves focus onto the row itself.
|
|
180
|
+
*
|
|
181
|
+
* The host is not focusable: what takes focus is the row inside this shadow
|
|
182
|
+
* root, and a menu moving between rows with the arrow keys has to reach it.
|
|
183
|
+
*/
|
|
184
|
+
focus(): void;
|
|
185
|
+
/**
|
|
186
|
+
* `NavigableItem`, so the arrow keys can be the shared controller's business.
|
|
187
|
+
*
|
|
188
|
+
* `focus()` above already puts focus on the control inside the row rather than
|
|
189
|
+
* on the row, which is the whole of what moving here means.
|
|
190
|
+
*/
|
|
191
|
+
focusItem(): void;
|
|
192
|
+
/** Chooses the row, which is the same act as pressing it. */
|
|
193
|
+
select(): void;
|
|
194
|
+
/**
|
|
195
|
+
* Whether this is the row the set is on, which in a menu is where focus is.
|
|
196
|
+
*
|
|
197
|
+
* A radio group's selection is an answer it holds; a menu has no answer, only
|
|
198
|
+
* a row the reader has walked to, so focus is the whole of what "the current
|
|
199
|
+
* one" can mean here. Read from the shadow root rather than kept as state,
|
|
200
|
+
* because focus moves for reasons this element is not told about: a press, a
|
|
201
|
+
* Tab, the drilldown putting the reader on the first row of a page it has
|
|
202
|
+
* just opened.
|
|
203
|
+
*/
|
|
204
|
+
get selected(): boolean;
|
|
205
|
+
private choose;
|
|
206
|
+
private readonly handleClick;
|
|
207
|
+
/**
|
|
208
|
+
* Enter and Space choose the row, which a `<div>` would not do on its own.
|
|
209
|
+
*
|
|
210
|
+
* It is not a `<button>`: a button inside a menu is announced as a button,
|
|
211
|
+
* and the roles a menu needs are not roles a button can take.
|
|
212
|
+
*
|
|
213
|
+
* **A linked row hands Enter back to the browser.** An anchor answers Enter
|
|
214
|
+
* itself, by firing a click and following the link, so preventing the default
|
|
215
|
+
* here would mean re-issuing that navigation by hand and losing whatever the
|
|
216
|
+
* browser does around it. `Tree.Item` records the same trap from the other
|
|
217
|
+
* side: its tree prevents every key it handles, which is exactly what was
|
|
218
|
+
* swallowing Enter on its rows.
|
|
219
|
+
*
|
|
220
|
+
* Space is different, and that is the platform rather than a choice: an
|
|
221
|
+
* anchor ignores it. So the row presses itself, and the click that comes back
|
|
222
|
+
* both announces and navigates.
|
|
223
|
+
*/
|
|
224
|
+
private readonly handleKeydown;
|
|
225
|
+
protected render(): TemplateResult | typeof nothing;
|
|
226
|
+
}
|
|
227
|
+
declare global {
|
|
228
|
+
interface HTMLElementTagNameMap {
|
|
229
|
+
'helix-headless-menu-item': HelixHeadlessMenuItem;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixElement, HelixSelectEvent } from '@fusion.dev/helix-core';
|
|
8
|
+
import { caretRight, check } from '@fusion.dev/helix-icons';
|
|
9
|
+
import { html, nothing } from 'lit';
|
|
10
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
|
+
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
|
+
/**
|
|
13
|
+
* @summary One row in a menu: a command, a choice, or a way into a submenu.
|
|
14
|
+
*
|
|
15
|
+
* @description
|
|
16
|
+
* Three shapes, one element, because a reader moving down a menu with the
|
|
17
|
+
* arrow keys does not care which kind each row is and neither should the
|
|
18
|
+
* markup. What it is follows from what it was given: something slotted into
|
|
19
|
+
* `submenu` makes it a way in, `checked` makes it a choice, and neither makes
|
|
20
|
+
* it a command.
|
|
21
|
+
*
|
|
22
|
+
* **The role follows the shape.** A row that can be ticked is
|
|
23
|
+
* `menuitemcheckbox` and carries `aria-checked`, because a reader is told "one
|
|
24
|
+
* of these is on" rather than "here is a button". A row that opens a page keeps
|
|
25
|
+
* `menuitem` and says `aria-haspopup`, since the page it opens is a menu.
|
|
26
|
+
*
|
|
27
|
+
* Whether it is on the page showing is set by the menu as a property, never as
|
|
28
|
+
* an attribute: an attribute written onto a consumer's element after it
|
|
29
|
+
* upgrades is a hydration mismatch waiting to happen.
|
|
30
|
+
*
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
* @status experimental
|
|
33
|
+
* @aliases Menu option, Menu row
|
|
34
|
+
* @documentation https://helix.fusion.dev/components/menu
|
|
35
|
+
*
|
|
36
|
+
* @slot - The label.
|
|
37
|
+
* @slot start - Before the label. A mark about the row.
|
|
38
|
+
* @slot submenu - Rows this one opens, which makes it a way in.
|
|
39
|
+
*
|
|
40
|
+
* @event {HelixSelectEvent} helix-select - Fired when the row is chosen. Cancelable: refusing it takes the decision and nothing moves.
|
|
41
|
+
*
|
|
42
|
+
* @csspart item - The row, which is an `<a>` where `href` is set.
|
|
43
|
+
* @csspart mark - The trailing mark: a tick when checked, a caret when it opens.
|
|
44
|
+
* a page. One column, because a row is never both.
|
|
45
|
+
* @csspart start - The leading column, which is a slot and is always there.
|
|
46
|
+
*/
|
|
47
|
+
let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
48
|
+
constructor() {
|
|
49
|
+
super(...arguments);
|
|
50
|
+
/**
|
|
51
|
+
* What this row stands for, carried in the event when it is chosen.
|
|
52
|
+
*/
|
|
53
|
+
this.value = '';
|
|
54
|
+
/**
|
|
55
|
+
* Whether the row is one of a set of choices.
|
|
56
|
+
*
|
|
57
|
+
* Separate from `checked`, and it has to be. A row that is off is still a
|
|
58
|
+
* choice, and `checked` alone cannot say so: an unticked row and a row that
|
|
59
|
+
* was never tickable look identical from here. Announcing only the ticked one
|
|
60
|
+
* as a checkbox tells a reader its siblings are commands, which is the one
|
|
61
|
+
* thing they are not.
|
|
62
|
+
*
|
|
63
|
+
* `checked` implies it, so a row that is on never has to say both.
|
|
64
|
+
*/
|
|
65
|
+
this.checkable = false;
|
|
66
|
+
/**
|
|
67
|
+
* Whether the row is ticked.
|
|
68
|
+
*/
|
|
69
|
+
this.checked = false;
|
|
70
|
+
/**
|
|
71
|
+
* Whether the row can be chosen.
|
|
72
|
+
*/
|
|
73
|
+
this.disabled = false;
|
|
74
|
+
/**
|
|
75
|
+
* Where the row goes, which makes it a real link.
|
|
76
|
+
*
|
|
77
|
+
* **The row itself becomes an `<a>`, and the role goes with it.** A
|
|
78
|
+
* `menuitem` cannot contain a link: measured in Chromium, an anchor inside
|
|
79
|
+
* one is exposed as a child and the row loses its accessible name entirely.
|
|
80
|
+
* So the anchor *is* the row, carrying `menuitem` and the `tabindex` the menu
|
|
81
|
+
* roves, which is the same manoeuvre `Tree.Item` makes and the reason `href`
|
|
82
|
+
* changes the markup rather than only adding an attribute.
|
|
83
|
+
*
|
|
84
|
+
* **`Option` cannot have this and the reason is worth knowing**, since a
|
|
85
|
+
* command palette is the obvious place to want it. A listbox has to keep
|
|
86
|
+
* focus somewhere else, in the text field for a combobox, so it says which
|
|
87
|
+
* option is current with `aria-activedescendant`. That reference can only
|
|
88
|
+
* point at the option's host, measured: assigning
|
|
89
|
+
* `ariaActiveDescendantElement` an element inside a sibling shadow root reads
|
|
90
|
+
* back `null`. So `role="option"` is pinned to the host, and an anchor under
|
|
91
|
+
* it hits the naming failure above. A menu has no such constraint because
|
|
92
|
+
* focus really moves to the row.
|
|
93
|
+
*/
|
|
94
|
+
this.href = '';
|
|
95
|
+
/**
|
|
96
|
+
* Where the link opens, for the rows that should leave the page.
|
|
97
|
+
*/
|
|
98
|
+
this.target = '';
|
|
99
|
+
/**
|
|
100
|
+
* The relationship, which `target="_blank"` should always set.
|
|
101
|
+
*/
|
|
102
|
+
this.rel = '';
|
|
103
|
+
/**
|
|
104
|
+
* Whether the link saves the file rather than opening it.
|
|
105
|
+
*
|
|
106
|
+
* Only meaningful beside an `href`, and only for a file served from this
|
|
107
|
+
* origin: the attribute is ignored cross-origin, which is the browser's rule
|
|
108
|
+
* about who may name a file on somebody's disk rather than a gap here.
|
|
109
|
+
*/
|
|
110
|
+
this.download = false;
|
|
111
|
+
/**
|
|
112
|
+
* Whether this row is on the page currently showing.
|
|
113
|
+
*
|
|
114
|
+
* Set by the menu, and a property rather than an attribute for that reason.
|
|
115
|
+
* Public because the menu has to reach it, and not something a consumer
|
|
116
|
+
* writes: which page is open is the menu's business.
|
|
117
|
+
*/
|
|
118
|
+
this.onPage = true;
|
|
119
|
+
/**
|
|
120
|
+
* Whether this row's submenu is the page showing.
|
|
121
|
+
*
|
|
122
|
+
* Separate from `onPage`, and it has to be. A row that opened a page is not
|
|
123
|
+
* itself on that page, so both are false for it, and rendering nothing on
|
|
124
|
+
* that basis takes its `slot[name="submenu"]` with it: the rows inside have
|
|
125
|
+
* nowhere to be assigned and are never laid out. The element that holds the
|
|
126
|
+
* page has to keep the slot even when it shows no row of its own.
|
|
127
|
+
*/
|
|
128
|
+
this.showingSubmenu = false;
|
|
129
|
+
/**
|
|
130
|
+
* @internal Read by a parent to identify its children across the light DOM,
|
|
131
|
+
* which is cheaper than `instanceof` and is why this is public rather than
|
|
132
|
+
* protected. Not a consumer's to read or set.
|
|
133
|
+
*/
|
|
134
|
+
this.isMenuItem = true;
|
|
135
|
+
/**
|
|
136
|
+
* Whether anything is slotted into `submenu`. Read after first render.
|
|
137
|
+
*
|
|
138
|
+
* State rather than a getter over `children`, which is what it was: read
|
|
139
|
+
* during `render()` with nothing to re-read it, a submenu arriving after the
|
|
140
|
+
* row's first render produced no caret and no `aria-haspopup` while selecting
|
|
141
|
+
* the row still drilled in. A framework filling a submenu on demand hit that
|
|
142
|
+
* every time. `Tree.Item` backs `branch` the same way for the same reason.
|
|
143
|
+
*
|
|
144
|
+
* Kept in step by `readSubmenu`, which is bound in the template rather than
|
|
145
|
+
* once at first render. See the note there: bound once, a single drill-in left
|
|
146
|
+
* it false for good.
|
|
147
|
+
*
|
|
148
|
+
* False on the server is the right answer rather than a safe one: the server
|
|
149
|
+
* cannot know whether a submenu was slotted, and a row that served an
|
|
150
|
+
* `aria-haspopup` it might not have would be a promise made to a screen
|
|
151
|
+
* reader on the strength of a guess.
|
|
152
|
+
*/
|
|
153
|
+
this.submenu = false;
|
|
154
|
+
/**
|
|
155
|
+
* Re-reads whether anything is in the submenu, from whichever slot is current.
|
|
156
|
+
*
|
|
157
|
+
* **Bound in the template, and it has to be.** It was `addEventListener` on
|
|
158
|
+
* the slot found at `firstUpdated`, which is a different element from the one
|
|
159
|
+
* showing a moment later: this row renders a `slot[name="submenu"]` in two
|
|
160
|
+
* template branches, one for the row and one for the page it opens, and moving
|
|
161
|
+
* between them makes Lit build a new slot and throw the old one away. The
|
|
162
|
+
* listener stayed on the discarded slot, and the discarded slot fired
|
|
163
|
+
* `slotchange` on its way out with nothing assigned to it any more. So one
|
|
164
|
+
* drill-in set `submenu` false and nothing was left listening to set it back:
|
|
165
|
+
* every caret in the menu vanished and every row stopped opening. Reported
|
|
166
|
+
* from a recording of the sandbox's Environment menu.
|
|
167
|
+
*
|
|
168
|
+
* The live slot is asked rather than `event.target`, so it does not matter
|
|
169
|
+
* which of the two fired or in what order.
|
|
170
|
+
*/
|
|
171
|
+
this.readSubmenu = () => {
|
|
172
|
+
const slot = this.renderRoot.querySelector('slot[name="submenu"]');
|
|
173
|
+
this.submenu = (slot?.assignedElements().length ?? 0) > 0;
|
|
174
|
+
};
|
|
175
|
+
this.handleClick = () => {
|
|
176
|
+
this.choose();
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Enter and Space choose the row, which a `<div>` would not do on its own.
|
|
180
|
+
*
|
|
181
|
+
* It is not a `<button>`: a button inside a menu is announced as a button,
|
|
182
|
+
* and the roles a menu needs are not roles a button can take.
|
|
183
|
+
*
|
|
184
|
+
* **A linked row hands Enter back to the browser.** An anchor answers Enter
|
|
185
|
+
* itself, by firing a click and following the link, so preventing the default
|
|
186
|
+
* here would mean re-issuing that navigation by hand and losing whatever the
|
|
187
|
+
* browser does around it. `Tree.Item` records the same trap from the other
|
|
188
|
+
* side: its tree prevents every key it handles, which is exactly what was
|
|
189
|
+
* swallowing Enter on its rows.
|
|
190
|
+
*
|
|
191
|
+
* Space is different, and that is the platform rather than a choice: an
|
|
192
|
+
* anchor ignores it. So the row presses itself, and the click that comes back
|
|
193
|
+
* both announces and navigates.
|
|
194
|
+
*/
|
|
195
|
+
this.handleKeydown = (event) => {
|
|
196
|
+
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const link = this.renderRoot.querySelector('a[part="item"]');
|
|
200
|
+
if (link && event.key === 'Enter') {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
if (link) {
|
|
205
|
+
link.click();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.handleClick();
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Whether anything was slotted into `submenu`.
|
|
213
|
+
*
|
|
214
|
+
* Client only, and the `?? []` is what makes that true rather than merely
|
|
215
|
+
* documented. Lit's element shim carries attributes but not a tree, so
|
|
216
|
+
* `children` is undefined on the server, and this getter is read during
|
|
217
|
+
* `render`: spreading it threw, and a menu row could not be served at all.
|
|
218
|
+
* Found by writing this component's SSR spec, which is the only place it
|
|
219
|
+
* could have been found, because every other path runs in a browser.
|
|
220
|
+
*
|
|
221
|
+
* False on the server is also the right answer rather than a safe one: the
|
|
222
|
+
* server cannot know whether a submenu was slotted, and a row that served a
|
|
223
|
+
* `aria-haspopup` it might not have would be a promise made to a screen
|
|
224
|
+
* reader on the strength of a guess.
|
|
225
|
+
*/
|
|
226
|
+
get hasSubmenu() {
|
|
227
|
+
return this.submenu;
|
|
228
|
+
}
|
|
229
|
+
firstUpdated(changed) {
|
|
230
|
+
super.firstUpdated(changed);
|
|
231
|
+
this.readSubmenu();
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Moves focus onto the row itself.
|
|
235
|
+
*
|
|
236
|
+
* The host is not focusable: what takes focus is the row inside this shadow
|
|
237
|
+
* root, and a menu moving between rows with the arrow keys has to reach it.
|
|
238
|
+
*/
|
|
239
|
+
focus() {
|
|
240
|
+
this.renderRoot.querySelector('[part="item"]')?.focus();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* `NavigableItem`, so the arrow keys can be the shared controller's business.
|
|
244
|
+
*
|
|
245
|
+
* `focus()` above already puts focus on the control inside the row rather than
|
|
246
|
+
* on the row, which is the whole of what moving here means.
|
|
247
|
+
*/
|
|
248
|
+
focusItem() {
|
|
249
|
+
this.focus();
|
|
250
|
+
}
|
|
251
|
+
/** Chooses the row, which is the same act as pressing it. */
|
|
252
|
+
select() {
|
|
253
|
+
this.choose();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Whether this is the row the set is on, which in a menu is where focus is.
|
|
257
|
+
*
|
|
258
|
+
* A radio group's selection is an answer it holds; a menu has no answer, only
|
|
259
|
+
* a row the reader has walked to, so focus is the whole of what "the current
|
|
260
|
+
* one" can mean here. Read from the shadow root rather than kept as state,
|
|
261
|
+
* because focus moves for reasons this element is not told about: a press, a
|
|
262
|
+
* Tab, the drilldown putting the reader on the first row of a page it has
|
|
263
|
+
* just opened.
|
|
264
|
+
*/
|
|
265
|
+
get selected() {
|
|
266
|
+
/* Asked of the document, not of the shadow root. Focus lands on the control
|
|
267
|
+
inside this row, and the platform retargets it: from outside a shadow
|
|
268
|
+
tree, `activeElement` is the host, which is this element. So the document
|
|
269
|
+
already answers the question and nothing has to walk a tree to do it.
|
|
270
|
+
`ShadowRoot.activeElement` is also the reading that does not survive
|
|
271
|
+
happy-dom, which climbs `getRootNode()` to the top and then dereferences
|
|
272
|
+
what it ran out of. */
|
|
273
|
+
return this.ownerDocument.activeElement === this;
|
|
274
|
+
}
|
|
275
|
+
choose() {
|
|
276
|
+
if (this.disabled) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
|
|
280
|
+
}
|
|
281
|
+
render() {
|
|
282
|
+
// The page this row opened is showing, so what it contributes is the slot
|
|
283
|
+
// its rows are assigned to and nothing else. Its own row belongs to the
|
|
284
|
+
// page above.
|
|
285
|
+
if (this.showingSubmenu) {
|
|
286
|
+
return html `<slot name="submenu" @slotchange=${this.readSubmenu}></slot>`;
|
|
287
|
+
}
|
|
288
|
+
// Not on the page showing, so there is nothing to draw. Rendering nothing
|
|
289
|
+
// rather than hiding it with an attribute, because the attribute would be
|
|
290
|
+
// one this element wrote onto itself, and there is no state to keep: the
|
|
291
|
+
// rows are in the light DOM and come back as they were.
|
|
292
|
+
if (!this.onPage) {
|
|
293
|
+
return nothing;
|
|
294
|
+
}
|
|
295
|
+
const submenu = this.hasSubmenu;
|
|
296
|
+
/* One row, two tags. Everything about it is the same but the element, which
|
|
297
|
+
is what an `href` changes: the anchor carries the role and the tab stop
|
|
298
|
+
rather than sitting inside something that does, for the reason the
|
|
299
|
+
property's own note records. `Tree.Item` writes its two the same way.
|
|
300
|
+
|
|
301
|
+
A disabled row renders no `href`. A disabled anchor is not a thing the
|
|
302
|
+
platform has, and `aria-disabled` alone still navigates. */
|
|
303
|
+
const linked = this.href !== '' && !this.disabled;
|
|
304
|
+
const role = this.checkable || this.checked ? 'menuitemcheckbox' : 'menuitem';
|
|
305
|
+
const checkedState = this.checkable || this.checked ? (this.checked ? 'true' : 'false') : nothing;
|
|
306
|
+
const inside = html `<span part="start"><slot name="start"></slot></span>
|
|
307
|
+
<slot></slot>
|
|
308
|
+
${
|
|
309
|
+
// One trailing column, and a row is never both: a way into a page has
|
|
310
|
+
// nothing to tick, and a choice opens nothing. The tick sits after the
|
|
311
|
+
// label rather than before it, so the labels start at the edge of the
|
|
312
|
+
// row and the marks line up down the side they are read from.
|
|
313
|
+
//
|
|
314
|
+
// `data-submenu` is what tells the two apart from CSS. They share a
|
|
315
|
+
// part name because they share a column, and only one of them is
|
|
316
|
+
// directional: a caret has to mirror in a right-to-left document and a
|
|
317
|
+
// tick must not, which Unicode's own mirroring list agrees on. The
|
|
318
|
+
// attribute is on an element in this shadow root rather than on the
|
|
319
|
+
// host, so it is the divider's `data-empty` and not a hydration
|
|
320
|
+
// hazard. `hasSubmenu` is derived from slot assignment and has no
|
|
321
|
+
// attribute a selector could read instead.
|
|
322
|
+
submenu
|
|
323
|
+
? html `<svg
|
|
324
|
+
part="mark"
|
|
325
|
+
data-submenu
|
|
326
|
+
fill="currentColor"
|
|
327
|
+
viewBox=${caretRight.viewBox}
|
|
328
|
+
aria-hidden="true"
|
|
329
|
+
>
|
|
330
|
+
${unsafeSVG(caretRight.content)}
|
|
331
|
+
</svg>`
|
|
332
|
+
: this.checked
|
|
333
|
+
? html `<svg
|
|
334
|
+
part="mark"
|
|
335
|
+
fill="currentColor"
|
|
336
|
+
viewBox=${check.viewBox}
|
|
337
|
+
aria-hidden="true"
|
|
338
|
+
>
|
|
339
|
+
${unsafeSVG(check.content)}
|
|
340
|
+
</svg>`
|
|
341
|
+
: html `<span part="mark" aria-hidden="true"></span>`}`;
|
|
342
|
+
return html `${linked
|
|
343
|
+
? html `<a
|
|
344
|
+
part="item"
|
|
345
|
+
href=${this.href}
|
|
346
|
+
target=${this.target || nothing}
|
|
347
|
+
rel=${this.rel || nothing}
|
|
348
|
+
?download=${this.download}
|
|
349
|
+
role=${role}
|
|
350
|
+
aria-checked=${checkedState}
|
|
351
|
+
aria-haspopup=${submenu ? 'menu' : nothing}
|
|
352
|
+
tabindex="-1"
|
|
353
|
+
@click=${this.handleClick}
|
|
354
|
+
@keydown=${this.handleKeydown}
|
|
355
|
+
>${inside}</a>`
|
|
356
|
+
: html `<div
|
|
357
|
+
part="item"
|
|
358
|
+
role=${role}
|
|
359
|
+
aria-checked=${checkedState}
|
|
360
|
+
aria-disabled=${this.disabled ? 'true' : nothing}
|
|
361
|
+
aria-haspopup=${submenu ? 'menu' : nothing}
|
|
362
|
+
tabindex=${this.disabled ? nothing : '-1'}
|
|
363
|
+
@click=${this.handleClick}
|
|
364
|
+
@keydown=${this.handleKeydown}
|
|
365
|
+
>${inside}</div>`}
|
|
366
|
+
<slot name="submenu" @slotchange=${this.readSubmenu}></slot>`;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
__decorate([
|
|
370
|
+
property()
|
|
371
|
+
], HelixHeadlessMenuItem.prototype, "value", void 0);
|
|
372
|
+
__decorate([
|
|
373
|
+
property({ reflect: true, type: Boolean })
|
|
374
|
+
], HelixHeadlessMenuItem.prototype, "checkable", void 0);
|
|
375
|
+
__decorate([
|
|
376
|
+
property({ reflect: true, type: Boolean })
|
|
377
|
+
], HelixHeadlessMenuItem.prototype, "checked", void 0);
|
|
378
|
+
__decorate([
|
|
379
|
+
property({ reflect: true, type: Boolean })
|
|
380
|
+
], HelixHeadlessMenuItem.prototype, "disabled", void 0);
|
|
381
|
+
__decorate([
|
|
382
|
+
property()
|
|
383
|
+
], HelixHeadlessMenuItem.prototype, "href", void 0);
|
|
384
|
+
__decorate([
|
|
385
|
+
property()
|
|
386
|
+
], HelixHeadlessMenuItem.prototype, "target", void 0);
|
|
387
|
+
__decorate([
|
|
388
|
+
property()
|
|
389
|
+
], HelixHeadlessMenuItem.prototype, "rel", void 0);
|
|
390
|
+
__decorate([
|
|
391
|
+
property({ type: Boolean })
|
|
392
|
+
], HelixHeadlessMenuItem.prototype, "download", void 0);
|
|
393
|
+
__decorate([
|
|
394
|
+
property({ attribute: false })
|
|
395
|
+
], HelixHeadlessMenuItem.prototype, "onPage", void 0);
|
|
396
|
+
__decorate([
|
|
397
|
+
property({ attribute: false })
|
|
398
|
+
], HelixHeadlessMenuItem.prototype, "showingSubmenu", void 0);
|
|
399
|
+
__decorate([
|
|
400
|
+
state()
|
|
401
|
+
], HelixHeadlessMenuItem.prototype, "submenu", void 0);
|
|
402
|
+
HelixHeadlessMenuItem = __decorate([
|
|
403
|
+
customElement('helix-headless-menu-item')
|
|
404
|
+
], HelixHeadlessMenuItem);
|
|
405
|
+
export { HelixHeadlessMenuItem };
|