@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,553 @@
|
|
|
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, HelixCompleteEvent, HelixFormElement, MixinControllerValue, MixinField, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
/**
|
|
11
|
+
* @summary A short code, one character to a box.
|
|
12
|
+
*
|
|
13
|
+
* @description
|
|
14
|
+
* **One `<input>` per cell, rather than one input drawn as cells.** Both are
|
|
15
|
+
* built in the wild and the trade is real: a single field autofills perfectly
|
|
16
|
+
* and needs a drawn caret nobody can style well, and separate fields give every
|
|
17
|
+
* box a real focus ring, a real caret and a real tab stop for free. This takes
|
|
18
|
+
* the second, and then closes autofill's gap rather than living with it, below.
|
|
19
|
+
*
|
|
20
|
+
* **Anything longer than one character is treated as a paste, wherever it
|
|
21
|
+
* lands.** That covers the obvious case and two that are not: a browser
|
|
22
|
+
* autofilling an SMS code drops the whole thing into whichever box is focused,
|
|
23
|
+
* and a password manager does the same. So a six-character arrival in cell three
|
|
24
|
+
* fills from cell three rather than being truncated to its first character,
|
|
25
|
+
* which is what a per-cell `maxlength` would have done silently.
|
|
26
|
+
*
|
|
27
|
+
* **The code is one value to a form.** The cells are inside a shadow root and
|
|
28
|
+
* submit nothing on their own; the host is form-associated and reports the
|
|
29
|
+
* joined string, which is also what carries validity and answers a reset.
|
|
30
|
+
*
|
|
31
|
+
* `autocomplete="one-time-code"` is on the first cell only. It is a hint about
|
|
32
|
+
* the field as a whole, and repeating it on six boxes offers a browser six
|
|
33
|
+
* places to put one code.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
* @status stable
|
|
37
|
+
* @category forms
|
|
38
|
+
* @aliases PinField, OTP, One-time code, Verification code, InputPin
|
|
39
|
+
* @documentation https://helix.fusion.dev/components/pin-input
|
|
40
|
+
*
|
|
41
|
+
* @event {Event} change - Announced with a filled code, and when an edited one loses focus.
|
|
42
|
+
* @event {HelixBeforeChangeEvent} helix-before-change - Fired as the code changes, and cancelable.
|
|
43
|
+
* @event {HelixCompleteEvent} helix-complete - Fired when the last box is filled.
|
|
44
|
+
* @event {Event} input - Announced once the value has moved.
|
|
45
|
+
*
|
|
46
|
+
* @csspart cell - One box.
|
|
47
|
+
* @csspart feedback - What is wrong with it.
|
|
48
|
+
* @csspart field - The row of boxes.
|
|
49
|
+
* @csspart hint - Help under the boxes, while nothing is wrong.
|
|
50
|
+
* @csspart label - The question, over the boxes.
|
|
51
|
+
*/
|
|
52
|
+
let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(...arguments);
|
|
55
|
+
/* A consumer's `aria-describedby` names ids in their own tree and the control
|
|
56
|
+
is in this shadow root, so the reference has to be carried down as an
|
|
57
|
+
element rather than copied as an id. See `AriaForwardController`: measured
|
|
58
|
+
in Chrome's own tree, the host held the description and the control the
|
|
59
|
+
reader is actually on was exposed without it. */
|
|
60
|
+
this.aria = new AriaForwardController(this, {
|
|
61
|
+
target: () => this.renderRoot?.querySelector('[part="cell"]') ?? null,
|
|
62
|
+
});
|
|
63
|
+
/** Typed or dragged, so every edit is announced. See `announcesInput`. */
|
|
64
|
+
this.announcesInput = true;
|
|
65
|
+
/**
|
|
66
|
+
* What the browser may autofill, reaching the first cell only.
|
|
67
|
+
*
|
|
68
|
+
* `one-time-code` is why this defaults to anything at all: it is the token
|
|
69
|
+
* that lets a phone offer the code out of a message. Later cells stay `off`
|
|
70
|
+
* for the reason in the class comment above, that six cells each offering
|
|
71
|
+
* the same code is one offer made six times.
|
|
72
|
+
*/
|
|
73
|
+
this.autocomplete = 'one-time-code';
|
|
74
|
+
/**
|
|
75
|
+
* How many characters the code has.
|
|
76
|
+
*/
|
|
77
|
+
this.length = 6;
|
|
78
|
+
/**
|
|
79
|
+
* What a box accepts, which decides the keyboard a phone offers.
|
|
80
|
+
*
|
|
81
|
+
* `numeric` and `alphanumeric` also refuse anything else on the way in,
|
|
82
|
+
* because a code that is digits is digits: letting a letter into a box a
|
|
83
|
+
* reader cannot submit is a mistake they have to find and undo themselves.
|
|
84
|
+
* `text` refuses nothing and only trims, so it is the mode for a code with a
|
|
85
|
+
* hyphen in it.
|
|
86
|
+
*
|
|
87
|
+
* **`alphanumeric` is ASCII letters and digits**, which is narrower than the
|
|
88
|
+
* word is in general and is the right narrowness here. A code of this kind
|
|
89
|
+
* comes off a keypad, an SMS or an authenticator, and none of them emit `é`;
|
|
90
|
+
* a filter that accepted it would let through a character the service on the
|
|
91
|
+
* other end is going to reject. A consumer whose codes really do carry
|
|
92
|
+
* marked letters wants `text`, which judges nothing.
|
|
93
|
+
*
|
|
94
|
+
* **Not reflected**, and it was until this component's own server spec caught
|
|
95
|
+
* it: nothing selects on it, so all reflection did was write `type="numeric"`
|
|
96
|
+
* onto every `<helix-pin-input>` ever rendered, an attribute no consumer's
|
|
97
|
+
* markup had. That is the fault `Input`'s own `type` had and the reason the
|
|
98
|
+
* area rules carry the sentence "reflect only what CSS selects on". The real
|
|
99
|
+
* inputs take it as a binding, which is what makes the keyboard follow.
|
|
100
|
+
*/
|
|
101
|
+
this.type = 'numeric';
|
|
102
|
+
/**
|
|
103
|
+
* Whether the characters are hidden, the way a password is.
|
|
104
|
+
*/
|
|
105
|
+
this.mask = false;
|
|
106
|
+
/**
|
|
107
|
+
* What an empty box shows, usually a single dot.
|
|
108
|
+
*
|
|
109
|
+
* The platform's own placeholder, on each cell, rather than a character
|
|
110
|
+
* drawn behind the field: it clears the moment anything is typed, it is
|
|
111
|
+
* already excluded from the accessible value, and it works the same under
|
|
112
|
+
* `mask`, where the cell is a password input and a hand-drawn glyph would
|
|
113
|
+
* have to know that.
|
|
114
|
+
*
|
|
115
|
+
* Empty by default, because a row of boxes already reads as a row of boxes
|
|
116
|
+
* and a dot in each is a choice about how much the field should announce
|
|
117
|
+
* itself before anybody has touched it.
|
|
118
|
+
*/
|
|
119
|
+
this.placeholder = '';
|
|
120
|
+
/**
|
|
121
|
+
* Whether the boxes refuse input while still submitting their value.
|
|
122
|
+
*/
|
|
123
|
+
this.readOnly = false;
|
|
124
|
+
/**
|
|
125
|
+
* Accessible name for one box, with `{index}` and `{length}` filled in.
|
|
126
|
+
*/
|
|
127
|
+
this.cellLabel = 'Character {index} of {length}';
|
|
128
|
+
this.handleInput = (event) => {
|
|
129
|
+
// Stopped for the reason `Input.handleInput` gives: the inner control's own
|
|
130
|
+
// `input` is composed and would reach a consumer beside this element's.
|
|
131
|
+
event.stopPropagation();
|
|
132
|
+
const cell = event.target;
|
|
133
|
+
const at = this.cells.indexOf(cell);
|
|
134
|
+
if (at === -1) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
/* **An emptied box is a deletion, and this used to be unreachable.** A
|
|
138
|
+
Backspace in a *filled* box is the browser's own: the keydown handler
|
|
139
|
+
leaves it alone, the character goes, and `input` arrives carrying "". That
|
|
140
|
+
fell through to `write`, which returns early on empty, and then `paint`
|
|
141
|
+
put the character straight back. Pressing Backspace on a full code changed
|
|
142
|
+
nothing at all, twice in a row, measured with real keystrokes rather than
|
|
143
|
+
dispatched events, which is why every spec and the whole state audit
|
|
144
|
+
passed over it.
|
|
145
|
+
|
|
146
|
+
The rest collapse left, which is what the model already requires and what
|
|
147
|
+
a single text field does: "123456" with the 2 deleted is "13456", not a
|
|
148
|
+
hole. Focus stays where the reader was, so the box under the caret now
|
|
149
|
+
holds what followed. */
|
|
150
|
+
if (cell.value === '') {
|
|
151
|
+
const characters = this.characters;
|
|
152
|
+
characters[at] = '';
|
|
153
|
+
this.requestValue(characters.join(''));
|
|
154
|
+
this.updateComplete.then(() => {
|
|
155
|
+
this.paint();
|
|
156
|
+
this.cells[at]?.focus();
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
/* Longer than one character is a paste or an autofill, whichever box it
|
|
161
|
+
landed in. A per-cell `maxlength` would have silently truncated it to the
|
|
162
|
+
first character and lost the rest. */
|
|
163
|
+
this.write(at, cell.value);
|
|
164
|
+
// And put the box back, in case what arrived was refused or filtered.
|
|
165
|
+
this.updateComplete.then(() => this.paint());
|
|
166
|
+
};
|
|
167
|
+
this.handlePaste = (event) => {
|
|
168
|
+
const cell = event.target;
|
|
169
|
+
const at = this.cells.indexOf(cell);
|
|
170
|
+
if (at === -1) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// Taken here rather than left to `input`, so the browser does not first put
|
|
174
|
+
// the whole string into one box and fire a change we then have to undo.
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
this.write(at, event.clipboardData?.getData('text') ?? '');
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* The keys that move between boxes, and the one that clears backwards.
|
|
180
|
+
*
|
|
181
|
+
* Backspace in an empty box goes back and clears the one it lands in, which
|
|
182
|
+
* is what makes a code correctable without the reader hunting for the box
|
|
183
|
+
* they meant. In a full box it does what it always does and is left alone.
|
|
184
|
+
*/
|
|
185
|
+
this.handleKeydown = (event) => {
|
|
186
|
+
const cell = event.target;
|
|
187
|
+
const at = this.cells.indexOf(cell);
|
|
188
|
+
if (at === -1 || this.isDisabled || this.readOnly) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (event.key === 'Backspace' && cell.value === '' && at > 0) {
|
|
192
|
+
const characters = this.characters;
|
|
193
|
+
characters[at - 1] = '';
|
|
194
|
+
this.requestValue(characters.join('').replace(/\s+$/, ''));
|
|
195
|
+
this.updateComplete.then(() => {
|
|
196
|
+
this.paint();
|
|
197
|
+
this.cells[at - 1]?.focus();
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
else if (event.key === 'ArrowLeft' && at > 0) {
|
|
201
|
+
this.cells[at - 1]?.focus();
|
|
202
|
+
this.cells[at - 1]?.select();
|
|
203
|
+
}
|
|
204
|
+
else if (event.key === 'ArrowRight' && at < this.count - 1) {
|
|
205
|
+
this.cells[at + 1]?.focus();
|
|
206
|
+
this.cells[at + 1]?.select();
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* The code as it stood when focus arrived, so a visit that changed nothing
|
|
215
|
+
* says nothing.
|
|
216
|
+
*
|
|
217
|
+
* **This is the platform's reading, and it was the other one until it was
|
|
218
|
+
* measured.** It held the value at the last *commit*, on the argument that
|
|
219
|
+
* recognizing an arrival is hard here: this component moves focus itself on
|
|
220
|
+
* every character, so most `focus` events are ones it caused, and telling
|
|
221
|
+
* those apart rests entirely on `relatedTarget`. That is true and it is not
|
|
222
|
+
* an obstacle, because `handleFocusOut` was already doing exactly that guard
|
|
223
|
+
* one method away. The reasoning had talked itself out of a check it had
|
|
224
|
+
* already written.
|
|
225
|
+
*
|
|
226
|
+
* **What the two readings do to a real browser**, measured in Chromium
|
|
227
|
+
* against a native `<input>` with real keystrokes:
|
|
228
|
+
*
|
|
229
|
+
* | | native | last commit | on arrival |
|
|
230
|
+
* |---|---|---|---|
|
|
231
|
+
* | types, leaves | `change` | `change` | `change` |
|
|
232
|
+
* | script assigns unfocused, visit with no edit | silent | **`change`** | silent |
|
|
233
|
+
* | script assigns unfocused, then edits | `change` | `change` | `change` |
|
|
234
|
+
* | types a character and deletes it | silent | silent | silent |
|
|
235
|
+
*
|
|
236
|
+
* One row differed and it is the one that matters: a consumer who assigns
|
|
237
|
+
* `value` and then has a reader tab through the row was told the reader had
|
|
238
|
+
* entered a code. `change` on a field means "the reader had a go at this",
|
|
239
|
+
* `MixinField` treats it as exactly that, and announcing a value the consumer
|
|
240
|
+
* set themselves is a false positive rather than the safe half of a trade.
|
|
241
|
+
*
|
|
242
|
+
* **The narrower divergence that is left** is a script assigning while the row
|
|
243
|
+
* is focused, where the platform stays silent because no user edit happened
|
|
244
|
+
* and this commits on the way out. Closing it needs a dirty flag set from real
|
|
245
|
+
* cell input rather than a baseline, which is more machinery than the case has
|
|
246
|
+
* earned: a consumer writing to a field the reader is inside is already in a
|
|
247
|
+
* fight with them.
|
|
248
|
+
*
|
|
249
|
+
* Seeded in `firstUpdated`, a code in the markup being committed by whoever
|
|
250
|
+
* wrote it, and re-taken on every arrival from outside the row.
|
|
251
|
+
*/
|
|
252
|
+
this.committed = '';
|
|
253
|
+
/** Selecting on focus, so typing replaces rather than appending. */
|
|
254
|
+
this.handleFocus = (event) => {
|
|
255
|
+
;
|
|
256
|
+
event.target.select();
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Whether focus is currently somewhere inside the row.
|
|
260
|
+
*
|
|
261
|
+
* **The arrival is a transition, not a `relatedTarget` reading**, which is
|
|
262
|
+
* what the first version of this did and is what the exit still does. It does
|
|
263
|
+
* not work on the way in for two separate reasons and the second is the one
|
|
264
|
+
* that decides it. Every character moves focus to the next box, so an
|
|
265
|
+
* unguarded `focusin` re-baselines on each keystroke and the row can never
|
|
266
|
+
* report anything: measured, the three commit specs went silent at once. And
|
|
267
|
+
* `focusin` carries `relatedTarget` in a browser and not in happy-dom, so a
|
|
268
|
+
* guard written on it is a guard that holds in production and not in the
|
|
269
|
+
* specs, which is the worse half. This flag needs neither: entering is
|
|
270
|
+
* `focusin` while it is `false`, and nothing else is.
|
|
271
|
+
*/
|
|
272
|
+
this.within = false;
|
|
273
|
+
/**
|
|
274
|
+
* Takes the baseline when focus arrives from outside the row.
|
|
275
|
+
*
|
|
276
|
+
* The value at arrival is what the platform compares against, and the table
|
|
277
|
+
* on `committed` is why.
|
|
278
|
+
*/
|
|
279
|
+
this.handleFocusIn = () => {
|
|
280
|
+
if (this.within) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
this.within = true;
|
|
284
|
+
this.committed = this.value;
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* Commits when focus leaves the row, which is the moment every other field in
|
|
288
|
+
* the library has and this one did not.
|
|
289
|
+
*
|
|
290
|
+
* `change` is one of the two events `MixinField` reads as "the reader has had
|
|
291
|
+
* a go at this", so a code that was never completed was never `touched` and
|
|
292
|
+
* its own `tooShort` message could not show: two boxes of four filled and
|
|
293
|
+
* tabbed away from was answered with nothing at all. `Input` gets this for
|
|
294
|
+
* free by relaying its control's own `change`; here the cells each fire one,
|
|
295
|
+
* none of them means the code is entered, and neither does any single one of
|
|
296
|
+
* them mean the reader has left.
|
|
297
|
+
*
|
|
298
|
+
* **Focus leaving the row, and not a cell.** Every character moves focus to
|
|
299
|
+
* the next box, so a per-cell blur would commit on every keystroke, which is
|
|
300
|
+
* the fault this exists to fix pointed the other way.
|
|
301
|
+
*/
|
|
302
|
+
this.handleFocusOut = (event) => {
|
|
303
|
+
if (this.cells.includes(event.relatedTarget)) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.within = false;
|
|
307
|
+
this.commit();
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/** Focus is delegated, so the host is never the focus target. */
|
|
311
|
+
static { this.shadowRootOptions = {
|
|
312
|
+
...HelixFormElement.shadowRootOptions,
|
|
313
|
+
delegatesFocus: true,
|
|
314
|
+
}; }
|
|
315
|
+
/** How many boxes there are, clamped once so every reader agrees. */
|
|
316
|
+
get count() {
|
|
317
|
+
return Math.max(1, Math.floor(this.length) || 1);
|
|
318
|
+
}
|
|
319
|
+
get cells() {
|
|
320
|
+
return [...this.renderRoot.querySelectorAll('[part="cell"]')];
|
|
321
|
+
}
|
|
322
|
+
/** The code as characters, padded to the row so every box has one to show. */
|
|
323
|
+
get characters() {
|
|
324
|
+
return Array.from({ length: this.count }, (_, at) => this.value[at] ?? '');
|
|
325
|
+
}
|
|
326
|
+
/** Everything this box would accept, with the rest dropped. */
|
|
327
|
+
clean(text) {
|
|
328
|
+
const trimmed = text.trim();
|
|
329
|
+
if (this.type === 'numeric') {
|
|
330
|
+
return trimmed.replace(/\D/g, '');
|
|
331
|
+
}
|
|
332
|
+
if (this.type === 'alphanumeric') {
|
|
333
|
+
return trimmed.replace(/[^0-9A-Za-z]/g, '');
|
|
334
|
+
}
|
|
335
|
+
return trimmed;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Writes a run of characters from one box onward.
|
|
339
|
+
*
|
|
340
|
+
* The single path for typing, pasting and autofill alike, because they are
|
|
341
|
+
* the same act at different lengths and splitting them is how the three
|
|
342
|
+
* disagree about trimming, about the cursor, and about what happens at the
|
|
343
|
+
* end.
|
|
344
|
+
*/
|
|
345
|
+
write(from, text) {
|
|
346
|
+
const cleaned = this.clean(text);
|
|
347
|
+
if (cleaned === '') {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
/* **A code has no holes in it**, so a character cannot land past the end of
|
|
351
|
+
what is already there. Clamped rather than allowed, because the value is
|
|
352
|
+
one compact string and a gap in the middle of it does not exist: writing
|
|
353
|
+
four characters into box three of an empty field produced "4321" and
|
|
354
|
+
showed it in boxes one to four, so the boxes and the value disagreed about
|
|
355
|
+
where the reader had typed.
|
|
356
|
+
|
|
357
|
+
Clamping is what makes them agree, and it reads correctly in both
|
|
358
|
+
directions. Pasting a whole code while the caret happens to sit in box
|
|
359
|
+
three fills from the start, which is what a reader pasting a code means.
|
|
360
|
+
Pasting two characters into box three of a field with two already in it
|
|
361
|
+
fills from box three, because that is where the end was. */
|
|
362
|
+
const start = Math.min(from, this.value.length);
|
|
363
|
+
const characters = this.characters;
|
|
364
|
+
for (let at = 0; at < cleaned.length && start + at < this.count; at += 1) {
|
|
365
|
+
characters[start + at] = cleaned[at];
|
|
366
|
+
}
|
|
367
|
+
const next = characters.join('');
|
|
368
|
+
const filled = Math.min(this.count - 1, start + cleaned.length);
|
|
369
|
+
this.requestValue(next);
|
|
370
|
+
this.updateComplete.then(() => {
|
|
371
|
+
this.paint();
|
|
372
|
+
this.cells[filled]?.focus();
|
|
373
|
+
this.cells[filled]?.select();
|
|
374
|
+
if (next.length === this.count) {
|
|
375
|
+
this.dispatchEvent(new HelixCompleteEvent({ value: next }));
|
|
376
|
+
/* And a native `change`, because a filled code is this control's
|
|
377
|
+
commit. There is no inner control to relay one from: six boxes each
|
|
378
|
+
fire their own, and none of them means the code is entered. */
|
|
379
|
+
this.commit();
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Puts the value back into the boxes.
|
|
385
|
+
*
|
|
386
|
+
* The cells are uncontrolled between renders, because Lit will not rewrite an
|
|
387
|
+
* input's `.value` when the bound string has not changed, and a refused
|
|
388
|
+
* keystroke leaves the box holding a character the element rejected. This is
|
|
389
|
+
* the same put-back `Input` does with `restoreValue`, once per box.
|
|
390
|
+
*/
|
|
391
|
+
paint() {
|
|
392
|
+
const characters = this.characters;
|
|
393
|
+
for (const [at, cell] of this.cells.entries()) {
|
|
394
|
+
const want = characters[at] ?? '';
|
|
395
|
+
if (cell.value !== want) {
|
|
396
|
+
cell.value = want;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Announces the code as entered, from whichever moment reached it first.
|
|
402
|
+
*
|
|
403
|
+
* **One writer, because the fact has two moments and only one meaning.** A
|
|
404
|
+
* filled code and a row being left are both "the reader is done with this",
|
|
405
|
+
* and they were two dispatch sites kept from disagreeing by both remembering
|
|
406
|
+
* to update `committed`. They are one call now, so a third moment cannot be
|
|
407
|
+
* added that fires without baselining, which is the way the pair would have
|
|
408
|
+
* drifted.
|
|
409
|
+
*
|
|
410
|
+
* Silent when nothing moved since the baseline, which is what makes it safe
|
|
411
|
+
* to call from both: completing a code that is already the committed one, or
|
|
412
|
+
* leaving a row nobody edited, says nothing.
|
|
413
|
+
*
|
|
414
|
+
* `composed: false`, like the native event this stands in for. It is
|
|
415
|
+
* dispatched on the host, so a consumer hears it.
|
|
416
|
+
*/
|
|
417
|
+
commit() {
|
|
418
|
+
if (this.value === this.committed) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
this.committed = this.value;
|
|
422
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: false }));
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Reports the code and a verdict on it.
|
|
426
|
+
*
|
|
427
|
+
* **A half-entered code is invalid whether or not it is required, and that is
|
|
428
|
+
* the platform's answer rather than ours.** It looked like an oversight worth
|
|
429
|
+
* a question: `required` guards `valueMissing` and nothing guards `tooShort`,
|
|
430
|
+
* so an optional code is refused for being half filled. Measured in Chromium
|
|
431
|
+
* against a native `<input minlength="4">` with real keystrokes, which is the
|
|
432
|
+
* feature this is the same shape as:
|
|
433
|
+
*
|
|
434
|
+
* | | optional | required |
|
|
435
|
+
* |---|---|---|
|
|
436
|
+
* | typed `12` | `tooShort`, invalid | `tooShort`, invalid |
|
|
437
|
+
* | left empty | valid | `valueMissing` |
|
|
438
|
+
* | typed `1234` | valid | valid |
|
|
439
|
+
*
|
|
440
|
+
* So `required` decides whether *nothing* is acceptable, and the length rule
|
|
441
|
+
* applies to anything that is not nothing. That is the right split for a code
|
|
442
|
+
* as well: two of four characters is not an optional code omitted, it is a
|
|
443
|
+
* code got wrong, and submitting it would send a fragment. The gate on the
|
|
444
|
+
* message is `touched` rather than `required`, which is what stops a reader
|
|
445
|
+
* being told off before they have had a go.
|
|
446
|
+
*
|
|
447
|
+
* `execCommand('insertText')` is not enough to measure this: `tooShort`
|
|
448
|
+
* applies only to a value the user edited, and the first run of the probe
|
|
449
|
+
* reported every case valid because nothing had set that flag.
|
|
450
|
+
*/
|
|
451
|
+
sync() {
|
|
452
|
+
this.internals.setFormValue(this.value);
|
|
453
|
+
const first = this.cells[0];
|
|
454
|
+
if (!first) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (this.required && this.value === '') {
|
|
458
|
+
this.internals.setValidity({ valueMissing: true }, 'Please fill in this field.', first);
|
|
459
|
+
}
|
|
460
|
+
else if (this.value !== '' && this.value.length < this.count) {
|
|
461
|
+
this.internals.setValidity({ tooShort: true }, `Please enter all ${this.count} characters.`, first);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
this.internals.setValidity({}, '', first);
|
|
465
|
+
}
|
|
466
|
+
// Straight after, because validity moved without any property moving.
|
|
467
|
+
this.refreshFeedback();
|
|
468
|
+
}
|
|
469
|
+
/** Puts the caret in the first empty box, which is where a reader resumes. */
|
|
470
|
+
focus(options) {
|
|
471
|
+
const cells = this.cells;
|
|
472
|
+
const at = Math.min(this.value.length, cells.length - 1);
|
|
473
|
+
cells[at]?.focus(options);
|
|
474
|
+
}
|
|
475
|
+
/** Empties every box and returns to the first. */
|
|
476
|
+
clear() {
|
|
477
|
+
this.requestValue('');
|
|
478
|
+
this.updateComplete.then(() => {
|
|
479
|
+
this.paint();
|
|
480
|
+
this.cells[0]?.focus();
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
firstUpdated(changed) {
|
|
484
|
+
super.firstUpdated(changed);
|
|
485
|
+
this.committed = this.value;
|
|
486
|
+
}
|
|
487
|
+
updated(changed) {
|
|
488
|
+
super.updated(changed);
|
|
489
|
+
this.paint();
|
|
490
|
+
this.sync();
|
|
491
|
+
}
|
|
492
|
+
render() {
|
|
493
|
+
const characters = this.characters;
|
|
494
|
+
/* `renderGroupLabel` and not `renderLabel`, which is what every other
|
|
495
|
+
grouped control here uses. `renderLabel` emits `for="control"` and no
|
|
496
|
+
element in this template carries that id, because a pin input is a row of
|
|
497
|
+
cells and not one control: the visible label pointed at nothing, so
|
|
498
|
+
pressing it focused nothing and the group had no accessible name at all.
|
|
499
|
+
The set names itself through `hostAria` instead. */
|
|
500
|
+
return html `${this.renderGroupLabel()}
|
|
501
|
+
<div
|
|
502
|
+
part="field"
|
|
503
|
+
role="group"
|
|
504
|
+
aria-describedby=${this.describedBy ?? nothing}
|
|
505
|
+
@focusin=${this.handleFocusIn}
|
|
506
|
+
@focusout=${this.handleFocusOut}
|
|
507
|
+
>
|
|
508
|
+
${characters.map((character, at) => html `<input
|
|
509
|
+
part="cell"
|
|
510
|
+
.value=${character}
|
|
511
|
+
type=${this.mask ? 'password' : 'text'}
|
|
512
|
+
placeholder=${this.placeholder || nothing}
|
|
513
|
+
inputmode=${this.type === 'numeric' ? 'numeric' : 'text'}
|
|
514
|
+
autocomplete=${at === 0 ? this.autocomplete || nothing : 'off'}
|
|
515
|
+
aria-label=${this.cellLabel
|
|
516
|
+
.replaceAll('{index}', String(at + 1))
|
|
517
|
+
.replaceAll('{length}', String(this.count))}
|
|
518
|
+
?disabled=${this.isDisabled}
|
|
519
|
+
?readonly=${this.readOnly}
|
|
520
|
+
@focus=${this.handleFocus}
|
|
521
|
+
@input=${this.handleInput}
|
|
522
|
+
@keydown=${this.handleKeydown}
|
|
523
|
+
@paste=${this.handlePaste}
|
|
524
|
+
/>`)}
|
|
525
|
+
</div>
|
|
526
|
+
${this.renderHint()}${this.renderFeedback()}`;
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
__decorate([
|
|
530
|
+
property()
|
|
531
|
+
], HelixHeadlessPinInput.prototype, "autocomplete", void 0);
|
|
532
|
+
__decorate([
|
|
533
|
+
property({ type: Number })
|
|
534
|
+
], HelixHeadlessPinInput.prototype, "length", void 0);
|
|
535
|
+
__decorate([
|
|
536
|
+
property()
|
|
537
|
+
], HelixHeadlessPinInput.prototype, "type", void 0);
|
|
538
|
+
__decorate([
|
|
539
|
+
property({ reflect: true, type: Boolean })
|
|
540
|
+
], HelixHeadlessPinInput.prototype, "mask", void 0);
|
|
541
|
+
__decorate([
|
|
542
|
+
property()
|
|
543
|
+
], HelixHeadlessPinInput.prototype, "placeholder", void 0);
|
|
544
|
+
__decorate([
|
|
545
|
+
property({ attribute: 'readonly', reflect: true, type: Boolean })
|
|
546
|
+
], HelixHeadlessPinInput.prototype, "readOnly", void 0);
|
|
547
|
+
__decorate([
|
|
548
|
+
property({ attribute: 'cell-label' })
|
|
549
|
+
], HelixHeadlessPinInput.prototype, "cellLabel", void 0);
|
|
550
|
+
HelixHeadlessPinInput = __decorate([
|
|
551
|
+
customElement('helix-headless-pin-input')
|
|
552
|
+
], HelixHeadlessPinInput);
|
|
553
|
+
export { HelixHeadlessPinInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/popover-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/popover-root.js';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { CommandController, HelixElement, type Placement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
declare const HelixHeadlessPopover_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
|
|
4
|
+
/**
|
|
5
|
+
* @summary A panel that opens against a trigger and holds anything.
|
|
6
|
+
*
|
|
7
|
+
* @description
|
|
8
|
+
* The general case the specific overlays are versions of. `Menu` holds
|
|
9
|
+
* commands, `Select` holds options, `Tooltip` holds a description; this holds
|
|
10
|
+
* whatever the consumer puts in it, which is why it has no keyboard of its own
|
|
11
|
+
* and no opinion about what is inside.
|
|
12
|
+
*
|
|
13
|
+
* **The trigger is slotted and stays the consumer's own element.** Nothing is
|
|
14
|
+
* written onto it: no `aria-expanded`, no `id`, no `tabindex`. A component that
|
|
15
|
+
* decorates a consumer's markup after upgrading is a component whose output a
|
|
16
|
+
* React consumer's hydration disagrees with, and the attribute they would care
|
|
17
|
+
* about most is the one that changes on every open.
|
|
18
|
+
*
|
|
19
|
+
* **The panel is in the top layer**, through the popover API, so it clears any
|
|
20
|
+
* ancestor with `overflow: hidden` without a `z-index` fight. `position: fixed`
|
|
21
|
+
* does not do that: it is still clipped by `overflow`, and re-anchored entirely
|
|
22
|
+
* by an ancestor carrying `transform`, `filter` or `contain`.
|
|
23
|
+
*
|
|
24
|
+
* **It opens on click, and from a command.** `command="--show"` and its
|
|
25
|
+
* siblings work here the way they do on `Dialog` and `Drawer`, so a trigger
|
|
26
|
+
* elsewhere on the page can open it without a line of script and without being
|
|
27
|
+
* slotted into it.
|
|
28
|
+
*
|
|
29
|
+
* @since 0.1.0
|
|
30
|
+
* @status stable
|
|
31
|
+
* @category overlays
|
|
32
|
+
* @aliases Popper, Flyout, Overlay
|
|
33
|
+
* @documentation https://helix.fusion.dev/components/popover
|
|
34
|
+
*
|
|
35
|
+
* @slot - The contents of the panel.
|
|
36
|
+
* @slot trigger - What opens it.
|
|
37
|
+
*
|
|
38
|
+
* @event {HelixBeforeCloseEvent} helix-before-close - Fired before it closes, and cancelable.
|
|
39
|
+
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before it opens, and cancelable.
|
|
40
|
+
* @event {Event} toggle - Says the state moved, carrying `newState` and `oldState`.
|
|
41
|
+
*
|
|
42
|
+
* @csspart content - The scrolling region inside the panel.
|
|
43
|
+
* @csspart popover - The panel.
|
|
44
|
+
*/
|
|
45
|
+
export declare class HelixHeadlessPopover extends HelixHeadlessPopover_base {
|
|
46
|
+
/**
|
|
47
|
+
* Accessible name for the panel.
|
|
48
|
+
*/
|
|
49
|
+
label: string;
|
|
50
|
+
/**
|
|
51
|
+
* Space between the trigger and the panel, in pixels.
|
|
52
|
+
*/
|
|
53
|
+
offset: number;
|
|
54
|
+
/**
|
|
55
|
+
* Preferred side.
|
|
56
|
+
*
|
|
57
|
+
* It moves if that side does not fit.
|
|
58
|
+
*/
|
|
59
|
+
placement: Placement;
|
|
60
|
+
private readonly panel;
|
|
61
|
+
/**
|
|
62
|
+
* The invoker that last opened the panel, when a command did.
|
|
63
|
+
*
|
|
64
|
+
* The handlers discarded the `source` the controller hands them, which cost
|
|
65
|
+
* two things at once. The panel was positioned against the host, and the
|
|
66
|
+
* styled tier makes the host a boxless `display: contents` element, so a
|
|
67
|
+
* popover opened by a command outside it opened against the window origin.
|
|
68
|
+
* And the outside-press dismissal had no way to know the invoker was part of
|
|
69
|
+
* the control: the press fires on `pointerdown`, the command arrives on the
|
|
70
|
+
* `click` after it, so a `--toggle` button outside the host closed the panel
|
|
71
|
+
* and immediately reopened it, for ever.
|
|
72
|
+
*/
|
|
73
|
+
private invoker;
|
|
74
|
+
protected readonly commands: CommandController;
|
|
75
|
+
/** The element the panel opens against. */
|
|
76
|
+
private get trigger();
|
|
77
|
+
/** Opens the panel. */
|
|
78
|
+
show(): void;
|
|
79
|
+
/** Closes the panel and puts focus back on the trigger. */
|
|
80
|
+
hide(): void;
|
|
81
|
+
private readonly handleTriggerClick;
|
|
82
|
+
/** Tells a slotted trigger the disclosure state, by property. See `tellDisclosure`. */
|
|
83
|
+
private tellTrigger;
|
|
84
|
+
/**
|
|
85
|
+
* Puts the panel where `open` says it should be, however it got there.
|
|
86
|
+
*
|
|
87
|
+
* `show()` and `hide()` raised and lowered it, and nothing else did, so a
|
|
88
|
+
* panel opened by *declaring* `open` (or `default-open`, or a consumer
|
|
89
|
+
* assigning the property) rendered visible and was never in the top layer and
|
|
90
|
+
* never anchored. Every imperative path worked and every declarative one was
|
|
91
|
+
* quietly half-open: clipped by the first ancestor with `overflow`, and
|
|
92
|
+
* positioned wherever normal flow left it.
|
|
93
|
+
*
|
|
94
|
+
* Driving the controller from the resolved state rather than from the two
|
|
95
|
+
* methods is what makes the two paths the same path. The controller ignores a
|
|
96
|
+
* `show()` on something already raised, so this costs nothing when the
|
|
97
|
+
* methods did the work.
|
|
98
|
+
*/
|
|
99
|
+
protected updated(changed: PropertyValues): void;
|
|
100
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
101
|
+
protected render(): TemplateResult;
|
|
102
|
+
}
|
|
103
|
+
declare global {
|
|
104
|
+
interface HTMLElementTagNameMap {
|
|
105
|
+
'helix-headless-popover': HelixHeadlessPopover;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export {};
|