@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,251 @@
|
|
|
1
|
+
import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* @summary Triggers an action, submits a form, or navigates.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Renders a real `<button>`, so activation, focus, and the browser's own
|
|
8
|
+
* affordances come from the platform rather than from key handlers.
|
|
9
|
+
*
|
|
10
|
+
* **It never renders an anchor.** A router has to render its own `<a>` to
|
|
11
|
+
* intercept a click and cannot render one inside this shadow root, so a button
|
|
12
|
+
* that could also be a link forced every such navigation to be a full page
|
|
13
|
+
* load. A link that looks like a button is styling rather than behavior, so it
|
|
14
|
+
* is a recipe in the base layer: `<Link data-button="solid">` is the same
|
|
15
|
+
* control, and the consumer keeps their anchor.
|
|
16
|
+
*
|
|
17
|
+
* `delegatesFocus` puts focus on the inner control, and it serializes into the
|
|
18
|
+
* server's markup, so the button is keyboard reachable before any JavaScript
|
|
19
|
+
* loads.
|
|
20
|
+
*
|
|
21
|
+
* Form submission is explicit. A `<button type="submit">` inside a shadow root
|
|
22
|
+
* submits nothing, because the form is on the other side of the boundary, so
|
|
23
|
+
* the inner control is always `type="button"` and this element calls
|
|
24
|
+
* `requestSubmit()` on its form instead. That routes through validation, unlike
|
|
25
|
+
* `submit()`.
|
|
26
|
+
*
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
* @status stable
|
|
29
|
+
* @category actions
|
|
30
|
+
* @documentation https://helix.fusion.dev/components/button
|
|
31
|
+
*
|
|
32
|
+
* @slot - The button's label.
|
|
33
|
+
* @slot end - Content after the label.
|
|
34
|
+
* @slot start - Content before the label, usually an icon.
|
|
35
|
+
*
|
|
36
|
+
* @csspart button - The inner control.
|
|
37
|
+
*/
|
|
38
|
+
export declare class HelixHeadlessButton extends HelixFormElement {
|
|
39
|
+
protected readonly aria: AriaForwardController;
|
|
40
|
+
/**
|
|
41
|
+
* What activating the button does to its form.
|
|
42
|
+
*/
|
|
43
|
+
type: 'button' | 'submit' | 'reset';
|
|
44
|
+
/**
|
|
45
|
+
* What this button submits under its own `name`, the way a native submitter does.
|
|
46
|
+
*
|
|
47
|
+
* **Reported for exactly the submission this button starts and not otherwise.**
|
|
48
|
+
* A native submit button contributes `name=value` only when it is the one
|
|
49
|
+
* that submitted, and every other button on the form contributes nothing. A
|
|
50
|
+
* form-associated element reporting a value continuously would contribute on
|
|
51
|
+
* every submission whoever started it, which is a different feature wearing
|
|
52
|
+
* the same name, so `submit` below sets it, submits, and clears it inside one
|
|
53
|
+
* synchronous call.
|
|
54
|
+
*
|
|
55
|
+
* `CopyButton` has a `value` of its own meaning the text to put on the
|
|
56
|
+
* clipboard, which is not this and is why it is declared there rather than
|
|
57
|
+
* inherited.
|
|
58
|
+
*/
|
|
59
|
+
value: string;
|
|
60
|
+
/**
|
|
61
|
+
* Where this button's submission goes, overriding the form's own.
|
|
62
|
+
*
|
|
63
|
+
* **The five `form*` overrides work by writing the form and putting it back**,
|
|
64
|
+
* which is the only mechanism available and is closer to the platform than it
|
|
65
|
+
* sounds. `requestSubmit(submitter)` is how a native submitter carries these,
|
|
66
|
+
* and it refuses ours twice over: the inner `<button>` is `type="button"` so
|
|
67
|
+
* it is not a submit button, and it sits in this shadow root so its form owner
|
|
68
|
+
* is not the consumer's form. Both throw. So the submission is started with no
|
|
69
|
+
* submitter and the overrides are applied to the form for the duration of it.
|
|
70
|
+
*
|
|
71
|
+
* The whole submit algorithm runs synchronously inside `requestSubmit()`, so
|
|
72
|
+
* the window is one call and a `finally` closes it whether or not a listener
|
|
73
|
+
* refused. What a `submit` handler observes during that window is the form a
|
|
74
|
+
* native submitter would have given it, which is the point.
|
|
75
|
+
*/
|
|
76
|
+
formAction: string;
|
|
77
|
+
/**
|
|
78
|
+
* How the submission is encoded, overriding the form's own.
|
|
79
|
+
*/
|
|
80
|
+
formEnctype: string;
|
|
81
|
+
/**
|
|
82
|
+
* Which HTTP method the submission uses, overriding the form's own.
|
|
83
|
+
*/
|
|
84
|
+
formMethod: string;
|
|
85
|
+
/**
|
|
86
|
+
* Whether this submission skips the form's validation.
|
|
87
|
+
*/
|
|
88
|
+
formNoValidate: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Which browsing context the response lands in, overriding the form's own.
|
|
91
|
+
*/
|
|
92
|
+
formTarget: string;
|
|
93
|
+
/**
|
|
94
|
+
* Accessible name, for a button whose content is not text.
|
|
95
|
+
*
|
|
96
|
+
* Lives here rather than only on `IconButton` so the two share one render.
|
|
97
|
+
* An icon-only button without it is a control a screen reader announces as
|
|
98
|
+
* "button" and nothing else.
|
|
99
|
+
*/
|
|
100
|
+
label: string;
|
|
101
|
+
/**
|
|
102
|
+
* What to send when the button is activated, and what to send it to.
|
|
103
|
+
*
|
|
104
|
+
* The platform's invoker attributes, forwarded onto the inner `<button>`
|
|
105
|
+
* rather than left on the host: only a real button is recognized as an
|
|
106
|
+
* invoker, so `commandfor` written on a custom element resolves nothing and
|
|
107
|
+
* the trigger silently does nothing at all. A wrapper has to hand them down,
|
|
108
|
+
* the same way `label` becomes `aria-label` in there.
|
|
109
|
+
*
|
|
110
|
+
* ```html
|
|
111
|
+
* <helix-button commandfor="confirm" command="--show">Delete…</helix-button>
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* `commandFor` spells its attribute out, because Lit lowercases a property
|
|
115
|
+
* name to derive one rather than dash-casing it, and what a consumer writes
|
|
116
|
+
* is the platform's `commandfor`.
|
|
117
|
+
*
|
|
118
|
+
* @reference https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-command Invoker Commands
|
|
119
|
+
*/
|
|
120
|
+
command: string;
|
|
121
|
+
/**
|
|
122
|
+
* The id of the element the command is sent to.
|
|
123
|
+
*
|
|
124
|
+
* @reference https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-commandfor Invoker Commands
|
|
125
|
+
*/
|
|
126
|
+
commandFor: string;
|
|
127
|
+
/** Moves focus to the inner control, so the host is never the focus target. */
|
|
128
|
+
static shadowRootOptions: ShadowRootInit;
|
|
129
|
+
private readonly handleClick;
|
|
130
|
+
/**
|
|
131
|
+
* Whether the button holds its group's tab stop.
|
|
132
|
+
*
|
|
133
|
+
* A toolbar keeps one tab stop across its children, and the obvious way to do
|
|
134
|
+
* that is to write `tabindex` onto each of them. That is a change to markup
|
|
135
|
+
* the consumer rendered, so a React consumer's hydration reports it and does
|
|
136
|
+
* not patch it up. A parent sets this property instead, which writes nothing,
|
|
137
|
+
* and the stop lands on the inner control inside the shadow root.
|
|
138
|
+
*
|
|
139
|
+
* No attribute, because it is coordination between elements rather than
|
|
140
|
+
* anything a consumer sets.
|
|
141
|
+
*/
|
|
142
|
+
focusable: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Whether this button is a disclosure trigger, and whether its panel is open.
|
|
145
|
+
*
|
|
146
|
+
* **Set by whatever owns the panel, and a property rather than an attribute
|
|
147
|
+
* for the reason `Menu` already states about its items**: an attribute
|
|
148
|
+
* written onto a consumer's element after it upgrades is a hydration mismatch
|
|
149
|
+
* waiting to happen. Measured 28 Aug 2026 against React 19.2.8, server markup
|
|
150
|
+
* hydrated with the definitions loaded either side of `hydrateRoot`:
|
|
151
|
+
* `Megamenu` and `SpeedDial` wrote `aria-expanded` onto the slotted trigger
|
|
152
|
+
* and React reported "some attributes of the server rendered HTML didn't
|
|
153
|
+
* match the client properties. This won't be patched up" in both orders;
|
|
154
|
+
* `Popover` and `Menu`, which write nothing, were clean in both.
|
|
155
|
+
*
|
|
156
|
+
* **A property crosses that gap because React never looks at one.** It diffs
|
|
157
|
+
* the attributes it rendered, and this one is `attribute: false`, so the state
|
|
158
|
+
* travels to the inner `<button>` and lands inside a shadow root React does
|
|
159
|
+
* not own and cannot compare. The consumer's own element is left exactly as
|
|
160
|
+
* they served it, which is what `Popover`'s note asks for, and the reader is
|
|
161
|
+
* still told what the control does, which is what `SpeedDial`'s note asks
|
|
162
|
+
* for. See #322.
|
|
163
|
+
*
|
|
164
|
+
* `undefined` renders nothing at all, so a button that is not a trigger
|
|
165
|
+
* carries no `aria-expanded`: the resting state is the attribute's absence,
|
|
166
|
+
* which is the rule every reflected property here follows.
|
|
167
|
+
*
|
|
168
|
+
* A consumer who wants to say this statically writes `aria-expanded` on the
|
|
169
|
+
* host instead, which `AriaForwardController` carries to the same place.
|
|
170
|
+
*/
|
|
171
|
+
expanded?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Submits the form as this button, for the length of one call.
|
|
174
|
+
*
|
|
175
|
+
* Everything a native submitter carries is written onto the form, the
|
|
176
|
+
* submission is started, and everything is put back. See `formAction` for why
|
|
177
|
+
* `requestSubmit(submitter)` is not available to a control inside a shadow
|
|
178
|
+
* root.
|
|
179
|
+
*
|
|
180
|
+
* `novalidate` is in the same list rather than reached by `form.submit()`,
|
|
181
|
+
* which looks like the shortcut and is not: `submit()` skips the `submit`
|
|
182
|
+
* event as well as the validation, so a consumer's handler would stop running
|
|
183
|
+
* the moment somebody wrote `formnovalidate`. Turning the flag on and calling
|
|
184
|
+
* `requestSubmit()` skips the validation alone, which is what the attribute
|
|
185
|
+
* means.
|
|
186
|
+
*/
|
|
187
|
+
private submit;
|
|
188
|
+
/**
|
|
189
|
+
* The element this button's command is sent to, resolved.
|
|
190
|
+
*
|
|
191
|
+
* The platform's own accessor, and this element had the resolution and kept
|
|
192
|
+
* it to itself: `linkCommand` writes the target onto the inner `<button>`,
|
|
193
|
+
* where a consumer cannot reach it, and `commandFor` answers with an id
|
|
194
|
+
* rather than an element. Writing assigns the inner control directly, which
|
|
195
|
+
* is what an invoker without an id in the same tree needs.
|
|
196
|
+
*/
|
|
197
|
+
get commandForElement(): Element | null;
|
|
198
|
+
set commandForElement(target: Element | null);
|
|
199
|
+
/**
|
|
200
|
+
* Points the inner button at the command's target, by reference.
|
|
201
|
+
*
|
|
202
|
+
* **The attribute cannot do this and it fails silently.** `commandfor` is an
|
|
203
|
+
* IDREF, and an IDREF resolves inside the tree it is written in: forwarded
|
|
204
|
+
* onto a button in this shadow root it looks for the id *here*, finds
|
|
205
|
+
* nothing, and the trigger does nothing at all. Measured before this existed:
|
|
206
|
+
* the button clicked, no `command` event was dispatched, and there was
|
|
207
|
+
* nothing anywhere to say why.
|
|
208
|
+
*
|
|
209
|
+
* `commandForElement` takes an element instead, and an element reference
|
|
210
|
+
* resolves outward: a node in a shadow root may point at one in the document.
|
|
211
|
+
* The same asymmetry the listbox relies on for `aria-activedescendant`, and
|
|
212
|
+
* the second place in this library where the id form is the one that cannot
|
|
213
|
+
* work.
|
|
214
|
+
*
|
|
215
|
+
* The id is looked up in the host's own root rather than the document, so a
|
|
216
|
+
* button inside somebody else's shadow root finds a target in that same root
|
|
217
|
+
* and not a coincidence in the page.
|
|
218
|
+
*/
|
|
219
|
+
private linkCommand;
|
|
220
|
+
/**
|
|
221
|
+
* The element this button commands, or nothing.
|
|
222
|
+
*
|
|
223
|
+
* Looked up in the button's own root rather than in the document, so a button
|
|
224
|
+
* inside somebody else's shadow root finds a target in that same root and not
|
|
225
|
+
* a coincidence in the page.
|
|
226
|
+
*/
|
|
227
|
+
private commandTarget;
|
|
228
|
+
/**
|
|
229
|
+
* Sends the `command` event where the browser will not.
|
|
230
|
+
*
|
|
231
|
+
* Invoker commands are recent, and where they are missing `linkCommand` set a
|
|
232
|
+
* property that does not exist and returned, so the button did nothing at all
|
|
233
|
+
* when clicked. Silently: the markup was right, the component was right, and
|
|
234
|
+
* every `command` in the library was inert at once with no error to find. A
|
|
235
|
+
* design system cannot ship an example that depends on a feature failing that
|
|
236
|
+
* quietly.
|
|
237
|
+
*
|
|
238
|
+
* Only where the platform is absent, so a browser that has invokers sends
|
|
239
|
+
* exactly one event and this sends none. Doing both would open a dialog and
|
|
240
|
+
* immediately toggle it shut again, which is a worse bug than the one this
|
|
241
|
+
* fixes and would only appear on the browsers that work.
|
|
242
|
+
*/
|
|
243
|
+
private sendCommand;
|
|
244
|
+
protected updated(changed: PropertyValues): void;
|
|
245
|
+
protected render(): TemplateResult;
|
|
246
|
+
}
|
|
247
|
+
declare global {
|
|
248
|
+
interface HTMLElementTagNameMap {
|
|
249
|
+
'helix-headless-button': HelixHeadlessButton;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
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 { AriaForwardController, customElement, HelixFallbackCommandEvent, HelixFormElement, supportsInvokers, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
/**
|
|
11
|
+
* @summary Triggers an action, submits a form, or navigates.
|
|
12
|
+
*
|
|
13
|
+
* @description
|
|
14
|
+
* Renders a real `<button>`, so activation, focus, and the browser's own
|
|
15
|
+
* affordances come from the platform rather than from key handlers.
|
|
16
|
+
*
|
|
17
|
+
* **It never renders an anchor.** A router has to render its own `<a>` to
|
|
18
|
+
* intercept a click and cannot render one inside this shadow root, so a button
|
|
19
|
+
* that could also be a link forced every such navigation to be a full page
|
|
20
|
+
* load. A link that looks like a button is styling rather than behavior, so it
|
|
21
|
+
* is a recipe in the base layer: `<Link data-button="solid">` is the same
|
|
22
|
+
* control, and the consumer keeps their anchor.
|
|
23
|
+
*
|
|
24
|
+
* `delegatesFocus` puts focus on the inner control, and it serializes into the
|
|
25
|
+
* server's markup, so the button is keyboard reachable before any JavaScript
|
|
26
|
+
* loads.
|
|
27
|
+
*
|
|
28
|
+
* Form submission is explicit. A `<button type="submit">` inside a shadow root
|
|
29
|
+
* submits nothing, because the form is on the other side of the boundary, so
|
|
30
|
+
* the inner control is always `type="button"` and this element calls
|
|
31
|
+
* `requestSubmit()` on its form instead. That routes through validation, unlike
|
|
32
|
+
* `submit()`.
|
|
33
|
+
*
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
* @status stable
|
|
36
|
+
* @category actions
|
|
37
|
+
* @documentation https://helix.fusion.dev/components/button
|
|
38
|
+
*
|
|
39
|
+
* @slot - The button's label.
|
|
40
|
+
* @slot end - Content after the label.
|
|
41
|
+
* @slot start - Content before the label, usually an icon.
|
|
42
|
+
*
|
|
43
|
+
* @csspart button - The inner control.
|
|
44
|
+
*/
|
|
45
|
+
let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
|
|
46
|
+
constructor() {
|
|
47
|
+
super(...arguments);
|
|
48
|
+
/* A consumer's `aria-describedby` names ids in their tree and this control is
|
|
49
|
+
in a shadow root, so the reference has to be carried down as an element
|
|
50
|
+
rather than copied as an id. See the controller: measured, the host held
|
|
51
|
+
the description and the accessibility tree exposed the inner control
|
|
52
|
+
without it. */
|
|
53
|
+
this.aria = new AriaForwardController(this, {
|
|
54
|
+
target: () => this.renderRoot?.querySelector('[part="button"]') ?? null,
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* What activating the button does to its form.
|
|
58
|
+
*/
|
|
59
|
+
this.type = 'button';
|
|
60
|
+
/**
|
|
61
|
+
* What this button submits under its own `name`, the way a native submitter does.
|
|
62
|
+
*
|
|
63
|
+
* **Reported for exactly the submission this button starts and not otherwise.**
|
|
64
|
+
* A native submit button contributes `name=value` only when it is the one
|
|
65
|
+
* that submitted, and every other button on the form contributes nothing. A
|
|
66
|
+
* form-associated element reporting a value continuously would contribute on
|
|
67
|
+
* every submission whoever started it, which is a different feature wearing
|
|
68
|
+
* the same name, so `submit` below sets it, submits, and clears it inside one
|
|
69
|
+
* synchronous call.
|
|
70
|
+
*
|
|
71
|
+
* `CopyButton` has a `value` of its own meaning the text to put on the
|
|
72
|
+
* clipboard, which is not this and is why it is declared there rather than
|
|
73
|
+
* inherited.
|
|
74
|
+
*/
|
|
75
|
+
this.value = '';
|
|
76
|
+
/**
|
|
77
|
+
* Where this button's submission goes, overriding the form's own.
|
|
78
|
+
*
|
|
79
|
+
* **The five `form*` overrides work by writing the form and putting it back**,
|
|
80
|
+
* which is the only mechanism available and is closer to the platform than it
|
|
81
|
+
* sounds. `requestSubmit(submitter)` is how a native submitter carries these,
|
|
82
|
+
* and it refuses ours twice over: the inner `<button>` is `type="button"` so
|
|
83
|
+
* it is not a submit button, and it sits in this shadow root so its form owner
|
|
84
|
+
* is not the consumer's form. Both throw. So the submission is started with no
|
|
85
|
+
* submitter and the overrides are applied to the form for the duration of it.
|
|
86
|
+
*
|
|
87
|
+
* The whole submit algorithm runs synchronously inside `requestSubmit()`, so
|
|
88
|
+
* the window is one call and a `finally` closes it whether or not a listener
|
|
89
|
+
* refused. What a `submit` handler observes during that window is the form a
|
|
90
|
+
* native submitter would have given it, which is the point.
|
|
91
|
+
*/
|
|
92
|
+
this.formAction = '';
|
|
93
|
+
/**
|
|
94
|
+
* How the submission is encoded, overriding the form's own.
|
|
95
|
+
*/
|
|
96
|
+
this.formEnctype = '';
|
|
97
|
+
/**
|
|
98
|
+
* Which HTTP method the submission uses, overriding the form's own.
|
|
99
|
+
*/
|
|
100
|
+
this.formMethod = '';
|
|
101
|
+
/**
|
|
102
|
+
* Whether this submission skips the form's validation.
|
|
103
|
+
*/
|
|
104
|
+
this.formNoValidate = false;
|
|
105
|
+
/**
|
|
106
|
+
* Which browsing context the response lands in, overriding the form's own.
|
|
107
|
+
*/
|
|
108
|
+
this.formTarget = '';
|
|
109
|
+
/**
|
|
110
|
+
* Accessible name, for a button whose content is not text.
|
|
111
|
+
*
|
|
112
|
+
* Lives here rather than only on `IconButton` so the two share one render.
|
|
113
|
+
* An icon-only button without it is a control a screen reader announces as
|
|
114
|
+
* "button" and nothing else.
|
|
115
|
+
*/
|
|
116
|
+
this.label = '';
|
|
117
|
+
/**
|
|
118
|
+
* What to send when the button is activated, and what to send it to.
|
|
119
|
+
*
|
|
120
|
+
* The platform's invoker attributes, forwarded onto the inner `<button>`
|
|
121
|
+
* rather than left on the host: only a real button is recognized as an
|
|
122
|
+
* invoker, so `commandfor` written on a custom element resolves nothing and
|
|
123
|
+
* the trigger silently does nothing at all. A wrapper has to hand them down,
|
|
124
|
+
* the same way `label` becomes `aria-label` in there.
|
|
125
|
+
*
|
|
126
|
+
* ```html
|
|
127
|
+
* <helix-button commandfor="confirm" command="--show">Delete…</helix-button>
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* `commandFor` spells its attribute out, because Lit lowercases a property
|
|
131
|
+
* name to derive one rather than dash-casing it, and what a consumer writes
|
|
132
|
+
* is the platform's `commandfor`.
|
|
133
|
+
*
|
|
134
|
+
* @reference https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-command Invoker Commands
|
|
135
|
+
*/
|
|
136
|
+
this.command = '';
|
|
137
|
+
/**
|
|
138
|
+
* The id of the element the command is sent to.
|
|
139
|
+
*
|
|
140
|
+
* @reference https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-commandfor Invoker Commands
|
|
141
|
+
*/
|
|
142
|
+
this.commandFor = '';
|
|
143
|
+
this.handleClick = () => {
|
|
144
|
+
/* **Re-linked here, and this is the line that makes a trigger reliable.**
|
|
145
|
+
*
|
|
146
|
+
* `linkCommand` used to run from `updated` alone, so the target was
|
|
147
|
+
* resolved once, whenever the button last rendered, and never again. A
|
|
148
|
+
* button whose properties do not change does not update again, so one whose
|
|
149
|
+
* target had not been inserted yet stayed wired to nothing for good.
|
|
150
|
+
* Measured: `commandForElement` read null with the target absent, null
|
|
151
|
+
* again after the target was inserted, and resolved the instant an
|
|
152
|
+
* unrelated property forced an update. Whether a trigger worked came down
|
|
153
|
+
* to insertion order, which is why it survived a full reload and not a
|
|
154
|
+
* client navigation, where the same markup arrives in pieces.
|
|
155
|
+
*
|
|
156
|
+
* **The fallback path was the correct one all along**, which is the tell:
|
|
157
|
+
* `sendCommand` resolves through `commandTarget` at activation, so a
|
|
158
|
+
* browser *without* invokers ran this correctly and a browser with them did
|
|
159
|
+
* not.
|
|
160
|
+
*
|
|
161
|
+
* A click listener is early enough, and that is measured rather than read
|
|
162
|
+
* off the specification: activation behavior runs after the click has
|
|
163
|
+
* finished dispatching, so a handler that assigns `commandForElement` sets
|
|
164
|
+
* it before the browser looks. Probed on a bare `<button command>` whose
|
|
165
|
+
* target was assigned from its own click listener, the dialog opened, and
|
|
166
|
+
* the control that assigned nothing did not.
|
|
167
|
+
*
|
|
168
|
+
* `updated` keeps its call, because the common case is a target that was
|
|
169
|
+
* there all along and `commandForElement` should be readable before anybody
|
|
170
|
+
* clicks anything. This is the guarantee, not the mechanism. */
|
|
171
|
+
this.linkCommand();
|
|
172
|
+
// Before the type check, which returns for `button`, the default and the
|
|
173
|
+
// type every invoker in this library uses.
|
|
174
|
+
this.sendCommand();
|
|
175
|
+
if (this.type === 'button') {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const { form } = this;
|
|
179
|
+
if (!form) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (this.type === 'submit') {
|
|
183
|
+
this.submit(form);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
form.reset();
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Whether the button holds its group's tab stop.
|
|
191
|
+
*
|
|
192
|
+
* A toolbar keeps one tab stop across its children, and the obvious way to do
|
|
193
|
+
* that is to write `tabindex` onto each of them. That is a change to markup
|
|
194
|
+
* the consumer rendered, so a React consumer's hydration reports it and does
|
|
195
|
+
* not patch it up. A parent sets this property instead, which writes nothing,
|
|
196
|
+
* and the stop lands on the inner control inside the shadow root.
|
|
197
|
+
*
|
|
198
|
+
* No attribute, because it is coordination between elements rather than
|
|
199
|
+
* anything a consumer sets.
|
|
200
|
+
*/
|
|
201
|
+
this.focusable = true;
|
|
202
|
+
}
|
|
203
|
+
/** Moves focus to the inner control, so the host is never the focus target. */
|
|
204
|
+
static { this.shadowRootOptions = {
|
|
205
|
+
...HelixFormElement.shadowRootOptions,
|
|
206
|
+
delegatesFocus: true,
|
|
207
|
+
}; }
|
|
208
|
+
/**
|
|
209
|
+
* Submits the form as this button, for the length of one call.
|
|
210
|
+
*
|
|
211
|
+
* Everything a native submitter carries is written onto the form, the
|
|
212
|
+
* submission is started, and everything is put back. See `formAction` for why
|
|
213
|
+
* `requestSubmit(submitter)` is not available to a control inside a shadow
|
|
214
|
+
* root.
|
|
215
|
+
*
|
|
216
|
+
* `novalidate` is in the same list rather than reached by `form.submit()`,
|
|
217
|
+
* which looks like the shortcut and is not: `submit()` skips the `submit`
|
|
218
|
+
* event as well as the validation, so a consumer's handler would stop running
|
|
219
|
+
* the moment somebody wrote `formnovalidate`. Turning the flag on and calling
|
|
220
|
+
* `requestSubmit()` skips the validation alone, which is what the attribute
|
|
221
|
+
* means.
|
|
222
|
+
*/
|
|
223
|
+
submit(form) {
|
|
224
|
+
const undo = [];
|
|
225
|
+
const override = (attribute, value) => {
|
|
226
|
+
if (value === null) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const had = form.getAttribute(attribute);
|
|
230
|
+
form.setAttribute(attribute, value);
|
|
231
|
+
undo.push(() => {
|
|
232
|
+
if (had === null) {
|
|
233
|
+
form.removeAttribute(attribute);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
form.setAttribute(attribute, had);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
override('action', this.formAction || null);
|
|
241
|
+
override('enctype', this.formEnctype || null);
|
|
242
|
+
override('method', this.formMethod || null);
|
|
243
|
+
override('novalidate', this.formNoValidate ? '' : null);
|
|
244
|
+
override('target', this.formTarget || null);
|
|
245
|
+
const reports = this.name !== '';
|
|
246
|
+
if (reports) {
|
|
247
|
+
this.internals.setFormValue(this.value);
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
form.requestSubmit();
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
for (const put of undo) {
|
|
254
|
+
put();
|
|
255
|
+
}
|
|
256
|
+
if (reports) {
|
|
257
|
+
this.internals.setFormValue(null);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The element this button's command is sent to, resolved.
|
|
263
|
+
*
|
|
264
|
+
* The platform's own accessor, and this element had the resolution and kept
|
|
265
|
+
* it to itself: `linkCommand` writes the target onto the inner `<button>`,
|
|
266
|
+
* where a consumer cannot reach it, and `commandFor` answers with an id
|
|
267
|
+
* rather than an element. Writing assigns the inner control directly, which
|
|
268
|
+
* is what an invoker without an id in the same tree needs.
|
|
269
|
+
*/
|
|
270
|
+
get commandForElement() {
|
|
271
|
+
const button = this.renderRoot?.querySelector('button');
|
|
272
|
+
return button?.commandForElement ?? this.commandTarget();
|
|
273
|
+
}
|
|
274
|
+
set commandForElement(target) {
|
|
275
|
+
const button = this.renderRoot?.querySelector('button');
|
|
276
|
+
if (button) {
|
|
277
|
+
button.commandForElement = target;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Points the inner button at the command's target, by reference.
|
|
282
|
+
*
|
|
283
|
+
* **The attribute cannot do this and it fails silently.** `commandfor` is an
|
|
284
|
+
* IDREF, and an IDREF resolves inside the tree it is written in: forwarded
|
|
285
|
+
* onto a button in this shadow root it looks for the id *here*, finds
|
|
286
|
+
* nothing, and the trigger does nothing at all. Measured before this existed:
|
|
287
|
+
* the button clicked, no `command` event was dispatched, and there was
|
|
288
|
+
* nothing anywhere to say why.
|
|
289
|
+
*
|
|
290
|
+
* `commandForElement` takes an element instead, and an element reference
|
|
291
|
+
* resolves outward: a node in a shadow root may point at one in the document.
|
|
292
|
+
* The same asymmetry the listbox relies on for `aria-activedescendant`, and
|
|
293
|
+
* the second place in this library where the id form is the one that cannot
|
|
294
|
+
* work.
|
|
295
|
+
*
|
|
296
|
+
* The id is looked up in the host's own root rather than the document, so a
|
|
297
|
+
* button inside somebody else's shadow root finds a target in that same root
|
|
298
|
+
* and not a coincidence in the page.
|
|
299
|
+
*/
|
|
300
|
+
linkCommand() {
|
|
301
|
+
const button = this.renderRoot?.querySelector('button');
|
|
302
|
+
if (!button || !supportsInvokers()) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
;
|
|
306
|
+
button.commandForElement =
|
|
307
|
+
this.commandTarget();
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* The element this button commands, or nothing.
|
|
311
|
+
*
|
|
312
|
+
* Looked up in the button's own root rather than in the document, so a button
|
|
313
|
+
* inside somebody else's shadow root finds a target in that same root and not
|
|
314
|
+
* a coincidence in the page.
|
|
315
|
+
*/
|
|
316
|
+
commandTarget() {
|
|
317
|
+
const root = this.getRootNode();
|
|
318
|
+
return this.commandFor ? (root.getElementById?.(this.commandFor) ?? null) : null;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Sends the `command` event where the browser will not.
|
|
322
|
+
*
|
|
323
|
+
* Invoker commands are recent, and where they are missing `linkCommand` set a
|
|
324
|
+
* property that does not exist and returned, so the button did nothing at all
|
|
325
|
+
* when clicked. Silently: the markup was right, the component was right, and
|
|
326
|
+
* every `command` in the library was inert at once with no error to find. A
|
|
327
|
+
* design system cannot ship an example that depends on a feature failing that
|
|
328
|
+
* quietly.
|
|
329
|
+
*
|
|
330
|
+
* Only where the platform is absent, so a browser that has invokers sends
|
|
331
|
+
* exactly one event and this sends none. Doing both would open a dialog and
|
|
332
|
+
* immediately toggle it shut again, which is a worse bug than the one this
|
|
333
|
+
* fixes and would only appear on the browsers that work.
|
|
334
|
+
*/
|
|
335
|
+
sendCommand() {
|
|
336
|
+
if (!this.command || supportsInvokers()) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
this.commandTarget()?.dispatchEvent(new HelixFallbackCommandEvent(this.command, this));
|
|
340
|
+
}
|
|
341
|
+
updated(changed) {
|
|
342
|
+
super.updated(changed);
|
|
343
|
+
this.linkCommand();
|
|
344
|
+
}
|
|
345
|
+
render() {
|
|
346
|
+
const label = html `<slot name="start"></slot><slot></slot><slot name="end"></slot>`;
|
|
347
|
+
const stop = this.focusable ? nothing : '-1';
|
|
348
|
+
return html `<button
|
|
349
|
+
part="button"
|
|
350
|
+
type="button"
|
|
351
|
+
aria-expanded=${this.expanded === undefined ? nothing : String(this.expanded)}
|
|
352
|
+
aria-label=${this.label || nothing}
|
|
353
|
+
command=${this.command || nothing}
|
|
354
|
+
tabindex=${stop}
|
|
355
|
+
?disabled=${this.isDisabled}
|
|
356
|
+
@click=${this.handleClick}
|
|
357
|
+
>
|
|
358
|
+
${label}
|
|
359
|
+
</button>`;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
__decorate([
|
|
363
|
+
property()
|
|
364
|
+
], HelixHeadlessButton.prototype, "type", void 0);
|
|
365
|
+
__decorate([
|
|
366
|
+
property()
|
|
367
|
+
], HelixHeadlessButton.prototype, "value", void 0);
|
|
368
|
+
__decorate([
|
|
369
|
+
property({ attribute: 'formaction' })
|
|
370
|
+
], HelixHeadlessButton.prototype, "formAction", void 0);
|
|
371
|
+
__decorate([
|
|
372
|
+
property({ attribute: 'formenctype' })
|
|
373
|
+
], HelixHeadlessButton.prototype, "formEnctype", void 0);
|
|
374
|
+
__decorate([
|
|
375
|
+
property({ attribute: 'formmethod' })
|
|
376
|
+
], HelixHeadlessButton.prototype, "formMethod", void 0);
|
|
377
|
+
__decorate([
|
|
378
|
+
property({ attribute: 'formnovalidate', type: Boolean })
|
|
379
|
+
], HelixHeadlessButton.prototype, "formNoValidate", void 0);
|
|
380
|
+
__decorate([
|
|
381
|
+
property({ attribute: 'formtarget' })
|
|
382
|
+
], HelixHeadlessButton.prototype, "formTarget", void 0);
|
|
383
|
+
__decorate([
|
|
384
|
+
property()
|
|
385
|
+
], HelixHeadlessButton.prototype, "label", void 0);
|
|
386
|
+
__decorate([
|
|
387
|
+
property()
|
|
388
|
+
], HelixHeadlessButton.prototype, "command", void 0);
|
|
389
|
+
__decorate([
|
|
390
|
+
property({ attribute: 'commandfor' })
|
|
391
|
+
], HelixHeadlessButton.prototype, "commandFor", void 0);
|
|
392
|
+
__decorate([
|
|
393
|
+
property({ attribute: false })
|
|
394
|
+
], HelixHeadlessButton.prototype, "focusable", void 0);
|
|
395
|
+
__decorate([
|
|
396
|
+
property({ attribute: false })
|
|
397
|
+
], HelixHeadlessButton.prototype, "expanded", void 0);
|
|
398
|
+
HelixHeadlessButton = __decorate([
|
|
399
|
+
customElement('helix-headless-button')
|
|
400
|
+
], HelixHeadlessButton);
|
|
401
|
+
export { HelixHeadlessButton };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/button-group-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/button-group-root.js';
|