@human-kit/svelte-components 1.0.0-alpha.1 → 1.0.0-alpha.10
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/dist/FOCUS_STATE_CONTRACT.md +63 -0
- package/dist/FOCUS_STATE_REVIEW_TEMPLATE.md +70 -0
- package/dist/button/README.md +48 -0
- package/dist/button/TODO.md +13 -0
- package/dist/button/index.d.ts +5 -0
- package/dist/button/index.js +4 -0
- package/dist/button/index.parts.d.ts +1 -0
- package/dist/button/index.parts.js +1 -0
- package/dist/button/root/README.md +43 -0
- package/dist/button/root/button-root.svelte +362 -0
- package/dist/button/root/button-root.svelte.d.ts +21 -0
- package/dist/button/root/button-test.svelte +76 -0
- package/dist/button/root/button-test.svelte.d.ts +11 -0
- package/dist/calendar/README.md +66 -0
- package/dist/calendar/TODO.md +23 -0
- package/dist/calendar/body-cell/README.md +15 -0
- package/dist/calendar/body-cell/calendar-body-cell.svelte +230 -0
- package/dist/calendar/body-cell/calendar-body-cell.svelte.d.ts +9 -0
- package/dist/calendar/grid/README.md +13 -0
- package/dist/calendar/grid/calendar-grid-month-scope.svelte +16 -0
- package/dist/calendar/grid/calendar-grid-month-scope.svelte.d.ts +8 -0
- package/dist/calendar/grid/calendar-grid.svelte +45 -0
- package/dist/calendar/grid/calendar-grid.svelte.d.ts +8 -0
- package/dist/calendar/grid/month-scope.d.ts +2 -0
- package/dist/calendar/grid/month-scope.js +8 -0
- package/dist/calendar/grid-body/README.md +13 -0
- package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte +13 -0
- package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte.d.ts +18 -0
- package/dist/calendar/grid-body/calendar-grid-body.svelte +36 -0
- package/dist/calendar/grid-body/calendar-grid-body.svelte.d.ts +8 -0
- package/dist/calendar/grid-header/README.md +13 -0
- package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte +13 -0
- package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte.d.ts +18 -0
- package/dist/calendar/grid-header/calendar-grid-header.svelte +31 -0
- package/dist/calendar/grid-header/calendar-grid-header.svelte.d.ts +8 -0
- package/dist/calendar/header-cell/README.md +14 -0
- package/dist/calendar/header-cell/calendar-header-cell-test.svelte +11 -0
- package/dist/calendar/header-cell/calendar-header-cell-test.svelte.d.ts +18 -0
- package/dist/calendar/header-cell/calendar-header-cell.svelte +16 -0
- package/dist/calendar/header-cell/calendar-header-cell.svelte.d.ts +8 -0
- package/dist/calendar/heading/README.md +13 -0
- package/dist/calendar/heading/calendar-heading.svelte +17 -0
- package/dist/calendar/heading/calendar-heading.svelte.d.ts +5 -0
- package/dist/calendar/index.d.ts +13 -0
- package/dist/calendar/index.js +13 -0
- package/dist/calendar/index.parts.d.ts +9 -0
- package/dist/calendar/index.parts.js +9 -0
- package/dist/calendar/root/README.md +24 -0
- package/dist/calendar/root/calendar-root-bind-value-test.svelte +14 -0
- package/dist/calendar/root/calendar-root-bind-value-test.svelte.d.ts +3 -0
- package/dist/calendar/root/calendar-root-controlled-clear-test.svelte +20 -0
- package/dist/calendar/root/calendar-root-controlled-clear-test.svelte.d.ts +3 -0
- package/dist/calendar/root/calendar-root-test.svelte +71 -0
- package/dist/calendar/root/calendar-root-test.svelte.d.ts +13 -0
- package/dist/calendar/root/calendar-root.svelte +143 -0
- package/dist/calendar/root/calendar-root.svelte.d.ts +31 -0
- package/dist/calendar/root/context.d.ts +66 -0
- package/dist/calendar/root/context.js +727 -0
- package/dist/calendar/root/date-utils.d.ts +17 -0
- package/dist/calendar/root/date-utils.js +94 -0
- package/dist/calendar/trigger-next/README.md +14 -0
- package/dist/calendar/trigger-next/calendar-trigger-next.svelte +43 -0
- package/dist/calendar/trigger-next/calendar-trigger-next.svelte.d.ts +9 -0
- package/dist/calendar/trigger-previous/README.md +14 -0
- package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte +43 -0
- package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte.d.ts +9 -0
- package/dist/checkbox/README.md +53 -0
- package/dist/checkbox/TODO.md +16 -0
- package/dist/checkbox/index.d.ts +6 -0
- package/dist/checkbox/index.js +6 -0
- package/dist/checkbox/index.parts.d.ts +2 -0
- package/dist/checkbox/index.parts.js +2 -0
- package/dist/checkbox/indicator/README.md +23 -0
- package/dist/checkbox/indicator/checkbox-indicator.svelte +43 -0
- package/dist/checkbox/indicator/checkbox-indicator.svelte.d.ts +10 -0
- package/dist/checkbox/root/README.md +47 -0
- package/dist/checkbox/root/checkbox-label-test.svelte +10 -0
- package/dist/checkbox/root/checkbox-label-test.svelte.d.ts +18 -0
- package/dist/checkbox/root/checkbox-root.svelte +361 -0
- package/dist/checkbox/root/checkbox-root.svelte.d.ts +23 -0
- package/dist/checkbox/root/checkbox-test.svelte +59 -0
- package/dist/checkbox/root/checkbox-test.svelte.d.ts +18 -0
- package/dist/checkbox/root/context.d.ts +21 -0
- package/dist/checkbox/root/context.js +15 -0
- package/dist/clock/README.md +75 -0
- package/dist/clock/axis/README.md +24 -0
- package/dist/clock/axis/clock-axis.svelte +37 -0
- package/dist/clock/axis/clock-axis.svelte.d.ts +8 -0
- package/dist/clock/hooks/use-wheel-scroll.svelte.d.ts +16 -0
- package/dist/clock/hooks/use-wheel-scroll.svelte.js +336 -0
- package/dist/clock/index.d.ts +10 -0
- package/dist/clock/index.js +10 -0
- package/dist/clock/index.parts.d.ts +4 -0
- package/dist/clock/index.parts.js +4 -0
- package/dist/clock/root/README.md +38 -0
- package/dist/clock/root/clock-root-test.svelte +62 -0
- package/dist/clock/root/clock-root-test.svelte.d.ts +14 -0
- package/dist/clock/root/clock-root.svelte +329 -0
- package/dist/clock/root/clock-root.svelte.d.ts +25 -0
- package/dist/clock/root/context.d.ts +22 -0
- package/dist/clock/root/context.js +15 -0
- package/dist/clock/root/resolve-visible-columns.d.ts +7 -0
- package/dist/clock/root/resolve-visible-columns.js +16 -0
- package/dist/clock/root/time-utils.d.ts +48 -0
- package/dist/clock/root/time-utils.js +314 -0
- package/dist/clock/root/wheel-options.d.ts +17 -0
- package/dist/clock/root/wheel-options.js +63 -0
- package/dist/clock/wheel-column/README.md +25 -0
- package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte +16 -0
- package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte.d.ts +3 -0
- package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte +29 -0
- package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte.d.ts +6 -0
- package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte +11 -0
- package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte.d.ts +3 -0
- package/dist/clock/wheel-column/clock-wheel-column-test.svelte +38 -0
- package/dist/clock/wheel-column/clock-wheel-column-test.svelte.d.ts +12 -0
- package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte +38 -0
- package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte.d.ts +12 -0
- package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte +29 -0
- package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte.d.ts +6 -0
- package/dist/clock/wheel-column/clock-wheel-column.svelte +499 -0
- package/dist/clock/wheel-column/clock-wheel-column.svelte.d.ts +17 -0
- package/dist/clock/wheel-item/README.md +17 -0
- package/dist/clock/wheel-item/clock-wheel-item.svelte +49 -0
- package/dist/clock/wheel-item/clock-wheel-item.svelte.d.ts +17 -0
- package/dist/combobox/README.md +46 -0
- package/dist/combobox/TODO.md +28 -175
- package/dist/combobox/button/README.md +20 -0
- package/dist/combobox/button/combobox-button-test.svelte +27 -0
- package/dist/combobox/button/combobox-button-test.svelte.d.ts +6 -0
- package/dist/combobox/button/combobox-button.svelte +10 -11
- package/dist/combobox/clear/README.md +21 -0
- package/dist/combobox/clear/combobox-clear-test.svelte +34 -0
- package/dist/combobox/clear/combobox-clear-test.svelte.d.ts +3 -0
- package/dist/combobox/clear/combobox-clear.svelte +61 -0
- package/dist/combobox/clear/combobox-clear.svelte.d.ts +9 -0
- package/dist/combobox/index.d.ts +5 -3
- package/dist/combobox/index.js +5 -3
- package/dist/combobox/index.parts.d.ts +2 -0
- package/dist/combobox/index.parts.js +2 -0
- package/dist/combobox/input/README.md +16 -0
- package/dist/combobox/input/combobox-input.svelte +43 -12
- package/dist/combobox/item/README.md +27 -0
- package/dist/combobox/item/combobox-item-implicit-text-test.svelte +1 -1
- package/dist/combobox/item/combobox-listboxitem.svelte +1 -9
- package/dist/combobox/item-indicator/README.md +15 -0
- package/dist/combobox/item-indicator/combobox-item-indicator.svelte +4 -15
- package/dist/combobox/list/README.md +27 -0
- package/dist/combobox/list/combobox-listbox.svelte.d.ts +1 -1
- package/dist/combobox/popover/README.md +13 -0
- package/dist/combobox/popover/combobox-popover.svelte +133 -17
- package/dist/combobox/popover/combobox-scrollable-list-test.svelte +23 -0
- package/dist/combobox/popover/combobox-scrollable-list-test.svelte.d.ts +18 -0
- package/dist/combobox/root/README.md +45 -0
- package/dist/combobox/root/combobox-multiselect-test.svelte +1 -1
- package/dist/combobox/root/combobox-numeric-string-id-test.svelte +1 -1
- package/dist/combobox/root/combobox-test.svelte +16 -3
- package/dist/combobox/root/combobox-test.svelte.d.ts +1 -0
- package/dist/combobox/root/combobox.svelte +78 -2
- package/dist/combobox/root/combobox.svelte.d.ts +1 -0
- package/dist/combobox/root/context.d.ts +9 -1
- package/dist/combobox/tag/README.md +37 -0
- package/dist/combobox/tag-remove/README.md +14 -0
- package/dist/combobox/tag-remove/combobox-tag-remove.svelte +3 -2
- package/dist/combobox/tags/README.md +23 -0
- package/dist/combobox/trigger/README.md +21 -0
- package/dist/combobox/trigger/combobox-trigger.svelte +56 -0
- package/dist/combobox/trigger/combobox-trigger.svelte.d.ts +9 -0
- package/dist/datepicker/README.md +100 -0
- package/dist/datepicker/TODO.md +28 -0
- package/dist/datepicker/calendar/README.md +19 -0
- package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte +60 -0
- package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte.d.ts +3 -0
- package/dist/datepicker/calendar/date-picker-calendar.svelte +65 -0
- package/dist/datepicker/calendar/date-picker-calendar.svelte.d.ts +10 -0
- package/dist/datepicker/index.d.ts +18 -0
- package/dist/datepicker/index.js +18 -0
- package/dist/datepicker/index.parts.d.ts +14 -0
- package/dist/datepicker/index.parts.js +14 -0
- package/dist/datepicker/input/README.md +15 -0
- package/dist/datepicker/input/date-picker-input.svelte +108 -0
- package/dist/datepicker/input/date-picker-input.svelte.d.ts +11 -0
- package/dist/datepicker/internal/strict-props.d.ts +2 -0
- package/dist/datepicker/internal/strict-props.js +28 -0
- package/dist/datepicker/popover/README.md +20 -0
- package/dist/datepicker/popover/date-picker-popover-handler-test.svelte +57 -0
- package/dist/datepicker/popover/date-picker-popover-handler-test.svelte.d.ts +3 -0
- package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte +45 -0
- package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte.d.ts +18 -0
- package/dist/datepicker/popover/date-picker-popover.svelte +87 -0
- package/dist/datepicker/popover/date-picker-popover.svelte.d.ts +7 -0
- package/dist/datepicker/root/README.md +38 -0
- package/dist/datepicker/root/context.d.ts +43 -0
- package/dist/datepicker/root/context.js +15 -0
- package/dist/datepicker/root/date-picker-bindable-empty-test.svelte +24 -0
- package/dist/datepicker/root/date-picker-bindable-empty-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-bindable-test.svelte +41 -0
- package/dist/datepicker/root/date-picker-bindable-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-empty-test.svelte +47 -0
- package/dist/datepicker/root/date-picker-empty-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-locale-typing-test.svelte +47 -0
- package/dist/datepicker/root/date-picker-locale-typing-test.svelte.d.ts +3 -0
- package/dist/datepicker/root/date-picker-open-cancel-test.svelte +54 -0
- package/dist/datepicker/root/date-picker-open-cancel-test.svelte.d.ts +8 -0
- package/dist/datepicker/root/date-picker-root.svelte +495 -0
- package/dist/datepicker/root/date-picker-root.svelte.d.ts +24 -0
- package/dist/datepicker/root/date-picker-test.svelte +86 -0
- package/dist/datepicker/root/date-picker-test.svelte.d.ts +13 -0
- package/dist/datepicker/root/date-utils.d.ts +17 -0
- package/dist/datepicker/root/date-utils.js +138 -0
- package/dist/datepicker/root/draft-evaluation.d.ts +13 -0
- package/dist/datepicker/root/draft-evaluation.js +56 -0
- package/dist/datepicker/root/focus-controller.d.ts +3 -0
- package/dist/datepicker/root/focus-controller.js +15 -0
- package/dist/datepicker/root/open-change.d.ts +5 -0
- package/dist/datepicker/root/open-change.js +13 -0
- package/dist/datepicker/root/open-controller.d.ts +7 -0
- package/dist/datepicker/root/open-controller.js +15 -0
- package/dist/datepicker/root/segment-controller.d.ts +8 -0
- package/dist/datepicker/root/segment-controller.js +53 -0
- package/dist/datepicker/root/segment-state.d.ts +18 -0
- package/dist/datepicker/root/segment-state.js +134 -0
- package/dist/datepicker/root/value-commit.d.ts +4 -0
- package/dist/datepicker/root/value-commit.js +8 -0
- package/dist/datepicker/segment/README.md +14 -0
- package/dist/datepicker/segment/date-picker-segment.svelte +319 -0
- package/dist/datepicker/segment/date-picker-segment.svelte.d.ts +9 -0
- package/dist/datepicker/trigger/README.md +14 -0
- package/dist/datepicker/trigger/date-picker-trigger.svelte +110 -0
- package/dist/datepicker/trigger/date-picker-trigger.svelte.d.ts +9 -0
- package/dist/dialog/README.md +35 -0
- package/dist/dialog/content/README.md +16 -0
- package/dist/dialog/content/dialog-content.svelte +6 -6
- package/dist/dialog/index.d.ts +3 -3
- package/dist/dialog/index.js +2 -2
- package/dist/dialog/overlay/README.md +13 -0
- package/dist/dialog/portal/README.md +12 -0
- package/dist/dialog/root/README.md +53 -0
- package/dist/dialog/root/context.d.ts +2 -1
- package/dist/dialog/root/dialog-root.svelte +9 -2
- package/dist/dialog/trigger/README.md +12 -0
- package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte +19 -0
- package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte.d.ts +18 -0
- package/dist/dialog/trigger/dialog-trigger.svelte +18 -6
- package/dist/index.d.ts +31 -13
- package/dist/index.js +31 -13
- package/dist/input/README.md +38 -0
- package/dist/input/TODO.md +12 -0
- package/dist/input/input-test.svelte +43 -0
- package/dist/input/input-test.svelte.d.ts +12 -0
- package/dist/input/input.svelte +151 -7
- package/dist/input/input.svelte.d.ts +8 -2
- package/dist/listbox/README.md +26 -0
- package/dist/listbox/index.d.ts +3 -3
- package/dist/listbox/index.js +3 -3
- package/dist/listbox/item/README.md +25 -0
- package/dist/listbox/item/listbox-item.svelte +152 -2
- package/dist/listbox/item/listbox-item.svelte.d.ts +2 -0
- package/dist/listbox/root/README.md +40 -0
- package/dist/listbox/root/listbox-test.svelte +14 -2
- package/dist/listbox/root/listbox-test.svelte.d.ts +1 -0
- package/dist/listbox/root/listbox.svelte +44 -0
- package/dist/listbox/root/listbox.svelte.d.ts +2 -2
- package/dist/locale-provider/context.d.ts +8 -0
- package/dist/locale-provider/context.js +18 -0
- package/dist/locale-provider/index.d.ts +4 -0
- package/dist/locale-provider/index.js +4 -0
- package/dist/locale-provider/locale-provider-initial-value-test.svelte +15 -0
- package/dist/locale-provider/locale-provider-initial-value-test.svelte.d.ts +7 -0
- package/dist/locale-provider/locale-provider-test.svelte +20 -0
- package/dist/locale-provider/locale-provider-test.svelte.d.ts +6 -0
- package/dist/locale-provider/locale-provider-value-probe.svelte +22 -0
- package/dist/locale-provider/locale-provider-value-probe.svelte.d.ts +6 -0
- package/dist/locale-provider/locale-provider.svelte +23 -0
- package/dist/locale-provider/locale-provider.svelte.d.ts +8 -0
- package/dist/popover/README.md +42 -0
- package/dist/popover/content/README.md +36 -0
- package/dist/popover/content/popover-content-standalone-test.svelte +28 -0
- package/dist/popover/content/popover-content-standalone-test.svelte.d.ts +6 -0
- package/dist/popover/content/popover-content-test.svelte +32 -2
- package/dist/popover/content/popover-content-test.svelte.d.ts +3 -1
- package/dist/popover/content/popover-content.svelte +276 -23
- package/dist/popover/content/popover-content.svelte.d.ts +5 -1
- package/dist/popover/index.d.ts +3 -3
- package/dist/popover/index.js +3 -5
- package/dist/popover/root/README.md +25 -0
- package/dist/popover/root/context.d.ts +16 -7
- package/dist/popover/root/context.js +0 -2
- package/dist/popover/root/focus-state.d.ts +4 -0
- package/dist/popover/root/focus-state.js +33 -0
- package/dist/popover/root/popover-root.svelte +90 -17
- package/dist/popover/root/popover-root.svelte.d.ts +2 -1
- package/dist/popover/root/popover-test.svelte +2 -1
- package/dist/popover/root/popover-test.svelte.d.ts +2 -1
- package/dist/popover/trigger/README.md +23 -0
- package/dist/popover/trigger/popover-trigger-button.svelte +14 -10
- package/dist/popover/trigger/popover-trigger-button.svelte.d.ts +2 -3
- package/dist/popover/trigger/popover-trigger-multi-button-test.svelte +16 -0
- package/dist/popover/trigger/popover-trigger-multi-button-test.svelte.d.ts +18 -0
- package/dist/popover/trigger/popover-trigger.svelte +19 -7
- package/dist/portal/portal.svelte +3 -1
- package/dist/primitives/click-outside.d.ts +1 -1
- package/dist/primitives/click-outside.js +1 -1
- package/dist/primitives/floating.js +12 -4
- package/dist/primitives/focus-trap.d.ts +7 -2
- package/dist/primitives/focus-trap.js +50 -17
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/input-modality.d.ts +7 -0
- package/dist/primitives/input-modality.js +125 -0
- package/dist/table/IMPLEMENTATION_NOTES.md +8 -0
- package/dist/table/PLAN-HIDDEN-COLUMNS.md +152 -0
- package/dist/table/PLAN.md +924 -0
- package/dist/table/README.md +116 -0
- package/dist/table/SELECTION_CHECKBOX_PLAN.md +234 -0
- package/dist/table/TODO.md +100 -0
- package/dist/table/body/README.md +24 -0
- package/dist/table/body/table-body.svelte +25 -0
- package/dist/table/body/table-body.svelte.d.ts +9 -0
- package/dist/table/cell/README.md +25 -0
- package/dist/table/cell/table-cell.svelte +247 -0
- package/dist/table/cell/table-cell.svelte.d.ts +9 -0
- package/dist/table/checkbox/README.md +38 -0
- package/dist/table/checkbox/table-checkbox-test.svelte +121 -0
- package/dist/table/checkbox/table-checkbox-test.svelte.d.ts +16 -0
- package/dist/table/checkbox/table-checkbox.svelte +274 -0
- package/dist/table/checkbox/table-checkbox.svelte.d.ts +13 -0
- package/dist/table/checkbox-indicator/README.md +29 -0
- package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte +22 -0
- package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte.d.ts +10 -0
- package/dist/table/column/README.md +32 -0
- package/dist/table/column/table-column.svelte +108 -0
- package/dist/table/column/table-column.svelte.d.ts +18 -0
- package/dist/table/column-header-cell/README.md +28 -0
- package/dist/table/column-header-cell/table-column-header-cell.svelte +281 -0
- package/dist/table/column-header-cell/table-column-header-cell.svelte.d.ts +9 -0
- package/dist/table/column-resizer/README.md +32 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte +51 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte +83 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-test.svelte +75 -0
- package/dist/table/column-resizer/table-column-resizer-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer.svelte +616 -0
- package/dist/table/column-resizer/table-column-resizer.svelte.d.ts +11 -0
- package/dist/table/empty-state/README.md +25 -0
- package/dist/table/empty-state/table-empty-state.svelte +38 -0
- package/dist/table/empty-state/table-empty-state.svelte.d.ts +8 -0
- package/dist/table/footer/README.md +24 -0
- package/dist/table/footer/table-footer.svelte +19 -0
- package/dist/table/footer/table-footer.svelte.d.ts +9 -0
- package/dist/table/header/README.md +24 -0
- package/dist/table/header/table-header.svelte +19 -0
- package/dist/table/header/table-header.svelte.d.ts +9 -0
- package/dist/table/index.d.ts +16 -0
- package/dist/table/index.js +16 -0
- package/dist/table/index.parts.d.ts +12 -0
- package/dist/table/index.parts.js +12 -0
- package/dist/table/root/README.md +56 -0
- package/dist/table/root/context.d.ts +198 -0
- package/dist/table/root/context.js +1426 -0
- package/dist/table/root/table-reorder-test.svelte +64 -0
- package/dist/table/root/table-reorder-test.svelte.d.ts +3 -0
- package/dist/table/root/table-root.svelte +410 -0
- package/dist/table/root/table-root.svelte.d.ts +29 -0
- package/dist/table/root/table-test.svelte +165 -0
- package/dist/table/root/table-test.svelte.d.ts +25 -0
- package/dist/table/row/README.md +27 -0
- package/dist/table/row/table-row.svelte +321 -0
- package/dist/table/row/table-row.svelte.d.ts +13 -0
- package/dist/test-utils/focus-contract.d.ts +3 -0
- package/dist/test-utils/focus-contract.js +26 -0
- package/dist/timepicker/IMPLEMENTATION_PLAN.md +254 -0
- package/dist/timepicker/README.md +97 -0
- package/dist/timepicker/TODO.md +86 -0
- package/dist/timepicker/clock/README.md +14 -0
- package/dist/timepicker/clock/time-picker-clock-test.svelte +45 -0
- package/dist/timepicker/clock/time-picker-clock-test.svelte.d.ts +11 -0
- package/dist/timepicker/clock/time-picker-clock.svelte +65 -0
- package/dist/timepicker/clock/time-picker-clock.svelte.d.ts +10 -0
- package/dist/timepicker/index.d.ts +14 -0
- package/dist/timepicker/index.js +14 -0
- package/dist/timepicker/index.parts.d.ts +8 -0
- package/dist/timepicker/index.parts.js +8 -0
- package/dist/timepicker/input/README.md +15 -0
- package/dist/timepicker/input/time-picker-input-forwarding-test.svelte +40 -0
- package/dist/timepicker/input/time-picker-input-forwarding-test.svelte.d.ts +3 -0
- package/dist/timepicker/input/time-picker-input.svelte +109 -0
- package/dist/timepicker/input/time-picker-input.svelte.d.ts +11 -0
- package/dist/timepicker/internal/strict-props.d.ts +4 -0
- package/dist/timepicker/internal/strict-props.js +51 -0
- package/dist/timepicker/popover/README.md +20 -0
- package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte +22 -0
- package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte.d.ts +3 -0
- package/dist/timepicker/popover/time-picker-popover.svelte +89 -0
- package/dist/timepicker/popover/time-picker-popover.svelte.d.ts +7 -0
- package/dist/timepicker/root/README.md +42 -0
- package/dist/timepicker/root/context.d.ts +51 -0
- package/dist/timepicker/root/context.js +15 -0
- package/dist/timepicker/root/time-picker-12h-test.svelte +22 -0
- package/dist/timepicker/root/time-picker-12h-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-bindable-test.svelte +25 -0
- package/dist/timepicker/root/time-picker-bindable-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-empty-test.svelte +20 -0
- package/dist/timepicker/root/time-picker-empty-test.svelte.d.ts +3 -0
- package/dist/timepicker/root/time-picker-root.svelte +625 -0
- package/dist/timepicker/root/time-picker-root.svelte.d.ts +28 -0
- package/dist/timepicker/root/time-picker-test.svelte +72 -0
- package/dist/timepicker/root/time-picker-test.svelte.d.ts +15 -0
- package/dist/timepicker/root/time-utils.d.ts +1 -0
- package/dist/timepicker/root/time-utils.js +3 -0
- package/dist/timepicker/segment/README.md +14 -0
- package/dist/timepicker/segment/time-picker-segment.svelte +365 -0
- package/dist/timepicker/segment/time-picker-segment.svelte.d.ts +9 -0
- package/dist/timepicker/trigger/README.md +14 -0
- package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte +35 -0
- package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte.d.ts +3 -0
- package/dist/timepicker/trigger/time-picker-trigger.svelte +122 -0
- package/dist/timepicker/trigger/time-picker-trigger.svelte.d.ts +9 -0
- package/dist/utils/date-only.d.ts +11 -0
- package/dist/utils/date-only.js +53 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +41 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Focus State Contract
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
Focus-state contract for composed and interactive library components.
|
|
6
|
+
|
|
7
|
+
## Canonical Attributes
|
|
8
|
+
|
|
9
|
+
- `data-focused`: focused interactive element (real focus or logical focus for active item).
|
|
10
|
+
- `data-focus-visible`: visible focus based on keyboard/screen-reader modality.
|
|
11
|
+
- `data-focus-within`: any descendant inside the composed container has focus.
|
|
12
|
+
|
|
13
|
+
## Serialization Rules
|
|
14
|
+
|
|
15
|
+
1. Attributes are serialized as presence (`"true"`) or absence.
|
|
16
|
+
2. Never serialize `"false"`.
|
|
17
|
+
|
|
18
|
+
## Invariants
|
|
19
|
+
|
|
20
|
+
1. On containers, `data-focus-visible` implies `data-focus-within`.
|
|
21
|
+
2. On items, `data-focus-visible` implies `data-focused`.
|
|
22
|
+
3. On external blur (focus leaves scope), clear container `data-focus-within` and `data-focus-visible`.
|
|
23
|
+
|
|
24
|
+
## Modality
|
|
25
|
+
|
|
26
|
+
- Keyboard/SR: may activate `data-focus-visible`.
|
|
27
|
+
- Pointer: should not activate `data-focus-visible` by default.
|
|
28
|
+
|
|
29
|
+
Canonical implementation lives in `primitives/input-modality.ts`:
|
|
30
|
+
|
|
31
|
+
- `trackInteractionModality(event, target)` records input modality transitions.
|
|
32
|
+
- `shouldShowFocusVisible(target)` resolves whether `data-focus-visible` should be shown.
|
|
33
|
+
- `focusWithModality(target, modality)` atomically sets modality + programmatic focus restore.
|
|
34
|
+
- Keep explicit `trackInteractionModality` calls in component keyboard/pointer handlers to guarantee deterministic modality updates before local focus-state logic runs.
|
|
35
|
+
|
|
36
|
+
## Restore focus
|
|
37
|
+
|
|
38
|
+
On overlay/popover close, transient trigger state is allowed:
|
|
39
|
+
|
|
40
|
+
- `escape-key` => `data-focused=true` and `data-focus-visible=true`.
|
|
41
|
+
- `outside-press` => `data-focused=true` and `data-focus-visible` absent.
|
|
42
|
+
|
|
43
|
+
## Recommended Implementation
|
|
44
|
+
|
|
45
|
+
- Native visual baseline: `:focus`, `:focus-visible`, `:focus-within`.
|
|
46
|
+
- Use `data-*` for composed state and restore semantics.
|
|
47
|
+
- Avoid overengineering: centralize minimal synchronization utilities and validate with contract tests.
|
|
48
|
+
|
|
49
|
+
## Operational Template
|
|
50
|
+
|
|
51
|
+
- Use `FOCUS_STATE_REVIEW_TEMPLATE.md` for PR/release reviews (modality matrix + component status + checklist).
|
|
52
|
+
|
|
53
|
+
## Component Coverage
|
|
54
|
+
|
|
55
|
+
The following components implement this contract:
|
|
56
|
+
|
|
57
|
+
- **Popover** — trigger + content, restore focus on close.
|
|
58
|
+
- **Dialog** — trigger + overlay/content, nested stack support.
|
|
59
|
+
- **DatePicker** — segment spinbuttons, trigger, popover (calendar).
|
|
60
|
+
- **TimePicker** — segment spinbuttons, trigger, popover (scrollable columns). Follows the same contract as DatePicker.
|
|
61
|
+
- **Calendar** — grid cells with roving tabindex.
|
|
62
|
+
- **ComboBox** — input + listbox with virtual focus.
|
|
63
|
+
- **ListBox** — items with roving tabindex.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Focus State Review Template
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
Template to validate focus-related changes without adding extra CI guardrails.
|
|
6
|
+
Fill this out for PRs that modify interaction, keyboard navigation, overlays, or focus-based styling.
|
|
7
|
+
|
|
8
|
+
## 1) Review Metadata
|
|
9
|
+
|
|
10
|
+
- PR/Branch:
|
|
11
|
+
- Component(s):
|
|
12
|
+
- Reviewer:
|
|
13
|
+
- Date:
|
|
14
|
+
- Verified browsers:
|
|
15
|
+
- [ ] Chromium
|
|
16
|
+
- [ ] Firefox
|
|
17
|
+
- [ ] WebKit/Safari
|
|
18
|
+
|
|
19
|
+
## 2) Minimum Modality Matrix (per component)
|
|
20
|
+
|
|
21
|
+
> Mark **OK/NA/FAIL** and add a short note when failing.
|
|
22
|
+
|
|
23
|
+
| Scenario | What to validate | Status | Note |
|
|
24
|
+
| ---------------------------------- | --------------------------------------------------------------------------- | ------ | ---- |
|
|
25
|
+
| Keyboard (Tab/Arrow/Home/End/Page) | `data-*` and ARIA stay in sync with logical focus | | |
|
|
26
|
+
| Pointer (click/mousedown) | do not elevate `data-focus-visible` by default | | |
|
|
27
|
+
| External blur | transient state cleanup (`data-focus-within`, `data-focus-visible`) | | |
|
|
28
|
+
| Close restore (`escape-key`) | trigger: `data-focused=true`, `data-focus-visible=true` (when applicable) | | |
|
|
29
|
+
| Close restore (`outside-press`) | trigger: `data-focused=true`, `data-focus-visible` absent (when applicable) | | |
|
|
30
|
+
| Programmatic focus | does not break invariants or leave stale state | | |
|
|
31
|
+
|
|
32
|
+
## 3) Required Invariants
|
|
33
|
+
|
|
34
|
+
- [ ] Never serialize `'false'` for `data-focused`, `data-focus-visible`, `data-focus-within`.
|
|
35
|
+
- [ ] On containers: `data-focus-visible => data-focus-within`.
|
|
36
|
+
- [ ] On items: `data-focus-visible => data-focused`.
|
|
37
|
+
- [ ] No visible desync between real focus, logical focus, and `data-*` attributes.
|
|
38
|
+
|
|
39
|
+
## 4) Component Status (living status)
|
|
40
|
+
|
|
41
|
+
> Use one row per component touched or audited.
|
|
42
|
+
|
|
43
|
+
| Component | Status | Current coverage | Residual risk | Owner |
|
|
44
|
+
| ---------- | --------------------------------- | ---------------- | --------------- | ----- |
|
|
45
|
+
| DatePicker | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
46
|
+
| Popover | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
47
|
+
| ListBox | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
48
|
+
| ComboBox | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
49
|
+
| Dialog | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
50
|
+
| Calendar | Contract-ready / Partial / Legacy | tests + manual | low/medium/high | |
|
|
51
|
+
|
|
52
|
+
## 5) PR Checklist
|
|
53
|
+
|
|
54
|
+
- [ ] If focus/keyboard/overlay behavior changed, tests were added or updated.
|
|
55
|
+
- [ ] Shared helper was used: `src/lib/test-utils/focus-contract.ts`.
|
|
56
|
+
- [ ] Focused suites for affected components were run.
|
|
57
|
+
- [ ] If the change was cross-cutting, full package test suite was run.
|
|
58
|
+
|
|
59
|
+
## 6) Suggested Commands
|
|
60
|
+
|
|
61
|
+
- Focused (example):
|
|
62
|
+
- `bun run test -- --run src/lib/datepicker src/lib/popover src/lib/listbox src/lib/combobox src/lib/dialog src/lib/calendar`
|
|
63
|
+
- Full:
|
|
64
|
+
- `bun run test -- --run`
|
|
65
|
+
|
|
66
|
+
## 7) Final Decision
|
|
67
|
+
|
|
68
|
+
- Final status: [ ] Approved [ ] Approved with risk [ ] Blocked
|
|
69
|
+
- Residual risk summary:
|
|
70
|
+
- Follow-up actions:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
`Button` is a headless native button with RAC-aligned pending semantics, pressed-state exposure, and modality-aware focus data attributes.
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
- `Button.Root`
|
|
10
|
+
|
|
11
|
+
```svelte
|
|
12
|
+
<Button.Root>
|
|
13
|
+
{#snippet children({ isPending, isPressed })}
|
|
14
|
+
{#if isPending}
|
|
15
|
+
<SavingSpinner />
|
|
16
|
+
{:else}
|
|
17
|
+
<span class:is-pressed={isPressed}>Save</span>
|
|
18
|
+
{/if}
|
|
19
|
+
{/snippet}
|
|
20
|
+
</Button.Root>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage guidelines
|
|
24
|
+
|
|
25
|
+
- Use native button props such as `type`, `name`, `value`, and form attributes directly on `Button.Root`.
|
|
26
|
+
- Use `isPending` to keep the button focusable while blocking activation and hover state.
|
|
27
|
+
- Style interaction states with `data-hovered`, `data-pressed`, `data-focused`, `data-focus-visible`, `data-disabled`, and `data-pending`.
|
|
28
|
+
|
|
29
|
+
## API reference
|
|
30
|
+
|
|
31
|
+
`Button.Root` supports:
|
|
32
|
+
|
|
33
|
+
- `isPending?: boolean`
|
|
34
|
+
- `isDisabled?: boolean`
|
|
35
|
+
- `children?: Snippet<[ButtonRenderState]> | Snippet`
|
|
36
|
+
- `type?: 'button' | 'submit' | 'reset'`
|
|
37
|
+
- `...restProps: HTMLButtonAttributes`
|
|
38
|
+
|
|
39
|
+
## Accessibility
|
|
40
|
+
|
|
41
|
+
- `Button.Root` renders a native `<button>`.
|
|
42
|
+
- `isPending` applies `aria-disabled="true"`, preserves focusability, blocks press behavior, and announces the pending state through an internal polite live region.
|
|
43
|
+
- `data-focus-visible` follows the shared modality contract and is only exposed for keyboard or virtual focus.
|
|
44
|
+
|
|
45
|
+
## Notes
|
|
46
|
+
|
|
47
|
+
- When `type="submit"` and `isPending` is true, the rendered button type switches to `button` to prevent implicit and explicit form submission.
|
|
48
|
+
- Pending does not serialize `data-disabled`; it is represented by `data-pending`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Button TODO
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Track Button work with a single mandatory TODO format.
|
|
6
|
+
|
|
7
|
+
## Backlog
|
|
8
|
+
|
|
9
|
+
- [x] [S][P0][Area: Architecture][Owner: Unassigned][Target: Done] Create base `root` part with namespace exports.
|
|
10
|
+
- [x] [S][P0][Area: Interaction][Owner: Unassigned][Target: Done] Implement native press, hover, focus, and focus-visible state exposure.
|
|
11
|
+
- [x] [S][P0][Area: Accessibility][Owner: Unassigned][Target: Done] Match RAC-style pending semantics with `aria-disabled`, live announcement, and submit suppression.
|
|
12
|
+
- [x] [S][P0][Area: Testing][Owner: Unassigned][Target: Done] Add baseline tests for press, pending, disabled, focus, and form behavior.
|
|
13
|
+
- [ ] [S][P1][Area: API][Owner: Unassigned][Target: TBD] Evaluate whether to expose a dedicated `pendingLabel` override if consumers need localized announcement text without custom child content.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Root } from './root/button-root.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Root } from './root/button-root.svelte';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Button Root
|
|
2
|
+
|
|
3
|
+
## API reference
|
|
4
|
+
|
|
5
|
+
### Button.Root
|
|
6
|
+
|
|
7
|
+
Name: `Button.Root`
|
|
8
|
+
Description: Native button root with pressed, hovered, focused, focus-visible, disabled, and pending render state.
|
|
9
|
+
|
|
10
|
+
| Prop | Type | Default | Description |
|
|
11
|
+
| -------------- | ----------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `id` | `string` | `$props.id()` | Stable id for the native button and pending announcement labelling. |
|
|
13
|
+
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native button type. While pending, `submit` is rendered as `button`. |
|
|
14
|
+
| `isPending` | `boolean` | `false` | Keeps the button focusable while disabling press and hover behavior. |
|
|
15
|
+
| `isDisabled` | `boolean` | `false` | Disables the native button. |
|
|
16
|
+
| `children` | `Snippet<[ButtonRenderState]> \| Snippet` | `undefined` | Optional renderer receiving the current interaction state. |
|
|
17
|
+
| `class` | `string` | `''` | CSS class names applied to the button element. |
|
|
18
|
+
| `...restProps` | `HTMLButtonAttributes` | `-` | Additional native button attributes forwarded to the element, excluding reserved disabled semantics. |
|
|
19
|
+
|
|
20
|
+
### Types
|
|
21
|
+
|
|
22
|
+
Name: `ButtonRenderState`
|
|
23
|
+
Description: Render-state payload available to the `children` snippet.
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
| ---------------- | --------- | ------- | --------------------------------------------------------------------- |
|
|
27
|
+
| `isHovered` | `boolean` | `false` | Whether the button is currently hovered by a mouse. |
|
|
28
|
+
| `isPressed` | `boolean` | `false` | Whether the button is currently being pressed. Cleared while pending. |
|
|
29
|
+
| `isFocused` | `boolean` | `false` | Whether the button currently holds DOM focus. |
|
|
30
|
+
| `isFocusVisible` | `boolean` | `false` | Whether focus is visibly keyboard or virtual driven. |
|
|
31
|
+
| `isDisabled` | `boolean` | `false` | Whether the button is disabled. |
|
|
32
|
+
| `isPending` | `boolean` | `false` | Whether the button is pending. |
|
|
33
|
+
|
|
34
|
+
```svelte
|
|
35
|
+
<Button.Root type="submit" isPending={saving} aria-label="Save changes">
|
|
36
|
+
{#snippet children({ isPending, isPressed })}
|
|
37
|
+
<span class:opacity-0={isPending}>Save</span>
|
|
38
|
+
{#if isPending}
|
|
39
|
+
<ProgressCircle aria-label="Saving" isIndeterminate />
|
|
40
|
+
{/if}
|
|
41
|
+
{/snippet}
|
|
42
|
+
</Button.Root>
|
|
43
|
+
```
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { untrack, type Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
|
+
import {
|
|
5
|
+
shouldShowFocusVisible,
|
|
6
|
+
trackInteractionModality
|
|
7
|
+
} from '../../primitives/input-modality';
|
|
8
|
+
import { cn } from '../../utils/cn';
|
|
9
|
+
|
|
10
|
+
export type ButtonRenderState = {
|
|
11
|
+
isHovered: boolean;
|
|
12
|
+
isPressed: boolean;
|
|
13
|
+
isFocused: boolean;
|
|
14
|
+
isFocusVisible: boolean;
|
|
15
|
+
isDisabled: boolean;
|
|
16
|
+
isPending: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type ButtonRootProps = Omit<
|
|
20
|
+
HTMLButtonAttributes,
|
|
21
|
+
'children' | 'class' | 'disabled' | 'aria-disabled'
|
|
22
|
+
> & {
|
|
23
|
+
children?: Snippet<[ButtonRenderState]> | Snippet;
|
|
24
|
+
class?: string;
|
|
25
|
+
isPending?: boolean;
|
|
26
|
+
isDisabled?: boolean;
|
|
27
|
+
element?: HTMLButtonElement | null;
|
|
28
|
+
pressed?: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function composeEventHandlers<TEvent extends Event>(
|
|
32
|
+
internalHandler: ((event: TEvent) => void) | undefined,
|
|
33
|
+
externalHandler: ((event: TEvent) => void) | undefined,
|
|
34
|
+
options?: { skipExternalOnDefaultPrevented?: boolean }
|
|
35
|
+
): (event: TEvent) => void {
|
|
36
|
+
return (event: TEvent) => {
|
|
37
|
+
let preventDefaultCalled = false;
|
|
38
|
+
const originalPreventDefault = event.preventDefault.bind(event);
|
|
39
|
+
event.preventDefault = () => {
|
|
40
|
+
preventDefaultCalled = true;
|
|
41
|
+
originalPreventDefault();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
internalHandler?.(event);
|
|
45
|
+
event.preventDefault = originalPreventDefault;
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
options?.skipExternalOnDefaultPrevented &&
|
|
49
|
+
(event.defaultPrevented || preventDefaultCalled)
|
|
50
|
+
) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
externalHandler?.(event);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const generatedId = $props.id();
|
|
59
|
+
|
|
60
|
+
let {
|
|
61
|
+
id,
|
|
62
|
+
type = 'button',
|
|
63
|
+
children,
|
|
64
|
+
class: className = '',
|
|
65
|
+
isPending = false,
|
|
66
|
+
isDisabled = false,
|
|
67
|
+
element = $bindable<HTMLButtonElement | null>(null),
|
|
68
|
+
pressed: pressedOverride,
|
|
69
|
+
onclick: onClickExternal,
|
|
70
|
+
onfocus: onFocusExternal,
|
|
71
|
+
onblur: onBlurExternal,
|
|
72
|
+
onkeydown: onKeyDownExternal,
|
|
73
|
+
onkeyup: onKeyUpExternal,
|
|
74
|
+
onpointerdown: onPointerDownExternal,
|
|
75
|
+
onpointerup: onPointerUpExternal,
|
|
76
|
+
onpointercancel: onPointerCancelExternal,
|
|
77
|
+
onpointerenter: onPointerEnterExternal,
|
|
78
|
+
onpointerleave: onPointerLeaveExternal,
|
|
79
|
+
onmousedown: onMouseDownExternal,
|
|
80
|
+
onmouseup: onMouseUpExternal,
|
|
81
|
+
onmouseenter: onMouseEnterExternal,
|
|
82
|
+
onmouseleave: onMouseLeaveExternal,
|
|
83
|
+
'aria-label': ariaLabel,
|
|
84
|
+
'aria-labelledby': ariaLabelledby,
|
|
85
|
+
...restProps
|
|
86
|
+
}: ButtonRootProps = $props();
|
|
87
|
+
|
|
88
|
+
const resolvedId = untrack(() => id) ?? generatedId;
|
|
89
|
+
|
|
90
|
+
let buttonRef: HTMLButtonElement | null = $state(null);
|
|
91
|
+
let hovered = $state(false);
|
|
92
|
+
let pressed = $state(false);
|
|
93
|
+
let focused = $state(false);
|
|
94
|
+
let focusVisible = $state(false);
|
|
95
|
+
let pressedKey: 'Enter' | 'Space' | null = $state(null);
|
|
96
|
+
|
|
97
|
+
const renderedType = $derived(type === 'submit' && isPending ? 'button' : type);
|
|
98
|
+
const renderedPressed = $derived(
|
|
99
|
+
pressedOverride !== undefined ? Boolean(pressedOverride) && !isPending : pressed && !isPending
|
|
100
|
+
);
|
|
101
|
+
const renderState = $derived.by<ButtonRenderState>(() => ({
|
|
102
|
+
isHovered: hovered,
|
|
103
|
+
isPressed: renderedPressed,
|
|
104
|
+
isFocused: focused,
|
|
105
|
+
isFocusVisible: focusVisible,
|
|
106
|
+
isDisabled,
|
|
107
|
+
isPending
|
|
108
|
+
}));
|
|
109
|
+
const pendingAnnouncement = $derived.by(() =>
|
|
110
|
+
isPending ? `${resolveButtonLabel() || 'Button'}, pending` : ''
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
function resolveReferencedLabel(ids: string | null | undefined): string {
|
|
114
|
+
if (!ids || !buttonRef) return '';
|
|
115
|
+
const ownerDocument = buttonRef.ownerDocument;
|
|
116
|
+
return ids
|
|
117
|
+
.split(/\s+/)
|
|
118
|
+
.map((token) => ownerDocument.getElementById(token)?.textContent?.trim() ?? '')
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
.join(' ')
|
|
121
|
+
.trim();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveButtonLabel(): string {
|
|
125
|
+
const directLabel = ariaLabel?.trim();
|
|
126
|
+
if (directLabel) return directLabel;
|
|
127
|
+
|
|
128
|
+
const labelledByLabel = resolveReferencedLabel(ariaLabelledby);
|
|
129
|
+
if (labelledByLabel) return labelledByLabel;
|
|
130
|
+
|
|
131
|
+
if (!buttonRef) return '';
|
|
132
|
+
|
|
133
|
+
const liveRegion = buttonRef.querySelector('[data-button-live-region="true"]');
|
|
134
|
+
return Array.from(buttonRef.childNodes)
|
|
135
|
+
.filter((node) => node !== liveRegion)
|
|
136
|
+
.map((node) => node.textContent?.trim() ?? '')
|
|
137
|
+
.filter(Boolean)
|
|
138
|
+
.join(' ')
|
|
139
|
+
.trim();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function clearInteractionState() {
|
|
143
|
+
hovered = false;
|
|
144
|
+
pressed = false;
|
|
145
|
+
pressedKey = null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
$effect(() => {
|
|
149
|
+
element = buttonRef;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
$effect(() => {
|
|
153
|
+
if (!isPending && !isDisabled) return;
|
|
154
|
+
clearInteractionState();
|
|
155
|
+
if (isDisabled) {
|
|
156
|
+
focusVisible = false;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
function handlePointerDown(event: PointerEvent) {
|
|
161
|
+
trackInteractionModality(event, buttonRef);
|
|
162
|
+
focusVisible = false;
|
|
163
|
+
|
|
164
|
+
if (isDisabled || isPending) {
|
|
165
|
+
event.preventDefault();
|
|
166
|
+
clearInteractionState();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (event.button !== 0) return;
|
|
171
|
+
pressed = true;
|
|
172
|
+
pressedKey = null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function handlePointerUp(event: PointerEvent) {
|
|
176
|
+
if (event.button !== 0) return;
|
|
177
|
+
pressed = false;
|
|
178
|
+
pressedKey = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function handlePointerCancel() {
|
|
182
|
+
pressed = false;
|
|
183
|
+
pressedKey = null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function handlePointerEnter(event: PointerEvent) {
|
|
187
|
+
if (isDisabled || isPending) return;
|
|
188
|
+
|
|
189
|
+
if ((event.buttons & 1) === 1 && pressedKey === null) {
|
|
190
|
+
pressed = true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function handlePointerLeave() {
|
|
195
|
+
if (pressedKey === null) {
|
|
196
|
+
pressed = false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function handleMouseDown(event: MouseEvent) {
|
|
201
|
+
trackInteractionModality(event, buttonRef);
|
|
202
|
+
focusVisible = false;
|
|
203
|
+
|
|
204
|
+
if (isDisabled || isPending) {
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
clearInteractionState();
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (event.button !== 0) return;
|
|
211
|
+
pressed = true;
|
|
212
|
+
pressedKey = null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function handleMouseUp(event: MouseEvent) {
|
|
216
|
+
if (event.button !== 0) return;
|
|
217
|
+
if (pressedKey === null) {
|
|
218
|
+
pressed = false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function handleMouseEnter() {
|
|
223
|
+
if (isDisabled || isPending) {
|
|
224
|
+
hovered = false;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
hovered = true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function handleMouseLeave() {
|
|
232
|
+
hovered = false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function handleFocus() {
|
|
236
|
+
focused = true;
|
|
237
|
+
focusVisible = shouldShowFocusVisible(buttonRef);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function handleBlur() {
|
|
241
|
+
focused = false;
|
|
242
|
+
focusVisible = false;
|
|
243
|
+
clearInteractionState();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
247
|
+
const key =
|
|
248
|
+
event.key === 'Enter'
|
|
249
|
+
? 'Enter'
|
|
250
|
+
: event.key === ' ' || event.key === 'Spacebar'
|
|
251
|
+
? 'Space'
|
|
252
|
+
: null;
|
|
253
|
+
|
|
254
|
+
if (!key) return;
|
|
255
|
+
|
|
256
|
+
trackInteractionModality(event, buttonRef);
|
|
257
|
+
focusVisible = focused ? true : shouldShowFocusVisible(buttonRef);
|
|
258
|
+
|
|
259
|
+
if (isDisabled || isPending) {
|
|
260
|
+
event.preventDefault();
|
|
261
|
+
clearInteractionState();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (event.repeat && pressed && pressedKey === key) return;
|
|
266
|
+
|
|
267
|
+
pressed = true;
|
|
268
|
+
pressedKey = key;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function handleKeyUp(event: KeyboardEvent) {
|
|
272
|
+
const key =
|
|
273
|
+
event.key === 'Enter'
|
|
274
|
+
? 'Enter'
|
|
275
|
+
: event.key === ' ' || event.key === 'Spacebar'
|
|
276
|
+
? 'Space'
|
|
277
|
+
: null;
|
|
278
|
+
|
|
279
|
+
if (!key) return;
|
|
280
|
+
|
|
281
|
+
trackInteractionModality(event, buttonRef);
|
|
282
|
+
focusVisible = focused ? true : shouldShowFocusVisible(buttonRef);
|
|
283
|
+
|
|
284
|
+
if (isDisabled || isPending) {
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
clearInteractionState();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (pressedKey === key) {
|
|
291
|
+
pressed = false;
|
|
292
|
+
pressedKey = null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function handleClick(event: MouseEvent) {
|
|
297
|
+
if (event.detail > 0) {
|
|
298
|
+
trackInteractionModality(event, buttonRef);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (isPending) {
|
|
302
|
+
event.preventDefault();
|
|
303
|
+
clearInteractionState();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
</script>
|
|
307
|
+
|
|
308
|
+
<button
|
|
309
|
+
{...restProps}
|
|
310
|
+
bind:this={buttonRef}
|
|
311
|
+
id={resolvedId}
|
|
312
|
+
type={renderedType}
|
|
313
|
+
disabled={isDisabled}
|
|
314
|
+
aria-label={ariaLabel}
|
|
315
|
+
aria-labelledby={ariaLabelledby}
|
|
316
|
+
aria-disabled={isPending ? 'true' : undefined}
|
|
317
|
+
data-button-root="true"
|
|
318
|
+
data-disabled={isDisabled || undefined}
|
|
319
|
+
data-pending={isPending || undefined}
|
|
320
|
+
data-hovered={hovered || undefined}
|
|
321
|
+
data-pressed={renderedPressed || undefined}
|
|
322
|
+
data-focused={focused || undefined}
|
|
323
|
+
data-focus-visible={focusVisible || undefined}
|
|
324
|
+
class={cn('outline-none', className)}
|
|
325
|
+
onclick={composeEventHandlers(handleClick, onClickExternal ?? undefined, {
|
|
326
|
+
skipExternalOnDefaultPrevented: true
|
|
327
|
+
})}
|
|
328
|
+
onfocus={composeEventHandlers(handleFocus, onFocusExternal ?? undefined)}
|
|
329
|
+
onblur={composeEventHandlers(handleBlur, onBlurExternal ?? undefined)}
|
|
330
|
+
onkeydown={composeEventHandlers(handleKeyDown, onKeyDownExternal ?? undefined, {
|
|
331
|
+
skipExternalOnDefaultPrevented: true
|
|
332
|
+
})}
|
|
333
|
+
onkeyup={composeEventHandlers(handleKeyUp, onKeyUpExternal ?? undefined, {
|
|
334
|
+
skipExternalOnDefaultPrevented: true
|
|
335
|
+
})}
|
|
336
|
+
onpointerdown={composeEventHandlers(handlePointerDown, onPointerDownExternal ?? undefined, {
|
|
337
|
+
skipExternalOnDefaultPrevented: true
|
|
338
|
+
})}
|
|
339
|
+
onpointerup={composeEventHandlers(handlePointerUp, onPointerUpExternal ?? undefined)}
|
|
340
|
+
onpointercancel={composeEventHandlers(handlePointerCancel, onPointerCancelExternal ?? undefined)}
|
|
341
|
+
onpointerenter={composeEventHandlers(handlePointerEnter, onPointerEnterExternal ?? undefined)}
|
|
342
|
+
onpointerleave={composeEventHandlers(handlePointerLeave, onPointerLeaveExternal ?? undefined)}
|
|
343
|
+
onmousedown={composeEventHandlers(handleMouseDown, onMouseDownExternal ?? undefined, {
|
|
344
|
+
skipExternalOnDefaultPrevented: true
|
|
345
|
+
})}
|
|
346
|
+
onmouseup={composeEventHandlers(handleMouseUp, onMouseUpExternal ?? undefined)}
|
|
347
|
+
onmouseenter={composeEventHandlers(handleMouseEnter, onMouseEnterExternal ?? undefined)}
|
|
348
|
+
onmouseleave={composeEventHandlers(handleMouseLeave, onMouseLeaveExternal ?? undefined)}
|
|
349
|
+
>
|
|
350
|
+
{#if children}
|
|
351
|
+
{@render (children as Snippet<[ButtonRenderState]>)(renderState)}
|
|
352
|
+
{/if}
|
|
353
|
+
|
|
354
|
+
<span
|
|
355
|
+
data-button-live-region="true"
|
|
356
|
+
role="status"
|
|
357
|
+
aria-live="polite"
|
|
358
|
+
aria-atomic="true"
|
|
359
|
+
style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;"
|
|
360
|
+
>{pendingAnnouncement}</span
|
|
361
|
+
>
|
|
362
|
+
</button>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
|
+
export type ButtonRenderState = {
|
|
4
|
+
isHovered: boolean;
|
|
5
|
+
isPressed: boolean;
|
|
6
|
+
isFocused: boolean;
|
|
7
|
+
isFocusVisible: boolean;
|
|
8
|
+
isDisabled: boolean;
|
|
9
|
+
isPending: boolean;
|
|
10
|
+
};
|
|
11
|
+
type ButtonRootProps = Omit<HTMLButtonAttributes, 'children' | 'class' | 'disabled' | 'aria-disabled'> & {
|
|
12
|
+
children?: Snippet<[ButtonRenderState]> | Snippet;
|
|
13
|
+
class?: string;
|
|
14
|
+
isPending?: boolean;
|
|
15
|
+
isDisabled?: boolean;
|
|
16
|
+
element?: HTMLButtonElement | null;
|
|
17
|
+
pressed?: boolean;
|
|
18
|
+
};
|
|
19
|
+
declare const ButtonRoot: import("svelte").Component<ButtonRootProps, {}, "element">;
|
|
20
|
+
type ButtonRoot = ReturnType<typeof ButtonRoot>;
|
|
21
|
+
export default ButtonRoot;
|