@svgrid/grid 2.6.21 → 2.7.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/CHANGELOG.md +150 -88
- package/README.md +221 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +181 -181
- package/dist/GridMenus.svelte +201 -18
- package/dist/SvAutoComplete.svelte +169 -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 +279 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +378 -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 +61 -9
- package/dist/SvGrid.controller.svelte.js +363 -123
- package/dist/SvGrid.css +222 -1
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +255 -56
- package/dist/SvGrid.types.d.ts +146 -1
- package/dist/SvGridCellEditor.svelte +744 -662
- 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 +270 -266
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +334 -326
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +293 -289
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +172 -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/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- 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/SvTextArea.svelte +5 -1
- 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 +239 -235
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/builtin-editors.d.ts +2 -2
- package/dist/builtin-editors.js +19 -3
- package/dist/cdn/GridMenus-C4oLsJyj.js +600 -0
- package/dist/cdn/GridMenus-yEivjNwl.js +601 -0
- package/dist/cdn/SvDateRangeInput-Bh0A0JkF.js +199 -0
- package/dist/cdn/SvDateRangeInput-DflbiP7N.js +198 -0
- package/dist/cdn/SvDateTimePicker-BWwpfB_o.js +812 -0
- package/dist/cdn/SvDateTimePicker-Bivn8dAP.js +816 -0
- package/dist/cdn/SvGridCellEditor-AXL8cHGO.js +550 -0
- package/dist/cdn/SvGridCellEditor-Ba7rY3eu.js +549 -0
- 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/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/date-format-CcP1tafP.js +1395 -0
- package/dist/cdn/date-format-DZT7T1wf.js +1393 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-BYq-qyrp.js → src-COC0vnpg.js} +7846 -7317
- package/dist/cdn/{src-DBel9wRZ.js → src-pYGV2rMR.js} +10033 -9504
- package/dist/cdn/svgrid.js +15 -11
- package/dist/cdn/svgrid.svelte-external.js +15 -11
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/chart-export.js +8 -8
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- package/dist/column-groups.js +1 -1
- package/dist/column-resize.d.ts +46 -0
- package/dist/column-resize.js +205 -0
- package/dist/columns.d.ts +0 -3
- package/dist/columns.js +0 -57
- package/dist/core.d.ts +19 -4
- package/dist/core.js +460 -119
- package/dist/editing.js +12 -7
- package/dist/editors/cell-editors.d.ts +11 -0
- package/dist/editors/cell-editors.js +18 -1
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +41 -8
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/sanitize-html.d.ts +37 -0
- package/dist/sanitize-html.js +234 -0
- package/dist/selection-bar-view.svelte.d.ts +25 -0
- package/dist/selection-bar-view.svelte.js +13 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +133 -8
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +181 -181
- package/src/GridMenus.svelte +201 -18
- package/src/SvAutoComplete.svelte +169 -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 +279 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +378 -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 +404 -133
- package/src/SvGrid.css +222 -1
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +255 -56
- package/src/SvGrid.types.ts +2092 -1940
- package/src/SvGridCellEditor.svelte +744 -662
- 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 +270 -266
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +334 -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 +293 -289
- package/src/SvNumberInput.svelte +172 -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/SvRichCell.svelte +125 -0
- 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/SvTextArea.svelte +5 -1
- 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 +239 -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 +1419 -1419
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +846 -846
- package/src/builtin-editors.grid.test.ts +83 -83
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-formatting.ts +171 -171
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +148 -118
- package/src/chart-export.ts +202 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2321 -2321
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.ts +78 -78
- package/src/column-resize.test.ts +381 -0
- package/src/column-resize.ts +227 -0
- package/src/columns.test.ts +0 -103
- package/src/columns.ts +0 -58
- package/src/core.aggregate.test.ts +134 -0
- package/src/core.filter.test.ts +156 -0
- package/src/core.grouping.test.ts +146 -0
- package/src/core.performance.test.ts +30 -30
- package/src/core.row-shape.test.ts +119 -0
- package/src/core.rowmodel-cache.test.ts +121 -0
- package/src/core.sort.test.ts +293 -0
- package/src/core.ts +1865 -1468
- 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 +322 -322
- package/src/date-string-column.test.ts +87 -0
- package/src/datetime/date-core.ts +206 -206
- package/src/datetime/date-restrict.ts +61 -61
- package/src/datetime/timezone.ts +135 -135
- package/src/dock-manager-model.ts +596 -596
- package/src/dock-model.ts +374 -374
- package/src/editing.test.ts +974 -949
- package/src/editing.ts +609 -606
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editor-contract.ts +171 -171
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/editors/cell-editors.ts +27 -1
- 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 -160
- package/src/filtering/excel-filters.ts +325 -295
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/grid-messages.ts +34 -0
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/headless.ts +87 -87
- package/src/index.ts +25 -1
- 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 +179 -173
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +597 -567
- package/src/merge-objects.ts +48 -48
- package/src/move-cells.test.ts +850 -0
- package/src/number-editor-literal.test.ts +84 -0
- package/src/number-editor.grid.test.ts +160 -0
- package/src/overlays.test.ts +90 -90
- package/src/positioning.ts +268 -268
- package/src/render-component.ts +28 -28
- package/src/resize-props.test.ts +361 -0
- package/src/row-drag-touch.ts +211 -211
- package/src/row-drag.test.ts +353 -353
- package/src/row-drag.ts +415 -415
- package/src/row-resize.test.ts +524 -493
- package/src/row-resize.ts +228 -210
- package/src/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/scheduler-ical.ts +181 -181
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +873 -873
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +900 -743
- package/src/selection.ts +135 -8
- package/src/server-data-source.test.ts +383 -383
- package/src/server-data-source.ts +469 -469
- 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/summaries.ts +113 -113
- package/src/svgrid-wrapper.types.ts +563 -563
- package/src/svgrid.async-editor-options.test.ts +273 -273
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +910 -910
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +127 -127
- 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 +377 -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.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.upsell-license.test.ts +117 -0
- 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/index.ts +288 -288
- package/src/toast-store.svelte.ts +250 -250
- 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 +322 -322
- package/dist/cdn/GridMenus-BfTAKn84.js +0 -488
- package/dist/cdn/GridMenus-C3bJd7w8.js +0 -489
- package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
- package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
- package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
- package/dist/cdn/SvGridCellEditor-G8IMWIws.js +0 -522
package/src/column-groups.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
// Collapsible column groups (
|
|
2
|
-
// carry a collapse toggle: its child columns tagged `columnGroupShow: 'open'`
|
|
3
|
-
// show only when the group is expanded, `'closed'` show only when collapsed,
|
|
4
|
-
// and untagged children always show.
|
|
5
|
-
//
|
|
6
|
-
// This module derives, from the user's column tree, the pure metadata the
|
|
7
|
-
// controller needs: which groups are collapsible, their default state, and each
|
|
8
|
-
// controlled leaf's group + show-mode. The controller turns that into a hidden
|
|
9
|
-
// map (merged into column visibility) AND feeds the same hidden set into the
|
|
10
|
-
// group-header colspan/width math, so headers stay aligned with the leaves.
|
|
11
|
-
|
|
12
|
-
export type ColumnGroupShow = "open" | "closed";
|
|
13
|
-
|
|
14
|
-
export type ColumnGroupMeta = {
|
|
15
|
-
/** Group ids that render a collapse toggle. */
|
|
16
|
-
collapsibleGroupIds: Set<string>;
|
|
17
|
-
/** Group id -> whether it starts expanded (openByDefault). */
|
|
18
|
-
defaultOpen: Map<string, boolean>;
|
|
19
|
-
/** Leaf column id -> the group controlling it + its show mode. */
|
|
20
|
-
leafControl: Map<string, { groupId: string; show: ColumnGroupShow }>;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
import { resolveColumnId } from "./column-id";
|
|
24
|
-
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
-
export function computeColumnGroupMeta(columns: ReadonlyArray<any>): ColumnGroupMeta {
|
|
27
|
-
const collapsibleGroupIds = new Set<string>();
|
|
28
|
-
const defaultOpen = new Map<string, boolean>();
|
|
29
|
-
const leafControl = new Map<string, { groupId: string; show: ColumnGroupShow }>();
|
|
30
|
-
|
|
31
|
-
// `depth` is threaded because it is part of the id of an unnamed column -
|
|
32
|
-
// drop it and the ids here stop matching the ones the engine assigns, so
|
|
33
|
-
// `leafControl` lookups silently miss for those columns.
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
function walk(defs: ReadonlyArray<any>, parentId: string | undefined, controllingGroup: string | undefined, depth: number) {
|
|
36
|
-
defs.forEach((def, ix) => {
|
|
37
|
-
const id = resolveColumnId(def, parentId, depth, ix);
|
|
38
|
-
if (def.columns?.length) {
|
|
39
|
-
// A group is collapsible when any DIRECT child declares columnGroupShow.
|
|
40
|
-
const collapsible = def.columns.some(
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
(c: any) => c.columnGroupShow === "open" || c.columnGroupShow === "closed",
|
|
43
|
-
);
|
|
44
|
-
if (collapsible) {
|
|
45
|
-
collapsibleGroupIds.add(id);
|
|
46
|
-
defaultOpen.set(id, def.openByDefault === true);
|
|
47
|
-
}
|
|
48
|
-
walk(def.columns, id, collapsible ? id : controllingGroup, depth + 1);
|
|
49
|
-
} else {
|
|
50
|
-
const show = def.columnGroupShow;
|
|
51
|
-
if ((show === "open" || show === "closed") && controllingGroup) {
|
|
52
|
-
leafControl.set(id, { groupId: controllingGroup, show });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
walk(columns, undefined, undefined, 0);
|
|
59
|
-
return { collapsibleGroupIds, defaultOpen, leafControl };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Which leaf ids are hidden right now, given the set of collapsed group ids.
|
|
64
|
-
* `'open'` leaves hide while their group is collapsed; `'closed'` leaves hide
|
|
65
|
-
* while it is expanded.
|
|
66
|
-
*/
|
|
67
|
-
export function hiddenLeavesForCollapse(
|
|
68
|
-
meta: ColumnGroupMeta,
|
|
69
|
-
collapsed: ReadonlySet<string>,
|
|
70
|
-
): Record<string, boolean> {
|
|
71
|
-
const hidden: Record<string, boolean> = {};
|
|
72
|
-
for (const [leafId, ctrl] of meta.leafControl) {
|
|
73
|
-
const isCollapsed = collapsed.has(ctrl.groupId);
|
|
74
|
-
const hide = ctrl.show === "open" ? isCollapsed : !isCollapsed;
|
|
75
|
-
if (hide) hidden[leafId] = true;
|
|
76
|
-
}
|
|
77
|
-
return hidden;
|
|
78
|
-
}
|
|
1
|
+
// Collapsible column groups (a per-column `columnGroupShow` flag). A column group can
|
|
2
|
+
// carry a collapse toggle: its child columns tagged `columnGroupShow: 'open'`
|
|
3
|
+
// show only when the group is expanded, `'closed'` show only when collapsed,
|
|
4
|
+
// and untagged children always show.
|
|
5
|
+
//
|
|
6
|
+
// This module derives, from the user's column tree, the pure metadata the
|
|
7
|
+
// controller needs: which groups are collapsible, their default state, and each
|
|
8
|
+
// controlled leaf's group + show-mode. The controller turns that into a hidden
|
|
9
|
+
// map (merged into column visibility) AND feeds the same hidden set into the
|
|
10
|
+
// group-header colspan/width math, so headers stay aligned with the leaves.
|
|
11
|
+
|
|
12
|
+
export type ColumnGroupShow = "open" | "closed";
|
|
13
|
+
|
|
14
|
+
export type ColumnGroupMeta = {
|
|
15
|
+
/** Group ids that render a collapse toggle. */
|
|
16
|
+
collapsibleGroupIds: Set<string>;
|
|
17
|
+
/** Group id -> whether it starts expanded (openByDefault). */
|
|
18
|
+
defaultOpen: Map<string, boolean>;
|
|
19
|
+
/** Leaf column id -> the group controlling it + its show mode. */
|
|
20
|
+
leafControl: Map<string, { groupId: string; show: ColumnGroupShow }>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
import { resolveColumnId } from "./column-id";
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
export function computeColumnGroupMeta(columns: ReadonlyArray<any>): ColumnGroupMeta {
|
|
27
|
+
const collapsibleGroupIds = new Set<string>();
|
|
28
|
+
const defaultOpen = new Map<string, boolean>();
|
|
29
|
+
const leafControl = new Map<string, { groupId: string; show: ColumnGroupShow }>();
|
|
30
|
+
|
|
31
|
+
// `depth` is threaded because it is part of the id of an unnamed column -
|
|
32
|
+
// drop it and the ids here stop matching the ones the engine assigns, so
|
|
33
|
+
// `leafControl` lookups silently miss for those columns.
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
function walk(defs: ReadonlyArray<any>, parentId: string | undefined, controllingGroup: string | undefined, depth: number) {
|
|
36
|
+
defs.forEach((def, ix) => {
|
|
37
|
+
const id = resolveColumnId(def, parentId, depth, ix);
|
|
38
|
+
if (def.columns?.length) {
|
|
39
|
+
// A group is collapsible when any DIRECT child declares columnGroupShow.
|
|
40
|
+
const collapsible = def.columns.some(
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
(c: any) => c.columnGroupShow === "open" || c.columnGroupShow === "closed",
|
|
43
|
+
);
|
|
44
|
+
if (collapsible) {
|
|
45
|
+
collapsibleGroupIds.add(id);
|
|
46
|
+
defaultOpen.set(id, def.openByDefault === true);
|
|
47
|
+
}
|
|
48
|
+
walk(def.columns, id, collapsible ? id : controllingGroup, depth + 1);
|
|
49
|
+
} else {
|
|
50
|
+
const show = def.columnGroupShow;
|
|
51
|
+
if ((show === "open" || show === "closed") && controllingGroup) {
|
|
52
|
+
leafControl.set(id, { groupId: controllingGroup, show });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
walk(columns, undefined, undefined, 0);
|
|
59
|
+
return { collapsibleGroupIds, defaultOpen, leafControl };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Which leaf ids are hidden right now, given the set of collapsed group ids.
|
|
64
|
+
* `'open'` leaves hide while their group is collapsed; `'closed'` leaves hide
|
|
65
|
+
* while it is expanded.
|
|
66
|
+
*/
|
|
67
|
+
export function hiddenLeavesForCollapse(
|
|
68
|
+
meta: ColumnGroupMeta,
|
|
69
|
+
collapsed: ReadonlySet<string>,
|
|
70
|
+
): Record<string, boolean> {
|
|
71
|
+
const hidden: Record<string, boolean> = {};
|
|
72
|
+
for (const [leafId, ctrl] of meta.leafControl) {
|
|
73
|
+
const isCollapsed = collapsed.has(ctrl.groupId);
|
|
74
|
+
const hide = ctrl.show === "open" ? isCollapsed : !isCollapsed;
|
|
75
|
+
if (hide) hidden[leafId] = true;
|
|
76
|
+
}
|
|
77
|
+
return hidden;
|
|
78
|
+
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `columnResize` action. This coverage moved here from columns.test.ts when
|
|
3
|
+
* the drag logic left the controller: the same behaviours (min clamp, rAF
|
|
4
|
+
* coalescing, final commit, teardown) are asserted, but against real DOM rather
|
|
5
|
+
* than a synthetic ctx, because the action now owns the handles as well as the
|
|
6
|
+
* drag.
|
|
7
|
+
*/
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
9
|
+
import { columnResize } from './column-resize'
|
|
10
|
+
|
|
11
|
+
const HANDLE = 'sv-grid-resize-handle'
|
|
12
|
+
|
|
13
|
+
/** A header row shaped like SvGrid's: `th.sv-grid-column[data-svgrid-header-col]`. */
|
|
14
|
+
function buildHeaders(
|
|
15
|
+
specs: Array<{ id?: string; classes?: string[] }>,
|
|
16
|
+
): { host: HTMLElement; ths: HTMLElement[] } {
|
|
17
|
+
const host = document.createElement('div')
|
|
18
|
+
const table = document.createElement('table')
|
|
19
|
+
const thead = document.createElement('thead')
|
|
20
|
+
const tr = document.createElement('tr')
|
|
21
|
+
const ths: HTMLElement[] = []
|
|
22
|
+
specs.forEach((spec, i) => {
|
|
23
|
+
const th = document.createElement('th')
|
|
24
|
+
th.className = ['sv-grid-column', ...(spec.classes ?? [])].join(' ')
|
|
25
|
+
if (spec.id !== undefined) th.dataset.svgridHeaderCol = spec.id
|
|
26
|
+
else if (!spec.classes) th.dataset.svgridHeaderCol = `c${i}`
|
|
27
|
+
tr.appendChild(th)
|
|
28
|
+
ths.push(th)
|
|
29
|
+
})
|
|
30
|
+
thead.appendChild(tr)
|
|
31
|
+
table.appendChild(thead)
|
|
32
|
+
host.appendChild(table)
|
|
33
|
+
document.body.appendChild(host)
|
|
34
|
+
return { host, ths }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleOf = (th: HTMLElement) => th.querySelector<HTMLElement>(`.${HANDLE}`)
|
|
38
|
+
|
|
39
|
+
function pointer(type: string, init: Partial<PointerEvent> = {}): PointerEvent {
|
|
40
|
+
return new PointerEvent(type, {
|
|
41
|
+
bubbles: true,
|
|
42
|
+
cancelable: true,
|
|
43
|
+
pointerId: 1,
|
|
44
|
+
...init,
|
|
45
|
+
} as PointerEventInit)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const hosts: HTMLElement[] = []
|
|
49
|
+
const track = (h: HTMLElement) => (hosts.push(h), h)
|
|
50
|
+
|
|
51
|
+
let rafCb: FrameRequestCallback | null = null
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
rafCb = null
|
|
54
|
+
vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => {
|
|
55
|
+
rafCb = cb
|
|
56
|
+
return 1
|
|
57
|
+
})
|
|
58
|
+
vi.stubGlobal('cancelAnimationFrame', vi.fn())
|
|
59
|
+
if (!HTMLElement.prototype.setPointerCapture) {
|
|
60
|
+
HTMLElement.prototype.setPointerCapture = function () {}
|
|
61
|
+
}
|
|
62
|
+
if (!HTMLElement.prototype.releasePointerCapture) {
|
|
63
|
+
HTMLElement.prototype.releasePointerCapture = function () {}
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
afterEach(() => {
|
|
67
|
+
while (hosts.length) hosts.pop()!.remove()
|
|
68
|
+
document.body.innerHTML = ''
|
|
69
|
+
document.body.style.cursor = ''
|
|
70
|
+
vi.unstubAllGlobals()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
/** Widths a test can read back, standing in for the controller. */
|
|
74
|
+
function widthStore(initial: Record<string, number> = {}) {
|
|
75
|
+
const widths = { ...initial }
|
|
76
|
+
return {
|
|
77
|
+
widths,
|
|
78
|
+
getWidth: (id: string) => widths[id] ?? 100,
|
|
79
|
+
onResize: (id: string, w: number) => {
|
|
80
|
+
widths[id] = w
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe('columnResize - handle injection', () => {
|
|
86
|
+
it('injects one handle per identified header', () => {
|
|
87
|
+
const { host, ths } = buildHeaders([{ id: 'a' }, { id: 'b' }])
|
|
88
|
+
track(host)
|
|
89
|
+
const a = columnResize(host, widthStore())
|
|
90
|
+
expect(handleOf(ths[0]!)).not.toBeNull()
|
|
91
|
+
expect(handleOf(ths[1]!)).not.toBeNull()
|
|
92
|
+
a.destroy()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('skips spacer columns and headers with no column id', () => {
|
|
96
|
+
// Group headers and the virtualization spacers have nothing to resize.
|
|
97
|
+
const { host, ths } = buildHeaders([
|
|
98
|
+
{ id: 'a' },
|
|
99
|
+
{ classes: ['sv-grid-column-spacer'] },
|
|
100
|
+
{ classes: ['sv-grid-group-header'] },
|
|
101
|
+
])
|
|
102
|
+
track(host)
|
|
103
|
+
const a = columnResize(host, widthStore())
|
|
104
|
+
expect(handleOf(ths[0]!)).not.toBeNull()
|
|
105
|
+
expect(handleOf(ths[1]!)).toBeNull()
|
|
106
|
+
expect(handleOf(ths[2]!)).toBeNull()
|
|
107
|
+
a.destroy()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('does not double-inject on re-decorate', () => {
|
|
111
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
112
|
+
track(host)
|
|
113
|
+
const a = columnResize(host, widthStore())
|
|
114
|
+
a.update({ ...widthStore(), disabled: false })
|
|
115
|
+
expect(ths[0]!.querySelectorAll(`.${HANDLE}`).length).toBe(1)
|
|
116
|
+
a.destroy()
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('injects nothing when created disabled', () => {
|
|
120
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
121
|
+
track(host)
|
|
122
|
+
const a = columnResize(host, { ...widthStore(), disabled: true })
|
|
123
|
+
expect(handleOf(ths[0]!)).toBeNull()
|
|
124
|
+
a.destroy()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('removes handles when toggled off and restores them when back on', () => {
|
|
128
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
129
|
+
track(host)
|
|
130
|
+
const store = widthStore()
|
|
131
|
+
const a = columnResize(host, store)
|
|
132
|
+
expect(handleOf(ths[0]!)).not.toBeNull()
|
|
133
|
+
a.update({ ...store, disabled: true })
|
|
134
|
+
expect(handleOf(ths[0]!)).toBeNull()
|
|
135
|
+
a.update({ ...store, disabled: false })
|
|
136
|
+
expect(handleOf(ths[0]!)).not.toBeNull()
|
|
137
|
+
a.destroy()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('gives the handle an accessible separator role and a name', () => {
|
|
141
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
142
|
+
track(host)
|
|
143
|
+
const a = columnResize(host, { ...widthStore({ a: 150 }), label: () => 'Name' })
|
|
144
|
+
const h = handleOf(ths[0]!)!
|
|
145
|
+
expect(h.getAttribute('role')).toBe('separator')
|
|
146
|
+
expect(h.getAttribute('aria-orientation')).toBe('vertical')
|
|
147
|
+
expect(h.getAttribute('aria-label')).toBe('Resize Name')
|
|
148
|
+
expect(h.tabIndex).toBe(0)
|
|
149
|
+
expect(h.getAttribute('aria-valuenow')).toBe('150')
|
|
150
|
+
a.destroy()
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('columnResize - drag', () => {
|
|
155
|
+
it('clamps to the minimum width and commits on the frame', () => {
|
|
156
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
157
|
+
track(host)
|
|
158
|
+
const store = widthStore({ a: 120 })
|
|
159
|
+
const a = columnResize(host, store)
|
|
160
|
+
const h = handleOf(ths[0]!)!
|
|
161
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
162
|
+
window.dispatchEvent(pointer('pointermove', { clientX: -1000 }))
|
|
163
|
+
expect(rafCb).not.toBeNull()
|
|
164
|
+
rafCb!(0)
|
|
165
|
+
expect(store.widths.a).toBe(40)
|
|
166
|
+
a.destroy()
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('coalesces multiple moves into a single frame', () => {
|
|
170
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
171
|
+
track(host)
|
|
172
|
+
const store = widthStore({ a: 120 })
|
|
173
|
+
const a = columnResize(host, store)
|
|
174
|
+
const h = handleOf(ths[0]!)!
|
|
175
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
176
|
+
window.dispatchEvent(pointer('pointermove', { clientX: 150 })) // +50 -> 170
|
|
177
|
+
const first = rafCb
|
|
178
|
+
window.dispatchEvent(pointer('pointermove', { clientX: 200 })) // +100 -> 220
|
|
179
|
+
expect(rafCb).toBe(first) // not rescheduled
|
|
180
|
+
rafCb!(0)
|
|
181
|
+
expect(store.widths.a).toBe(220)
|
|
182
|
+
a.destroy()
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('commits the final width on pointerup even if the frame never ran', () => {
|
|
186
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
187
|
+
track(host)
|
|
188
|
+
const store = widthStore({ a: 120 })
|
|
189
|
+
const a = columnResize(host, store)
|
|
190
|
+
const h = handleOf(ths[0]!)!
|
|
191
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
192
|
+
window.dispatchEvent(pointer('pointermove', { clientX: 190 }))
|
|
193
|
+
window.dispatchEvent(pointer('pointerup', { clientX: 190 })) // rAF cancelled
|
|
194
|
+
expect(store.widths.a).toBe(210)
|
|
195
|
+
a.destroy()
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('marks the handle while dragging and clears it afterwards', () => {
|
|
199
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
200
|
+
track(host)
|
|
201
|
+
const a = columnResize(host, widthStore({ a: 120 }))
|
|
202
|
+
const h = handleOf(ths[0]!)!
|
|
203
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
204
|
+
expect(h.classList.contains('is-resizing')).toBe(true)
|
|
205
|
+
window.dispatchEvent(pointer('pointerup', { clientX: 100 }))
|
|
206
|
+
expect(h.classList.contains('is-resizing')).toBe(false)
|
|
207
|
+
a.destroy()
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('ignores pointerdown when disabled, and on a non-handle target', () => {
|
|
211
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
212
|
+
track(host)
|
|
213
|
+
const store = widthStore({ a: 120 })
|
|
214
|
+
const a = columnResize(host, store)
|
|
215
|
+
const h = handleOf(ths[0]!)!
|
|
216
|
+
a.update({ ...store, disabled: true })
|
|
217
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
218
|
+
window.dispatchEvent(pointer('pointermove', { clientX: 300 }))
|
|
219
|
+
expect(rafCb).toBeNull()
|
|
220
|
+
// A click on the header itself must not start a drag either.
|
|
221
|
+
ths[0]!.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
222
|
+
expect(rafCb).toBeNull()
|
|
223
|
+
a.destroy()
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
describe('columnResize - keyboard', () => {
|
|
228
|
+
it('ArrowRight grows by 10 and ArrowLeft shrinks by 10', () => {
|
|
229
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
230
|
+
track(host)
|
|
231
|
+
const store = widthStore({ a: 120 })
|
|
232
|
+
const a = columnResize(host, store)
|
|
233
|
+
const h = handleOf(ths[0]!)!
|
|
234
|
+
h.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }))
|
|
235
|
+
expect(store.widths.a).toBe(130)
|
|
236
|
+
h.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }))
|
|
237
|
+
expect(store.widths.a).toBe(120)
|
|
238
|
+
a.destroy()
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('Shift gives a 1px step and the minimum still holds', () => {
|
|
242
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
243
|
+
track(host)
|
|
244
|
+
const store = widthStore({ a: 41 })
|
|
245
|
+
const a = columnResize(host, store)
|
|
246
|
+
const h = handleOf(ths[0]!)!
|
|
247
|
+
h.dispatchEvent(
|
|
248
|
+
new KeyboardEvent('keydown', { key: 'ArrowLeft', shiftKey: true, bubbles: true }),
|
|
249
|
+
)
|
|
250
|
+
expect(store.widths.a).toBe(40)
|
|
251
|
+
h.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }))
|
|
252
|
+
expect(store.widths.a).toBe(40) // clamped, not 30
|
|
253
|
+
a.destroy()
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('ignores other keys and does nothing while disabled', () => {
|
|
257
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
258
|
+
track(host)
|
|
259
|
+
const store = widthStore({ a: 120 })
|
|
260
|
+
const a = columnResize(host, store)
|
|
261
|
+
const h = handleOf(ths[0]!)!
|
|
262
|
+
h.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
|
|
263
|
+
expect(store.widths.a).toBe(120)
|
|
264
|
+
a.update({ ...store, disabled: true })
|
|
265
|
+
h.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }))
|
|
266
|
+
expect(store.widths.a).toBe(120)
|
|
267
|
+
a.destroy()
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
describe('columnResize - teardown', () => {
|
|
272
|
+
it('destroy removes every handle and detaches the drag listeners', () => {
|
|
273
|
+
const { host, ths } = buildHeaders([{ id: 'a' }, { id: 'b' }])
|
|
274
|
+
track(host)
|
|
275
|
+
const store = widthStore({ a: 120 })
|
|
276
|
+
const a = columnResize(host, store)
|
|
277
|
+
const h = handleOf(ths[0]!)!
|
|
278
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
279
|
+
a.destroy()
|
|
280
|
+
expect(host.querySelectorAll(`.${HANDLE}`).length).toBe(0)
|
|
281
|
+
// An in-flight drag must not keep writing after unmount (#58).
|
|
282
|
+
window.dispatchEvent(pointer('pointermove', { clientX: 400 }))
|
|
283
|
+
expect(rafCb).toBeNull()
|
|
284
|
+
expect(store.widths.a).toBe(120)
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
describe('columnResize - per-column resizable', () => {
|
|
289
|
+
it('creates no handle for a column that opts out', () => {
|
|
290
|
+
const { host, ths } = buildHeaders([{ id: 'a' }, { id: 'b' }])
|
|
291
|
+
track(host)
|
|
292
|
+
const a = columnResize(host, { ...widthStore(), canResize: (id) => id !== 'b' })
|
|
293
|
+
expect(handleOf(ths[0]!)).not.toBeNull()
|
|
294
|
+
expect(handleOf(ths[1]!)).toBeNull()
|
|
295
|
+
a.destroy()
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('takes the handle away when a column stops being resizable', () => {
|
|
299
|
+
// A re-decorate has to remove, not merely stop adding: the handle is
|
|
300
|
+
// already in the DOM by then.
|
|
301
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
302
|
+
track(host)
|
|
303
|
+
const store = widthStore()
|
|
304
|
+
let allow = true
|
|
305
|
+
const a = columnResize(host, { ...store, canResize: () => allow })
|
|
306
|
+
expect(handleOf(ths[0]!)).not.toBeNull()
|
|
307
|
+
allow = false
|
|
308
|
+
a.update({ ...store, canResize: () => allow, disabled: true })
|
|
309
|
+
a.update({ ...store, canResize: () => allow, disabled: false })
|
|
310
|
+
expect(handleOf(ths[0]!)).toBeNull()
|
|
311
|
+
a.destroy()
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('refuses the drag and the arrow keys even if a handle is reached', () => {
|
|
315
|
+
// Defence in depth: no handle is created, so this can only happen if one
|
|
316
|
+
// survives a stale render. It must still not resize.
|
|
317
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
318
|
+
track(host)
|
|
319
|
+
const store = widthStore({ a: 120 })
|
|
320
|
+
let allow = true
|
|
321
|
+
const a = columnResize(host, { ...store, canResize: () => allow })
|
|
322
|
+
const h = handleOf(ths[0]!)!
|
|
323
|
+
allow = false
|
|
324
|
+
a.update({ ...store, canResize: () => allow })
|
|
325
|
+
|
|
326
|
+
h.dispatchEvent(pointer('pointerdown', { clientX: 100 }))
|
|
327
|
+
window.dispatchEvent(pointer('pointermove', { clientX: 300 }))
|
|
328
|
+
expect(rafCb).toBeNull()
|
|
329
|
+
h.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }))
|
|
330
|
+
expect(store.widths.a).toBe(120)
|
|
331
|
+
a.destroy()
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
describe('columnResize - double-click autosize', () => {
|
|
336
|
+
it('double-clicking the handle autosizes that column', () => {
|
|
337
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
338
|
+
track(host)
|
|
339
|
+
const onAutosize = vi.fn()
|
|
340
|
+
const a = columnResize(host, { ...widthStore(), onAutosize })
|
|
341
|
+
handleOf(ths[0]!)!.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }))
|
|
342
|
+
expect(onAutosize).toHaveBeenCalledWith('a')
|
|
343
|
+
a.destroy()
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('does not autosize a column that opted out - it has no handle to hit', () => {
|
|
347
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
348
|
+
track(host)
|
|
349
|
+
const onAutosize = vi.fn()
|
|
350
|
+
const a = columnResize(host, { ...widthStore(), onAutosize, canResize: () => false })
|
|
351
|
+
expect(handleOf(ths[0]!)).toBeNull()
|
|
352
|
+
// The header itself is all that is left, and it carries no autosize.
|
|
353
|
+
ths[0]!.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }))
|
|
354
|
+
expect(onAutosize).not.toHaveBeenCalled()
|
|
355
|
+
a.destroy()
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
it('does not let the double-click reach the header underneath', () => {
|
|
359
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
360
|
+
track(host)
|
|
361
|
+
const onHeader = vi.fn()
|
|
362
|
+
ths[0]!.addEventListener('dblclick', onHeader)
|
|
363
|
+
const a = columnResize(host, { ...widthStore(), onAutosize: vi.fn() })
|
|
364
|
+
handleOf(ths[0]!)!.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }))
|
|
365
|
+
expect(onHeader).not.toHaveBeenCalled()
|
|
366
|
+
a.destroy()
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('does nothing on double-click while disabled', () => {
|
|
370
|
+
const { host, ths } = buildHeaders([{ id: 'a' }])
|
|
371
|
+
track(host)
|
|
372
|
+
const onAutosize = vi.fn()
|
|
373
|
+
const store = widthStore()
|
|
374
|
+
const a = columnResize(host, { ...store, onAutosize })
|
|
375
|
+
const h = handleOf(ths[0]!)!
|
|
376
|
+
a.update({ ...store, onAutosize, disabled: true })
|
|
377
|
+
h.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }))
|
|
378
|
+
expect(onAutosize).not.toHaveBeenCalled()
|
|
379
|
+
a.destroy()
|
|
380
|
+
})
|
|
381
|
+
})
|