@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
|
@@ -15,18 +15,30 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The styled <SvCheckBox> is just one renderer over this core.
|
|
17
17
|
*/
|
|
18
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
19
|
+
|
|
18
20
|
export type CheckboxConfig = {
|
|
19
21
|
checked: () => boolean
|
|
20
22
|
indeterminate?: () => boolean
|
|
21
23
|
onChange?: (checked: boolean) => void
|
|
22
24
|
disabled?: () => boolean
|
|
25
|
+
// Editor contract (ARIA + validation) - folded into boxProps().
|
|
26
|
+
id?: () => string | undefined
|
|
27
|
+
invalid?: () => boolean
|
|
28
|
+
required?: () => boolean
|
|
29
|
+
error?: () => string | undefined
|
|
30
|
+
hint?: () => string | undefined
|
|
23
31
|
ariaLabel?: () => string | undefined
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
export type CheckboxBoxProps = {
|
|
27
35
|
type: 'button'
|
|
28
36
|
role: 'checkbox'
|
|
37
|
+
id: string | undefined
|
|
29
38
|
'aria-checked': boolean | 'mixed'
|
|
39
|
+
'aria-invalid': 'true' | undefined
|
|
40
|
+
'aria-required': 'true' | undefined
|
|
41
|
+
'aria-describedby': string | undefined
|
|
30
42
|
'aria-label': string | undefined
|
|
31
43
|
disabled: boolean
|
|
32
44
|
'data-checked': '' | undefined
|
|
@@ -55,6 +67,15 @@ export function createCheckbox(config: CheckboxConfig): Checkbox {
|
|
|
55
67
|
config.onChange?.(indeterminate() ? true : !checked())
|
|
56
68
|
}
|
|
57
69
|
|
|
70
|
+
const ariaState = (): EditorAriaState => ({
|
|
71
|
+
id: config.id?.(),
|
|
72
|
+
invalid: config.invalid?.(),
|
|
73
|
+
required: config.required?.(),
|
|
74
|
+
error: config.error?.(),
|
|
75
|
+
hint: config.hint?.(),
|
|
76
|
+
ariaLabel: config.ariaLabel?.(),
|
|
77
|
+
})
|
|
78
|
+
|
|
58
79
|
return {
|
|
59
80
|
get checked() { return checked() },
|
|
60
81
|
get indeterminate() { return indeterminate() },
|
|
@@ -63,11 +84,12 @@ export function createCheckbox(config: CheckboxConfig): Checkbox {
|
|
|
63
84
|
boxProps: () => ({
|
|
64
85
|
type: 'button',
|
|
65
86
|
role: 'checkbox',
|
|
87
|
+
id: config.id?.(),
|
|
66
88
|
'aria-checked': indeterminate() ? 'mixed' : checked(),
|
|
67
|
-
'aria-label': config.ariaLabel?.(),
|
|
68
89
|
disabled: disabled(),
|
|
69
90
|
'data-checked': checked() && !indeterminate() ? '' : undefined,
|
|
70
91
|
'data-indeterminate': indeterminate() ? '' : undefined,
|
|
92
|
+
...editorAria(ariaState()),
|
|
71
93
|
onclick: toggle,
|
|
72
94
|
}),
|
|
73
95
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* The styled <SvColorInput> is just one renderer over this core (it adds the
|
|
18
18
|
* portal + anchored positioning).
|
|
19
19
|
*/
|
|
20
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
20
21
|
|
|
21
22
|
export const DEFAULT_PALETTE = [
|
|
22
23
|
'#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6',
|
|
@@ -39,6 +40,12 @@ export type ColorInputConfig = {
|
|
|
39
40
|
disabled?: () => boolean
|
|
40
41
|
readonly?: () => boolean
|
|
41
42
|
palette?: () => string[]
|
|
43
|
+
// Editor contract (ARIA + validation) - folded into swatchProps().
|
|
44
|
+
id?: () => string | undefined
|
|
45
|
+
invalid?: () => boolean
|
|
46
|
+
required?: () => boolean
|
|
47
|
+
error?: () => string | undefined
|
|
48
|
+
hint?: () => string | undefined
|
|
42
49
|
ariaLabel?: () => string | undefined
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -71,6 +78,15 @@ export function createColorInput(config: ColorInputConfig) {
|
|
|
71
78
|
}
|
|
72
79
|
const isActive = (c: string) => normalizeHex(c) === normalized
|
|
73
80
|
|
|
81
|
+
const ariaState = (): EditorAriaState => ({
|
|
82
|
+
id: config.id?.(),
|
|
83
|
+
invalid: config.invalid?.(),
|
|
84
|
+
required: config.required?.(),
|
|
85
|
+
error: config.error?.(),
|
|
86
|
+
hint: config.hint?.(),
|
|
87
|
+
ariaLabel: config.ariaLabel?.(),
|
|
88
|
+
})
|
|
89
|
+
|
|
74
90
|
// Popover control surface (open state + show/close/toggle), no DOM measurement.
|
|
75
91
|
const popover = {
|
|
76
92
|
get open() { return open },
|
|
@@ -96,9 +112,13 @@ export function createColorInput(config: ColorInputConfig) {
|
|
|
96
112
|
isActive,
|
|
97
113
|
/** Spread onto the swatch trigger button. */
|
|
98
114
|
swatchProps: () => ({
|
|
115
|
+
id: config.id?.(),
|
|
99
116
|
type: 'button' as const,
|
|
100
117
|
'aria-haspopup': 'dialog' as const,
|
|
101
118
|
'aria-expanded': open,
|
|
119
|
+
// aria-invalid/required/describedby come from the editor contract; the
|
|
120
|
+
// aria-label default names the swatch by its current color when unset.
|
|
121
|
+
...editorAria(ariaState()),
|
|
102
122
|
'aria-label': config.ariaLabel?.() ?? `Color ${normalized}`,
|
|
103
123
|
disabled: !isInteractive,
|
|
104
124
|
onclick: toggle,
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
import { filterOptions, type ListOption } from './list-option'
|
|
23
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
23
24
|
|
|
24
25
|
export type ComboboxValue = string | number | null
|
|
25
26
|
|
|
@@ -30,9 +31,17 @@ export type ComboboxConfig = {
|
|
|
30
31
|
onChange?: (value: ComboboxValue) => void
|
|
31
32
|
disabled?: () => boolean
|
|
32
33
|
ariaLabel?: () => string | undefined
|
|
34
|
+
/** Filter the options locally by the query. Set false for server-side search. */
|
|
35
|
+
localFilter?: () => boolean
|
|
33
36
|
/** DOM focus hooks provided by the renderer; the core itself never touches the DOM. */
|
|
34
37
|
focusInput?: () => void
|
|
35
38
|
blurInput?: () => void
|
|
39
|
+
// Editor contract (ARIA + validation) - folded into inputProps().
|
|
40
|
+
id?: () => string | undefined
|
|
41
|
+
invalid?: () => boolean
|
|
42
|
+
required?: () => boolean
|
|
43
|
+
error?: () => string | undefined
|
|
44
|
+
hint?: () => string | undefined
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
let uid = 0
|
|
@@ -48,8 +57,9 @@ export function createCombobox(config: ComboboxConfig) {
|
|
|
48
57
|
let active = $state(0)
|
|
49
58
|
let editing = $state(false)
|
|
50
59
|
|
|
60
|
+
const localFilter = () => config.localFilter?.() ?? true
|
|
51
61
|
const selected = $derived(opts().find((o) => o.value === config.value()) ?? null)
|
|
52
|
-
const filtered = $derived(editing ? filterOptions(opts(), query) : [...opts()])
|
|
62
|
+
const filtered = $derived(editing && localFilter() ? filterOptions(opts(), query) : [...opts()])
|
|
53
63
|
const shownText = $derived(editing ? query : selected?.label ?? '')
|
|
54
64
|
|
|
55
65
|
// While not actively editing, keep the shown text in sync with the selection.
|
|
@@ -57,6 +67,15 @@ export function createCombobox(config: ComboboxConfig) {
|
|
|
57
67
|
|
|
58
68
|
const optionId = (i: number) => `${listId}-opt-${i}`
|
|
59
69
|
|
|
70
|
+
const ariaState = (): EditorAriaState => ({
|
|
71
|
+
id: config.id?.(),
|
|
72
|
+
invalid: config.invalid?.(),
|
|
73
|
+
required: config.required?.(),
|
|
74
|
+
error: config.error?.(),
|
|
75
|
+
hint: config.hint?.(),
|
|
76
|
+
ariaLabel: config.ariaLabel?.(),
|
|
77
|
+
})
|
|
78
|
+
|
|
60
79
|
function openPanel() { if (disabled() || open) return; open = true; active = 0 }
|
|
61
80
|
function close(revert = true) {
|
|
62
81
|
open = false; editing = false
|
|
@@ -114,11 +133,12 @@ export function createCombobox(config: ComboboxConfig) {
|
|
|
114
133
|
onKeydown,
|
|
115
134
|
/** Spread onto the editable <input>. */
|
|
116
135
|
inputProps: () => ({
|
|
136
|
+
id: config.id?.(),
|
|
117
137
|
role: 'combobox' as const,
|
|
118
138
|
'aria-expanded': open,
|
|
119
139
|
'aria-controls': listId,
|
|
120
140
|
'aria-autocomplete': 'list' as const,
|
|
121
|
-
|
|
141
|
+
...editorAria(ariaState()),
|
|
122
142
|
value: shownText,
|
|
123
143
|
disabled: disabled(),
|
|
124
144
|
oninput: onInput,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
import { COUNTRIES, COUNTRY_BY_CODE, type Country } from './countries'
|
|
24
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
24
25
|
|
|
25
26
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
26
27
|
export type CountryInputConfig = {
|
|
@@ -28,13 +29,30 @@ export type CountryInputConfig = {
|
|
|
28
29
|
onChange?: (code: string) => void
|
|
29
30
|
disabled?: () => boolean
|
|
30
31
|
ariaLabel?: () => string | undefined
|
|
32
|
+
/** Accessible label for the in-panel search field (localizable). */
|
|
33
|
+
searchLabel?: () => string | undefined
|
|
31
34
|
/** DOM focus hook provided by the renderer; the core never touches the DOM. */
|
|
32
35
|
focusTrigger?: () => void
|
|
36
|
+
// Editor contract (ARIA + validation) - folded into triggerProps().
|
|
37
|
+
id?: () => string | undefined
|
|
38
|
+
invalid?: () => boolean
|
|
39
|
+
required?: () => boolean
|
|
40
|
+
error?: () => string | undefined
|
|
41
|
+
hint?: () => string | undefined
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
export function createCountryInput(config: CountryInputConfig) {
|
|
36
45
|
const disabled = () => config.disabled?.() ?? false
|
|
37
46
|
|
|
47
|
+
const ariaState = (): EditorAriaState => ({
|
|
48
|
+
id: config.id?.(),
|
|
49
|
+
invalid: config.invalid?.(),
|
|
50
|
+
required: config.required?.(),
|
|
51
|
+
error: config.error?.(),
|
|
52
|
+
hint: config.hint?.(),
|
|
53
|
+
ariaLabel: config.ariaLabel?.(),
|
|
54
|
+
})
|
|
55
|
+
|
|
38
56
|
let open = $state(false)
|
|
39
57
|
let query = $state('')
|
|
40
58
|
let active = $state(0)
|
|
@@ -89,10 +107,11 @@ export function createCountryInput(config: CountryInputConfig) {
|
|
|
89
107
|
onKeydown,
|
|
90
108
|
/** Spread onto the trigger <button>. */
|
|
91
109
|
triggerProps: () => ({
|
|
110
|
+
id: config.id?.(),
|
|
92
111
|
type: 'button' as const,
|
|
93
112
|
'aria-haspopup': 'listbox' as const,
|
|
94
113
|
'aria-expanded': open,
|
|
95
|
-
|
|
114
|
+
...editorAria(ariaState()),
|
|
96
115
|
disabled: disabled(),
|
|
97
116
|
onclick: toggle,
|
|
98
117
|
}),
|
|
@@ -100,7 +119,7 @@ export function createCountryInput(config: CountryInputConfig) {
|
|
|
100
119
|
searchProps: () => ({
|
|
101
120
|
type: 'text' as const,
|
|
102
121
|
value: query,
|
|
103
|
-
'aria-label': 'Search countries',
|
|
122
|
+
'aria-label': config.searchLabel?.() ?? 'Search countries',
|
|
104
123
|
oninput: onSearchInput,
|
|
105
124
|
onkeydown: onKeydown,
|
|
106
125
|
}),
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { formatDate, parseDate } from './datetime/date-format'
|
|
19
19
|
import { toDate, clampDate, withTime, startOfDay, type DateLike } from './datetime/date-core'
|
|
20
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
20
21
|
|
|
21
22
|
export type DateTimeValue = Date | string | number | null
|
|
22
23
|
export type DropDownDisplayMode = 'both' | 'calendar' | 'time'
|
|
@@ -40,6 +41,17 @@ export type DateTimePickerConfig = {
|
|
|
40
41
|
stepMinutes?: () => number
|
|
41
42
|
disabled?: () => boolean
|
|
42
43
|
readonly?: () => boolean
|
|
44
|
+
/** Accessible label for the open-picker toggle (localizable). */
|
|
45
|
+
toggleLabel?: () => string | undefined
|
|
46
|
+
/** Accessible label for the clear button (localizable). */
|
|
47
|
+
clearLabel?: () => string | undefined
|
|
48
|
+
// Editor contract (ARIA + validation) - folded into inputProps().
|
|
49
|
+
id?: () => string | undefined
|
|
50
|
+
invalid?: () => boolean
|
|
51
|
+
required?: () => boolean
|
|
52
|
+
error?: () => string | undefined
|
|
53
|
+
hint?: () => string | undefined
|
|
54
|
+
ariaLabel?: () => string | undefined
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
const defaultLocale = () => (typeof navigator !== 'undefined' ? navigator.language : 'en-US')
|
|
@@ -164,9 +176,18 @@ export function createDateTimePicker(config: DateTimePickerConfig) {
|
|
|
164
176
|
* alongside `bind:value={dtp.text}`. */
|
|
165
177
|
inputProps() {
|
|
166
178
|
return {
|
|
179
|
+
id: config.id?.(),
|
|
167
180
|
type: 'text' as const,
|
|
168
181
|
disabled: isDisabled(),
|
|
169
182
|
readonly: isReadonly(),
|
|
183
|
+
...editorAria({
|
|
184
|
+
id: config.id?.(),
|
|
185
|
+
invalid: config.invalid?.(),
|
|
186
|
+
required: config.required?.(),
|
|
187
|
+
error: config.error?.(),
|
|
188
|
+
hint: config.hint?.(),
|
|
189
|
+
ariaLabel: config.ariaLabel?.(),
|
|
190
|
+
} satisfies EditorAriaState),
|
|
170
191
|
onblur: commitFromText,
|
|
171
192
|
onkeydown: onInputKeydown,
|
|
172
193
|
}
|
|
@@ -175,7 +196,7 @@ export function createDateTimePicker(config: DateTimePickerConfig) {
|
|
|
175
196
|
toggleProps() {
|
|
176
197
|
return {
|
|
177
198
|
type: 'button' as const,
|
|
178
|
-
'aria-label': 'Open picker',
|
|
199
|
+
'aria-label': config.toggleLabel?.() ?? 'Open picker',
|
|
179
200
|
'aria-haspopup': 'dialog' as const,
|
|
180
201
|
'aria-expanded': open,
|
|
181
202
|
disabled: !isInteractive,
|
|
@@ -187,7 +208,7 @@ export function createDateTimePicker(config: DateTimePickerConfig) {
|
|
|
187
208
|
clearProps() {
|
|
188
209
|
return {
|
|
189
210
|
type: 'button' as const,
|
|
190
|
-
'aria-label': 'Clear',
|
|
211
|
+
'aria-label': config.clearLabel?.() ?? 'Clear',
|
|
191
212
|
onclick: () => commit(null),
|
|
192
213
|
}
|
|
193
214
|
},
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* {/if}
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
import type
|
|
22
|
+
import { createTypeaheadBuffer, isTypeaheadKey, nextTypeaheadIndex, type ListOption } from './list-option'
|
|
23
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
23
24
|
|
|
24
25
|
export type DropdownValue = string | number | null
|
|
25
26
|
|
|
@@ -32,12 +33,27 @@ export type DropdownListConfig = {
|
|
|
32
33
|
ariaLabel?: () => string | undefined
|
|
33
34
|
/** DOM focus hook provided by the renderer; the core never touches the DOM. */
|
|
34
35
|
focusTrigger?: () => void
|
|
36
|
+
// Editor contract (ARIA + validation) - folded into triggerProps().
|
|
37
|
+
id?: () => string | undefined
|
|
38
|
+
invalid?: () => boolean
|
|
39
|
+
required?: () => boolean
|
|
40
|
+
error?: () => string | undefined
|
|
41
|
+
hint?: () => string | undefined
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
export function createDropdownList(config: DropdownListConfig) {
|
|
38
45
|
const opts = () => config.options()
|
|
39
46
|
const disabled = () => config.disabled?.() ?? false
|
|
40
47
|
|
|
48
|
+
const ariaState = (): EditorAriaState => ({
|
|
49
|
+
id: config.id?.(),
|
|
50
|
+
invalid: config.invalid?.(),
|
|
51
|
+
required: config.required?.(),
|
|
52
|
+
error: config.error?.(),
|
|
53
|
+
hint: config.hint?.(),
|
|
54
|
+
ariaLabel: config.ariaLabel?.(),
|
|
55
|
+
})
|
|
56
|
+
|
|
41
57
|
let open = $state(false)
|
|
42
58
|
let active = $state(-1)
|
|
43
59
|
|
|
@@ -61,8 +77,20 @@ export function createDropdownList(config: DropdownListConfig) {
|
|
|
61
77
|
const pos = enabledIdx.indexOf(active)
|
|
62
78
|
active = enabledIdx[(pos + d + enabledIdx.length) % enabledIdx.length] ?? active
|
|
63
79
|
}
|
|
80
|
+
// Type-ahead: typing a label prefix jumps the active option (and selects it
|
|
81
|
+
// when the list is closed, matching a native <select>).
|
|
82
|
+
const typeahead = createTypeaheadBuffer()
|
|
83
|
+
function onTypeahead(char: string) {
|
|
84
|
+
const buffer = typeahead.push(char.toLowerCase())
|
|
85
|
+
const from = active >= 0 ? active : opts().findIndex((o) => o.value === config.value())
|
|
86
|
+
const next = nextTypeaheadIndex(opts(), buffer, from)
|
|
87
|
+
if (next < 0) return
|
|
88
|
+
active = next
|
|
89
|
+
if (!open) config.onChange?.(opts()[next]!.value)
|
|
90
|
+
}
|
|
64
91
|
function onKeydown(e: KeyboardEvent) {
|
|
65
92
|
if (disabled()) return
|
|
93
|
+
if (isTypeaheadKey(e)) { onTypeahead(e.key); return }
|
|
66
94
|
if (!open && (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); openPanel(); return }
|
|
67
95
|
if (!open) return
|
|
68
96
|
if (e.key === 'ArrowDown') { e.preventDefault(); move(1) }
|
|
@@ -93,10 +121,11 @@ export function createDropdownList(config: DropdownListConfig) {
|
|
|
93
121
|
onKeydown,
|
|
94
122
|
/** Spread onto the trigger <button>. */
|
|
95
123
|
triggerProps: () => ({
|
|
124
|
+
id: config.id?.(),
|
|
96
125
|
type: 'button' as const,
|
|
97
126
|
'aria-haspopup': 'listbox' as const,
|
|
98
127
|
'aria-expanded': open,
|
|
99
|
-
|
|
128
|
+
...editorAria(ariaState()),
|
|
100
129
|
disabled: disabled(),
|
|
101
130
|
onclick: toggle,
|
|
102
131
|
onkeydown: onKeydown,
|
|
@@ -0,0 +1,144 @@
|
|
|
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, type EditorAriaState } from './editor-contract'
|
|
19
|
+
|
|
20
|
+
export type FileRejectReason = 'type' | 'size' | 'count'
|
|
21
|
+
export type FileRejection = { file: File; reason: FileRejectReason }
|
|
22
|
+
|
|
23
|
+
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
24
|
+
export type FileUploadConfig = {
|
|
25
|
+
files: () => File[]
|
|
26
|
+
onChange?: (files: File[]) => void
|
|
27
|
+
/** Rejected files (wrong type / too big / over the count cap). */
|
|
28
|
+
onReject?: (rejections: FileRejection[]) => void
|
|
29
|
+
/** Comma list like "image/*,.pdf" (matches MIME or extension). */
|
|
30
|
+
accept?: () => string | undefined
|
|
31
|
+
multiple?: () => boolean
|
|
32
|
+
/** Max size per file in bytes. */
|
|
33
|
+
maxSize?: () => number | undefined
|
|
34
|
+
/** Max number of files (multiple mode). */
|
|
35
|
+
maxFiles?: () => number | undefined
|
|
36
|
+
disabled?: () => boolean
|
|
37
|
+
readonly?: () => boolean
|
|
38
|
+
// Editor contract (ARIA + validation) - folded into dropzoneProps().
|
|
39
|
+
id?: () => string | undefined
|
|
40
|
+
invalid?: () => boolean
|
|
41
|
+
required?: () => boolean
|
|
42
|
+
error?: () => string | undefined
|
|
43
|
+
hint?: () => string | undefined
|
|
44
|
+
ariaLabel?: () => string | undefined
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Whether `file` satisfies an `accept` list (MIME globs or extensions). Pure. */
|
|
48
|
+
export function fileMatchesAccept(file: File, accept?: string): boolean {
|
|
49
|
+
if (!accept || !accept.trim()) return true
|
|
50
|
+
const name = file.name.toLowerCase()
|
|
51
|
+
const type = file.type.toLowerCase()
|
|
52
|
+
return accept.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean).some((tok) => {
|
|
53
|
+
if (tok.startsWith('.')) return name.endsWith(tok)
|
|
54
|
+
if (tok.endsWith('/*')) return type.startsWith(tok.slice(0, -1))
|
|
55
|
+
return type === tok
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createFileUpload(config: FileUploadConfig) {
|
|
60
|
+
const accept = () => config.accept?.()
|
|
61
|
+
const multiple = () => config.multiple?.() ?? false
|
|
62
|
+
const maxSize = () => config.maxSize?.()
|
|
63
|
+
const maxFiles = () => config.maxFiles?.()
|
|
64
|
+
const disabled = () => config.disabled?.() ?? false
|
|
65
|
+
const readonly = () => config.readonly?.() ?? false
|
|
66
|
+
const isInteractive = $derived(!disabled() && !readonly())
|
|
67
|
+
|
|
68
|
+
let dragging = $state(false)
|
|
69
|
+
|
|
70
|
+
function addFiles(incoming: File[]) {
|
|
71
|
+
if (!isInteractive || !incoming.length) return
|
|
72
|
+
const accepted: File[] = []
|
|
73
|
+
const rejected: FileRejection[] = []
|
|
74
|
+
for (const f of incoming) {
|
|
75
|
+
if (!fileMatchesAccept(f, accept())) { rejected.push({ file: f, reason: 'type' }); continue }
|
|
76
|
+
const cap = maxSize()
|
|
77
|
+
if (cap != null && f.size > cap) { rejected.push({ file: f, reason: 'size' }); continue }
|
|
78
|
+
accepted.push(f)
|
|
79
|
+
}
|
|
80
|
+
let next = multiple() ? [...config.files(), ...accepted] : accepted.slice(-1)
|
|
81
|
+
const limit = maxFiles()
|
|
82
|
+
if (multiple() && limit != null && next.length > limit) {
|
|
83
|
+
for (const f of next.slice(limit)) rejected.push({ file: f, reason: 'count' })
|
|
84
|
+
next = next.slice(0, limit)
|
|
85
|
+
}
|
|
86
|
+
if (rejected.length) config.onReject?.(rejected)
|
|
87
|
+
config.onChange?.(next)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function removeAt(index: number) {
|
|
91
|
+
if (!isInteractive) return
|
|
92
|
+
config.onChange?.(config.files().filter((_, i) => i !== index))
|
|
93
|
+
}
|
|
94
|
+
function clear() { if (isInteractive) config.onChange?.([]) }
|
|
95
|
+
|
|
96
|
+
const setDragging = (v: boolean) => { if (isInteractive) dragging = v }
|
|
97
|
+
|
|
98
|
+
const ariaState = (): EditorAriaState => ({
|
|
99
|
+
id: config.id?.(),
|
|
100
|
+
invalid: config.invalid?.(),
|
|
101
|
+
required: config.required?.(),
|
|
102
|
+
error: config.error?.(),
|
|
103
|
+
hint: config.hint?.(),
|
|
104
|
+
ariaLabel: config.ariaLabel?.(),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
/** Whether files are being dragged over the dropzone. */
|
|
109
|
+
get dragging() { return dragging },
|
|
110
|
+
/** Whether interaction is allowed. */
|
|
111
|
+
get isInteractive() { return isInteractive },
|
|
112
|
+
/** Selected files (controlled value). */
|
|
113
|
+
get files() { return config.files() },
|
|
114
|
+
addFiles,
|
|
115
|
+
removeAt,
|
|
116
|
+
clear,
|
|
117
|
+
setDragging,
|
|
118
|
+
/** Spread onto the dropzone element (click opens the picker; Enter/Space too).
|
|
119
|
+
* Wire drag events yourself (they carry a DataTransfer): call
|
|
120
|
+
* `setDragging(true/false)` and `addFiles([...e.dataTransfer.files])`. */
|
|
121
|
+
dropzoneProps: () => ({
|
|
122
|
+
role: 'button' as const,
|
|
123
|
+
tabindex: isInteractive ? 0 : -1,
|
|
124
|
+
'data-dragging': dragging ? '' : undefined,
|
|
125
|
+
'aria-disabled': !isInteractive || undefined,
|
|
126
|
+
...editorAria(ariaState()),
|
|
127
|
+
}),
|
|
128
|
+
/** Spread onto the visually-hidden <input type="file">. */
|
|
129
|
+
inputProps: () => ({
|
|
130
|
+
type: 'file' as const,
|
|
131
|
+
accept: accept(),
|
|
132
|
+
multiple: multiple(),
|
|
133
|
+
disabled: !isInteractive,
|
|
134
|
+
tabindex: -1,
|
|
135
|
+
}),
|
|
136
|
+
/** Spread onto a selected file's remove <button> at `index`. */
|
|
137
|
+
removeProps: (index: number) => ({
|
|
138
|
+
type: 'button' as const,
|
|
139
|
+
onclick: () => removeAt(index),
|
|
140
|
+
}),
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type FileUpload = ReturnType<typeof createFileUpload>
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
*
|
|
23
23
|
* The styled <SvListBox> is just one renderer over this core.
|
|
24
24
|
*/
|
|
25
|
-
import type
|
|
25
|
+
import { createTypeaheadBuffer, isTypeaheadKey, nextTypeaheadIndex, type ListOption } from './list-option'
|
|
26
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
26
27
|
|
|
27
28
|
export type ListboxValue = string | number | Array<string | number> | null
|
|
28
29
|
|
|
@@ -35,6 +36,12 @@ export type ListboxConfig = {
|
|
|
35
36
|
multiple?: () => boolean
|
|
36
37
|
disabled?: () => boolean
|
|
37
38
|
ariaLabel?: () => string | undefined
|
|
39
|
+
// Editor contract (ARIA + validation) - folded into rootProps().
|
|
40
|
+
id?: () => string | undefined
|
|
41
|
+
invalid?: () => boolean
|
|
42
|
+
required?: () => boolean
|
|
43
|
+
error?: () => string | undefined
|
|
44
|
+
hint?: () => string | undefined
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
export type OptionProps = {
|
|
@@ -51,10 +58,14 @@ export type OptionProps = {
|
|
|
51
58
|
|
|
52
59
|
export type ListboxRootProps = {
|
|
53
60
|
role: 'listbox'
|
|
61
|
+
id: string | undefined
|
|
54
62
|
'aria-multiselectable': boolean
|
|
55
63
|
'aria-label': string | undefined
|
|
56
64
|
'aria-disabled': boolean
|
|
57
65
|
'aria-activedescendant': string | undefined
|
|
66
|
+
'aria-invalid': 'true' | undefined
|
|
67
|
+
'aria-required': 'true' | undefined
|
|
68
|
+
'aria-describedby': string | undefined
|
|
58
69
|
tabindex: number
|
|
59
70
|
onkeydown: (e: KeyboardEvent) => void
|
|
60
71
|
}
|
|
@@ -136,6 +147,14 @@ export function createListbox(config: ListboxConfig): Listbox {
|
|
|
136
147
|
const first = () => { active = enabledIdx[0] ?? 0 }
|
|
137
148
|
const last = () => { active = enabledIdx.at(-1) ?? 0 }
|
|
138
149
|
|
|
150
|
+
// Type-ahead: typing a label prefix jumps the active highlight to it.
|
|
151
|
+
const typeahead = createTypeaheadBuffer()
|
|
152
|
+
function onTypeahead(char: string) {
|
|
153
|
+
const buffer = typeahead.push(char.toLowerCase())
|
|
154
|
+
const next = nextTypeaheadIndex(opts(), buffer, active)
|
|
155
|
+
if (next >= 0) active = next
|
|
156
|
+
}
|
|
157
|
+
|
|
139
158
|
function onKeydown(e: KeyboardEvent) {
|
|
140
159
|
if (disabled()) return
|
|
141
160
|
switch (e.key) {
|
|
@@ -145,9 +164,20 @@ export function createListbox(config: ListboxConfig): Listbox {
|
|
|
145
164
|
case 'End': e.preventDefault(); last(); break
|
|
146
165
|
case ' ':
|
|
147
166
|
case 'Enter': e.preventDefault(); pick(active); break
|
|
167
|
+
default: if (isTypeaheadKey(e)) onTypeahead(e.key)
|
|
148
168
|
}
|
|
149
169
|
}
|
|
150
170
|
|
|
171
|
+
// Editor-contract ARIA state, folded onto the focusable listbox root.
|
|
172
|
+
const ariaState = (): EditorAriaState => ({
|
|
173
|
+
id: config.id?.(),
|
|
174
|
+
invalid: config.invalid?.(),
|
|
175
|
+
required: config.required?.(),
|
|
176
|
+
error: config.error?.(),
|
|
177
|
+
hint: config.hint?.(),
|
|
178
|
+
ariaLabel: config.ariaLabel?.(),
|
|
179
|
+
})
|
|
180
|
+
|
|
151
181
|
return {
|
|
152
182
|
get activeIndex() { return active },
|
|
153
183
|
get selectedValues() { return selected },
|
|
@@ -160,11 +190,13 @@ export function createListbox(config: ListboxConfig): Listbox {
|
|
|
160
190
|
last,
|
|
161
191
|
onKeydown,
|
|
162
192
|
rootProps: () => ({
|
|
163
|
-
role: 'listbox',
|
|
193
|
+
role: 'listbox' as const,
|
|
194
|
+
id: config.id?.(),
|
|
164
195
|
'aria-multiselectable': multiple(),
|
|
165
|
-
'aria-label': config.ariaLabel?.(),
|
|
166
196
|
'aria-disabled': disabled(),
|
|
167
197
|
'aria-activedescendant': opts()[active] ? optionId(active) : undefined,
|
|
198
|
+
// aria-label + aria-invalid/required/describedby from the editor contract.
|
|
199
|
+
...editorAria(ariaState()),
|
|
168
200
|
tabindex: disabled() ? -1 : 0,
|
|
169
201
|
onkeydown: onKeydown,
|
|
170
202
|
}),
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* The styled <SvMaskedInput> is just one renderer over this core.
|
|
17
17
|
*/
|
|
18
18
|
import { applyMask, unmask, isMaskComplete } from './datetime/mask'
|
|
19
|
+
import { editorAria, type EditorAriaState } from './editor-contract'
|
|
19
20
|
|
|
20
21
|
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
21
22
|
export type MaskedInputConfig = {
|
|
@@ -26,6 +27,12 @@ export type MaskedInputConfig = {
|
|
|
26
27
|
placeholder?: () => string | undefined
|
|
27
28
|
disabled?: () => boolean
|
|
28
29
|
readonly?: () => boolean
|
|
30
|
+
// Editor contract (ARIA + validation) - folded into inputProps().
|
|
31
|
+
id?: () => string | undefined
|
|
32
|
+
invalid?: () => boolean
|
|
33
|
+
required?: () => boolean
|
|
34
|
+
error?: () => string | undefined
|
|
35
|
+
hint?: () => string | undefined
|
|
29
36
|
ariaLabel?: () => string | undefined
|
|
30
37
|
}
|
|
31
38
|
|
|
@@ -58,6 +65,15 @@ export function createMaskedInput(config: MaskedInputConfig) {
|
|
|
58
65
|
config.onChange?.(masked, raw, m ? isMaskComplete(masked, m) : true)
|
|
59
66
|
}
|
|
60
67
|
|
|
68
|
+
const ariaState = (): EditorAriaState => ({
|
|
69
|
+
id: config.id?.(),
|
|
70
|
+
invalid: config.invalid?.(),
|
|
71
|
+
required: config.required?.(),
|
|
72
|
+
error: config.error?.(),
|
|
73
|
+
hint: config.hint?.(),
|
|
74
|
+
ariaLabel: config.ariaLabel?.(),
|
|
75
|
+
})
|
|
76
|
+
|
|
61
77
|
return {
|
|
62
78
|
/** The current masked display text. */
|
|
63
79
|
get masked() { return text },
|
|
@@ -67,12 +83,13 @@ export function createMaskedInput(config: MaskedInputConfig) {
|
|
|
67
83
|
get complete() { return mask() ? isMaskComplete(text, mask()) : true },
|
|
68
84
|
/** Spread onto the text input element. */
|
|
69
85
|
inputProps: () => ({
|
|
86
|
+
id: config.id?.(),
|
|
70
87
|
value: text,
|
|
71
88
|
type: 'text' as const,
|
|
72
89
|
placeholder: config.placeholder?.() ?? mask(),
|
|
73
90
|
disabled: disabled(),
|
|
74
91
|
readonly: readonly(),
|
|
75
|
-
|
|
92
|
+
...editorAria(ariaState()),
|
|
76
93
|
oninput: onInput,
|
|
77
94
|
}),
|
|
78
95
|
}
|
|
@@ -39,6 +39,7 @@ export type NumberInputConfig = {
|
|
|
39
39
|
invalid?: () => boolean
|
|
40
40
|
required?: () => boolean
|
|
41
41
|
error?: () => string | undefined
|
|
42
|
+
hint?: () => string | undefined
|
|
42
43
|
ariaLabel?: () => string | undefined
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -138,6 +139,7 @@ export function createNumberInput(config: NumberInputConfig) {
|
|
|
138
139
|
invalid: config.invalid?.(),
|
|
139
140
|
required: config.required?.(),
|
|
140
141
|
error: config.error?.(),
|
|
142
|
+
hint: config.hint?.(),
|
|
141
143
|
ariaLabel: config.ariaLabel?.(),
|
|
142
144
|
})
|
|
143
145
|
|