@svgrid/grid 2.6.3 → 2.6.4
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 +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +178 -178
- package/dist/GridMenus.svelte +705 -705
- package/dist/SvAutoComplete.svelte +165 -165
- package/dist/SvAvatar.svelte +75 -75
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +102 -102
- package/dist/SvCircularProgress.svelte +109 -109
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +275 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +374 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.controller.svelte.d.ts +2 -1
- package/dist/SvGrid.controller.svelte.js +145 -3
- package/dist/SvGrid.css +2846 -2814
- package/dist/SvGrid.svelte +3548 -3490
- package/dist/SvGrid.types.d.ts +61 -0
- package/dist/SvGridChart.svelte +1716 -1716
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +266 -266
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +326 -326
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +289 -289
- package/dist/SvNumberInput.svelte +168 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +203 -203
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +115 -115
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +235 -235
- package/dist/a11y/grid-announcements.d.ts +58 -0
- package/dist/a11y/grid-announcements.js +70 -0
- package/dist/advanced-filter.svelte.d.ts +42 -0
- package/dist/advanced-filter.svelte.js +13 -0
- package/dist/build-api.js +44 -2
- package/dist/cdn/{GridMenus-DL1zrDwf.js → GridMenus-1NLNSqWz.js} +15 -15
- package/dist/cdn/{GridMenus-BoVKgAv8.js → GridMenus-B2Dt25-8.js} +15 -15
- package/dist/cdn/{SvDateTimePicker-CHnUkWcH.js → SvDateTimePicker-DSUhIta8.js} +2 -2
- package/dist/cdn/{SvDateTimePicker-CRQH_U7E.js → SvDateTimePicker-h9kPyszT.js} +2 -2
- package/dist/cdn/{SvGridChart-BwVos976.js → SvGridChart-Bs2GIR2Q.js} +1 -1
- package/dist/cdn/{SvGridChart-BEJmNNx9.js → SvGridChart-CwhFz7GV.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-BOknOkrP.js → SvGridChartPanel-1WXuStzM.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-D2PjII4O.js → SvGridChartPanel-e5_nqNP9.js} +1 -1
- package/dist/cdn/{SvGridChartView-00LPUHL1.js → SvGridChartView-SmPW10dI.js} +1 -1
- package/dist/cdn/{SvGridChartView-BhUEJ5ki.js → SvGridChartView-eSPuJE6g.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-B1ZcLpgs.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D13MtJ2j.js → SvGridDropdown-CtdbHcIS.js} +1 -1
- package/dist/cdn/{src-M2NfKnjX.js → src-D35VGBU5.js} +5954 -5765
- package/dist/cdn/{src-CEpw6Ato.js → src-D7DLIAPD.js} +3396 -3207
- package/dist/cdn/svgrid.js +9 -9
- package/dist/cdn/svgrid.svelte-external.js +9 -9
- package/dist/chart-export.js +8 -8
- package/dist/filter-operators.d.ts +2 -1
- package/dist/filter-operators.js +5 -6
- package/dist/filtering/excel-filters.d.ts +1 -0
- package/dist/filtering/excel-filters.js +5 -0
- package/dist/filtering/filter-operator-catalogue.d.ts +42 -0
- package/dist/filtering/filter-operator-catalogue.js +42 -0
- package/dist/filtering/predicate-expr.d.ts +78 -0
- package/dist/filtering/predicate-expr.js +1 -0
- package/dist/filtering/row-predicate.d.ts +17 -0
- package/dist/filtering/row-predicate.js +55 -0
- package/dist/grid-messages.d.ts +7 -0
- package/dist/grid-messages.js +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/_svelte_metadata.json +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/_metadata.json +178 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js +3843 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js +23 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js +1925 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js +206 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js +20 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js +264 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js +698 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/package.json +3 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js +135 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js +57 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_attachments.js +2 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js +46691 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js +239 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_events.js +2 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js +6 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_client.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js +6 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_server.js +6 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js +85 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js +561 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js +26 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js +124 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_server.js +2 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js +91 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js +242 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js +497 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js +4929 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js +192 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js +10 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js +140 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js.map +1 -0
- package/dist/server-data-source.d.ts +34 -0
- package/dist/server-data-source.js +19 -0
- package/dist/svgrid-wrapper.types.d.ts +37 -5
- package/dist/themes/index.js +12 -4
- package/package.json +12 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +178 -178
- package/src/GridMenus.svelte +705 -705
- package/src/SvAutoComplete.svelte +165 -165
- package/src/SvAvatar.svelte +75 -75
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +102 -102
- package/src/SvCircularProgress.svelte +109 -109
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +275 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +374 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +3858 -3691
- package/src/SvGrid.css +2846 -2814
- package/src/SvGrid.svelte +3548 -3490
- package/src/SvGrid.types.ts +1769 -1708
- package/src/SvGridChart.svelte +1716 -1716
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +266 -266
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +326 -326
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +289 -289
- package/src/SvNumberInput.svelte +168 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +203 -203
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +115 -115
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +235 -235
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y/grid-announcements.test.ts +78 -0
- package/src/a11y/grid-announcements.ts +94 -0
- package/src/a11y.announce.test.ts +250 -0
- package/src/a11y.axe.test.ts +194 -0
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/advanced-filter.svelte.ts +59 -0
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1391 -1391
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +846 -805
- package/src/builtin-editors.grid.test.ts +85 -85
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +118 -118
- package/src/chart-export.ts +201 -201
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2295 -2295
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1261 -1261
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- 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 +250 -250
- package/src/createMenu.svelte.ts +224 -224
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +319 -319
- package/src/editing.test.ts +949 -949
- package/src/editing.ts +606 -606
- package/src/editor-contract.ts +170 -170
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +601 -601
- package/src/filter-operators.ts +160 -157
- package/src/filtering/excel-filters.ts +15 -0
- package/src/filtering/filter-operator-catalogue.ts +74 -0
- package/src/filtering/node-entry.test.ts +75 -0
- package/src/filtering/predicate-expr.ts +50 -0
- package/src/filtering/row-predicate.ts +57 -0
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/grid-messages.ts +18 -0
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/index.ts +754 -726
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard-handlers.ts +270 -270
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +172 -172
- package/src/menus.ts +567 -567
- package/src/merge-objects.ts +48 -48
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/_svelte_metadata.json +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/_metadata.json +178 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js +3843 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js +23 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js +1925 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js +206 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js +20 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js +264 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js +698 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/package.json +3 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js +135 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js +57 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_attachments.js +2 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js +46691 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js +239 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_events.js +2 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js +6 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_client.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js +6 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_server.js +6 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js +85 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js +561 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js +26 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js +124 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_server.js +2 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js +91 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js +242 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js +497 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js +4929 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js +192 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js +10 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js +140 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js.map +1 -0
- package/src/overlays.test.ts +90 -90
- package/src/render-component.ts +28 -28
- package/src/row-resize.ts +210 -210
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +872 -872
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +491 -491
- package/src/server-data-source.test.ts +383 -289
- package/src/server-data-source.ts +469 -413
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +488 -488
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +558 -526
- package/src/svgrid.advanced-filter.test.ts +481 -0
- package/src/svgrid.async-editor-options.test.ts +271 -271
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +705 -705
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +123 -123
- package/src/svgrid.context-menu.test.ts +147 -147
- package/src/svgrid.displayed-rows.test.ts +144 -0
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +331 -331
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.grand-total.test.ts +188 -188
- package/src/svgrid.group-display-mode.test.ts +171 -171
- package/src/svgrid.group-footers.test.ts +121 -121
- package/src/svgrid.group-pagination.test.ts +153 -153
- package/src/svgrid.group-props-reactive.svelte.test.ts +151 -0
- package/src/svgrid.group-props.test.ts +280 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.wrapper.test.ts +63 -63
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/contrast.test.ts +112 -0
- package/src/themes/index.ts +286 -278
- package/src/toast-store.svelte.ts +246 -246
- package/src/toast-store.test.ts +147 -147
- package/src/tree-row-model.test.ts +168 -168
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-localization.test.ts +113 -113
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
- package/themes/catppuccin.css +3 -3
- package/themes/dracula.css +3 -3
- package/themes/nord.css +3 -3
- package/themes/notion.css +4 -4
package/dist/SvTreeSelect.svelte
CHANGED
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
export type { TreeSelectNode } from './ui-app.types'
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<script lang="ts">
|
|
6
|
-
/**
|
|
7
|
-
* SvTreeSelect - a single-select dropdown that shows an indented, collapsible
|
|
8
|
-
* tree in its panel (pick a node from a hierarchy). The open / position /
|
|
9
|
-
* dismiss / roving / keyboard / ARIA mechanics come from the shared
|
|
10
|
-
* `createPopoverSelect` core (with ArrowLeft/Right handled via onExtraKey for
|
|
11
|
-
* expand/collapse); this component adds the tree flattening + rendering. Covers
|
|
12
|
-
* the "cascader / tree select" pattern without depending on SvTree.
|
|
13
|
-
*/
|
|
14
|
-
import type { TreeSelectNode } from './ui-app.types'
|
|
15
|
-
import SvField from './SvField.svelte'
|
|
16
|
-
import { editorAria, nextEditorId, type SvEditorProps } from './editor-contract'
|
|
17
|
-
import { portalToBody, popIn } from './popover'
|
|
18
|
-
import { createPopoverSelect } from './createPopoverSelect.svelte'
|
|
19
|
-
import { flattenVisible, findNodePath } from './tree-select'
|
|
20
|
-
|
|
21
|
-
type Props = Pick<
|
|
22
|
-
SvEditorProps,
|
|
23
|
-
'disabled' | 'readonly' | 'label' | 'hint' | 'error' | 'required' | 'invalid' | 'size' | 'id' | 'name' | 'dir' | 'ariaLabel' | 'loading'
|
|
24
|
-
> & {
|
|
25
|
-
nodes: ReadonlyArray<TreeSelectNode>
|
|
26
|
-
value?: string | number | null
|
|
27
|
-
onChange?: (value: string | number) => void
|
|
28
|
-
onCommit?: (value: string | number) => void
|
|
29
|
-
onCancel?: () => void
|
|
30
|
-
placeholder?: string
|
|
31
|
-
showPath?: boolean
|
|
32
|
-
expandedIds?: ReadonlyArray<string | number>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let {
|
|
36
|
-
nodes,
|
|
37
|
-
value = $bindable<string | number | null>(null),
|
|
38
|
-
onChange,
|
|
39
|
-
placeholder = 'Select…',
|
|
40
|
-
showPath = false,
|
|
41
|
-
expandedIds = [],
|
|
42
|
-
disabled = false,
|
|
43
|
-
readonly = false,
|
|
44
|
-
label,
|
|
45
|
-
hint,
|
|
46
|
-
error,
|
|
47
|
-
required = false,
|
|
48
|
-
invalid = false,
|
|
49
|
-
size = 'md',
|
|
50
|
-
id,
|
|
51
|
-
name,
|
|
52
|
-
dir,
|
|
53
|
-
ariaLabel,
|
|
54
|
-
loading = false,
|
|
55
|
-
}: Props = $props()
|
|
56
|
-
|
|
57
|
-
const autoId = nextEditorId('sv-ts')
|
|
58
|
-
const uid = $derived(id ?? autoId)
|
|
59
|
-
|
|
60
|
-
let expanded = $state(new Set<string | number>(expandedIds))
|
|
61
|
-
let triggerEl = $state<HTMLButtonElement | null>(null)
|
|
62
|
-
let panelEl = $state<HTMLDivElement | null>(null)
|
|
63
|
-
let treeEl = $state<HTMLUListElement | null>(null)
|
|
64
|
-
|
|
65
|
-
const rows = $derived(flattenVisible(nodes, expanded))
|
|
66
|
-
const selectedPath = $derived(value == null ? null : findNodePath(nodes, value))
|
|
67
|
-
const triggerText = $derived(
|
|
68
|
-
!selectedPath
|
|
69
|
-
? ''
|
|
70
|
-
: showPath
|
|
71
|
-
? selectedPath.map((n) => n.label).join(' / ')
|
|
72
|
-
: selectedPath[selectedPath.length - 1]!.label,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
function setExpanded(v: string | number, on: boolean) {
|
|
76
|
-
const next = new Set(expanded)
|
|
77
|
-
on ? next.add(v) : next.delete(v)
|
|
78
|
-
expanded = next
|
|
79
|
-
}
|
|
80
|
-
function toggleExpand(v: string | number) {
|
|
81
|
-
setExpanded(v, !expanded.has(v))
|
|
82
|
-
}
|
|
83
|
-
function pick(node: TreeSelectNode) {
|
|
84
|
-
if (node.disabled) return
|
|
85
|
-
value = node.value // enables bind:value
|
|
86
|
-
onChange?.(node.value)
|
|
87
|
-
ts.closePanel()
|
|
88
|
-
}
|
|
89
|
-
function onRowClick(row: { node: TreeSelectNode; hasChildren: boolean }) {
|
|
90
|
-
if (row.hasChildren) toggleExpand(row.node.value)
|
|
91
|
-
else pick(row.node)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const ts = createPopoverSelect({
|
|
95
|
-
itemCount: () => rows.length,
|
|
96
|
-
disabled: (i) => !!rows[i]?.node.disabled,
|
|
97
|
-
readonly: () => readonly,
|
|
98
|
-
onSelect: (i) => { const row = rows[i]; if (row) onRowClick(row) },
|
|
99
|
-
getTrigger: () => triggerEl,
|
|
100
|
-
getPanel: () => panelEl,
|
|
101
|
-
getInitialFocus: () => treeEl,
|
|
102
|
-
closeOnSelect: () => false, // onRowClick handles pick + close (branches stay open)
|
|
103
|
-
initialActive: () => Math.max(0, rows.findIndex((r) => r.node.value === value)),
|
|
104
|
-
minWidth: () => 220,
|
|
105
|
-
estimatedHeight: () => 300,
|
|
106
|
-
onExtraKey: (e, act) => {
|
|
107
|
-
const row = rows[act]
|
|
108
|
-
if (!row) return false
|
|
109
|
-
// "expand" opens the node or steps into the first child; "collapse" closes
|
|
110
|
-
// it or jumps to the parent - same fallback as createTree's core.
|
|
111
|
-
const expand = () => {
|
|
112
|
-
if (row.hasChildren && !row.expanded) setExpanded(row.node.value, true)
|
|
113
|
-
else if (row.hasChildren && row.expanded) ts.active = Math.min(act + 1, rows.length - 1)
|
|
114
|
-
}
|
|
115
|
-
const collapse = () => {
|
|
116
|
-
if (row.hasChildren && row.expanded) setExpanded(row.node.value, false)
|
|
117
|
-
else {
|
|
118
|
-
let pi = act - 1
|
|
119
|
-
while (pi >= 0 && rows[pi]!.depth >= row.depth) pi--
|
|
120
|
-
if (pi >= 0) ts.active = pi
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (e.key === 'ArrowRight') { e.preventDefault(); expand(); return true }
|
|
124
|
-
if (e.key === 'ArrowLeft') { e.preventDefault(); collapse(); return true }
|
|
125
|
-
return false
|
|
126
|
-
},
|
|
127
|
-
idPrefix: 'sv-ts',
|
|
128
|
-
})
|
|
129
|
-
</script>
|
|
130
|
-
|
|
131
|
-
<SvField id={uid} {label} {hint} {error} {required} {dir} {loading}>
|
|
132
|
-
<div class="sv-ts sv-ts--{size}" class:is-disabled={disabled}>
|
|
133
|
-
<button
|
|
134
|
-
bind:this={triggerEl}
|
|
135
|
-
type="button"
|
|
136
|
-
class="sv-ts__trigger"
|
|
137
|
-
class:is-invalid={invalid}
|
|
138
|
-
{disabled}
|
|
139
|
-
onclick={() => ts.toggle()}
|
|
140
|
-
{...ts.triggerProps('tree')}
|
|
141
|
-
{...editorAria({ id: uid, invalid, required, error, hint, ariaLabel })}
|
|
142
|
-
>
|
|
143
|
-
<span class="sv-ts__value" class:is-placeholder={!triggerText}>{triggerText || placeholder}</span>
|
|
144
|
-
<span class="sv-ts__arrow" aria-hidden="true">▾</span>
|
|
145
|
-
</button>
|
|
146
|
-
{#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
|
|
147
|
-
</div>
|
|
148
|
-
</SvField>
|
|
149
|
-
|
|
150
|
-
{#if ts.open}
|
|
151
|
-
<div
|
|
152
|
-
bind:this={panelEl}
|
|
153
|
-
class="sv-ts__panel"
|
|
154
|
-
dir={dir}
|
|
155
|
-
use:portalToBody
|
|
156
|
-
use:popIn={{ up: ts.rect.openUpward }}
|
|
157
|
-
style:position="fixed"
|
|
158
|
-
style:top={`${ts.rect.top}px`}
|
|
159
|
-
style:left={`${ts.rect.left}px`}
|
|
160
|
-
style:min-width={`${ts.rect.width}px`}
|
|
161
|
-
>
|
|
162
|
-
<ul
|
|
163
|
-
bind:this={treeEl}
|
|
164
|
-
id={ts.panelId}
|
|
165
|
-
class="sv-ts__tree"
|
|
166
|
-
role="tree"
|
|
167
|
-
aria-label={label ?? ariaLabel ?? 'Tree'}
|
|
168
|
-
aria-activedescendant={ts.activeDescendant()}
|
|
169
|
-
tabindex="0"
|
|
170
|
-
style:max-height={`${ts.rect.maxHeight}px`}
|
|
171
|
-
onkeydown={ts.onPanelKeydown}
|
|
172
|
-
>
|
|
173
|
-
{#each rows as row, i (row.node.value)}
|
|
174
|
-
<li
|
|
175
|
-
class="sv-ts__row"
|
|
176
|
-
class:is-active={ts.isActive(i)}
|
|
177
|
-
class:is-selected={row.node.value === value}
|
|
178
|
-
class:is-disabled={row.node.disabled}
|
|
179
|
-
role="treeitem"
|
|
180
|
-
aria-selected={row.node.value === value}
|
|
181
|
-
aria-expanded={row.hasChildren ? row.expanded : undefined}
|
|
182
|
-
style:padding-inline-start={`${8 + row.depth * 16}px`}
|
|
183
|
-
onclick={() => onRowClick(row)}
|
|
184
|
-
{...ts.itemProps(i)}
|
|
185
|
-
>
|
|
186
|
-
{#if row.hasChildren}
|
|
187
|
-
<!-- Same chevron as SvTree and the grid's expanders. -->
|
|
188
|
-
<span class="sv-ts__chev" class:is-open={row.expanded} aria-hidden="true">
|
|
189
|
-
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor"
|
|
190
|
-
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>
|
|
191
|
-
</span>
|
|
192
|
-
{:else}
|
|
193
|
-
<span class="sv-ts__chev sv-ts__chev--leaf" aria-hidden="true"></span>
|
|
194
|
-
{/if}
|
|
195
|
-
<span class="sv-ts__label">{row.node.label}</span>
|
|
196
|
-
</li>
|
|
197
|
-
{/each}
|
|
198
|
-
</ul>
|
|
199
|
-
</div>
|
|
200
|
-
{/if}
|
|
201
|
-
|
|
202
|
-
<style>
|
|
203
|
-
.sv-ts { --_accent: var(--sg-accent, #2563eb); display: inline-flex; width: 240px; }
|
|
204
|
-
.sv-ts.is-disabled { opacity: 0.6; }
|
|
205
|
-
.sv-ts__trigger {
|
|
206
|
-
flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; text-align: start;
|
|
207
|
-
background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
208
|
-
border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
|
|
209
|
-
font: inherit; cursor: pointer; padding: 0 9px;
|
|
210
|
-
}
|
|
211
|
-
.sv-ts--sm .sv-ts__trigger { height: 28px; font-size: 12px; }
|
|
212
|
-
.sv-ts--md .sv-ts__trigger { height: 34px; font-size: 13px; }
|
|
213
|
-
.sv-ts--lg .sv-ts__trigger { height: 40px; font-size: 15px; }
|
|
214
|
-
.sv-ts__trigger:focus-visible { outline: none; border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
|
|
215
|
-
.sv-ts__trigger.is-invalid { border-color: var(--sg-danger, #dc2626); }
|
|
216
|
-
.sv-ts__value { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
217
|
-
.sv-ts__value.is-placeholder { color: var(--sg-muted, #94a3b8); }
|
|
218
|
-
.sv-ts__arrow { color: var(--sg-muted, #64748b); font-size: 10px; }
|
|
219
|
-
|
|
220
|
-
:global(.sv-ts__panel) {
|
|
221
|
-
z-index: 2147483646; max-height: 320px; max-width: 92vw; overflow: hidden;
|
|
222
|
-
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
223
|
-
border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px;
|
|
224
|
-
box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35); font-size: 13px;
|
|
225
|
-
}
|
|
226
|
-
:global(.sv-ts__tree) { list-style: none; margin: 0; padding: 4px; overflow: auto; max-height: 320px; outline: none; }
|
|
227
|
-
:global(.sv-ts__row) { display: flex; align-items: center; gap: 4px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
|
|
228
|
-
:global(.sv-ts__row.is-active) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
229
|
-
:global(.sv-ts__row.is-selected) { color: var(--sg-accent, #2563eb); font-weight: 600; }
|
|
230
|
-
:global(.sv-ts__row.is-disabled) { opacity: 0.5; cursor: not-allowed; }
|
|
231
|
-
:global(.sv-ts__chev) { width: 14px; flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--sg-muted, #64748b); transition: transform 0.12s; }
|
|
232
|
-
:global(.sv-ts__chev.is-open) { transform: rotate(90deg); }
|
|
233
|
-
:global(.sv-ts__chev--leaf) { visibility: hidden; }
|
|
234
|
-
:global(.sv-ts__label) { flex: 1; white-space: nowrap; }
|
|
235
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type { TreeSelectNode } from './ui-app.types'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* SvTreeSelect - a single-select dropdown that shows an indented, collapsible
|
|
8
|
+
* tree in its panel (pick a node from a hierarchy). The open / position /
|
|
9
|
+
* dismiss / roving / keyboard / ARIA mechanics come from the shared
|
|
10
|
+
* `createPopoverSelect` core (with ArrowLeft/Right handled via onExtraKey for
|
|
11
|
+
* expand/collapse); this component adds the tree flattening + rendering. Covers
|
|
12
|
+
* the "cascader / tree select" pattern without depending on SvTree.
|
|
13
|
+
*/
|
|
14
|
+
import type { TreeSelectNode } from './ui-app.types'
|
|
15
|
+
import SvField from './SvField.svelte'
|
|
16
|
+
import { editorAria, nextEditorId, type SvEditorProps } from './editor-contract'
|
|
17
|
+
import { portalToBody, popIn } from './popover'
|
|
18
|
+
import { createPopoverSelect } from './createPopoverSelect.svelte'
|
|
19
|
+
import { flattenVisible, findNodePath } from './tree-select'
|
|
20
|
+
|
|
21
|
+
type Props = Pick<
|
|
22
|
+
SvEditorProps,
|
|
23
|
+
'disabled' | 'readonly' | 'label' | 'hint' | 'error' | 'required' | 'invalid' | 'size' | 'id' | 'name' | 'dir' | 'ariaLabel' | 'loading'
|
|
24
|
+
> & {
|
|
25
|
+
nodes: ReadonlyArray<TreeSelectNode>
|
|
26
|
+
value?: string | number | null
|
|
27
|
+
onChange?: (value: string | number) => void
|
|
28
|
+
onCommit?: (value: string | number) => void
|
|
29
|
+
onCancel?: () => void
|
|
30
|
+
placeholder?: string
|
|
31
|
+
showPath?: boolean
|
|
32
|
+
expandedIds?: ReadonlyArray<string | number>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
nodes,
|
|
37
|
+
value = $bindable<string | number | null>(null),
|
|
38
|
+
onChange,
|
|
39
|
+
placeholder = 'Select…',
|
|
40
|
+
showPath = false,
|
|
41
|
+
expandedIds = [],
|
|
42
|
+
disabled = false,
|
|
43
|
+
readonly = false,
|
|
44
|
+
label,
|
|
45
|
+
hint,
|
|
46
|
+
error,
|
|
47
|
+
required = false,
|
|
48
|
+
invalid = false,
|
|
49
|
+
size = 'md',
|
|
50
|
+
id,
|
|
51
|
+
name,
|
|
52
|
+
dir,
|
|
53
|
+
ariaLabel,
|
|
54
|
+
loading = false,
|
|
55
|
+
}: Props = $props()
|
|
56
|
+
|
|
57
|
+
const autoId = nextEditorId('sv-ts')
|
|
58
|
+
const uid = $derived(id ?? autoId)
|
|
59
|
+
|
|
60
|
+
let expanded = $state(new Set<string | number>(expandedIds))
|
|
61
|
+
let triggerEl = $state<HTMLButtonElement | null>(null)
|
|
62
|
+
let panelEl = $state<HTMLDivElement | null>(null)
|
|
63
|
+
let treeEl = $state<HTMLUListElement | null>(null)
|
|
64
|
+
|
|
65
|
+
const rows = $derived(flattenVisible(nodes, expanded))
|
|
66
|
+
const selectedPath = $derived(value == null ? null : findNodePath(nodes, value))
|
|
67
|
+
const triggerText = $derived(
|
|
68
|
+
!selectedPath
|
|
69
|
+
? ''
|
|
70
|
+
: showPath
|
|
71
|
+
? selectedPath.map((n) => n.label).join(' / ')
|
|
72
|
+
: selectedPath[selectedPath.length - 1]!.label,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
function setExpanded(v: string | number, on: boolean) {
|
|
76
|
+
const next = new Set(expanded)
|
|
77
|
+
on ? next.add(v) : next.delete(v)
|
|
78
|
+
expanded = next
|
|
79
|
+
}
|
|
80
|
+
function toggleExpand(v: string | number) {
|
|
81
|
+
setExpanded(v, !expanded.has(v))
|
|
82
|
+
}
|
|
83
|
+
function pick(node: TreeSelectNode) {
|
|
84
|
+
if (node.disabled) return
|
|
85
|
+
value = node.value // enables bind:value
|
|
86
|
+
onChange?.(node.value)
|
|
87
|
+
ts.closePanel()
|
|
88
|
+
}
|
|
89
|
+
function onRowClick(row: { node: TreeSelectNode; hasChildren: boolean }) {
|
|
90
|
+
if (row.hasChildren) toggleExpand(row.node.value)
|
|
91
|
+
else pick(row.node)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const ts = createPopoverSelect({
|
|
95
|
+
itemCount: () => rows.length,
|
|
96
|
+
disabled: (i) => !!rows[i]?.node.disabled,
|
|
97
|
+
readonly: () => readonly,
|
|
98
|
+
onSelect: (i) => { const row = rows[i]; if (row) onRowClick(row) },
|
|
99
|
+
getTrigger: () => triggerEl,
|
|
100
|
+
getPanel: () => panelEl,
|
|
101
|
+
getInitialFocus: () => treeEl,
|
|
102
|
+
closeOnSelect: () => false, // onRowClick handles pick + close (branches stay open)
|
|
103
|
+
initialActive: () => Math.max(0, rows.findIndex((r) => r.node.value === value)),
|
|
104
|
+
minWidth: () => 220,
|
|
105
|
+
estimatedHeight: () => 300,
|
|
106
|
+
onExtraKey: (e, act) => {
|
|
107
|
+
const row = rows[act]
|
|
108
|
+
if (!row) return false
|
|
109
|
+
// "expand" opens the node or steps into the first child; "collapse" closes
|
|
110
|
+
// it or jumps to the parent - same fallback as createTree's core.
|
|
111
|
+
const expand = () => {
|
|
112
|
+
if (row.hasChildren && !row.expanded) setExpanded(row.node.value, true)
|
|
113
|
+
else if (row.hasChildren && row.expanded) ts.active = Math.min(act + 1, rows.length - 1)
|
|
114
|
+
}
|
|
115
|
+
const collapse = () => {
|
|
116
|
+
if (row.hasChildren && row.expanded) setExpanded(row.node.value, false)
|
|
117
|
+
else {
|
|
118
|
+
let pi = act - 1
|
|
119
|
+
while (pi >= 0 && rows[pi]!.depth >= row.depth) pi--
|
|
120
|
+
if (pi >= 0) ts.active = pi
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (e.key === 'ArrowRight') { e.preventDefault(); expand(); return true }
|
|
124
|
+
if (e.key === 'ArrowLeft') { e.preventDefault(); collapse(); return true }
|
|
125
|
+
return false
|
|
126
|
+
},
|
|
127
|
+
idPrefix: 'sv-ts',
|
|
128
|
+
})
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<SvField id={uid} {label} {hint} {error} {required} {dir} {loading}>
|
|
132
|
+
<div class="sv-ts sv-ts--{size}" class:is-disabled={disabled}>
|
|
133
|
+
<button
|
|
134
|
+
bind:this={triggerEl}
|
|
135
|
+
type="button"
|
|
136
|
+
class="sv-ts__trigger"
|
|
137
|
+
class:is-invalid={invalid}
|
|
138
|
+
{disabled}
|
|
139
|
+
onclick={() => ts.toggle()}
|
|
140
|
+
{...ts.triggerProps('tree')}
|
|
141
|
+
{...editorAria({ id: uid, invalid, required, error, hint, ariaLabel })}
|
|
142
|
+
>
|
|
143
|
+
<span class="sv-ts__value" class:is-placeholder={!triggerText}>{triggerText || placeholder}</span>
|
|
144
|
+
<span class="sv-ts__arrow" aria-hidden="true">▾</span>
|
|
145
|
+
</button>
|
|
146
|
+
{#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
|
|
147
|
+
</div>
|
|
148
|
+
</SvField>
|
|
149
|
+
|
|
150
|
+
{#if ts.open}
|
|
151
|
+
<div
|
|
152
|
+
bind:this={panelEl}
|
|
153
|
+
class="sv-ts__panel"
|
|
154
|
+
dir={dir}
|
|
155
|
+
use:portalToBody
|
|
156
|
+
use:popIn={{ up: ts.rect.openUpward }}
|
|
157
|
+
style:position="fixed"
|
|
158
|
+
style:top={`${ts.rect.top}px`}
|
|
159
|
+
style:left={`${ts.rect.left}px`}
|
|
160
|
+
style:min-width={`${ts.rect.width}px`}
|
|
161
|
+
>
|
|
162
|
+
<ul
|
|
163
|
+
bind:this={treeEl}
|
|
164
|
+
id={ts.panelId}
|
|
165
|
+
class="sv-ts__tree"
|
|
166
|
+
role="tree"
|
|
167
|
+
aria-label={label ?? ariaLabel ?? 'Tree'}
|
|
168
|
+
aria-activedescendant={ts.activeDescendant()}
|
|
169
|
+
tabindex="0"
|
|
170
|
+
style:max-height={`${ts.rect.maxHeight}px`}
|
|
171
|
+
onkeydown={ts.onPanelKeydown}
|
|
172
|
+
>
|
|
173
|
+
{#each rows as row, i (row.node.value)}
|
|
174
|
+
<li
|
|
175
|
+
class="sv-ts__row"
|
|
176
|
+
class:is-active={ts.isActive(i)}
|
|
177
|
+
class:is-selected={row.node.value === value}
|
|
178
|
+
class:is-disabled={row.node.disabled}
|
|
179
|
+
role="treeitem"
|
|
180
|
+
aria-selected={row.node.value === value}
|
|
181
|
+
aria-expanded={row.hasChildren ? row.expanded : undefined}
|
|
182
|
+
style:padding-inline-start={`${8 + row.depth * 16}px`}
|
|
183
|
+
onclick={() => onRowClick(row)}
|
|
184
|
+
{...ts.itemProps(i)}
|
|
185
|
+
>
|
|
186
|
+
{#if row.hasChildren}
|
|
187
|
+
<!-- Same chevron as SvTree and the grid's expanders. -->
|
|
188
|
+
<span class="sv-ts__chev" class:is-open={row.expanded} aria-hidden="true">
|
|
189
|
+
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor"
|
|
190
|
+
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>
|
|
191
|
+
</span>
|
|
192
|
+
{:else}
|
|
193
|
+
<span class="sv-ts__chev sv-ts__chev--leaf" aria-hidden="true"></span>
|
|
194
|
+
{/if}
|
|
195
|
+
<span class="sv-ts__label">{row.node.label}</span>
|
|
196
|
+
</li>
|
|
197
|
+
{/each}
|
|
198
|
+
</ul>
|
|
199
|
+
</div>
|
|
200
|
+
{/if}
|
|
201
|
+
|
|
202
|
+
<style>
|
|
203
|
+
.sv-ts { --_accent: var(--sg-accent, #2563eb); display: inline-flex; width: 240px; }
|
|
204
|
+
.sv-ts.is-disabled { opacity: 0.6; }
|
|
205
|
+
.sv-ts__trigger {
|
|
206
|
+
flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; text-align: start;
|
|
207
|
+
background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
208
|
+
border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
|
|
209
|
+
font: inherit; cursor: pointer; padding: 0 9px;
|
|
210
|
+
}
|
|
211
|
+
.sv-ts--sm .sv-ts__trigger { height: 28px; font-size: 12px; }
|
|
212
|
+
.sv-ts--md .sv-ts__trigger { height: 34px; font-size: 13px; }
|
|
213
|
+
.sv-ts--lg .sv-ts__trigger { height: 40px; font-size: 15px; }
|
|
214
|
+
.sv-ts__trigger:focus-visible { outline: none; border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
|
|
215
|
+
.sv-ts__trigger.is-invalid { border-color: var(--sg-danger, #dc2626); }
|
|
216
|
+
.sv-ts__value { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
217
|
+
.sv-ts__value.is-placeholder { color: var(--sg-muted, #94a3b8); }
|
|
218
|
+
.sv-ts__arrow { color: var(--sg-muted, #64748b); font-size: 10px; }
|
|
219
|
+
|
|
220
|
+
:global(.sv-ts__panel) {
|
|
221
|
+
z-index: 2147483646; max-height: 320px; max-width: 92vw; overflow: hidden;
|
|
222
|
+
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
223
|
+
border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px;
|
|
224
|
+
box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35); font-size: 13px;
|
|
225
|
+
}
|
|
226
|
+
:global(.sv-ts__tree) { list-style: none; margin: 0; padding: 4px; overflow: auto; max-height: 320px; outline: none; }
|
|
227
|
+
:global(.sv-ts__row) { display: flex; align-items: center; gap: 4px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
|
|
228
|
+
:global(.sv-ts__row.is-active) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
229
|
+
:global(.sv-ts__row.is-selected) { color: var(--sg-accent, #2563eb); font-weight: 600; }
|
|
230
|
+
:global(.sv-ts__row.is-disabled) { opacity: 0.5; cursor: not-allowed; }
|
|
231
|
+
:global(.sv-ts__chev) { width: 14px; flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--sg-muted, #64748b); transition: transform 0.12s; }
|
|
232
|
+
:global(.sv-ts__chev.is-open) { transform: rotate(90deg); }
|
|
233
|
+
:global(.sv-ts__chev--leaf) { visibility: hidden; }
|
|
234
|
+
:global(.sv-ts__label) { flex: 1; white-space: nowrap; }
|
|
235
|
+
</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* grid-announcements - what the grid says to a screen reader, and when.
|
|
3
|
+
*
|
|
4
|
+
* WCAG 4.1.3 covers *status messages*: information the user needs that does not
|
|
5
|
+
* receive focus. That is a narrow set, and getting the boundary right matters
|
|
6
|
+
* more than announcing a lot.
|
|
7
|
+
*
|
|
8
|
+
* Things the grid deliberately does NOT announce here, because the
|
|
9
|
+
* accessibility tree already conveys them when focus lands:
|
|
10
|
+
*
|
|
11
|
+
* - the value of the cell you moved to (roving tabindex moves focus)
|
|
12
|
+
* - the sort state of a header (`aria-sort` on the columnheader)
|
|
13
|
+
* - whether the row you are on is selected (`aria-selected` on the row)
|
|
14
|
+
*
|
|
15
|
+
* Announcing those again would make the grid talk over itself. What is left is
|
|
16
|
+
* genuinely invisible to a screen reader: how many rows survived a filter, and
|
|
17
|
+
* bulk selection changes that move no focus (select-all, clear).
|
|
18
|
+
*
|
|
19
|
+
* These builders are pure so they can be unit-tested without a DOM, and take
|
|
20
|
+
* their strings from `GridMessages` so an announcement is translated like any
|
|
21
|
+
* other label.
|
|
22
|
+
*/
|
|
23
|
+
/** The subset of `GridMessages` this module reads. */
|
|
24
|
+
export type AnnouncementMessages = {
|
|
25
|
+
announceFilterResults: string;
|
|
26
|
+
announceNoMatches: string;
|
|
27
|
+
announceFiltersCleared: string;
|
|
28
|
+
announceRowsSelected: string;
|
|
29
|
+
announceSelectionCleared: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Interpolate `{name}` placeholders.
|
|
33
|
+
*
|
|
34
|
+
* The rest of `GridMessages` is single words assembled by the caller (`of`,
|
|
35
|
+
* `rowsSuffix`). That works for labels but not for sentences: languages order
|
|
36
|
+
* "12 of 250 match" differently, and a translator handed three separate atoms
|
|
37
|
+
* cannot fix the order. Announcements are whole sentences, so they get whole
|
|
38
|
+
* templates. An unknown placeholder is left alone rather than blanked, so a
|
|
39
|
+
* typo in an override is visible instead of silently eating the number.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatMessage(template: string, vars: Record<string, string | number>): string;
|
|
42
|
+
/**
|
|
43
|
+
* What to say after the row set changes, or null to stay quiet.
|
|
44
|
+
*
|
|
45
|
+
* `visible` is the count after filtering but BEFORE pagination - the answer to
|
|
46
|
+
* "how many rows matched", not "how many are on this page".
|
|
47
|
+
*/
|
|
48
|
+
export declare function filterAnnouncement(visible: number, total: number, filtersActive: boolean, filtersWereActive: boolean, messages: AnnouncementMessages): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* What to say after the selection changes, or null to stay quiet.
|
|
51
|
+
*
|
|
52
|
+
* Only BULK changes are announced. Selecting one row moves focus to it and the
|
|
53
|
+
* screen reader reads it as selected from `aria-selected`; announcing "1 row
|
|
54
|
+
* selected" on top of that is the grid talking over itself. A change of more
|
|
55
|
+
* than one row cannot have come from a single focus move, so it is the
|
|
56
|
+
* select-all / range / clear case that otherwise passes silently.
|
|
57
|
+
*/
|
|
58
|
+
export declare function selectionAnnouncement(previousCount: number, count: number, messages: AnnouncementMessages): string | null;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* grid-announcements - what the grid says to a screen reader, and when.
|
|
3
|
+
*
|
|
4
|
+
* WCAG 4.1.3 covers *status messages*: information the user needs that does not
|
|
5
|
+
* receive focus. That is a narrow set, and getting the boundary right matters
|
|
6
|
+
* more than announcing a lot.
|
|
7
|
+
*
|
|
8
|
+
* Things the grid deliberately does NOT announce here, because the
|
|
9
|
+
* accessibility tree already conveys them when focus lands:
|
|
10
|
+
*
|
|
11
|
+
* - the value of the cell you moved to (roving tabindex moves focus)
|
|
12
|
+
* - the sort state of a header (`aria-sort` on the columnheader)
|
|
13
|
+
* - whether the row you are on is selected (`aria-selected` on the row)
|
|
14
|
+
*
|
|
15
|
+
* Announcing those again would make the grid talk over itself. What is left is
|
|
16
|
+
* genuinely invisible to a screen reader: how many rows survived a filter, and
|
|
17
|
+
* bulk selection changes that move no focus (select-all, clear).
|
|
18
|
+
*
|
|
19
|
+
* These builders are pure so they can be unit-tested without a DOM, and take
|
|
20
|
+
* their strings from `GridMessages` so an announcement is translated like any
|
|
21
|
+
* other label.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Interpolate `{name}` placeholders.
|
|
25
|
+
*
|
|
26
|
+
* The rest of `GridMessages` is single words assembled by the caller (`of`,
|
|
27
|
+
* `rowsSuffix`). That works for labels but not for sentences: languages order
|
|
28
|
+
* "12 of 250 match" differently, and a translator handed three separate atoms
|
|
29
|
+
* cannot fix the order. Announcements are whole sentences, so they get whole
|
|
30
|
+
* templates. An unknown placeholder is left alone rather than blanked, so a
|
|
31
|
+
* typo in an override is visible instead of silently eating the number.
|
|
32
|
+
*/
|
|
33
|
+
export function formatMessage(template, vars) {
|
|
34
|
+
return template.replace(/\{(\w+)\}/g, (whole, key) => key in vars ? String(vars[key]) : whole);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* What to say after the row set changes, or null to stay quiet.
|
|
38
|
+
*
|
|
39
|
+
* `visible` is the count after filtering but BEFORE pagination - the answer to
|
|
40
|
+
* "how many rows matched", not "how many are on this page".
|
|
41
|
+
*/
|
|
42
|
+
export function filterAnnouncement(visible, total, filtersActive, filtersWereActive, messages) {
|
|
43
|
+
if (filtersActive) {
|
|
44
|
+
if (visible === 0)
|
|
45
|
+
return messages.announceNoMatches;
|
|
46
|
+
return formatMessage(messages.announceFilterResults, { visible, total });
|
|
47
|
+
}
|
|
48
|
+
// Clearing a filter is itself a status change worth confirming; without this
|
|
49
|
+
// the user hears nothing back and cannot tell the clear took effect.
|
|
50
|
+
if (filtersWereActive) {
|
|
51
|
+
return formatMessage(messages.announceFiltersCleared, { total });
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* What to say after the selection changes, or null to stay quiet.
|
|
57
|
+
*
|
|
58
|
+
* Only BULK changes are announced. Selecting one row moves focus to it and the
|
|
59
|
+
* screen reader reads it as selected from `aria-selected`; announcing "1 row
|
|
60
|
+
* selected" on top of that is the grid talking over itself. A change of more
|
|
61
|
+
* than one row cannot have come from a single focus move, so it is the
|
|
62
|
+
* select-all / range / clear case that otherwise passes silently.
|
|
63
|
+
*/
|
|
64
|
+
export function selectionAnnouncement(previousCount, count, messages) {
|
|
65
|
+
if (Math.abs(count - previousCount) <= 1)
|
|
66
|
+
return null;
|
|
67
|
+
if (count === 0)
|
|
68
|
+
return messages.announceSelectionCleared;
|
|
69
|
+
return formatMessage(messages.announceRowsSelected, { count });
|
|
70
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration seam for the advanced filter (a `@svgrid/enterprise` feature).
|
|
3
|
+
*
|
|
4
|
+
* The grid owns the state and the pipeline slot; enterprise owns the compiler.
|
|
5
|
+
* Same shape as `pivot-view.svelte.ts`: a module-level `$state` holder with a
|
|
6
|
+
* register / get / has triple, so the free package carries no commercial code.
|
|
7
|
+
*/
|
|
8
|
+
import type { GridPredicateExpr } from './filtering/predicate-expr';
|
|
9
|
+
/** What the engine needs in order to compile an expression into a predicate. */
|
|
10
|
+
export type AdvancedFilterCompileContext<TRow> = {
|
|
11
|
+
/**
|
|
12
|
+
* Resolve a column id to its cell value for a row. The grid passes the same
|
|
13
|
+
* accessor the column-menu filter stage uses, so a `cmp` on a `fieldFn` or
|
|
14
|
+
* value-getter column resolves identically in both.
|
|
15
|
+
*/
|
|
16
|
+
getValue: (row: TRow, columnId: string) => unknown;
|
|
17
|
+
/** Locale for text folding, matching the column filters. */
|
|
18
|
+
locale?: string | ReadonlyArray<string>;
|
|
19
|
+
/**
|
|
20
|
+
* The rows entering this stage. Aggregates (`SUM(amount) > 1000`) are folded
|
|
21
|
+
* over exactly these, once, so they see the rows the user is currently
|
|
22
|
+
* looking at rather than the raw dataset.
|
|
23
|
+
*/
|
|
24
|
+
rows: ReadonlyArray<TRow>;
|
|
25
|
+
};
|
|
26
|
+
/** A compiled expression: cheap to call once per row. */
|
|
27
|
+
export type CompiledRowPredicate<TRow> = (row: TRow) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Compile an expression to a row predicate.
|
|
30
|
+
*
|
|
31
|
+
* Returns `null` when the expression cannot be compiled (an unknown node kind,
|
|
32
|
+
* an unresolvable column). Must never throw: the grid treats both `null` and a
|
|
33
|
+
* thrown error as "do not filter", because a half-applied filter silently
|
|
34
|
+
* showing the wrong rows is worse than an unapplied one.
|
|
35
|
+
*/
|
|
36
|
+
export type AdvancedFilterEngine = <TRow>(expr: GridPredicateExpr, ctx: AdvancedFilterCompileContext<TRow>) => CompiledRowPredicate<TRow> | null;
|
|
37
|
+
/** Register the engine. Enterprise calls this from `enableAdvancedFilter()`. */
|
|
38
|
+
export declare function registerAdvancedFilterEngine(fn: AdvancedFilterEngine | null): void;
|
|
39
|
+
/** The registered engine, or null when enterprise is not installed. */
|
|
40
|
+
export declare function getAdvancedFilterEngine(): AdvancedFilterEngine | null;
|
|
41
|
+
/** Whether an advanced-filter engine has been registered. */
|
|
42
|
+
export declare function hasAdvancedFilterEngine(): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
let engine = $state(null);
|
|
2
|
+
/** Register the engine. Enterprise calls this from `enableAdvancedFilter()`. */
|
|
3
|
+
export function registerAdvancedFilterEngine(fn) {
|
|
4
|
+
engine = fn;
|
|
5
|
+
}
|
|
6
|
+
/** The registered engine, or null when enterprise is not installed. */
|
|
7
|
+
export function getAdvancedFilterEngine() {
|
|
8
|
+
return engine;
|
|
9
|
+
}
|
|
10
|
+
/** Whether an advanced-filter engine has been registered. */
|
|
11
|
+
export function hasAdvancedFilterEngine() {
|
|
12
|
+
return engine != null;
|
|
13
|
+
}
|