@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,707 @@
|
|
|
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 { anchorElement, customElement, HelixElement, hostAria, MixinControllerOpen, PopoverController, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
/** Tags that exist to hold something, and say nothing when they hold nothing. */
|
|
11
|
+
const GENERIC = new Set(['div', 'p', 'section', 'span']);
|
|
12
|
+
/**
|
|
13
|
+
* @summary A short description that appears beside what it describes.
|
|
14
|
+
*
|
|
15
|
+
* @description
|
|
16
|
+
* The trigger is slotted and stays the consumer's own element. The bubble is
|
|
17
|
+
* rendered here, in the top layer through the popover API, so it clears any
|
|
18
|
+
* ancestor with `overflow: hidden` without a `z-index` anywhere.
|
|
19
|
+
*
|
|
20
|
+
* Position comes from `FloatingController`, which flips to another side when
|
|
21
|
+
* the preferred one does not fit and slides along it to stay in view.
|
|
22
|
+
*
|
|
23
|
+
* **Two forms, and they are not equivalent for a screen reader.**
|
|
24
|
+
*
|
|
25
|
+
* Wrapping is the convenience form and is right where the bubble decorates a
|
|
26
|
+
* control that already says what it is. The trigger stays the consumer's own
|
|
27
|
+
* element, slotted, and nothing here can describe it: an IDREF cannot cross a
|
|
28
|
+
* shadow boundary, an element reference is valid only into the same tree or a
|
|
29
|
+
* shadow-including *ancestor* (the bubble's tree is neither, it is inward), and
|
|
30
|
+
* a string on the trigger means writing to markup the consumer server-rendered.
|
|
31
|
+
* All three measured.
|
|
32
|
+
*
|
|
33
|
+
* `for` is the form that works. The tooltip sits *beside* the trigger in the
|
|
34
|
+
* consumer's own tree and names it by id, so both are in one tree and an IDREF
|
|
35
|
+
* between them is legal in the ordinary way:
|
|
36
|
+
*
|
|
37
|
+
* ```html
|
|
38
|
+
* <helix-button id="save" aria-describedby="save-tip">Save</helix-button>
|
|
39
|
+
* <helix-tooltip id="save-tip" for="save" content="Save your work"></helix-tooltip>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* Note that the reference points at *this host*, not at the bubble inside its
|
|
43
|
+
* shadow root: the description is computed from the flat tree, which descends
|
|
44
|
+
* into shadow content on its own.
|
|
45
|
+
*
|
|
46
|
+
* **The consumer writes that `aria-describedby`, and nothing here writes it for
|
|
47
|
+
* them.** WebAwesome and Spectrum Web Components both do write it, with
|
|
48
|
+
* `setAttribute` on the trigger at runtime, which is the one thing this library
|
|
49
|
+
* declines everywhere: React rendered that element on the server without the
|
|
50
|
+
* attribute and reports a mismatch it will not patch up. Two ids in the markup
|
|
51
|
+
* is the whole cost of the version that is correct before any JavaScript runs.
|
|
52
|
+
*
|
|
53
|
+
* Because it is the consumer's line to write, it is also theirs to forget, so
|
|
54
|
+
* `for` audits itself on first open and says so once, rather than on every
|
|
55
|
+
* open, since a warning that repeats is one people learn to filter out.
|
|
56
|
+
*
|
|
57
|
+
* It shows on hover and on focus, and hides on leave, on blur, on Escape, and
|
|
58
|
+
* once the trigger has been activated: the hint is spent when the control has
|
|
59
|
+
* been used.
|
|
60
|
+
* A tooltip that answers only the pointer is one a keyboard reader never sees.
|
|
61
|
+
*
|
|
62
|
+
* The pointer waits out `delay` first, or sweeping a row of controls flashes a
|
|
63
|
+
* tooltip for each of them. Focus does not wait: a keyboard reader landing on a
|
|
64
|
+
* control has already committed to it.
|
|
65
|
+
*
|
|
66
|
+
* Open state comes from `MixinControllerOpen`, so it announces `helix-before-open` and
|
|
67
|
+
* `helix-before-close` like every other overlay here, and a consumer can drive it with
|
|
68
|
+
* `open` where they need to.
|
|
69
|
+
*
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
* @status experimental
|
|
72
|
+
* @category overlays
|
|
73
|
+
* @aliases Hint, Popup label
|
|
74
|
+
* @documentation https://helix.fusion.dev/components/tooltip
|
|
75
|
+
*
|
|
76
|
+
* @slot - The trigger.
|
|
77
|
+
* @slot content - The bubble's content, when `content` is not enough. Anything
|
|
78
|
+
* slotted here replaces the property, which is this slot's fallback.
|
|
79
|
+
*
|
|
80
|
+
* @event {HelixBeforeCloseEvent} helix-before-close - Fired before it hides, and cancelable.
|
|
81
|
+
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before the bubble shows, and cancelable.
|
|
82
|
+
* @event {Event} toggle - Says the state moved, carrying `newState` and `oldState`.
|
|
83
|
+
*
|
|
84
|
+
* @csspart arrow - The point, resting against the trigger.
|
|
85
|
+
* @csspart tooltip - The bubble.
|
|
86
|
+
*/
|
|
87
|
+
let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpen(HelixElement) {
|
|
88
|
+
constructor() {
|
|
89
|
+
super(...arguments);
|
|
90
|
+
/**
|
|
91
|
+
* The text shown in the bubble.
|
|
92
|
+
*
|
|
93
|
+
* The common case, and the fallback for the `content` slot: slot something
|
|
94
|
+
* there and it replaces this. Both cannot show at once, which is the point
|
|
95
|
+
* of using fallback content rather than rendering whichever is present.
|
|
96
|
+
*/
|
|
97
|
+
this.content = '';
|
|
98
|
+
/**
|
|
99
|
+
* The id of the element this describes, in this tooltip's own tree.
|
|
100
|
+
*
|
|
101
|
+
* Set it and the tooltip stops wrapping: it renders no trigger, listens on
|
|
102
|
+
* the element it names, and points its bubble there. Leave it unset and the
|
|
103
|
+
* default slot is the trigger, which is the older form.
|
|
104
|
+
*
|
|
105
|
+
* Resolved through `getRootNode()`, so a tooltip inside another component's
|
|
106
|
+
* shadow root names an element in that root rather than in the document.
|
|
107
|
+
*
|
|
108
|
+
* **In React this arrives as a property and not as an attribute**, which is
|
|
109
|
+
* worth knowing before someone reads the DOM and thinks it did not take.
|
|
110
|
+
* Measured against React 19.2.8: `for` exists on the prototype, so React sets
|
|
111
|
+
* `element.for = 'save'` and writes nothing to the markup, where `content`
|
|
112
|
+
* and `placement`, which are not properties on the class at that moment, come
|
|
113
|
+
* through as attributes. Both paths reach the same place through Lit, so
|
|
114
|
+
* nothing is broken by it; a `[for]` selector would find nothing, which is
|
|
115
|
+
* why this does not reflect and nothing styles on it.
|
|
116
|
+
*/
|
|
117
|
+
this.for = '';
|
|
118
|
+
/**
|
|
119
|
+
* Preferred side.
|
|
120
|
+
*
|
|
121
|
+
* It moves if that side does not fit.
|
|
122
|
+
*/
|
|
123
|
+
this.placement = 'top';
|
|
124
|
+
/**
|
|
125
|
+
* Space between the trigger and the point of the bubble, in pixels.
|
|
126
|
+
*
|
|
127
|
+
* **Measured from the point, which is what the arrow being back changed.**
|
|
128
|
+
* The arrow is drawn half outside the bubble, so a gap measured to the
|
|
129
|
+
* bubble's edge is a smaller gap to the tip: at the default it left the point
|
|
130
|
+
* two pixels off the trigger, which reads as touching. The controller is
|
|
131
|
+
* given this plus however far the arrow actually protrudes, so eight means
|
|
132
|
+
* eight from whatever is nearest, arrow or edge, and a consumer who changes
|
|
133
|
+
* it changes the visible gap rather than a number the arrow then eats into.
|
|
134
|
+
*/
|
|
135
|
+
this.offset = 8;
|
|
136
|
+
/**
|
|
137
|
+
* The id of a box to keep the bubble inside, instead of the viewport.
|
|
138
|
+
*
|
|
139
|
+
* Resolved the way `for` is, so an id in the same tree, which is what lets a
|
|
140
|
+
* component name a box in its own shadow root. A chart does: its bubble is
|
|
141
|
+
* centered on a reading, and the first and last readings sit at the ends of
|
|
142
|
+
* the plot, so half the box hangs past the drawing and, measured on a chart
|
|
143
|
+
* in a 320px card, three pixels past the card itself.
|
|
144
|
+
*
|
|
145
|
+
* Clamping is on the cross axis only, which is what makes this safe to point
|
|
146
|
+
* at a box the bubble genuinely does not fit inside. See `within` on
|
|
147
|
+
* `FloatingController` for what happens if it is applied to the rest.
|
|
148
|
+
*/
|
|
149
|
+
this.within = '';
|
|
150
|
+
/**
|
|
151
|
+
* Whether the tooltip never shows.
|
|
152
|
+
*/
|
|
153
|
+
this.disabled = false;
|
|
154
|
+
/**
|
|
155
|
+
* Whether the bubble survives the trigger being used.
|
|
156
|
+
*
|
|
157
|
+
* **Activation takes a tooltip down, because a hint about what a control will
|
|
158
|
+
* do is spent once the control has done it.** That is right for a gear that
|
|
159
|
+
* opens a drawer and wrong for a control whose tooltip *becomes* the answer:
|
|
160
|
+
* `CopyButton` swaps its content from Copy to Copied on the same click, so the
|
|
161
|
+
* bubble was being hidden at the exact moment it had something to say. What a
|
|
162
|
+
* reader saw was a flash, and the confirmation the component had carefully
|
|
163
|
+
* written was never on screen long enough to read.
|
|
164
|
+
*
|
|
165
|
+
* So this is not "keep it open", it is "the hint is not spent": set it where
|
|
166
|
+
* the words change to describe what just happened, and leave it alone
|
|
167
|
+
* everywhere else. Escape, blur and pointer-leave still close it, because
|
|
168
|
+
* those are a reader saying they are done rather than a control saying it
|
|
169
|
+
* worked.
|
|
170
|
+
*/
|
|
171
|
+
this.persist = false;
|
|
172
|
+
/**
|
|
173
|
+
* How long the pointer must rest before the bubble appears, in milliseconds.
|
|
174
|
+
*
|
|
175
|
+
* Without one, sweeping across a row of controls flashes a tooltip for each
|
|
176
|
+
* of them, which is noise rather than help. Focus opens with no delay: a
|
|
177
|
+
* keyboard reader arriving on a control has already committed to it.
|
|
178
|
+
*/
|
|
179
|
+
this.delay = 400;
|
|
180
|
+
/**
|
|
181
|
+
* How long the bubble stays after the pointer leaves, in milliseconds.
|
|
182
|
+
*/
|
|
183
|
+
this.delayOut = 100;
|
|
184
|
+
/**
|
|
185
|
+
* What is currently keeping the bubble up.
|
|
186
|
+
*
|
|
187
|
+
* The pointer and focus are two holders and they shared one timer with
|
|
188
|
+
* nothing counting them, so whichever left last won: moving the pointer off a
|
|
189
|
+
* control that still had keyboard focus scheduled a hide and took the bubble
|
|
190
|
+
* away from under the reader. The same fault the toast timer had, and the
|
|
191
|
+
* same answer.
|
|
192
|
+
*/
|
|
193
|
+
this.holds = new Set();
|
|
194
|
+
/* One controller rather than the pair this was: a `FloatingController` beside
|
|
195
|
+
hand-written `showPopover`/`hidePopover` calls, which is exactly the
|
|
196
|
+
composition `PopoverController` exists to own. Its own documentation named
|
|
197
|
+
the controller it was not using.
|
|
198
|
+
*
|
|
199
|
+
* The boundary is the host and not the bubble, for the reason `Popover` gives:
|
|
200
|
+
* the host is `display: contents`, so a press on the trigger is inside the
|
|
201
|
+
* host and outside the bubble, and a boundary of the bubble alone would treat
|
|
202
|
+
* the press that opened it as a press outside. */
|
|
203
|
+
this.bubbleLayer = new PopoverController(this, {
|
|
204
|
+
anchor: () => this.trigger,
|
|
205
|
+
arrow: () => this.renderRoot.querySelector('[part="arrow"]'),
|
|
206
|
+
/* Annotated, because without it the array's element type is inferred from
|
|
207
|
+
`this`, whose own type depends on this controller, and TypeScript stops
|
|
208
|
+
at the cycle. */
|
|
209
|
+
boundary: () => [this, this.named],
|
|
210
|
+
/* The declared gap plus the arrow's own reach, so the gap is to the point.
|
|
211
|
+
*
|
|
212
|
+
* A square of side `n` rotated forty-five degrees about its own center
|
|
213
|
+
* reaches `n / sqrt(2)` from that center, and the arrow is placed with its
|
|
214
|
+
* center on the bubble's edge, so that is exactly how far past the edge the
|
|
215
|
+
* tip goes. Read from the element rather than assumed, because
|
|
216
|
+
* `--tooltip-arrow-size` is a hook and a theme may have moved it.
|
|
217
|
+
*
|
|
218
|
+
* `offsetWidth` and not `getBoundingClientRect`, which reports the rotated
|
|
219
|
+
* box and would count the reach twice. Zero while the bubble is closed and
|
|
220
|
+
* unrendered, which is right: there is nothing to place then either. */
|
|
221
|
+
offset: () => {
|
|
222
|
+
const point = this.renderRoot.querySelector('[part="arrow"]');
|
|
223
|
+
return this.offset + (point?.offsetWidth ?? 0) / Math.SQRT2;
|
|
224
|
+
},
|
|
225
|
+
onDismiss: () => this.hide(),
|
|
226
|
+
placement: () => this.placement,
|
|
227
|
+
surface: () => this.renderRoot.querySelector('[part="tooltip"]'),
|
|
228
|
+
within: () => this.enclosure,
|
|
229
|
+
});
|
|
230
|
+
/**
|
|
231
|
+
* Re-links the description when what is in the bubble changes.
|
|
232
|
+
*
|
|
233
|
+
* `updated` alone is not enough: content arriving from the light DOM changes
|
|
234
|
+
* no property here, so nothing would schedule the update that re-reads it. A
|
|
235
|
+
* consumer rendering the bubble's content after this upgrades, which is what
|
|
236
|
+
* a framework does, would leave the trigger describing nothing.
|
|
237
|
+
*/
|
|
238
|
+
this.handleContentChange = () => {
|
|
239
|
+
this.requestUpdate();
|
|
240
|
+
};
|
|
241
|
+
/** Shows after the pointer has rested for `delay`. */
|
|
242
|
+
this.showLater = () => {
|
|
243
|
+
this.holds.add('pointer');
|
|
244
|
+
clearTimeout(this.timer);
|
|
245
|
+
this.timer = setTimeout(() => this.show(), this.delay);
|
|
246
|
+
};
|
|
247
|
+
/** Hides after `delayOut`, so crossing the gap to the bubble does not close it. */
|
|
248
|
+
this.hideLater = () => {
|
|
249
|
+
this.holds.delete('pointer');
|
|
250
|
+
clearTimeout(this.timer);
|
|
251
|
+
this.timer = setTimeout(() => this.release(), this.delayOut);
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Focus opens the bubble, but only the kind of focus a reader chose.
|
|
255
|
+
*
|
|
256
|
+
* **A mouse press focuses the button it pressed**, so an unconditional focus
|
|
257
|
+
* hold meant that after any click the bubble was held up by a reader who was
|
|
258
|
+
* not asking for it: moving the pointer away released the pointer hold, found
|
|
259
|
+
* a focus hold still set, and left the tooltip on screen until something else
|
|
260
|
+
* blurred the control.
|
|
261
|
+
*
|
|
262
|
+
* It never showed until `persist` arrived, because activation used to clear
|
|
263
|
+
* every hold and hide, so the click path threw the stale hold away before
|
|
264
|
+
* anyone could see it. A tooltip that survives its own activation is the first
|
|
265
|
+
* one to hold that focus long enough to matter.
|
|
266
|
+
*
|
|
267
|
+
* `:focus-visible` is the platform's own answer to "did they mean it", and a
|
|
268
|
+
* button does not match it on a mouse press. Keyboard focus still opens with no
|
|
269
|
+
* delay, which is the case this branch exists for: a reader who tabbed to a
|
|
270
|
+
* control has already committed to it. Where it does not match there is nothing
|
|
271
|
+
* to do, the pointer having opened the bubble already if a pointer is involved.
|
|
272
|
+
*/
|
|
273
|
+
this.showNow = (event) => {
|
|
274
|
+
const focused = event.target;
|
|
275
|
+
if (focused instanceof Element && !focused.matches(':focus-visible')) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
this.holds.add('focus');
|
|
279
|
+
this.show();
|
|
280
|
+
};
|
|
281
|
+
this.releaseFocus = () => {
|
|
282
|
+
this.holds.delete('focus');
|
|
283
|
+
this.release();
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Activating the trigger closes the bubble, which nothing was doing.
|
|
287
|
+
*
|
|
288
|
+
* A tooltip is a hint about what a control will do, and once the control has
|
|
289
|
+
* been used the hint is spent. Nothing else was going to take it down either:
|
|
290
|
+
* `pointerleave` cannot fire while the pointer is still on the button it just
|
|
291
|
+
* pressed, and `focusout` does not fire when activation leaves focus where it
|
|
292
|
+
* was. So a gear that opened a drawer left its own label floating over the
|
|
293
|
+
* drawer until the reader moved the mouse away.
|
|
294
|
+
*
|
|
295
|
+
* `click` rather than `pointerdown`, because it is the one event both a mouse
|
|
296
|
+
* and a keyboard produce: Enter and Space on a focused button raise it, and a
|
|
297
|
+
* hint that outlived activation for keyboard readers alone would be the same
|
|
298
|
+
* bug with a smaller audience.
|
|
299
|
+
*
|
|
300
|
+
* Holds are cleared as well as hiding, the same as Escape. Leaving them set
|
|
301
|
+
* means the pointer is still recorded as being over the trigger, so the next
|
|
302
|
+
* `focusout` anywhere releases nothing and the bubble comes back on a
|
|
303
|
+
* `release` it should have been dropped from.
|
|
304
|
+
*
|
|
305
|
+
* `persist` opts out entirely, holds included: a tooltip whose content becomes
|
|
306
|
+
* the confirmation has not spent its hint, it has changed it, and the pointer
|
|
307
|
+
* really is still over the trigger. See the property.
|
|
308
|
+
*/
|
|
309
|
+
this.handleActivate = () => {
|
|
310
|
+
if (this.persist) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
this.holds.clear();
|
|
314
|
+
this.hide();
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Escape closes the bubble from wherever the reader is.
|
|
318
|
+
*
|
|
319
|
+
* On the document, not on the host, which is where it was: a tooltip opened
|
|
320
|
+
* by hover has focus somewhere else entirely, so the press never reached this
|
|
321
|
+
* element and Escape worked only for the one case that needed it least. That
|
|
322
|
+
* is the case the pattern names, a reader escaping a tooltip obscuring what
|
|
323
|
+
* is under it, and it was the case that did nothing.
|
|
324
|
+
*
|
|
325
|
+
* Not stopped from propagating: a tooltip is not what the reader was trying
|
|
326
|
+
* to escape if it sits inside something else that also closes.
|
|
327
|
+
*/
|
|
328
|
+
this.handleKeydown = (event) => {
|
|
329
|
+
if (event.key === 'Escape' && this.open) {
|
|
330
|
+
this.holds.clear();
|
|
331
|
+
this.hide();
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
/** Said once per element, however many times it opens. */
|
|
335
|
+
this.audited = false;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* The element the bubble points at.
|
|
339
|
+
*
|
|
340
|
+
* The trigger, not the host. The host is `display: contents` so it wraps
|
|
341
|
+
* without adding a box, and an element that generates no box measures as
|
|
342
|
+
* nothing at the document origin: the bubble was placed against the corner
|
|
343
|
+
* of the viewport rather than against the button.
|
|
344
|
+
*/
|
|
345
|
+
get trigger() {
|
|
346
|
+
/* Named, or slotted. The named one is looked up every time rather than
|
|
347
|
+
cached: `for` can be pointed somewhere else, and the element it names can
|
|
348
|
+
upgrade, move or be replaced by a framework without telling this. */
|
|
349
|
+
const named = this.named;
|
|
350
|
+
if (named) {
|
|
351
|
+
return named;
|
|
352
|
+
}
|
|
353
|
+
/* Through the util, which was written from a measurement of this component
|
|
354
|
+
and then not used by it. Its extra is the descent: the first assigned
|
|
355
|
+
element may itself draw nothing, and an icon button wrapped in a
|
|
356
|
+
`display: contents` span was the case that took the bubble to the corner
|
|
357
|
+
of the window. */
|
|
358
|
+
return anchorElement(this.renderRoot.querySelector('slot:not([name])'), this);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Places the bubble again, for an anchor that moved without the page moving.
|
|
362
|
+
*
|
|
363
|
+
* Public because a chart needs it. Its mark walks from reading to reading
|
|
364
|
+
* while the page holds still, and `autoUpdate` watches scrolling, resizing
|
|
365
|
+
* and layout shift, none of which is that. Everything else floating here is
|
|
366
|
+
* anchored to something the page moves, which is why nothing wanted this
|
|
367
|
+
* until a chart did.
|
|
368
|
+
*/
|
|
369
|
+
reposition() {
|
|
370
|
+
this.bubbleLayer.reposition();
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The element `for` names, or null.
|
|
374
|
+
*
|
|
375
|
+
* `getRootNode()` rather than `document`, which is what makes this work inside
|
|
376
|
+
* another component: an id is unique to a tree, and a tooltip in a shadow root
|
|
377
|
+
* names an element in that root. Client only, since the server has no tree to
|
|
378
|
+
* search.
|
|
379
|
+
*/
|
|
380
|
+
get named() {
|
|
381
|
+
if (this.for === '') {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
const root = this.getRootNode();
|
|
385
|
+
return root.getElementById?.(this.for) ?? null;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* The element `within` names, or null.
|
|
389
|
+
*
|
|
390
|
+
* Same resolution as `named`, and separate from it because they answer
|
|
391
|
+
* different questions: one is what the bubble points at and the other is what
|
|
392
|
+
* it must stay inside, and a component often has one and not the other.
|
|
393
|
+
*/
|
|
394
|
+
get enclosure() {
|
|
395
|
+
if (this.within === '') {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
const root = this.getRootNode();
|
|
399
|
+
return root instanceof Document || root instanceof ShadowRoot
|
|
400
|
+
? root.getElementById(this.within)
|
|
401
|
+
: null;
|
|
402
|
+
}
|
|
403
|
+
get bubble() {
|
|
404
|
+
return this.renderRoot.querySelector('[part="tooltip"]');
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Whether there is anything to show.
|
|
408
|
+
*
|
|
409
|
+
* Asked of the slot rather than of the property, because either can carry
|
|
410
|
+
* the content. `assignedNodes` without `flatten` answers what the consumer
|
|
411
|
+
* provided and not what the fallback would render, which is exactly the
|
|
412
|
+
* question: a bubble with only fallback and an empty property is empty.
|
|
413
|
+
*
|
|
414
|
+
* Text or an element, because neither alone is the test. An element on its
|
|
415
|
+
* own would count `<span slot="content"> </span>` as something to say,
|
|
416
|
+
* and text on its own would count an icon as nothing, when an icon is the
|
|
417
|
+
* whole point of slotting rather than setting the property.
|
|
418
|
+
*
|
|
419
|
+
* Client only. The server has no assignment to inspect, and nothing calls
|
|
420
|
+
* this there: a tooltip opens in response to a pointer or to focus.
|
|
421
|
+
*/
|
|
422
|
+
get hasContent() {
|
|
423
|
+
if (this.content !== '') {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
const slot = this.renderRoot.querySelector('slot[name="content"]');
|
|
427
|
+
return (slot?.assignedNodes() ?? []).some((node) => {
|
|
428
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
429
|
+
return node.textContent?.trim() !== '';
|
|
430
|
+
}
|
|
431
|
+
/* A wrapper is empty when it holds nothing; anything else draws.
|
|
432
|
+
*
|
|
433
|
+
* The test was children-or-text for every element, which reads as careful
|
|
434
|
+
* and refused the exact case this getter's own paragraph calls the whole
|
|
435
|
+
* point of slotting. `<helix-icon slot="content">` has no children and no
|
|
436
|
+
* text, so `hasContent` was false, `show()` returned early, and a tooltip
|
|
437
|
+
* whose bubble is an icon could not be opened at all. Measured in a
|
|
438
|
+
* browser: `open` stayed false through a direct `show()`.
|
|
439
|
+
*
|
|
440
|
+
* The spec beside this one is the other half, and it is why the answer is
|
|
441
|
+
* not simply "an element is content": `<span slot="content"> </span>`
|
|
442
|
+
* is an element too and says nothing. In the light DOM those two are
|
|
443
|
+
* identical, so nothing can separate them by shape and the tag is what
|
|
444
|
+
* is left. A generic wrapper is asked to hold something; everything else,
|
|
445
|
+
* an icon, an image, an svg, a component, draws on its own. */
|
|
446
|
+
const element = node;
|
|
447
|
+
return GENERIC.has(element.localName)
|
|
448
|
+
? element.childElementCount > 0 || element.textContent?.trim() !== ''
|
|
449
|
+
: true;
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* The bubble's words as a plain string, for naming the host.
|
|
454
|
+
*
|
|
455
|
+
* The property when it is set, otherwise whatever was slotted, flattened.
|
|
456
|
+
* Rich content collapses to its text, which is the right answer for a
|
|
457
|
+
* description: `Save ⌘ S` is what a reader needs, not the markup.
|
|
458
|
+
*/
|
|
459
|
+
get describedText() {
|
|
460
|
+
if (this.content !== '') {
|
|
461
|
+
return this.content;
|
|
462
|
+
}
|
|
463
|
+
const slot = this.renderRoot.querySelector('slot[name="content"]');
|
|
464
|
+
return (slot?.assignedNodes() ?? [])
|
|
465
|
+
.map((node) => node.textContent ?? '')
|
|
466
|
+
.join(' ')
|
|
467
|
+
.replace(/\s+/gu, ' ')
|
|
468
|
+
.trim();
|
|
469
|
+
}
|
|
470
|
+
/** Shows the bubble, unless there is nothing to show. */
|
|
471
|
+
show() {
|
|
472
|
+
clearTimeout(this.timer);
|
|
473
|
+
if (this.disabled || !this.hasContent || this.open) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (!this.requestOpen(true)) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
// Raised in `updated()`, which the state change has just scheduled: one
|
|
480
|
+
// path whether the bubble was opened by a pointer or by declaring `open`.
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Closes the bubble, and cancels a show that had not happened yet.
|
|
484
|
+
*
|
|
485
|
+
* The timer matters as much as the state: a pointer that arrives and leaves
|
|
486
|
+
* inside the delay left one queued, and the tooltip appeared over whatever
|
|
487
|
+
* the reader had moved on to.
|
|
488
|
+
*/
|
|
489
|
+
hide() {
|
|
490
|
+
clearTimeout(this.timer);
|
|
491
|
+
if (!this.open) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (!this.requestOpen(false)) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
this.bubbleLayer.hide();
|
|
498
|
+
}
|
|
499
|
+
/** Hides only once nothing is holding the bubble up. */
|
|
500
|
+
release() {
|
|
501
|
+
if (this.holds.size === 0) {
|
|
502
|
+
this.hide();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Puts the listeners on whatever the trigger is.
|
|
507
|
+
*
|
|
508
|
+
* The host when it wraps, since the trigger is inside it and both `focusin`
|
|
509
|
+
* and `focusout` bubble. The named element when `for` is set, because then the
|
|
510
|
+
* host wraps nothing and `pointerenter` does not bubble at all.
|
|
511
|
+
*
|
|
512
|
+
* Client only, and after the first render: `for` names an element that may not
|
|
513
|
+
* have upgraded, or existed, when this one connected.
|
|
514
|
+
*/
|
|
515
|
+
bind() {
|
|
516
|
+
const target = this.named ?? this;
|
|
517
|
+
if (this.bound === target) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
this.unbind();
|
|
521
|
+
target.addEventListener('pointerenter', this.showLater);
|
|
522
|
+
target.addEventListener('pointerleave', this.hideLater);
|
|
523
|
+
// Focus opens at once: a keyboard reader landing on a control has already
|
|
524
|
+
// committed to it, where a pointer crossing one has not.
|
|
525
|
+
target.addEventListener('focusin', this.showNow);
|
|
526
|
+
target.addEventListener('focusout', this.releaseFocus);
|
|
527
|
+
target.addEventListener('click', this.handleActivate);
|
|
528
|
+
this.bound = target;
|
|
529
|
+
}
|
|
530
|
+
unbind() {
|
|
531
|
+
this.bound?.removeEventListener('pointerenter', this.showLater);
|
|
532
|
+
this.bound?.removeEventListener('pointerleave', this.hideLater);
|
|
533
|
+
this.bound?.removeEventListener('click', this.handleActivate);
|
|
534
|
+
this.bound?.removeEventListener('focusin', this.showNow);
|
|
535
|
+
this.bound?.removeEventListener('focusout', this.releaseFocus);
|
|
536
|
+
this.bound = undefined;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Checks that the trigger actually points back, and says so once if not.
|
|
540
|
+
*
|
|
541
|
+
* `for` is only half a link: this element finds the trigger, and the trigger
|
|
542
|
+
* has to name this element for a screen reader to read the two together. That
|
|
543
|
+
* second half is the consumer's line to write, deliberately, because writing
|
|
544
|
+
* it from here means `setAttribute` on markup they server-rendered. So it is
|
|
545
|
+
* theirs to forget, and a silent accessibility gap is the worst kind: the
|
|
546
|
+
* tooltip looks completely correct.
|
|
547
|
+
*
|
|
548
|
+
* On first open rather than on connect, because a framework may render the
|
|
549
|
+
* attribute after this element upgrades, and because a tooltip that never
|
|
550
|
+
* opens has described nothing eitherway.
|
|
551
|
+
*
|
|
552
|
+
* Either attribute counts. WebAwesome uses `aria-labelledby` over
|
|
553
|
+
* `aria-describedby` on purpose, their reasoning being that some screen
|
|
554
|
+
* readers want the description on the focusable element itself while a label
|
|
555
|
+
* reads on first focus regardless, and that is a live enough question that
|
|
556
|
+
* this should not be the thing to settle it.
|
|
557
|
+
*/
|
|
558
|
+
auditDescription() {
|
|
559
|
+
if (this.audited || this.for === '' || !this.hasContent) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
this.audited = true;
|
|
563
|
+
const trigger = this.named;
|
|
564
|
+
if (!trigger) {
|
|
565
|
+
console.warn(`<${this.localName} for="${this.for}"> found no element with that id in its own tree, ` +
|
|
566
|
+
'so it has nothing to describe and nothing to point at. Ids resolve per tree: an ' +
|
|
567
|
+
'element inside another shadow root is not reachable from here.');
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const names = `${trigger.getAttribute('aria-describedby') ?? ''} ${trigger.getAttribute('aria-labelledby') ?? ''}`
|
|
571
|
+
.split(/\s+/)
|
|
572
|
+
.filter(Boolean);
|
|
573
|
+
if (this.id !== '' && names.includes(this.id)) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
console.warn(`<${this.localName} for="${this.for}"> is not read out with its trigger. The link is ` +
|
|
577
|
+
'written from the trigger, not from here: give this tooltip an `id` and add ' +
|
|
578
|
+
'`aria-describedby="<that id>"` to the element it names. Written in your own markup ' +
|
|
579
|
+
'it survives server rendering, which is why this element will not write it for you.');
|
|
580
|
+
}
|
|
581
|
+
connectedCallback() {
|
|
582
|
+
super.connectedCallback();
|
|
583
|
+
document.addEventListener('keydown', this.handleKeydown);
|
|
584
|
+
}
|
|
585
|
+
disconnectedCallback() {
|
|
586
|
+
super.disconnectedCallback();
|
|
587
|
+
this.unbind();
|
|
588
|
+
document.removeEventListener('keydown', this.handleKeydown);
|
|
589
|
+
this.holds.clear();
|
|
590
|
+
clearTimeout(this.timer);
|
|
591
|
+
}
|
|
592
|
+
firstUpdated(changed) {
|
|
593
|
+
super.firstUpdated(changed);
|
|
594
|
+
this.bind();
|
|
595
|
+
}
|
|
596
|
+
updated(changed) {
|
|
597
|
+
super.updated(changed);
|
|
598
|
+
/* Raised and lowered from the resolved state, so a bubble opened by
|
|
599
|
+
declaring `open` takes the same path as one opened by a pointer. Here
|
|
600
|
+
rather than a microtask later: the render has been written, so the bubble
|
|
601
|
+
is no longer `display: none` and `showPopover` is safe on it. */
|
|
602
|
+
if (changed.has('for')) {
|
|
603
|
+
this.bind();
|
|
604
|
+
}
|
|
605
|
+
if (changed.has('open')) {
|
|
606
|
+
if (this.open) {
|
|
607
|
+
this.bubbleLayer.show();
|
|
608
|
+
this.auditDescription();
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
this.bubbleLayer.hide();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const bubble = this.bubble;
|
|
615
|
+
if (!bubble) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
/* The reference goes on the host, and the host is not what a reader is on.
|
|
619
|
+
*
|
|
620
|
+
* The host is `display: contents` and carries no role; the thing that takes
|
|
621
|
+
* focus is the consumer's trigger inside it, and a description on an
|
|
622
|
+
* ancestor is not announced with it. So the trigger has to be described
|
|
623
|
+
* too, and it cannot be described by reference: measured, assigning
|
|
624
|
+
* `trigger.ariaDescribedByElements = [bubble]` reads back as an empty array,
|
|
625
|
+
* a reference being valid only into the same tree or a shadow-including
|
|
626
|
+
* ancestor of it. The bubble's tree is neither, it is inward.
|
|
627
|
+
*
|
|
628
|
+
* A string would cross, and `aria-description` on the trigger was tried:
|
|
629
|
+
* it works, and it is a hydration mismatch. The trigger is the consumer's
|
|
630
|
+
* own element, React rendered it on the server without that attribute, and
|
|
631
|
+
* writing it at upgrade is exactly what this library declines to do
|
|
632
|
+
* everywhere else. Measured against the site's own header: React logged the
|
|
633
|
+
* mismatch on every tooltip on the page and said it would not patch it up.
|
|
634
|
+
*
|
|
635
|
+
* So the description stays on the host and the gap is recorded rather than
|
|
636
|
+
* papered over. There is no third option: a description has to live on the
|
|
637
|
+
* element being described, that element belongs to the consumer, and the
|
|
638
|
+
* bubble is in a tree no reference from there can name. Closing it means
|
|
639
|
+
* either writing to the consumer's markup or moving the bubble into the
|
|
640
|
+
* light DOM, and both are decisions rather than fixes. */
|
|
641
|
+
hostAria(this.internals, {
|
|
642
|
+
describedBy: this.hasContent ? [bubble] : [],
|
|
643
|
+
/* **And the host names itself, which is what makes `for` work when the
|
|
644
|
+
bubble is down.**
|
|
645
|
+
*
|
|
646
|
+
* A reference resolves to the referenced element's accessible *name*, and
|
|
647
|
+
* that name comes from `aria-labelledby`, then `aria-label`, then the
|
|
648
|
+
* element's contents, in that order. Only the third can be defeated by
|
|
649
|
+
* the contents being `display: none`, which is exactly what a closed
|
|
650
|
+
* popover is. So without a name of its own the host describes nothing
|
|
651
|
+
* until the bubble is already up, and a screen reader computes a
|
|
652
|
+
* description when focus lands, which is a frame or two before that.
|
|
653
|
+
*
|
|
654
|
+
* Measured, one page, two identical tooltips:
|
|
655
|
+
*
|
|
656
|
+
* CLOSED bare host (none)
|
|
657
|
+
* CLOSED labeled host Description B
|
|
658
|
+
* OPEN bare host Description A
|
|
659
|
+
* OPEN labeled host Description B
|
|
660
|
+
*
|
|
661
|
+
* Only when `for` is set, because only then is this host the thing being
|
|
662
|
+
* referenced. Wrapping, it is an ancestor of the trigger and naming it
|
|
663
|
+
* would put a name on a box that is `display: contents`. */
|
|
664
|
+
label: this.for === '' ? '' : this.describedText,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
render() {
|
|
668
|
+
return html `<slot></slot>
|
|
669
|
+
<div part="tooltip" popover="manual" role="tooltip">
|
|
670
|
+
<span part="arrow"></span>
|
|
671
|
+
<slot name="content" @slotchange=${this.handleContentChange}
|
|
672
|
+
>${this.content}</slot
|
|
673
|
+
>
|
|
674
|
+
</div>`;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
__decorate([
|
|
678
|
+
property()
|
|
679
|
+
], HelixHeadlessTooltip.prototype, "content", void 0);
|
|
680
|
+
__decorate([
|
|
681
|
+
property()
|
|
682
|
+
], HelixHeadlessTooltip.prototype, "for", void 0);
|
|
683
|
+
__decorate([
|
|
684
|
+
property()
|
|
685
|
+
], HelixHeadlessTooltip.prototype, "placement", void 0);
|
|
686
|
+
__decorate([
|
|
687
|
+
property({ type: Number })
|
|
688
|
+
], HelixHeadlessTooltip.prototype, "offset", void 0);
|
|
689
|
+
__decorate([
|
|
690
|
+
property()
|
|
691
|
+
], HelixHeadlessTooltip.prototype, "within", void 0);
|
|
692
|
+
__decorate([
|
|
693
|
+
property({ reflect: true, type: Boolean })
|
|
694
|
+
], HelixHeadlessTooltip.prototype, "disabled", void 0);
|
|
695
|
+
__decorate([
|
|
696
|
+
property({ reflect: true, type: Boolean })
|
|
697
|
+
], HelixHeadlessTooltip.prototype, "persist", void 0);
|
|
698
|
+
__decorate([
|
|
699
|
+
property({ attribute: 'delay', type: Number })
|
|
700
|
+
], HelixHeadlessTooltip.prototype, "delay", void 0);
|
|
701
|
+
__decorate([
|
|
702
|
+
property({ attribute: 'delay-out', type: Number })
|
|
703
|
+
], HelixHeadlessTooltip.prototype, "delayOut", void 0);
|
|
704
|
+
HelixHeadlessTooltip = __decorate([
|
|
705
|
+
customElement('helix-headless-tooltip')
|
|
706
|
+
], HelixHeadlessTooltip);
|
|
707
|
+
export { HelixHeadlessTooltip };
|