@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
package/src/countries.ts
CHANGED
|
@@ -68,4 +68,29 @@ export const COUNTRIES: Country[] = [
|
|
|
68
68
|
{ code: 'MX', name: 'Mexico', dial: '+52' },
|
|
69
69
|
]
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Expected national (post-dial-code) significant-digit count per country, as an
|
|
73
|
+
* exact length or an inclusive [min, max] range. A pragmatic length-based check
|
|
74
|
+
* (not full libphonenumber). Countries without an entry accept any non-empty
|
|
75
|
+
* number.
|
|
76
|
+
*/
|
|
77
|
+
export const PHONE_NATIONAL_LENGTHS: Record<string, number | [number, number]> = {
|
|
78
|
+
US: 10, CA: 10, GB: 10, IE: 9, FR: 9, DE: [10, 11], ES: 9, IT: [9, 10], NL: 9, BE: [8, 9],
|
|
79
|
+
CH: 9, AT: [10, 12], SE: [7, 9], NO: 8, DK: 8, FI: [9, 10], PL: 9, PT: 9, GR: 10, CZ: 9,
|
|
80
|
+
RU: 10, TR: 10, IL: 9, AE: 9, SA: 9, IN: 10, PK: 10, CN: 11, HK: 8, JP: 10, KR: [9, 10],
|
|
81
|
+
SG: 8, MY: [9, 10], TH: 9, ID: [9, 11], PH: 10, VN: 9, AU: 9, NZ: [8, 10], ZA: 9, NG: 10,
|
|
82
|
+
EG: 10, KE: 9, MA: 9, BR: 11, AR: 10, CL: 9, CO: 10, MX: 10,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether `nationalDigits` is a plausible national number for `code` (length
|
|
87
|
+
* check). Unknown countries pass on any non-empty input. Pure.
|
|
88
|
+
*/
|
|
89
|
+
export function phoneDigitsValid(code: string, nationalDigits: string): boolean {
|
|
90
|
+
const n = (nationalDigits.match(/\d/g) ?? []).length
|
|
91
|
+
const rule = PHONE_NATIONAL_LENGTHS[code.toUpperCase()]
|
|
92
|
+
if (rule == null) return n > 0
|
|
93
|
+
return typeof rule === 'number' ? n === rule : n >= rule[0] && n <= rule[1]
|
|
94
|
+
}
|
|
95
|
+
|
|
71
96
|
export const COUNTRY_BY_CODE = new Map(COUNTRIES.map((c) => [c.code, c]))
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createAccordion - the HEADLESS core behind <SvAccordion>: a WAI-ARIA accordion
|
|
3
|
+
* (collapsible sections) with single- or multiple-expand, roving header focus
|
|
4
|
+
* (Up/Down/Home/End) and full ARIA, exposed as **prop-getters** you spread onto
|
|
5
|
+
* YOUR OWN markup. No styles, no DOM. Parity: Smart `smart-accordion`.
|
|
6
|
+
*
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <script lang="ts">
|
|
9
|
+
* import { createAccordion } from '@svgrid/grid'
|
|
10
|
+
* let expanded = $state<string[]>(['a'])
|
|
11
|
+
* const acc = createAccordion({ items: () => items, expanded: () => expanded, onChange: (ids) => (expanded = ids) })
|
|
12
|
+
* </script>
|
|
13
|
+
* {#each items as it, i (it.id)}
|
|
14
|
+
* <h3><button {...acc.headerProps(i)}>{it.label}</button></h3>
|
|
15
|
+
* {#if acc.isExpanded(it.id)}<div {...acc.panelProps(i)}>...</div>{/if}
|
|
16
|
+
* {/each}
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The styled <SvAccordion> is one renderer over this core. DOM focus movement is
|
|
20
|
+
* a render concern: the component watches `focusTick`/`focusIndex` and calls
|
|
21
|
+
* `.focus()` itself.
|
|
22
|
+
*/
|
|
23
|
+
export type AccordionItem = { id: string; label: string; disabled?: boolean }
|
|
24
|
+
/** `single` keeps at most one panel open; `multiple` allows any number. */
|
|
25
|
+
export type AccordionExpandMode = 'single' | 'multiple'
|
|
26
|
+
|
|
27
|
+
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
28
|
+
export type AccordionConfig = {
|
|
29
|
+
items: () => ReadonlyArray<AccordionItem>
|
|
30
|
+
/** Expanded item ids (controlled). */
|
|
31
|
+
expanded: () => string[]
|
|
32
|
+
onChange?: (expandedIds: string[]) => void
|
|
33
|
+
expandMode?: () => AccordionExpandMode
|
|
34
|
+
disabled?: () => boolean
|
|
35
|
+
dir?: () => 'ltr' | 'rtl' | 'auto' | undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let uid = 0
|
|
39
|
+
|
|
40
|
+
export function createAccordion(config: AccordionConfig) {
|
|
41
|
+
const id = `sv-acc-${uid++}`
|
|
42
|
+
const items = () => config.items()
|
|
43
|
+
const expandMode = () => config.expandMode?.() ?? 'single'
|
|
44
|
+
const disabled = () => config.disabled?.() ?? false
|
|
45
|
+
|
|
46
|
+
const expandedSet = $derived(new Set(config.expanded()))
|
|
47
|
+
const enabledIdx = $derived(items().map((it, i) => (it.disabled ? -1 : i)).filter((i) => i >= 0))
|
|
48
|
+
|
|
49
|
+
let focusIndex = $state(0)
|
|
50
|
+
let focusTick = $state(0)
|
|
51
|
+
const requestFocus = (i: number) => { focusIndex = i; focusTick++ }
|
|
52
|
+
|
|
53
|
+
const headerId = (i: number) => `${id}-h-${i}`
|
|
54
|
+
const panelId = (i: number) => `${id}-p-${i}`
|
|
55
|
+
const isExpanded = (itemId: string) => expandedSet.has(itemId)
|
|
56
|
+
|
|
57
|
+
function toggle(i: number) {
|
|
58
|
+
const it = items()[i]
|
|
59
|
+
if (!it || it.disabled || disabled()) return
|
|
60
|
+
const open = expandedSet.has(it.id)
|
|
61
|
+
if (expandMode() === 'single') {
|
|
62
|
+
config.onChange?.(open ? [] : [it.id])
|
|
63
|
+
} else {
|
|
64
|
+
const next = new Set(expandedSet)
|
|
65
|
+
open ? next.delete(it.id) : next.add(it.id)
|
|
66
|
+
config.onChange?.([...next])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function move(delta: number) {
|
|
71
|
+
if (!enabledIdx.length) return
|
|
72
|
+
const pos = enabledIdx.indexOf(focusIndex)
|
|
73
|
+
const base = pos < 0 ? 0 : pos
|
|
74
|
+
const next = enabledIdx[(base + delta + enabledIdx.length) % enabledIdx.length]
|
|
75
|
+
if (next != null) requestFocus(next)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function onHeaderKeydown(e: KeyboardEvent, i: number) {
|
|
79
|
+
if (disabled()) return
|
|
80
|
+
switch (e.key) {
|
|
81
|
+
case 'ArrowDown': e.preventDefault(); move(1); break
|
|
82
|
+
case 'ArrowUp': e.preventDefault(); move(-1); break
|
|
83
|
+
case 'Home': e.preventDefault(); requestFocus(enabledIdx[0] ?? 0); break
|
|
84
|
+
case 'End': e.preventDefault(); requestFocus(enabledIdx.at(-1) ?? 0); break
|
|
85
|
+
case 'Enter':
|
|
86
|
+
case ' ': e.preventDefault(); toggle(i); break
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
/** Header index that should hold DOM focus after the latest keyboard move. */
|
|
92
|
+
get focusIndex() { return focusIndex },
|
|
93
|
+
/** Monotonic counter; changes only on keyboard navigation. */
|
|
94
|
+
get focusTick() { return focusTick },
|
|
95
|
+
/** Currently-expanded item ids. */
|
|
96
|
+
get expandedIds() { return [...expandedSet] },
|
|
97
|
+
isExpanded,
|
|
98
|
+
toggle,
|
|
99
|
+
/** Spread onto the accordion root container. */
|
|
100
|
+
rootProps: () => ({
|
|
101
|
+
'data-orientation': 'vertical' as const,
|
|
102
|
+
}),
|
|
103
|
+
/** Spread onto the header <button> at `index`. */
|
|
104
|
+
headerProps: (index: number) => {
|
|
105
|
+
const it = items()[index]
|
|
106
|
+
return {
|
|
107
|
+
type: 'button' as const,
|
|
108
|
+
id: headerId(index),
|
|
109
|
+
'data-idx': index,
|
|
110
|
+
'aria-expanded': it ? isExpanded(it.id) : false,
|
|
111
|
+
'aria-controls': panelId(index),
|
|
112
|
+
disabled: disabled() || it?.disabled || undefined,
|
|
113
|
+
onclick: () => toggle(index),
|
|
114
|
+
onkeydown: (e: KeyboardEvent) => onHeaderKeydown(e, index),
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
/** Spread onto the panel/region element at `index`. */
|
|
118
|
+
panelProps: (index: number) => ({
|
|
119
|
+
role: 'region' as const,
|
|
120
|
+
id: panelId(index),
|
|
121
|
+
'aria-labelledby': headerId(index),
|
|
122
|
+
}),
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type Accordion = ReturnType<typeof createAccordion>
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
import { filterOptions, type ListOption } from './list-option'
|
|
23
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
23
24
|
|
|
24
25
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
25
26
|
export type AutocompleteConfig = {
|
|
@@ -32,11 +33,26 @@ export type AutocompleteConfig = {
|
|
|
32
33
|
ariaLabel?: () => string | undefined
|
|
33
34
|
/** DOM focus hook provided by the renderer; the core never touches the DOM. */
|
|
34
35
|
focusInput?: () => void
|
|
36
|
+
// Editor contract (ARIA + validation) - folded into inputProps().
|
|
37
|
+
id?: () => string | undefined
|
|
38
|
+
invalid?: () => boolean
|
|
39
|
+
required?: () => boolean
|
|
40
|
+
error?: () => string | undefined
|
|
41
|
+
hint?: () => string | undefined
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
export function createAutocomplete(config: AutocompleteConfig) {
|
|
38
45
|
const minChars = () => config.minChars?.() ?? 1
|
|
39
46
|
|
|
47
|
+
const ariaState = (): EditorAriaState => ({
|
|
48
|
+
id: config.id?.(),
|
|
49
|
+
invalid: config.invalid?.(),
|
|
50
|
+
required: config.required?.(),
|
|
51
|
+
error: config.error?.(),
|
|
52
|
+
hint: config.hint?.(),
|
|
53
|
+
ariaLabel: config.ariaLabel?.(),
|
|
54
|
+
})
|
|
55
|
+
|
|
40
56
|
let open = $state(false)
|
|
41
57
|
let active = $state(0)
|
|
42
58
|
|
|
@@ -75,10 +91,11 @@ export function createAutocomplete(config: AutocompleteConfig) {
|
|
|
75
91
|
onKeydown,
|
|
76
92
|
/** Spread onto the text <input>. */
|
|
77
93
|
inputProps: () => ({
|
|
94
|
+
id: config.id?.(),
|
|
78
95
|
role: 'combobox' as const,
|
|
79
96
|
'aria-expanded': open,
|
|
80
97
|
'aria-autocomplete': 'list' as const,
|
|
81
|
-
|
|
98
|
+
...editorAria(ariaState()),
|
|
82
99
|
value: config.value(),
|
|
83
100
|
disabled: config.disabled?.() ?? false,
|
|
84
101
|
oninput: onInput,
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createButtonGroup - the HEADLESS core behind <SvButtonGroup>: a segmented
|
|
3
|
+
* button bar with single- or multi-select (or plain action buttons), roving
|
|
4
|
+
* tabindex + full keyboard, exposed as **prop-getters** you spread onto YOUR OWN
|
|
5
|
+
* markup. No styles, no DOM. Parity: Smart `smart-button-group`.
|
|
6
|
+
*
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <script lang="ts">
|
|
9
|
+
* import { createButtonGroup } from '@svgrid/grid'
|
|
10
|
+
* let value = $state<string | null>('day')
|
|
11
|
+
* const g = createButtonGroup({ items: () => items, value: () => value, onChange: (v) => (value = v) })
|
|
12
|
+
* </script>
|
|
13
|
+
* <div {...g.rootProps()}>
|
|
14
|
+
* {#each items as it, i (it.value)}<button {...g.buttonProps(i)}>{it.label}</button>{/each}
|
|
15
|
+
* </div>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The styled <SvButtonGroup> is one renderer over this core.
|
|
19
|
+
*/
|
|
20
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
21
|
+
|
|
22
|
+
export type ButtonGroupItem = { value: string | number; label?: string; disabled?: boolean }
|
|
23
|
+
/** `single` = radio semantics; `multiple` = toggle set; `none` = action buttons. */
|
|
24
|
+
export type ButtonGroupMode = 'single' | 'multiple' | 'none'
|
|
25
|
+
export type ButtonGroupValue = string | number | Array<string | number> | null
|
|
26
|
+
|
|
27
|
+
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
28
|
+
export type ButtonGroupConfig = {
|
|
29
|
+
items: () => ReadonlyArray<ButtonGroupItem>
|
|
30
|
+
value: () => ButtonGroupValue
|
|
31
|
+
onChange?: (value: string | number | Array<string | number>) => void
|
|
32
|
+
mode?: () => ButtonGroupMode
|
|
33
|
+
disabled?: () => boolean
|
|
34
|
+
orientation?: () => 'horizontal' | 'vertical'
|
|
35
|
+
/** Under `'rtl'` the horizontal Left/Right arrow direction flips. */
|
|
36
|
+
dir?: () => 'ltr' | 'rtl' | 'auto' | undefined
|
|
37
|
+
// Editor contract (ARIA + validation) - folded into rootProps().
|
|
38
|
+
id?: () => string | undefined
|
|
39
|
+
invalid?: () => boolean
|
|
40
|
+
required?: () => boolean
|
|
41
|
+
error?: () => string | undefined
|
|
42
|
+
hint?: () => string | undefined
|
|
43
|
+
ariaLabel?: () => string | undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Normalize the value to an array (pure). */
|
|
47
|
+
export function toGroupArray(value: ButtonGroupValue): Array<string | number> {
|
|
48
|
+
if (value == null) return []
|
|
49
|
+
return Array.isArray(value) ? [...value] : [value]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createButtonGroup(config: ButtonGroupConfig) {
|
|
53
|
+
const items = () => config.items()
|
|
54
|
+
const mode = () => config.mode?.() ?? 'single'
|
|
55
|
+
const disabled = () => config.disabled?.() ?? false
|
|
56
|
+
const isVert = () => (config.orientation?.() ?? 'horizontal') === 'vertical'
|
|
57
|
+
const rtl = () => config.dir?.() === 'rtl'
|
|
58
|
+
|
|
59
|
+
const selected = $derived(toGroupArray(config.value()))
|
|
60
|
+
const enabledIdx = $derived(items().map((it, i) => (it.disabled ? -1 : i)).filter((i) => i >= 0))
|
|
61
|
+
|
|
62
|
+
const isSelected = (v: string | number) => selected.includes(v)
|
|
63
|
+
const selectedIdx = $derived(items().findIndex((it) => isSelected(it.value)))
|
|
64
|
+
|
|
65
|
+
// Focus cursor. In single (radio) mode moving the cursor also selects, so the
|
|
66
|
+
// roving-tabindex target follows the selection; multiple/none keep their own
|
|
67
|
+
// cursor in `active`.
|
|
68
|
+
let active = $state(0)
|
|
69
|
+
$effect(() => {
|
|
70
|
+
const it = items()[active]
|
|
71
|
+
if (!it || it.disabled) active = enabledIdx[0] ?? 0
|
|
72
|
+
})
|
|
73
|
+
const cursor = $derived(
|
|
74
|
+
mode() === 'single' ? (selectedIdx >= 0 ? selectedIdx : enabledIdx[0] ?? 0) : active,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
function pick(i: number) {
|
|
78
|
+
const it = items()[i]
|
|
79
|
+
if (!it || it.disabled || disabled()) return
|
|
80
|
+
active = i
|
|
81
|
+
if (mode() === 'none') { config.onChange?.(it.value); return }
|
|
82
|
+
if (mode() === 'multiple') {
|
|
83
|
+
const set = new Set(selected)
|
|
84
|
+
set.has(it.value) ? set.delete(it.value) : set.add(it.value)
|
|
85
|
+
config.onChange?.([...set])
|
|
86
|
+
} else {
|
|
87
|
+
config.onChange?.(it.value)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function goto(i: number) {
|
|
92
|
+
// Radio semantics select on move; toggle/action only move focus.
|
|
93
|
+
if (mode() === 'single') pick(i)
|
|
94
|
+
else active = i
|
|
95
|
+
}
|
|
96
|
+
function move(delta: number) {
|
|
97
|
+
if (!enabledIdx.length) return
|
|
98
|
+
const pos = enabledIdx.indexOf(cursor)
|
|
99
|
+
const next = enabledIdx[(pos + delta + enabledIdx.length) % enabledIdx.length]
|
|
100
|
+
if (next != null) goto(next)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function onKeydown(e: KeyboardEvent) {
|
|
104
|
+
if (disabled()) return
|
|
105
|
+
const horiz = !isVert()
|
|
106
|
+
const rtlH = horiz && rtl()
|
|
107
|
+
const fwd = horiz ? (rtlH ? 'ArrowLeft' : 'ArrowRight') : 'ArrowDown'
|
|
108
|
+
const back = horiz ? (rtlH ? 'ArrowRight' : 'ArrowLeft') : 'ArrowUp'
|
|
109
|
+
if (e.key === fwd) { e.preventDefault(); move(1) }
|
|
110
|
+
else if (e.key === back) { e.preventDefault(); move(-1) }
|
|
111
|
+
else if (e.key === 'Home') { e.preventDefault(); goto(enabledIdx[0] ?? 0) }
|
|
112
|
+
else if (e.key === 'End') { e.preventDefault(); goto(enabledIdx.at(-1) ?? 0) }
|
|
113
|
+
else if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); pick(cursor) }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const ariaState = (): EditorAriaState => ({
|
|
117
|
+
id: config.id?.(),
|
|
118
|
+
invalid: config.invalid?.(),
|
|
119
|
+
required: config.required?.(),
|
|
120
|
+
error: config.error?.(),
|
|
121
|
+
hint: config.hint?.(),
|
|
122
|
+
ariaLabel: config.ariaLabel?.(),
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const groupRole = () => (mode() === 'single' ? 'radiogroup' : 'group')
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
/** Roving-focus target index (follows the selection in single mode). */
|
|
129
|
+
get activeIndex() { return cursor },
|
|
130
|
+
/** Selected values as an array (single mode -> 0 or 1 entry). */
|
|
131
|
+
get selectedValues() { return selected },
|
|
132
|
+
isSelected,
|
|
133
|
+
isActive: (i: number) => i === cursor,
|
|
134
|
+
pick,
|
|
135
|
+
move,
|
|
136
|
+
onKeydown,
|
|
137
|
+
/** Spread onto the group container element. */
|
|
138
|
+
rootProps: () => ({
|
|
139
|
+
role: groupRole(),
|
|
140
|
+
id: config.id?.(),
|
|
141
|
+
'aria-orientation': isVert() ? ('vertical' as const) : ('horizontal' as const),
|
|
142
|
+
...editorAria(ariaState()),
|
|
143
|
+
onkeydown: onKeydown,
|
|
144
|
+
}),
|
|
145
|
+
/** Spread onto the button element at `index`. */
|
|
146
|
+
buttonProps: (index: number) => {
|
|
147
|
+
const it = items()[index]
|
|
148
|
+
const on = it ? isSelected(it.value) : false
|
|
149
|
+
const single = mode() === 'single'
|
|
150
|
+
return {
|
|
151
|
+
type: 'button' as const,
|
|
152
|
+
role: single ? ('radio' as const) : undefined,
|
|
153
|
+
'data-idx': index,
|
|
154
|
+
'aria-checked': single ? on : undefined,
|
|
155
|
+
'aria-pressed': mode() === 'multiple' ? on : undefined,
|
|
156
|
+
'data-selected': on ? '' : undefined,
|
|
157
|
+
disabled: disabled() || it?.disabled || undefined,
|
|
158
|
+
// Roving tabindex: only the cursor (selection in single mode) is tabbable.
|
|
159
|
+
tabindex: mode() === 'none' ? 0 : index === cursor ? 0 : -1,
|
|
160
|
+
onclick: () => pick(index),
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export type ButtonGroup = ReturnType<typeof createButtonGroup>
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
type SelectionState,
|
|
49
49
|
} from './datetime/date-selection'
|
|
50
50
|
import { isDisabledDay, isImportant, type RestrictOptions } from './datetime/date-restrict'
|
|
51
|
+
import { matchesRecurrence, type RecurrenceRule } from './recurrence'
|
|
51
52
|
|
|
52
53
|
export type DisplayMode = 'month' | 'year' | 'decade'
|
|
53
54
|
export type CalendarNameFormat = 'narrow' | 'short' | 'long'
|
|
@@ -82,6 +83,8 @@ export type CalendarDayState = {
|
|
|
82
83
|
outside: boolean
|
|
83
84
|
focused: boolean
|
|
84
85
|
preview: boolean
|
|
86
|
+
/** Matches one of the `recurrence` rules (repeat pattern). */
|
|
87
|
+
recurring: boolean
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
/** Reactive inputs are passed as getters so the core tracks live prop changes;
|
|
@@ -105,6 +108,8 @@ export type CalendarConfig = {
|
|
|
105
108
|
dayNameFormat?: () => CalendarNameFormat
|
|
106
109
|
monthNameFormat?: () => CalendarNameFormat
|
|
107
110
|
dateTooltip?: () => ((date: Date) => string | null | undefined) | undefined
|
|
111
|
+
/** Repeat pattern(s) - matching days get `dayState().recurring = true`. */
|
|
112
|
+
recurrence?: () => RecurrenceRule | ReadonlyArray<RecurrenceRule> | null
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
const defaultLocale = () => (typeof navigator !== 'undefined' ? navigator.language : 'en-US')
|
|
@@ -216,6 +221,7 @@ export function createCalendar(config: CalendarConfig) {
|
|
|
216
221
|
outside: !isSameMonth(d, panelMonth),
|
|
217
222
|
focused: isSameDay(d, focusDate),
|
|
218
223
|
preview: inPreview(d),
|
|
224
|
+
recurring: matchesRecurrence(d, config.recurrence?.() ?? null),
|
|
219
225
|
}
|
|
220
226
|
}
|
|
221
227
|
|
|
@@ -15,18 +15,30 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The styled <SvCheckBox> is just one renderer over this core.
|
|
17
17
|
*/
|
|
18
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
19
|
+
|
|
18
20
|
export type CheckboxConfig = {
|
|
19
21
|
checked: () => boolean
|
|
20
22
|
indeterminate?: () => boolean
|
|
21
23
|
onChange?: (checked: boolean) => void
|
|
22
24
|
disabled?: () => boolean
|
|
25
|
+
// Editor contract (ARIA + validation) - folded into boxProps().
|
|
26
|
+
id?: () => string | undefined
|
|
27
|
+
invalid?: () => boolean
|
|
28
|
+
required?: () => boolean
|
|
29
|
+
error?: () => string | undefined
|
|
30
|
+
hint?: () => string | undefined
|
|
23
31
|
ariaLabel?: () => string | undefined
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
export type CheckboxBoxProps = {
|
|
27
35
|
type: 'button'
|
|
28
36
|
role: 'checkbox'
|
|
37
|
+
id: string | undefined
|
|
29
38
|
'aria-checked': boolean | 'mixed'
|
|
39
|
+
'aria-invalid': 'true' | undefined
|
|
40
|
+
'aria-required': 'true' | undefined
|
|
41
|
+
'aria-describedby': string | undefined
|
|
30
42
|
'aria-label': string | undefined
|
|
31
43
|
disabled: boolean
|
|
32
44
|
'data-checked': '' | undefined
|
|
@@ -55,6 +67,15 @@ export function createCheckbox(config: CheckboxConfig): Checkbox {
|
|
|
55
67
|
config.onChange?.(indeterminate() ? true : !checked())
|
|
56
68
|
}
|
|
57
69
|
|
|
70
|
+
const ariaState = (): EditorAriaState => ({
|
|
71
|
+
id: config.id?.(),
|
|
72
|
+
invalid: config.invalid?.(),
|
|
73
|
+
required: config.required?.(),
|
|
74
|
+
error: config.error?.(),
|
|
75
|
+
hint: config.hint?.(),
|
|
76
|
+
ariaLabel: config.ariaLabel?.(),
|
|
77
|
+
})
|
|
78
|
+
|
|
58
79
|
return {
|
|
59
80
|
get checked() { return checked() },
|
|
60
81
|
get indeterminate() { return indeterminate() },
|
|
@@ -63,11 +84,12 @@ export function createCheckbox(config: CheckboxConfig): Checkbox {
|
|
|
63
84
|
boxProps: () => ({
|
|
64
85
|
type: 'button',
|
|
65
86
|
role: 'checkbox',
|
|
87
|
+
id: config.id?.(),
|
|
66
88
|
'aria-checked': indeterminate() ? 'mixed' : checked(),
|
|
67
|
-
'aria-label': config.ariaLabel?.(),
|
|
68
89
|
disabled: disabled(),
|
|
69
90
|
'data-checked': checked() && !indeterminate() ? '' : undefined,
|
|
70
91
|
'data-indeterminate': indeterminate() ? '' : undefined,
|
|
92
|
+
...editorAria(ariaState()),
|
|
71
93
|
onclick: toggle,
|
|
72
94
|
}),
|
|
73
95
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* The styled <SvColorInput> is just one renderer over this core (it adds the
|
|
18
18
|
* portal + anchored positioning).
|
|
19
19
|
*/
|
|
20
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
20
21
|
|
|
21
22
|
export const DEFAULT_PALETTE = [
|
|
22
23
|
'#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6',
|
|
@@ -39,6 +40,12 @@ export type ColorInputConfig = {
|
|
|
39
40
|
disabled?: () => boolean
|
|
40
41
|
readonly?: () => boolean
|
|
41
42
|
palette?: () => string[]
|
|
43
|
+
// Editor contract (ARIA + validation) - folded into swatchProps().
|
|
44
|
+
id?: () => string | undefined
|
|
45
|
+
invalid?: () => boolean
|
|
46
|
+
required?: () => boolean
|
|
47
|
+
error?: () => string | undefined
|
|
48
|
+
hint?: () => string | undefined
|
|
42
49
|
ariaLabel?: () => string | undefined
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -71,6 +78,15 @@ export function createColorInput(config: ColorInputConfig) {
|
|
|
71
78
|
}
|
|
72
79
|
const isActive = (c: string) => normalizeHex(c) === normalized
|
|
73
80
|
|
|
81
|
+
const ariaState = (): EditorAriaState => ({
|
|
82
|
+
id: config.id?.(),
|
|
83
|
+
invalid: config.invalid?.(),
|
|
84
|
+
required: config.required?.(),
|
|
85
|
+
error: config.error?.(),
|
|
86
|
+
hint: config.hint?.(),
|
|
87
|
+
ariaLabel: config.ariaLabel?.(),
|
|
88
|
+
})
|
|
89
|
+
|
|
74
90
|
// Popover control surface (open state + show/close/toggle), no DOM measurement.
|
|
75
91
|
const popover = {
|
|
76
92
|
get open() { return open },
|
|
@@ -96,9 +112,13 @@ export function createColorInput(config: ColorInputConfig) {
|
|
|
96
112
|
isActive,
|
|
97
113
|
/** Spread onto the swatch trigger button. */
|
|
98
114
|
swatchProps: () => ({
|
|
115
|
+
id: config.id?.(),
|
|
99
116
|
type: 'button' as const,
|
|
100
117
|
'aria-haspopup': 'dialog' as const,
|
|
101
118
|
'aria-expanded': open,
|
|
119
|
+
// aria-invalid/required/describedby come from the editor contract; the
|
|
120
|
+
// aria-label default names the swatch by its current color when unset.
|
|
121
|
+
...editorAria(ariaState()),
|
|
102
122
|
'aria-label': config.ariaLabel?.() ?? `Color ${normalized}`,
|
|
103
123
|
disabled: !isInteractive,
|
|
104
124
|
onclick: toggle,
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
import { filterOptions, type ListOption } from './list-option'
|
|
23
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
23
24
|
|
|
24
25
|
export type ComboboxValue = string | number | null
|
|
25
26
|
|
|
@@ -30,9 +31,17 @@ export type ComboboxConfig = {
|
|
|
30
31
|
onChange?: (value: ComboboxValue) => void
|
|
31
32
|
disabled?: () => boolean
|
|
32
33
|
ariaLabel?: () => string | undefined
|
|
34
|
+
/** Filter the options locally by the query. Set false for server-side search. */
|
|
35
|
+
localFilter?: () => boolean
|
|
33
36
|
/** DOM focus hooks provided by the renderer; the core itself never touches the DOM. */
|
|
34
37
|
focusInput?: () => void
|
|
35
38
|
blurInput?: () => void
|
|
39
|
+
// Editor contract (ARIA + validation) - folded into inputProps().
|
|
40
|
+
id?: () => string | undefined
|
|
41
|
+
invalid?: () => boolean
|
|
42
|
+
required?: () => boolean
|
|
43
|
+
error?: () => string | undefined
|
|
44
|
+
hint?: () => string | undefined
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
let uid = 0
|
|
@@ -48,8 +57,9 @@ export function createCombobox(config: ComboboxConfig) {
|
|
|
48
57
|
let active = $state(0)
|
|
49
58
|
let editing = $state(false)
|
|
50
59
|
|
|
60
|
+
const localFilter = () => config.localFilter?.() ?? true
|
|
51
61
|
const selected = $derived(opts().find((o) => o.value === config.value()) ?? null)
|
|
52
|
-
const filtered = $derived(editing ? filterOptions(opts(), query) : [...opts()])
|
|
62
|
+
const filtered = $derived(editing && localFilter() ? filterOptions(opts(), query) : [...opts()])
|
|
53
63
|
const shownText = $derived(editing ? query : selected?.label ?? '')
|
|
54
64
|
|
|
55
65
|
// While not actively editing, keep the shown text in sync with the selection.
|
|
@@ -57,6 +67,15 @@ export function createCombobox(config: ComboboxConfig) {
|
|
|
57
67
|
|
|
58
68
|
const optionId = (i: number) => `${listId}-opt-${i}`
|
|
59
69
|
|
|
70
|
+
const ariaState = (): EditorAriaState => ({
|
|
71
|
+
id: config.id?.(),
|
|
72
|
+
invalid: config.invalid?.(),
|
|
73
|
+
required: config.required?.(),
|
|
74
|
+
error: config.error?.(),
|
|
75
|
+
hint: config.hint?.(),
|
|
76
|
+
ariaLabel: config.ariaLabel?.(),
|
|
77
|
+
})
|
|
78
|
+
|
|
60
79
|
function openPanel() { if (disabled() || open) return; open = true; active = 0 }
|
|
61
80
|
function close(revert = true) {
|
|
62
81
|
open = false; editing = false
|
|
@@ -114,11 +133,12 @@ export function createCombobox(config: ComboboxConfig) {
|
|
|
114
133
|
onKeydown,
|
|
115
134
|
/** Spread onto the editable <input>. */
|
|
116
135
|
inputProps: () => ({
|
|
136
|
+
id: config.id?.(),
|
|
117
137
|
role: 'combobox' as const,
|
|
118
138
|
'aria-expanded': open,
|
|
119
139
|
'aria-controls': listId,
|
|
120
140
|
'aria-autocomplete': 'list' as const,
|
|
121
|
-
|
|
141
|
+
...editorAria(ariaState()),
|
|
122
142
|
value: shownText,
|
|
123
143
|
disabled: disabled(),
|
|
124
144
|
oninput: onInput,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
import { COUNTRIES, COUNTRY_BY_CODE, type Country } from './countries'
|
|
24
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
24
25
|
|
|
25
26
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
26
27
|
export type CountryInputConfig = {
|
|
@@ -28,13 +29,30 @@ export type CountryInputConfig = {
|
|
|
28
29
|
onChange?: (code: string) => void
|
|
29
30
|
disabled?: () => boolean
|
|
30
31
|
ariaLabel?: () => string | undefined
|
|
32
|
+
/** Accessible label for the in-panel search field (localizable). */
|
|
33
|
+
searchLabel?: () => string | undefined
|
|
31
34
|
/** DOM focus hook provided by the renderer; the core never touches the DOM. */
|
|
32
35
|
focusTrigger?: () => void
|
|
36
|
+
// Editor contract (ARIA + validation) - folded into triggerProps().
|
|
37
|
+
id?: () => string | undefined
|
|
38
|
+
invalid?: () => boolean
|
|
39
|
+
required?: () => boolean
|
|
40
|
+
error?: () => string | undefined
|
|
41
|
+
hint?: () => string | undefined
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
export function createCountryInput(config: CountryInputConfig) {
|
|
36
45
|
const disabled = () => config.disabled?.() ?? false
|
|
37
46
|
|
|
47
|
+
const ariaState = (): EditorAriaState => ({
|
|
48
|
+
id: config.id?.(),
|
|
49
|
+
invalid: config.invalid?.(),
|
|
50
|
+
required: config.required?.(),
|
|
51
|
+
error: config.error?.(),
|
|
52
|
+
hint: config.hint?.(),
|
|
53
|
+
ariaLabel: config.ariaLabel?.(),
|
|
54
|
+
})
|
|
55
|
+
|
|
38
56
|
let open = $state(false)
|
|
39
57
|
let query = $state('')
|
|
40
58
|
let active = $state(0)
|
|
@@ -89,10 +107,11 @@ export function createCountryInput(config: CountryInputConfig) {
|
|
|
89
107
|
onKeydown,
|
|
90
108
|
/** Spread onto the trigger <button>. */
|
|
91
109
|
triggerProps: () => ({
|
|
110
|
+
id: config.id?.(),
|
|
92
111
|
type: 'button' as const,
|
|
93
112
|
'aria-haspopup': 'listbox' as const,
|
|
94
113
|
'aria-expanded': open,
|
|
95
|
-
|
|
114
|
+
...editorAria(ariaState()),
|
|
96
115
|
disabled: disabled(),
|
|
97
116
|
onclick: toggle,
|
|
98
117
|
}),
|
|
@@ -100,7 +119,7 @@ export function createCountryInput(config: CountryInputConfig) {
|
|
|
100
119
|
searchProps: () => ({
|
|
101
120
|
type: 'text' as const,
|
|
102
121
|
value: query,
|
|
103
|
-
'aria-label': 'Search countries',
|
|
122
|
+
'aria-label': config.searchLabel?.() ?? 'Search countries',
|
|
104
123
|
oninput: onSearchInput,
|
|
105
124
|
onkeydown: onKeydown,
|
|
106
125
|
}),
|