@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,108 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerActiveInterface) & typeof HelixElement;
|
|
4
|
+
/**
|
|
5
|
+
* @summary A set of clips and the one player they take turns in.
|
|
6
|
+
*
|
|
7
|
+
* @description
|
|
8
|
+
* **The player is slotted, never rendered here.** A consumer's player carries
|
|
9
|
+
* their appearance, their extra controls, their captions and their poster
|
|
10
|
+
* treatment, and a playlist that rendered its own would either lose all of that
|
|
11
|
+
* or grow a property for each of them. `Sandbox` takes its preview and `Menu`
|
|
12
|
+
* its trigger the same way.
|
|
13
|
+
*
|
|
14
|
+
* **The entries are markup rather than an array property.** A list of objects
|
|
15
|
+
* would be the shorter API and it does not survive the trip: React skips every
|
|
16
|
+
* property it would have to apply imperatively while hydrating, so an array
|
|
17
|
+
* handed to a custom element is missing on the first paint of every
|
|
18
|
+
* server-rendered page. `using-the-elements.mdx` carries the measurement. Markup
|
|
19
|
+
* is also what makes each entry a real button with a real name in it.
|
|
20
|
+
*
|
|
21
|
+
* **Nothing here plays anything.** It sets `src` on the player and lets the
|
|
22
|
+
* player be a player. What the playlist owns is which entry is current, and it
|
|
23
|
+
* moves that on three signals: an entry pressed, `helix-next` or
|
|
24
|
+
* `helix-previous` from the player, and the player reaching the end.
|
|
25
|
+
*
|
|
26
|
+
* `ended` advances without asking, because a set of clips that stopped between
|
|
27
|
+
* each one is a set the reader has to nurse. The clip that follows starts where
|
|
28
|
+
* the last one left off, which is `autoplay` in the only sense that matters
|
|
29
|
+
* here: the reader already pressed play once.
|
|
30
|
+
*
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
* @status experimental
|
|
33
|
+
* @category media
|
|
34
|
+
* @aliases Playlist, Queue, Up next
|
|
35
|
+
* @documentation https://helix.fusion.dev/components/video-playlist
|
|
36
|
+
*
|
|
37
|
+
* @slot - The entries.
|
|
38
|
+
* @slot player - The `helix-video` they play in.
|
|
39
|
+
*
|
|
40
|
+
* @event {HelixActivateEvent} helix-activate - Fired once the current one has moved, so the element already holds it.
|
|
41
|
+
* @event {HelixBeforeActivateEvent} helix-before-activate - Fired before the current entry changes, and cancelable.
|
|
42
|
+
*
|
|
43
|
+
* @csspart items - The list of entries.
|
|
44
|
+
* @csspart player - The region holding the player.
|
|
45
|
+
*/
|
|
46
|
+
export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlaylist_base {
|
|
47
|
+
/**
|
|
48
|
+
* Accessible name for the list, which usually has no visible heading.
|
|
49
|
+
*/
|
|
50
|
+
label: string;
|
|
51
|
+
/**
|
|
52
|
+
* The entries, in the order the consumer wrote them.
|
|
53
|
+
*
|
|
54
|
+
* **Found by tag and filtered on the property, never on `[value]`.** The
|
|
55
|
+
* prefilter used to be an attribute selector, which is redundant beside the
|
|
56
|
+
* tag match and is the half that broke: React assigns a matching property
|
|
57
|
+
* rather than an attribute, so a playlist written in JSX carried no `value`
|
|
58
|
+
* attribute anywhere and `querySelectorAll('[value]')` returned nothing. Every
|
|
59
|
+
* entry vanished from the list, the active one included, and the component
|
|
60
|
+
* threw no error because an empty playlist is a legal playlist.
|
|
61
|
+
*
|
|
62
|
+
* Reading `one.value` answers the same question and answers it the same way
|
|
63
|
+
* whether the consumer wrote markup or set a property, which is the only form
|
|
64
|
+
* that works for both. Found by rendering the React twin of this family's
|
|
65
|
+
* examples beside the markup ones.
|
|
66
|
+
*/
|
|
67
|
+
private get items();
|
|
68
|
+
/**
|
|
69
|
+
* Whether the entry changed because somebody moved through the playlist.
|
|
70
|
+
*
|
|
71
|
+
* All three ways it moves are the reader going somewhere: pressing an entry,
|
|
72
|
+
* pressing next or previous, or a clip ending and the next one following it.
|
|
73
|
+
* Every one of those means play, and the one case that does not is the first
|
|
74
|
+
* render, where the playlist is simply pointing the player at its opening
|
|
75
|
+
* entry and nobody has asked for anything yet.
|
|
76
|
+
*
|
|
77
|
+
* A press is also what makes the play legal. Autoplay is refused when nothing
|
|
78
|
+
* the reader did asked for it, so starting here works because this is inside
|
|
79
|
+
* the gesture that chose the entry, and where it is refused anyway the player
|
|
80
|
+
* shows its start control and waits.
|
|
81
|
+
*/
|
|
82
|
+
private chased;
|
|
83
|
+
private get player();
|
|
84
|
+
/** Moves by one, and does nothing at either end. */
|
|
85
|
+
private step;
|
|
86
|
+
private readonly handleSelect;
|
|
87
|
+
private readonly handleNext;
|
|
88
|
+
private readonly handlePrevious;
|
|
89
|
+
private readonly handleEnded;
|
|
90
|
+
connectedCallback(): void;
|
|
91
|
+
disconnectedCallback(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Points the player at the current entry, and tells the entries which is on.
|
|
94
|
+
*
|
|
95
|
+
* In `updated` rather than in the handlers, so the three signals that move the
|
|
96
|
+
* current entry all arrive at one place that applies it. The server never runs
|
|
97
|
+
* this, which is correct: the entries are light DOM it cannot see, and what it
|
|
98
|
+
* renders is the player with whatever `src` the consumer wrote.
|
|
99
|
+
*/
|
|
100
|
+
protected updated(changed: PropertyValues): void;
|
|
101
|
+
protected render(): TemplateResult;
|
|
102
|
+
}
|
|
103
|
+
declare global {
|
|
104
|
+
interface HTMLElementTagNameMap {
|
|
105
|
+
'helix-headless-video-playlist': HelixHeadlessVideoPlaylist;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export {};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixElement, MixinControllerActive } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
/**
|
|
11
|
+
* @summary A set of clips and the one player they take turns in.
|
|
12
|
+
*
|
|
13
|
+
* @description
|
|
14
|
+
* **The player is slotted, never rendered here.** A consumer's player carries
|
|
15
|
+
* their appearance, their extra controls, their captions and their poster
|
|
16
|
+
* treatment, and a playlist that rendered its own would either lose all of that
|
|
17
|
+
* or grow a property for each of them. `Sandbox` takes its preview and `Menu`
|
|
18
|
+
* its trigger the same way.
|
|
19
|
+
*
|
|
20
|
+
* **The entries are markup rather than an array property.** A list of objects
|
|
21
|
+
* would be the shorter API and it does not survive the trip: React skips every
|
|
22
|
+
* property it would have to apply imperatively while hydrating, so an array
|
|
23
|
+
* handed to a custom element is missing on the first paint of every
|
|
24
|
+
* server-rendered page. `using-the-elements.mdx` carries the measurement. Markup
|
|
25
|
+
* is also what makes each entry a real button with a real name in it.
|
|
26
|
+
*
|
|
27
|
+
* **Nothing here plays anything.** It sets `src` on the player and lets the
|
|
28
|
+
* player be a player. What the playlist owns is which entry is current, and it
|
|
29
|
+
* moves that on three signals: an entry pressed, `helix-next` or
|
|
30
|
+
* `helix-previous` from the player, and the player reaching the end.
|
|
31
|
+
*
|
|
32
|
+
* `ended` advances without asking, because a set of clips that stopped between
|
|
33
|
+
* each one is a set the reader has to nurse. The clip that follows starts where
|
|
34
|
+
* the last one left off, which is `autoplay` in the only sense that matters
|
|
35
|
+
* here: the reader already pressed play once.
|
|
36
|
+
*
|
|
37
|
+
* @since 0.1.0
|
|
38
|
+
* @status experimental
|
|
39
|
+
* @category media
|
|
40
|
+
* @aliases Playlist, Queue, Up next
|
|
41
|
+
* @documentation https://helix.fusion.dev/components/video-playlist
|
|
42
|
+
*
|
|
43
|
+
* @slot - The entries.
|
|
44
|
+
* @slot player - The `helix-video` they play in.
|
|
45
|
+
*
|
|
46
|
+
* @event {HelixActivateEvent} helix-activate - Fired once the current one has moved, so the element already holds it.
|
|
47
|
+
* @event {HelixBeforeActivateEvent} helix-before-activate - Fired before the current entry changes, and cancelable.
|
|
48
|
+
*
|
|
49
|
+
* @csspart items - The list of entries.
|
|
50
|
+
* @csspart player - The region holding the player.
|
|
51
|
+
*/
|
|
52
|
+
let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinControllerActive(HelixElement) {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(...arguments);
|
|
55
|
+
/**
|
|
56
|
+
* Accessible name for the list, which usually has no visible heading.
|
|
57
|
+
*/
|
|
58
|
+
this.label = 'Playlist';
|
|
59
|
+
/*
|
|
60
|
+
* The player, whichever tag the consumer slotted.
|
|
61
|
+
*
|
|
62
|
+
* Asked of the slot rather than matched by tag name: the headless tier's
|
|
63
|
+
* player is `helix-headless-video` and the styled tier's is `helix-video`, and a
|
|
64
|
+
* consumer bringing a third under either name should still be driven. What is
|
|
65
|
+
* needed of it is `src`, which is the whole of the contract.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* Whether the entry changed because somebody moved through the playlist.
|
|
69
|
+
*
|
|
70
|
+
* All three ways it moves are the reader going somewhere: pressing an entry,
|
|
71
|
+
* pressing next or previous, or a clip ending and the next one following it.
|
|
72
|
+
* Every one of those means play, and the one case that does not is the first
|
|
73
|
+
* render, where the playlist is simply pointing the player at its opening
|
|
74
|
+
* entry and nobody has asked for anything yet.
|
|
75
|
+
*
|
|
76
|
+
* A press is also what makes the play legal. Autoplay is refused when nothing
|
|
77
|
+
* the reader did asked for it, so starting here works because this is inside
|
|
78
|
+
* the gesture that chose the entry, and where it is refused anyway the player
|
|
79
|
+
* shows its start control and waits.
|
|
80
|
+
*/
|
|
81
|
+
this.chased = false;
|
|
82
|
+
this.handleSelect = (event) => {
|
|
83
|
+
/* Stopped and not relayed. The entry says it was chosen, this element says
|
|
84
|
+
what is playing, and a consumer hearing both would hear one press twice
|
|
85
|
+
under two names.
|
|
86
|
+
*
|
|
87
|
+
* `stopImmediatePropagation`, not `stopPropagation`. The consumer's listener
|
|
88
|
+
* is on this same element, and stopping propagation only keeps an event off
|
|
89
|
+
* the *next* node: every listener already bound here still runs, so the
|
|
90
|
+
* entry's selection reached them anyway. Command carries the longer note,
|
|
91
|
+
* and the spec below is what caught this one. */
|
|
92
|
+
event.stopImmediatePropagation();
|
|
93
|
+
this.chased = true;
|
|
94
|
+
this.requestActive(event.detail.value);
|
|
95
|
+
};
|
|
96
|
+
this.handleNext = () => {
|
|
97
|
+
this.chased = true;
|
|
98
|
+
this.step(1);
|
|
99
|
+
};
|
|
100
|
+
this.handlePrevious = () => {
|
|
101
|
+
this.chased = true;
|
|
102
|
+
this.step(-1);
|
|
103
|
+
};
|
|
104
|
+
this.handleEnded = () => {
|
|
105
|
+
this.chased = true;
|
|
106
|
+
this.step(1);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The entries, in the order the consumer wrote them.
|
|
111
|
+
*
|
|
112
|
+
* **Found by tag and filtered on the property, never on `[value]`.** The
|
|
113
|
+
* prefilter used to be an attribute selector, which is redundant beside the
|
|
114
|
+
* tag match and is the half that broke: React assigns a matching property
|
|
115
|
+
* rather than an attribute, so a playlist written in JSX carried no `value`
|
|
116
|
+
* attribute anywhere and `querySelectorAll('[value]')` returned nothing. Every
|
|
117
|
+
* entry vanished from the list, the active one included, and the component
|
|
118
|
+
* threw no error because an empty playlist is a legal playlist.
|
|
119
|
+
*
|
|
120
|
+
* Reading `one.value` answers the same question and answers it the same way
|
|
121
|
+
* whether the consumer wrote markup or set a property, which is the only form
|
|
122
|
+
* that works for both. Found by rendering the React twin of this family's
|
|
123
|
+
* examples beside the markup ones.
|
|
124
|
+
*/
|
|
125
|
+
get items() {
|
|
126
|
+
return [
|
|
127
|
+
...this.querySelectorAll('helix-video-playlist-item, helix-headless-video-playlist-item'),
|
|
128
|
+
].filter((one) => one.value !== '');
|
|
129
|
+
}
|
|
130
|
+
get player() {
|
|
131
|
+
const slot = this.renderRoot?.querySelector('slot[name="player"]');
|
|
132
|
+
const first = slot?.assignedElements()[0];
|
|
133
|
+
return (first ?? null);
|
|
134
|
+
}
|
|
135
|
+
/** Moves by one, and does nothing at either end. */
|
|
136
|
+
step(by) {
|
|
137
|
+
const items = this.items.filter((one) => !one.disabled);
|
|
138
|
+
const at = items.findIndex((one) => one.value === this.resolveActive(items)?.value);
|
|
139
|
+
const next = items[at + by];
|
|
140
|
+
if (next) {
|
|
141
|
+
this.requestActive(next.value);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
connectedCallback() {
|
|
145
|
+
super.connectedCallback();
|
|
146
|
+
this.addEventListener('helix-select', this.handleSelect);
|
|
147
|
+
this.addEventListener('helix-next', this.handleNext);
|
|
148
|
+
this.addEventListener('helix-previous', this.handlePrevious);
|
|
149
|
+
/* `ended` is a media event and does not compose out of the player's shadow
|
|
150
|
+
root on its own. The player relays it, which is why listening here works
|
|
151
|
+
at all, and why this listens on the element rather than on a `<video>` it
|
|
152
|
+
has no reference to. */
|
|
153
|
+
this.addEventListener('ended', this.handleEnded);
|
|
154
|
+
}
|
|
155
|
+
disconnectedCallback() {
|
|
156
|
+
super.disconnectedCallback();
|
|
157
|
+
this.removeEventListener('helix-select', this.handleSelect);
|
|
158
|
+
this.removeEventListener('helix-next', this.handleNext);
|
|
159
|
+
this.removeEventListener('helix-previous', this.handlePrevious);
|
|
160
|
+
this.removeEventListener('ended', this.handleEnded);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Points the player at the current entry, and tells the entries which is on.
|
|
164
|
+
*
|
|
165
|
+
* In `updated` rather than in the handlers, so the three signals that move the
|
|
166
|
+
* current entry all arrive at one place that applies it. The server never runs
|
|
167
|
+
* this, which is correct: the entries are light DOM it cannot see, and what it
|
|
168
|
+
* renders is the player with whatever `src` the consumer wrote.
|
|
169
|
+
*/
|
|
170
|
+
updated(changed) {
|
|
171
|
+
super.updated?.(changed);
|
|
172
|
+
const items = this.items;
|
|
173
|
+
const current = this.resolveActive(items);
|
|
174
|
+
for (const item of items) {
|
|
175
|
+
item.current = item === current;
|
|
176
|
+
}
|
|
177
|
+
const player = this.player;
|
|
178
|
+
if (!player || !current) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
/* Only on a change. Writing `src` with the value it already holds reloads
|
|
182
|
+
the resource in every engine, so a playlist that assigned on each update
|
|
183
|
+
would restart the clip whenever anything else about it rendered. */
|
|
184
|
+
if (player.src !== current.src) {
|
|
185
|
+
player.src = current.src;
|
|
186
|
+
player.poster = current.poster;
|
|
187
|
+
player.label = current.text;
|
|
188
|
+
if (this.chased) {
|
|
189
|
+
/* After the player has rendered, not now. `src` here is the element's
|
|
190
|
+
property, and the `<video>` inside it only takes the new source on
|
|
191
|
+
its next render: playing at this point plays the outgoing clip for an
|
|
192
|
+
instant and the load that follows stops it again. Measured, and the
|
|
193
|
+
reading was a player left paused on the entry that had just been
|
|
194
|
+
chosen.
|
|
195
|
+
|
|
196
|
+
A microtask later is still inside the press that got us here, so the
|
|
197
|
+
platform still counts it as asked for. */
|
|
198
|
+
void player.updateComplete?.then(() => {
|
|
199
|
+
player.play();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
this.chased = false;
|
|
204
|
+
}
|
|
205
|
+
render() {
|
|
206
|
+
return html `<div part="player"><slot name="player"></slot></div>
|
|
207
|
+
<div part="items" role="list" aria-label=${this.label}>
|
|
208
|
+
<slot></slot>
|
|
209
|
+
</div>`;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
__decorate([
|
|
213
|
+
property()
|
|
214
|
+
], HelixHeadlessVideoPlaylist.prototype, "label", void 0);
|
|
215
|
+
HelixHeadlessVideoPlaylist = __decorate([
|
|
216
|
+
customElement('helix-headless-video-playlist')
|
|
217
|
+
], HelixHeadlessVideoPlaylist);
|
|
218
|
+
export { HelixHeadlessVideoPlaylist };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/virtual-list-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/virtual-list-root.js';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* @summary Renders the rows of a long set that are on screen, and no others.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* **The set arrives as data, which is what makes this a virtual list rather
|
|
8
|
+
* than a box that hides children.** `for` names a
|
|
9
|
+
* `<script type="application/json">` holding an array of objects, and a
|
|
10
|
+
* `<template>` child says what one row looks like: elements carrying
|
|
11
|
+
* `data-field="name"` are filled with that field of the item.
|
|
12
|
+
*
|
|
13
|
+
* Both halves are markup, and that is the whole reason for the shape.
|
|
14
|
+
* `VirtualizeController` says why the obvious alternatives are shut: an `items`
|
|
15
|
+
* property cannot survive React hydration, which skips every property it would
|
|
16
|
+
* have to apply imperatively, and light-DOM children exist by the time anything
|
|
17
|
+
* could decide not to create them. Text in the document is neither. A server
|
|
18
|
+
* emits it, React re-emits it as a child rather than applying it, and it is one
|
|
19
|
+
* node whatever the row count.
|
|
20
|
+
*
|
|
21
|
+
* **The same shape the charts already use**, `for="<id>"` pointing at markup the
|
|
22
|
+
* consumer wrote, and the difference is what is at the other end. A chart names
|
|
23
|
+
* a `<table>`, which is the accessible version of the picture and which a reader
|
|
24
|
+
* with no JavaScript still gets. Ten thousand rows cannot be that: the table
|
|
25
|
+
* would be the cost this exists to avoid. So the accessible version here is the
|
|
26
|
+
* rendered window plus `aria-setsize` and `aria-posinset`, which is what says
|
|
27
|
+
* "four thousand of ten thousand" rather than "four of twenty".
|
|
28
|
+
*
|
|
29
|
+
* **Rows are light DOM, deliberately.** They are cloned from the consumer's
|
|
30
|
+
* template and appended to the host, so a document rule reaches them, the base
|
|
31
|
+
* layer applies, and a class in the template means what it says. Cloned into
|
|
32
|
+
* the shadow root they would be markup the consumer wrote and could not style,
|
|
33
|
+
* which is the one thing a row template must not be.
|
|
34
|
+
*
|
|
35
|
+
* **Reading the source is client-only**, because a server has no document to
|
|
36
|
+
* query. The window renders empty from the server and fills on the first update,
|
|
37
|
+
* which is the same client-side enhancement rule every tree-reading component
|
|
38
|
+
* here follows.
|
|
39
|
+
*
|
|
40
|
+
* **On this tier the consumer gives the viewport its box**, and the component
|
|
41
|
+
* cannot do it for them: `helix/headless` ships no stylesheet, and a height is a
|
|
42
|
+
* decision rather than a fact this could publish. So a base list wants two rules
|
|
43
|
+
* before it is a list at all:
|
|
44
|
+
*
|
|
45
|
+
* ```css
|
|
46
|
+
* helix-headless-virtual-list::part(viewport) {
|
|
47
|
+
* block-size: 12rem;
|
|
48
|
+
* overflow-y: auto;
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* **Without them it is not a scroller**, and the failure is worse than it
|
|
53
|
+
* sounds. `scrollTop` is always zero, so nothing ever windows; and the box has
|
|
54
|
+
* no height of its own, so `clientHeight` is a function of the rows just drawn.
|
|
55
|
+
* Measuring then moves what is being measured, and the two need not converge:
|
|
56
|
+
* measured in Chromium at 500 rows, it settled into a two-cycle between 378 and
|
|
57
|
+
* 379 rows and stayed there, at two and a half thousand passes in eight seconds.
|
|
58
|
+
* `VirtualizeController` gives up after ten passes and says so, so this now
|
|
59
|
+
* draws a bad window rather than stopping the page, but a bad window is still
|
|
60
|
+
* the answer until the rules are written.
|
|
61
|
+
*
|
|
62
|
+
* The styled tier writes them for you, with `--virtual-list-block-size`.
|
|
63
|
+
*
|
|
64
|
+
* @category layout
|
|
65
|
+
* @since 0.1.0
|
|
66
|
+
* @status experimental
|
|
67
|
+
* @documentation https://helix.fusion.dev/components/virtual-list
|
|
68
|
+
*
|
|
69
|
+
* @csspart after - The space standing in for rows below the window.
|
|
70
|
+
* @csspart before - The space standing in for rows above the window.
|
|
71
|
+
* @csspart viewport - The scrolling box. Needs a block size and an overflow on this tier.
|
|
72
|
+
*/
|
|
73
|
+
export declare class HelixHeadlessVirtualList extends HelixElement {
|
|
74
|
+
/**
|
|
75
|
+
* How tall one row is, in pixels, used to map scroll offset to an index.
|
|
76
|
+
*/
|
|
77
|
+
estimate: number;
|
|
78
|
+
/**
|
|
79
|
+
* Id of the element holding the rows, as JSON in a script.
|
|
80
|
+
*/
|
|
81
|
+
for: string;
|
|
82
|
+
/**
|
|
83
|
+
* What the list is called, since the rendered rows are only a window on it.
|
|
84
|
+
*/
|
|
85
|
+
label: string;
|
|
86
|
+
/**
|
|
87
|
+
* The parsed source.
|
|
88
|
+
*
|
|
89
|
+
* Empty on a server, which has nothing to read.
|
|
90
|
+
*/
|
|
91
|
+
private rows;
|
|
92
|
+
private readonly virtualize;
|
|
93
|
+
/**
|
|
94
|
+
* Reads the source named by `for`, once the document exists to read.
|
|
95
|
+
*
|
|
96
|
+
* `firstUpdated` rather than `connectedCallback`, per the server rules: the
|
|
97
|
+
* shim has attributes and no tree, so `getElementById` is undefined there.
|
|
98
|
+
* A source that is missing or not an array leaves the list empty rather than
|
|
99
|
+
* throwing, because a consumer whose data has not arrived yet is the ordinary
|
|
100
|
+
* case and an empty list says so by itself.
|
|
101
|
+
*/
|
|
102
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
103
|
+
protected updated(changed: PropertyValues): void;
|
|
104
|
+
private read;
|
|
105
|
+
/**
|
|
106
|
+
* Puts the windowed rows into the light DOM, and takes the rest out.
|
|
107
|
+
*
|
|
108
|
+
* Cleared and re-cloned rather than reconciled, which is the simple thing and
|
|
109
|
+
* is measured rather than assumed to be enough: a window is twenty-odd rows
|
|
110
|
+
* and cloning that many is well under a frame. A reconciler here would be
|
|
111
|
+
* carrying keys and identity for a set nothing selects into, and is the kind
|
|
112
|
+
* of optimisation to add against a measurement rather than ahead of one.
|
|
113
|
+
*/
|
|
114
|
+
private place;
|
|
115
|
+
protected render(): TemplateResult;
|
|
116
|
+
}
|
|
117
|
+
declare global {
|
|
118
|
+
interface HTMLElementTagNameMap {
|
|
119
|
+
'helix-headless-virtual-list': HelixHeadlessVirtualList;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixElement, VirtualizeController } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html } from 'lit';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
|
+
/**
|
|
11
|
+
* @summary Renders the rows of a long set that are on screen, and no others.
|
|
12
|
+
*
|
|
13
|
+
* @description
|
|
14
|
+
* **The set arrives as data, which is what makes this a virtual list rather
|
|
15
|
+
* than a box that hides children.** `for` names a
|
|
16
|
+
* `<script type="application/json">` holding an array of objects, and a
|
|
17
|
+
* `<template>` child says what one row looks like: elements carrying
|
|
18
|
+
* `data-field="name"` are filled with that field of the item.
|
|
19
|
+
*
|
|
20
|
+
* Both halves are markup, and that is the whole reason for the shape.
|
|
21
|
+
* `VirtualizeController` says why the obvious alternatives are shut: an `items`
|
|
22
|
+
* property cannot survive React hydration, which skips every property it would
|
|
23
|
+
* have to apply imperatively, and light-DOM children exist by the time anything
|
|
24
|
+
* could decide not to create them. Text in the document is neither. A server
|
|
25
|
+
* emits it, React re-emits it as a child rather than applying it, and it is one
|
|
26
|
+
* node whatever the row count.
|
|
27
|
+
*
|
|
28
|
+
* **The same shape the charts already use**, `for="<id>"` pointing at markup the
|
|
29
|
+
* consumer wrote, and the difference is what is at the other end. A chart names
|
|
30
|
+
* a `<table>`, which is the accessible version of the picture and which a reader
|
|
31
|
+
* with no JavaScript still gets. Ten thousand rows cannot be that: the table
|
|
32
|
+
* would be the cost this exists to avoid. So the accessible version here is the
|
|
33
|
+
* rendered window plus `aria-setsize` and `aria-posinset`, which is what says
|
|
34
|
+
* "four thousand of ten thousand" rather than "four of twenty".
|
|
35
|
+
*
|
|
36
|
+
* **Rows are light DOM, deliberately.** They are cloned from the consumer's
|
|
37
|
+
* template and appended to the host, so a document rule reaches them, the base
|
|
38
|
+
* layer applies, and a class in the template means what it says. Cloned into
|
|
39
|
+
* the shadow root they would be markup the consumer wrote and could not style,
|
|
40
|
+
* which is the one thing a row template must not be.
|
|
41
|
+
*
|
|
42
|
+
* **Reading the source is client-only**, because a server has no document to
|
|
43
|
+
* query. The window renders empty from the server and fills on the first update,
|
|
44
|
+
* which is the same client-side enhancement rule every tree-reading component
|
|
45
|
+
* here follows.
|
|
46
|
+
*
|
|
47
|
+
* **On this tier the consumer gives the viewport its box**, and the component
|
|
48
|
+
* cannot do it for them: `helix/headless` ships no stylesheet, and a height is a
|
|
49
|
+
* decision rather than a fact this could publish. So a base list wants two rules
|
|
50
|
+
* before it is a list at all:
|
|
51
|
+
*
|
|
52
|
+
* ```css
|
|
53
|
+
* helix-headless-virtual-list::part(viewport) {
|
|
54
|
+
* block-size: 12rem;
|
|
55
|
+
* overflow-y: auto;
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* **Without them it is not a scroller**, and the failure is worse than it
|
|
60
|
+
* sounds. `scrollTop` is always zero, so nothing ever windows; and the box has
|
|
61
|
+
* no height of its own, so `clientHeight` is a function of the rows just drawn.
|
|
62
|
+
* Measuring then moves what is being measured, and the two need not converge:
|
|
63
|
+
* measured in Chromium at 500 rows, it settled into a two-cycle between 378 and
|
|
64
|
+
* 379 rows and stayed there, at two and a half thousand passes in eight seconds.
|
|
65
|
+
* `VirtualizeController` gives up after ten passes and says so, so this now
|
|
66
|
+
* draws a bad window rather than stopping the page, but a bad window is still
|
|
67
|
+
* the answer until the rules are written.
|
|
68
|
+
*
|
|
69
|
+
* The styled tier writes them for you, with `--virtual-list-block-size`.
|
|
70
|
+
*
|
|
71
|
+
* @category layout
|
|
72
|
+
* @since 0.1.0
|
|
73
|
+
* @status experimental
|
|
74
|
+
* @documentation https://helix.fusion.dev/components/virtual-list
|
|
75
|
+
*
|
|
76
|
+
* @csspart after - The space standing in for rows below the window.
|
|
77
|
+
* @csspart before - The space standing in for rows above the window.
|
|
78
|
+
* @csspart viewport - The scrolling box. Needs a block size and an overflow on this tier.
|
|
79
|
+
*/
|
|
80
|
+
let HelixHeadlessVirtualList = class HelixHeadlessVirtualList extends HelixElement {
|
|
81
|
+
constructor() {
|
|
82
|
+
super(...arguments);
|
|
83
|
+
/**
|
|
84
|
+
* How tall one row is, in pixels, used to map scroll offset to an index.
|
|
85
|
+
*/
|
|
86
|
+
this.estimate = 32;
|
|
87
|
+
/**
|
|
88
|
+
* Id of the element holding the rows, as JSON in a script.
|
|
89
|
+
*/
|
|
90
|
+
this.for = '';
|
|
91
|
+
/**
|
|
92
|
+
* What the list is called, since the rendered rows are only a window on it.
|
|
93
|
+
*/
|
|
94
|
+
this.label = '';
|
|
95
|
+
/**
|
|
96
|
+
* The parsed source.
|
|
97
|
+
*
|
|
98
|
+
* Empty on a server, which has nothing to read.
|
|
99
|
+
*/
|
|
100
|
+
this.rows = [];
|
|
101
|
+
/* No `overscan` property. The controller's four is a statement about a frame
|
|
102
|
+
of fast scrolling at a 32 pixel row, which is a fact about the platform
|
|
103
|
+
rather than a preference, and a knob here would be one more thing to get
|
|
104
|
+
wrong for no case anybody has. */
|
|
105
|
+
this.virtualize = new VirtualizeController(this, {
|
|
106
|
+
container: () => this.renderRoot.querySelector('[part="viewport"]'),
|
|
107
|
+
count: () => this.rows.length,
|
|
108
|
+
estimate: () => this.estimate,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Reads the source named by `for`, once the document exists to read.
|
|
113
|
+
*
|
|
114
|
+
* `firstUpdated` rather than `connectedCallback`, per the server rules: the
|
|
115
|
+
* shim has attributes and no tree, so `getElementById` is undefined there.
|
|
116
|
+
* A source that is missing or not an array leaves the list empty rather than
|
|
117
|
+
* throwing, because a consumer whose data has not arrived yet is the ordinary
|
|
118
|
+
* case and an empty list says so by itself.
|
|
119
|
+
*/
|
|
120
|
+
firstUpdated(changed) {
|
|
121
|
+
super.firstUpdated?.(changed);
|
|
122
|
+
this.read();
|
|
123
|
+
}
|
|
124
|
+
updated(changed) {
|
|
125
|
+
super.updated?.(changed);
|
|
126
|
+
if (changed.has('for')) {
|
|
127
|
+
this.read();
|
|
128
|
+
}
|
|
129
|
+
this.virtualize.measure();
|
|
130
|
+
this.place();
|
|
131
|
+
}
|
|
132
|
+
read() {
|
|
133
|
+
const source = this.for === '' ? null : this.ownerDocument.getElementById(this.for);
|
|
134
|
+
const text = source?.textContent ?? '';
|
|
135
|
+
if (text.trim() === '') {
|
|
136
|
+
this.rows = [];
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(text);
|
|
141
|
+
this.rows = Array.isArray(parsed) ? parsed : [];
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
/* Left empty rather than thrown. The source is a consumer's markup, and a
|
|
145
|
+
component that throws on it takes the page down over a comma. */
|
|
146
|
+
this.rows = [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Puts the windowed rows into the light DOM, and takes the rest out.
|
|
151
|
+
*
|
|
152
|
+
* Cleared and re-cloned rather than reconciled, which is the simple thing and
|
|
153
|
+
* is measured rather than assumed to be enough: a window is twenty-odd rows
|
|
154
|
+
* and cloning that many is well under a frame. A reconciler here would be
|
|
155
|
+
* carrying keys and identity for a set nothing selects into, and is the kind
|
|
156
|
+
* of optimisation to add against a measurement rather than ahead of one.
|
|
157
|
+
*/
|
|
158
|
+
place() {
|
|
159
|
+
const template = this.querySelector('template');
|
|
160
|
+
if (!template) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
for (const stale of [...this.children]) {
|
|
164
|
+
if (stale !== template) {
|
|
165
|
+
stale.remove();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const { first, last } = this.virtualize.window;
|
|
169
|
+
const total = this.rows.length;
|
|
170
|
+
const fragment = this.ownerDocument.createDocumentFragment();
|
|
171
|
+
for (let at = first; at < last; at += 1) {
|
|
172
|
+
const row = this.rows[at];
|
|
173
|
+
if (!row) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const clone = template.content.cloneNode(true);
|
|
177
|
+
for (const hole of clone.querySelectorAll('[data-field]')) {
|
|
178
|
+
hole.textContent = row[hole.dataset.field ?? ''] ?? '';
|
|
179
|
+
}
|
|
180
|
+
/* The real index and the real total, not the position in the window. A
|
|
181
|
+
reader arrowing through this should hear where they are in the set. */
|
|
182
|
+
for (const element of clone.children) {
|
|
183
|
+
element.setAttribute('role', 'listitem');
|
|
184
|
+
element.setAttribute('aria-posinset', String(at + 1));
|
|
185
|
+
element.setAttribute('aria-setsize', String(total));
|
|
186
|
+
}
|
|
187
|
+
fragment.append(clone);
|
|
188
|
+
}
|
|
189
|
+
this.append(fragment);
|
|
190
|
+
}
|
|
191
|
+
render() {
|
|
192
|
+
const { after, before } = this.virtualize.window;
|
|
193
|
+
return html `<div
|
|
194
|
+
data-scroller
|
|
195
|
+
part="viewport"
|
|
196
|
+
role="list"
|
|
197
|
+
aria-label=${this.label}
|
|
198
|
+
@scroll=${() => this.virtualize.measure()}
|
|
199
|
+
>
|
|
200
|
+
<div part="before" style="block-size: ${before}px"></div>
|
|
201
|
+
<slot></slot>
|
|
202
|
+
<div part="after" style="block-size: ${after}px"></div>
|
|
203
|
+
</div>`;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
__decorate([
|
|
207
|
+
property({ type: Number })
|
|
208
|
+
], HelixHeadlessVirtualList.prototype, "estimate", void 0);
|
|
209
|
+
__decorate([
|
|
210
|
+
property()
|
|
211
|
+
], HelixHeadlessVirtualList.prototype, "for", void 0);
|
|
212
|
+
__decorate([
|
|
213
|
+
property()
|
|
214
|
+
], HelixHeadlessVirtualList.prototype, "label", void 0);
|
|
215
|
+
__decorate([
|
|
216
|
+
state()
|
|
217
|
+
], HelixHeadlessVirtualList.prototype, "rows", void 0);
|
|
218
|
+
HelixHeadlessVirtualList = __decorate([
|
|
219
|
+
customElement('helix-headless-virtual-list')
|
|
220
|
+
], HelixHeadlessVirtualList);
|
|
221
|
+
export { HelixHeadlessVirtualList };
|