@svgrid/grid 2.2.33 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +178 -139
- 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 +160 -160
- 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.css +2814 -2804
- package/dist/SvGrid.svelte +3490 -3490
- 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 +200 -200
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +112 -112
- 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/cdn/{GridMenus-B7XhhqdN.js → GridMenus-BpVcKyUr.js} +4 -4
- package/dist/cdn/{GridMenus-BxmIvCJh.js → GridMenus-CeXL5IW4.js} +153 -150
- package/dist/cdn/{SvDateTimePicker-ClqtVIj3.js → SvDateTimePicker-DSUhIta8.js} +165 -165
- package/dist/cdn/{SvDateTimePicker-o94wfSUa.js → SvDateTimePicker-h9kPyszT.js} +572 -555
- package/dist/cdn/{SvGridChart-Cr_90pPD.js → SvGridChart-Bs2GIR2Q.js} +28 -21
- package/dist/cdn/{SvGridChart-BhnFdLd0.js → SvGridChart-CwhFz7GV.js} +2 -2
- package/dist/cdn/{SvGridChartPanel-Cmx9DHAS.js → SvGridChartPanel-1WXuStzM.js} +2 -2
- package/dist/cdn/{SvGridChartPanel-ICbxiUvK.js → SvGridChartPanel-e5_nqNP9.js} +20 -12
- package/dist/cdn/{SvGridChartView-Bx42PMvf.js → SvGridChartView-SmPW10dI.js} +3 -3
- package/dist/cdn/{SvGridChartView-B40DTJSX.js → SvGridChartView-eSPuJE6g.js} +2 -2
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-B1ZcLpgs.js} +1 -1
- package/dist/cdn/{SvGridDropdown-CUOdm7YA.js → SvGridDropdown-CtdbHcIS.js} +6 -5
- package/dist/cdn/{chart-T1usDN0o.js → chart-i4XcXyHJ.js} +12 -10
- package/dist/cdn/{disclose-version-DljhZohK.js → disclose-version-Dr7rEVv-.js} +989 -1081
- package/dist/cdn/{export-format-JT5pWVT8.js → export-format-Cv1Dll6k.js} +66 -245
- package/dist/cdn/{src-erjBRYfX.js → src-DbeN1ReZ.js} +9221 -8925
- package/dist/cdn/{src-ktbQVOk-.js → src-DmEQFW5_.js} +9021 -8809
- 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/columns.js +9 -2
- 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/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +178 -139
- 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 +160 -160
- 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 +3680 -3680
- package/src/SvGrid.css +2814 -2804
- package/src/SvGrid.svelte +3490 -3490
- package/src/SvGrid.types.ts +1708 -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 +200 -200
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +112 -112
- 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.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- 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 +805 -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/columns.ts +7 -2
- 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 +164 -164
- 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 +157 -157
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/index.ts +724 -724
- 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 +289 -289
- package/src/server-data-source.ts +413 -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 +526 -526
- 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.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +322 -322
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.footer-pagesize.svelte.test.ts +86 -0
- 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.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +257 -257
- 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/src/createTree.svelte.ts
CHANGED
|
@@ -1,319 +1,319 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* createTree - the HEADLESS core behind <SvTree>, in the same spirit as
|
|
3
|
-
* `createSvGrid` for the grid: a runes-based state machine (expand/collapse,
|
|
4
|
-
* single-select highlight, cascading tri-state checkboxes, full keyboard) with
|
|
5
|
-
* **prop-getters** you spread onto YOUR OWN markup. The visible tree is
|
|
6
|
-
* flattened into `rows` so roving-focus keyboard nav stays a flat index walk.
|
|
7
|
-
*
|
|
8
|
-
* ```svelte
|
|
9
|
-
* <script lang="ts">
|
|
10
|
-
* import { createTree } from '@svgrid/grid'
|
|
11
|
-
* let selected = $state<string | null>(null)
|
|
12
|
-
* const tree = createTree({ nodes: () => nodes, selected: () => selected, onSelect: (id) => (selected = id) })
|
|
13
|
-
* </script>
|
|
14
|
-
* <div {...tree.treeProps()}>
|
|
15
|
-
* {#each tree.rows as row (row.node.id)}
|
|
16
|
-
* <div {...tree.itemProps(row)}>{row.node.label}</div>
|
|
17
|
-
* {/each}
|
|
18
|
-
* </div>
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* The styled <SvTree> is just one renderer over this core. The cascade math
|
|
22
|
-
* (`treeDescendantIds`, `treeCheckState`) is exported as pure functions.
|
|
23
|
-
*/
|
|
24
|
-
export type TreeNode = {
|
|
25
|
-
id: string
|
|
26
|
-
label: string
|
|
27
|
-
children?: TreeNode[]
|
|
28
|
-
disabled?: boolean
|
|
29
|
-
/** Marks a node whose children are loaded on demand (shows an expand arrow
|
|
30
|
-
* even with no children yet; the renderer loads them on first expand). */
|
|
31
|
-
lazy?: boolean
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type CheckState = 'checked' | 'indeterminate' | 'unchecked'
|
|
35
|
-
|
|
36
|
-
/** A single visible (flattened) tree row. */
|
|
37
|
-
export type TreeRow = {
|
|
38
|
-
node: TreeNode
|
|
39
|
-
depth: number
|
|
40
|
-
hasChildren: boolean
|
|
41
|
-
open: boolean
|
|
42
|
-
parentId: string | null
|
|
43
|
-
index: number
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** All descendant ids of `node` (pure, depth-first). */
|
|
47
|
-
export function treeDescendantIds(node: TreeNode): string[] {
|
|
48
|
-
const ids: string[] = []
|
|
49
|
-
const walk = (n: TreeNode) => { for (const c of n.children ?? []) { ids.push(c.id); walk(c) } }
|
|
50
|
-
walk(node)
|
|
51
|
-
return ids
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Tri-state check status of `node` given the set of checked ids (pure). */
|
|
55
|
-
export function treeCheckState(node: TreeNode, checked: ReadonlySet<string>): CheckState {
|
|
56
|
-
if (!node.children?.length) return checked.has(node.id) ? 'checked' : 'unchecked'
|
|
57
|
-
const desc = treeDescendantIds(node)
|
|
58
|
-
const on = desc.filter((id) => checked.has(id)).length
|
|
59
|
-
if (on === 0 && !checked.has(node.id)) return 'unchecked'
|
|
60
|
-
if (on === desc.length) return 'checked'
|
|
61
|
-
return 'indeterminate'
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function findNode(list: ReadonlyArray<TreeNode>, id: string): TreeNode | null {
|
|
65
|
-
for (const n of list) {
|
|
66
|
-
if (n.id === id) return n
|
|
67
|
-
if (n.children) { const f = findNode(n.children, id); if (f) return f }
|
|
68
|
-
}
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Where a dragged node lands relative to the drop target. */
|
|
73
|
-
export type TreeDropPosition = 'before' | 'after' | 'inside'
|
|
74
|
-
|
|
75
|
-
/** True if `id` is inside `node`'s subtree (used to block invalid drops). */
|
|
76
|
-
export function treeContains(node: TreeNode, id: string): boolean {
|
|
77
|
-
return node.children?.some((c) => c.id === id || treeContains(c, id)) ?? false
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Return a NEW node tree with `dragId` moved before/after/inside `targetId`
|
|
82
|
-
* (pure - the caller sets it back as the controlled `nodes`). No-ops on invalid
|
|
83
|
-
* moves (self, or into own descendant).
|
|
84
|
-
*/
|
|
85
|
-
export function moveTreeNode(
|
|
86
|
-
nodes: ReadonlyArray<TreeNode>,
|
|
87
|
-
dragId: string,
|
|
88
|
-
targetId: string,
|
|
89
|
-
position: TreeDropPosition,
|
|
90
|
-
): TreeNode[] {
|
|
91
|
-
if (dragId === targetId) return [...nodes]
|
|
92
|
-
const dragged = findNode(nodes, dragId)
|
|
93
|
-
if (!dragged) return [...nodes]
|
|
94
|
-
if (dragId === targetId || treeContains(dragged, targetId)) return [...nodes]
|
|
95
|
-
|
|
96
|
-
const without = (list: ReadonlyArray<TreeNode>): TreeNode[] => {
|
|
97
|
-
const out: TreeNode[] = []
|
|
98
|
-
for (const n of list) {
|
|
99
|
-
if (n.id === dragId) continue
|
|
100
|
-
out.push(n.children ? { ...n, children: without(n.children) } : n)
|
|
101
|
-
}
|
|
102
|
-
return out
|
|
103
|
-
}
|
|
104
|
-
const insert = (list: ReadonlyArray<TreeNode>): TreeNode[] => {
|
|
105
|
-
const out: TreeNode[] = []
|
|
106
|
-
for (const n of list) {
|
|
107
|
-
if (n.id === targetId) {
|
|
108
|
-
if (position === 'before') { out.push(dragged, n) }
|
|
109
|
-
else if (position === 'after') { out.push(n, dragged) }
|
|
110
|
-
else out.push({ ...n, children: [...(n.children ?? []), dragged] })
|
|
111
|
-
} else if (n.children) {
|
|
112
|
-
out.push({ ...n, children: insert(n.children) })
|
|
113
|
-
} else out.push(n)
|
|
114
|
-
}
|
|
115
|
-
return out
|
|
116
|
-
}
|
|
117
|
-
return insert(without(nodes))
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Sort siblings (recursively) by a comparator (pure). */
|
|
121
|
-
export function sortTreeNodes(
|
|
122
|
-
nodes: ReadonlyArray<TreeNode>,
|
|
123
|
-
compare: (a: TreeNode, b: TreeNode) => number,
|
|
124
|
-
): TreeNode[] {
|
|
125
|
-
return [...nodes]
|
|
126
|
-
.sort(compare)
|
|
127
|
-
.map((n) => (n.children ? { ...n, children: sortTreeNodes(n.children, compare) } : n))
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
131
|
-
export type TreeConfig = {
|
|
132
|
-
nodes: () => ReadonlyArray<TreeNode>
|
|
133
|
-
selected?: () => string | null
|
|
134
|
-
onSelect?: (id: string) => void
|
|
135
|
-
/** Seed the internally-managed expanded set (read once). */
|
|
136
|
-
expandedIds?: () => string[] | undefined
|
|
137
|
-
onToggle?: (id: string, expanded: boolean) => void
|
|
138
|
-
checkable?: () => boolean
|
|
139
|
-
checked?: () => string[]
|
|
140
|
-
onCheck?: (ids: string[]) => void
|
|
141
|
-
ariaLabel?: () => string | undefined
|
|
142
|
-
/** Text direction; under `'rtl'` the Left/Right expand/collapse keys swap. */
|
|
143
|
-
dir?: () => import('./editor-contract').EditorDir | undefined
|
|
144
|
-
/** Filter query: show only matching nodes + their ancestors, auto-expanded. */
|
|
145
|
-
filter?: () => string | undefined
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function createTree(config: TreeConfig) {
|
|
149
|
-
const nodes = () => config.nodes()
|
|
150
|
-
const checkable = () => config.checkable?.() ?? false
|
|
151
|
-
const selected = () => config.selected?.() ?? null
|
|
152
|
-
|
|
153
|
-
// Internal expanded set, seeded from the prop once (then self-managed).
|
|
154
|
-
let expanded = $state<Set<string>>(new Set())
|
|
155
|
-
let seeded = false
|
|
156
|
-
$effect(() => {
|
|
157
|
-
if (!seeded) { seeded = true; const ids = config.expandedIds?.(); if (ids) expanded = new Set(ids) }
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
// When filtering, precompute which nodes to show (a match, or an ancestor of a
|
|
161
|
-
// match) and which to force-open so matches are revealed.
|
|
162
|
-
const filterInfo = $derived.by(() => {
|
|
163
|
-
const q = config.filter?.()?.trim().toLowerCase()
|
|
164
|
-
if (!q) return null
|
|
165
|
-
const visible = new Set<string>()
|
|
166
|
-
const forceOpen = new Set<string>()
|
|
167
|
-
const walk = (node: TreeNode): boolean => {
|
|
168
|
-
let childMatch = false
|
|
169
|
-
for (const c of node.children ?? []) childMatch = walk(c) || childMatch
|
|
170
|
-
const selfMatch = node.label.toLowerCase().includes(q)
|
|
171
|
-
if (selfMatch || childMatch) { visible.add(node.id); if (childMatch) forceOpen.add(node.id) }
|
|
172
|
-
return selfMatch || childMatch
|
|
173
|
-
}
|
|
174
|
-
for (const n of nodes()) walk(n)
|
|
175
|
-
return { visible, forceOpen }
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
const rows = $derived.by<TreeRow[]>(() => {
|
|
179
|
-
const out: TreeRow[] = []
|
|
180
|
-
const fi = filterInfo
|
|
181
|
-
const walk = (list: ReadonlyArray<TreeNode>, depth: number, parentId: string | null) => {
|
|
182
|
-
for (const node of list) {
|
|
183
|
-
if (fi && !fi.visible.has(node.id)) continue
|
|
184
|
-
const hasChildren = !!node.children?.length || !!node.lazy
|
|
185
|
-
const open = fi ? fi.forceOpen.has(node.id) || expanded.has(node.id) : expanded.has(node.id)
|
|
186
|
-
out.push({ node, depth, hasChildren, open, parentId, index: out.length })
|
|
187
|
-
if (open && node.children?.length) walk(node.children, depth + 1, node.id)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
walk(nodes(), 0, null)
|
|
191
|
-
return out
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
let active = $state(0)
|
|
195
|
-
$effect(() => { if (active >= rows.length) active = Math.max(0, rows.length - 1) })
|
|
196
|
-
|
|
197
|
-
const checkedSet = $derived(new Set(config.checked?.() ?? []))
|
|
198
|
-
|
|
199
|
-
// Roving focus target; `focusTick` bumps only on keyboard moves.
|
|
200
|
-
let focusTick = $state(0)
|
|
201
|
-
function setActive(i: number, focus = false) { active = i; if (focus) focusTick++ }
|
|
202
|
-
|
|
203
|
-
const checkStateOf = (node: TreeNode): CheckState => treeCheckState(node, checkedSet)
|
|
204
|
-
const isExpanded = (id: string) => expanded.has(id)
|
|
205
|
-
const isSelected = (id: string) => id === selected()
|
|
206
|
-
|
|
207
|
-
function toggleExpand(node: TreeNode) {
|
|
208
|
-
// Lazy nodes have no children *yet* - they must still be expandable so the
|
|
209
|
-
// toggle fires onToggle and the host can fetch children on demand.
|
|
210
|
-
if (!node.children?.length && !node.lazy) return
|
|
211
|
-
const next = new Set(expanded)
|
|
212
|
-
const willOpen = !next.has(node.id)
|
|
213
|
-
willOpen ? next.add(node.id) : next.delete(node.id)
|
|
214
|
-
expanded = next
|
|
215
|
-
config.onToggle?.(node.id, willOpen)
|
|
216
|
-
}
|
|
217
|
-
const toggle = (id: string) => { const n = findNode(nodes(), id); if (n) toggleExpand(n) }
|
|
218
|
-
|
|
219
|
-
function select(node: TreeNode) {
|
|
220
|
-
if (node.disabled) return
|
|
221
|
-
config.onSelect?.(node.id)
|
|
222
|
-
}
|
|
223
|
-
const selectId = (id: string) => { const n = findNode(nodes(), id); if (n) select(n) }
|
|
224
|
-
|
|
225
|
-
function toggleCheck(node: TreeNode) {
|
|
226
|
-
if (node.disabled) return
|
|
227
|
-
const next = new Set(checkedSet)
|
|
228
|
-
const ids = [node.id, ...treeDescendantIds(node)]
|
|
229
|
-
const currentlyOn = checkStateOf(node) === 'checked'
|
|
230
|
-
for (const id of ids) currentlyOn ? next.delete(id) : next.add(id)
|
|
231
|
-
config.onCheck?.([...next])
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Step from `from` towards `dir` (+1/-1), skipping disabled rows, so roving
|
|
235
|
-
// focus never lands on a node with zero semantic affordance for AT users.
|
|
236
|
-
// Falls back to `from` if every row in that direction is disabled.
|
|
237
|
-
function stepEnabled(from: number, dir: 1 | -1): number {
|
|
238
|
-
let i = from
|
|
239
|
-
while (i + dir >= 0 && i + dir <= rows.length - 1) {
|
|
240
|
-
i += dir
|
|
241
|
-
if (!rows[i]?.node.disabled) return i
|
|
242
|
-
}
|
|
243
|
-
return from
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function onKeydown(e: KeyboardEvent) {
|
|
247
|
-
const item = rows[active]
|
|
248
|
-
if (!item) return
|
|
249
|
-
// "expand" opens the node or steps into the first child; "collapse" closes it
|
|
250
|
-
// or jumps to the parent. Under RTL the physical Left/Right keys are swapped.
|
|
251
|
-
const expand = () => {
|
|
252
|
-
if (item.hasChildren && !item.open) toggleExpand(item.node)
|
|
253
|
-
else if (item.hasChildren && item.open) setActive(Math.min(active + 1, rows.length - 1), true)
|
|
254
|
-
}
|
|
255
|
-
const collapse = () => {
|
|
256
|
-
if (item.hasChildren && item.open) toggleExpand(item.node)
|
|
257
|
-
else if (item.parentId) { const pi = rows.findIndex((r) => r.node.id === item.parentId); if (pi >= 0) setActive(pi, true) }
|
|
258
|
-
}
|
|
259
|
-
const rtl = config.dir?.() === 'rtl'
|
|
260
|
-
switch (e.key) {
|
|
261
|
-
case 'ArrowDown': e.preventDefault(); setActive(stepEnabled(active, 1), true); break
|
|
262
|
-
case 'ArrowUp': e.preventDefault(); setActive(stepEnabled(active, -1), true); break
|
|
263
|
-
case 'ArrowRight': e.preventDefault(); (rtl ? collapse : expand)(); break
|
|
264
|
-
case 'ArrowLeft': e.preventDefault(); (rtl ? expand : collapse)(); break
|
|
265
|
-
case 'Enter': e.preventDefault(); select(item.node); break
|
|
266
|
-
case ' ': e.preventDefault(); checkable() ? toggleCheck(item.node) : select(item.node); break
|
|
267
|
-
case 'Home': e.preventDefault(); setActive(0, true); break
|
|
268
|
-
case 'End': e.preventDefault(); setActive(rows.length - 1, true); break
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return {
|
|
273
|
-
/** Internally-managed set of expanded node ids. */
|
|
274
|
-
get expanded() { return expanded },
|
|
275
|
-
/** Selected node id (single-select highlight). */
|
|
276
|
-
get selected() { return selected() },
|
|
277
|
-
/** Flattened list of visible rows. */
|
|
278
|
-
get rows() { return rows },
|
|
279
|
-
/** Roving-focus row index. */
|
|
280
|
-
get activeIndex() { return active },
|
|
281
|
-
/** Monotonic counter; changes only on keyboard navigation. */
|
|
282
|
-
get focusTick() { return focusTick },
|
|
283
|
-
isExpanded,
|
|
284
|
-
isSelected,
|
|
285
|
-
checkStateOf,
|
|
286
|
-
toggle,
|
|
287
|
-
toggleExpand,
|
|
288
|
-
select: selectId,
|
|
289
|
-
toggleCheck,
|
|
290
|
-
setActive,
|
|
291
|
-
onKeydown,
|
|
292
|
-
/** Spread onto the tree container element. */
|
|
293
|
-
treeProps: () => ({
|
|
294
|
-
role: 'tree' as const,
|
|
295
|
-
'aria-label': config.ariaLabel?.(),
|
|
296
|
-
'aria-multiselectable': checkable(),
|
|
297
|
-
}),
|
|
298
|
-
/** Spread onto the treeitem element for a flattened `row`. */
|
|
299
|
-
itemProps: (row: TreeRow) => {
|
|
300
|
-
const cs = checkable() ? checkStateOf(row.node) : 'unchecked'
|
|
301
|
-
const ariaChecked: 'true' | 'mixed' | 'false' | undefined =
|
|
302
|
-
checkable() ? (cs === 'checked' ? 'true' : cs === 'indeterminate' ? 'mixed' : 'false') : undefined
|
|
303
|
-
return {
|
|
304
|
-
role: 'treeitem' as const,
|
|
305
|
-
'aria-level': row.depth + 1,
|
|
306
|
-
'aria-selected': row.node.id === selected(),
|
|
307
|
-
'aria-expanded': row.hasChildren ? row.open : undefined,
|
|
308
|
-
'aria-checked': ariaChecked,
|
|
309
|
-
'aria-disabled': row.node.disabled || undefined,
|
|
310
|
-
'data-row': row.index,
|
|
311
|
-
tabindex: row.index === active ? 0 : -1,
|
|
312
|
-
onclick: () => { setActive(row.index); select(row.node) },
|
|
313
|
-
onkeydown: onKeydown,
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export type Tree = ReturnType<typeof createTree>
|
|
1
|
+
/**
|
|
2
|
+
* createTree - the HEADLESS core behind <SvTree>, in the same spirit as
|
|
3
|
+
* `createSvGrid` for the grid: a runes-based state machine (expand/collapse,
|
|
4
|
+
* single-select highlight, cascading tri-state checkboxes, full keyboard) with
|
|
5
|
+
* **prop-getters** you spread onto YOUR OWN markup. The visible tree is
|
|
6
|
+
* flattened into `rows` so roving-focus keyboard nav stays a flat index walk.
|
|
7
|
+
*
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script lang="ts">
|
|
10
|
+
* import { createTree } from '@svgrid/grid'
|
|
11
|
+
* let selected = $state<string | null>(null)
|
|
12
|
+
* const tree = createTree({ nodes: () => nodes, selected: () => selected, onSelect: (id) => (selected = id) })
|
|
13
|
+
* </script>
|
|
14
|
+
* <div {...tree.treeProps()}>
|
|
15
|
+
* {#each tree.rows as row (row.node.id)}
|
|
16
|
+
* <div {...tree.itemProps(row)}>{row.node.label}</div>
|
|
17
|
+
* {/each}
|
|
18
|
+
* </div>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* The styled <SvTree> is just one renderer over this core. The cascade math
|
|
22
|
+
* (`treeDescendantIds`, `treeCheckState`) is exported as pure functions.
|
|
23
|
+
*/
|
|
24
|
+
export type TreeNode = {
|
|
25
|
+
id: string
|
|
26
|
+
label: string
|
|
27
|
+
children?: TreeNode[]
|
|
28
|
+
disabled?: boolean
|
|
29
|
+
/** Marks a node whose children are loaded on demand (shows an expand arrow
|
|
30
|
+
* even with no children yet; the renderer loads them on first expand). */
|
|
31
|
+
lazy?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type CheckState = 'checked' | 'indeterminate' | 'unchecked'
|
|
35
|
+
|
|
36
|
+
/** A single visible (flattened) tree row. */
|
|
37
|
+
export type TreeRow = {
|
|
38
|
+
node: TreeNode
|
|
39
|
+
depth: number
|
|
40
|
+
hasChildren: boolean
|
|
41
|
+
open: boolean
|
|
42
|
+
parentId: string | null
|
|
43
|
+
index: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** All descendant ids of `node` (pure, depth-first). */
|
|
47
|
+
export function treeDescendantIds(node: TreeNode): string[] {
|
|
48
|
+
const ids: string[] = []
|
|
49
|
+
const walk = (n: TreeNode) => { for (const c of n.children ?? []) { ids.push(c.id); walk(c) } }
|
|
50
|
+
walk(node)
|
|
51
|
+
return ids
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Tri-state check status of `node` given the set of checked ids (pure). */
|
|
55
|
+
export function treeCheckState(node: TreeNode, checked: ReadonlySet<string>): CheckState {
|
|
56
|
+
if (!node.children?.length) return checked.has(node.id) ? 'checked' : 'unchecked'
|
|
57
|
+
const desc = treeDescendantIds(node)
|
|
58
|
+
const on = desc.filter((id) => checked.has(id)).length
|
|
59
|
+
if (on === 0 && !checked.has(node.id)) return 'unchecked'
|
|
60
|
+
if (on === desc.length) return 'checked'
|
|
61
|
+
return 'indeterminate'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function findNode(list: ReadonlyArray<TreeNode>, id: string): TreeNode | null {
|
|
65
|
+
for (const n of list) {
|
|
66
|
+
if (n.id === id) return n
|
|
67
|
+
if (n.children) { const f = findNode(n.children, id); if (f) return f }
|
|
68
|
+
}
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Where a dragged node lands relative to the drop target. */
|
|
73
|
+
export type TreeDropPosition = 'before' | 'after' | 'inside'
|
|
74
|
+
|
|
75
|
+
/** True if `id` is inside `node`'s subtree (used to block invalid drops). */
|
|
76
|
+
export function treeContains(node: TreeNode, id: string): boolean {
|
|
77
|
+
return node.children?.some((c) => c.id === id || treeContains(c, id)) ?? false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Return a NEW node tree with `dragId` moved before/after/inside `targetId`
|
|
82
|
+
* (pure - the caller sets it back as the controlled `nodes`). No-ops on invalid
|
|
83
|
+
* moves (self, or into own descendant).
|
|
84
|
+
*/
|
|
85
|
+
export function moveTreeNode(
|
|
86
|
+
nodes: ReadonlyArray<TreeNode>,
|
|
87
|
+
dragId: string,
|
|
88
|
+
targetId: string,
|
|
89
|
+
position: TreeDropPosition,
|
|
90
|
+
): TreeNode[] {
|
|
91
|
+
if (dragId === targetId) return [...nodes]
|
|
92
|
+
const dragged = findNode(nodes, dragId)
|
|
93
|
+
if (!dragged) return [...nodes]
|
|
94
|
+
if (dragId === targetId || treeContains(dragged, targetId)) return [...nodes]
|
|
95
|
+
|
|
96
|
+
const without = (list: ReadonlyArray<TreeNode>): TreeNode[] => {
|
|
97
|
+
const out: TreeNode[] = []
|
|
98
|
+
for (const n of list) {
|
|
99
|
+
if (n.id === dragId) continue
|
|
100
|
+
out.push(n.children ? { ...n, children: without(n.children) } : n)
|
|
101
|
+
}
|
|
102
|
+
return out
|
|
103
|
+
}
|
|
104
|
+
const insert = (list: ReadonlyArray<TreeNode>): TreeNode[] => {
|
|
105
|
+
const out: TreeNode[] = []
|
|
106
|
+
for (const n of list) {
|
|
107
|
+
if (n.id === targetId) {
|
|
108
|
+
if (position === 'before') { out.push(dragged, n) }
|
|
109
|
+
else if (position === 'after') { out.push(n, dragged) }
|
|
110
|
+
else out.push({ ...n, children: [...(n.children ?? []), dragged] })
|
|
111
|
+
} else if (n.children) {
|
|
112
|
+
out.push({ ...n, children: insert(n.children) })
|
|
113
|
+
} else out.push(n)
|
|
114
|
+
}
|
|
115
|
+
return out
|
|
116
|
+
}
|
|
117
|
+
return insert(without(nodes))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Sort siblings (recursively) by a comparator (pure). */
|
|
121
|
+
export function sortTreeNodes(
|
|
122
|
+
nodes: ReadonlyArray<TreeNode>,
|
|
123
|
+
compare: (a: TreeNode, b: TreeNode) => number,
|
|
124
|
+
): TreeNode[] {
|
|
125
|
+
return [...nodes]
|
|
126
|
+
.sort(compare)
|
|
127
|
+
.map((n) => (n.children ? { ...n, children: sortTreeNodes(n.children, compare) } : n))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Reactive inputs are passed as getters so the core tracks live prop changes. */
|
|
131
|
+
export type TreeConfig = {
|
|
132
|
+
nodes: () => ReadonlyArray<TreeNode>
|
|
133
|
+
selected?: () => string | null
|
|
134
|
+
onSelect?: (id: string) => void
|
|
135
|
+
/** Seed the internally-managed expanded set (read once). */
|
|
136
|
+
expandedIds?: () => string[] | undefined
|
|
137
|
+
onToggle?: (id: string, expanded: boolean) => void
|
|
138
|
+
checkable?: () => boolean
|
|
139
|
+
checked?: () => string[]
|
|
140
|
+
onCheck?: (ids: string[]) => void
|
|
141
|
+
ariaLabel?: () => string | undefined
|
|
142
|
+
/** Text direction; under `'rtl'` the Left/Right expand/collapse keys swap. */
|
|
143
|
+
dir?: () => import('./editor-contract').EditorDir | undefined
|
|
144
|
+
/** Filter query: show only matching nodes + their ancestors, auto-expanded. */
|
|
145
|
+
filter?: () => string | undefined
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function createTree(config: TreeConfig) {
|
|
149
|
+
const nodes = () => config.nodes()
|
|
150
|
+
const checkable = () => config.checkable?.() ?? false
|
|
151
|
+
const selected = () => config.selected?.() ?? null
|
|
152
|
+
|
|
153
|
+
// Internal expanded set, seeded from the prop once (then self-managed).
|
|
154
|
+
let expanded = $state<Set<string>>(new Set())
|
|
155
|
+
let seeded = false
|
|
156
|
+
$effect(() => {
|
|
157
|
+
if (!seeded) { seeded = true; const ids = config.expandedIds?.(); if (ids) expanded = new Set(ids) }
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// When filtering, precompute which nodes to show (a match, or an ancestor of a
|
|
161
|
+
// match) and which to force-open so matches are revealed.
|
|
162
|
+
const filterInfo = $derived.by(() => {
|
|
163
|
+
const q = config.filter?.()?.trim().toLowerCase()
|
|
164
|
+
if (!q) return null
|
|
165
|
+
const visible = new Set<string>()
|
|
166
|
+
const forceOpen = new Set<string>()
|
|
167
|
+
const walk = (node: TreeNode): boolean => {
|
|
168
|
+
let childMatch = false
|
|
169
|
+
for (const c of node.children ?? []) childMatch = walk(c) || childMatch
|
|
170
|
+
const selfMatch = node.label.toLowerCase().includes(q)
|
|
171
|
+
if (selfMatch || childMatch) { visible.add(node.id); if (childMatch) forceOpen.add(node.id) }
|
|
172
|
+
return selfMatch || childMatch
|
|
173
|
+
}
|
|
174
|
+
for (const n of nodes()) walk(n)
|
|
175
|
+
return { visible, forceOpen }
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const rows = $derived.by<TreeRow[]>(() => {
|
|
179
|
+
const out: TreeRow[] = []
|
|
180
|
+
const fi = filterInfo
|
|
181
|
+
const walk = (list: ReadonlyArray<TreeNode>, depth: number, parentId: string | null) => {
|
|
182
|
+
for (const node of list) {
|
|
183
|
+
if (fi && !fi.visible.has(node.id)) continue
|
|
184
|
+
const hasChildren = !!node.children?.length || !!node.lazy
|
|
185
|
+
const open = fi ? fi.forceOpen.has(node.id) || expanded.has(node.id) : expanded.has(node.id)
|
|
186
|
+
out.push({ node, depth, hasChildren, open, parentId, index: out.length })
|
|
187
|
+
if (open && node.children?.length) walk(node.children, depth + 1, node.id)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
walk(nodes(), 0, null)
|
|
191
|
+
return out
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
let active = $state(0)
|
|
195
|
+
$effect(() => { if (active >= rows.length) active = Math.max(0, rows.length - 1) })
|
|
196
|
+
|
|
197
|
+
const checkedSet = $derived(new Set(config.checked?.() ?? []))
|
|
198
|
+
|
|
199
|
+
// Roving focus target; `focusTick` bumps only on keyboard moves.
|
|
200
|
+
let focusTick = $state(0)
|
|
201
|
+
function setActive(i: number, focus = false) { active = i; if (focus) focusTick++ }
|
|
202
|
+
|
|
203
|
+
const checkStateOf = (node: TreeNode): CheckState => treeCheckState(node, checkedSet)
|
|
204
|
+
const isExpanded = (id: string) => expanded.has(id)
|
|
205
|
+
const isSelected = (id: string) => id === selected()
|
|
206
|
+
|
|
207
|
+
function toggleExpand(node: TreeNode) {
|
|
208
|
+
// Lazy nodes have no children *yet* - they must still be expandable so the
|
|
209
|
+
// toggle fires onToggle and the host can fetch children on demand.
|
|
210
|
+
if (!node.children?.length && !node.lazy) return
|
|
211
|
+
const next = new Set(expanded)
|
|
212
|
+
const willOpen = !next.has(node.id)
|
|
213
|
+
willOpen ? next.add(node.id) : next.delete(node.id)
|
|
214
|
+
expanded = next
|
|
215
|
+
config.onToggle?.(node.id, willOpen)
|
|
216
|
+
}
|
|
217
|
+
const toggle = (id: string) => { const n = findNode(nodes(), id); if (n) toggleExpand(n) }
|
|
218
|
+
|
|
219
|
+
function select(node: TreeNode) {
|
|
220
|
+
if (node.disabled) return
|
|
221
|
+
config.onSelect?.(node.id)
|
|
222
|
+
}
|
|
223
|
+
const selectId = (id: string) => { const n = findNode(nodes(), id); if (n) select(n) }
|
|
224
|
+
|
|
225
|
+
function toggleCheck(node: TreeNode) {
|
|
226
|
+
if (node.disabled) return
|
|
227
|
+
const next = new Set(checkedSet)
|
|
228
|
+
const ids = [node.id, ...treeDescendantIds(node)]
|
|
229
|
+
const currentlyOn = checkStateOf(node) === 'checked'
|
|
230
|
+
for (const id of ids) currentlyOn ? next.delete(id) : next.add(id)
|
|
231
|
+
config.onCheck?.([...next])
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Step from `from` towards `dir` (+1/-1), skipping disabled rows, so roving
|
|
235
|
+
// focus never lands on a node with zero semantic affordance for AT users.
|
|
236
|
+
// Falls back to `from` if every row in that direction is disabled.
|
|
237
|
+
function stepEnabled(from: number, dir: 1 | -1): number {
|
|
238
|
+
let i = from
|
|
239
|
+
while (i + dir >= 0 && i + dir <= rows.length - 1) {
|
|
240
|
+
i += dir
|
|
241
|
+
if (!rows[i]?.node.disabled) return i
|
|
242
|
+
}
|
|
243
|
+
return from
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function onKeydown(e: KeyboardEvent) {
|
|
247
|
+
const item = rows[active]
|
|
248
|
+
if (!item) return
|
|
249
|
+
// "expand" opens the node or steps into the first child; "collapse" closes it
|
|
250
|
+
// or jumps to the parent. Under RTL the physical Left/Right keys are swapped.
|
|
251
|
+
const expand = () => {
|
|
252
|
+
if (item.hasChildren && !item.open) toggleExpand(item.node)
|
|
253
|
+
else if (item.hasChildren && item.open) setActive(Math.min(active + 1, rows.length - 1), true)
|
|
254
|
+
}
|
|
255
|
+
const collapse = () => {
|
|
256
|
+
if (item.hasChildren && item.open) toggleExpand(item.node)
|
|
257
|
+
else if (item.parentId) { const pi = rows.findIndex((r) => r.node.id === item.parentId); if (pi >= 0) setActive(pi, true) }
|
|
258
|
+
}
|
|
259
|
+
const rtl = config.dir?.() === 'rtl'
|
|
260
|
+
switch (e.key) {
|
|
261
|
+
case 'ArrowDown': e.preventDefault(); setActive(stepEnabled(active, 1), true); break
|
|
262
|
+
case 'ArrowUp': e.preventDefault(); setActive(stepEnabled(active, -1), true); break
|
|
263
|
+
case 'ArrowRight': e.preventDefault(); (rtl ? collapse : expand)(); break
|
|
264
|
+
case 'ArrowLeft': e.preventDefault(); (rtl ? expand : collapse)(); break
|
|
265
|
+
case 'Enter': e.preventDefault(); select(item.node); break
|
|
266
|
+
case ' ': e.preventDefault(); checkable() ? toggleCheck(item.node) : select(item.node); break
|
|
267
|
+
case 'Home': e.preventDefault(); setActive(0, true); break
|
|
268
|
+
case 'End': e.preventDefault(); setActive(rows.length - 1, true); break
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
/** Internally-managed set of expanded node ids. */
|
|
274
|
+
get expanded() { return expanded },
|
|
275
|
+
/** Selected node id (single-select highlight). */
|
|
276
|
+
get selected() { return selected() },
|
|
277
|
+
/** Flattened list of visible rows. */
|
|
278
|
+
get rows() { return rows },
|
|
279
|
+
/** Roving-focus row index. */
|
|
280
|
+
get activeIndex() { return active },
|
|
281
|
+
/** Monotonic counter; changes only on keyboard navigation. */
|
|
282
|
+
get focusTick() { return focusTick },
|
|
283
|
+
isExpanded,
|
|
284
|
+
isSelected,
|
|
285
|
+
checkStateOf,
|
|
286
|
+
toggle,
|
|
287
|
+
toggleExpand,
|
|
288
|
+
select: selectId,
|
|
289
|
+
toggleCheck,
|
|
290
|
+
setActive,
|
|
291
|
+
onKeydown,
|
|
292
|
+
/** Spread onto the tree container element. */
|
|
293
|
+
treeProps: () => ({
|
|
294
|
+
role: 'tree' as const,
|
|
295
|
+
'aria-label': config.ariaLabel?.(),
|
|
296
|
+
'aria-multiselectable': checkable(),
|
|
297
|
+
}),
|
|
298
|
+
/** Spread onto the treeitem element for a flattened `row`. */
|
|
299
|
+
itemProps: (row: TreeRow) => {
|
|
300
|
+
const cs = checkable() ? checkStateOf(row.node) : 'unchecked'
|
|
301
|
+
const ariaChecked: 'true' | 'mixed' | 'false' | undefined =
|
|
302
|
+
checkable() ? (cs === 'checked' ? 'true' : cs === 'indeterminate' ? 'mixed' : 'false') : undefined
|
|
303
|
+
return {
|
|
304
|
+
role: 'treeitem' as const,
|
|
305
|
+
'aria-level': row.depth + 1,
|
|
306
|
+
'aria-selected': row.node.id === selected(),
|
|
307
|
+
'aria-expanded': row.hasChildren ? row.open : undefined,
|
|
308
|
+
'aria-checked': ariaChecked,
|
|
309
|
+
'aria-disabled': row.node.disabled || undefined,
|
|
310
|
+
'data-row': row.index,
|
|
311
|
+
tabindex: row.index === active ? 0 : -1,
|
|
312
|
+
onclick: () => { setActive(row.index); select(row.node) },
|
|
313
|
+
onkeydown: onKeydown,
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export type Tree = ReturnType<typeof createTree>
|