@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,101 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type TemplateResult } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* @summary One entry in a video playlist.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* **It holds what the player needs and nothing about playing.** A source, a
|
|
8
|
+
* poster and a name: the entry knows which file it stands for, and the playlist
|
|
9
|
+
* is what hands that to a player. Nothing here touches a `<video>`, which is
|
|
10
|
+
* what keeps a playlist usable with a player the consumer configured
|
|
11
|
+
* themselves.
|
|
12
|
+
*
|
|
13
|
+
* **A real `<button>`, in a `listitem`.** A playlist is a list of things to
|
|
14
|
+
* choose, and the list roles are the honest ones: `list` and `listitem` say how
|
|
15
|
+
* many there are and where you are in them, which `listbox` would too, at the
|
|
16
|
+
* cost of the pattern a listbox obliges. `Option` records that trap in full,
|
|
17
|
+
* that a listbox has to keep focus elsewhere and say which item is current with
|
|
18
|
+
* `aria-activedescendant`. Nothing here needs that, because focus really moves
|
|
19
|
+
* to the button.
|
|
20
|
+
*
|
|
21
|
+
* `aria-current` rather than `aria-selected`, which belongs to the roles that
|
|
22
|
+
* have a selection. What is true of the entry playing now is that it is the one
|
|
23
|
+
* the player is on.
|
|
24
|
+
*
|
|
25
|
+
* @since 0.1.0
|
|
26
|
+
* @status experimental
|
|
27
|
+
* @category media
|
|
28
|
+
* @documentation https://helix.fusion.dev/components/video-playlist
|
|
29
|
+
*
|
|
30
|
+
* @slot - The entry's name.
|
|
31
|
+
* @slot media - A thumbnail. Falls back to an image of `poster`.
|
|
32
|
+
* @slot meta - After the name. A duration, or a date.
|
|
33
|
+
*
|
|
34
|
+
* @event {HelixSelectEvent} helix-select - Fired when the entry is chosen. Cancelable: refusing it takes the decision and nothing moves.
|
|
35
|
+
*
|
|
36
|
+
* @csspart container - The `listitem`, which the button sits in.
|
|
37
|
+
* @csspart item - The row, which is a button.
|
|
38
|
+
* @csspart label - The name and whatever follows it.
|
|
39
|
+
* @csspart media - The leading column.
|
|
40
|
+
* @csspart thumbnail - The image `poster` falls back to.
|
|
41
|
+
*/
|
|
42
|
+
export declare class HelixHeadlessVideoPlaylistItem extends HelixElement {
|
|
43
|
+
/**
|
|
44
|
+
* What identifies the entry, and what `active` is compared against.
|
|
45
|
+
*/
|
|
46
|
+
value: string;
|
|
47
|
+
/**
|
|
48
|
+
* The file this entry plays.
|
|
49
|
+
*/
|
|
50
|
+
src: string;
|
|
51
|
+
/**
|
|
52
|
+
* What covers the picture before this entry plays, and the row's thumbnail.
|
|
53
|
+
*
|
|
54
|
+
* One property doing both, because they are the same picture: the frame that
|
|
55
|
+
* stands for the clip stands for it in the list too. It is the `media` slot's
|
|
56
|
+
* fallback rather than something drawn beside it, which is this library's rule
|
|
57
|
+
* for a content property, so a consumer with something better to show slots it
|
|
58
|
+
* and the two can never both appear.
|
|
59
|
+
*/
|
|
60
|
+
poster: string;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the entry can be chosen.
|
|
63
|
+
*/
|
|
64
|
+
disabled: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the player is on this entry.
|
|
67
|
+
*
|
|
68
|
+
* Written by the playlist rather than by the entry, the way an option's
|
|
69
|
+
* selection is: an entry that made itself current would be a second answer to
|
|
70
|
+
* which one is playing.
|
|
71
|
+
*/
|
|
72
|
+
current: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* The entry's name, which is the words and not everything in it.
|
|
75
|
+
*
|
|
76
|
+
* `textContent` was what this used, and it takes the whole subtree: a row with
|
|
77
|
+
* a duration in `meta` reported "His Girl Friday0:06", and that string went to
|
|
78
|
+
* the player and on to the lock screen. Only the nodes the consumer left
|
|
79
|
+
* unslotted are the name; anything named is a column beside it.
|
|
80
|
+
*
|
|
81
|
+
* Light DOM rather than `assignedNodes`, so it answers on the server too,
|
|
82
|
+
* where there is no slot to ask. `Option.text` reads its own the same way.
|
|
83
|
+
*/
|
|
84
|
+
get text(): string;
|
|
85
|
+
private readonly choose;
|
|
86
|
+
/**
|
|
87
|
+
* What stands for the row: a thumbnail where there is one, and nothing where
|
|
88
|
+
* there is not.
|
|
89
|
+
*
|
|
90
|
+
* A seam rather than inline, so a tier can draw a duration badge over it or
|
|
91
|
+
* a placeholder in its place without reimplementing the row. A consumer
|
|
92
|
+
* slots `media` for the same reasons and neither has to know about the other.
|
|
93
|
+
*/
|
|
94
|
+
protected renderMedia(): TemplateResult;
|
|
95
|
+
protected render(): TemplateResult;
|
|
96
|
+
}
|
|
97
|
+
declare global {
|
|
98
|
+
interface HTMLElementTagNameMap {
|
|
99
|
+
'helix-headless-video-playlist-item': HelixHeadlessVideoPlaylistItem;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixElement, HelixSelectEvent } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
/**
|
|
11
|
+
* @summary One entry in a video playlist.
|
|
12
|
+
*
|
|
13
|
+
* @description
|
|
14
|
+
* **It holds what the player needs and nothing about playing.** A source, a
|
|
15
|
+
* poster and a name: the entry knows which file it stands for, and the playlist
|
|
16
|
+
* is what hands that to a player. Nothing here touches a `<video>`, which is
|
|
17
|
+
* what keeps a playlist usable with a player the consumer configured
|
|
18
|
+
* themselves.
|
|
19
|
+
*
|
|
20
|
+
* **A real `<button>`, in a `listitem`.** A playlist is a list of things to
|
|
21
|
+
* choose, and the list roles are the honest ones: `list` and `listitem` say how
|
|
22
|
+
* many there are and where you are in them, which `listbox` would too, at the
|
|
23
|
+
* cost of the pattern a listbox obliges. `Option` records that trap in full,
|
|
24
|
+
* that a listbox has to keep focus elsewhere and say which item is current with
|
|
25
|
+
* `aria-activedescendant`. Nothing here needs that, because focus really moves
|
|
26
|
+
* to the button.
|
|
27
|
+
*
|
|
28
|
+
* `aria-current` rather than `aria-selected`, which belongs to the roles that
|
|
29
|
+
* have a selection. What is true of the entry playing now is that it is the one
|
|
30
|
+
* the player is on.
|
|
31
|
+
*
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
* @status experimental
|
|
34
|
+
* @category media
|
|
35
|
+
* @documentation https://helix.fusion.dev/components/video-playlist
|
|
36
|
+
*
|
|
37
|
+
* @slot - The entry's name.
|
|
38
|
+
* @slot media - A thumbnail. Falls back to an image of `poster`.
|
|
39
|
+
* @slot meta - After the name. A duration, or a date.
|
|
40
|
+
*
|
|
41
|
+
* @event {HelixSelectEvent} helix-select - Fired when the entry is chosen. Cancelable: refusing it takes the decision and nothing moves.
|
|
42
|
+
*
|
|
43
|
+
* @csspart container - The `listitem`, which the button sits in.
|
|
44
|
+
* @csspart item - The row, which is a button.
|
|
45
|
+
* @csspart label - The name and whatever follows it.
|
|
46
|
+
* @csspart media - The leading column.
|
|
47
|
+
* @csspart thumbnail - The image `poster` falls back to.
|
|
48
|
+
*/
|
|
49
|
+
let HelixHeadlessVideoPlaylistItem = class HelixHeadlessVideoPlaylistItem extends HelixElement {
|
|
50
|
+
constructor() {
|
|
51
|
+
super(...arguments);
|
|
52
|
+
/**
|
|
53
|
+
* What identifies the entry, and what `active` is compared against.
|
|
54
|
+
*/
|
|
55
|
+
this.value = '';
|
|
56
|
+
/**
|
|
57
|
+
* The file this entry plays.
|
|
58
|
+
*/
|
|
59
|
+
this.src = '';
|
|
60
|
+
/**
|
|
61
|
+
* What covers the picture before this entry plays, and the row's thumbnail.
|
|
62
|
+
*
|
|
63
|
+
* One property doing both, because they are the same picture: the frame that
|
|
64
|
+
* stands for the clip stands for it in the list too. It is the `media` slot's
|
|
65
|
+
* fallback rather than something drawn beside it, which is this library's rule
|
|
66
|
+
* for a content property, so a consumer with something better to show slots it
|
|
67
|
+
* and the two can never both appear.
|
|
68
|
+
*/
|
|
69
|
+
this.poster = '';
|
|
70
|
+
/**
|
|
71
|
+
* Whether the entry can be chosen.
|
|
72
|
+
*/
|
|
73
|
+
this.disabled = false;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the player is on this entry.
|
|
76
|
+
*
|
|
77
|
+
* Written by the playlist rather than by the entry, the way an option's
|
|
78
|
+
* selection is: an entry that made itself current would be a second answer to
|
|
79
|
+
* which one is playing.
|
|
80
|
+
*/
|
|
81
|
+
this.current = false;
|
|
82
|
+
this.choose = () => {
|
|
83
|
+
if (this.disabled) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The entry's name, which is the words and not everything in it.
|
|
91
|
+
*
|
|
92
|
+
* `textContent` was what this used, and it takes the whole subtree: a row with
|
|
93
|
+
* a duration in `meta` reported "His Girl Friday0:06", and that string went to
|
|
94
|
+
* the player and on to the lock screen. Only the nodes the consumer left
|
|
95
|
+
* unslotted are the name; anything named is a column beside it.
|
|
96
|
+
*
|
|
97
|
+
* Light DOM rather than `assignedNodes`, so it answers on the server too,
|
|
98
|
+
* where there is no slot to ask. `Option.text` reads its own the same way.
|
|
99
|
+
*/
|
|
100
|
+
get text() {
|
|
101
|
+
return [...this.childNodes]
|
|
102
|
+
.filter((node) => !(node instanceof Element) || !node.hasAttribute('slot'))
|
|
103
|
+
.map((node) => node.textContent ?? '')
|
|
104
|
+
.join('')
|
|
105
|
+
.trim();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* What stands for the row: a thumbnail where there is one, and nothing where
|
|
109
|
+
* there is not.
|
|
110
|
+
*
|
|
111
|
+
* A seam rather than inline, so a tier can draw a duration badge over it or
|
|
112
|
+
* a placeholder in its place without reimplementing the row. A consumer
|
|
113
|
+
* slots `media` for the same reasons and neither has to know about the other.
|
|
114
|
+
*/
|
|
115
|
+
renderMedia() {
|
|
116
|
+
return html `<slot name="media">
|
|
117
|
+
${
|
|
118
|
+
// `alt=""`, because the row's own words already name it and a
|
|
119
|
+
// thumbnail repeating them is the same thing said twice.
|
|
120
|
+
this.poster === '' ? nothing : html `<img part="thumbnail" alt="" src=${this.poster} />`}
|
|
121
|
+
</slot>`;
|
|
122
|
+
}
|
|
123
|
+
render() {
|
|
124
|
+
/* `aria-current="true"`, and absent rather than `"false"` otherwise. A
|
|
125
|
+
reader is told which one is playing; being told about each of the others
|
|
126
|
+
that it is not playing is the same fact eleven times. */
|
|
127
|
+
return html `<div part="container" role="listitem">
|
|
128
|
+
<button
|
|
129
|
+
part="item"
|
|
130
|
+
type="button"
|
|
131
|
+
aria-current=${this.current ? 'true' : nothing}
|
|
132
|
+
?disabled=${this.disabled}
|
|
133
|
+
@click=${this.choose}
|
|
134
|
+
>
|
|
135
|
+
<span part="media">${this.renderMedia()}</span>
|
|
136
|
+
<span part="label">
|
|
137
|
+
<slot></slot>
|
|
138
|
+
<slot name="meta"></slot>
|
|
139
|
+
</span>
|
|
140
|
+
</button>
|
|
141
|
+
</div>`;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
__decorate([
|
|
145
|
+
property()
|
|
146
|
+
], HelixHeadlessVideoPlaylistItem.prototype, "value", void 0);
|
|
147
|
+
__decorate([
|
|
148
|
+
property()
|
|
149
|
+
], HelixHeadlessVideoPlaylistItem.prototype, "src", void 0);
|
|
150
|
+
__decorate([
|
|
151
|
+
property()
|
|
152
|
+
], HelixHeadlessVideoPlaylistItem.prototype, "poster", void 0);
|
|
153
|
+
__decorate([
|
|
154
|
+
property({ reflect: true, type: Boolean })
|
|
155
|
+
], HelixHeadlessVideoPlaylistItem.prototype, "disabled", void 0);
|
|
156
|
+
__decorate([
|
|
157
|
+
property({ attribute: false })
|
|
158
|
+
], HelixHeadlessVideoPlaylistItem.prototype, "current", void 0);
|
|
159
|
+
HelixHeadlessVideoPlaylistItem = __decorate([
|
|
160
|
+
customElement('helix-headless-video-playlist-item')
|
|
161
|
+
], HelixHeadlessVideoPlaylistItem);
|
|
162
|
+
export { HelixHeadlessVideoPlaylistItem };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type TemplateResult } from 'lit';
|
|
3
|
+
import '../../menu/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* @summary The playlist's entries, as a menu inside the player's control bar.
|
|
6
|
+
*
|
|
7
|
+
* @description
|
|
8
|
+
* The entries again, reachable without leaving the picture. A playlist beside
|
|
9
|
+
* the player is a list a reader scrolls to; the same list in the control bar is
|
|
10
|
+
* one they open over the video, which is what a full-screen player has and what
|
|
11
|
+
* a phone has room for.
|
|
12
|
+
*
|
|
13
|
+
* **It is placed by the consumer, not rendered by the playlist**, and that is
|
|
14
|
+
* forced rather than chosen. A shadow root cannot project into a sibling's
|
|
15
|
+
* light DOM, so nothing the playlist renders can land inside the player's
|
|
16
|
+
* control bar. What can is an element the consumer writes there, which is what
|
|
17
|
+
* `Video`'s `controls` slot is for:
|
|
18
|
+
*
|
|
19
|
+
* ```html
|
|
20
|
+
* <helix-video-playlist>
|
|
21
|
+
* <helix-video slot="player" controls>
|
|
22
|
+
* <helix-video-playlist-menu slot="controls"></helix-video-playlist-menu>
|
|
23
|
+
* </helix-video>
|
|
24
|
+
* <helix-video-playlist-item value="one" src="/one.mp4">One</helix-video-playlist-item>
|
|
25
|
+
* </helix-video-playlist>
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* So it finds its playlist upward rather than being handed one, and that chain
|
|
29
|
+
* crosses a shadow boundary in the ordinary arrangement, which is why it is
|
|
30
|
+
* `closestInstance` and not `closest`.
|
|
31
|
+
*
|
|
32
|
+
* **Choosing dispatches `helix-select`, the same event an entry dispatches.**
|
|
33
|
+
* It never writes `active` on the playlist, which looks like the shorter path
|
|
34
|
+
* and would latch the controlled contract: a playlist assigned `active` once
|
|
35
|
+
* goes quiet and waits for a consumer who was never there. Announcing is what
|
|
36
|
+
* an entry does, the playlist already listens for it, and a consumer refusing a
|
|
37
|
+
* selection gets `preventDefault()` on one event rather than two paths that
|
|
38
|
+
* behave differently.
|
|
39
|
+
*
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
* @status experimental
|
|
42
|
+
* @category media
|
|
43
|
+
* @aliases Episode Menu, Queue Menu, Up Next
|
|
44
|
+
* @documentation https://helix.fusion.dev/components/video-playlist
|
|
45
|
+
*
|
|
46
|
+
* @slot trigger - Replaces the button that opens the menu.
|
|
47
|
+
*
|
|
48
|
+
* @event {HelixSelectEvent} helix-select - Fired with the chosen entry's value. The playlist listens for it. Cancelable: refusing it takes the decision and nothing moves.
|
|
49
|
+
*
|
|
50
|
+
* @csspart entry - One entry in the menu.
|
|
51
|
+
* @csspart entry-item - The row, which is an `<a>` where `href` is set.
|
|
52
|
+
* @csspart entry-mark - The trailing mark: a tick when checked, a caret when it opens
|
|
53
|
+
* @csspart entry-start - The leading column, which is a slot and is always there.
|
|
54
|
+
* @csspart icon - The glyph on the trigger.
|
|
55
|
+
* @csspart menu - The menu that holds the entries.
|
|
56
|
+
* @csspart menu-back - The control that returns from a submenu.
|
|
57
|
+
* @csspart menu-back-mark - The glyph on the back control of a submenu.
|
|
58
|
+
* @csspart menu-empty - Shown when there are no items to show.
|
|
59
|
+
* @csspart menu-header - The bar above a submenu, holding the way back and its name.
|
|
60
|
+
* @csspart menu-loading - Shown while the items are still on their way.
|
|
61
|
+
* @csspart menu-surface - The floating panel the menu is drawn on.
|
|
62
|
+
* @csspart menu-title - The name of the submenu that is showing.
|
|
63
|
+
* @csspart poster - An entry's still, where it has one.
|
|
64
|
+
* @csspart trigger - The button that opens it.
|
|
65
|
+
*/
|
|
66
|
+
export declare class HelixHeadlessVideoPlaylistMenu extends HelixElement {
|
|
67
|
+
#private;
|
|
68
|
+
/**
|
|
69
|
+
* The menu's accessible name, and its trigger's.
|
|
70
|
+
*
|
|
71
|
+
* "Episodes" suits a series and is wrong for a set of camera angles, and the
|
|
72
|
+
* component cannot know which one it is in.
|
|
73
|
+
*/
|
|
74
|
+
label: string;
|
|
75
|
+
/**
|
|
76
|
+
* Where the menu opens relative to its trigger.
|
|
77
|
+
*
|
|
78
|
+
* `top-end` because a control bar sits at the foot of the picture, and a menu
|
|
79
|
+
* opening downward from there opens off the player.
|
|
80
|
+
*/
|
|
81
|
+
placement: string;
|
|
82
|
+
connectedCallback(): void;
|
|
83
|
+
disconnectedCallback(): void;
|
|
84
|
+
/** The playlist this menu belongs to, found upward through the slots. */
|
|
85
|
+
private get playlist();
|
|
86
|
+
/** The playlist's entries, in the order it holds them. */
|
|
87
|
+
private get entries();
|
|
88
|
+
/**
|
|
89
|
+
* Watches the playlist for entries arriving, and re-reads it on every open.
|
|
90
|
+
*
|
|
91
|
+
* Two signals rather than one, because the two facts change on different
|
|
92
|
+
* clocks and only one of them is observable.
|
|
93
|
+
*
|
|
94
|
+
* **Which entries exist** is the playlist's light DOM, and a `MutationObserver`
|
|
95
|
+
* sees it directly.
|
|
96
|
+
*
|
|
97
|
+
* **Which entry is current** is `active`, a property with no attribute, so
|
|
98
|
+
* there is nothing to observe: events travel up from the playlist and this
|
|
99
|
+
* element is below it, and mirroring `active` to a `data-` name is ruled out
|
|
100
|
+
* on its own merits, `active` being a payload rather than a boolean fact.
|
|
101
|
+
* Mirroring it was written and reverted here: it put `data-active` onto every
|
|
102
|
+
* host, which `tree-root.ssr.spec.ts` caught, and the rule it broke was the
|
|
103
|
+
* one that stops a copied host coming back frozen.
|
|
104
|
+
*
|
|
105
|
+
* So it is re-read at the moment it is looked at. `helix-before-open` comes from the
|
|
106
|
+
* menu in this element's own shadow root, which is the one direction events
|
|
107
|
+
* do arrive from, and a closed menu showing a stale mark is a thing nobody
|
|
108
|
+
* can see.
|
|
109
|
+
*/
|
|
110
|
+
private watch;
|
|
111
|
+
private readonly reread;
|
|
112
|
+
private readonly choose;
|
|
113
|
+
protected render(): TemplateResult;
|
|
114
|
+
/** The menu itself, behind a method so a styled tier can render another one. */
|
|
115
|
+
protected renderMenu(): TemplateResult;
|
|
116
|
+
}
|
|
117
|
+
declare global {
|
|
118
|
+
interface HTMLElementTagNameMap {
|
|
119
|
+
'helix-headless-video-playlist-menu': HelixHeadlessVideoPlaylistMenu;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
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 { closestInstance, customElement, drawIcon, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { list } from '@fusion.dev/helix-icons';
|
|
9
|
+
import { nothing } from 'lit';
|
|
10
|
+
import { property } from 'lit/decorators.js';
|
|
11
|
+
import { html } from 'lit/static-html.js';
|
|
12
|
+
import { HelixHeadlessVideoPlaylist } from '../root/video-playlist-root.js';
|
|
13
|
+
import '../../menu/index.js';
|
|
14
|
+
const ITEMS = 'helix-video-playlist-item, helix-headless-video-playlist-item';
|
|
15
|
+
/**
|
|
16
|
+
* @summary The playlist's entries, as a menu inside the player's control bar.
|
|
17
|
+
*
|
|
18
|
+
* @description
|
|
19
|
+
* The entries again, reachable without leaving the picture. A playlist beside
|
|
20
|
+
* the player is a list a reader scrolls to; the same list in the control bar is
|
|
21
|
+
* one they open over the video, which is what a full-screen player has and what
|
|
22
|
+
* a phone has room for.
|
|
23
|
+
*
|
|
24
|
+
* **It is placed by the consumer, not rendered by the playlist**, and that is
|
|
25
|
+
* forced rather than chosen. A shadow root cannot project into a sibling's
|
|
26
|
+
* light DOM, so nothing the playlist renders can land inside the player's
|
|
27
|
+
* control bar. What can is an element the consumer writes there, which is what
|
|
28
|
+
* `Video`'s `controls` slot is for:
|
|
29
|
+
*
|
|
30
|
+
* ```html
|
|
31
|
+
* <helix-video-playlist>
|
|
32
|
+
* <helix-video slot="player" controls>
|
|
33
|
+
* <helix-video-playlist-menu slot="controls"></helix-video-playlist-menu>
|
|
34
|
+
* </helix-video>
|
|
35
|
+
* <helix-video-playlist-item value="one" src="/one.mp4">One</helix-video-playlist-item>
|
|
36
|
+
* </helix-video-playlist>
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* So it finds its playlist upward rather than being handed one, and that chain
|
|
40
|
+
* crosses a shadow boundary in the ordinary arrangement, which is why it is
|
|
41
|
+
* `closestInstance` and not `closest`.
|
|
42
|
+
*
|
|
43
|
+
* **Choosing dispatches `helix-select`, the same event an entry dispatches.**
|
|
44
|
+
* It never writes `active` on the playlist, which looks like the shorter path
|
|
45
|
+
* and would latch the controlled contract: a playlist assigned `active` once
|
|
46
|
+
* goes quiet and waits for a consumer who was never there. Announcing is what
|
|
47
|
+
* an entry does, the playlist already listens for it, and a consumer refusing a
|
|
48
|
+
* selection gets `preventDefault()` on one event rather than two paths that
|
|
49
|
+
* behave differently.
|
|
50
|
+
*
|
|
51
|
+
* @since 0.1.0
|
|
52
|
+
* @status experimental
|
|
53
|
+
* @category media
|
|
54
|
+
* @aliases Episode Menu, Queue Menu, Up Next
|
|
55
|
+
* @documentation https://helix.fusion.dev/components/video-playlist
|
|
56
|
+
*
|
|
57
|
+
* @slot trigger - Replaces the button that opens the menu.
|
|
58
|
+
*
|
|
59
|
+
* @event {HelixSelectEvent} helix-select - Fired with the chosen entry's value. The playlist listens for it. Cancelable: refusing it takes the decision and nothing moves.
|
|
60
|
+
*
|
|
61
|
+
* @csspart entry - One entry in the menu.
|
|
62
|
+
* @csspart entry-item - The row, which is an `<a>` where `href` is set.
|
|
63
|
+
* @csspart entry-mark - The trailing mark: a tick when checked, a caret when it opens
|
|
64
|
+
* @csspart entry-start - The leading column, which is a slot and is always there.
|
|
65
|
+
* @csspart icon - The glyph on the trigger.
|
|
66
|
+
* @csspart menu - The menu that holds the entries.
|
|
67
|
+
* @csspart menu-back - The control that returns from a submenu.
|
|
68
|
+
* @csspart menu-back-mark - The glyph on the back control of a submenu.
|
|
69
|
+
* @csspart menu-empty - Shown when there are no items to show.
|
|
70
|
+
* @csspart menu-header - The bar above a submenu, holding the way back and its name.
|
|
71
|
+
* @csspart menu-loading - Shown while the items are still on their way.
|
|
72
|
+
* @csspart menu-surface - The floating panel the menu is drawn on.
|
|
73
|
+
* @csspart menu-title - The name of the submenu that is showing.
|
|
74
|
+
* @csspart poster - An entry's still, where it has one.
|
|
75
|
+
* @csspart trigger - The button that opens it.
|
|
76
|
+
*/
|
|
77
|
+
let HelixHeadlessVideoPlaylistMenu = class HelixHeadlessVideoPlaylistMenu extends HelixElement {
|
|
78
|
+
constructor() {
|
|
79
|
+
super(...arguments);
|
|
80
|
+
/**
|
|
81
|
+
* The menu's accessible name, and its trigger's.
|
|
82
|
+
*
|
|
83
|
+
* "Episodes" suits a series and is wrong for a set of camera angles, and the
|
|
84
|
+
* component cannot know which one it is in.
|
|
85
|
+
*/
|
|
86
|
+
this.label = 'Episodes';
|
|
87
|
+
/**
|
|
88
|
+
* Where the menu opens relative to its trigger.
|
|
89
|
+
*
|
|
90
|
+
* `top-end` because a control bar sits at the foot of the picture, and a menu
|
|
91
|
+
* opening downward from there opens off the player.
|
|
92
|
+
*/
|
|
93
|
+
this.placement = 'top-end';
|
|
94
|
+
/* `requestUpdate` rather than a counter held in `@state`.
|
|
95
|
+
*
|
|
96
|
+
* The entries are the playlist's light DOM rather than this element's, so
|
|
97
|
+
* nothing about them schedules an update here: an entry added, removed, or
|
|
98
|
+
* made current changes a tree this element only looks at. A bumped field is
|
|
99
|
+
* the usual way to say "render again", and here it is a field nothing reads,
|
|
100
|
+
* which the linter is right to call out. Asking directly says the same thing
|
|
101
|
+
* with nothing to explain. */
|
|
102
|
+
this.reread = () => {
|
|
103
|
+
this.requestUpdate();
|
|
104
|
+
};
|
|
105
|
+
this.choose = (value) => {
|
|
106
|
+
this.dispatchEvent(new HelixSelectEvent({ value }));
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
#observer;
|
|
110
|
+
connectedCallback() {
|
|
111
|
+
super.connectedCallback();
|
|
112
|
+
/* After the first update, because the playlist is found by walking the tree
|
|
113
|
+
and the server has no tree to walk. */
|
|
114
|
+
this.updateComplete.then(() => this.watch());
|
|
115
|
+
}
|
|
116
|
+
disconnectedCallback() {
|
|
117
|
+
super.disconnectedCallback();
|
|
118
|
+
this.removeEventListener('helix-before-open', this.reread);
|
|
119
|
+
this.#observer?.disconnect();
|
|
120
|
+
this.#observer = undefined;
|
|
121
|
+
}
|
|
122
|
+
/* Resolved once, on the client, and never during a render.
|
|
123
|
+
*
|
|
124
|
+
* `render` reached for this through `closestInstance` at first, which walks
|
|
125
|
+
* `parentNode` and steps across a `ShadowRoot` by following its `host`. That
|
|
126
|
+
* names `ShadowRoot`, which the server has no global for, so every SSR of
|
|
127
|
+
* this element threw `ReferenceError: Can't find variable: ShadowRoot` from
|
|
128
|
+
* inside Lit's own result collector, three frames from anything recognisable.
|
|
129
|
+
*
|
|
130
|
+
* Which is the tree-inspection rule arriving as a crash rather than as a
|
|
131
|
+
* wrong answer: a component that walks the tree to render cannot render on a
|
|
132
|
+
* server, and the server has no tree to walk anyway. Held here instead, set
|
|
133
|
+
* after the first update, and `undefined` on the server, where an empty menu
|
|
134
|
+
* is the honest output. */
|
|
135
|
+
#playlist;
|
|
136
|
+
/** The playlist this menu belongs to, found upward through the slots. */
|
|
137
|
+
get playlist() {
|
|
138
|
+
return this.#playlist;
|
|
139
|
+
}
|
|
140
|
+
/** The playlist's entries, in the order it holds them. */
|
|
141
|
+
get entries() {
|
|
142
|
+
const playlist = this.playlist;
|
|
143
|
+
return playlist ? [...playlist.querySelectorAll(ITEMS)] : [];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Watches the playlist for entries arriving, and re-reads it on every open.
|
|
147
|
+
*
|
|
148
|
+
* Two signals rather than one, because the two facts change on different
|
|
149
|
+
* clocks and only one of them is observable.
|
|
150
|
+
*
|
|
151
|
+
* **Which entries exist** is the playlist's light DOM, and a `MutationObserver`
|
|
152
|
+
* sees it directly.
|
|
153
|
+
*
|
|
154
|
+
* **Which entry is current** is `active`, a property with no attribute, so
|
|
155
|
+
* there is nothing to observe: events travel up from the playlist and this
|
|
156
|
+
* element is below it, and mirroring `active` to a `data-` name is ruled out
|
|
157
|
+
* on its own merits, `active` being a payload rather than a boolean fact.
|
|
158
|
+
* Mirroring it was written and reverted here: it put `data-active` onto every
|
|
159
|
+
* host, which `tree-root.ssr.spec.ts` caught, and the rule it broke was the
|
|
160
|
+
* one that stops a copied host coming back frozen.
|
|
161
|
+
*
|
|
162
|
+
* So it is re-read at the moment it is looked at. `helix-before-open` comes from the
|
|
163
|
+
* menu in this element's own shadow root, which is the one direction events
|
|
164
|
+
* do arrive from, and a closed menu showing a stale mark is a thing nobody
|
|
165
|
+
* can see.
|
|
166
|
+
*/
|
|
167
|
+
watch() {
|
|
168
|
+
this.#playlist ??= closestInstance(this, HelixHeadlessVideoPlaylist) ?? undefined;
|
|
169
|
+
const playlist = this.#playlist;
|
|
170
|
+
if (!playlist || this.#observer) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.addEventListener('helix-before-open', this.reread);
|
|
174
|
+
this.#observer = new MutationObserver(this.reread);
|
|
175
|
+
this.#observer.observe(playlist, { childList: true, subtree: true });
|
|
176
|
+
this.reread();
|
|
177
|
+
}
|
|
178
|
+
render() {
|
|
179
|
+
return this.renderMenu();
|
|
180
|
+
}
|
|
181
|
+
/** The menu itself, behind a method so a styled tier can render another one. */
|
|
182
|
+
renderMenu() {
|
|
183
|
+
const active = this.playlist?.active;
|
|
184
|
+
return html `<${this.tag('menu')} part="menu"
|
|
185
|
+
exportparts="back: menu-back, back-mark: menu-back-mark, empty: menu-empty, header: menu-header,
|
|
186
|
+
loading: menu-loading, surface: menu-surface, title: menu-title" label=${this.label} placement=${this.placement}>
|
|
187
|
+
<slot name="trigger" slot="trigger">
|
|
188
|
+
<button part="trigger" type="button" aria-label=${this.label}>${drawIcon(list)}</button>
|
|
189
|
+
</slot>
|
|
190
|
+
${this.entries.map((entry) => html `<${this.tag('menu-item')}
|
|
191
|
+
part="entry"
|
|
192
|
+
exportparts="item: entry-item, mark: entry-mark, start: entry-start"
|
|
193
|
+
value=${entry.value}
|
|
194
|
+
aria-checked=${entry.value === active ? 'true' : 'false'}
|
|
195
|
+
?disabled=${entry.disabled}
|
|
196
|
+
role="menuitemradio"
|
|
197
|
+
@helix-select=${(event) => {
|
|
198
|
+
/* Stopped and re-announced from the host rather than let through.
|
|
199
|
+
What travels up is then this element's own event, so a consumer
|
|
200
|
+
listening on the menu sees what the playlist sees, and
|
|
201
|
+
`preventDefault()` refuses the selection in one place. */
|
|
202
|
+
event.stopPropagation();
|
|
203
|
+
this.choose(entry.value);
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
${
|
|
207
|
+
/* The still, where there is one, for the reason the list beside the
|
|
208
|
+
player shows one: a reader picks an episode by recognising it
|
|
209
|
+
faster than by reading its title, and this menu opens over the
|
|
210
|
+
picture where reading is hardest.
|
|
211
|
+
*
|
|
212
|
+
* In `start`, which is free: `Menu.Item` renders its tick *after*
|
|
213
|
+
* the label as `part="mark"`, so the leading column has nothing to
|
|
214
|
+
* collide with. `alt=""`, because the name is in the same row and a
|
|
215
|
+
* still repeating it is the same thing said twice, which is what
|
|
216
|
+
* `Video.Playlist.Item` already decided about its own thumbnail.
|
|
217
|
+
*
|
|
218
|
+
* Omitted rather than empty where there is no poster, an `<img>`
|
|
219
|
+
* with no `src` being a broken-image glyph in every engine. */
|
|
220
|
+
entry.poster === ''
|
|
221
|
+
? nothing
|
|
222
|
+
: html `<img alt="" part="poster" slot="start" src=${entry.poster} />`}
|
|
223
|
+
${entry.text}
|
|
224
|
+
</${this.tag('menu-item')}>`)}
|
|
225
|
+
</${this.tag('menu')}>`;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
__decorate([
|
|
229
|
+
property()
|
|
230
|
+
], HelixHeadlessVideoPlaylistMenu.prototype, "label", void 0);
|
|
231
|
+
__decorate([
|
|
232
|
+
property()
|
|
233
|
+
], HelixHeadlessVideoPlaylistMenu.prototype, "placement", void 0);
|
|
234
|
+
HelixHeadlessVideoPlaylistMenu = __decorate([
|
|
235
|
+
customElement('helix-headless-video-playlist-menu')
|
|
236
|
+
], HelixHeadlessVideoPlaylistMenu);
|
|
237
|
+
export { HelixHeadlessVideoPlaylistMenu };
|