@svgrid/grid 1.2.34 → 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
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* createColorInput - the HEADLESS core behind <SvColorInput>: a runes-based
|
|
3
|
-
* state machine that owns hex normalization, the palette, the draft hex field
|
|
4
|
-
* and the open/close popover state, exposed as **prop-getters** you spread onto
|
|
5
|
-
* YOUR OWN markup. DOM measurement / portalling stays in the renderer, NOT here.
|
|
6
|
-
*
|
|
7
|
-
* ```svelte
|
|
8
|
-
* <script lang="ts">
|
|
9
|
-
* import { createColorInput } from '@svgrid/grid'
|
|
10
|
-
* let value = $state('#3b82f6')
|
|
11
|
-
* const col = createColorInput({ value: () => value, onChange: (v) => (value = v) })
|
|
12
|
-
* </script>
|
|
13
|
-
* <button {...col.swatchProps()}>{col.normalized}</button>
|
|
14
|
-
* {#if col.popover.open}<div>...palette...</div>{/if}
|
|
15
|
-
* ```
|
|
16
|
-
*
|
|
17
|
-
* The styled <SvColorInput> is just one renderer over this core (it adds the
|
|
18
|
-
* portal + anchored positioning).
|
|
19
|
-
*/
|
|
20
1
|
export declare const DEFAULT_PALETTE: string[];
|
|
21
2
|
/** Normalize a hex string (#abc / #aabbcc / abc) to lower-case #aabbcc (pure). */
|
|
22
3
|
export declare function normalizeHex(h: string): string | null;
|
|
@@ -27,6 +8,11 @@ export type ColorInputConfig = {
|
|
|
27
8
|
disabled?: () => boolean;
|
|
28
9
|
readonly?: () => boolean;
|
|
29
10
|
palette?: () => string[];
|
|
11
|
+
id?: () => string | undefined;
|
|
12
|
+
invalid?: () => boolean;
|
|
13
|
+
required?: () => boolean;
|
|
14
|
+
error?: () => string | undefined;
|
|
15
|
+
hint?: () => string | undefined;
|
|
30
16
|
ariaLabel?: () => string | undefined;
|
|
31
17
|
};
|
|
32
18
|
export declare function createColorInput(config: ColorInputConfig): {
|
|
@@ -50,12 +36,16 @@ export declare function createColorInput(config: ColorInputConfig): {
|
|
|
50
36
|
isActive: (c: string) => boolean;
|
|
51
37
|
/** Spread onto the swatch trigger button. */
|
|
52
38
|
swatchProps: () => {
|
|
53
|
-
type: "button";
|
|
54
|
-
'aria-haspopup': "dialog";
|
|
55
|
-
'aria-expanded': boolean;
|
|
56
39
|
'aria-label': string;
|
|
57
40
|
disabled: boolean;
|
|
58
41
|
onclick: () => void;
|
|
42
|
+
'aria-invalid': "true" | undefined;
|
|
43
|
+
'aria-required': "true" | undefined;
|
|
44
|
+
'aria-describedby': string | undefined;
|
|
45
|
+
id: string | undefined;
|
|
46
|
+
type: "button";
|
|
47
|
+
'aria-haspopup': "dialog";
|
|
48
|
+
'aria-expanded': boolean;
|
|
59
49
|
};
|
|
60
50
|
/** Spread onto a palette chip button for color `c`. */
|
|
61
51
|
chipProps: (c: string) => {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* The styled <SvColorInput> is just one renderer over this core (it adds the
|
|
18
18
|
* portal + anchored positioning).
|
|
19
19
|
*/
|
|
20
|
+
import { editorAria } from './editor-contract';
|
|
20
21
|
export const DEFAULT_PALETTE = [
|
|
21
22
|
'#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6',
|
|
22
23
|
'#06b6d4', '#0ea5e9', '#3b82f6', '#6366f1', '#8b5cf6', '#a855f7', '#d946ef', '#ec4899',
|
|
@@ -59,6 +60,14 @@ export function createColorInput(config) {
|
|
|
59
60
|
hexDraft = normalized;
|
|
60
61
|
}
|
|
61
62
|
const isActive = (c) => normalizeHex(c) === normalized;
|
|
63
|
+
const ariaState = () => ({
|
|
64
|
+
id: config.id?.(),
|
|
65
|
+
invalid: config.invalid?.(),
|
|
66
|
+
required: config.required?.(),
|
|
67
|
+
error: config.error?.(),
|
|
68
|
+
hint: config.hint?.(),
|
|
69
|
+
ariaLabel: config.ariaLabel?.(),
|
|
70
|
+
});
|
|
62
71
|
// Popover control surface (open state + show/close/toggle), no DOM measurement.
|
|
63
72
|
const popover = {
|
|
64
73
|
get open() { return open; },
|
|
@@ -83,9 +92,13 @@ export function createColorInput(config) {
|
|
|
83
92
|
isActive,
|
|
84
93
|
/** Spread onto the swatch trigger button. */
|
|
85
94
|
swatchProps: () => ({
|
|
95
|
+
id: config.id?.(),
|
|
86
96
|
type: 'button',
|
|
87
97
|
'aria-haspopup': 'dialog',
|
|
88
98
|
'aria-expanded': open,
|
|
99
|
+
// aria-invalid/required/describedby come from the editor contract; the
|
|
100
|
+
// aria-label default names the swatch by its current color when unset.
|
|
101
|
+
...editorAria(ariaState()),
|
|
89
102
|
'aria-label': config.ariaLabel?.() ?? `Color ${normalized}`,
|
|
90
103
|
disabled: !isInteractive,
|
|
91
104
|
onclick: toggle,
|
|
@@ -28,9 +28,16 @@ export type ComboboxConfig = {
|
|
|
28
28
|
onChange?: (value: ComboboxValue) => void;
|
|
29
29
|
disabled?: () => boolean;
|
|
30
30
|
ariaLabel?: () => string | undefined;
|
|
31
|
+
/** Filter the options locally by the query. Set false for server-side search. */
|
|
32
|
+
localFilter?: () => boolean;
|
|
31
33
|
/** DOM focus hooks provided by the renderer; the core itself never touches the DOM. */
|
|
32
34
|
focusInput?: () => void;
|
|
33
35
|
blurInput?: () => void;
|
|
36
|
+
id?: () => string | undefined;
|
|
37
|
+
invalid?: () => boolean;
|
|
38
|
+
required?: () => boolean;
|
|
39
|
+
error?: () => string | undefined;
|
|
40
|
+
hint?: () => string | undefined;
|
|
34
41
|
};
|
|
35
42
|
export declare function createCombobox(config: ComboboxConfig): {
|
|
36
43
|
/** Panel open state. */
|
|
@@ -63,16 +70,20 @@ export declare function createCombobox(config: ComboboxConfig): {
|
|
|
63
70
|
onKeydown: (e: KeyboardEvent) => void;
|
|
64
71
|
/** Spread onto the editable <input>. */
|
|
65
72
|
inputProps: () => {
|
|
66
|
-
role: "combobox";
|
|
67
|
-
'aria-expanded': boolean;
|
|
68
|
-
'aria-controls': string;
|
|
69
|
-
'aria-autocomplete': "list";
|
|
70
|
-
'aria-label': string | undefined;
|
|
71
73
|
value: string;
|
|
72
74
|
disabled: boolean;
|
|
73
75
|
oninput: (e: Event) => void;
|
|
74
76
|
onfocus: () => void;
|
|
75
77
|
onkeydown: (e: KeyboardEvent) => void;
|
|
78
|
+
'aria-invalid': "true" | undefined;
|
|
79
|
+
'aria-required': "true" | undefined;
|
|
80
|
+
'aria-describedby': string | undefined;
|
|
81
|
+
'aria-label': string | undefined;
|
|
82
|
+
id: string | undefined;
|
|
83
|
+
role: "combobox";
|
|
84
|
+
'aria-expanded': boolean;
|
|
85
|
+
'aria-controls': string;
|
|
86
|
+
'aria-autocomplete': "list";
|
|
76
87
|
};
|
|
77
88
|
/** Spread onto the chevron/toggle <button>. */
|
|
78
89
|
triggerProps: () => {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
import { filterOptions } from './list-option';
|
|
23
|
+
import { editorAria } from './editor-contract';
|
|
23
24
|
let uid = 0;
|
|
24
25
|
export function createCombobox(config) {
|
|
25
26
|
const id = `sv-combo-${uid++}`;
|
|
@@ -30,13 +31,22 @@ export function createCombobox(config) {
|
|
|
30
31
|
let query = $state('');
|
|
31
32
|
let active = $state(0);
|
|
32
33
|
let editing = $state(false);
|
|
34
|
+
const localFilter = () => config.localFilter?.() ?? true;
|
|
33
35
|
const selected = $derived(opts().find((o) => o.value === config.value()) ?? null);
|
|
34
|
-
const filtered = $derived(editing ? filterOptions(opts(), query) : [...opts()]);
|
|
36
|
+
const filtered = $derived(editing && localFilter() ? filterOptions(opts(), query) : [...opts()]);
|
|
35
37
|
const shownText = $derived(editing ? query : selected?.label ?? '');
|
|
36
38
|
// While not actively editing, keep the shown text in sync with the selection.
|
|
37
39
|
$effect(() => { if (!editing)
|
|
38
40
|
query = selected?.label ?? ''; });
|
|
39
41
|
const optionId = (i) => `${listId}-opt-${i}`;
|
|
42
|
+
const ariaState = () => ({
|
|
43
|
+
id: config.id?.(),
|
|
44
|
+
invalid: config.invalid?.(),
|
|
45
|
+
required: config.required?.(),
|
|
46
|
+
error: config.error?.(),
|
|
47
|
+
hint: config.hint?.(),
|
|
48
|
+
ariaLabel: config.ariaLabel?.(),
|
|
49
|
+
});
|
|
40
50
|
function openPanel() { if (disabled() || open)
|
|
41
51
|
return; open = true; active = 0; }
|
|
42
52
|
function close(revert = true) {
|
|
@@ -128,11 +138,12 @@ export function createCombobox(config) {
|
|
|
128
138
|
onKeydown,
|
|
129
139
|
/** Spread onto the editable <input>. */
|
|
130
140
|
inputProps: () => ({
|
|
141
|
+
id: config.id?.(),
|
|
131
142
|
role: 'combobox',
|
|
132
143
|
'aria-expanded': open,
|
|
133
144
|
'aria-controls': listId,
|
|
134
145
|
'aria-autocomplete': 'list',
|
|
135
|
-
|
|
146
|
+
...editorAria(ariaState()),
|
|
136
147
|
value: shownText,
|
|
137
148
|
disabled: disabled(),
|
|
138
149
|
oninput: onInput,
|
|
@@ -27,8 +27,15 @@ export type CountryInputConfig = {
|
|
|
27
27
|
onChange?: (code: string) => void;
|
|
28
28
|
disabled?: () => boolean;
|
|
29
29
|
ariaLabel?: () => string | undefined;
|
|
30
|
+
/** Accessible label for the in-panel search field (localizable). */
|
|
31
|
+
searchLabel?: () => string | undefined;
|
|
30
32
|
/** DOM focus hook provided by the renderer; the core never touches the DOM. */
|
|
31
33
|
focusTrigger?: () => void;
|
|
34
|
+
id?: () => string | undefined;
|
|
35
|
+
invalid?: () => boolean;
|
|
36
|
+
required?: () => boolean;
|
|
37
|
+
error?: () => string | undefined;
|
|
38
|
+
hint?: () => string | undefined;
|
|
32
39
|
};
|
|
33
40
|
export declare function createCountryInput(config: CountryInputConfig): {
|
|
34
41
|
/** Panel open state. */
|
|
@@ -54,12 +61,16 @@ export declare function createCountryInput(config: CountryInputConfig): {
|
|
|
54
61
|
onKeydown: (e: KeyboardEvent) => void;
|
|
55
62
|
/** Spread onto the trigger <button>. */
|
|
56
63
|
triggerProps: () => {
|
|
64
|
+
disabled: boolean;
|
|
65
|
+
onclick: () => void;
|
|
66
|
+
'aria-invalid': "true" | undefined;
|
|
67
|
+
'aria-required': "true" | undefined;
|
|
68
|
+
'aria-describedby': string | undefined;
|
|
69
|
+
'aria-label': string | undefined;
|
|
70
|
+
id: string | undefined;
|
|
57
71
|
type: "button";
|
|
58
72
|
'aria-haspopup': "listbox";
|
|
59
73
|
'aria-expanded': boolean;
|
|
60
|
-
'aria-label': string | undefined;
|
|
61
|
-
disabled: boolean;
|
|
62
|
-
onclick: () => void;
|
|
63
74
|
};
|
|
64
75
|
/** Spread onto the search <input> inside the panel. */
|
|
65
76
|
searchProps: () => {
|
|
@@ -21,8 +21,17 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
import { COUNTRIES, COUNTRY_BY_CODE } from './countries';
|
|
24
|
+
import { editorAria } from './editor-contract';
|
|
24
25
|
export function createCountryInput(config) {
|
|
25
26
|
const disabled = () => config.disabled?.() ?? false;
|
|
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 open = $state(false);
|
|
27
36
|
let query = $state('');
|
|
28
37
|
let active = $state(0);
|
|
@@ -90,10 +99,11 @@ export function createCountryInput(config) {
|
|
|
90
99
|
onKeydown,
|
|
91
100
|
/** Spread onto the trigger <button>. */
|
|
92
101
|
triggerProps: () => ({
|
|
102
|
+
id: config.id?.(),
|
|
93
103
|
type: 'button',
|
|
94
104
|
'aria-haspopup': 'listbox',
|
|
95
105
|
'aria-expanded': open,
|
|
96
|
-
|
|
106
|
+
...editorAria(ariaState()),
|
|
97
107
|
disabled: disabled(),
|
|
98
108
|
onclick: toggle,
|
|
99
109
|
}),
|
|
@@ -101,7 +111,7 @@ export function createCountryInput(config) {
|
|
|
101
111
|
searchProps: () => ({
|
|
102
112
|
type: 'text',
|
|
103
113
|
value: query,
|
|
104
|
-
'aria-label': 'Search countries',
|
|
114
|
+
'aria-label': config.searchLabel?.() ?? 'Search countries',
|
|
105
115
|
oninput: onSearchInput,
|
|
106
116
|
onkeydown: onKeydown,
|
|
107
117
|
}),
|
|
@@ -20,6 +20,16 @@ export type DateTimePickerConfig = {
|
|
|
20
20
|
stepMinutes?: () => number;
|
|
21
21
|
disabled?: () => boolean;
|
|
22
22
|
readonly?: () => boolean;
|
|
23
|
+
/** Accessible label for the open-picker toggle (localizable). */
|
|
24
|
+
toggleLabel?: () => string | undefined;
|
|
25
|
+
/** Accessible label for the clear button (localizable). */
|
|
26
|
+
clearLabel?: () => string | undefined;
|
|
27
|
+
id?: () => string | undefined;
|
|
28
|
+
invalid?: () => boolean;
|
|
29
|
+
required?: () => boolean;
|
|
30
|
+
error?: () => string | undefined;
|
|
31
|
+
hint?: () => string | undefined;
|
|
32
|
+
ariaLabel?: () => string | undefined;
|
|
23
33
|
};
|
|
24
34
|
export declare function createDateTimePicker(config: DateTimePickerConfig): {
|
|
25
35
|
readonly current: Date | null;
|
|
@@ -42,11 +52,16 @@ export declare function createDateTimePicker(config: DateTimePickerConfig): {
|
|
|
42
52
|
/** The text field: masked value round-trips through the token engine. Spread
|
|
43
53
|
* alongside `bind:value={dtp.text}`. */
|
|
44
54
|
inputProps(): {
|
|
55
|
+
onblur: () => void;
|
|
56
|
+
onkeydown: (e: KeyboardEvent) => void;
|
|
57
|
+
'aria-invalid': "true" | undefined;
|
|
58
|
+
'aria-required': "true" | undefined;
|
|
59
|
+
'aria-describedby': string | undefined;
|
|
60
|
+
'aria-label': string | undefined;
|
|
61
|
+
id: string | undefined;
|
|
45
62
|
type: "text";
|
|
46
63
|
disabled: boolean;
|
|
47
64
|
readonly: boolean;
|
|
48
|
-
onblur: () => void;
|
|
49
|
-
onkeydown: (e: KeyboardEvent) => void;
|
|
50
65
|
};
|
|
51
66
|
/** The calendar-toggle button. */
|
|
52
67
|
toggleProps(): {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { formatDate, parseDate } from './datetime/date-format';
|
|
19
19
|
import { toDate, clampDate, withTime, startOfDay } from './datetime/date-core';
|
|
20
|
+
import { editorAria } from './editor-contract';
|
|
20
21
|
const defaultLocale = () => (typeof navigator !== 'undefined' ? navigator.language : 'en-US');
|
|
21
22
|
export function createDateTimePicker(config) {
|
|
22
23
|
const formatString = () => config.formatString?.() ?? 'yyyy-MM-dd HH:mm';
|
|
@@ -152,9 +153,18 @@ export function createDateTimePicker(config) {
|
|
|
152
153
|
* alongside `bind:value={dtp.text}`. */
|
|
153
154
|
inputProps() {
|
|
154
155
|
return {
|
|
156
|
+
id: config.id?.(),
|
|
155
157
|
type: 'text',
|
|
156
158
|
disabled: isDisabled(),
|
|
157
159
|
readonly: isReadonly(),
|
|
160
|
+
...editorAria({
|
|
161
|
+
id: config.id?.(),
|
|
162
|
+
invalid: config.invalid?.(),
|
|
163
|
+
required: config.required?.(),
|
|
164
|
+
error: config.error?.(),
|
|
165
|
+
hint: config.hint?.(),
|
|
166
|
+
ariaLabel: config.ariaLabel?.(),
|
|
167
|
+
}),
|
|
158
168
|
onblur: commitFromText,
|
|
159
169
|
onkeydown: onInputKeydown,
|
|
160
170
|
};
|
|
@@ -163,7 +173,7 @@ export function createDateTimePicker(config) {
|
|
|
163
173
|
toggleProps() {
|
|
164
174
|
return {
|
|
165
175
|
type: 'button',
|
|
166
|
-
'aria-label': 'Open picker',
|
|
176
|
+
'aria-label': config.toggleLabel?.() ?? 'Open picker',
|
|
167
177
|
'aria-haspopup': 'dialog',
|
|
168
178
|
'aria-expanded': open,
|
|
169
179
|
disabled: !isInteractive,
|
|
@@ -175,7 +185,7 @@ export function createDateTimePicker(config) {
|
|
|
175
185
|
clearProps() {
|
|
176
186
|
return {
|
|
177
187
|
type: 'button',
|
|
178
|
-
'aria-label': 'Clear',
|
|
188
|
+
'aria-label': config.clearLabel?.() ?? 'Clear',
|
|
179
189
|
onclick: () => commit(null),
|
|
180
190
|
};
|
|
181
191
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* {/if}
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
import type
|
|
22
|
+
import { type ListOption } from './list-option';
|
|
23
23
|
export type DropdownValue = string | number | null;
|
|
24
24
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
25
25
|
export type DropdownListConfig = {
|
|
@@ -30,6 +30,11 @@ export type DropdownListConfig = {
|
|
|
30
30
|
ariaLabel?: () => string | undefined;
|
|
31
31
|
/** DOM focus hook provided by the renderer; the core never touches the DOM. */
|
|
32
32
|
focusTrigger?: () => void;
|
|
33
|
+
id?: () => string | undefined;
|
|
34
|
+
invalid?: () => boolean;
|
|
35
|
+
required?: () => boolean;
|
|
36
|
+
error?: () => string | undefined;
|
|
37
|
+
hint?: () => string | undefined;
|
|
33
38
|
};
|
|
34
39
|
export declare function createDropdownList(config: DropdownListConfig): {
|
|
35
40
|
/** Panel open state. */
|
|
@@ -51,13 +56,17 @@ export declare function createDropdownList(config: DropdownListConfig): {
|
|
|
51
56
|
onKeydown: (e: KeyboardEvent) => void;
|
|
52
57
|
/** Spread onto the trigger <button>. */
|
|
53
58
|
triggerProps: () => {
|
|
54
|
-
type: "button";
|
|
55
|
-
'aria-haspopup': "listbox";
|
|
56
|
-
'aria-expanded': boolean;
|
|
57
|
-
'aria-label': string | undefined;
|
|
58
59
|
disabled: boolean;
|
|
59
60
|
onclick: () => void;
|
|
60
61
|
onkeydown: (e: KeyboardEvent) => void;
|
|
62
|
+
'aria-invalid': "true" | undefined;
|
|
63
|
+
'aria-required': "true" | undefined;
|
|
64
|
+
'aria-describedby': string | undefined;
|
|
65
|
+
'aria-label': string | undefined;
|
|
66
|
+
id: string | undefined;
|
|
67
|
+
type: "button";
|
|
68
|
+
'aria-haspopup': "listbox";
|
|
69
|
+
'aria-expanded': boolean;
|
|
61
70
|
};
|
|
62
71
|
/** Spread onto the listbox container. */
|
|
63
72
|
listboxProps: () => {
|
|
@@ -1,6 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createDropdownList - the HEADLESS core behind <SvDropDownList>: a single-select
|
|
3
|
+
* dropdown state machine (trigger open/close, roving active index over enabled
|
|
4
|
+
* options, full keyboard) exposed as **prop-getters** you spread onto YOUR OWN
|
|
5
|
+
* markup. No styles, no DOM, no portal/measurement - those stay in the styled
|
|
6
|
+
* component.
|
|
7
|
+
*
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script lang="ts">
|
|
10
|
+
* import { createDropdownList } from '@svgrid/grid'
|
|
11
|
+
* let value = $state<string | null>(null)
|
|
12
|
+
* const dd = createDropdownList({ options: () => options, value: () => value, onChange: (v) => (value = v) })
|
|
13
|
+
* </script>
|
|
14
|
+
* <button {...dd.triggerProps()}>{dd.selected?.label ?? 'Select'}</button>
|
|
15
|
+
* {#if dd.open}
|
|
16
|
+
* <ul {...dd.listboxProps()}>
|
|
17
|
+
* {#each options as opt, i (opt.value)}<li {...dd.optionProps(i)}>{opt.label}</li>{/each}
|
|
18
|
+
* </ul>
|
|
19
|
+
* {/if}
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
import { createTypeaheadBuffer, isTypeaheadKey, nextTypeaheadIndex } from './list-option';
|
|
23
|
+
import { editorAria } from './editor-contract';
|
|
1
24
|
export function createDropdownList(config) {
|
|
2
25
|
const opts = () => config.options();
|
|
3
26
|
const disabled = () => config.disabled?.() ?? false;
|
|
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
|
+
});
|
|
4
35
|
let open = $state(false);
|
|
5
36
|
let active = $state(-1);
|
|
6
37
|
const selected = $derived(opts().find((o) => o.value === config.value()) ?? null);
|
|
@@ -32,9 +63,26 @@ export function createDropdownList(config) {
|
|
|
32
63
|
const pos = enabledIdx.indexOf(active);
|
|
33
64
|
active = enabledIdx[(pos + d + enabledIdx.length) % enabledIdx.length] ?? active;
|
|
34
65
|
}
|
|
66
|
+
// Type-ahead: typing a label prefix jumps the active option (and selects it
|
|
67
|
+
// when the list is closed, matching a native <select>).
|
|
68
|
+
const typeahead = createTypeaheadBuffer();
|
|
69
|
+
function onTypeahead(char) {
|
|
70
|
+
const buffer = typeahead.push(char.toLowerCase());
|
|
71
|
+
const from = active >= 0 ? active : opts().findIndex((o) => o.value === config.value());
|
|
72
|
+
const next = nextTypeaheadIndex(opts(), buffer, from);
|
|
73
|
+
if (next < 0)
|
|
74
|
+
return;
|
|
75
|
+
active = next;
|
|
76
|
+
if (!open)
|
|
77
|
+
config.onChange?.(opts()[next].value);
|
|
78
|
+
}
|
|
35
79
|
function onKeydown(e) {
|
|
36
80
|
if (disabled())
|
|
37
81
|
return;
|
|
82
|
+
if (isTypeaheadKey(e)) {
|
|
83
|
+
onTypeahead(e.key);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
38
86
|
if (!open && (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')) {
|
|
39
87
|
e.preventDefault();
|
|
40
88
|
openPanel();
|
|
@@ -86,10 +134,11 @@ export function createDropdownList(config) {
|
|
|
86
134
|
onKeydown,
|
|
87
135
|
/** Spread onto the trigger <button>. */
|
|
88
136
|
triggerProps: () => ({
|
|
137
|
+
id: config.id?.(),
|
|
89
138
|
type: 'button',
|
|
90
139
|
'aria-haspopup': 'listbox',
|
|
91
140
|
'aria-expanded': open,
|
|
92
|
-
|
|
141
|
+
...editorAria(ariaState()),
|
|
93
142
|
disabled: disabled(),
|
|
94
143
|
onclick: toggle,
|
|
95
144
|
onkeydown: onKeydown,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type FileRejectReason = 'type' | 'size' | 'count';
|
|
2
|
+
export type FileRejection = {
|
|
3
|
+
file: File;
|
|
4
|
+
reason: FileRejectReason;
|
|
5
|
+
};
|
|
6
|
+
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
7
|
+
export type FileUploadConfig = {
|
|
8
|
+
files: () => File[];
|
|
9
|
+
onChange?: (files: File[]) => void;
|
|
10
|
+
/** Rejected files (wrong type / too big / over the count cap). */
|
|
11
|
+
onReject?: (rejections: FileRejection[]) => void;
|
|
12
|
+
/** Comma list like "image/*,.pdf" (matches MIME or extension). */
|
|
13
|
+
accept?: () => string | undefined;
|
|
14
|
+
multiple?: () => boolean;
|
|
15
|
+
/** Max size per file in bytes. */
|
|
16
|
+
maxSize?: () => number | undefined;
|
|
17
|
+
/** Max number of files (multiple mode). */
|
|
18
|
+
maxFiles?: () => number | undefined;
|
|
19
|
+
disabled?: () => boolean;
|
|
20
|
+
readonly?: () => boolean;
|
|
21
|
+
id?: () => string | undefined;
|
|
22
|
+
invalid?: () => boolean;
|
|
23
|
+
required?: () => boolean;
|
|
24
|
+
error?: () => string | undefined;
|
|
25
|
+
hint?: () => string | undefined;
|
|
26
|
+
ariaLabel?: () => string | undefined;
|
|
27
|
+
};
|
|
28
|
+
/** Whether `file` satisfies an `accept` list (MIME globs or extensions). Pure. */
|
|
29
|
+
export declare function fileMatchesAccept(file: File, accept?: string): boolean;
|
|
30
|
+
export declare function createFileUpload(config: FileUploadConfig): {
|
|
31
|
+
/** Whether files are being dragged over the dropzone. */
|
|
32
|
+
readonly dragging: boolean;
|
|
33
|
+
/** Whether interaction is allowed. */
|
|
34
|
+
readonly isInteractive: boolean;
|
|
35
|
+
/** Selected files (controlled value). */
|
|
36
|
+
readonly files: File[];
|
|
37
|
+
addFiles: (incoming: File[]) => void;
|
|
38
|
+
removeAt: (index: number) => void;
|
|
39
|
+
clear: () => void;
|
|
40
|
+
setDragging: (v: boolean) => void;
|
|
41
|
+
/** Spread onto the dropzone element (click opens the picker; Enter/Space too).
|
|
42
|
+
* Wire drag events yourself (they carry a DataTransfer): call
|
|
43
|
+
* `setDragging(true/false)` and `addFiles([...e.dataTransfer.files])`. */
|
|
44
|
+
dropzoneProps: () => {
|
|
45
|
+
'aria-invalid': "true" | undefined;
|
|
46
|
+
'aria-required': "true" | undefined;
|
|
47
|
+
'aria-describedby': string | undefined;
|
|
48
|
+
'aria-label': string | undefined;
|
|
49
|
+
role: "button";
|
|
50
|
+
tabindex: number;
|
|
51
|
+
'data-dragging': string | undefined;
|
|
52
|
+
'aria-disabled': true | undefined;
|
|
53
|
+
};
|
|
54
|
+
/** Spread onto the visually-hidden <input type="file">. */
|
|
55
|
+
inputProps: () => {
|
|
56
|
+
type: "file";
|
|
57
|
+
accept: string | undefined;
|
|
58
|
+
multiple: boolean;
|
|
59
|
+
disabled: boolean;
|
|
60
|
+
tabindex: number;
|
|
61
|
+
};
|
|
62
|
+
/** Spread onto a selected file's remove <button> at `index`. */
|
|
63
|
+
removeProps: (index: number) => {
|
|
64
|
+
type: "button";
|
|
65
|
+
onclick: () => void;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export type FileUpload = ReturnType<typeof createFileUpload>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createFileUpload - the HEADLESS core behind <SvFileUpload>: a drag-and-drop
|
|
3
|
+
* file field with accept / size / count validation, exposed as **prop-getters**
|
|
4
|
+
* you spread onto YOUR OWN markup. The DataTransfer / File reading is a DOM
|
|
5
|
+
* concern that stays in the renderer; the core owns the list, validation, drag
|
|
6
|
+
* state and ARIA. Parity: Smart `smart-file-upload`.
|
|
7
|
+
*
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script lang="ts">
|
|
10
|
+
* import { createFileUpload } from '@svgrid/grid'
|
|
11
|
+
* let files = $state<File[]>([])
|
|
12
|
+
* const fu = createFileUpload({ files: () => files, onChange: (f) => (files = f) })
|
|
13
|
+
* </script>
|
|
14
|
+
* <div {...fu.dropzoneProps()} ondrop={(e) => fu.addFiles([...e.dataTransfer.files])}>...</div>
|
|
15
|
+
* <input {...fu.inputProps()} />
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { editorAria } from './editor-contract';
|
|
19
|
+
/** Whether `file` satisfies an `accept` list (MIME globs or extensions). Pure. */
|
|
20
|
+
export function fileMatchesAccept(file, accept) {
|
|
21
|
+
if (!accept || !accept.trim())
|
|
22
|
+
return true;
|
|
23
|
+
const name = file.name.toLowerCase();
|
|
24
|
+
const type = file.type.toLowerCase();
|
|
25
|
+
return accept.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean).some((tok) => {
|
|
26
|
+
if (tok.startsWith('.'))
|
|
27
|
+
return name.endsWith(tok);
|
|
28
|
+
if (tok.endsWith('/*'))
|
|
29
|
+
return type.startsWith(tok.slice(0, -1));
|
|
30
|
+
return type === tok;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function createFileUpload(config) {
|
|
34
|
+
const accept = () => config.accept?.();
|
|
35
|
+
const multiple = () => config.multiple?.() ?? false;
|
|
36
|
+
const maxSize = () => config.maxSize?.();
|
|
37
|
+
const maxFiles = () => config.maxFiles?.();
|
|
38
|
+
const disabled = () => config.disabled?.() ?? false;
|
|
39
|
+
const readonly = () => config.readonly?.() ?? false;
|
|
40
|
+
const isInteractive = $derived(!disabled() && !readonly());
|
|
41
|
+
let dragging = $state(false);
|
|
42
|
+
function addFiles(incoming) {
|
|
43
|
+
if (!isInteractive || !incoming.length)
|
|
44
|
+
return;
|
|
45
|
+
const accepted = [];
|
|
46
|
+
const rejected = [];
|
|
47
|
+
for (const f of incoming) {
|
|
48
|
+
if (!fileMatchesAccept(f, accept())) {
|
|
49
|
+
rejected.push({ file: f, reason: 'type' });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const cap = maxSize();
|
|
53
|
+
if (cap != null && f.size > cap) {
|
|
54
|
+
rejected.push({ file: f, reason: 'size' });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
accepted.push(f);
|
|
58
|
+
}
|
|
59
|
+
let next = multiple() ? [...config.files(), ...accepted] : accepted.slice(-1);
|
|
60
|
+
const limit = maxFiles();
|
|
61
|
+
if (multiple() && limit != null && next.length > limit) {
|
|
62
|
+
for (const f of next.slice(limit))
|
|
63
|
+
rejected.push({ file: f, reason: 'count' });
|
|
64
|
+
next = next.slice(0, limit);
|
|
65
|
+
}
|
|
66
|
+
if (rejected.length)
|
|
67
|
+
config.onReject?.(rejected);
|
|
68
|
+
config.onChange?.(next);
|
|
69
|
+
}
|
|
70
|
+
function removeAt(index) {
|
|
71
|
+
if (!isInteractive)
|
|
72
|
+
return;
|
|
73
|
+
config.onChange?.(config.files().filter((_, i) => i !== index));
|
|
74
|
+
}
|
|
75
|
+
function clear() { if (isInteractive)
|
|
76
|
+
config.onChange?.([]); }
|
|
77
|
+
const setDragging = (v) => { if (isInteractive)
|
|
78
|
+
dragging = v; };
|
|
79
|
+
const ariaState = () => ({
|
|
80
|
+
id: config.id?.(),
|
|
81
|
+
invalid: config.invalid?.(),
|
|
82
|
+
required: config.required?.(),
|
|
83
|
+
error: config.error?.(),
|
|
84
|
+
hint: config.hint?.(),
|
|
85
|
+
ariaLabel: config.ariaLabel?.(),
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
/** Whether files are being dragged over the dropzone. */
|
|
89
|
+
get dragging() { return dragging; },
|
|
90
|
+
/** Whether interaction is allowed. */
|
|
91
|
+
get isInteractive() { return isInteractive; },
|
|
92
|
+
/** Selected files (controlled value). */
|
|
93
|
+
get files() { return config.files(); },
|
|
94
|
+
addFiles,
|
|
95
|
+
removeAt,
|
|
96
|
+
clear,
|
|
97
|
+
setDragging,
|
|
98
|
+
/** Spread onto the dropzone element (click opens the picker; Enter/Space too).
|
|
99
|
+
* Wire drag events yourself (they carry a DataTransfer): call
|
|
100
|
+
* `setDragging(true/false)` and `addFiles([...e.dataTransfer.files])`. */
|
|
101
|
+
dropzoneProps: () => ({
|
|
102
|
+
role: 'button',
|
|
103
|
+
tabindex: isInteractive ? 0 : -1,
|
|
104
|
+
'data-dragging': dragging ? '' : undefined,
|
|
105
|
+
'aria-disabled': !isInteractive || undefined,
|
|
106
|
+
...editorAria(ariaState()),
|
|
107
|
+
}),
|
|
108
|
+
/** Spread onto the visually-hidden <input type="file">. */
|
|
109
|
+
inputProps: () => ({
|
|
110
|
+
type: 'file',
|
|
111
|
+
accept: accept(),
|
|
112
|
+
multiple: multiple(),
|
|
113
|
+
disabled: !isInteractive,
|
|
114
|
+
tabindex: -1,
|
|
115
|
+
}),
|
|
116
|
+
/** Spread onto a selected file's remove <button> at `index`. */
|
|
117
|
+
removeProps: (index) => ({
|
|
118
|
+
type: 'button',
|
|
119
|
+
onclick: () => removeAt(index),
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
}
|