@svgrid/grid 2.6.2 → 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
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
2
|
-
import { createDismissableLayer, dismissableDepth, onScrollOutside } from './dismissable'
|
|
3
|
-
|
|
4
|
-
function panel(): HTMLElement {
|
|
5
|
-
const el = document.createElement('div')
|
|
6
|
-
document.body.appendChild(el)
|
|
7
|
-
return el
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
document.body.innerHTML = ''
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
describe('createDismissableLayer', () => {
|
|
15
|
-
it('dismisses on Escape when topmost', () => {
|
|
16
|
-
const el = panel()
|
|
17
|
-
let reason: string | null = null
|
|
18
|
-
const layer = createDismissableLayer({ element: () => el, onDismiss: (r) => (reason = r) })
|
|
19
|
-
layer.activate()
|
|
20
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
21
|
-
expect(reason).toBe('escape')
|
|
22
|
-
layer.release()
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('dismisses on outside pointerdown but not inside', () => {
|
|
26
|
-
const el = panel()
|
|
27
|
-
const inside = document.createElement('button')
|
|
28
|
-
el.appendChild(inside)
|
|
29
|
-
let reason: string | null = null
|
|
30
|
-
const layer = createDismissableLayer({ element: () => el, onDismiss: (r) => (reason = r) })
|
|
31
|
-
layer.activate()
|
|
32
|
-
|
|
33
|
-
inside.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
34
|
-
expect(reason).toBeNull()
|
|
35
|
-
|
|
36
|
-
document.body.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
37
|
-
expect(reason).toBe('outside')
|
|
38
|
-
layer.release()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('treats a pointerdown in ANY of multiple elements as inside', () => {
|
|
42
|
-
const panelEl = panel()
|
|
43
|
-
const trigger = panel()
|
|
44
|
-
let reason: string | null = null
|
|
45
|
-
const layer = createDismissableLayer({
|
|
46
|
-
element: () => [panelEl, trigger],
|
|
47
|
-
onDismiss: (r) => (reason = r),
|
|
48
|
-
})
|
|
49
|
-
layer.activate()
|
|
50
|
-
|
|
51
|
-
trigger.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
52
|
-
expect(reason).toBeNull() // trigger counts as inside
|
|
53
|
-
|
|
54
|
-
document.body.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
55
|
-
expect(reason).toBe('outside')
|
|
56
|
-
layer.release()
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('only the top layer responds to Escape', () => {
|
|
60
|
-
const a = panel()
|
|
61
|
-
const b = panel()
|
|
62
|
-
const hits: string[] = []
|
|
63
|
-
const layerA = createDismissableLayer({ element: () => a, onDismiss: () => hits.push('a') })
|
|
64
|
-
const layerB = createDismissableLayer({ element: () => b, onDismiss: () => hits.push('b') })
|
|
65
|
-
layerA.activate()
|
|
66
|
-
layerB.activate()
|
|
67
|
-
expect(dismissableDepth()).toBe(2)
|
|
68
|
-
|
|
69
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
70
|
-
expect(hits).toEqual(['b']) // only the topmost
|
|
71
|
-
|
|
72
|
-
layerB.release()
|
|
73
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
74
|
-
expect(hits).toEqual(['b', 'a']) // now a is on top
|
|
75
|
-
|
|
76
|
-
layerA.release()
|
|
77
|
-
expect(dismissableDepth()).toBe(0)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('close-on-scroll ignores scrolling inside the panel', () => {
|
|
81
|
-
const el = panel()
|
|
82
|
-
const list = document.createElement('div') // e.g. the filter menu's facet list
|
|
83
|
-
el.appendChild(list)
|
|
84
|
-
let closed = 0
|
|
85
|
-
const off = onScrollOutside(() => el, () => closed++)
|
|
86
|
-
|
|
87
|
-
// Inner scroll containers don't bubble, so this only reaches the capture
|
|
88
|
-
// listener - it must NOT close the menu the user is scrolling.
|
|
89
|
-
list.dispatchEvent(new Event('scroll'))
|
|
90
|
-
el.dispatchEvent(new Event('scroll'))
|
|
91
|
-
expect(closed).toBe(0)
|
|
92
|
-
|
|
93
|
-
// A scroll anywhere else moves the anchor, so the panel closes.
|
|
94
|
-
document.body.dispatchEvent(new Event('scroll'))
|
|
95
|
-
expect(closed).toBe(1)
|
|
96
|
-
document.dispatchEvent(new Event('scroll'))
|
|
97
|
-
expect(closed).toBe(2)
|
|
98
|
-
|
|
99
|
-
off()
|
|
100
|
-
document.body.dispatchEvent(new Event('scroll'))
|
|
101
|
-
expect(closed).toBe(2)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('respects closeOnEscape / closeOnOutside flags', () => {
|
|
105
|
-
const el = panel()
|
|
106
|
-
let count = 0
|
|
107
|
-
const layer = createDismissableLayer({
|
|
108
|
-
element: () => el,
|
|
109
|
-
onDismiss: () => count++,
|
|
110
|
-
closeOnEscape: false,
|
|
111
|
-
closeOnOutside: false,
|
|
112
|
-
})
|
|
113
|
-
layer.activate()
|
|
114
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
115
|
-
document.body.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
116
|
-
expect(count).toBe(0)
|
|
117
|
-
layer.release()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
2
|
+
import { createDismissableLayer, dismissableDepth, onScrollOutside } from './dismissable'
|
|
3
|
+
|
|
4
|
+
function panel(): HTMLElement {
|
|
5
|
+
const el = document.createElement('div')
|
|
6
|
+
document.body.appendChild(el)
|
|
7
|
+
return el
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
document.body.innerHTML = ''
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
describe('createDismissableLayer', () => {
|
|
15
|
+
it('dismisses on Escape when topmost', () => {
|
|
16
|
+
const el = panel()
|
|
17
|
+
let reason: string | null = null
|
|
18
|
+
const layer = createDismissableLayer({ element: () => el, onDismiss: (r) => (reason = r) })
|
|
19
|
+
layer.activate()
|
|
20
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
21
|
+
expect(reason).toBe('escape')
|
|
22
|
+
layer.release()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('dismisses on outside pointerdown but not inside', () => {
|
|
26
|
+
const el = panel()
|
|
27
|
+
const inside = document.createElement('button')
|
|
28
|
+
el.appendChild(inside)
|
|
29
|
+
let reason: string | null = null
|
|
30
|
+
const layer = createDismissableLayer({ element: () => el, onDismiss: (r) => (reason = r) })
|
|
31
|
+
layer.activate()
|
|
32
|
+
|
|
33
|
+
inside.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
34
|
+
expect(reason).toBeNull()
|
|
35
|
+
|
|
36
|
+
document.body.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
37
|
+
expect(reason).toBe('outside')
|
|
38
|
+
layer.release()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('treats a pointerdown in ANY of multiple elements as inside', () => {
|
|
42
|
+
const panelEl = panel()
|
|
43
|
+
const trigger = panel()
|
|
44
|
+
let reason: string | null = null
|
|
45
|
+
const layer = createDismissableLayer({
|
|
46
|
+
element: () => [panelEl, trigger],
|
|
47
|
+
onDismiss: (r) => (reason = r),
|
|
48
|
+
})
|
|
49
|
+
layer.activate()
|
|
50
|
+
|
|
51
|
+
trigger.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
52
|
+
expect(reason).toBeNull() // trigger counts as inside
|
|
53
|
+
|
|
54
|
+
document.body.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
55
|
+
expect(reason).toBe('outside')
|
|
56
|
+
layer.release()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('only the top layer responds to Escape', () => {
|
|
60
|
+
const a = panel()
|
|
61
|
+
const b = panel()
|
|
62
|
+
const hits: string[] = []
|
|
63
|
+
const layerA = createDismissableLayer({ element: () => a, onDismiss: () => hits.push('a') })
|
|
64
|
+
const layerB = createDismissableLayer({ element: () => b, onDismiss: () => hits.push('b') })
|
|
65
|
+
layerA.activate()
|
|
66
|
+
layerB.activate()
|
|
67
|
+
expect(dismissableDepth()).toBe(2)
|
|
68
|
+
|
|
69
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
70
|
+
expect(hits).toEqual(['b']) // only the topmost
|
|
71
|
+
|
|
72
|
+
layerB.release()
|
|
73
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
74
|
+
expect(hits).toEqual(['b', 'a']) // now a is on top
|
|
75
|
+
|
|
76
|
+
layerA.release()
|
|
77
|
+
expect(dismissableDepth()).toBe(0)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('close-on-scroll ignores scrolling inside the panel', () => {
|
|
81
|
+
const el = panel()
|
|
82
|
+
const list = document.createElement('div') // e.g. the filter menu's facet list
|
|
83
|
+
el.appendChild(list)
|
|
84
|
+
let closed = 0
|
|
85
|
+
const off = onScrollOutside(() => el, () => closed++)
|
|
86
|
+
|
|
87
|
+
// Inner scroll containers don't bubble, so this only reaches the capture
|
|
88
|
+
// listener - it must NOT close the menu the user is scrolling.
|
|
89
|
+
list.dispatchEvent(new Event('scroll'))
|
|
90
|
+
el.dispatchEvent(new Event('scroll'))
|
|
91
|
+
expect(closed).toBe(0)
|
|
92
|
+
|
|
93
|
+
// A scroll anywhere else moves the anchor, so the panel closes.
|
|
94
|
+
document.body.dispatchEvent(new Event('scroll'))
|
|
95
|
+
expect(closed).toBe(1)
|
|
96
|
+
document.dispatchEvent(new Event('scroll'))
|
|
97
|
+
expect(closed).toBe(2)
|
|
98
|
+
|
|
99
|
+
off()
|
|
100
|
+
document.body.dispatchEvent(new Event('scroll'))
|
|
101
|
+
expect(closed).toBe(2)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('respects closeOnEscape / closeOnOutside flags', () => {
|
|
105
|
+
const el = panel()
|
|
106
|
+
let count = 0
|
|
107
|
+
const layer = createDismissableLayer({
|
|
108
|
+
element: () => el,
|
|
109
|
+
onDismiss: () => count++,
|
|
110
|
+
closeOnEscape: false,
|
|
111
|
+
closeOnOutside: false,
|
|
112
|
+
})
|
|
113
|
+
layer.activate()
|
|
114
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
115
|
+
document.body.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
|
|
116
|
+
expect(count).toBe(0)
|
|
117
|
+
layer.release()
|
|
118
|
+
})
|
|
119
|
+
})
|
package/src/a11y/dismissable.ts
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dismissable-layer - a global stack of dismissable overlays (dropdowns,
|
|
3
|
-
* popovers, dialogs). Escape and outside-pointer dismiss only the TOP layer, so
|
|
4
|
-
* a menu opened inside a dialog closes the menu first and the dialog second -
|
|
5
|
-
* the ordering users expect from nested overlays. One set of document listeners
|
|
6
|
-
* is shared by every layer instead of each component wiring its own.
|
|
7
|
-
*/
|
|
8
|
-
type Reason = 'escape' | 'outside'
|
|
9
|
-
|
|
10
|
-
/** One element, several elements, or a getter of either. A pointerdown counts
|
|
11
|
-
* as "inside" when it lands in ANY of them (e.g. a popover's panel AND its
|
|
12
|
-
* trigger, so clicking the trigger doesn't immediately re-dismiss). */
|
|
13
|
-
export type DismissTarget = HTMLElement | null | ReadonlyArray<HTMLElement | null>
|
|
14
|
-
|
|
15
|
-
type Layer = {
|
|
16
|
-
element: () => DismissTarget
|
|
17
|
-
onDismiss: (reason: Reason) => void
|
|
18
|
-
closeOnEscape: boolean
|
|
19
|
-
closeOnOutside: boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function containsTarget(target: DismissTarget, node: Node | null): boolean {
|
|
23
|
-
if (!node) return false
|
|
24
|
-
const els = Array.isArray(target) ? target : [target as HTMLElement | null]
|
|
25
|
-
return els.some((el) => el != null && el.contains(node))
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const stack: Layer[] = []
|
|
29
|
-
let wired = false
|
|
30
|
-
|
|
31
|
-
function ensureWired() {
|
|
32
|
-
if (wired || typeof document === 'undefined') return
|
|
33
|
-
wired = true
|
|
34
|
-
document.addEventListener(
|
|
35
|
-
'keydown',
|
|
36
|
-
(e) => {
|
|
37
|
-
if (e.key !== 'Escape') return
|
|
38
|
-
const top = stack[stack.length - 1]
|
|
39
|
-
if (top?.closeOnEscape) {
|
|
40
|
-
e.stopPropagation()
|
|
41
|
-
top.onDismiss('escape')
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
true,
|
|
45
|
-
)
|
|
46
|
-
document.addEventListener(
|
|
47
|
-
'pointerdown',
|
|
48
|
-
(e) => {
|
|
49
|
-
const top = stack[stack.length - 1]
|
|
50
|
-
if (!top?.closeOnOutside) return
|
|
51
|
-
if (!containsTarget(top.element(), e.target as Node)) top.onDismiss('outside')
|
|
52
|
-
},
|
|
53
|
-
true,
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export type DismissableOptions = {
|
|
58
|
-
/** The panel element(s); a pointerdown outside ALL of them dismisses the layer.
|
|
59
|
-
* Pass multiple (e.g. panel + trigger) so clicking the trigger doesn't
|
|
60
|
-
* immediately re-dismiss what it just opened. */
|
|
61
|
-
element: () => DismissTarget
|
|
62
|
-
onDismiss: (reason: Reason) => void
|
|
63
|
-
/** Dismiss on Escape when topmost. Default true. */
|
|
64
|
-
closeOnEscape?: boolean
|
|
65
|
-
/** Dismiss on outside pointerdown when topmost. Default true. */
|
|
66
|
-
closeOnOutside?: boolean
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export type DismissableLayer = { activate: () => void; release: () => void }
|
|
70
|
-
|
|
71
|
-
/** Register a dismissable layer. Inert until `activate()`. */
|
|
72
|
-
export function createDismissableLayer(options: DismissableOptions): DismissableLayer {
|
|
73
|
-
const layer: Layer = {
|
|
74
|
-
element: options.element,
|
|
75
|
-
onDismiss: options.onDismiss,
|
|
76
|
-
closeOnEscape: options.closeOnEscape ?? true,
|
|
77
|
-
closeOnOutside: options.closeOnOutside ?? true,
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
activate() {
|
|
81
|
-
ensureWired()
|
|
82
|
-
if (!stack.includes(layer)) stack.push(layer)
|
|
83
|
-
},
|
|
84
|
-
release() {
|
|
85
|
-
const i = stack.indexOf(layer)
|
|
86
|
-
if (i >= 0) stack.splice(i, 1)
|
|
87
|
-
},
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Test/introspection helper: current number of active layers. */
|
|
92
|
-
export function dismissableDepth(): number {
|
|
93
|
-
return stack.length
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Close-on-scroll for popovers that are position:fixed and anchored by a
|
|
98
|
-
* one-time measurement taken when they open. Scrolling an ancestor slides the
|
|
99
|
-
* anchor out from under them, so they have to close - but scrolling INSIDE
|
|
100
|
-
* them (a long menu, a facet list) must not, which is what a bare capture-phase
|
|
101
|
-
* window listener gets wrong: inner scroll containers don't bubble, so the
|
|
102
|
-
* capture listener sees them and dismisses the panel the user is scrolling.
|
|
103
|
-
*
|
|
104
|
-
* Returns the unsubscribe function.
|
|
105
|
-
*/
|
|
106
|
-
export function onScrollOutside(element: () => DismissTarget, onOutside: () => void): () => void {
|
|
107
|
-
if (typeof window === 'undefined') return () => {}
|
|
108
|
-
const handler = (event: Event) => {
|
|
109
|
-
if (containsTarget(element(), event.target as Node)) return
|
|
110
|
-
onOutside()
|
|
111
|
-
}
|
|
112
|
-
window.addEventListener('scroll', handler, true)
|
|
113
|
-
return () => window.removeEventListener('scroll', handler, true)
|
|
114
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* dismissable-layer - a global stack of dismissable overlays (dropdowns,
|
|
3
|
+
* popovers, dialogs). Escape and outside-pointer dismiss only the TOP layer, so
|
|
4
|
+
* a menu opened inside a dialog closes the menu first and the dialog second -
|
|
5
|
+
* the ordering users expect from nested overlays. One set of document listeners
|
|
6
|
+
* is shared by every layer instead of each component wiring its own.
|
|
7
|
+
*/
|
|
8
|
+
type Reason = 'escape' | 'outside'
|
|
9
|
+
|
|
10
|
+
/** One element, several elements, or a getter of either. A pointerdown counts
|
|
11
|
+
* as "inside" when it lands in ANY of them (e.g. a popover's panel AND its
|
|
12
|
+
* trigger, so clicking the trigger doesn't immediately re-dismiss). */
|
|
13
|
+
export type DismissTarget = HTMLElement | null | ReadonlyArray<HTMLElement | null>
|
|
14
|
+
|
|
15
|
+
type Layer = {
|
|
16
|
+
element: () => DismissTarget
|
|
17
|
+
onDismiss: (reason: Reason) => void
|
|
18
|
+
closeOnEscape: boolean
|
|
19
|
+
closeOnOutside: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function containsTarget(target: DismissTarget, node: Node | null): boolean {
|
|
23
|
+
if (!node) return false
|
|
24
|
+
const els = Array.isArray(target) ? target : [target as HTMLElement | null]
|
|
25
|
+
return els.some((el) => el != null && el.contains(node))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const stack: Layer[] = []
|
|
29
|
+
let wired = false
|
|
30
|
+
|
|
31
|
+
function ensureWired() {
|
|
32
|
+
if (wired || typeof document === 'undefined') return
|
|
33
|
+
wired = true
|
|
34
|
+
document.addEventListener(
|
|
35
|
+
'keydown',
|
|
36
|
+
(e) => {
|
|
37
|
+
if (e.key !== 'Escape') return
|
|
38
|
+
const top = stack[stack.length - 1]
|
|
39
|
+
if (top?.closeOnEscape) {
|
|
40
|
+
e.stopPropagation()
|
|
41
|
+
top.onDismiss('escape')
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
true,
|
|
45
|
+
)
|
|
46
|
+
document.addEventListener(
|
|
47
|
+
'pointerdown',
|
|
48
|
+
(e) => {
|
|
49
|
+
const top = stack[stack.length - 1]
|
|
50
|
+
if (!top?.closeOnOutside) return
|
|
51
|
+
if (!containsTarget(top.element(), e.target as Node)) top.onDismiss('outside')
|
|
52
|
+
},
|
|
53
|
+
true,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type DismissableOptions = {
|
|
58
|
+
/** The panel element(s); a pointerdown outside ALL of them dismisses the layer.
|
|
59
|
+
* Pass multiple (e.g. panel + trigger) so clicking the trigger doesn't
|
|
60
|
+
* immediately re-dismiss what it just opened. */
|
|
61
|
+
element: () => DismissTarget
|
|
62
|
+
onDismiss: (reason: Reason) => void
|
|
63
|
+
/** Dismiss on Escape when topmost. Default true. */
|
|
64
|
+
closeOnEscape?: boolean
|
|
65
|
+
/** Dismiss on outside pointerdown when topmost. Default true. */
|
|
66
|
+
closeOnOutside?: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type DismissableLayer = { activate: () => void; release: () => void }
|
|
70
|
+
|
|
71
|
+
/** Register a dismissable layer. Inert until `activate()`. */
|
|
72
|
+
export function createDismissableLayer(options: DismissableOptions): DismissableLayer {
|
|
73
|
+
const layer: Layer = {
|
|
74
|
+
element: options.element,
|
|
75
|
+
onDismiss: options.onDismiss,
|
|
76
|
+
closeOnEscape: options.closeOnEscape ?? true,
|
|
77
|
+
closeOnOutside: options.closeOnOutside ?? true,
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
activate() {
|
|
81
|
+
ensureWired()
|
|
82
|
+
if (!stack.includes(layer)) stack.push(layer)
|
|
83
|
+
},
|
|
84
|
+
release() {
|
|
85
|
+
const i = stack.indexOf(layer)
|
|
86
|
+
if (i >= 0) stack.splice(i, 1)
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Test/introspection helper: current number of active layers. */
|
|
92
|
+
export function dismissableDepth(): number {
|
|
93
|
+
return stack.length
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Close-on-scroll for popovers that are position:fixed and anchored by a
|
|
98
|
+
* one-time measurement taken when they open. Scrolling an ancestor slides the
|
|
99
|
+
* anchor out from under them, so they have to close - but scrolling INSIDE
|
|
100
|
+
* them (a long menu, a facet list) must not, which is what a bare capture-phase
|
|
101
|
+
* window listener gets wrong: inner scroll containers don't bubble, so the
|
|
102
|
+
* capture listener sees them and dismisses the panel the user is scrolling.
|
|
103
|
+
*
|
|
104
|
+
* Returns the unsubscribe function.
|
|
105
|
+
*/
|
|
106
|
+
export function onScrollOutside(element: () => DismissTarget, onOutside: () => void): () => void {
|
|
107
|
+
if (typeof window === 'undefined') return () => {}
|
|
108
|
+
const handler = (event: Event) => {
|
|
109
|
+
if (containsTarget(element(), event.target as Node)) return
|
|
110
|
+
onOutside()
|
|
111
|
+
}
|
|
112
|
+
window.addEventListener('scroll', handler, true)
|
|
113
|
+
return () => window.removeEventListener('scroll', handler, true)
|
|
114
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
formatMessage,
|
|
4
|
+
filterAnnouncement,
|
|
5
|
+
selectionAnnouncement,
|
|
6
|
+
} from './grid-announcements'
|
|
7
|
+
import { defaultGridMessages } from '../grid-messages'
|
|
8
|
+
|
|
9
|
+
const m = defaultGridMessages
|
|
10
|
+
|
|
11
|
+
describe('formatMessage', () => {
|
|
12
|
+
it('substitutes placeholders', () => {
|
|
13
|
+
expect(formatMessage('{a} of {b}', { a: 1, b: 2 })).toBe('1 of 2')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('leaves an unknown placeholder alone rather than blanking it', () => {
|
|
17
|
+
// A typo in a consumer override should be visible, not silently swallow
|
|
18
|
+
// the number it was meant to carry.
|
|
19
|
+
expect(formatMessage('{visible} of {totl}', { visible: 3 })).toBe('3 of {totl}')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('substitutes a placeholder used more than once', () => {
|
|
23
|
+
expect(formatMessage('{n}/{n}', { n: 7 })).toBe('7/7')
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('filterAnnouncement', () => {
|
|
28
|
+
it('reports the match count while a filter is active', () => {
|
|
29
|
+
expect(filterAnnouncement(12, 250, true, false, m)).toBe(
|
|
30
|
+
'12 of 250 rows match the current filters',
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('has a distinct message for zero matches', () => {
|
|
35
|
+
// "0 of 250 rows match" is technically true but reads as a broken counter.
|
|
36
|
+
expect(filterAnnouncement(0, 250, true, false, m)).toBe(
|
|
37
|
+
'No rows match the current filters',
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('confirms that clearing the filters took effect', () => {
|
|
42
|
+
expect(filterAnnouncement(250, 250, false, true, m)).toBe(
|
|
43
|
+
'Filters cleared, showing all 250 rows',
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('stays silent when no filter is or was active', () => {
|
|
48
|
+
// Replacing the data array must not make the grid announce a count nobody
|
|
49
|
+
// asked for.
|
|
50
|
+
expect(filterAnnouncement(250, 250, false, false, m)).toBeNull()
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('selectionAnnouncement', () => {
|
|
55
|
+
it('announces select-all', () => {
|
|
56
|
+
expect(selectionAnnouncement(0, 250, m)).toBe('250 rows selected')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('announces clearing a multi-row selection', () => {
|
|
60
|
+
expect(selectionAnnouncement(250, 0, m)).toBe('Selection cleared')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('stays silent for a single-row change', () => {
|
|
64
|
+
// Focus moves to the row and the reader announces it from aria-selected;
|
|
65
|
+
// a second announcement would talk over that.
|
|
66
|
+
expect(selectionAnnouncement(0, 1, m)).toBeNull()
|
|
67
|
+
expect(selectionAnnouncement(3, 4, m)).toBeNull()
|
|
68
|
+
expect(selectionAnnouncement(1, 0, m)).toBeNull()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('stays silent when the count did not change', () => {
|
|
72
|
+
expect(selectionAnnouncement(5, 5, m)).toBeNull()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('announces a shrinking range selection', () => {
|
|
76
|
+
expect(selectionAnnouncement(10, 4, m)).toBe('4 rows selected')
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
/** The subset of `GridMessages` this module reads. */
|
|
25
|
+
export type AnnouncementMessages = {
|
|
26
|
+
announceFilterResults: string
|
|
27
|
+
announceNoMatches: string
|
|
28
|
+
announceFiltersCleared: string
|
|
29
|
+
announceRowsSelected: string
|
|
30
|
+
announceSelectionCleared: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Interpolate `{name}` placeholders.
|
|
35
|
+
*
|
|
36
|
+
* The rest of `GridMessages` is single words assembled by the caller (`of`,
|
|
37
|
+
* `rowsSuffix`). That works for labels but not for sentences: languages order
|
|
38
|
+
* "12 of 250 match" differently, and a translator handed three separate atoms
|
|
39
|
+
* cannot fix the order. Announcements are whole sentences, so they get whole
|
|
40
|
+
* templates. An unknown placeholder is left alone rather than blanked, so a
|
|
41
|
+
* typo in an override is visible instead of silently eating the number.
|
|
42
|
+
*/
|
|
43
|
+
export function formatMessage(
|
|
44
|
+
template: string,
|
|
45
|
+
vars: Record<string, string | number>,
|
|
46
|
+
): string {
|
|
47
|
+
return template.replace(/\{(\w+)\}/g, (whole, key: string) =>
|
|
48
|
+
key in vars ? String(vars[key]) : whole,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* What to say after the row set changes, or null to stay quiet.
|
|
54
|
+
*
|
|
55
|
+
* `visible` is the count after filtering but BEFORE pagination - the answer to
|
|
56
|
+
* "how many rows matched", not "how many are on this page".
|
|
57
|
+
*/
|
|
58
|
+
export function filterAnnouncement(
|
|
59
|
+
visible: number,
|
|
60
|
+
total: number,
|
|
61
|
+
filtersActive: boolean,
|
|
62
|
+
filtersWereActive: boolean,
|
|
63
|
+
messages: AnnouncementMessages,
|
|
64
|
+
): string | null {
|
|
65
|
+
if (filtersActive) {
|
|
66
|
+
if (visible === 0) return messages.announceNoMatches
|
|
67
|
+
return formatMessage(messages.announceFilterResults, { visible, total })
|
|
68
|
+
}
|
|
69
|
+
// Clearing a filter is itself a status change worth confirming; without this
|
|
70
|
+
// the user hears nothing back and cannot tell the clear took effect.
|
|
71
|
+
if (filtersWereActive) {
|
|
72
|
+
return formatMessage(messages.announceFiltersCleared, { total })
|
|
73
|
+
}
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* What to say after the selection changes, or null to stay quiet.
|
|
79
|
+
*
|
|
80
|
+
* Only BULK changes are announced. Selecting one row moves focus to it and the
|
|
81
|
+
* screen reader reads it as selected from `aria-selected`; announcing "1 row
|
|
82
|
+
* selected" on top of that is the grid talking over itself. A change of more
|
|
83
|
+
* than one row cannot have come from a single focus move, so it is the
|
|
84
|
+
* select-all / range / clear case that otherwise passes silently.
|
|
85
|
+
*/
|
|
86
|
+
export function selectionAnnouncement(
|
|
87
|
+
previousCount: number,
|
|
88
|
+
count: number,
|
|
89
|
+
messages: AnnouncementMessages,
|
|
90
|
+
): string | null {
|
|
91
|
+
if (Math.abs(count - previousCount) <= 1) return null
|
|
92
|
+
if (count === 0) return messages.announceSelectionCleared
|
|
93
|
+
return formatMessage(messages.announceRowsSelected, { count })
|
|
94
|
+
}
|