@svgrid/grid 1.2.34 → 2.0.1
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/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 +33 -0
- package/dist/SvGrid.svelte +116 -3
- 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 +2 -1
- 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 +16021 -8234
- package/dist/cdn/svgrid.svelte-external.js +14542 -6764
- 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 +1 -1
- 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 +68 -9
- package/src/SvGrid.css +33 -0
- package/src/SvGrid.svelte +116 -3
- package/src/SvGrid.types.ts +374 -0
- package/src/SvGridBoard.svelte +2281 -0
- package/src/SvGridDropdown.svelte +2 -1
- 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.ts +3 -1
- package/src/avatar.test.ts +32 -0
- package/src/avatar.ts +21 -0
- package/src/board.test.ts +927 -0
- 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/columns.ts +3 -2
- package/src/core.ts +10 -0
- 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/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/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/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 +150 -5
- package/src/keyboard-handlers.ts +20 -0
- package/src/list-option.ts +74 -0
- package/src/list-power.test.ts +71 -0
- 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/server-data-source.ts +102 -0
- package/src/server-group-model.test.ts +294 -0
- package/src/server-group-model.ts +370 -0
- package/src/svgrid.responsive.test.ts +72 -0
- 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/index.ts
CHANGED
|
@@ -69,12 +69,60 @@ export { createGrid, createSvGrid, type SvelteGrid } from './createGrid.svelte'
|
|
|
69
69
|
export { createGridState, createSvGridState } from './createGridState.svelte'
|
|
70
70
|
export { subscribeGrid, subscribeSvGrid } from './subscribe'
|
|
71
71
|
export { default as SvGrid } from './SvGrid.svelte'
|
|
72
|
+
export { default as SvGridBoard } from './SvGridBoard.svelte'
|
|
73
|
+
export type { BoardConfig, BoardLane, BoardCardMoveEvent, BoardCardCommitEvent, BoardLayout, BoardSubtask, BoardComment, BoardMenuContext, BoardLaneMenuContext, BoardDrawerConfig } from './SvGrid.types'
|
|
72
74
|
export { default as SvGridDropdown } from './SvGridDropdown.svelte'
|
|
73
75
|
export { default as SvCalendar, type CalendarValue, type CalendarPreset, type CalendarAnimation } from './SvCalendar.svelte'
|
|
76
|
+
export { matchesRecurrence, expandRecurrence, type RecurrenceRule, type RecurrenceFreq } from './recurrence'
|
|
74
77
|
export { default as SvTimePicker, type TimeValue } from './SvTimePicker.svelte'
|
|
75
78
|
export { default as SvDateTimePicker, type DateTimeValue } from './SvDateTimePicker.svelte'
|
|
79
|
+
export { default as SvDateRangeInput, type DateRangeValue } from './SvDateRangeInput.svelte'
|
|
80
|
+
export { default as SvButtonGroup, type ButtonGroupItem, type ButtonGroupMode, type ButtonGroupValue } from './SvButtonGroup.svelte'
|
|
76
81
|
// UI kit - shared editor contract (common props + a11y wiring for every editor)
|
|
77
|
-
export {
|
|
82
|
+
export {
|
|
83
|
+
editorAria,
|
|
84
|
+
editorErrorId,
|
|
85
|
+
editorHintId,
|
|
86
|
+
nextEditorId,
|
|
87
|
+
resolveMessages,
|
|
88
|
+
type SvEditorProps,
|
|
89
|
+
type EditorSize,
|
|
90
|
+
type EditorDir,
|
|
91
|
+
type EditorAriaState,
|
|
92
|
+
} from './editor-contract'
|
|
93
|
+
// UI kit - editor interaction surface (commit/cancel/close for grid-cell use)
|
|
94
|
+
export { type EditorInteraction } from './editor-contract'
|
|
95
|
+
// UI kit - shared a11y primitives (focus trap, scroll lock, dismissable layers, live region)
|
|
96
|
+
export {
|
|
97
|
+
FOCUSABLE_SELECTOR,
|
|
98
|
+
getFocusable,
|
|
99
|
+
createFocusTrap,
|
|
100
|
+
type FocusTrap,
|
|
101
|
+
type FocusTrapOptions,
|
|
102
|
+
} from './a11y/focus-trap'
|
|
103
|
+
export { lockScroll, scrollLockDepth } from './a11y/scroll-lock'
|
|
104
|
+
export {
|
|
105
|
+
createDismissableLayer,
|
|
106
|
+
dismissableDepth,
|
|
107
|
+
type DismissableLayer,
|
|
108
|
+
type DismissableOptions,
|
|
109
|
+
} from './a11y/dismissable'
|
|
110
|
+
export { announce, type AnnounceOptions } from './a11y/live-region'
|
|
111
|
+
// UI kit - custom cell-editor registry (register any component as a grid editor)
|
|
112
|
+
export {
|
|
113
|
+
registerCellEditor,
|
|
114
|
+
getCellEditor,
|
|
115
|
+
hasCellEditor,
|
|
116
|
+
unregisterCellEditor,
|
|
117
|
+
registeredCellEditorTypes,
|
|
118
|
+
defaultEditorProps,
|
|
119
|
+
resolveEditorProps,
|
|
120
|
+
type CellEditorContext,
|
|
121
|
+
type CellEditorRegistration,
|
|
122
|
+
} from './editor-registry'
|
|
123
|
+
export { registerBuiltinEditors } from './builtin-editors'
|
|
124
|
+
// UI kit - shared field chrome (label + hint + error + direction wrapper)
|
|
125
|
+
export { default as SvField } from './SvField.svelte'
|
|
78
126
|
// UI kit - headless cores (state + prop-getters you render yourself, like createSvGrid)
|
|
79
127
|
export {
|
|
80
128
|
createListbox,
|
|
@@ -94,7 +142,7 @@ export { createCountryInput, type CountryInput, type CountryInputConfig } from '
|
|
|
94
142
|
// Text-input family cores
|
|
95
143
|
export { createNumberInput, type NumberInputConfig, type NumberInputCore } from './createNumberInput.svelte'
|
|
96
144
|
export { createMaskedInput, type MaskedInputConfig, type MaskedInputCore } from './createMaskedInput.svelte'
|
|
97
|
-
export { createPhoneInput, type PhoneInputConfig, type PhoneInputCore } from './createPhoneInput.svelte'
|
|
145
|
+
export { createPhoneInput, type PhoneInputConfig, type PhoneInputCore, type PhoneParts } from './createPhoneInput.svelte'
|
|
98
146
|
export { createColorInput, normalizeHex, type ColorInputConfig, type ColorInputCore } from './createColorInput.svelte'
|
|
99
147
|
export { createPasswordInput, passwordStrength, STRENGTH_LABELS, type PasswordInputConfig, type PasswordInputCore } from './createPasswordInput.svelte'
|
|
100
148
|
// Buttons & toggles family cores
|
|
@@ -109,9 +157,10 @@ export { createTimePicker, parseTimeValue, type TimePicker, type TimePickerConfi
|
|
|
109
157
|
export { createDateTimePicker, type DateTimePicker, type DateTimePickerConfig, type DropDownDisplayMode, type DateTimeTab } from './createDateTimePicker.svelte'
|
|
110
158
|
// Layout & range family cores
|
|
111
159
|
export { createTabs, type Tabs, type TabsConfig, type TabsOrientation, type TabsActivation } from './createTabs.svelte'
|
|
112
|
-
export { createTree, treeDescendantIds, treeCheckState, type Tree, type TreeConfig, type TreeRow, type CheckState } from './createTree.svelte'
|
|
160
|
+
export { createTree, treeDescendantIds, treeCheckState, moveTreeNode, sortTreeNodes, treeContains, type Tree, type TreeConfig, type TreeRow, type CheckState, type TreeDropPosition } from './createTree.svelte'
|
|
113
161
|
export { createSlider, type Slider, type SliderConfig, type SliderValue, type SliderOrientation, type SliderThumb, type SliderPoint } from './createSlider.svelte'
|
|
114
162
|
export { createGauge, type Gauge, type GaugeConfig, type GaugePoint } from './createGauge.svelte'
|
|
163
|
+
export { createButtonGroup, toGroupArray, type ButtonGroup, type ButtonGroupConfig } from './createButtonGroup.svelte'
|
|
115
164
|
// UI kit - Group B (buttons & toggles)
|
|
116
165
|
export { default as SvButton } from './SvButton.svelte'
|
|
117
166
|
export { default as SvRepeatButton } from './SvRepeatButton.svelte'
|
|
@@ -121,28 +170,106 @@ export { default as SvCheckBox } from './SvCheckBox.svelte'
|
|
|
121
170
|
export { default as SvRadioGroup, type RadioOption } from './SvRadioGroup.svelte'
|
|
122
171
|
export { default as SvRating } from './SvRating.svelte'
|
|
123
172
|
// UI kit - Group C (text inputs)
|
|
173
|
+
export { default as SvTextInput } from './SvTextInput.svelte'
|
|
174
|
+
export { default as SvTextArea } from './SvTextArea.svelte'
|
|
175
|
+
export { default as SvOtpInput } from './SvOtpInput.svelte'
|
|
176
|
+
export { sanitizeOtp, otpCells, isOtpComplete } from './otp'
|
|
177
|
+
export { default as SvDurationInput } from './SvDurationInput.svelte'
|
|
178
|
+
export { parseDuration, formatDuration } from './duration'
|
|
124
179
|
export { default as SvNumberInput } from './SvNumberInput.svelte'
|
|
125
180
|
export { default as SvPasswordInput } from './SvPasswordInput.svelte'
|
|
126
181
|
export { default as SvMaskedInput } from './SvMaskedInput.svelte'
|
|
127
182
|
export { default as SvPhoneInput } from './SvPhoneInput.svelte'
|
|
128
183
|
export { default as SvColorInput } from './SvColorInput.svelte'
|
|
129
184
|
export { applyMask, unmask, isMaskComplete } from './datetime/mask'
|
|
130
|
-
export { COUNTRIES, COUNTRY_BY_CODE, flagEmoji, type Country } from './countries'
|
|
185
|
+
export { COUNTRIES, COUNTRY_BY_CODE, flagEmoji, phoneDigitsValid, PHONE_NATIONAL_LENGTHS, type Country } from './countries'
|
|
131
186
|
// UI kit - Group D (selection overlays)
|
|
132
187
|
export { default as SvListBox } from './SvListBox.svelte'
|
|
133
188
|
export { default as SvDropDownList } from './SvDropDownList.svelte'
|
|
134
189
|
export { default as SvComboBox } from './SvComboBox.svelte'
|
|
135
190
|
export { default as SvAutoComplete } from './SvAutoComplete.svelte'
|
|
136
191
|
export { default as SvTagsInput } from './SvTagsInput.svelte'
|
|
192
|
+
export { default as SvMultiSelect, type MultiSelectOption } from './SvMultiSelect.svelte'
|
|
193
|
+
export { debounce, type Debounced } from './debounce'
|
|
194
|
+
export { default as SvTreeSelect, type TreeSelectNode } from './SvTreeSelect.svelte'
|
|
195
|
+
export { flattenVisible, findNodePath, branchValues, type FlatRow } from './tree-select'
|
|
196
|
+
export { default as SvGridSelect, type GridSelectColumn } from './SvGridSelect.svelte'
|
|
197
|
+
export { filterGridRows } from './grid-select'
|
|
137
198
|
export { default as SvCountryInput } from './SvCountryInput.svelte'
|
|
138
|
-
export {
|
|
199
|
+
export {
|
|
200
|
+
filterOptions,
|
|
201
|
+
groupOptions,
|
|
202
|
+
hasGroups,
|
|
203
|
+
nextTypeaheadIndex,
|
|
204
|
+
createTypeaheadBuffer,
|
|
205
|
+
isTypeaheadKey,
|
|
206
|
+
type ListOption,
|
|
207
|
+
type IndexedOption,
|
|
208
|
+
type OptionGroup,
|
|
209
|
+
} from './list-option'
|
|
210
|
+
export { virtualRange, scrollToIndex, type VirtualRange, type VirtualParams } from './virtual'
|
|
139
211
|
// UI kit - Group E (range & feedback)
|
|
140
212
|
export { default as SvSlider } from './SvSlider.svelte'
|
|
141
213
|
export { default as SvGauge } from './SvGauge.svelte'
|
|
142
214
|
// UI kit - Group F (composite / layout)
|
|
143
215
|
export { default as SvTabs, type TabItem } from './SvTabs.svelte'
|
|
144
216
|
export { default as SvTree, type TreeNode as SvTreeNode } from './SvTree.svelte'
|
|
217
|
+
export { default as SvAccordion, type AccordionItem, type AccordionExpandMode } from './SvAccordion.svelte'
|
|
218
|
+
export { createAccordion, type Accordion, type AccordionConfig } from './createAccordion.svelte'
|
|
219
|
+
export { default as SvSplitter, type SplitterOrientation } from './SvSplitter.svelte'
|
|
220
|
+
export { createSplitter, type Splitter, type SplitterConfig, type SplitterPoint } from './createSplitter.svelte'
|
|
221
|
+
export { default as SvFileUpload, type FileRejection, type FileRejectReason } from './SvFileUpload.svelte'
|
|
222
|
+
export { createFileUpload, fileMatchesAccept, type FileUpload, type FileUploadConfig } from './createFileUpload.svelte'
|
|
223
|
+
// UI kit - overlays (popover / tooltip / modal), sharing the popover.ts positioning + animation
|
|
224
|
+
export { default as SvPopover } from './SvPopover.svelte'
|
|
225
|
+
export { default as SvTooltip } from './SvTooltip.svelte'
|
|
226
|
+
export { default as SvModal } from './SvModal.svelte'
|
|
227
|
+
export { default as SvDrawer } from './SvDrawer.svelte'
|
|
228
|
+
export type DrawerSide = 'right' | 'left' | 'top' | 'bottom'
|
|
229
|
+
export { default as SvToaster } from './SvToaster.svelte'
|
|
230
|
+
export { default as SvBadge } from './SvBadge.svelte'
|
|
231
|
+
export { default as SvSkeleton } from './SvSkeleton.svelte'
|
|
232
|
+
export { default as SvCard } from './SvCard.svelte'
|
|
233
|
+
export { default as SvAlert } from './SvAlert.svelte'
|
|
234
|
+
export { default as SvEmptyState } from './SvEmptyState.svelte'
|
|
235
|
+
export { default as SvDivider } from './SvDivider.svelte'
|
|
236
|
+
export { default as SvChip } from './SvChip.svelte'
|
|
237
|
+
export { default as SvStat } from './SvStat.svelte'
|
|
238
|
+
export { default as SvTimeline, type TimelineItem } from './SvTimeline.svelte'
|
|
239
|
+
export { default as SvSparkline } from './SvSparkline.svelte'
|
|
240
|
+
export { default as SvScrollArea } from './SvScrollArea.svelte'
|
|
241
|
+
export { default as SvAvatarGroup } from './SvAvatarGroup.svelte'
|
|
242
|
+
export { default as SvCommand, type CommandItem } from './SvCommand.svelte'
|
|
243
|
+
export { fuzzyScore, fuzzyMatch } from './fuzzy'
|
|
244
|
+
export { default as SvRichText, type RichTextTool } from './SvRichText.svelte'
|
|
245
|
+
export { default as SvCarousel } from './SvCarousel.svelte'
|
|
246
|
+
export { default as SvTour, type TourStep } from './SvTour.svelte'
|
|
247
|
+
export { default as SvPagination } from './SvPagination.svelte'
|
|
248
|
+
export { paginationRange, type PaginationItem, type PaginationRangeOptions } from './paginate'
|
|
249
|
+
export { default as SvBreadcrumb, type BreadcrumbItem } from './SvBreadcrumb.svelte'
|
|
250
|
+
export { default as SvStepper, type StepItem } from './SvStepper.svelte'
|
|
251
|
+
export { default as SvAvatar } from './SvAvatar.svelte'
|
|
252
|
+
export { avatarInitials, avatarColorHue } from './avatar'
|
|
253
|
+
export { default as SvContextMenu } from './SvContextMenu.svelte'
|
|
254
|
+
export {
|
|
255
|
+
toast,
|
|
256
|
+
dismissToast,
|
|
257
|
+
clearToasts,
|
|
258
|
+
pauseToast,
|
|
259
|
+
resumeToast,
|
|
260
|
+
toastStore,
|
|
261
|
+
type Toast,
|
|
262
|
+
type ToastVariant,
|
|
263
|
+
type ToastOptions,
|
|
264
|
+
type ToastFn,
|
|
265
|
+
} from './toast-store.svelte'
|
|
266
|
+
export { default as SvMenu, type MenuItem } from './SvMenu.svelte'
|
|
267
|
+
export { default as SvNavPane, type NavItem, type NavSection, type NavModule } from './SvNavPane.svelte'
|
|
268
|
+
export { default as SvProgress } from './SvProgress.svelte'
|
|
269
|
+
export { default as SvCircularProgress } from './SvCircularProgress.svelte'
|
|
270
|
+
export { popIn } from './popover'
|
|
145
271
|
export { default as SvForm, type FormField, type FormFieldType } from './SvForm.svelte'
|
|
272
|
+
export { rules, runRules, isEmptyValue, type Validator, type RuleOptions, type CompareOp } from './validators'
|
|
146
273
|
export { anchoredRect, portalToBody, PANEL_THEME_VARS, type AnchoredRect } from './popover'
|
|
147
274
|
export {
|
|
148
275
|
toDate, startOfDay, addDays, addMonths, addYears, clampDate, isSameDay,
|
|
@@ -258,7 +385,25 @@ export {
|
|
|
258
385
|
type ServerState,
|
|
259
386
|
type ServerSortModel,
|
|
260
387
|
type ServerFilterModel,
|
|
388
|
+
type ServerAggregation,
|
|
389
|
+
type ServerGroupRow,
|
|
390
|
+
type ServerLeafRow,
|
|
391
|
+
type ServerMoreRow,
|
|
392
|
+
type ServerFooterRow,
|
|
393
|
+
type ServerSkeletonRow,
|
|
394
|
+
type ServerDisplayRow,
|
|
261
395
|
} from './server-data-source'
|
|
396
|
+
export {
|
|
397
|
+
createServerGroupModel,
|
|
398
|
+
serverGroupRows,
|
|
399
|
+
serverGroupNav,
|
|
400
|
+
type ServerGroupController,
|
|
401
|
+
type ServerGroupControllerOptions,
|
|
402
|
+
type ServerGroupState,
|
|
403
|
+
type ServerGroupGridRow,
|
|
404
|
+
} from './server-group-model'
|
|
405
|
+
export { default as SvGroupCell } from './SvGroupCell.svelte'
|
|
406
|
+
export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte'
|
|
262
407
|
export {
|
|
263
408
|
createNamedViews,
|
|
264
409
|
memoryViews,
|
package/src/keyboard-handlers.ts
CHANGED
|
@@ -245,6 +245,26 @@ export function createKeyboard<
|
|
|
245
245
|
}
|
|
246
246
|
event.preventDefault();
|
|
247
247
|
|
|
248
|
+
// Server-side group / tree navigation, built into the grid: ArrowRight
|
|
249
|
+
// expands a collapsed group row, ArrowLeft collapses an expanded one,
|
|
250
|
+
// instead of moving the active cell (treegrid-style). Leaves fall through
|
|
251
|
+
// to normal navigation.
|
|
252
|
+
const serverGroup = ctx.props.serverGroup;
|
|
253
|
+
if (serverGroup && (intent === "moveRight" || intent === "moveLeft")) {
|
|
254
|
+
const data = ctx.allRows[current.rowIndex]?.original;
|
|
255
|
+
if (data !== undefined && serverGroup.isGroup(data)) {
|
|
256
|
+
const expanded = serverGroup.expanded?.(data) ?? false;
|
|
257
|
+
if (intent === "moveRight" && !expanded) {
|
|
258
|
+
serverGroup.onToggle(data);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (intent === "moveLeft" && expanded) {
|
|
262
|
+
serverGroup.onToggle(data);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
248
268
|
if (intent === "clearCells") {
|
|
249
269
|
// Excel `Delete` - clear contents of every cell in the selection
|
|
250
270
|
// (or the active cell if no range). Formatting is left alone, only
|
package/src/list-option.ts
CHANGED
|
@@ -13,3 +13,77 @@ export function filterOptions(options: ReadonlyArray<ListOption>, query: string)
|
|
|
13
13
|
if (!q) return [...options]
|
|
14
14
|
return options.filter((o) => o.label.toLowerCase().includes(q))
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
/** An option carrying its position in the flat source array (for prop-getters). */
|
|
18
|
+
export type IndexedOption = ListOption & { index: number }
|
|
19
|
+
/** A section of options that share a `group` heading (null = ungrouped). */
|
|
20
|
+
export type OptionGroup = { group: string | null; options: IndexedOption[] }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Bucket options by their `group` heading, preserving each option's flat index
|
|
24
|
+
* so grouped rendering still drives `optionProps(index)` etc. Groups appear in
|
|
25
|
+
* first-seen order. When no option sets `group`, the result is a single
|
|
26
|
+
* `group: null` section (render it flat).
|
|
27
|
+
*/
|
|
28
|
+
export function groupOptions(options: ReadonlyArray<ListOption>): OptionGroup[] {
|
|
29
|
+
const groups: OptionGroup[] = []
|
|
30
|
+
const byName = new Map<string | null, OptionGroup>()
|
|
31
|
+
options.forEach((o, index) => {
|
|
32
|
+
const key = o.group ?? null
|
|
33
|
+
let g = byName.get(key)
|
|
34
|
+
if (!g) { g = { group: key, options: [] }; byName.set(key, g); groups.push(g) }
|
|
35
|
+
g.options.push({ ...o, index })
|
|
36
|
+
})
|
|
37
|
+
return groups
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Whether any option declares a `group` (so headings are worth rendering). */
|
|
41
|
+
export function hasGroups(options: ReadonlyArray<ListOption>): boolean {
|
|
42
|
+
return options.some((o) => o.group != null)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Next enabled option index whose label starts with `buffer` (type-ahead),
|
|
47
|
+
* searching forward from just after `from` and wrapping. Returns -1 for no
|
|
48
|
+
* match / empty buffer. Case-insensitive.
|
|
49
|
+
*/
|
|
50
|
+
export function nextTypeaheadIndex(
|
|
51
|
+
options: ReadonlyArray<ListOption>,
|
|
52
|
+
buffer: string,
|
|
53
|
+
from: number,
|
|
54
|
+
): number {
|
|
55
|
+
const b = buffer.trim().toLowerCase()
|
|
56
|
+
if (!b || !options.length) return -1
|
|
57
|
+
const n = options.length
|
|
58
|
+
for (let k = 1; k <= n; k++) {
|
|
59
|
+
const i = (from + k) % n
|
|
60
|
+
const o = options[i]
|
|
61
|
+
if (o && !o.disabled && o.label.toLowerCase().startsWith(b)) return i
|
|
62
|
+
}
|
|
63
|
+
return -1
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A tiny type-ahead buffer: accumulates printable characters within `timeout`
|
|
68
|
+
* ms of each other, then resets. Frameworkfree; the caller supplies scheduling.
|
|
69
|
+
*/
|
|
70
|
+
export function createTypeaheadBuffer(timeout = 600) {
|
|
71
|
+
let buffer = ''
|
|
72
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
73
|
+
return {
|
|
74
|
+
get value() { return buffer },
|
|
75
|
+
/** Append a character and (re)arm the reset timer. Returns the new buffer. */
|
|
76
|
+
push(char: string): string {
|
|
77
|
+
buffer += char
|
|
78
|
+
if (timer) clearTimeout(timer)
|
|
79
|
+
timer = setTimeout(() => { buffer = '' }, timeout)
|
|
80
|
+
return buffer
|
|
81
|
+
},
|
|
82
|
+
clear() { buffer = ''; if (timer) clearTimeout(timer) },
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Whether a keydown is a bare printable character (a type-ahead candidate). */
|
|
87
|
+
export function isTypeaheadKey(e: KeyboardEvent): boolean {
|
|
88
|
+
return e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && e.key !== ' '
|
|
89
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the list "power" features: option grouping (preserving flat index),
|
|
3
|
+
* type-ahead matching, and their wiring into SvListBox (group headings render;
|
|
4
|
+
* typing a prefix moves the active option).
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it } from 'vitest'
|
|
7
|
+
import { mount, unmount, flushSync } from 'svelte'
|
|
8
|
+
import SvListBox from './SvListBox.svelte'
|
|
9
|
+
import { groupOptions, hasGroups, nextTypeaheadIndex } from './list-option'
|
|
10
|
+
|
|
11
|
+
const opts = [
|
|
12
|
+
{ value: 'us', label: 'United States', group: 'Americas' },
|
|
13
|
+
{ value: 'ca', label: 'Canada', group: 'Americas' },
|
|
14
|
+
{ value: 'fr', label: 'France', group: 'Europe' },
|
|
15
|
+
{ value: 'de', label: 'Germany', group: 'Europe' },
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
describe('groupOptions', () => {
|
|
19
|
+
it('buckets by group in first-seen order and preserves the flat index', () => {
|
|
20
|
+
const groups = groupOptions(opts)
|
|
21
|
+
expect(groups.map((g) => g.group)).toEqual(['Americas', 'Europe'])
|
|
22
|
+
expect(groups[0].options.map((o) => o.index)).toEqual([0, 1])
|
|
23
|
+
expect(groups[1].options.map((o) => o.index)).toEqual([2, 3])
|
|
24
|
+
expect(hasGroups(opts)).toBe(true)
|
|
25
|
+
})
|
|
26
|
+
it('returns a single null group when nothing is grouped', () => {
|
|
27
|
+
const flat = [{ value: 'a', label: 'A' }, { value: 'b', label: 'B' }]
|
|
28
|
+
expect(groupOptions(flat)).toEqual([{ group: null, options: [{ value: 'a', label: 'A', index: 0 }, { value: 'b', label: 'B', index: 1 }] }])
|
|
29
|
+
expect(hasGroups(flat)).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('nextTypeaheadIndex', () => {
|
|
34
|
+
it('finds the next enabled label-prefix match, wrapping from `from`', () => {
|
|
35
|
+
expect(nextTypeaheadIndex(opts, 'f', -1)).toBe(2) // France
|
|
36
|
+
expect(nextTypeaheadIndex(opts, 'g', 2)).toBe(3) // Germany
|
|
37
|
+
expect(nextTypeaheadIndex(opts, 'can', 0)).toBe(1) // Canada
|
|
38
|
+
expect(nextTypeaheadIndex(opts, 'z', 0)).toBe(-1) // no match
|
|
39
|
+
expect(nextTypeaheadIndex(opts, '', 0)).toBe(-1)
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
function mountLb(props: Record<string, unknown>) {
|
|
44
|
+
const target = document.createElement('div')
|
|
45
|
+
document.body.appendChild(target)
|
|
46
|
+
const app = mount(SvListBox, { target, props: props as any })
|
|
47
|
+
flushSync()
|
|
48
|
+
return { target, destroy: () => { unmount(app); target.remove() } }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('SvListBox grouping + type-ahead', () => {
|
|
52
|
+
it('renders group headings above their options', () => {
|
|
53
|
+
const { target, destroy } = mountLb({ options: opts, value: 'us' })
|
|
54
|
+
try {
|
|
55
|
+
const headings = [...target.querySelectorAll('.sv-listbox__group')].map((n) => n.textContent)
|
|
56
|
+
expect(headings).toEqual(['Americas', 'Europe'])
|
|
57
|
+
expect(target.querySelectorAll('.sv-listbox__opt')).toHaveLength(4)
|
|
58
|
+
} finally { destroy() }
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('type-ahead moves the active option to a label prefix', () => {
|
|
62
|
+
const { target, destroy } = mountLb({ options: opts, value: 'us' })
|
|
63
|
+
try {
|
|
64
|
+
const list = target.querySelector<HTMLElement>('.sv-listbox')!
|
|
65
|
+
list.dispatchEvent(new KeyboardEvent('keydown', { key: 'g', bubbles: true }))
|
|
66
|
+
flushSync()
|
|
67
|
+
const active = target.querySelector('.sv-listbox__opt.is-active .sv-listbox__label')!
|
|
68
|
+
expect(active.textContent).toBe('Germany')
|
|
69
|
+
} finally { destroy() }
|
|
70
|
+
})
|
|
71
|
+
})
|
package/src/otp.test.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { sanitizeOtp, otpCells, isOtpComplete } from './otp'
|
|
3
|
+
|
|
4
|
+
describe('sanitizeOtp', () => {
|
|
5
|
+
it('keeps only digits in numeric mode and clamps to length', () => {
|
|
6
|
+
expect(sanitizeOtp('12ab34-56', 4, true)).toBe('1234')
|
|
7
|
+
})
|
|
8
|
+
it('keeps non-whitespace in text mode', () => {
|
|
9
|
+
expect(sanitizeOtp('a b\tc d', 3, false)).toBe('abc')
|
|
10
|
+
})
|
|
11
|
+
it('clamps to length', () => {
|
|
12
|
+
expect(sanitizeOtp('123456789', 6, true)).toBe('123456')
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('otpCells', () => {
|
|
17
|
+
it('pads to exactly length cells', () => {
|
|
18
|
+
expect(otpCells('12', 4)).toEqual(['1', '2', '', ''])
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('isOtpComplete', () => {
|
|
23
|
+
it('is true only when every cell is filled', () => {
|
|
24
|
+
expect(isOtpComplete('1234', 4)).toBe(true)
|
|
25
|
+
expect(isOtpComplete('12', 4)).toBe(false)
|
|
26
|
+
expect(isOtpComplete('', 4)).toBe(false)
|
|
27
|
+
})
|
|
28
|
+
})
|
package/src/otp.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* otp - pure helpers behind <SvOtpInput>: sanitize/spread a raw string across N
|
|
3
|
+
* segments (used on paste and per-cell input) and detect completion.
|
|
4
|
+
* Framework-free + pure so they are unit-tested directly.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Keep only the characters allowed for `numeric` mode (digits) or any
|
|
9
|
+
* non-whitespace otherwise, then clamp to `length`. Returns the cleaned string
|
|
10
|
+
* (<= length chars).
|
|
11
|
+
*/
|
|
12
|
+
export function sanitizeOtp(raw: string, length: number, numeric: boolean): string {
|
|
13
|
+
const kept = numeric ? raw.replace(/\D/g, '') : raw.replace(/\s/g, '')
|
|
14
|
+
return kept.slice(0, Math.max(0, length))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Split a value into exactly `length` single-char cells (padding with ''). */
|
|
18
|
+
export function otpCells(value: string, length: number): string[] {
|
|
19
|
+
const cells: string[] = []
|
|
20
|
+
for (let i = 0; i < length; i++) cells[i] = value[i] ?? ''
|
|
21
|
+
return cells
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Whether `value` fills every cell (a joined OTP has no internal gaps, so an
|
|
25
|
+
* exact-length string is complete). */
|
|
26
|
+
export function isOtpComplete(value: string, length: number): boolean {
|
|
27
|
+
return value.length === length
|
|
28
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component tests for the overlay trio: SvModal (dialog semantics, Escape /
|
|
3
|
+
* backdrop close), SvPopover (click open + outside close) and SvTooltip
|
|
4
|
+
* (focus-triggered, aria-describedby wiring).
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it, vi, afterEach } from 'vitest'
|
|
7
|
+
import { mount, unmount, flushSync, createRawSnippet } from 'svelte'
|
|
8
|
+
import SvModal from './SvModal.svelte'
|
|
9
|
+
import SvPopover from './SvPopover.svelte'
|
|
10
|
+
import SvTooltip from './SvTooltip.svelte'
|
|
11
|
+
|
|
12
|
+
const body = createRawSnippet(() => ({ render: () => `<p class="body">hello</p>` }))
|
|
13
|
+
const anchorBtn = createRawSnippet(() => ({ render: () => `<button class="anchor">open</button>` }))
|
|
14
|
+
|
|
15
|
+
function mountC(Comp: any, props: Record<string, unknown>) {
|
|
16
|
+
const target = document.createElement('div')
|
|
17
|
+
document.body.appendChild(target)
|
|
18
|
+
const app = mount(Comp, { target, props: props as any })
|
|
19
|
+
flushSync()
|
|
20
|
+
return { target, destroy: () => { unmount(app); target.remove() } }
|
|
21
|
+
}
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
document.querySelectorAll('.sv-modal__backdrop, .sv-pop__panel, .sv-tip').forEach((n) => n.remove())
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('SvModal', () => {
|
|
27
|
+
it('renders a labelled modal dialog when open', () => {
|
|
28
|
+
const { destroy } = mountC(SvModal, { open: true, title: 'Edit', children: body })
|
|
29
|
+
try {
|
|
30
|
+
const dialog = document.querySelector<HTMLElement>('.sv-modal')!
|
|
31
|
+
expect(dialog.getAttribute('role')).toBe('dialog')
|
|
32
|
+
expect(dialog.getAttribute('aria-modal')).toBe('true')
|
|
33
|
+
const titleId = dialog.getAttribute('aria-labelledby')!
|
|
34
|
+
expect(document.getElementById(titleId)!.textContent).toBe('Edit')
|
|
35
|
+
expect(document.querySelector('.sv-modal .body')!.textContent).toBe('hello')
|
|
36
|
+
} finally { destroy() }
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('Escape and the close button both fire onClose', () => {
|
|
40
|
+
let closed = 0
|
|
41
|
+
const { destroy } = mountC(SvModal, { open: true, title: 'X', onClose: () => (closed++), children: body })
|
|
42
|
+
try {
|
|
43
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
44
|
+
flushSync()
|
|
45
|
+
expect(closed).toBe(1)
|
|
46
|
+
} finally { destroy() }
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('SvPopover', () => {
|
|
51
|
+
it('opens the panel on anchor click and closes on outside pointerdown', () => {
|
|
52
|
+
const { target, destroy } = mountC(SvPopover, { anchor: anchorBtn, children: body })
|
|
53
|
+
try {
|
|
54
|
+
expect(document.querySelector('.sv-pop__panel')).toBeNull()
|
|
55
|
+
target.querySelector<HTMLElement>('.anchor')!.click()
|
|
56
|
+
flushSync()
|
|
57
|
+
const panel = document.querySelector<HTMLElement>('.sv-pop__panel')!
|
|
58
|
+
expect(panel).not.toBeNull()
|
|
59
|
+
expect(panel.getAttribute('role')).toBe('dialog')
|
|
60
|
+
// Outside click closes.
|
|
61
|
+
document.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
62
|
+
flushSync()
|
|
63
|
+
expect(document.querySelector('.sv-pop__panel')).toBeNull()
|
|
64
|
+
} finally { destroy() }
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('SvTooltip', () => {
|
|
69
|
+
it('shows after the delay on focus and wires aria-describedby', () => {
|
|
70
|
+
vi.useFakeTimers()
|
|
71
|
+
try {
|
|
72
|
+
const child = createRawSnippet(() => ({ render: () => `<button class="t">i</button>` }))
|
|
73
|
+
const { target, destroy } = mountC(SvTooltip, { text: 'Tip text', delay: 200, children: child })
|
|
74
|
+
const anchor = target.querySelector<HTMLElement>('.sv-tip__anchor')!
|
|
75
|
+
anchor.dispatchEvent(new FocusEvent('focusin', { bubbles: true }))
|
|
76
|
+
vi.advanceTimersByTime(210)
|
|
77
|
+
flushSync()
|
|
78
|
+
const tip = document.querySelector<HTMLElement>('.sv-tip')!
|
|
79
|
+
expect(tip).not.toBeNull()
|
|
80
|
+
expect(tip.getAttribute('role')).toBe('tooltip')
|
|
81
|
+
expect(anchor.getAttribute('aria-describedby')).toBe(tip.id)
|
|
82
|
+
destroy()
|
|
83
|
+
} finally { vi.useRealTimers() }
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { paginationRange } from './paginate'
|
|
3
|
+
|
|
4
|
+
describe('paginationRange', () => {
|
|
5
|
+
it('returns an empty list for zero pages', () => {
|
|
6
|
+
expect(paginationRange({ page: 1, pageCount: 0 })).toEqual([])
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('shows every page when the count is small', () => {
|
|
10
|
+
expect(paginationRange({ page: 3, pageCount: 5 })).toEqual([1, 2, 3, 4, 5])
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('adds a trailing ellipsis near the start', () => {
|
|
14
|
+
expect(paginationRange({ page: 1, pageCount: 10 })).toEqual([1, 2, 'ellipsis-right', 10])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('adds ellipses on both sides in the middle', () => {
|
|
18
|
+
expect(paginationRange({ page: 5, pageCount: 10 })).toEqual([
|
|
19
|
+
1, 'ellipsis-left', 4, 5, 6, 'ellipsis-right', 10,
|
|
20
|
+
])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('adds a leading ellipsis near the end', () => {
|
|
24
|
+
expect(paginationRange({ page: 10, pageCount: 10 })).toEqual([1, 'ellipsis-left', 9, 10])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('collapses a single-page gap to that page number, not an ellipsis', () => {
|
|
28
|
+
// Between boundary 1 and sibling-start 3 there is exactly one page (2): show it.
|
|
29
|
+
expect(paginationRange({ page: 4, pageCount: 8 })).toEqual([1, 2, 3, 4, 5, 'ellipsis-right', 8])
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('clamps an out-of-range page', () => {
|
|
33
|
+
expect(paginationRange({ page: 99, pageCount: 5 })).toEqual([1, 2, 3, 4, 5])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('honours a larger siblingCount', () => {
|
|
37
|
+
expect(paginationRange({ page: 6, pageCount: 12, siblingCount: 2 })).toEqual([
|
|
38
|
+
1, 'ellipsis-left', 4, 5, 6, 7, 8, 'ellipsis-right', 12,
|
|
39
|
+
])
|
|
40
|
+
})
|
|
41
|
+
})
|
package/src/paginate.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paginate - the pure page-range algorithm behind <SvPagination>. Given the
|
|
3
|
+
* current page and total page count, returns the sequence of page numbers and
|
|
4
|
+
* ellipsis markers to render (boundary pages + a window of siblings around the
|
|
5
|
+
* current page, with gaps collapsed to an ellipsis). Framework-free + pure so
|
|
6
|
+
* it is unit-tested on its own.
|
|
7
|
+
*/
|
|
8
|
+
export type PaginationItem = number | 'ellipsis-left' | 'ellipsis-right'
|
|
9
|
+
|
|
10
|
+
export type PaginationRangeOptions = {
|
|
11
|
+
/** 1-based current page. */
|
|
12
|
+
page: number
|
|
13
|
+
/** Total number of pages. */
|
|
14
|
+
pageCount: number
|
|
15
|
+
/** Pages shown on each side of the current page. Default 1. */
|
|
16
|
+
siblingCount?: number
|
|
17
|
+
/** Pages pinned at each end. Default 1. */
|
|
18
|
+
boundaryCount?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const clamp = (n: number, lo: number, hi: number) => Math.min(Math.max(n, lo), hi)
|
|
22
|
+
|
|
23
|
+
/** Build the page/ellipsis sequence (e.g. `[1, 'ellipsis-left', 4, 5, 6, 'ellipsis-right', 20]`). */
|
|
24
|
+
export function paginationRange(options: PaginationRangeOptions): PaginationItem[] {
|
|
25
|
+
const { pageCount } = options
|
|
26
|
+
if (pageCount <= 0) return []
|
|
27
|
+
const siblingCount = Math.max(0, options.siblingCount ?? 1)
|
|
28
|
+
const boundaryCount = Math.max(1, options.boundaryCount ?? 1)
|
|
29
|
+
const page = clamp(options.page, 1, pageCount)
|
|
30
|
+
|
|
31
|
+
// If ellipsis could hide at most one page, just show every page.
|
|
32
|
+
if (pageCount <= boundaryCount * 2 + siblingCount * 2 + 3) {
|
|
33
|
+
return Array.from({ length: pageCount }, (_, i) => i + 1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const pages = new Set<number>()
|
|
37
|
+
// End boundaries (both ends).
|
|
38
|
+
for (let i = 1; i <= boundaryCount && i <= pageCount; i++) {
|
|
39
|
+
pages.add(i)
|
|
40
|
+
pages.add(pageCount - i + 1)
|
|
41
|
+
}
|
|
42
|
+
// Siblings around the current page.
|
|
43
|
+
for (let i = page - siblingCount; i <= page + siblingCount; i++) {
|
|
44
|
+
if (i >= 1 && i <= pageCount) pages.add(i)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const sorted = [...pages].sort((a, b) => a - b)
|
|
48
|
+
const result: PaginationItem[] = []
|
|
49
|
+
let prev = 0
|
|
50
|
+
for (const p of sorted) {
|
|
51
|
+
if (p - prev === 2) {
|
|
52
|
+
// Exactly one page in the gap: show it instead of an ellipsis.
|
|
53
|
+
result.push(prev + 1)
|
|
54
|
+
} else if (p - prev > 2) {
|
|
55
|
+
result.push(p <= page ? 'ellipsis-left' : 'ellipsis-right')
|
|
56
|
+
}
|
|
57
|
+
result.push(p)
|
|
58
|
+
prev = p
|
|
59
|
+
}
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-country phone length validation (phoneDigitsValid): exact lengths, ranges,
|
|
3
|
+
* unknown countries (any non-empty), and empty handling.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it } from 'vitest'
|
|
6
|
+
import { phoneDigitsValid } from './countries'
|
|
7
|
+
|
|
8
|
+
describe('phoneDigitsValid', () => {
|
|
9
|
+
it('checks exact national lengths', () => {
|
|
10
|
+
expect(phoneDigitsValid('US', '4155551234')).toBe(true) // 10
|
|
11
|
+
expect(phoneDigitsValid('US', '415555')).toBe(false)
|
|
12
|
+
expect(phoneDigitsValid('IN', '9876543210')).toBe(true) // 10
|
|
13
|
+
expect(phoneDigitsValid('SG', '81234567')).toBe(true) // 8
|
|
14
|
+
expect(phoneDigitsValid('SG', '8123456')).toBe(false)
|
|
15
|
+
})
|
|
16
|
+
it('accepts ranges', () => {
|
|
17
|
+
expect(phoneDigitsValid('DE', '1701234567')).toBe(true) // 10 in [10,11]
|
|
18
|
+
expect(phoneDigitsValid('DE', '17012345678')).toBe(true) // 11
|
|
19
|
+
expect(phoneDigitsValid('DE', '170')).toBe(false)
|
|
20
|
+
})
|
|
21
|
+
it('ignores non-digits and unknown countries', () => {
|
|
22
|
+
expect(phoneDigitsValid('US', '(415) 555-1234')).toBe(true)
|
|
23
|
+
expect(phoneDigitsValid('ZZ', '12345')).toBe(true) // unknown -> any non-empty
|
|
24
|
+
expect(phoneDigitsValid('ZZ', '')).toBe(false)
|
|
25
|
+
})
|
|
26
|
+
})
|