@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,196 @@
|
|
|
1
|
+
import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
import '../../tag/index.js';
|
|
4
|
+
declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
5
|
+
/**
|
|
6
|
+
* @summary A control for choosing files, by pointer or by dropping them on it.
|
|
7
|
+
*
|
|
8
|
+
* @description
|
|
9
|
+
* **A real `<input type="file">`, kept and made invisible rather than
|
|
10
|
+
* replaced.** It is the only way to open a file picker: the dialog is a
|
|
11
|
+
* privileged action a script cannot start on its own, and it opens for a click
|
|
12
|
+
* on the input or on a `<label>` pointing at it. Every version of this component
|
|
13
|
+
* that draws its own button ends up calling `.click()` on a hidden input
|
|
14
|
+
* anyway, and then has two things a screen reader can find where there is one
|
|
15
|
+
* control.
|
|
16
|
+
*
|
|
17
|
+
* So the input stays, holds the label, the name, the validity and the focus,
|
|
18
|
+
* and the visible trigger is a `<label for>` rather than a button. A label is
|
|
19
|
+
* not focusable and does not need to be: the input is, and Enter and Space on it
|
|
20
|
+
* open the picker because that is what the platform does with a file input.
|
|
21
|
+
*
|
|
22
|
+
* **Dropping is why this is a component rather than an attribute.** A bare file
|
|
23
|
+
* input accepts a drop only on the input itself, which is invisible here, so the
|
|
24
|
+
* field is the drop target and it says when something is over it.
|
|
25
|
+
*
|
|
26
|
+
* **Files are not a value a consumer can hand back.** Every other control here
|
|
27
|
+
* takes the controlled and uncontrolled pair, and this one does not: a `FileList`
|
|
28
|
+
* cannot be constructed from markup, and a consumer writing `value` on a file
|
|
29
|
+
* input is the one thing browsers refuse outright, because a page that could
|
|
30
|
+
* name a file could read it. So this element owns what was chosen, announces it,
|
|
31
|
+
* and offers `clear()` for the one change a consumer can legitimately ask for.
|
|
32
|
+
*
|
|
33
|
+
* **The list under it is the control's own, not decoration.** A file input with
|
|
34
|
+
* files chosen reports "3 files" and nothing else, which is unusable for
|
|
35
|
+
* checking that the right ones were picked. The names are rendered, and each
|
|
36
|
+
* carries the way to take it back out.
|
|
37
|
+
*
|
|
38
|
+
* @since 0.1.0
|
|
39
|
+
* @status stable
|
|
40
|
+
* @category forms
|
|
41
|
+
* @aliases FileUpload, FilePicker, Dropzone, InputFile
|
|
42
|
+
* @documentation https://helix.fusion.dev/components/file-input
|
|
43
|
+
*
|
|
44
|
+
* @slot feedback - What is wrong with it.
|
|
45
|
+
* @slot hint - Help under the field.
|
|
46
|
+
* @slot trigger - What the reader presses to open the picker.
|
|
47
|
+
*
|
|
48
|
+
* @event {Event} change - Announced once the value has moved.
|
|
49
|
+
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves.
|
|
50
|
+
* @event {Event} input - Announced once the value has moved.
|
|
51
|
+
*
|
|
52
|
+
* @csspart control - The file input itself, which carries the label and validity.
|
|
53
|
+
* @csspart detail - Its name and its reading, stacked.
|
|
54
|
+
* @csspart feedback - What is wrong with it.
|
|
55
|
+
* @csspart field - The box that is pressed and dropped on.
|
|
56
|
+
* @csspart file - One chosen file.
|
|
57
|
+
* @csspart hint - Help under the field, while nothing is wrong.
|
|
58
|
+
* @csspart icon - A glyph this component draws.
|
|
59
|
+
* @csspart kind - What kind of file it is.
|
|
60
|
+
* @csspart kind-dismiss - The remove button, when `dismissible`.
|
|
61
|
+
* @csspart kind-dismiss-mark - The glyph on the dismiss control, where none is slotted.
|
|
62
|
+
* @csspart kind-prefix - The category before the label.
|
|
63
|
+
* @csspart label - The question, over the field.
|
|
64
|
+
* @csspart list - The chosen files.
|
|
65
|
+
* @csspart meta - The row holding its size and kind.
|
|
66
|
+
* @csspart name - Its name.
|
|
67
|
+
* @csspart remove - The control that takes it back out.
|
|
68
|
+
* @csspart size - How big it is.
|
|
69
|
+
* @csspart size-dismiss - The remove button, when `dismissible`.
|
|
70
|
+
* @csspart size-dismiss-mark - The glyph on the dismiss control, where none is slotted.
|
|
71
|
+
* @csspart size-prefix - The category before the label.
|
|
72
|
+
* @csspart trigger - The label that opens the picker.
|
|
73
|
+
*/
|
|
74
|
+
export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base {
|
|
75
|
+
protected readonly aria: AriaForwardController;
|
|
76
|
+
/**
|
|
77
|
+
* Which files the picker offers, as a native `accept` list.
|
|
78
|
+
*/
|
|
79
|
+
accept: string;
|
|
80
|
+
/**
|
|
81
|
+
* Whether more than one file can be chosen.
|
|
82
|
+
*/
|
|
83
|
+
multiple: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Which camera or microphone a mobile picker should offer, if any.
|
|
86
|
+
*/
|
|
87
|
+
capture?: 'environment' | 'user';
|
|
88
|
+
/**
|
|
89
|
+
* What the trigger reads, where nothing is slotted into it.
|
|
90
|
+
*/
|
|
91
|
+
triggerLabel: string;
|
|
92
|
+
/**
|
|
93
|
+
* Language tag the sizes are written in.
|
|
94
|
+
*
|
|
95
|
+
* Absent uses the runtime's own.
|
|
96
|
+
*/
|
|
97
|
+
locale: string;
|
|
98
|
+
/**
|
|
99
|
+
* What the control removing one file is called, per file.
|
|
100
|
+
*/
|
|
101
|
+
removeLabel: string;
|
|
102
|
+
/**
|
|
103
|
+
* Whether something is being dragged over the field.
|
|
104
|
+
*
|
|
105
|
+
* State rather than a property: it is a fact about a gesture in progress and
|
|
106
|
+
* nothing a consumer sets. It reaches the stylesheet as `data-dropping` on an
|
|
107
|
+
* element in this shadow root, never on the host, which is markup a consumer
|
|
108
|
+
* rendered.
|
|
109
|
+
*/
|
|
110
|
+
private dropping;
|
|
111
|
+
/**
|
|
112
|
+
* The files chosen, which is the element's own and never assigned.
|
|
113
|
+
*
|
|
114
|
+
* Read from the input rather than mirrored into a field. The input is what
|
|
115
|
+
* the picker filled and what a form submits, so a copy here could only ever
|
|
116
|
+
* disagree with it.
|
|
117
|
+
*/
|
|
118
|
+
get files(): File[];
|
|
119
|
+
/**
|
|
120
|
+
* The input, or nothing where there is no tree to ask.
|
|
121
|
+
*
|
|
122
|
+
* The guard is for the server, whose element shim carries attributes but not
|
|
123
|
+
* traversal: `render` reads `files`, `files` reads this, and an unguarded
|
|
124
|
+
* `querySelector` threw before a single character of markup came out. Nothing
|
|
125
|
+
* is lost by answering `null` there, because a file only ever arrives through
|
|
126
|
+
* a reader and a server has never met one.
|
|
127
|
+
*/
|
|
128
|
+
private get control();
|
|
129
|
+
/**
|
|
130
|
+
* Takes every file back out.
|
|
131
|
+
*
|
|
132
|
+
* The one change a consumer can ask for. Setting a `FileList` is refused by
|
|
133
|
+
* every browser for a reason worth keeping, but emptying one is not: nothing
|
|
134
|
+
* about the page learns anything it did not already have.
|
|
135
|
+
*/
|
|
136
|
+
clear(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Drops one file, by index.
|
|
139
|
+
*
|
|
140
|
+
* `removeAt` rather than `remove`, which is `Element`'s own and takes no
|
|
141
|
+
* arguments: shadowing it would break every caller who meant the node.
|
|
142
|
+
*
|
|
143
|
+
* Through `DataTransfer`, which is the only way to build a `FileList`: the
|
|
144
|
+
* type has no constructor and `input.files` takes nothing else. It is the same
|
|
145
|
+
* mechanism a drop uses, so a file put in by dragging and one put in by the
|
|
146
|
+
* picker come back out the same way.
|
|
147
|
+
*/
|
|
148
|
+
removeAt(at: number): void;
|
|
149
|
+
/** Says what is now chosen, by name, and puts it in the form. */
|
|
150
|
+
private announce;
|
|
151
|
+
/**
|
|
152
|
+
* Reports the files and the validity the host stands behind.
|
|
153
|
+
*
|
|
154
|
+
* `FormData` rather than a string, because a file input submits the file
|
|
155
|
+
* itself and several of them under one name. The anchor on `setValidity` is
|
|
156
|
+
* the input, so the browser's own bubble points at something a reader can see
|
|
157
|
+
* rather than at a host with no text in it.
|
|
158
|
+
*/
|
|
159
|
+
private sync;
|
|
160
|
+
private readonly handleChange;
|
|
161
|
+
private readonly handleDragOver;
|
|
162
|
+
private readonly handleDragLeave;
|
|
163
|
+
private readonly handleDrop;
|
|
164
|
+
private readonly handleRemove;
|
|
165
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
166
|
+
/** Puts focus on the input, the way a label pointing at one would. */
|
|
167
|
+
focus(options?: FocusOptions): void;
|
|
168
|
+
/** Empties it, which is what a form reset means for a file input. */
|
|
169
|
+
formResetCallback(): void;
|
|
170
|
+
/**
|
|
171
|
+
* What is known about a chosen file, as tags, behind a seam the styled tier
|
|
172
|
+
* can move.
|
|
173
|
+
*
|
|
174
|
+
* One method for both the size and the kind, because they are the same
|
|
175
|
+
* element saying two things and two methods would be two places to keep in
|
|
176
|
+
* step. Both parts are written out rather than passed in, and that is not
|
|
177
|
+
* style: a part name behind an interpolation is a part the manifest cannot
|
|
178
|
+
* see, so `check:selectors` reported the stylesheet as matching nothing the
|
|
179
|
+
* moment this took the name as an argument.
|
|
180
|
+
*
|
|
181
|
+
* A method rather than a fragment inline, so a tier above can render its own,
|
|
182
|
+
* wrap this one, pass it a property the base does not know about, or leave it
|
|
183
|
+
* out, without reimplementing the whole template to do any of them. The tag is
|
|
184
|
+
* interpolated rather than written, so `tier` resolves it: this renders the
|
|
185
|
+
* headless element, and a styled subclass that sets `tier` renders the styled one
|
|
186
|
+
* from the same template.
|
|
187
|
+
*/
|
|
188
|
+
protected renderMeta(file: File): TemplateResult;
|
|
189
|
+
protected render(): TemplateResult;
|
|
190
|
+
}
|
|
191
|
+
declare global {
|
|
192
|
+
interface HTMLElementTagNameMap {
|
|
193
|
+
'helix-headless-file-input': HelixHeadlessFileInput;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export {};
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { AriaForwardController, customElement, drawIcon, HelixBeforeChangeEvent, HelixFormElement, MixinField, restoreValue, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { trash } from '@fusion.dev/helix-icons';
|
|
9
|
+
import { nothing } from 'lit';
|
|
10
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
|
+
import { html } from 'lit/static-html.js';
|
|
12
|
+
import '../../tag/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* A file's size, at the largest unit that leaves a number worth reading.
|
|
15
|
+
*
|
|
16
|
+
* Binary steps, because that is what a file system reports and what the number
|
|
17
|
+
* beside it in a finder will say. One decimal place from kilobytes up and none
|
|
18
|
+
* for bytes: `1.2 MB` is the reading, `1.23 MB` is noise, and `1.5 bytes` is not
|
|
19
|
+
* a thing.
|
|
20
|
+
*
|
|
21
|
+
* `Intl.NumberFormat` for the number rather than `toFixed`, so a decimal comma
|
|
22
|
+
* lands where a decimal comma belongs. The unit is not localised with it: `MB`
|
|
23
|
+
* is written the same way in the places this ships, and `Intl`'s own byte units
|
|
24
|
+
* carry no scale to pick from.
|
|
25
|
+
*/
|
|
26
|
+
const size = (bytes, locale) => {
|
|
27
|
+
const units = ['bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
28
|
+
let value = bytes;
|
|
29
|
+
let step = 0;
|
|
30
|
+
while (value >= 1024 && step < units.length - 1) {
|
|
31
|
+
value /= 1024;
|
|
32
|
+
step += 1;
|
|
33
|
+
}
|
|
34
|
+
const written = new Intl.NumberFormat(locale || undefined, {
|
|
35
|
+
maximumFractionDigits: step === 0 ? 0 : 1,
|
|
36
|
+
}).format(value);
|
|
37
|
+
// One byte is a byte. The plural is only wrong at exactly one and it is the
|
|
38
|
+
// size an empty-ish file has, so it is the one a reader sees while testing.
|
|
39
|
+
const unit = step === 0 && value === 1 ? 'byte' : units[step];
|
|
40
|
+
return `${written} ${unit}`;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* What kind of file it is, in the shortest honest form.
|
|
44
|
+
*
|
|
45
|
+
* The subtype of the MIME type, so `image/png` reads `PNG`, which is the word a
|
|
46
|
+
* reader already has for it. A browser leaves `type` empty for anything it does
|
|
47
|
+
* not recognize, so the extension is the fallback: it is what the name already
|
|
48
|
+
* shows, which makes it a poor label and a better one than nothing.
|
|
49
|
+
*/
|
|
50
|
+
const kind = (file) => {
|
|
51
|
+
const subtype = file.type.split('/')[1];
|
|
52
|
+
if (subtype) {
|
|
53
|
+
return subtype.replace(/^x-/, '').split('+')[0]?.toUpperCase() ?? 'File';
|
|
54
|
+
}
|
|
55
|
+
const extension = file.name.includes('.') ? file.name.split('.').pop() : '';
|
|
56
|
+
return extension ? extension.toUpperCase() : 'File';
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* @summary A control for choosing files, by pointer or by dropping them on it.
|
|
60
|
+
*
|
|
61
|
+
* @description
|
|
62
|
+
* **A real `<input type="file">`, kept and made invisible rather than
|
|
63
|
+
* replaced.** It is the only way to open a file picker: the dialog is a
|
|
64
|
+
* privileged action a script cannot start on its own, and it opens for a click
|
|
65
|
+
* on the input or on a `<label>` pointing at it. Every version of this component
|
|
66
|
+
* that draws its own button ends up calling `.click()` on a hidden input
|
|
67
|
+
* anyway, and then has two things a screen reader can find where there is one
|
|
68
|
+
* control.
|
|
69
|
+
*
|
|
70
|
+
* So the input stays, holds the label, the name, the validity and the focus,
|
|
71
|
+
* and the visible trigger is a `<label for>` rather than a button. A label is
|
|
72
|
+
* not focusable and does not need to be: the input is, and Enter and Space on it
|
|
73
|
+
* open the picker because that is what the platform does with a file input.
|
|
74
|
+
*
|
|
75
|
+
* **Dropping is why this is a component rather than an attribute.** A bare file
|
|
76
|
+
* input accepts a drop only on the input itself, which is invisible here, so the
|
|
77
|
+
* field is the drop target and it says when something is over it.
|
|
78
|
+
*
|
|
79
|
+
* **Files are not a value a consumer can hand back.** Every other control here
|
|
80
|
+
* takes the controlled and uncontrolled pair, and this one does not: a `FileList`
|
|
81
|
+
* cannot be constructed from markup, and a consumer writing `value` on a file
|
|
82
|
+
* input is the one thing browsers refuse outright, because a page that could
|
|
83
|
+
* name a file could read it. So this element owns what was chosen, announces it,
|
|
84
|
+
* and offers `clear()` for the one change a consumer can legitimately ask for.
|
|
85
|
+
*
|
|
86
|
+
* **The list under it is the control's own, not decoration.** A file input with
|
|
87
|
+
* files chosen reports "3 files" and nothing else, which is unusable for
|
|
88
|
+
* checking that the right ones were picked. The names are rendered, and each
|
|
89
|
+
* carries the way to take it back out.
|
|
90
|
+
*
|
|
91
|
+
* @since 0.1.0
|
|
92
|
+
* @status stable
|
|
93
|
+
* @category forms
|
|
94
|
+
* @aliases FileUpload, FilePicker, Dropzone, InputFile
|
|
95
|
+
* @documentation https://helix.fusion.dev/components/file-input
|
|
96
|
+
*
|
|
97
|
+
* @slot feedback - What is wrong with it.
|
|
98
|
+
* @slot hint - Help under the field.
|
|
99
|
+
* @slot trigger - What the reader presses to open the picker.
|
|
100
|
+
*
|
|
101
|
+
* @event {Event} change - Announced once the value has moved.
|
|
102
|
+
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and nothing moves.
|
|
103
|
+
* @event {Event} input - Announced once the value has moved.
|
|
104
|
+
*
|
|
105
|
+
* @csspart control - The file input itself, which carries the label and validity.
|
|
106
|
+
* @csspart detail - Its name and its reading, stacked.
|
|
107
|
+
* @csspart feedback - What is wrong with it.
|
|
108
|
+
* @csspart field - The box that is pressed and dropped on.
|
|
109
|
+
* @csspart file - One chosen file.
|
|
110
|
+
* @csspart hint - Help under the field, while nothing is wrong.
|
|
111
|
+
* @csspart icon - A glyph this component draws.
|
|
112
|
+
* @csspart kind - What kind of file it is.
|
|
113
|
+
* @csspart kind-dismiss - The remove button, when `dismissible`.
|
|
114
|
+
* @csspart kind-dismiss-mark - The glyph on the dismiss control, where none is slotted.
|
|
115
|
+
* @csspart kind-prefix - The category before the label.
|
|
116
|
+
* @csspart label - The question, over the field.
|
|
117
|
+
* @csspart list - The chosen files.
|
|
118
|
+
* @csspart meta - The row holding its size and kind.
|
|
119
|
+
* @csspart name - Its name.
|
|
120
|
+
* @csspart remove - The control that takes it back out.
|
|
121
|
+
* @csspart size - How big it is.
|
|
122
|
+
* @csspart size-dismiss - The remove button, when `dismissible`.
|
|
123
|
+
* @csspart size-dismiss-mark - The glyph on the dismiss control, where none is slotted.
|
|
124
|
+
* @csspart size-prefix - The category before the label.
|
|
125
|
+
* @csspart trigger - The label that opens the picker.
|
|
126
|
+
*/
|
|
127
|
+
let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(HelixFormElement) {
|
|
128
|
+
constructor() {
|
|
129
|
+
super(...arguments);
|
|
130
|
+
/* A consumer's `aria-describedby` names ids in their own tree and the control
|
|
131
|
+
is in this shadow root, so the reference has to be carried down as an
|
|
132
|
+
element rather than copied as an id. See `AriaForwardController`: measured
|
|
133
|
+
in Chrome's own tree, the host held the description and the control the
|
|
134
|
+
reader is actually on was exposed without it. */
|
|
135
|
+
this.aria = new AriaForwardController(this, {
|
|
136
|
+
target: () => this.renderRoot?.querySelector('[part="control"]') ?? null,
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* Which files the picker offers, as a native `accept` list.
|
|
140
|
+
*/
|
|
141
|
+
this.accept = '';
|
|
142
|
+
/**
|
|
143
|
+
* Whether more than one file can be chosen.
|
|
144
|
+
*/
|
|
145
|
+
this.multiple = false;
|
|
146
|
+
/**
|
|
147
|
+
* What the trigger reads, where nothing is slotted into it.
|
|
148
|
+
*/
|
|
149
|
+
this.triggerLabel = 'Choose files';
|
|
150
|
+
/**
|
|
151
|
+
* Language tag the sizes are written in.
|
|
152
|
+
*
|
|
153
|
+
* Absent uses the runtime's own.
|
|
154
|
+
*/
|
|
155
|
+
this.locale = '';
|
|
156
|
+
/**
|
|
157
|
+
* What the control removing one file is called, per file.
|
|
158
|
+
*/
|
|
159
|
+
this.removeLabel = 'Remove';
|
|
160
|
+
/**
|
|
161
|
+
* Whether something is being dragged over the field.
|
|
162
|
+
*
|
|
163
|
+
* State rather than a property: it is a fact about a gesture in progress and
|
|
164
|
+
* nothing a consumer sets. It reaches the stylesheet as `data-dropping` on an
|
|
165
|
+
* element in this shadow root, never on the host, which is markup a consumer
|
|
166
|
+
* rendered.
|
|
167
|
+
*/
|
|
168
|
+
this.dropping = false;
|
|
169
|
+
this.handleChange = (event) => {
|
|
170
|
+
// The native `change` is relayed under this element's own name, so a
|
|
171
|
+
// consumer hears one event from one element rather than two from two.
|
|
172
|
+
event.stopPropagation();
|
|
173
|
+
this.announce();
|
|
174
|
+
};
|
|
175
|
+
/*
|
|
176
|
+
* Takes a drop, which a bare file input can only do on itself.
|
|
177
|
+
*
|
|
178
|
+
* The input is invisible here, so the field is the target and the files are
|
|
179
|
+
* put into the input afterwards: that keeps one source of truth, and it is
|
|
180
|
+
* what makes a dropped file and a picked file indistinguishable to a form.
|
|
181
|
+
*
|
|
182
|
+
* `preventDefault` on dragover as well as on drop, because without it the
|
|
183
|
+
* browser navigates to the file and the page is gone.
|
|
184
|
+
*/
|
|
185
|
+
this.handleDragOver = (event) => {
|
|
186
|
+
if (this.isDisabled) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
this.dropping = true;
|
|
191
|
+
};
|
|
192
|
+
this.handleDragLeave = () => {
|
|
193
|
+
this.dropping = false;
|
|
194
|
+
};
|
|
195
|
+
this.handleDrop = (event) => {
|
|
196
|
+
this.dropping = false;
|
|
197
|
+
const control = this.control;
|
|
198
|
+
if (this.isDisabled || !control || !event.dataTransfer) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
const dropped = [...event.dataTransfer.files];
|
|
203
|
+
if (dropped.length === 0) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const keeping = new DataTransfer();
|
|
207
|
+
// A single-file control takes the last thing dropped, the way choosing
|
|
208
|
+
// again in the picker replaces rather than adds.
|
|
209
|
+
for (const file of this.multiple ? [...this.files, ...dropped] : dropped.slice(-1)) {
|
|
210
|
+
keeping.items.add(file);
|
|
211
|
+
}
|
|
212
|
+
control.files = keeping.files;
|
|
213
|
+
this.announce();
|
|
214
|
+
};
|
|
215
|
+
this.handleRemove = (event) => {
|
|
216
|
+
const at = Number(event.currentTarget.dataset.at);
|
|
217
|
+
if (Number.isFinite(at)) {
|
|
218
|
+
this.removeAt(at);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The files chosen, which is the element's own and never assigned.
|
|
224
|
+
*
|
|
225
|
+
* Read from the input rather than mirrored into a field. The input is what
|
|
226
|
+
* the picker filled and what a form submits, so a copy here could only ever
|
|
227
|
+
* disagree with it.
|
|
228
|
+
*/
|
|
229
|
+
get files() {
|
|
230
|
+
return [...(this.control?.files ?? [])];
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The input, or nothing where there is no tree to ask.
|
|
234
|
+
*
|
|
235
|
+
* The guard is for the server, whose element shim carries attributes but not
|
|
236
|
+
* traversal: `render` reads `files`, `files` reads this, and an unguarded
|
|
237
|
+
* `querySelector` threw before a single character of markup came out. Nothing
|
|
238
|
+
* is lost by answering `null` there, because a file only ever arrives through
|
|
239
|
+
* a reader and a server has never met one.
|
|
240
|
+
*/
|
|
241
|
+
get control() {
|
|
242
|
+
if (typeof this.renderRoot?.querySelector !== 'function') {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
return this.renderRoot.querySelector('[part="control"]');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Takes every file back out.
|
|
249
|
+
*
|
|
250
|
+
* The one change a consumer can ask for. Setting a `FileList` is refused by
|
|
251
|
+
* every browser for a reason worth keeping, but emptying one is not: nothing
|
|
252
|
+
* about the page learns anything it did not already have.
|
|
253
|
+
*/
|
|
254
|
+
clear() {
|
|
255
|
+
const control = this.control;
|
|
256
|
+
if (!control) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
control.value = '';
|
|
260
|
+
this.announce();
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Drops one file, by index.
|
|
264
|
+
*
|
|
265
|
+
* `removeAt` rather than `remove`, which is `Element`'s own and takes no
|
|
266
|
+
* arguments: shadowing it would break every caller who meant the node.
|
|
267
|
+
*
|
|
268
|
+
* Through `DataTransfer`, which is the only way to build a `FileList`: the
|
|
269
|
+
* type has no constructor and `input.files` takes nothing else. It is the same
|
|
270
|
+
* mechanism a drop uses, so a file put in by dragging and one put in by the
|
|
271
|
+
* picker come back out the same way.
|
|
272
|
+
*/
|
|
273
|
+
removeAt(at) {
|
|
274
|
+
const control = this.control;
|
|
275
|
+
if (!control || typeof DataTransfer === 'undefined') {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const keeping = new DataTransfer();
|
|
279
|
+
for (const [index, file] of this.files.entries()) {
|
|
280
|
+
if (index !== at) {
|
|
281
|
+
keeping.items.add(file);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
control.files = keeping.files;
|
|
285
|
+
this.announce();
|
|
286
|
+
}
|
|
287
|
+
/** Says what is now chosen, by name, and puts it in the form. */
|
|
288
|
+
announce() {
|
|
289
|
+
this.sync();
|
|
290
|
+
this.requestUpdate();
|
|
291
|
+
this.dispatchEvent(new HelixBeforeChangeEvent({
|
|
292
|
+
value: this.files.map((file) => file.name).join(', '),
|
|
293
|
+
}));
|
|
294
|
+
/* And under the platform's own names, which is what `onChange` and `onInput`
|
|
295
|
+
bind and what a native file input sends when the chosen files move: both,
|
|
296
|
+
in that order, the way every other control here does.
|
|
297
|
+
|
|
298
|
+
`input` was declared and never sent until 27 Aug 2026. The only one a
|
|
299
|
+
consumer received was the inner `<input type="file">`'s own, composed out
|
|
300
|
+
of this shadow root, so the documented event did not exist and an
|
|
301
|
+
undocumented one arrived in its place. Found by sweeping every family
|
|
302
|
+
that renders a native control rather than every family the first pass
|
|
303
|
+
named. See 308. */
|
|
304
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
305
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: false }));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Reports the files and the validity the host stands behind.
|
|
309
|
+
*
|
|
310
|
+
* `FormData` rather than a string, because a file input submits the file
|
|
311
|
+
* itself and several of them under one name. The anchor on `setValidity` is
|
|
312
|
+
* the input, so the browser's own bubble points at something a reader can see
|
|
313
|
+
* rather than at a host with no text in it.
|
|
314
|
+
*/
|
|
315
|
+
sync() {
|
|
316
|
+
const data = new FormData();
|
|
317
|
+
if (this.name) {
|
|
318
|
+
for (const file of this.files) {
|
|
319
|
+
data.append(this.name, file);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/* **An empty one contributes no entry, and the native control contributes an
|
|
323
|
+
empty file.** Measured, one form holding both: `<input type="file"
|
|
324
|
+
name="nf">` with nothing chosen submits `nf=` carrying a `File` of zero
|
|
325
|
+
bytes and no name, and this submits nothing at all.
|
|
326
|
+
|
|
327
|
+
Kept, as a decision rather than an omission. A phantom entry naming a file
|
|
328
|
+
that does not exist is a thing every server reading this form has to know
|
|
329
|
+
to discard, and the reason the platform sends one is compatibility with
|
|
330
|
+
form encodings that predate `FormData` rather than anything a reader wants.
|
|
331
|
+
`check:platform-fidelity` excuses `value` on this family already, for the
|
|
332
|
+
neighbouring reason that the native one is `C:\fakepath\name`; this is
|
|
333
|
+
the same component declining to reproduce a lie, one level down. Recorded
|
|
334
|
+
here because a divergence nobody wrote down is indistinguishable from one
|
|
335
|
+
nobody noticed. */
|
|
336
|
+
this.internals.setFormValue(data);
|
|
337
|
+
const control = this.control;
|
|
338
|
+
if (!control) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
this.internals.setValidity(control.validity, control.validationMessage, control);
|
|
342
|
+
this.refreshFeedback();
|
|
343
|
+
}
|
|
344
|
+
firstUpdated(changed) {
|
|
345
|
+
super.firstUpdated(changed);
|
|
346
|
+
// Whatever the markup arrived with, so a form knows before anything is
|
|
347
|
+
// touched. `willUpdate` is too early: there is no control to read yet.
|
|
348
|
+
this.sync();
|
|
349
|
+
}
|
|
350
|
+
/** Puts focus on the input, the way a label pointing at one would. */
|
|
351
|
+
focus(options) {
|
|
352
|
+
this.control?.focus(options);
|
|
353
|
+
}
|
|
354
|
+
/** Empties it, which is what a form reset means for a file input. */
|
|
355
|
+
formResetCallback() {
|
|
356
|
+
if (this.control) {
|
|
357
|
+
restoreValue(this.control, '');
|
|
358
|
+
}
|
|
359
|
+
this.announce();
|
|
360
|
+
}
|
|
361
|
+
/*
|
|
362
|
+
* The hint goes under the field, and the list after both.
|
|
363
|
+
*
|
|
364
|
+
* It was under the list, on the reading that the field and its files are one
|
|
365
|
+
* block and the hint describes the block. Three things say otherwise.
|
|
366
|
+
*
|
|
367
|
+
* A hint is instruction for the next action rather than a note about the
|
|
368
|
+
* result: "up to 10MB each" is a rule for what may be added, and a rule read
|
|
369
|
+
* after the outcome is a rule read too late.
|
|
370
|
+
*
|
|
371
|
+
* Its position has to be stable, and this is the one that decides it. Under
|
|
372
|
+
* the list the hint travels down the page as files are added, a card's height
|
|
373
|
+
* at a time, so the one piece of guidance the control carries is never twice
|
|
374
|
+
* in the same place. Every other field here puts it immediately under the
|
|
375
|
+
* control, and a reader who has learned where to look should not have to
|
|
376
|
+
* relearn it for this one.
|
|
377
|
+
*
|
|
378
|
+
* And feedback stands in place of the hint rather than beside it, so wherever
|
|
379
|
+
* the hint is, an error is. An error about a file that was refused belongs
|
|
380
|
+
* against the control that refused it, not below a list of the ones that were
|
|
381
|
+
* not.
|
|
382
|
+
*/
|
|
383
|
+
/**
|
|
384
|
+
* What is known about a chosen file, as tags, behind a seam the styled tier
|
|
385
|
+
* can move.
|
|
386
|
+
*
|
|
387
|
+
* One method for both the size and the kind, because they are the same
|
|
388
|
+
* element saying two things and two methods would be two places to keep in
|
|
389
|
+
* step. Both parts are written out rather than passed in, and that is not
|
|
390
|
+
* style: a part name behind an interpolation is a part the manifest cannot
|
|
391
|
+
* see, so `check:selectors` reported the stylesheet as matching nothing the
|
|
392
|
+
* moment this took the name as an argument.
|
|
393
|
+
*
|
|
394
|
+
* A method rather than a fragment inline, so a tier above can render its own,
|
|
395
|
+
* wrap this one, pass it a property the base does not know about, or leave it
|
|
396
|
+
* out, without reimplementing the whole template to do any of them. The tag is
|
|
397
|
+
* interpolated rather than written, so `tier` resolves it: this renders the
|
|
398
|
+
* headless element, and a styled subclass that sets `tier` renders the styled one
|
|
399
|
+
* from the same template.
|
|
400
|
+
*/
|
|
401
|
+
renderMeta(file) {
|
|
402
|
+
return html `<${this.tag('tag')} part="size"
|
|
403
|
+
exportparts="dismiss: size-dismiss, dismiss-mark: size-dismiss-mark, prefix: size-prefix">${size(file.size, this.locale)}</${this.tag('tag')}>
|
|
404
|
+
<${this.tag('tag')}
|
|
405
|
+
part="kind"
|
|
406
|
+
exportparts="dismiss: kind-dismiss, dismiss-mark: kind-dismiss-mark, prefix: kind-prefix">${kind(file)}</${this.tag('tag')}>`;
|
|
407
|
+
}
|
|
408
|
+
render() {
|
|
409
|
+
const files = this.files;
|
|
410
|
+
return html `${this.renderLabel()}
|
|
411
|
+
<div
|
|
412
|
+
part="field"
|
|
413
|
+
?data-dropping=${this.dropping}
|
|
414
|
+
@dragleave=${this.handleDragLeave}
|
|
415
|
+
@dragover=${this.handleDragOver}
|
|
416
|
+
@drop=${this.handleDrop}
|
|
417
|
+
>
|
|
418
|
+
<input
|
|
419
|
+
part="control"
|
|
420
|
+
id="control"
|
|
421
|
+
type="file"
|
|
422
|
+
accept=${this.accept || nothing}
|
|
423
|
+
capture=${this.capture ?? nothing}
|
|
424
|
+
name=${this.name || nothing}
|
|
425
|
+
aria-describedby=${this.describedBy ?? nothing}
|
|
426
|
+
?disabled=${this.isDisabled}
|
|
427
|
+
?multiple=${this.multiple}
|
|
428
|
+
?required=${this.required}
|
|
429
|
+
@change=${this.handleChange}
|
|
430
|
+
@input=${this.stopInner}
|
|
431
|
+
/>
|
|
432
|
+
<label part="trigger" for="control">
|
|
433
|
+
<slot name="trigger">${this.triggerLabel}</slot>
|
|
434
|
+
</label>
|
|
435
|
+
</div>
|
|
436
|
+
<!-- The hint sits under the field and the list goes last, which is the
|
|
437
|
+
one ordering decision this component makes differently from the way it
|
|
438
|
+
reads. Reasoning below the template. -->
|
|
439
|
+
${this.renderHint()}${this.renderFeedback()}
|
|
440
|
+
${files.length === 0
|
|
441
|
+
? nothing
|
|
442
|
+
: html `<ul part="list">
|
|
443
|
+
${files.map((file, at) => html `<li part="file">
|
|
444
|
+
<span part="detail">
|
|
445
|
+
<span part="name">${file.name}</span>
|
|
446
|
+
<span part="meta">
|
|
447
|
+
${this.renderMeta(file)}
|
|
448
|
+
</span>
|
|
449
|
+
</span>
|
|
450
|
+
<button
|
|
451
|
+
part="remove"
|
|
452
|
+
type="button"
|
|
453
|
+
aria-label=${`${this.removeLabel} ${file.name}`}
|
|
454
|
+
data-at=${at}
|
|
455
|
+
?disabled=${this.isDisabled}
|
|
456
|
+
@click=${this.handleRemove}
|
|
457
|
+
>
|
|
458
|
+
${drawIcon(trash)}
|
|
459
|
+
</button>
|
|
460
|
+
</li>`)}
|
|
461
|
+
</ul>`}`;
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
__decorate([
|
|
465
|
+
property()
|
|
466
|
+
], HelixHeadlessFileInput.prototype, "accept", void 0);
|
|
467
|
+
__decorate([
|
|
468
|
+
property({ reflect: true, type: Boolean })
|
|
469
|
+
], HelixHeadlessFileInput.prototype, "multiple", void 0);
|
|
470
|
+
__decorate([
|
|
471
|
+
property()
|
|
472
|
+
], HelixHeadlessFileInput.prototype, "capture", void 0);
|
|
473
|
+
__decorate([
|
|
474
|
+
property({ attribute: 'trigger-label' })
|
|
475
|
+
], HelixHeadlessFileInput.prototype, "triggerLabel", void 0);
|
|
476
|
+
__decorate([
|
|
477
|
+
property()
|
|
478
|
+
], HelixHeadlessFileInput.prototype, "locale", void 0);
|
|
479
|
+
__decorate([
|
|
480
|
+
property({ attribute: 'remove-label' })
|
|
481
|
+
], HelixHeadlessFileInput.prototype, "removeLabel", void 0);
|
|
482
|
+
__decorate([
|
|
483
|
+
state()
|
|
484
|
+
], HelixHeadlessFileInput.prototype, "dropping", void 0);
|
|
485
|
+
HelixHeadlessFileInput = __decorate([
|
|
486
|
+
customElement('helix-headless-file-input')
|
|
487
|
+
], HelixHeadlessFileInput);
|
|
488
|
+
export { HelixHeadlessFileInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HelixHeadlessFormatDate } from './root/format-date-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HelixHeadlessFormatDate } from './root/format-date-root.js';
|