@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
|
@@ -19,13 +19,22 @@ export function createTabs(config) {
|
|
|
19
19
|
return;
|
|
20
20
|
config.onChange?.(tid);
|
|
21
21
|
}
|
|
22
|
+
function close(tid) {
|
|
23
|
+
const t = tabs().find((x) => x.id === tid);
|
|
24
|
+
if (t?.closable)
|
|
25
|
+
config.onClose?.(tid);
|
|
26
|
+
}
|
|
22
27
|
function onKeydown(e) {
|
|
23
28
|
const list = enabled;
|
|
24
29
|
if (!list.length)
|
|
25
30
|
return;
|
|
26
31
|
const idx = list.findIndex((t) => t.id === active);
|
|
27
|
-
const
|
|
28
|
-
|
|
32
|
+
const horiz = orientation() === 'horizontal';
|
|
33
|
+
// Under horizontal RTL the Left/Right arrows swap so focus follows the
|
|
34
|
+
// visual order (Right moves to the previous tab).
|
|
35
|
+
const rtlH = horiz && config.dir?.() === 'rtl';
|
|
36
|
+
const fwd = horiz ? (rtlH ? 'ArrowLeft' : 'ArrowRight') : 'ArrowDown';
|
|
37
|
+
const back = horiz ? (rtlH ? 'ArrowRight' : 'ArrowLeft') : 'ArrowUp';
|
|
29
38
|
let next = null;
|
|
30
39
|
if (e.key === fwd)
|
|
31
40
|
next = list[(idx + 1) % list.length]?.id ?? null;
|
|
@@ -40,6 +49,11 @@ export function createTabs(config) {
|
|
|
40
49
|
select(active);
|
|
41
50
|
return;
|
|
42
51
|
}
|
|
52
|
+
else if (e.key === 'Delete') {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
close(active);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
43
57
|
else
|
|
44
58
|
return;
|
|
45
59
|
if (next) {
|
|
@@ -58,6 +72,7 @@ export function createTabs(config) {
|
|
|
58
72
|
get focusTick() { return focusTick; },
|
|
59
73
|
isActive: (tid) => tid === active,
|
|
60
74
|
select,
|
|
75
|
+
close,
|
|
61
76
|
onKeydown,
|
|
62
77
|
/** Spread onto the tablist container element. */
|
|
63
78
|
tablistProps: () => ({
|
|
@@ -27,6 +27,15 @@ export type TagsInputConfig = {
|
|
|
27
27
|
unique?: () => boolean;
|
|
28
28
|
max?: () => number;
|
|
29
29
|
ariaLabel?: () => string | undefined;
|
|
30
|
+
/** Accessible label for the draft input (localizable). */
|
|
31
|
+
addLabel?: () => string | undefined;
|
|
32
|
+
/** Accessible label prefix for a chip's remove button (localizable). */
|
|
33
|
+
removeLabel?: () => string | undefined;
|
|
34
|
+
id?: () => string | undefined;
|
|
35
|
+
invalid?: () => boolean;
|
|
36
|
+
required?: () => boolean;
|
|
37
|
+
error?: () => string | undefined;
|
|
38
|
+
hint?: () => string | undefined;
|
|
30
39
|
};
|
|
31
40
|
export declare function createTagsInput(config: TagsInputConfig): {
|
|
32
41
|
/** Current tags (controlled value). */
|
|
@@ -38,8 +47,11 @@ export declare function createTagsInput(config: TagsInputConfig): {
|
|
|
38
47
|
setDraft: (v: string) => void;
|
|
39
48
|
/** Spread onto the container element. */
|
|
40
49
|
rootProps: () => {
|
|
41
|
-
|
|
50
|
+
'aria-invalid': "true" | undefined;
|
|
51
|
+
'aria-required': "true" | undefined;
|
|
52
|
+
'aria-describedby': string | undefined;
|
|
42
53
|
'aria-label': string | undefined;
|
|
54
|
+
role: "group";
|
|
43
55
|
};
|
|
44
56
|
/** Spread onto the text <input>. */
|
|
45
57
|
inputProps: () => {
|
|
@@ -18,11 +18,20 @@
|
|
|
18
18
|
* </div>
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
+
import { editorAria } from './editor-contract';
|
|
21
22
|
export function createTagsInput(config) {
|
|
22
23
|
const disabled = () => config.disabled?.() ?? false;
|
|
23
24
|
const unique = () => config.unique?.() ?? true;
|
|
24
25
|
const max = () => config.max?.() ?? Infinity;
|
|
25
26
|
const tags = () => config.value();
|
|
27
|
+
const ariaState = () => ({
|
|
28
|
+
id: config.id?.(),
|
|
29
|
+
invalid: config.invalid?.(),
|
|
30
|
+
required: config.required?.(),
|
|
31
|
+
error: config.error?.(),
|
|
32
|
+
hint: config.hint?.(),
|
|
33
|
+
ariaLabel: config.ariaLabel?.(),
|
|
34
|
+
});
|
|
26
35
|
let draft = $state('');
|
|
27
36
|
function add(raw) {
|
|
28
37
|
const tag = raw.trim();
|
|
@@ -67,14 +76,14 @@ export function createTagsInput(config) {
|
|
|
67
76
|
/** Spread onto the container element. */
|
|
68
77
|
rootProps: () => ({
|
|
69
78
|
role: 'group',
|
|
70
|
-
|
|
79
|
+
...editorAria(ariaState()),
|
|
71
80
|
}),
|
|
72
81
|
/** Spread onto the text <input>. */
|
|
73
82
|
inputProps: () => ({
|
|
74
83
|
type: 'text',
|
|
75
84
|
value: draft,
|
|
76
85
|
disabled: disabled(),
|
|
77
|
-
'aria-label': config.ariaLabel?.() ?? 'Add tag',
|
|
86
|
+
'aria-label': config.addLabel?.() ?? config.ariaLabel?.() ?? 'Add tag',
|
|
78
87
|
oninput: onInput,
|
|
79
88
|
onkeydown: onKeydown,
|
|
80
89
|
onblur: onBlur,
|
|
@@ -86,7 +95,7 @@ export function createTagsInput(config) {
|
|
|
86
95
|
/** Spread onto a chip's remove <button> at `index`. */
|
|
87
96
|
removeProps: (index) => ({
|
|
88
97
|
type: 'button',
|
|
89
|
-
'aria-label':
|
|
98
|
+
'aria-label': `${config.removeLabel?.() ?? 'Remove'} ${tags()[index] ?? ''}`.trim(),
|
|
90
99
|
onclick: () => removeAt(index),
|
|
91
100
|
}),
|
|
92
101
|
};
|
|
@@ -21,11 +21,20 @@ export type ToggleConfig = {
|
|
|
21
21
|
pressed: () => boolean;
|
|
22
22
|
onChange?: (pressed: boolean) => void;
|
|
23
23
|
disabled?: () => boolean;
|
|
24
|
+
id?: () => string | undefined;
|
|
25
|
+
invalid?: () => boolean;
|
|
26
|
+
required?: () => boolean;
|
|
27
|
+
error?: () => string | undefined;
|
|
28
|
+
hint?: () => string | undefined;
|
|
24
29
|
ariaLabel?: () => string | undefined;
|
|
25
30
|
};
|
|
26
31
|
export type ToggleButtonProps = {
|
|
27
32
|
type: 'button';
|
|
33
|
+
id: string | undefined;
|
|
28
34
|
'aria-pressed': boolean;
|
|
35
|
+
'aria-invalid': 'true' | undefined;
|
|
36
|
+
'aria-required': 'true' | undefined;
|
|
37
|
+
'aria-describedby': string | undefined;
|
|
29
38
|
'aria-label': string | undefined;
|
|
30
39
|
disabled: boolean;
|
|
31
40
|
'data-pressed': '' | undefined;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The styled <SvToggleButton> is just one renderer over this core.
|
|
17
17
|
*/
|
|
18
|
+
import { editorAria } from './editor-contract';
|
|
18
19
|
export function createToggle(config) {
|
|
19
20
|
const pressed = () => config.pressed();
|
|
20
21
|
const disabled = () => config.disabled?.() ?? false;
|
|
@@ -23,16 +24,26 @@ export function createToggle(config) {
|
|
|
23
24
|
return;
|
|
24
25
|
config.onChange?.(!pressed());
|
|
25
26
|
}
|
|
27
|
+
// The validation/label ARIA is derived from the editor contract state.
|
|
28
|
+
const ariaState = () => ({
|
|
29
|
+
id: config.id?.(),
|
|
30
|
+
invalid: config.invalid?.(),
|
|
31
|
+
required: config.required?.(),
|
|
32
|
+
error: config.error?.(),
|
|
33
|
+
hint: config.hint?.(),
|
|
34
|
+
ariaLabel: config.ariaLabel?.(),
|
|
35
|
+
});
|
|
26
36
|
return {
|
|
27
37
|
get pressed() { return pressed(); },
|
|
28
38
|
get disabled() { return disabled(); },
|
|
29
39
|
toggle,
|
|
30
40
|
buttonProps: () => ({
|
|
31
41
|
type: 'button',
|
|
42
|
+
id: config.id?.(),
|
|
32
43
|
'aria-pressed': pressed(),
|
|
33
|
-
'aria-label': config.ariaLabel?.(),
|
|
34
44
|
disabled: disabled(),
|
|
35
45
|
'data-pressed': pressed() ? '' : undefined,
|
|
46
|
+
...editorAria(ariaState()),
|
|
36
47
|
onclick: toggle,
|
|
37
48
|
}),
|
|
38
49
|
};
|
|
@@ -26,6 +26,9 @@ export type TreeNode = {
|
|
|
26
26
|
label: string;
|
|
27
27
|
children?: TreeNode[];
|
|
28
28
|
disabled?: boolean;
|
|
29
|
+
/** Marks a node whose children are loaded on demand (shows an expand arrow
|
|
30
|
+
* even with no children yet; the renderer loads them on first expand). */
|
|
31
|
+
lazy?: boolean;
|
|
29
32
|
};
|
|
30
33
|
export type CheckState = 'checked' | 'indeterminate' | 'unchecked';
|
|
31
34
|
/** A single visible (flattened) tree row. */
|
|
@@ -41,6 +44,18 @@ export type TreeRow = {
|
|
|
41
44
|
export declare function treeDescendantIds(node: TreeNode): string[];
|
|
42
45
|
/** Tri-state check status of `node` given the set of checked ids (pure). */
|
|
43
46
|
export declare function treeCheckState(node: TreeNode, checked: ReadonlySet<string>): CheckState;
|
|
47
|
+
/** Where a dragged node lands relative to the drop target. */
|
|
48
|
+
export type TreeDropPosition = 'before' | 'after' | 'inside';
|
|
49
|
+
/** True if `id` is inside `node`'s subtree (used to block invalid drops). */
|
|
50
|
+
export declare function treeContains(node: TreeNode, id: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Return a NEW node tree with `dragId` moved before/after/inside `targetId`
|
|
53
|
+
* (pure - the caller sets it back as the controlled `nodes`). No-ops on invalid
|
|
54
|
+
* moves (self, or into own descendant).
|
|
55
|
+
*/
|
|
56
|
+
export declare function moveTreeNode(nodes: ReadonlyArray<TreeNode>, dragId: string, targetId: string, position: TreeDropPosition): TreeNode[];
|
|
57
|
+
/** Sort siblings (recursively) by a comparator (pure). */
|
|
58
|
+
export declare function sortTreeNodes(nodes: ReadonlyArray<TreeNode>, compare: (a: TreeNode, b: TreeNode) => number): TreeNode[];
|
|
44
59
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
45
60
|
export type TreeConfig = {
|
|
46
61
|
nodes: () => ReadonlyArray<TreeNode>;
|
|
@@ -53,6 +68,10 @@ export type TreeConfig = {
|
|
|
53
68
|
checked?: () => string[];
|
|
54
69
|
onCheck?: (ids: string[]) => void;
|
|
55
70
|
ariaLabel?: () => string | undefined;
|
|
71
|
+
/** Text direction; under `'rtl'` the Left/Right expand/collapse keys swap. */
|
|
72
|
+
dir?: () => import('./editor-contract').EditorDir | undefined;
|
|
73
|
+
/** Filter query: show only matching nodes + their ancestors, auto-expanded. */
|
|
74
|
+
filter?: () => string | undefined;
|
|
56
75
|
};
|
|
57
76
|
export declare function createTree(config: TreeConfig): {
|
|
58
77
|
/** Internally-managed set of expanded node ids. */
|
|
@@ -32,6 +32,61 @@ function findNode(list, id) {
|
|
|
32
32
|
}
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
|
+
/** True if `id` is inside `node`'s subtree (used to block invalid drops). */
|
|
36
|
+
export function treeContains(node, id) {
|
|
37
|
+
return node.children?.some((c) => c.id === id || treeContains(c, id)) ?? false;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Return a NEW node tree with `dragId` moved before/after/inside `targetId`
|
|
41
|
+
* (pure - the caller sets it back as the controlled `nodes`). No-ops on invalid
|
|
42
|
+
* moves (self, or into own descendant).
|
|
43
|
+
*/
|
|
44
|
+
export function moveTreeNode(nodes, dragId, targetId, position) {
|
|
45
|
+
if (dragId === targetId)
|
|
46
|
+
return [...nodes];
|
|
47
|
+
const dragged = findNode(nodes, dragId);
|
|
48
|
+
if (!dragged)
|
|
49
|
+
return [...nodes];
|
|
50
|
+
if (dragId === targetId || treeContains(dragged, targetId))
|
|
51
|
+
return [...nodes];
|
|
52
|
+
const without = (list) => {
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const n of list) {
|
|
55
|
+
if (n.id === dragId)
|
|
56
|
+
continue;
|
|
57
|
+
out.push(n.children ? { ...n, children: without(n.children) } : n);
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
};
|
|
61
|
+
const insert = (list) => {
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const n of list) {
|
|
64
|
+
if (n.id === targetId) {
|
|
65
|
+
if (position === 'before') {
|
|
66
|
+
out.push(dragged, n);
|
|
67
|
+
}
|
|
68
|
+
else if (position === 'after') {
|
|
69
|
+
out.push(n, dragged);
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
out.push({ ...n, children: [...(n.children ?? []), dragged] });
|
|
73
|
+
}
|
|
74
|
+
else if (n.children) {
|
|
75
|
+
out.push({ ...n, children: insert(n.children) });
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
out.push(n);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
};
|
|
82
|
+
return insert(without(nodes));
|
|
83
|
+
}
|
|
84
|
+
/** Sort siblings (recursively) by a comparator (pure). */
|
|
85
|
+
export function sortTreeNodes(nodes, compare) {
|
|
86
|
+
return [...nodes]
|
|
87
|
+
.sort(compare)
|
|
88
|
+
.map((n) => (n.children ? { ...n, children: sortTreeNodes(n.children, compare) } : n));
|
|
89
|
+
}
|
|
35
90
|
export function createTree(config) {
|
|
36
91
|
const nodes = () => config.nodes();
|
|
37
92
|
const checkable = () => config.checkable?.() ?? false;
|
|
@@ -47,14 +102,41 @@ export function createTree(config) {
|
|
|
47
102
|
expanded = new Set(ids);
|
|
48
103
|
}
|
|
49
104
|
});
|
|
105
|
+
// When filtering, precompute which nodes to show (a match, or an ancestor of a
|
|
106
|
+
// match) and which to force-open so matches are revealed.
|
|
107
|
+
const filterInfo = $derived.by(() => {
|
|
108
|
+
const q = config.filter?.()?.trim().toLowerCase();
|
|
109
|
+
if (!q)
|
|
110
|
+
return null;
|
|
111
|
+
const visible = new Set();
|
|
112
|
+
const forceOpen = new Set();
|
|
113
|
+
const walk = (node) => {
|
|
114
|
+
let childMatch = false;
|
|
115
|
+
for (const c of node.children ?? [])
|
|
116
|
+
childMatch = walk(c) || childMatch;
|
|
117
|
+
const selfMatch = node.label.toLowerCase().includes(q);
|
|
118
|
+
if (selfMatch || childMatch) {
|
|
119
|
+
visible.add(node.id);
|
|
120
|
+
if (childMatch)
|
|
121
|
+
forceOpen.add(node.id);
|
|
122
|
+
}
|
|
123
|
+
return selfMatch || childMatch;
|
|
124
|
+
};
|
|
125
|
+
for (const n of nodes())
|
|
126
|
+
walk(n);
|
|
127
|
+
return { visible, forceOpen };
|
|
128
|
+
});
|
|
50
129
|
const rows = $derived.by(() => {
|
|
51
130
|
const out = [];
|
|
131
|
+
const fi = filterInfo;
|
|
52
132
|
const walk = (list, depth, parentId) => {
|
|
53
133
|
for (const node of list) {
|
|
54
|
-
|
|
55
|
-
|
|
134
|
+
if (fi && !fi.visible.has(node.id))
|
|
135
|
+
continue;
|
|
136
|
+
const hasChildren = !!node.children?.length || !!node.lazy;
|
|
137
|
+
const open = fi ? fi.forceOpen.has(node.id) || expanded.has(node.id) : expanded.has(node.id);
|
|
56
138
|
out.push({ node, depth, hasChildren, open, parentId, index: out.length });
|
|
57
|
-
if (
|
|
139
|
+
if (open && node.children?.length)
|
|
58
140
|
walk(node.children, depth + 1, node.id);
|
|
59
141
|
}
|
|
60
142
|
};
|
|
@@ -73,7 +155,9 @@ export function createTree(config) {
|
|
|
73
155
|
const isExpanded = (id) => expanded.has(id);
|
|
74
156
|
const isSelected = (id) => id === selected();
|
|
75
157
|
function toggleExpand(node) {
|
|
76
|
-
|
|
158
|
+
// Lazy nodes have no children *yet* - they must still be expandable so the
|
|
159
|
+
// toggle fires onToggle and the host can fetch children on demand.
|
|
160
|
+
if (!node.children?.length && !node.lazy)
|
|
77
161
|
return;
|
|
78
162
|
const next = new Set(expanded);
|
|
79
163
|
const willOpen = !next.has(node.id);
|
|
@@ -104,6 +188,24 @@ export function createTree(config) {
|
|
|
104
188
|
const item = rows[active];
|
|
105
189
|
if (!item)
|
|
106
190
|
return;
|
|
191
|
+
// "expand" opens the node or steps into the first child; "collapse" closes it
|
|
192
|
+
// or jumps to the parent. Under RTL the physical Left/Right keys are swapped.
|
|
193
|
+
const expand = () => {
|
|
194
|
+
if (item.hasChildren && !item.open)
|
|
195
|
+
toggleExpand(item.node);
|
|
196
|
+
else if (item.hasChildren && item.open)
|
|
197
|
+
setActive(Math.min(active + 1, rows.length - 1), true);
|
|
198
|
+
};
|
|
199
|
+
const collapse = () => {
|
|
200
|
+
if (item.hasChildren && item.open)
|
|
201
|
+
toggleExpand(item.node);
|
|
202
|
+
else if (item.parentId) {
|
|
203
|
+
const pi = rows.findIndex((r) => r.node.id === item.parentId);
|
|
204
|
+
if (pi >= 0)
|
|
205
|
+
setActive(pi, true);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const rtl = config.dir?.() === 'rtl';
|
|
107
209
|
switch (e.key) {
|
|
108
210
|
case 'ArrowDown':
|
|
109
211
|
e.preventDefault();
|
|
@@ -115,20 +217,11 @@ export function createTree(config) {
|
|
|
115
217
|
break;
|
|
116
218
|
case 'ArrowRight':
|
|
117
219
|
e.preventDefault();
|
|
118
|
-
|
|
119
|
-
toggleExpand(item.node);
|
|
120
|
-
else if (item.hasChildren && item.open)
|
|
121
|
-
setActive(Math.min(active + 1, rows.length - 1), true);
|
|
220
|
+
(rtl ? collapse : expand)();
|
|
122
221
|
break;
|
|
123
222
|
case 'ArrowLeft':
|
|
124
223
|
e.preventDefault();
|
|
125
|
-
|
|
126
|
-
toggleExpand(item.node);
|
|
127
|
-
else if (item.parentId) {
|
|
128
|
-
const pi = rows.findIndex((r) => r.node.id === item.parentId);
|
|
129
|
-
if (pi >= 0)
|
|
130
|
-
setActive(pi, true);
|
|
131
|
-
}
|
|
224
|
+
(rtl ? expand : collapse)();
|
|
132
225
|
break;
|
|
133
226
|
case 'Enter':
|
|
134
227
|
e.preventDefault();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* debounce - coalesce rapid calls into one trailing call after `ms` of quiet.
|
|
3
|
+
* Used for remote search in the async dropdowns (SvMultiSelect/GridSelect/...),
|
|
4
|
+
* but general-purpose. Returns the debounced function with `cancel()` (drop the
|
|
5
|
+
* pending call) and `flush()` (run it now). Framework-free + pure.
|
|
6
|
+
*/
|
|
7
|
+
export type Debounced<A extends unknown[]> = ((...args: A) => void) & {
|
|
8
|
+
cancel: () => void;
|
|
9
|
+
flush: () => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function debounce<A extends unknown[]>(fn: (...args: A) => void, ms: number): Debounced<A>;
|
package/dist/debounce.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function debounce(fn, ms) {
|
|
2
|
+
let timer = null;
|
|
3
|
+
let pending = null;
|
|
4
|
+
const run = () => {
|
|
5
|
+
timer = null;
|
|
6
|
+
const args = pending;
|
|
7
|
+
pending = null;
|
|
8
|
+
if (args)
|
|
9
|
+
fn(...args);
|
|
10
|
+
};
|
|
11
|
+
const debounced = ((...args) => {
|
|
12
|
+
pending = args;
|
|
13
|
+
if (timer)
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
timer = setTimeout(run, ms);
|
|
16
|
+
});
|
|
17
|
+
debounced.cancel = () => {
|
|
18
|
+
if (timer)
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
timer = null;
|
|
21
|
+
pending = null;
|
|
22
|
+
};
|
|
23
|
+
debounced.flush = () => {
|
|
24
|
+
if (timer) {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
run();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return debounced;
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* duration - pure parse/format helpers behind <SvDurationInput>. The canonical
|
|
3
|
+
* value is a number of MINUTES; these convert to/from the human forms people
|
|
4
|
+
* type ("1h 30m", "1:30", "90"). Framework-free + pure so they are unit-tested
|
|
5
|
+
* directly.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse a human duration to minutes, or `null` if unparseable. Accepts:
|
|
9
|
+
* - `h:mm` e.g. "1:30" -> 90
|
|
10
|
+
* - unit form e.g. "1h 30m", "1.5h", "45m" -> 90 / 90 / 45
|
|
11
|
+
* - a bare number e.g. "90" -> 90 (treated as minutes)
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseDuration(input: string): number | null;
|
|
14
|
+
/** Format minutes as `h:mm` (colon) or `Nh Nm` (units). Empty for invalid input. */
|
|
15
|
+
export declare function formatDuration(minutes: number, style?: 'colon' | 'units'): string;
|
package/dist/duration.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* duration - pure parse/format helpers behind <SvDurationInput>. The canonical
|
|
3
|
+
* value is a number of MINUTES; these convert to/from the human forms people
|
|
4
|
+
* type ("1h 30m", "1:30", "90"). Framework-free + pure so they are unit-tested
|
|
5
|
+
* directly.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse a human duration to minutes, or `null` if unparseable. Accepts:
|
|
9
|
+
* - `h:mm` e.g. "1:30" -> 90
|
|
10
|
+
* - unit form e.g. "1h 30m", "1.5h", "45m" -> 90 / 90 / 45
|
|
11
|
+
* - a bare number e.g. "90" -> 90 (treated as minutes)
|
|
12
|
+
*/
|
|
13
|
+
export function parseDuration(input) {
|
|
14
|
+
const s = input.trim().toLowerCase();
|
|
15
|
+
if (!s)
|
|
16
|
+
return null;
|
|
17
|
+
const colon = s.match(/^(\d+):(\d{1,2})$/);
|
|
18
|
+
if (colon) {
|
|
19
|
+
const mm = parseInt(colon[2], 10);
|
|
20
|
+
if (mm > 59)
|
|
21
|
+
return null;
|
|
22
|
+
return parseInt(colon[1], 10) * 60 + mm;
|
|
23
|
+
}
|
|
24
|
+
const units = s.match(/\d+(?:\.\d+)?\s*[hm]/g);
|
|
25
|
+
if (units && units.join('').replace(/\s/g, '').length === s.replace(/\s/g, '').length) {
|
|
26
|
+
let total = 0;
|
|
27
|
+
for (const part of units) {
|
|
28
|
+
const m = part.match(/(\d+(?:\.\d+)?)\s*([hm])/);
|
|
29
|
+
const n = parseFloat(m[1]);
|
|
30
|
+
total += m[2] === 'h' ? n * 60 : n;
|
|
31
|
+
}
|
|
32
|
+
return Math.round(total);
|
|
33
|
+
}
|
|
34
|
+
const num = Number(s);
|
|
35
|
+
return Number.isFinite(num) ? Math.round(num) : null;
|
|
36
|
+
}
|
|
37
|
+
/** Format minutes as `h:mm` (colon) or `Nh Nm` (units). Empty for invalid input. */
|
|
38
|
+
export function formatDuration(minutes, style = 'colon') {
|
|
39
|
+
if (!Number.isFinite(minutes) || minutes < 0)
|
|
40
|
+
return '';
|
|
41
|
+
const h = Math.floor(minutes / 60);
|
|
42
|
+
const m = Math.round(minutes % 60);
|
|
43
|
+
if (style === 'units') {
|
|
44
|
+
if (h && m)
|
|
45
|
+
return `${h}h ${m}m`;
|
|
46
|
+
if (h)
|
|
47
|
+
return `${h}h`;
|
|
48
|
+
return `${m}m`;
|
|
49
|
+
}
|
|
50
|
+
return `${h}:${String(m).padStart(2, '0')}`;
|
|
51
|
+
}
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
* The shared "editor contract" for the SvGrid UI kit.
|
|
3
3
|
*
|
|
4
4
|
* Every value-bearing editor (inputs, selects, pickers) accepts this common set
|
|
5
|
-
* of props so forms, validation
|
|
6
|
-
* kit - the same spirit as the grid's headless
|
|
7
|
-
* Components spread their own `value` / options /
|
|
5
|
+
* of props so forms, validation, accessibility, direction (RTL) and localization
|
|
6
|
+
* behave IDENTICALLY across the kit - the same spirit as the grid's headless
|
|
7
|
+
* core, applied to the editors. Components spread their own `value` / options /
|
|
8
|
+
* behavior on top of this base.
|
|
8
9
|
*
|
|
9
10
|
* Framework-free (no Svelte, no DOM) so it can be unit-tested and reused.
|
|
10
11
|
*/
|
|
11
12
|
export type EditorSize = 'sm' | 'md' | 'lg';
|
|
13
|
+
/** Text direction. `'auto'` defers to the surrounding document/CSS. */
|
|
14
|
+
export type EditorDir = 'ltr' | 'rtl' | 'auto';
|
|
12
15
|
/** Props shared by every value-bearing editor in the kit. */
|
|
13
16
|
export type SvEditorProps = {
|
|
14
17
|
/** Disable interaction + form submission. */
|
|
@@ -21,23 +24,62 @@ export type SvEditorProps = {
|
|
|
21
24
|
invalid?: boolean;
|
|
22
25
|
/** Error message; when set it is announced via `aria-describedby` and shown. */
|
|
23
26
|
error?: string;
|
|
27
|
+
/** Visible field label, rendered above the control and wired via `for`/`id`. */
|
|
28
|
+
label?: string;
|
|
29
|
+
/** Helper text shown under the control and announced via `aria-describedby`. */
|
|
30
|
+
hint?: string;
|
|
24
31
|
/** Control size / density. */
|
|
25
32
|
size?: EditorSize;
|
|
33
|
+
/**
|
|
34
|
+
* Text direction. `'rtl'` mirrors layout for Arabic/Hebrew/etc.; `'auto'` (the
|
|
35
|
+
* default) inherits from the surrounding document so a page-level `dir` wins.
|
|
36
|
+
*/
|
|
37
|
+
dir?: EditorDir;
|
|
26
38
|
/** Form field name; the editor emits a hidden input carrying its value. */
|
|
27
39
|
name?: string;
|
|
28
|
-
/** Root/control element id (the error
|
|
40
|
+
/** Root/control element id (the label/hint/error ids derive from it). */
|
|
29
41
|
id?: string;
|
|
30
|
-
/** Accessible name when there is no visible
|
|
42
|
+
/** Accessible name when there is no visible `label`. */
|
|
31
43
|
ariaLabel?: string;
|
|
32
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* The interaction surface an editor exposes when embedded as a grid cell editor.
|
|
47
|
+
* Standalone usage ignores these; the grid supplies them so every editor shares
|
|
48
|
+
* one commit / cancel / move contract (Enter commits, Escape cancels, Tab moves)
|
|
49
|
+
* instead of each cell-editor re-implementing key handling. Consumed in Phase 2
|
|
50
|
+
* (the editor registry).
|
|
51
|
+
*/
|
|
52
|
+
export type EditorInteraction<V = unknown> = {
|
|
53
|
+
/** Commit the current value and stop editing (Enter, option pick, blur-commit). */
|
|
54
|
+
onCommit?: (value: V) => void;
|
|
55
|
+
/** Abandon the edit and restore the previous value (Escape). */
|
|
56
|
+
onCancel?: () => void;
|
|
57
|
+
/** Commit, then move to the adjacent cell (Tab = 1, Shift+Tab = -1). */
|
|
58
|
+
onCommitAndMove?: (value: V, direction: 1 | -1) => void;
|
|
59
|
+
/** Ask the surrounding popover/panel to close without committing. */
|
|
60
|
+
onRequestClose?: () => void;
|
|
61
|
+
/** True while mounted inside a grid cell (lets an editor tune autofocus etc.). */
|
|
62
|
+
inCell?: boolean;
|
|
63
|
+
};
|
|
33
64
|
/** Stable DOM id for an editor's error text, for `aria-describedby` wiring. */
|
|
34
65
|
export declare const editorErrorId: (id?: string) => string | undefined;
|
|
66
|
+
/** Stable DOM id for an editor's hint text, for `aria-describedby` wiring. */
|
|
67
|
+
export declare const editorHintId: (id?: string) => string | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Generate a stable-per-instance element id. Call ONCE per component instance
|
|
70
|
+
* (e.g. `const uid = provided ?? nextEditorId()`) so label/hint/error wiring
|
|
71
|
+
* always has an anchor even when the consumer passes no `id`.
|
|
72
|
+
*/
|
|
73
|
+
export declare const nextEditorId: (prefix?: string) => string;
|
|
35
74
|
/** A subset of {@link SvEditorProps} that drives ARIA on the focusable control. */
|
|
36
|
-
export type EditorAriaState = Pick<SvEditorProps, 'id' | 'invalid' | 'required' | 'error' | 'ariaLabel'>;
|
|
75
|
+
export type EditorAriaState = Pick<SvEditorProps, 'id' | 'invalid' | 'required' | 'error' | 'hint' | 'ariaLabel'>;
|
|
37
76
|
/**
|
|
38
77
|
* ARIA attributes for the focusable control of an editor, derived from its state.
|
|
39
78
|
* Spread onto the input/button/combobox:
|
|
40
|
-
* `<input {...editorAria({ id, invalid, required, error, ariaLabel })} />`
|
|
79
|
+
* `<input {...editorAria({ id, invalid, required, error, hint, ariaLabel })} />`
|
|
80
|
+
*
|
|
81
|
+
* `aria-describedby` points at the error text (when invalid) and/or the hint
|
|
82
|
+
* text - both ids derive from `id`, so pass a stable `id` (see {@link nextEditorId}).
|
|
41
83
|
* Undefined values are omitted by Svelte, so unset props add no attributes.
|
|
42
84
|
*/
|
|
43
85
|
export declare function editorAria(state: EditorAriaState): {
|
|
@@ -46,3 +88,16 @@ export declare function editorAria(state: EditorAriaState): {
|
|
|
46
88
|
'aria-describedby': string | undefined;
|
|
47
89
|
'aria-label': string | undefined;
|
|
48
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Merge a component's default message strings with a consumer's `messages`
|
|
93
|
+
* override, so every editor can be fully localized from the outside.
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* const M = { clear: 'Clear', noResults: 'No results' }
|
|
97
|
+
* const messages = resolveMessages(M, props.messages) // Partial override
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* Undefined/empty overrides fall back to the default, so a partial `messages`
|
|
101
|
+
* object only replaces the keys it sets.
|
|
102
|
+
*/
|
|
103
|
+
export declare function resolveMessages<T extends Record<string, string>>(defaults: T, overrides?: Partial<T> | null): T;
|
package/dist/editor-contract.js
CHANGED
|
@@ -2,25 +2,65 @@
|
|
|
2
2
|
* The shared "editor contract" for the SvGrid UI kit.
|
|
3
3
|
*
|
|
4
4
|
* Every value-bearing editor (inputs, selects, pickers) accepts this common set
|
|
5
|
-
* of props so forms, validation
|
|
6
|
-
* kit - the same spirit as the grid's headless
|
|
7
|
-
* Components spread their own `value` / options /
|
|
5
|
+
* of props so forms, validation, accessibility, direction (RTL) and localization
|
|
6
|
+
* behave IDENTICALLY across the kit - the same spirit as the grid's headless
|
|
7
|
+
* core, applied to the editors. Components spread their own `value` / options /
|
|
8
|
+
* behavior on top of this base.
|
|
8
9
|
*
|
|
9
10
|
* Framework-free (no Svelte, no DOM) so it can be unit-tested and reused.
|
|
10
11
|
*/
|
|
11
12
|
/** Stable DOM id for an editor's error text, for `aria-describedby` wiring. */
|
|
12
13
|
export const editorErrorId = (id) => (id ? `${id}__error` : undefined);
|
|
14
|
+
/** Stable DOM id for an editor's hint text, for `aria-describedby` wiring. */
|
|
15
|
+
export const editorHintId = (id) => (id ? `${id}__hint` : undefined);
|
|
16
|
+
let _autoId = 0;
|
|
17
|
+
/**
|
|
18
|
+
* Generate a stable-per-instance element id. Call ONCE per component instance
|
|
19
|
+
* (e.g. `const uid = provided ?? nextEditorId()`) so label/hint/error wiring
|
|
20
|
+
* always has an anchor even when the consumer passes no `id`.
|
|
21
|
+
*/
|
|
22
|
+
export const nextEditorId = (prefix = 'sv-ed') => `${prefix}-${_autoId++}`;
|
|
13
23
|
/**
|
|
14
24
|
* ARIA attributes for the focusable control of an editor, derived from its state.
|
|
15
25
|
* Spread onto the input/button/combobox:
|
|
16
|
-
* `<input {...editorAria({ id, invalid, required, error, ariaLabel })} />`
|
|
26
|
+
* `<input {...editorAria({ id, invalid, required, error, hint, ariaLabel })} />`
|
|
27
|
+
*
|
|
28
|
+
* `aria-describedby` points at the error text (when invalid) and/or the hint
|
|
29
|
+
* text - both ids derive from `id`, so pass a stable `id` (see {@link nextEditorId}).
|
|
17
30
|
* Undefined values are omitted by Svelte, so unset props add no attributes.
|
|
18
31
|
*/
|
|
19
32
|
export function editorAria(state) {
|
|
33
|
+
const described = [
|
|
34
|
+
state.error ? editorErrorId(state.id) : undefined,
|
|
35
|
+
state.hint ? editorHintId(state.id) : undefined,
|
|
36
|
+
].filter(Boolean);
|
|
20
37
|
return {
|
|
21
38
|
'aria-invalid': state.invalid ? 'true' : undefined,
|
|
22
39
|
'aria-required': state.required ? 'true' : undefined,
|
|
23
|
-
'aria-describedby':
|
|
40
|
+
'aria-describedby': described.length ? described.join(' ') : undefined,
|
|
24
41
|
'aria-label': state.ariaLabel,
|
|
25
42
|
};
|
|
26
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Merge a component's default message strings with a consumer's `messages`
|
|
46
|
+
* override, so every editor can be fully localized from the outside.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const M = { clear: 'Clear', noResults: 'No results' }
|
|
50
|
+
* const messages = resolveMessages(M, props.messages) // Partial override
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Undefined/empty overrides fall back to the default, so a partial `messages`
|
|
54
|
+
* object only replaces the keys it sets.
|
|
55
|
+
*/
|
|
56
|
+
export function resolveMessages(defaults, overrides) {
|
|
57
|
+
if (!overrides)
|
|
58
|
+
return defaults;
|
|
59
|
+
const out = { ...defaults };
|
|
60
|
+
for (const k in overrides) {
|
|
61
|
+
const v = overrides[k];
|
|
62
|
+
if (v != null && v !== '')
|
|
63
|
+
out[k] = v;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|