@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,519 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixBeforeActivateEvent, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html } from 'lit';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
|
+
/**
|
|
11
|
+
* @summary Links to the headings on this page, marking the one being read.
|
|
12
|
+
*
|
|
13
|
+
* @description
|
|
14
|
+
* A table of contents, named for the job. Three of the obvious names are taken
|
|
15
|
+
* by things that are not this: `Outline` is a CSS property and the focus ring's
|
|
16
|
+
* whole vocabulary, `Contents` is `display: contents` and the `content` part
|
|
17
|
+
* half this library renders, and `TableOfContents` would sit beside `Table` and
|
|
18
|
+
* `TableColumn` and read as a third part of that family. `Outline` sits beside
|
|
19
|
+
* `Navigation`, which is the same job at the other scale: one navigates a site, the
|
|
20
|
+
* other navigates a document.
|
|
21
|
+
*
|
|
22
|
+
* **The links are the consumer's own anchors**, the same as `Navigation`. A router
|
|
23
|
+
* renders its own `<a>` and cannot render one inside this shadow root, and the
|
|
24
|
+
* hrefs are fragments the page already knows.
|
|
25
|
+
*
|
|
26
|
+
* **What is current is decided by position, not by intersection.** The obvious
|
|
27
|
+
* build is an `IntersectionObserver` with a thin band near the top of the
|
|
28
|
+
* window, and it fails in both directions: with headings far apart nothing is
|
|
29
|
+
* in the band and the marker goes out entirely, and with headings close
|
|
30
|
+
* together several are and it flickers between them. So the observer is only
|
|
31
|
+
* the *trigger*, and the answer is computed from where the headings actually
|
|
32
|
+
* are: the current section is the last one whose top has passed the reading
|
|
33
|
+
* line, or the first heading before any of them has.
|
|
34
|
+
*
|
|
35
|
+
* That rule is what a reader means. Two headings on screen at once are not
|
|
36
|
+
* ambiguous to a person: they are reading under the upper one.
|
|
37
|
+
*
|
|
38
|
+
* **`aria-current="location"`, and it is written onto the consumer's anchor.**
|
|
39
|
+
* This library does not normally write to light-DOM children, because an
|
|
40
|
+
* attribute added after upgrade is one a server's markup never had. It is right
|
|
41
|
+
* here for the reason `Carousel` names its slides: what is current is derived
|
|
42
|
+
* from scroll position, so no server could have rendered it and there is
|
|
43
|
+
* nothing for hydration to disagree with. `location` rather than `page`, which
|
|
44
|
+
* is what `Navigation` uses: `page` means this is the page, and every one of these
|
|
45
|
+
* is the same page.
|
|
46
|
+
*
|
|
47
|
+
* @since 0.1.0
|
|
48
|
+
* @status experimental
|
|
49
|
+
* @category navigation
|
|
50
|
+
* @aliases Table of contents, On this page, Outline
|
|
51
|
+
* @documentation https://helix.fusion.dev/components/outline
|
|
52
|
+
*
|
|
53
|
+
* @slot - The links, as anchors with fragment hrefs.
|
|
54
|
+
*
|
|
55
|
+
* @event {HelixBeforeActivateEvent} helix-before-activate - The section being read has changed, carrying its id as the value. Cancelable: refusing it takes the decision and nothing moves.
|
|
56
|
+
*
|
|
57
|
+
* @csspart indicator - The mark that travels to the section being read.
|
|
58
|
+
* @csspart nav - The navigation landmark.
|
|
59
|
+
*/
|
|
60
|
+
let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
61
|
+
constructor() {
|
|
62
|
+
super(...arguments);
|
|
63
|
+
/* The mark. Vertical, which the controller needs no telling about: it writes
|
|
64
|
+
all four values and the stylesheet uses the two it cares about. */
|
|
65
|
+
this.indicator = new IndicatorController(this, {
|
|
66
|
+
active: () => this.links.find((link) => link.hasAttribute('aria-current')),
|
|
67
|
+
indicator: () => this.renderRoot?.querySelector('[part="indicator"]'),
|
|
68
|
+
surface: () => this.renderRoot?.querySelector('[part="nav"]'),
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* Accessible name for the navigation.
|
|
72
|
+
*/
|
|
73
|
+
this.label = 'On this page';
|
|
74
|
+
/**
|
|
75
|
+
* How far below the top of the window a heading counts as reached, in pixels.
|
|
76
|
+
*
|
|
77
|
+
* The reading line. A page with a sticky masthead sets this to its height, or
|
|
78
|
+
* a little more: without it the current section changes as a heading slides
|
|
79
|
+
* under the chrome rather than as it reaches the top of what can be seen.
|
|
80
|
+
*/
|
|
81
|
+
this.offset = 0;
|
|
82
|
+
/**
|
|
83
|
+
* How a click on one of these links travels to its section.
|
|
84
|
+
*
|
|
85
|
+
* The platform's own vocabulary, because this is the platform's own option:
|
|
86
|
+
* `auto` defers to whatever `scroll-behavior` the page has set on its scroll
|
|
87
|
+
* container, which is the honest default and means an outline changes nothing
|
|
88
|
+
* a consumer did not ask it to. `smooth` slides, `instant` jumps.
|
|
89
|
+
*
|
|
90
|
+
* **`auto` is also the only value that leaves the navigation to the browser.**
|
|
91
|
+
* A fragment link does four things: it scrolls, it writes the hash, it pushes
|
|
92
|
+
* a history entry, and it moves the sequential focus point so the next Tab
|
|
93
|
+
* continues from the heading rather than from the link. Choosing a behavior
|
|
94
|
+
* means intercepting the click, and an interception that does only the first
|
|
95
|
+
* of those is a keyboard reader stranded in the contents list. So the other
|
|
96
|
+
* three are done by hand here, and only when there is a reason to take over.
|
|
97
|
+
*
|
|
98
|
+
* `smooth` becomes `instant` under `prefers-reduced-motion: reduce`. That is
|
|
99
|
+
* read at the click rather than cached, because a reader can change it while
|
|
100
|
+
* the page is open and this is the only motion in the component that
|
|
101
|
+
* JavaScript rather than CSS decides.
|
|
102
|
+
*/
|
|
103
|
+
this.behavior = 'auto';
|
|
104
|
+
/**
|
|
105
|
+
* What `current` reports, which only `mark` writes.
|
|
106
|
+
*/
|
|
107
|
+
this.reading = '';
|
|
108
|
+
/** The frame a mark is already scheduled for, so a scroll asks for one. */
|
|
109
|
+
this.frame = 0;
|
|
110
|
+
/**
|
|
111
|
+
* The two things an `IntersectionObserver` cannot see, and one listener for
|
|
112
|
+
* both.
|
|
113
|
+
*
|
|
114
|
+
* This component's own note said the observer keeps the marker honest without
|
|
115
|
+
* a scroll listener. It was wrong twice.
|
|
116
|
+
*
|
|
117
|
+
* *At the bottom*, arriving at the end of a page changes no intersection: a
|
|
118
|
+
* short last section is fully visible long before the final pixel, so its
|
|
119
|
+
* ratio reached 1 and stopped moving. Nothing fires for the scroll that ends
|
|
120
|
+
* the page.
|
|
121
|
+
*
|
|
122
|
+
* *And at the end of a smooth scroll*, which is the one that showed up in
|
|
123
|
+
* use. Clicking a link in the contents list scrolled to the section and left
|
|
124
|
+
* the mark where it was. Measured on the shipped example: after clicking
|
|
125
|
+
* `#usage` the heading sat at 72px, exactly the reading line, and `current`
|
|
126
|
+
* was still `install`. The observer fires several times during the travel and
|
|
127
|
+
* the last of them lands a frame or two before the final position, where the
|
|
128
|
+
* heading has not crossed yet, and no callback follows for the rest of the
|
|
129
|
+
* way.
|
|
130
|
+
*
|
|
131
|
+
* There was a listener already, added for the first of those, and it compared
|
|
132
|
+
* two numbers and marked only when the answer *changed*. That was too clever
|
|
133
|
+
* by half: it fixed the bottom and could not fix the settle. Marking once per
|
|
134
|
+
* animation frame while a scroll is happening covers both and every other case
|
|
135
|
+
* of the same shape, and the cost is what `mark()` costs, a `getBoundingClientRect`
|
|
136
|
+
* per section on frames a reader is already scrolling through. Passive, and
|
|
137
|
+
* coalesced to one call per frame however many events arrive.
|
|
138
|
+
*/
|
|
139
|
+
this.handleScroll = () => {
|
|
140
|
+
if (this.frame !== 0) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.frame = globalThis.requestAnimationFrame(() => {
|
|
144
|
+
this.frame = 0;
|
|
145
|
+
this.mark();
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Takes a link's click, but only where `auto` would not have done the job.
|
|
150
|
+
*
|
|
151
|
+
* Left the platform's wherever it can be: a modified click is a new tab or a
|
|
152
|
+
* download and none of this applies, a click something else has already
|
|
153
|
+
* handled is not ours, and `auto` is the whole point of `auto`.
|
|
154
|
+
*/
|
|
155
|
+
this.handleClick = (event) => {
|
|
156
|
+
if (event.defaultPrevented || event.button !== 0) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const link = event.target?.closest?.('a[href^="#"]');
|
|
163
|
+
const section = this.sections.find((one) => one.link === link);
|
|
164
|
+
if (!section) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
/* The destination is marked now, and everything between here and there is
|
|
168
|
+
* not.
|
|
169
|
+
*
|
|
170
|
+
* The marker follows the scroll, which is right when a reader is scrolling
|
|
171
|
+
* and wrong when they have said where they are going: traveling to the
|
|
172
|
+
* fifth section passes the second, third and fourth, each became current
|
|
173
|
+
* for a frame or two on the way, and the mark stepped down the list one
|
|
174
|
+
* item at a time instead of making the journey. Asked for as "it stops at
|
|
175
|
+
* each item rather than seamlessly moving", which is exactly what it was
|
|
176
|
+
* doing.
|
|
177
|
+
*
|
|
178
|
+
* Suppressed for any click on one of these links, not only the ones this
|
|
179
|
+
* component scrolls itself: a consumer with `scroll-behavior: smooth` on
|
|
180
|
+
* their own scroll container gets the same travel and the same stepping,
|
|
181
|
+
* and `behavior="auto"` does not mean "nobody is animating".
|
|
182
|
+
*
|
|
183
|
+
* `landing` is cleared when the page stops moving, by the same `scrollend`
|
|
184
|
+
* and timeout pair the mark uses elsewhere, because a scroll that is
|
|
185
|
+
* interrupted by a reader grabbing the page has to hand the marker back. */
|
|
186
|
+
this.landing = section;
|
|
187
|
+
this.markTo(section);
|
|
188
|
+
clearTimeout(this.landed);
|
|
189
|
+
const arrive = () => {
|
|
190
|
+
clearTimeout(this.landed);
|
|
191
|
+
this.landing = undefined;
|
|
192
|
+
globalThis.removeEventListener?.('scrollend', arrive);
|
|
193
|
+
this.mark();
|
|
194
|
+
};
|
|
195
|
+
globalThis.addEventListener?.('scrollend', arrive, { once: true });
|
|
196
|
+
this.landed = setTimeout(arrive, 1200);
|
|
197
|
+
if (this.behavior === 'auto') {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
const reduced = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;
|
|
202
|
+
section.target.scrollIntoView({
|
|
203
|
+
behavior: reduced && this.behavior === 'smooth' ? 'instant' : this.behavior,
|
|
204
|
+
block: 'start',
|
|
205
|
+
});
|
|
206
|
+
/* The three things the anchor would have done and the scroll does not. The
|
|
207
|
+
history entry keeps Back working and the address bar copyable; the focus
|
|
208
|
+
move is what stops Tab continuing from the contents list. `preventScroll`
|
|
209
|
+
because the scroll above is already under way and focusing would fight it,
|
|
210
|
+
instantly, wherever `smooth` was asked for. */
|
|
211
|
+
globalThis.history?.pushState(null, '', link?.getAttribute('href') ?? '');
|
|
212
|
+
const target = section.target;
|
|
213
|
+
if (!target.hasAttribute('tabindex')) {
|
|
214
|
+
target.setAttribute('tabindex', '-1');
|
|
215
|
+
}
|
|
216
|
+
target.focus({ preventScroll: true });
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* The set changed: watch it, and measure it.
|
|
220
|
+
*
|
|
221
|
+
* Both, for the reason the tab strip records: the host renders before its
|
|
222
|
+
* links have anything to point at, so the first measurement finds nothing,
|
|
223
|
+
* and without a second one at `slotchange` the mark stays unplaced for the
|
|
224
|
+
* life of the page.
|
|
225
|
+
*/
|
|
226
|
+
this.handleSlotChange = () => {
|
|
227
|
+
this.watch();
|
|
228
|
+
this.indicator.place();
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The id of the section being read, or empty before anything is measured.
|
|
233
|
+
*
|
|
234
|
+
* Read-only, and a getter rather than a property for that reason: it is
|
|
235
|
+
* derived from where the page is scrolled to, so a consumer setting it would
|
|
236
|
+
* be overwritten by the next thing that crossed the reading line. Every other
|
|
237
|
+
* writable property in this library that a component also writes is state a
|
|
238
|
+
* consumer can own; this one cannot be, and saying so with the type is better
|
|
239
|
+
* than saying it in a sentence nobody reads.
|
|
240
|
+
*/
|
|
241
|
+
get current() {
|
|
242
|
+
return this.reading;
|
|
243
|
+
}
|
|
244
|
+
/** The links, in document order. Empty on the server, which has no tree. */
|
|
245
|
+
get links() {
|
|
246
|
+
const slot = this.renderRoot?.querySelector('slot');
|
|
247
|
+
return (slot?.assignedElements({ flatten: true }) ?? []).filter((element) => element instanceof HTMLAnchorElement &&
|
|
248
|
+
element.getAttribute('href')?.startsWith('#') === true);
|
|
249
|
+
}
|
|
250
|
+
/** What each link points at, paired with the link, skipping anything missing. */
|
|
251
|
+
get sections() {
|
|
252
|
+
const root = this.getRootNode();
|
|
253
|
+
return this.links.flatMap((link) => {
|
|
254
|
+
const id = decodeURIComponent(link.getAttribute('href')?.slice(1) ?? '');
|
|
255
|
+
const target = id ? root.getElementById?.(id) : null;
|
|
256
|
+
/* A section that is not rendered is not one that can be read.
|
|
257
|
+
*
|
|
258
|
+
* `display: none` reports a zero rect, and zero is above the reading line,
|
|
259
|
+
* so an unrendered section counts as passed on every measurement and the
|
|
260
|
+
* marker lands on the last one of them. Found on a reference page that
|
|
261
|
+
* shows one API tier at a time and hides the other: the rail marked a
|
|
262
|
+
* heading the reader could not see, and no scrolling could move it,
|
|
263
|
+
* because the hidden sections always won the comparison.
|
|
264
|
+
*
|
|
265
|
+
* `getClientRects()` rather than a size check, because it is empty for
|
|
266
|
+
* exactly the case in question and non-empty for a section that is merely
|
|
267
|
+
* scrolled out of view, which is very much readable. */
|
|
268
|
+
return target && target.getClientRects().length > 0 ? [{ link, target }] : [];
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Works out which section is being read, and marks its link.
|
|
273
|
+
*
|
|
274
|
+
* Positional rather than a reading of what intersects, for the reason above.
|
|
275
|
+
* The comparison is against `offset` and not zero, so the reading line is
|
|
276
|
+
* wherever the page says the top of the readable area is.
|
|
277
|
+
*/
|
|
278
|
+
mark() {
|
|
279
|
+
/* A destination beats a measurement. See `handleClick`: while the page is
|
|
280
|
+
traveling to somewhere a reader asked for, what it passes on the way is
|
|
281
|
+
not what they are reading. */
|
|
282
|
+
if (this.landing) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const sections = this.sections;
|
|
286
|
+
if (sections.length === 0) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const passed = sections.filter(({ target }) => target.getBoundingClientRect().top - this.offset <= 1);
|
|
290
|
+
/* At the end of the page the last section is what is being read, whether or
|
|
291
|
+
not its top ever reached the reading line.
|
|
292
|
+
*
|
|
293
|
+
* The positional rule has one blind spot and it is at the bottom: a section
|
|
294
|
+
* is current once its top passes the line, and a section near the end of the
|
|
295
|
+
* document may not have enough page beneath it to get there. Measured on
|
|
296
|
+
* three sections with a short last one, scrolled fully to the end, 910 of
|
|
297
|
+
* 910: the third heading sat at 530px, plainly on screen and plainly the
|
|
298
|
+
* thing being read, and the marker stayed on the second. The last item in a
|
|
299
|
+
* contents list was unreachable by scrolling.
|
|
300
|
+
*
|
|
301
|
+
* The same fault has no mirror at the top, where `passed` is empty and the
|
|
302
|
+
* first section is already the fallback. Asked, because the shape suggests
|
|
303
|
+
* one: the first heading's top starts *at* the line rather than below it,
|
|
304
|
+
* so there is nothing for it to fail to reach.
|
|
305
|
+
*
|
|
306
|
+
* What this cannot do is tell trailing sections apart. If the last three
|
|
307
|
+
* share the final screenful, none of them can reach the line and the page
|
|
308
|
+
* has no scroll left to distinguish them, so all three collapse into "you
|
|
309
|
+
* are at the end". That is a fact about the page rather than a choice made
|
|
310
|
+
* here: the component cannot invent room to scroll. */
|
|
311
|
+
const active = this.atEnd ? sections.at(-1) : (passed.at(-1) ?? sections[0]);
|
|
312
|
+
this.markTo(active);
|
|
313
|
+
}
|
|
314
|
+
/** Where the destination is set, whether measured or asked for. */
|
|
315
|
+
markTo(active) {
|
|
316
|
+
for (const { link, target } of this.sections) {
|
|
317
|
+
if (target === active?.target) {
|
|
318
|
+
link.setAttribute('aria-current', 'location');
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
link.removeAttribute('aria-current');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/* And say so, which it did not.
|
|
325
|
+
*
|
|
326
|
+
* `reading` is derived from scroll position and was announced to nobody, so
|
|
327
|
+
* a consumer syncing a URL hash to the reading position had to poll for it:
|
|
328
|
+
* the one fact this component computes that nothing else on the page can,
|
|
329
|
+
* and the only way out of it was a `MutationObserver` on the anchors.
|
|
330
|
+
*
|
|
331
|
+
* Only on a change, because this runs from a scroll frame and an event per
|
|
332
|
+
* frame is not a signal, it is a firehose. */
|
|
333
|
+
const next = active?.target.id ?? '';
|
|
334
|
+
if (next !== this.reading) {
|
|
335
|
+
this.reading = next;
|
|
336
|
+
this.dispatchEvent(new HelixBeforeActivateEvent({ value: next }));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Whether the page has no more scroll left.
|
|
341
|
+
*
|
|
342
|
+
* `Math.ceil` and a pixel of slack, because a fractional device pixel ratio
|
|
343
|
+
* leaves the position a hair under the height at the very bottom and an exact
|
|
344
|
+
* comparison never fires.
|
|
345
|
+
*
|
|
346
|
+
* **All three numbers off the same element**, which this did not do: it added
|
|
347
|
+
* `scrollY` and `innerHeight`, both of the window, and compared them against
|
|
348
|
+
* `documentElement.scrollHeight`. Those are one coordinate system only while
|
|
349
|
+
* the viewport is what scrolls, and the body takes that job the moment it
|
|
350
|
+
* becomes a scroll container. Measured on a page whose body is
|
|
351
|
+
* `overflow: clip visible`, which is a pair CSS resolves by making the
|
|
352
|
+
* `visible` axis `auto`: `documentElement.scrollTop` read 1126 at the bottom
|
|
353
|
+
* of the page and `window.scrollY` read 240, so a contents rail never learned
|
|
354
|
+
* it had reached the end and its last item stayed unreachable. That page is
|
|
355
|
+
* this library's own base layer.
|
|
356
|
+
*
|
|
357
|
+
* `clientHeight` rather than `innerHeight` for the same reason, and because it
|
|
358
|
+
* is the one that excludes a scrollbar, which is what `scrollHeight` is
|
|
359
|
+
* measured against.
|
|
360
|
+
*/
|
|
361
|
+
get atEnd() {
|
|
362
|
+
const doc = globalThis.document?.scrollingElement ?? globalThis.document?.documentElement;
|
|
363
|
+
if (!doc) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
return Math.ceil(doc.scrollTop + doc.clientHeight) >= doc.scrollHeight - 1;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Gives every section the same reading line the marker uses.
|
|
370
|
+
*
|
|
371
|
+
* `offset` described where the top of the readable area is and was read by
|
|
372
|
+
* exactly one thing, the marker. Click a link and the browser put the heading
|
|
373
|
+
* against the very top of the window, underneath the sticky masthead whose
|
|
374
|
+
* height the consumer had just finished measuring for us. So the component
|
|
375
|
+
* knew about the chrome when deciding what you were reading and forgot about
|
|
376
|
+
* it when taking you somewhere, which is the same number meaning two things.
|
|
377
|
+
*
|
|
378
|
+
* `scroll-margin-block-start` rather than an interception, because it is the
|
|
379
|
+
* property the platform provides for exactly this and the UA honours it on
|
|
380
|
+
* ordinary fragment navigation, on `scrollIntoView`, and on a browser
|
|
381
|
+
* restoring a position. Nothing has to be taken over to get it.
|
|
382
|
+
*
|
|
383
|
+
* Written onto the consumer's element, like `aria-current` above and for the
|
|
384
|
+
* same reason: it is derived from a property describing the page's chrome,
|
|
385
|
+
* so no server rendered it and there is nothing to disagree with. Cleared
|
|
386
|
+
* rather than set to `0px` when there is no offset, so the common case leaves
|
|
387
|
+
* no attribute at all.
|
|
388
|
+
*/
|
|
389
|
+
reach() {
|
|
390
|
+
for (const { target } of this.sections) {
|
|
391
|
+
const element = target;
|
|
392
|
+
element.style.scrollMarginBlockStart = this.offset > 0 ? `${this.offset}px` : '';
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Watches every section, and re-marks whenever any of them crosses anything.
|
|
397
|
+
*
|
|
398
|
+
* The thresholds are what make the trigger frequent enough: an observer fires
|
|
399
|
+
* on entering and leaving, and a long section scrolled slowly crosses neither
|
|
400
|
+
* for a long time. Asking for a callback at each quarter of visibility is
|
|
401
|
+
* cheap and keeps the marker honest without a scroll listener.
|
|
402
|
+
*/
|
|
403
|
+
watch() {
|
|
404
|
+
this.observer?.disconnect();
|
|
405
|
+
/* Nothing to watch yet is not nothing to watch ever.
|
|
406
|
+
*
|
|
407
|
+
* This returned when no href resolved at first update, having already
|
|
408
|
+
* disconnected the observer, and only `slotchange` ever called it again: a
|
|
409
|
+
* page whose sections arrive after the nav does, which is every page that
|
|
410
|
+
* renders its content asynchronously, left the component dead for good. The
|
|
411
|
+
* observer is left disconnected either way, so re-running is the whole of
|
|
412
|
+
* the fix, and `updated` re-runs it while there is nothing to watch. */
|
|
413
|
+
if (this.sections.length === 0) {
|
|
414
|
+
this.observer = undefined;
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
this.observer = new IntersectionObserver(() => this.mark(), {
|
|
418
|
+
threshold: [0, 0.25, 0.5, 0.75, 1],
|
|
419
|
+
});
|
|
420
|
+
for (const { target } of this.sections) {
|
|
421
|
+
this.observer.observe(target);
|
|
422
|
+
}
|
|
423
|
+
this.reach();
|
|
424
|
+
this.mark();
|
|
425
|
+
}
|
|
426
|
+
disconnectedCallback() {
|
|
427
|
+
super.disconnectedCallback();
|
|
428
|
+
this.removeEventListener('click', this.handleClick);
|
|
429
|
+
globalThis.removeEventListener?.('scroll', this.handleScroll);
|
|
430
|
+
if (this.frame !== 0) {
|
|
431
|
+
globalThis.cancelAnimationFrame(this.frame);
|
|
432
|
+
this.frame = 0;
|
|
433
|
+
}
|
|
434
|
+
this.observer?.disconnect();
|
|
435
|
+
this.observer = undefined;
|
|
436
|
+
this.resize?.disconnect();
|
|
437
|
+
this.resize = undefined;
|
|
438
|
+
/* The landing timer, which nothing was clearing. It holds a closure over
|
|
439
|
+
this element for 1.2 seconds after it leaves the document and then
|
|
440
|
+
touches it, which on a route change is a write to a tree nobody is
|
|
441
|
+
looking at. Every other subscription here was already torn down, which is
|
|
442
|
+
what made this one easy to miss. */
|
|
443
|
+
clearTimeout(this.landed);
|
|
444
|
+
this.landed = undefined;
|
|
445
|
+
}
|
|
446
|
+
connectedCallback() {
|
|
447
|
+
super.connectedCallback();
|
|
448
|
+
this.addEventListener('click', this.handleClick);
|
|
449
|
+
globalThis.addEventListener?.('scroll', this.handleScroll, { passive: true });
|
|
450
|
+
/* Both observers are rebuilt on reconnection, and neither was.
|
|
451
|
+
*
|
|
452
|
+
* They were created in `firstUpdated`, which runs once for the life of the
|
|
453
|
+
* element, and torn down in `disconnectedCallback`, which runs every time it
|
|
454
|
+
* moves. So a outline moved in the DOM, which a framework does on any
|
|
455
|
+
* reorder, came back permanently frozen: `aria-current`, `current` and the
|
|
456
|
+
* traveling mark all stopped for the rest of the page's life with nothing
|
|
457
|
+
* to say why. Guarded on `hasUpdated`, because the first connection happens
|
|
458
|
+
* before the first render and there is nothing to watch yet. */
|
|
459
|
+
if (this.hasUpdated) {
|
|
460
|
+
this.watch();
|
|
461
|
+
this.resize = new ResizeObserver(() => this.indicator.place());
|
|
462
|
+
this.resize.observe(this);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
firstUpdated(changed) {
|
|
466
|
+
super.firstUpdated(changed);
|
|
467
|
+
this.watch();
|
|
468
|
+
/* The links are text in a column, so anything that changes the column's
|
|
469
|
+
width rewraps them and moves every one below. Nothing else would notice:
|
|
470
|
+
the intersection observer watches the sections, and a resize moves the
|
|
471
|
+
links without moving the page. */
|
|
472
|
+
this.resize = new ResizeObserver(() => this.indicator.place());
|
|
473
|
+
this.resize.observe(this);
|
|
474
|
+
}
|
|
475
|
+
updated(changed) {
|
|
476
|
+
super.updated(changed);
|
|
477
|
+
/* The reading line moved, so where the reader is may have changed without
|
|
478
|
+
the page having moved at all. Nothing else would notice: the observer
|
|
479
|
+
fires on sections crossing the viewport, and this changes what crossing
|
|
480
|
+
means rather than whether anything did. */
|
|
481
|
+
if (changed.has('offset')) {
|
|
482
|
+
this.reach();
|
|
483
|
+
this.mark();
|
|
484
|
+
}
|
|
485
|
+
/* Try again while there is nothing being watched. Cheap: `watch` returns on
|
|
486
|
+
the first line once it has something, and this is the only path back for
|
|
487
|
+
a nav whose sections had not rendered when it first looked. */
|
|
488
|
+
if (!this.observer) {
|
|
489
|
+
this.watch();
|
|
490
|
+
}
|
|
491
|
+
/* After the render that applied the new `aria-current`, not inside `mark()`
|
|
492
|
+
beside the attribute write. The current link is styled differently, and
|
|
493
|
+
on a narrow column the extra weight is enough to rewrap it, so measuring
|
|
494
|
+
before the style lands measures the wrong height. */
|
|
495
|
+
this.indicator.place();
|
|
496
|
+
}
|
|
497
|
+
render() {
|
|
498
|
+
return html `<nav part="nav" aria-label=${this.label}>
|
|
499
|
+
<slot @slotchange=${this.handleSlotChange}></slot>
|
|
500
|
+
<span part="indicator" aria-hidden="true"></span>
|
|
501
|
+
</nav>`;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
__decorate([
|
|
505
|
+
property()
|
|
506
|
+
], HelixHeadlessOutline.prototype, "label", void 0);
|
|
507
|
+
__decorate([
|
|
508
|
+
property({ type: Number })
|
|
509
|
+
], HelixHeadlessOutline.prototype, "offset", void 0);
|
|
510
|
+
__decorate([
|
|
511
|
+
property()
|
|
512
|
+
], HelixHeadlessOutline.prototype, "behavior", void 0);
|
|
513
|
+
__decorate([
|
|
514
|
+
state()
|
|
515
|
+
], HelixHeadlessOutline.prototype, "reading", void 0);
|
|
516
|
+
HelixHeadlessOutline = __decorate([
|
|
517
|
+
customElement('helix-headless-outline')
|
|
518
|
+
], HelixHeadlessOutline);
|
|
519
|
+
export { HelixHeadlessOutline };
|