@svgrid/grid 1.2.33 → 2.0.0
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/README.md +6 -5
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +178 -178
- package/dist/SvAccordion.svelte +109 -0
- package/dist/SvAccordion.svelte.d.ts +28 -0
- package/dist/SvAlert.svelte +92 -0
- package/dist/SvAlert.svelte.d.ts +27 -0
- package/dist/SvAutoComplete.svelte +47 -18
- package/dist/SvAutoComplete.svelte.d.ts +2 -5
- package/dist/SvAvatar.svelte +75 -0
- package/dist/SvAvatar.svelte.d.ts +14 -0
- package/dist/SvAvatarGroup.svelte +48 -0
- package/dist/SvAvatarGroup.svelte.d.ts +16 -0
- package/dist/SvBadge.svelte +54 -0
- package/dist/SvBadge.svelte.d.ts +23 -0
- package/dist/SvBreadcrumb.svelte +88 -0
- package/dist/SvBreadcrumb.svelte.d.ts +23 -0
- package/dist/SvButtonGroup.svelte +121 -0
- package/dist/SvButtonGroup.svelte.d.ts +16 -0
- package/dist/SvCalendar.svelte +72 -9
- package/dist/SvCalendar.svelte.d.ts +34 -1
- package/dist/SvCard.svelte +71 -0
- package/dist/SvCard.svelte.d.ts +28 -0
- package/dist/SvCarousel.svelte +134 -0
- package/dist/SvCarousel.svelte.d.ts +28 -0
- package/dist/SvCheckBox.svelte +36 -21
- package/dist/SvCheckBox.svelte.d.ts +2 -5
- package/dist/SvChip.svelte +88 -0
- package/dist/SvChip.svelte.d.ts +31 -0
- package/dist/SvCircularProgress.svelte +109 -0
- package/dist/SvCircularProgress.svelte.d.ts +31 -0
- package/dist/SvColorInput.svelte +54 -27
- package/dist/SvColorInput.svelte.d.ts +10 -6
- package/dist/SvComboBox.svelte +119 -33
- package/dist/SvComboBox.svelte.d.ts +18 -6
- package/dist/SvCommand.svelte +197 -0
- package/dist/SvCommand.svelte.d.ts +26 -0
- package/dist/SvContextMenu.svelte +96 -0
- package/dist/SvContextMenu.svelte.d.ts +25 -0
- package/dist/SvCountryInput.svelte +56 -21
- package/dist/SvCountryInput.svelte.d.ts +9 -5
- package/dist/SvDateRangeInput.svelte +256 -0
- package/dist/SvDateRangeInput.svelte.d.ts +37 -0
- package/dist/SvDateTimePicker.svelte +57 -20
- package/dist/SvDateTimePicker.svelte.d.ts +12 -4
- package/dist/SvDivider.svelte +61 -0
- package/dist/SvDivider.svelte.d.ts +22 -0
- package/dist/SvDrawer.svelte +226 -0
- package/dist/SvDrawer.svelte.d.ts +43 -0
- package/dist/SvDropDownList.svelte +114 -35
- package/dist/SvDropDownList.svelte.d.ts +7 -6
- package/dist/SvDurationInput.svelte +108 -0
- package/dist/SvDurationInput.svelte.d.ts +15 -0
- package/dist/SvEmptyState.svelte +59 -0
- package/dist/SvEmptyState.svelte.d.ts +25 -0
- package/dist/SvField.svelte +85 -0
- package/dist/SvField.svelte.d.ts +30 -0
- package/dist/SvFileUpload.svelte +169 -0
- package/dist/SvFileUpload.svelte.d.ts +24 -0
- package/dist/SvForm.svelte +7 -1
- package/dist/SvForm.svelte.d.ts +3 -0
- package/dist/SvGauge.svelte +7 -1
- package/dist/SvGauge.svelte.d.ts +11 -0
- package/dist/SvGrid.controller.svelte.d.ts +6 -0
- package/dist/SvGrid.controller.svelte.js +64 -9
- package/dist/SvGrid.css +2376 -2343
- package/dist/SvGrid.svelte +2764 -2651
- package/dist/SvGrid.types.d.ts +366 -0
- package/dist/SvGridBoard.svelte +2281 -0
- package/dist/SvGridBoard.svelte.d.ts +31 -0
- package/dist/SvGridDropdown.svelte +666 -665
- package/dist/SvGridSelect.svelte +255 -0
- package/dist/SvGridSelect.svelte.d.ts +32 -0
- package/dist/SvGroupCell.svelte +109 -0
- package/dist/SvGroupCell.svelte.d.ts +41 -0
- package/dist/SvListBox.svelte +123 -36
- package/dist/SvListBox.svelte.d.ts +12 -8
- package/dist/SvMaskedInput.svelte +53 -13
- package/dist/SvMaskedInput.svelte.d.ts +16 -6
- package/dist/SvMenu.svelte +101 -0
- package/dist/SvMenu.svelte.d.ts +26 -0
- package/dist/SvMenuList.svelte +143 -0
- package/dist/SvMenuList.svelte.d.ts +24 -0
- package/dist/SvModal.svelte +220 -0
- package/dist/SvModal.svelte.d.ts +31 -0
- package/dist/SvMultiSelect.svelte +277 -0
- package/dist/SvMultiSelect.svelte.d.ts +31 -0
- package/dist/SvNavPane.svelte +314 -0
- package/dist/SvNavPane.svelte.d.ts +59 -0
- package/dist/SvNumberInput.svelte +23 -9
- package/dist/SvNumberInput.svelte.d.ts +2 -0
- package/dist/SvOtpInput.svelte +157 -0
- package/dist/SvOtpInput.svelte.d.ts +16 -0
- package/dist/SvPagination.svelte +100 -0
- package/dist/SvPagination.svelte.d.ts +16 -0
- package/dist/SvPasswordInput.svelte +42 -12
- package/dist/SvPasswordInput.svelte.d.ts +13 -6
- package/dist/SvPhoneInput.svelte +43 -11
- package/dist/SvPhoneInput.svelte.d.ts +10 -11
- package/dist/SvPopover.svelte +127 -0
- package/dist/SvPopover.svelte.d.ts +35 -0
- package/dist/SvProgress.svelte +116 -0
- package/dist/SvProgress.svelte.d.ts +32 -0
- package/dist/SvRadioGroup.svelte +40 -22
- package/dist/SvRadioGroup.svelte.d.ts +2 -10
- package/dist/SvRating.svelte +51 -30
- package/dist/SvRating.svelte.d.ts +10 -7
- package/dist/SvRichText.svelte +192 -0
- package/dist/SvRichText.svelte.d.ts +16 -0
- package/dist/SvRowGroupPanel.svelte +147 -0
- package/dist/SvRowGroupPanel.svelte.d.ts +17 -0
- package/dist/SvScrollArea.svelte +50 -0
- package/dist/SvScrollArea.svelte.d.ts +22 -0
- package/dist/SvSkeleton.svelte +76 -0
- package/dist/SvSkeleton.svelte.d.ts +25 -0
- package/dist/SvSlider.svelte +41 -9
- package/dist/SvSlider.svelte.d.ts +4 -4
- package/dist/SvSparkline.svelte +77 -0
- package/dist/SvSparkline.svelte.d.ts +28 -0
- package/dist/SvSplitter.svelte +138 -0
- package/dist/SvSplitter.svelte.d.ts +32 -0
- package/dist/SvStat.svelte +86 -0
- package/dist/SvStat.svelte.d.ts +30 -0
- package/dist/SvStepper.svelte +91 -0
- package/dist/SvStepper.svelte.d.ts +24 -0
- package/dist/SvSwitchButton.svelte +39 -24
- package/dist/SvSwitchButton.svelte.d.ts +2 -5
- package/dist/SvTabs.svelte +147 -28
- package/dist/SvTabs.svelte.d.ts +16 -4
- package/dist/SvTagsInput.svelte +59 -22
- package/dist/SvTagsInput.svelte.d.ts +9 -4
- package/dist/SvTextArea.svelte +113 -0
- package/dist/SvTextArea.svelte.d.ts +18 -0
- package/dist/SvTextInput.svelte +115 -0
- package/dist/SvTextInput.svelte.d.ts +20 -0
- package/dist/SvTimePicker.svelte +19 -5
- package/dist/SvTimePicker.svelte.d.ts +12 -0
- package/dist/SvTimeline.svelte +75 -0
- package/dist/SvTimeline.svelte.d.ts +24 -0
- package/dist/SvToaster.svelte +124 -0
- package/dist/SvToaster.svelte.d.ts +10 -0
- package/dist/SvToggleButton.svelte +37 -12
- package/dist/SvToggleButton.svelte.d.ts +2 -4
- package/dist/SvTooltip.svelte +113 -0
- package/dist/SvTooltip.svelte.d.ts +22 -0
- package/dist/SvTour.svelte +151 -0
- package/dist/SvTour.svelte.d.ts +25 -0
- package/dist/SvTree.svelte +286 -44
- package/dist/SvTree.svelte.d.ts +26 -9
- package/dist/SvTreeSelect.svelte +222 -0
- package/dist/SvTreeSelect.svelte.d.ts +24 -0
- package/dist/a11y/dismissable.d.ts +32 -0
- package/dist/a11y/dismissable.js +54 -0
- package/dist/a11y/focus-trap.d.ts +39 -0
- package/dist/a11y/focus-trap.js +142 -0
- package/dist/a11y/live-region.d.ts +8 -0
- package/dist/a11y/live-region.js +60 -0
- package/dist/a11y/scroll-lock.d.ts +4 -0
- package/dist/a11y/scroll-lock.js +41 -0
- package/dist/a11y.d.ts +3 -1
- package/dist/a11y.js +1 -1
- package/dist/avatar.d.ts +10 -0
- package/dist/avatar.js +22 -0
- package/dist/builtin-editors.d.ts +5 -0
- package/dist/builtin-editors.js +55 -0
- package/dist/cdn/svgrid.js +16022 -8235
- package/dist/cdn/svgrid.svelte-external.js +14543 -6765
- package/dist/columns.js +3 -2
- package/dist/core.d.ts +7 -1
- package/dist/countries.d.ts +12 -0
- package/dist/countries.js +24 -0
- package/dist/createAccordion.svelte.d.ts +71 -0
- package/dist/createAccordion.svelte.js +99 -0
- package/dist/createAutocomplete.svelte.d.ts +13 -4
- package/dist/createAutocomplete.svelte.js +11 -1
- package/dist/createButtonGroup.svelte.d.ts +62 -0
- package/dist/createButtonGroup.svelte.js +154 -0
- package/dist/createCalendar.svelte.d.ts +5 -0
- package/dist/createCalendar.svelte.js +2 -0
- package/dist/createCheckbox.svelte.d.ts +9 -17
- package/dist/createCheckbox.svelte.js +28 -1
- package/dist/createColorInput.svelte.d.ts +12 -22
- package/dist/createColorInput.svelte.js +13 -0
- package/dist/createCombobox.svelte.d.ts +16 -5
- package/dist/createCombobox.svelte.js +13 -2
- package/dist/createCountryInput.svelte.d.ts +14 -3
- package/dist/createCountryInput.svelte.js +12 -2
- package/dist/createDateTimePicker.svelte.d.ts +17 -2
- package/dist/createDateTimePicker.svelte.js +12 -2
- package/dist/createDropdownList.svelte.d.ts +14 -5
- package/dist/createDropdownList.svelte.js +50 -1
- package/dist/createFileUpload.svelte.d.ts +68 -0
- package/dist/createFileUpload.svelte.js +122 -0
- package/dist/createListbox.svelte.d.ts +10 -1
- package/dist/createListbox.svelte.js +48 -1
- package/dist/createMaskedInput.svelte.d.ts +11 -2
- package/dist/createMaskedInput.svelte.js +11 -1
- package/dist/createNumberInput.svelte.d.ts +1 -0
- package/dist/createNumberInput.svelte.js +1 -0
- package/dist/createPasswordInput.svelte.d.ts +19 -21
- package/dist/createPasswordInput.svelte.js +16 -4
- package/dist/createPhoneInput.svelte.d.ts +26 -7
- package/dist/createPhoneInput.svelte.js +24 -3
- package/dist/createPopoverSelect.svelte.d.ts +86 -0
- package/dist/createPopoverSelect.svelte.js +196 -0
- package/dist/createRadioGroup.svelte.d.ts +9 -0
- package/dist/createRadioGroup.svelte.js +11 -1
- package/dist/createRating.svelte.d.ts +11 -0
- package/dist/createRating.svelte.js +12 -1
- package/dist/createSlider.svelte.d.ts +2 -0
- package/dist/createSlider.svelte.js +16 -5
- package/dist/createSplitter.svelte.d.ts +67 -0
- package/dist/createSplitter.svelte.js +99 -0
- package/dist/createSwitch.svelte.d.ts +9 -16
- package/dist/createSwitch.svelte.js +28 -1
- package/dist/createTabs.svelte.d.ts +7 -0
- package/dist/createTabs.svelte.js +17 -2
- package/dist/createTagsInput.svelte.d.ts +13 -1
- package/dist/createTagsInput.svelte.js +12 -3
- package/dist/createToggle.svelte.d.ts +9 -0
- package/dist/createToggle.svelte.js +12 -1
- package/dist/createTree.svelte.d.ts +19 -0
- package/dist/createTree.svelte.js +108 -15
- package/dist/debounce.d.ts +11 -0
- package/dist/debounce.js +30 -0
- package/dist/duration.d.ts +15 -0
- package/dist/duration.js +51 -0
- package/dist/editor-contract.d.ts +62 -7
- package/dist/editor-contract.js +45 -5
- package/dist/editor-registry.d.ts +67 -0
- package/dist/editor-registry.js +38 -0
- package/dist/editors/cell-editors.d.ts +1 -1
- package/dist/editors/cell-editors.js +5 -0
- package/dist/fuzzy.d.ts +3 -0
- package/dist/fuzzy.js +41 -0
- package/dist/grid-select.d.ts +9 -0
- package/dist/grid-select.js +8 -0
- package/dist/index.d.ts +79 -6
- package/dist/index.js +78 -4
- package/dist/keyboard-handlers.js +19 -0
- package/dist/list-option.d.ts +36 -0
- package/dist/list-option.js +68 -0
- package/dist/otp.d.ts +16 -0
- package/dist/otp.js +26 -0
- package/dist/paginate.d.ts +20 -0
- package/dist/paginate.js +40 -0
- package/dist/popover.d.ts +15 -1
- package/dist/popover.js +29 -3
- package/dist/recurrence.d.ts +36 -0
- package/dist/recurrence.js +90 -0
- package/dist/server-data-source.d.ts +89 -0
- package/dist/server-data-source.js +4 -0
- package/dist/server-group-model.d.ts +98 -0
- package/dist/server-group-model.js +263 -0
- package/dist/toast-store.svelte.d.ts +41 -0
- package/dist/toast-store.svelte.js +113 -0
- package/dist/tree-select.d.ts +19 -0
- package/dist/tree-select.js +40 -0
- package/dist/ui-app.types.d.ts +80 -0
- package/dist/ui-app.types.js +7 -0
- package/dist/validators.d.ts +43 -0
- package/dist/validators.js +53 -0
- package/dist/virtual.d.ts +35 -0
- package/dist/virtual.js +34 -0
- package/package.json +85 -85
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +178 -178
- package/src/SvAccordion.svelte +109 -0
- package/src/SvAccordion.test.ts +66 -0
- package/src/SvAlert.svelte +92 -0
- package/src/SvAutoComplete.svelte +47 -18
- package/src/SvAvatar.svelte +75 -0
- package/src/SvAvatarGroup.svelte +48 -0
- package/src/SvBadge.svelte +54 -0
- package/src/SvBreadcrumb.svelte +88 -0
- package/src/SvButtonGroup.svelte +121 -0
- package/src/SvButtonGroup.test.ts +72 -0
- package/src/SvCalendar.svelte +72 -9
- package/src/SvCard.svelte +71 -0
- package/src/SvCarousel.svelte +134 -0
- package/src/SvCheckBox.svelte +36 -21
- package/src/SvChip.svelte +88 -0
- package/src/SvCircularProgress.svelte +109 -0
- package/src/SvColorInput.svelte +54 -27
- package/src/SvComboBox.svelte +119 -33
- package/src/SvCommand.svelte +197 -0
- package/src/SvContextMenu.svelte +96 -0
- package/src/SvCountryInput.svelte +56 -21
- package/src/SvDateRangeInput.svelte +256 -0
- package/src/SvDateRangeInput.test.ts +80 -0
- package/src/SvDateTimePicker.svelte +57 -20
- package/src/SvDivider.svelte +61 -0
- package/src/SvDrawer.svelte +226 -0
- package/src/SvDrawer.test.ts +125 -0
- package/src/SvDropDownList.svelte +114 -35
- package/src/SvDurationInput.svelte +108 -0
- package/src/SvEmptyState.svelte +59 -0
- package/src/SvField.svelte +85 -0
- package/src/SvFileUpload.svelte +169 -0
- package/src/SvFileUpload.test.ts +59 -0
- package/src/SvForm.svelte +7 -1
- package/src/SvGauge.svelte +7 -1
- package/src/SvGrid.controller.svelte.ts +2553 -2494
- package/src/SvGrid.css +2376 -2343
- package/src/SvGrid.svelte +2764 -2651
- package/src/SvGrid.types.ts +944 -570
- package/src/SvGridBoard.svelte +2281 -0
- package/src/SvGridDropdown.svelte +666 -665
- package/src/SvGridSelect.svelte +255 -0
- package/src/SvGridSelect.test.ts +100 -0
- package/src/SvGroupCell.svelte +109 -0
- package/src/SvListBox.svelte +123 -36
- package/src/SvMaskedInput.svelte +53 -13
- package/src/SvMenu.svelte +101 -0
- package/src/SvMenu.test.ts +73 -0
- package/src/SvMenuList.svelte +143 -0
- package/src/SvModal.svelte +220 -0
- package/src/SvMultiSelect.svelte +277 -0
- package/src/SvMultiSelect.test.ts +167 -0
- package/src/SvNavPane.svelte +314 -0
- package/src/SvNavPane.test.ts +70 -0
- package/src/SvNumberInput.svelte +23 -9
- package/src/SvOtpInput.svelte +157 -0
- package/src/SvPagination.svelte +100 -0
- package/src/SvPasswordInput.svelte +42 -12
- package/src/SvPhoneInput.svelte +43 -11
- package/src/SvPopover.svelte +127 -0
- package/src/SvProgress.svelte +116 -0
- package/src/SvRadioGroup.svelte +40 -22
- package/src/SvRating.svelte +51 -30
- package/src/SvRichText.svelte +192 -0
- package/src/SvRowGroupPanel.svelte +147 -0
- package/src/SvScrollArea.svelte +50 -0
- package/src/SvSkeleton.svelte +76 -0
- package/src/SvSlider.svelte +41 -9
- package/src/SvSparkline.svelte +77 -0
- package/src/SvSplitter.svelte +138 -0
- package/src/SvSplitter.test.ts +55 -0
- package/src/SvStat.svelte +86 -0
- package/src/SvStepper.svelte +91 -0
- package/src/SvSwitchButton.svelte +39 -24
- package/src/SvTabs.svelte +147 -28
- package/src/SvTagsInput.svelte +59 -22
- package/src/SvTextArea.svelte +113 -0
- package/src/SvTextInput.svelte +115 -0
- package/src/SvTimePicker.svelte +19 -5
- package/src/SvTimeline.svelte +75 -0
- package/src/SvToaster.svelte +124 -0
- package/src/SvToaster.test.ts +63 -0
- package/src/SvToggleButton.svelte +37 -12
- package/src/SvTooltip.svelte +113 -0
- package/src/SvTour.svelte +151 -0
- package/src/SvTree.svelte +286 -44
- package/src/SvTreeSelect.svelte +222 -0
- package/src/SvTreeSelect.test.ts +87 -0
- package/src/a11y/dismissable.test.ts +95 -0
- package/src/a11y/dismissable.ts +94 -0
- package/src/a11y/focus-trap.test.ts +110 -0
- package/src/a11y/focus-trap.ts +134 -0
- package/src/a11y/live-region.test.ts +40 -0
- package/src/a11y/live-region.ts +65 -0
- package/src/a11y/scroll-lock.test.ts +38 -0
- package/src/a11y/scroll-lock.ts +41 -0
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -59
- package/src/avatar.test.ts +32 -0
- package/src/avatar.ts +21 -0
- package/src/board.test.ts +927 -0
- package/src/build-api.ts +798 -798
- package/src/builtin-editors.grid.test.ts +85 -0
- package/src/builtin-editors.test.ts +58 -0
- package/src/builtin-editors.ts +56 -0
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.ts +469 -469
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/columns.ts +3 -2
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1111 -1101
- package/src/countries.ts +25 -0
- package/src/createAccordion.svelte.ts +126 -0
- package/src/createAutocomplete.svelte.ts +18 -1
- package/src/createButtonGroup.svelte.ts +166 -0
- package/src/createCalendar.svelte.ts +6 -0
- package/src/createCheckbox.svelte.ts +23 -1
- package/src/createColorInput.svelte.ts +20 -0
- package/src/createCombobox.svelte.ts +22 -2
- package/src/createCountryInput.svelte.ts +21 -2
- package/src/createDateTimePicker.svelte.ts +23 -2
- package/src/createDropdownList.svelte.ts +31 -2
- package/src/createFileUpload.svelte.ts +144 -0
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +35 -3
- package/src/createMaskedInput.svelte.ts +18 -1
- package/src/createNumberInput.svelte.ts +2 -0
- package/src/createPasswordInput.svelte.ts +31 -5
- package/src/createPhoneInput.svelte.ts +38 -4
- package/src/createPopoverSelect.svelte.ts +208 -0
- package/src/createRadioGroup.svelte.ts +23 -1
- package/src/createRating.svelte.ts +26 -1
- package/src/createSlider.svelte.ts +14 -3
- package/src/createSplitter.svelte.ts +119 -0
- package/src/createSwitch.svelte.ts +26 -3
- package/src/createTabs.svelte.ts +20 -3
- package/src/createTagsInput.svelte.ts +24 -3
- package/src/createToggle.svelte.ts +25 -2
- package/src/createTree.svelte.ts +104 -14
- package/src/debounce.test.ts +50 -0
- package/src/debounce.ts +41 -0
- package/src/duration.test.ts +41 -0
- package/src/duration.ts +51 -0
- package/src/editing.test.ts +859 -859
- package/src/editing.ts +675 -675
- package/src/editor-contract.ts +87 -8
- package/src/editor-registry.grid.test.ts +98 -0
- package/src/editor-registry.test.ts +86 -0
- package/src/editor-registry.ts +106 -0
- package/src/editors/cell-editors.ts +10 -1
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +598 -598
- package/src/flex-render.ts +3 -3
- package/src/fuzzy.test.ts +40 -0
- package/src/fuzzy.ts +39 -0
- package/src/grid-select.test.ts +28 -0
- package/src/grid-select.ts +20 -0
- package/src/index.ts +463 -318
- package/src/keyboard-handlers.ts +20 -0
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-option.ts +74 -0
- package/src/list-power.test.ts +71 -0
- package/src/menus.ts +582 -582
- package/src/merge-objects.ts +48 -48
- package/src/otp.test.ts +28 -0
- package/src/otp.ts +28 -0
- package/src/overlays.test.ts +85 -0
- package/src/paginate.test.ts +41 -0
- package/src/paginate.ts +61 -0
- package/src/phone-valid.test.ts +26 -0
- package/src/popover.ts +31 -3
- package/src/progress.test.ts +55 -0
- package/src/recurrence.test.ts +91 -0
- package/src/recurrence.ts +111 -0
- package/src/render-component.ts +28 -28
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +600 -600
- package/src/server-data-source.test.ts +289 -289
- package/src/server-data-source.ts +413 -311
- package/src/server-group-model.test.ts +294 -0
- package/src/server-group-model.ts +370 -0
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +489 -489
- package/src/spreadsheet.ts +304 -304
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +439 -439
- package/src/svgrid.behavior.test.ts +706 -706
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.responsive.test.ts +72 -0
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/toast-store.svelte.ts +148 -0
- package/src/toast-store.test.ts +86 -0
- package/src/tree-edit.test.ts +49 -0
- package/src/tree-filter.test.ts +56 -0
- package/src/tree-select.test.ts +48 -0
- package/src/tree-select.ts +62 -0
- package/src/ui-app.test.ts +130 -0
- package/src/ui-app.types.ts +88 -0
- package/src/ui-display.test.ts +79 -0
- package/src/ui-inputs.test.ts +1 -1
- package/src/ui-status.test.ts +141 -0
- package/src/ui-tier1.test.ts +142 -0
- package/src/ui-tier2.test.ts +124 -0
- package/src/ui-tier3.test.ts +139 -0
- package/src/validators.test.ts +58 -0
- package/src/validators.ts +105 -0
- package/src/virtual.test.ts +72 -0
- package/src/virtual.ts +67 -0
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
|
@@ -17,18 +17,30 @@
|
|
|
17
17
|
*
|
|
18
18
|
* The styled <SvPhoneInput> is just one renderer over this core.
|
|
19
19
|
*/
|
|
20
|
-
import { COUNTRIES, COUNTRY_BY_CODE, flagEmoji } from './countries'
|
|
20
|
+
import { COUNTRIES, COUNTRY_BY_CODE, flagEmoji, phoneDigitsValid } from './countries'
|
|
21
21
|
import { applyMask } from './datetime/mask'
|
|
22
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
23
|
+
|
|
24
|
+
/** Emitted alongside the phone value: parsed parts + per-country validity. */
|
|
25
|
+
export type PhoneParts = { country: string; dial: string; national: string; valid: boolean; complete: boolean }
|
|
22
26
|
|
|
23
27
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
24
28
|
export type PhoneInputConfig = {
|
|
25
29
|
value: () => string
|
|
26
|
-
onChange?: (value: string, parts:
|
|
30
|
+
onChange?: (value: string, parts: PhoneParts) => void
|
|
27
31
|
/** Default country ISO code. */
|
|
28
32
|
country?: () => string
|
|
29
33
|
disabled?: () => boolean
|
|
30
34
|
readonly?: () => boolean
|
|
31
35
|
placeholder?: () => string | undefined
|
|
36
|
+
/** Accessible name for the country `<select>` (localizable). */
|
|
37
|
+
countryLabel?: () => string | undefined
|
|
38
|
+
// Editor contract (ARIA + validation) - folded into inputProps().
|
|
39
|
+
id?: () => string | undefined
|
|
40
|
+
invalid?: () => boolean
|
|
41
|
+
required?: () => boolean
|
|
42
|
+
error?: () => string | undefined
|
|
43
|
+
hint?: () => string | undefined
|
|
32
44
|
ariaLabel?: () => string | undefined
|
|
33
45
|
}
|
|
34
46
|
|
|
@@ -69,8 +81,13 @@ export function createPhoneInput(config: PhoneInputConfig) {
|
|
|
69
81
|
}
|
|
70
82
|
})
|
|
71
83
|
|
|
84
|
+
// Per-country length validity: empty is valid (required handles emptiness);
|
|
85
|
+
// a non-empty number must match the country's expected national length.
|
|
86
|
+
const complete = $derived(national.length > 0 && phoneDigitsValid(iso, national))
|
|
87
|
+
const valid = $derived(national.length === 0 || phoneDigitsValid(iso, national))
|
|
88
|
+
|
|
72
89
|
function emit() {
|
|
73
|
-
config.onChange?.(`${dial}${national}`, { country: iso, dial, national })
|
|
90
|
+
config.onChange?.(`${dial}${national}`, { country: iso, dial, national, valid, complete })
|
|
74
91
|
}
|
|
75
92
|
function onNumberInput(e: Event) {
|
|
76
93
|
const raw = (e.currentTarget as HTMLInputElement).value.replace(/\D/g, '')
|
|
@@ -84,6 +101,15 @@ export function createPhoneInput(config: PhoneInputConfig) {
|
|
|
84
101
|
emit()
|
|
85
102
|
}
|
|
86
103
|
|
|
104
|
+
const ariaState = (): EditorAriaState => ({
|
|
105
|
+
id: config.id?.(),
|
|
106
|
+
invalid: config.invalid?.(),
|
|
107
|
+
required: config.required?.(),
|
|
108
|
+
error: config.error?.(),
|
|
109
|
+
hint: config.hint?.(),
|
|
110
|
+
ariaLabel: config.ariaLabel?.(),
|
|
111
|
+
})
|
|
112
|
+
|
|
87
113
|
return {
|
|
88
114
|
/** Selected country ISO code. */
|
|
89
115
|
get iso() { return iso },
|
|
@@ -95,21 +121,29 @@ export function createPhoneInput(config: PhoneInputConfig) {
|
|
|
95
121
|
get displayNational() { return displayNational },
|
|
96
122
|
/** The full emitted value (`+<dial><digits>`). */
|
|
97
123
|
get value() { return `${dial}${national}` },
|
|
124
|
+
/** Whether the national number is a plausible length for the country. */
|
|
125
|
+
get valid() { return valid },
|
|
126
|
+
/** Whether a non-empty number matches the country's expected length. */
|
|
127
|
+
get complete() { return complete },
|
|
98
128
|
/** Spread onto the country `<select>` element. */
|
|
99
129
|
selectProps: () => ({
|
|
100
130
|
value: iso,
|
|
101
131
|
disabled: disabled(),
|
|
102
|
-
'aria-label': 'Country',
|
|
132
|
+
'aria-label': config.countryLabel?.() ?? 'Country',
|
|
103
133
|
onchange: onCountryChange,
|
|
104
134
|
}),
|
|
105
135
|
/** Spread onto the national number input element. */
|
|
106
136
|
inputProps: () => ({
|
|
137
|
+
id: config.id?.(),
|
|
107
138
|
value: displayNational,
|
|
108
139
|
type: 'tel' as const,
|
|
109
140
|
inputmode: 'tel' as const,
|
|
110
141
|
placeholder: config.placeholder?.() ?? 'Phone number',
|
|
111
142
|
disabled: disabled(),
|
|
112
143
|
readonly: readonly(),
|
|
144
|
+
// aria-invalid/required/describedby come from the editor contract; the
|
|
145
|
+
// aria-label default stays 'Phone number' so the field is always named.
|
|
146
|
+
...editorAria(ariaState()),
|
|
113
147
|
'aria-label': config.ariaLabel?.() ?? 'Phone number',
|
|
114
148
|
oninput: onNumberInput,
|
|
115
149
|
}),
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPopoverSelect - the shared HEADLESS engine behind the kit's dropdown
|
|
3
|
+
* selects (SvMultiSelect, SvTreeSelect, SvGridSelect and any future one). It
|
|
4
|
+
* owns the parts every anchored-panel select repeats: open/close state, the
|
|
5
|
+
* `anchoredRect` positioning (reposition on scroll/resize), outside/Escape
|
|
6
|
+
* dismissal via the shared layer stack, a roving `active` index over the visible
|
|
7
|
+
* items, the WAI-ARIA combobox wiring (`aria-expanded` / `aria-controls` /
|
|
8
|
+
* `aria-activedescendant` + stable option ids), and focus management (focus
|
|
9
|
+
* moves into the panel on open, back to the trigger on close).
|
|
10
|
+
*
|
|
11
|
+
* The component owns its own `bind:this` refs and item rendering; it passes the
|
|
12
|
+
* refs in as getters and spreads the returned prop-getters onto its trigger,
|
|
13
|
+
* panel and items. Runes-based, like `createListbox`.
|
|
14
|
+
*/
|
|
15
|
+
import { anchoredRect, type AnchoredRect } from './popover'
|
|
16
|
+
import { createDismissableLayer } from './a11y/dismissable'
|
|
17
|
+
|
|
18
|
+
let uidSeq = 0
|
|
19
|
+
|
|
20
|
+
export type PopoverSelectConfig = {
|
|
21
|
+
/** Number of currently-visible/navigable items (post-filter). */
|
|
22
|
+
itemCount: () => number
|
|
23
|
+
/** Whether item `i` is disabled (skipped by roving, not selectable). */
|
|
24
|
+
disabled?: (index: number) => boolean
|
|
25
|
+
/** Commit the item at `index` (Enter or a click the component forwards). */
|
|
26
|
+
onSelect: (index: number) => void
|
|
27
|
+
/** Notified whenever the open state changes. */
|
|
28
|
+
onOpenChange?: (open: boolean) => void
|
|
29
|
+
/** The trigger element (component owns `bind:this`, passes a getter). */
|
|
30
|
+
getTrigger: () => HTMLElement | null
|
|
31
|
+
/** The panel element. */
|
|
32
|
+
getPanel: () => HTMLElement | null
|
|
33
|
+
/** Element focused when the panel opens (e.g. a search input). Defaults to the
|
|
34
|
+
* panel itself. Its id-holder also carries `aria-activedescendant`. */
|
|
35
|
+
getInitialFocus?: () => HTMLElement | null
|
|
36
|
+
/** Estimated panel height, for the flip-up decision. Default 280. */
|
|
37
|
+
estimatedHeight?: () => number
|
|
38
|
+
/** Minimum panel width. Default 200. */
|
|
39
|
+
minWidth?: () => number
|
|
40
|
+
/** Close after a selection (single-select). Default true. */
|
|
41
|
+
closeOnSelect?: () => boolean
|
|
42
|
+
/** Active index to start on when opening (e.g. the selected row). Default 0. */
|
|
43
|
+
initialActive?: () => number
|
|
44
|
+
/** Handle a key the core doesn't (e.g. tree ArrowLeft/Right). Return true if
|
|
45
|
+
* handled to stop the core's default handling. */
|
|
46
|
+
onExtraKey?: (event: KeyboardEvent, active: number) => boolean
|
|
47
|
+
idPrefix?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createPopoverSelect(config: PopoverSelectConfig) {
|
|
51
|
+
const id = `${config.idPrefix ?? 'sv-pop'}-${uidSeq++}`
|
|
52
|
+
const panelId = `${id}-panel`
|
|
53
|
+
const optionId = (i: number) => `${id}-opt-${i}`
|
|
54
|
+
|
|
55
|
+
let open = $state(false)
|
|
56
|
+
let active = $state(0)
|
|
57
|
+
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
58
|
+
|
|
59
|
+
const count = () => config.itemCount()
|
|
60
|
+
const isDisabled = (i: number) => config.disabled?.(i) ?? false
|
|
61
|
+
|
|
62
|
+
function enabledIndices(): number[] {
|
|
63
|
+
const out: number[] = []
|
|
64
|
+
const n = count()
|
|
65
|
+
for (let i = 0; i < n; i++) if (!isDisabled(i)) out.push(i)
|
|
66
|
+
return out
|
|
67
|
+
}
|
|
68
|
+
function clampActive(i: number): number {
|
|
69
|
+
const n = count()
|
|
70
|
+
return n <= 0 ? 0 : Math.max(0, Math.min(i, n - 1))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setOpen(v: boolean) {
|
|
74
|
+
if (v === open) return
|
|
75
|
+
open = v
|
|
76
|
+
config.onOpenChange?.(v)
|
|
77
|
+
}
|
|
78
|
+
function openPanel() {
|
|
79
|
+
active = clampActive(config.initialActive?.() ?? 0)
|
|
80
|
+
setOpen(true)
|
|
81
|
+
}
|
|
82
|
+
function closePanel() {
|
|
83
|
+
setOpen(false)
|
|
84
|
+
config.getTrigger()?.focus()
|
|
85
|
+
}
|
|
86
|
+
function toggle() {
|
|
87
|
+
open ? setOpen(false) : openPanel()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function move(delta: number) {
|
|
91
|
+
const list = enabledIndices()
|
|
92
|
+
if (!list.length) return
|
|
93
|
+
const pos = list.indexOf(active)
|
|
94
|
+
active = list[(pos + delta + list.length) % list.length] ?? list[0]!
|
|
95
|
+
}
|
|
96
|
+
function first() { active = enabledIndices()[0] ?? 0 }
|
|
97
|
+
function last() { active = enabledIndices().at(-1) ?? 0 }
|
|
98
|
+
function selectActive() {
|
|
99
|
+
if (count() === 0 || isDisabled(active)) return
|
|
100
|
+
config.onSelect(active)
|
|
101
|
+
if (config.closeOnSelect?.() ?? true) closePanel()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function updatePos() {
|
|
105
|
+
const t = config.getTrigger()
|
|
106
|
+
if (!t) return
|
|
107
|
+
rect = anchoredRect(t.getBoundingClientRect(), {
|
|
108
|
+
estimatedHeight: config.estimatedHeight?.() ?? 280,
|
|
109
|
+
minWidth: config.minWidth?.() ?? 200,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// While open: position, focus into the panel, reposition on scroll/resize, and
|
|
114
|
+
// register a dismissable layer (Escape / outside-click, top-of-stack first).
|
|
115
|
+
$effect(() => {
|
|
116
|
+
if (!open) return
|
|
117
|
+
updatePos()
|
|
118
|
+
const initial = config.getInitialFocus?.() ?? config.getPanel()
|
|
119
|
+
queueMicrotask(() => initial?.focus())
|
|
120
|
+
const rp = () => updatePos()
|
|
121
|
+
window.addEventListener('scroll', rp, true)
|
|
122
|
+
window.addEventListener('resize', rp)
|
|
123
|
+
const layer = createDismissableLayer({
|
|
124
|
+
element: () => [config.getTrigger(), config.getPanel()],
|
|
125
|
+
onDismiss: () => setOpen(false),
|
|
126
|
+
})
|
|
127
|
+
layer.activate()
|
|
128
|
+
return () => {
|
|
129
|
+
window.removeEventListener('scroll', rp, true)
|
|
130
|
+
window.removeEventListener('resize', rp)
|
|
131
|
+
layer.release()
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
function onTriggerKeydown(e: KeyboardEvent) {
|
|
136
|
+
if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
|
|
137
|
+
e.preventDefault()
|
|
138
|
+
openPanel()
|
|
139
|
+
} else if (e.key === 'ArrowUp') {
|
|
140
|
+
e.preventDefault()
|
|
141
|
+
openPanel()
|
|
142
|
+
last()
|
|
143
|
+
} else if (e.key === 'Escape') {
|
|
144
|
+
setOpen(false)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function onPanelKeydown(e: KeyboardEvent) {
|
|
148
|
+
if (config.onExtraKey?.(e, active)) return
|
|
149
|
+
switch (e.key) {
|
|
150
|
+
case 'ArrowDown': e.preventDefault(); move(1); break
|
|
151
|
+
case 'ArrowUp': e.preventDefault(); move(-1); break
|
|
152
|
+
case 'Home': e.preventDefault(); first(); break
|
|
153
|
+
case 'End': e.preventDefault(); last(); break
|
|
154
|
+
case 'Enter': e.preventDefault(); selectActive(); break
|
|
155
|
+
case 'Escape': e.preventDefault(); closePanel(); break
|
|
156
|
+
case 'Tab': setOpen(false); break
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The current active-descendant id (for the focused element), or undefined. */
|
|
161
|
+
const activeDescendant = () => (open && count() > 0 ? optionId(active) : undefined)
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
get open() { return open },
|
|
165
|
+
set open(v: boolean) { setOpen(v) },
|
|
166
|
+
get active() { return active },
|
|
167
|
+
set active(i: number) { active = clampActive(i) },
|
|
168
|
+
get rect() { return rect },
|
|
169
|
+
/** Stable id of the panel element (for aria-controls / labelling). */
|
|
170
|
+
panelId,
|
|
171
|
+
/** Stable id of the option at `i` (for aria-activedescendant + item id). */
|
|
172
|
+
optionId,
|
|
173
|
+
activeDescendant,
|
|
174
|
+
openPanel,
|
|
175
|
+
closePanel,
|
|
176
|
+
toggle,
|
|
177
|
+
move,
|
|
178
|
+
first,
|
|
179
|
+
last,
|
|
180
|
+
selectActive,
|
|
181
|
+
updatePos,
|
|
182
|
+
onPanelKeydown,
|
|
183
|
+
/** True when item `i` is the roving-active one. */
|
|
184
|
+
isActive: (i: number) => i === active,
|
|
185
|
+
/** Spread onto the trigger button. */
|
|
186
|
+
triggerProps: () => ({
|
|
187
|
+
'aria-haspopup': 'listbox' as const,
|
|
188
|
+
'aria-expanded': open,
|
|
189
|
+
'aria-controls': open ? panelId : undefined,
|
|
190
|
+
onkeydown: onTriggerKeydown,
|
|
191
|
+
}),
|
|
192
|
+
/** Attributes for the element that OWNS focus + activedescendant (the panel,
|
|
193
|
+
* or a search input). Pass the ARIA role the panel should expose. */
|
|
194
|
+
focusOwnerProps: (role: 'listbox' | 'tree' | 'grid' = 'listbox') => ({
|
|
195
|
+
role,
|
|
196
|
+
'aria-activedescendant': activeDescendant(),
|
|
197
|
+
onkeydown: onPanelKeydown,
|
|
198
|
+
}),
|
|
199
|
+
/** Spread onto the item element at `i`. */
|
|
200
|
+
itemProps: (i: number) => ({
|
|
201
|
+
id: optionId(i),
|
|
202
|
+
'data-active': i === active ? '' : undefined,
|
|
203
|
+
onpointermove: () => { if (!isDisabled(i)) active = i },
|
|
204
|
+
}),
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type PopoverSelect = ReturnType<typeof createPopoverSelect>
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export type RadioOption = { value: string | number; label: string; disabled?: boolean }
|
|
23
23
|
|
|
24
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
25
|
+
|
|
24
26
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
25
27
|
export type RadioGroupConfig = {
|
|
26
28
|
options: () => ReadonlyArray<RadioOption>
|
|
@@ -28,6 +30,12 @@ export type RadioGroupConfig = {
|
|
|
28
30
|
onChange?: (value: string | number) => void
|
|
29
31
|
disabled?: () => boolean
|
|
30
32
|
ariaLabel?: () => string | undefined
|
|
33
|
+
// Editor contract (ARIA + validation) - folded into groupProps().
|
|
34
|
+
id?: () => string | undefined
|
|
35
|
+
invalid?: () => boolean
|
|
36
|
+
required?: () => boolean
|
|
37
|
+
error?: () => string | undefined
|
|
38
|
+
hint?: () => string | undefined
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export type RadioProps = {
|
|
@@ -43,7 +51,11 @@ export type RadioProps = {
|
|
|
43
51
|
|
|
44
52
|
export type RadioGroupProps = {
|
|
45
53
|
role: 'radiogroup'
|
|
54
|
+
id: string | undefined
|
|
46
55
|
'aria-label': string | undefined
|
|
56
|
+
'aria-invalid': 'true' | undefined
|
|
57
|
+
'aria-required': 'true' | undefined
|
|
58
|
+
'aria-describedby': string | undefined
|
|
47
59
|
'aria-disabled': boolean
|
|
48
60
|
onkeydown: (e: KeyboardEvent) => void
|
|
49
61
|
}
|
|
@@ -102,6 +114,15 @@ export function createRadioGroup(config: RadioGroupConfig): RadioGroup {
|
|
|
102
114
|
container.querySelectorAll<HTMLElement>('[role="radio"]')[target]?.focus()
|
|
103
115
|
}
|
|
104
116
|
|
|
117
|
+
const ariaState = (): EditorAriaState => ({
|
|
118
|
+
id: config.id?.(),
|
|
119
|
+
invalid: config.invalid?.(),
|
|
120
|
+
required: config.required?.(),
|
|
121
|
+
error: config.error?.(),
|
|
122
|
+
hint: config.hint?.(),
|
|
123
|
+
ariaLabel: config.ariaLabel?.(),
|
|
124
|
+
})
|
|
125
|
+
|
|
105
126
|
return {
|
|
106
127
|
get value() { return config.value() },
|
|
107
128
|
get disabled() { return disabled() },
|
|
@@ -110,8 +131,9 @@ export function createRadioGroup(config: RadioGroupConfig): RadioGroup {
|
|
|
110
131
|
onKeydown,
|
|
111
132
|
groupProps: () => ({
|
|
112
133
|
role: 'radiogroup',
|
|
113
|
-
|
|
134
|
+
id: config.id?.(),
|
|
114
135
|
'aria-disabled': disabled(),
|
|
136
|
+
...editorAria(ariaState()),
|
|
115
137
|
onkeydown: onKeydown,
|
|
116
138
|
}),
|
|
117
139
|
radioProps: (option: RadioOption) => {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
*/
|
|
20
20
|
export type RatingFill = 'full' | 'half' | 'empty'
|
|
21
21
|
|
|
22
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
23
|
+
|
|
22
24
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
23
25
|
export type RatingConfig = {
|
|
24
26
|
value: () => number
|
|
@@ -28,12 +30,24 @@ export type RatingConfig = {
|
|
|
28
30
|
readonly?: () => boolean
|
|
29
31
|
disabled?: () => boolean
|
|
30
32
|
ariaLabel?: () => string | undefined
|
|
33
|
+
/** Accessible label for a star given its 1-based position (localizable). */
|
|
34
|
+
starLabel?: (position: number) => string | undefined
|
|
35
|
+
// Editor contract (ARIA + validation) - folded into rootProps().
|
|
36
|
+
id?: () => string | undefined
|
|
37
|
+
invalid?: () => boolean
|
|
38
|
+
required?: () => boolean
|
|
39
|
+
error?: () => string | undefined
|
|
40
|
+
hint?: () => string | undefined
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
export type RatingRootProps = {
|
|
34
44
|
role: 'slider'
|
|
45
|
+
id: string | undefined
|
|
35
46
|
tabindex: number
|
|
36
47
|
'aria-label': string
|
|
48
|
+
'aria-invalid': 'true' | undefined
|
|
49
|
+
'aria-required': 'true' | undefined
|
|
50
|
+
'aria-describedby': string | undefined
|
|
37
51
|
'aria-valuenow': number
|
|
38
52
|
'aria-valuemin': number
|
|
39
53
|
'aria-valuemax': number
|
|
@@ -112,6 +126,15 @@ export function createRating(config: RatingConfig): Rating {
|
|
|
112
126
|
else if (e.key === 'End') { e.preventDefault(); config.onChange?.(max()) }
|
|
113
127
|
}
|
|
114
128
|
|
|
129
|
+
const ariaState = (): EditorAriaState => ({
|
|
130
|
+
id: config.id?.(),
|
|
131
|
+
invalid: config.invalid?.(),
|
|
132
|
+
required: config.required?.(),
|
|
133
|
+
error: config.error?.(),
|
|
134
|
+
hint: config.hint?.(),
|
|
135
|
+
ariaLabel: config.ariaLabel?.(),
|
|
136
|
+
})
|
|
137
|
+
|
|
115
138
|
return {
|
|
116
139
|
get value() { return config.value() },
|
|
117
140
|
get preview() { return preview },
|
|
@@ -123,7 +146,9 @@ export function createRating(config: RatingConfig): Rating {
|
|
|
123
146
|
onKeydown,
|
|
124
147
|
rootProps: () => ({
|
|
125
148
|
role: 'slider',
|
|
149
|
+
id: config.id?.(),
|
|
126
150
|
tabindex: interactive() ? 0 : -1,
|
|
151
|
+
...editorAria(ariaState()),
|
|
127
152
|
'aria-label': config.ariaLabel?.() ?? 'Rating',
|
|
128
153
|
'aria-valuenow': config.value(),
|
|
129
154
|
'aria-valuemin': 0,
|
|
@@ -136,7 +161,7 @@ export function createRating(config: RatingConfig): Rating {
|
|
|
136
161
|
type: 'button',
|
|
137
162
|
tabindex: -1,
|
|
138
163
|
disabled: !interactive(),
|
|
139
|
-
'aria-label': `${index + 1} star${index ? 's' : ''}`,
|
|
164
|
+
'aria-label': config.starLabel?.(index + 1) ?? `${index + 1} star${index ? 's' : ''}`,
|
|
140
165
|
'data-fill': fillOf(index),
|
|
141
166
|
onpointermove: (e: PointerEvent) => { if (interactive()) hover = valueAt(e, index) },
|
|
142
167
|
onclick: (e: MouseEvent) => { if (interactive()) config.onChange?.(valueAt(e, index)) },
|
|
@@ -35,6 +35,8 @@ export type SliderConfig = {
|
|
|
35
35
|
orientation?: () => SliderOrientation
|
|
36
36
|
disabled?: () => boolean
|
|
37
37
|
ariaLabel?: () => string | undefined
|
|
38
|
+
/** Right-to-left: mirrors horizontal pointer + Left/Right key direction. */
|
|
39
|
+
rtl?: () => boolean
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export function createSlider(config: SliderConfig) {
|
|
@@ -44,6 +46,7 @@ export function createSlider(config: SliderConfig) {
|
|
|
44
46
|
const range = () => config.range?.() ?? false
|
|
45
47
|
const isVert = () => (config.orientation?.() ?? 'horizontal') === 'vertical'
|
|
46
48
|
const disabled = () => config.disabled?.() ?? false
|
|
49
|
+
const rtl = () => config.rtl?.() ?? false
|
|
47
50
|
|
|
48
51
|
const lo = $derived(range() ? (config.value() as [number, number])[0] : min())
|
|
49
52
|
const hi = $derived(range() ? (config.value() as [number, number])[1] : (config.value() as number))
|
|
@@ -59,9 +62,10 @@ export function createSlider(config: SliderConfig) {
|
|
|
59
62
|
|
|
60
63
|
/** Turn a pointer position (client coords) + measured track rect into a value. */
|
|
61
64
|
function valueFromPointer(pos: SliderPoint, rect: DOMRect): number {
|
|
65
|
+
const horiz = (pos.x - rect.left) / rect.width
|
|
62
66
|
const frac = isVert()
|
|
63
67
|
? 1 - (pos.y - rect.top) / rect.height
|
|
64
|
-
: (
|
|
68
|
+
: rtl() ? 1 - horiz : horiz
|
|
65
69
|
return clampStep(min() + Math.min(1, Math.max(0, frac)) * (max() - min()))
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -104,9 +108,16 @@ export function createSlider(config: SliderConfig) {
|
|
|
104
108
|
function onThumbKey(e: KeyboardEvent, which: SliderThumb) {
|
|
105
109
|
if (disabled()) return
|
|
106
110
|
const cur = which === 'lo' ? lo : hi
|
|
111
|
+
// Under horizontal RTL, Left increases and Right decreases (Up/Down keep
|
|
112
|
+
// their natural up=more direction regardless of text direction).
|
|
113
|
+
const rtlH = rtl() && !isVert()
|
|
114
|
+
const inc = clampStep(cur + stepSize())
|
|
115
|
+
const dec = clampStep(cur - stepSize())
|
|
107
116
|
let next = cur
|
|
108
|
-
if (e.key === '
|
|
109
|
-
else if (e.key === '
|
|
117
|
+
if (e.key === 'ArrowUp') next = inc
|
|
118
|
+
else if (e.key === 'ArrowDown') next = dec
|
|
119
|
+
else if (e.key === 'ArrowRight') next = rtlH ? dec : inc
|
|
120
|
+
else if (e.key === 'ArrowLeft') next = rtlH ? inc : dec
|
|
110
121
|
else if (e.key === 'Home') next = min()
|
|
111
122
|
else if (e.key === 'End') next = max()
|
|
112
123
|
else if (e.key === 'PageUp') next = clampStep(cur + stepSize() * 10)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createSplitter - the HEADLESS core behind <SvSplitter>: a two-pane resizable
|
|
3
|
+
* split with a draggable separator (WAI-ARIA `separator`), keyboard resize and
|
|
4
|
+
* clamping, exposed as **prop-getters** you spread onto YOUR OWN markup. The core
|
|
5
|
+
* never touches the DOM - the renderer measures the container rect and passes the
|
|
6
|
+
* pointer position + rect IN via `setFromPointer` (same split as createSlider).
|
|
7
|
+
* Parity: Smart `smart-splitter`.
|
|
8
|
+
*
|
|
9
|
+
* ```svelte
|
|
10
|
+
* <script lang="ts">
|
|
11
|
+
* import { createSplitter } from '@svgrid/grid'
|
|
12
|
+
* let frac = $state(0.5)
|
|
13
|
+
* const sp = createSplitter({ fraction: () => frac, onChange: (f) => (frac = f) })
|
|
14
|
+
* </script>
|
|
15
|
+
* <div class="row" bind:this={row}>
|
|
16
|
+
* <div style:flex-basis={sp.percent + '%'}>A</div>
|
|
17
|
+
* <div {...sp.separatorProps()} onpointerdown={...}></div>
|
|
18
|
+
* <div style:flex="1">B</div>
|
|
19
|
+
* </div>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type SplitterOrientation = 'horizontal' | 'vertical'
|
|
23
|
+
export type SplitterPoint = { x: number; y: number }
|
|
24
|
+
|
|
25
|
+
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
26
|
+
export type SplitterConfig = {
|
|
27
|
+
/** Size of the FIRST pane as a fraction of the container (0..1). */
|
|
28
|
+
fraction: () => number
|
|
29
|
+
onChange?: (fraction: number) => void
|
|
30
|
+
/** Split direction: `horizontal` = a vertical gutter between left/right panes. */
|
|
31
|
+
orientation?: () => SplitterOrientation
|
|
32
|
+
/** Min / max fraction for the first pane (0..1). */
|
|
33
|
+
min?: () => number
|
|
34
|
+
max?: () => number
|
|
35
|
+
/** Keyboard step as a fraction (default 0.02 = 2%). */
|
|
36
|
+
step?: () => number
|
|
37
|
+
disabled?: () => boolean
|
|
38
|
+
dir?: () => 'ltr' | 'rtl' | 'auto' | undefined
|
|
39
|
+
ariaLabel?: () => string | undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createSplitter(config: SplitterConfig) {
|
|
43
|
+
const isVert = () => (config.orientation?.() ?? 'horizontal') === 'vertical'
|
|
44
|
+
const min = () => config.min?.() ?? 0.1
|
|
45
|
+
const max = () => config.max?.() ?? 0.9
|
|
46
|
+
const step = () => config.step?.() ?? 0.02
|
|
47
|
+
const disabled = () => config.disabled?.() ?? false
|
|
48
|
+
const rtl = () => config.dir?.() === 'rtl'
|
|
49
|
+
|
|
50
|
+
const clamp = (f: number) => Math.min(max(), Math.max(min(), f))
|
|
51
|
+
const fraction = $derived(clamp(config.fraction()))
|
|
52
|
+
const percent = $derived(Math.round(fraction * 1000) / 10)
|
|
53
|
+
|
|
54
|
+
let dragging = $state(false)
|
|
55
|
+
|
|
56
|
+
function fromPointer(pos: SplitterPoint, rect: DOMRect): number {
|
|
57
|
+
const raw = isVert()
|
|
58
|
+
? (pos.y - rect.top) / rect.height
|
|
59
|
+
: (pos.x - rect.left) / rect.width
|
|
60
|
+
// Horizontal RTL: the first pane sits on the right, so invert.
|
|
61
|
+
const f = !isVert() && rtl() ? 1 - raw : raw
|
|
62
|
+
return clamp(f)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function pointerDown() { if (!disabled()) dragging = true }
|
|
66
|
+
function setFromPointer(pos: SplitterPoint, rect: DOMRect) {
|
|
67
|
+
if (!dragging || disabled()) return
|
|
68
|
+
config.onChange?.(fromPointer(pos, rect))
|
|
69
|
+
}
|
|
70
|
+
function endDrag() { dragging = false }
|
|
71
|
+
|
|
72
|
+
function nudge(delta: number) {
|
|
73
|
+
if (disabled()) return
|
|
74
|
+
config.onChange?.(clamp(fraction + delta))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function onKeydown(e: KeyboardEvent) {
|
|
78
|
+
if (disabled()) return
|
|
79
|
+
const horiz = !isVert()
|
|
80
|
+
const rtlH = horiz && rtl()
|
|
81
|
+
const inc = () => nudge(step())
|
|
82
|
+
const dec = () => nudge(-step())
|
|
83
|
+
switch (e.key) {
|
|
84
|
+
case 'ArrowRight': if (horiz) { e.preventDefault(); rtlH ? dec() : inc() } break
|
|
85
|
+
case 'ArrowLeft': if (horiz) { e.preventDefault(); rtlH ? inc() : dec() } break
|
|
86
|
+
case 'ArrowDown': if (!horiz) { e.preventDefault(); inc() } break
|
|
87
|
+
case 'ArrowUp': if (!horiz) { e.preventDefault(); dec() } break
|
|
88
|
+
case 'Home': e.preventDefault(); config.onChange?.(min()); break
|
|
89
|
+
case 'End': e.preventDefault(); config.onChange?.(max()); break
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
/** Clamped fraction (0..1) of the first pane. */
|
|
95
|
+
get fraction() { return fraction },
|
|
96
|
+
/** Clamped size of the first pane as a percentage (0..100). */
|
|
97
|
+
get percent() { return percent },
|
|
98
|
+
/** Whether the separator is being dragged. */
|
|
99
|
+
get dragging() { return dragging },
|
|
100
|
+
pointerDown,
|
|
101
|
+
setFromPointer,
|
|
102
|
+
endDrag,
|
|
103
|
+
nudge,
|
|
104
|
+
/** Spread onto the draggable separator element. */
|
|
105
|
+
separatorProps: () => ({
|
|
106
|
+
role: 'separator' as const,
|
|
107
|
+
tabindex: disabled() ? -1 : 0,
|
|
108
|
+
'aria-orientation': (isVert() ? 'horizontal' : 'vertical') as 'horizontal' | 'vertical',
|
|
109
|
+
'aria-valuemin': Math.round(min() * 100),
|
|
110
|
+
'aria-valuemax': Math.round(max() * 100),
|
|
111
|
+
'aria-valuenow': Math.round(fraction * 100),
|
|
112
|
+
'aria-label': config.ariaLabel?.() ?? 'Resize panes',
|
|
113
|
+
'aria-disabled': disabled() || undefined,
|
|
114
|
+
onkeydown: onKeydown,
|
|
115
|
+
}),
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type Splitter = ReturnType<typeof createSplitter>
|
|
@@ -14,17 +14,29 @@
|
|
|
14
14
|
*
|
|
15
15
|
* The styled <SvSwitchButton> is just one renderer over this core.
|
|
16
16
|
*/
|
|
17
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
18
|
+
|
|
17
19
|
export type SwitchConfig = {
|
|
18
20
|
checked: () => boolean
|
|
19
21
|
onChange?: (checked: boolean) => void
|
|
20
22
|
disabled?: () => boolean
|
|
23
|
+
// Editor contract (ARIA + validation) - folded into switchProps().
|
|
24
|
+
id?: () => string | undefined
|
|
25
|
+
invalid?: () => boolean
|
|
26
|
+
required?: () => boolean
|
|
27
|
+
error?: () => string | undefined
|
|
28
|
+
hint?: () => string | undefined
|
|
21
29
|
ariaLabel?: () => string | undefined
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
export type SwitchProps = {
|
|
25
33
|
type: 'button'
|
|
26
34
|
role: 'switch'
|
|
35
|
+
id: string | undefined
|
|
27
36
|
'aria-checked': boolean
|
|
37
|
+
'aria-invalid': 'true' | undefined
|
|
38
|
+
'aria-required': 'true' | undefined
|
|
39
|
+
'aria-describedby': string | undefined
|
|
28
40
|
'aria-label': string | undefined
|
|
29
41
|
disabled: boolean
|
|
30
42
|
'data-checked': '' | undefined
|
|
@@ -59,18 +71,29 @@ export function createSwitch(config: SwitchConfig): Switch {
|
|
|
59
71
|
else if (e.key === 'ArrowLeft') { if (checked()) config.onChange?.(false) }
|
|
60
72
|
}
|
|
61
73
|
|
|
74
|
+
// The validation/label ARIA is derived from the editor contract state.
|
|
75
|
+
const ariaState = (): EditorAriaState => ({
|
|
76
|
+
id: config.id?.(),
|
|
77
|
+
invalid: config.invalid?.(),
|
|
78
|
+
required: config.required?.(),
|
|
79
|
+
error: config.error?.(),
|
|
80
|
+
hint: config.hint?.(),
|
|
81
|
+
ariaLabel: config.ariaLabel?.(),
|
|
82
|
+
})
|
|
83
|
+
|
|
62
84
|
return {
|
|
63
85
|
get checked() { return checked() },
|
|
64
86
|
get disabled() { return disabled() },
|
|
65
87
|
toggle,
|
|
66
88
|
onKeydown,
|
|
67
89
|
switchProps: () => ({
|
|
68
|
-
type: 'button',
|
|
69
|
-
role: 'switch',
|
|
90
|
+
type: 'button' as const,
|
|
91
|
+
role: 'switch' as const,
|
|
92
|
+
id: config.id?.(),
|
|
70
93
|
'aria-checked': checked(),
|
|
71
|
-
'aria-label': config.ariaLabel?.(),
|
|
72
94
|
disabled: disabled(),
|
|
73
95
|
'data-checked': checked() ? '' : undefined,
|
|
96
|
+
...editorAria(ariaState()),
|
|
74
97
|
onclick: toggle,
|
|
75
98
|
onkeydown: onKeydown,
|
|
76
99
|
}),
|