@svgrid/grid 2.7.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -88
- package/README.md +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +181 -181
- package/dist/SvAutoComplete.svelte +169 -169
- 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 -279
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +378 -378
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGridCellEditor.svelte +748 -744
- 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 -270
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +334 -334
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +293 -293
- package/dist/SvNumberInput.svelte +172 -172
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +203 -203
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +115 -115
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +239 -239
- package/dist/cdn/{GridMenus-C4oLsJyj.js → GridMenus-CYzOvmNQ.js} +3 -3
- package/dist/cdn/{GridMenus-yEivjNwl.js → GridMenus-DFB6iwF8.js} +3 -3
- package/dist/cdn/{SvDateRangeInput-DflbiP7N.js → SvDateRangeInput-CaOuMs8O.js} +1 -1
- package/dist/cdn/{SvDateRangeInput-Bh0A0JkF.js → SvDateRangeInput-DMLKmGEc.js} +1 -1
- package/dist/cdn/{SvDateTimePicker-Bivn8dAP.js → SvDateTimePicker-CCbDZNZB.js} +1 -1
- package/dist/cdn/{SvDateTimePicker-BWwpfB_o.js → SvDateTimePicker-sonaH0oh.js} +1 -1
- package/dist/cdn/{SvGridCellEditor-Ba7rY3eu.js → SvGridCellEditor-BWnTi2N7.js} +2 -2
- package/dist/cdn/{SvGridCellEditor-AXL8cHGO.js → SvGridCellEditor-CjEJMJKc.js} +2 -2
- package/dist/cdn/{SvGridChart-CwhFz7GV.js → SvGridChart-BEJmNNx9.js} +1 -1
- package/dist/cdn/{SvGridChart-Bs2GIR2Q.js → SvGridChart-BwVos976.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-1WXuStzM.js → SvGridChartPanel-BOknOkrP.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-e5_nqNP9.js → SvGridChartPanel-D2PjII4O.js} +1 -1
- package/dist/cdn/{SvGridChartView-SmPW10dI.js → SvGridChartView-00LPUHL1.js} +1 -1
- package/dist/cdn/{SvGridChartView-eSPuJE6g.js → SvGridChartView-BhUEJ5ki.js} +1 -1
- package/dist/cdn/{SvGridDropdown-B1ZcLpgs.js → SvGridDropdown-D0VdjeR8.js} +1 -1
- package/dist/cdn/{SvGridDropdown-CtdbHcIS.js → SvGridDropdown-D13MtJ2j.js} +1 -1
- package/dist/cdn/{date-format-DZT7T1wf.js → date-format-BNii4zeD.js} +2 -2
- package/dist/cdn/{date-format-CcP1tafP.js → date-format-BnnHlqGw.js} +2 -2
- package/dist/cdn/{row-drag-touch-Ddaa-QeB.js → row-drag-touch-CycRR65n.js} +12 -10
- package/dist/cdn/{src-COC0vnpg.js → src-C8QjSaq-.js} +53 -55
- package/dist/cdn/{src-pYGV2rMR.js → src-D0hO2eYN.js} +52 -54
- package/dist/cdn/svgrid.js +7 -7
- package/dist/cdn/svgrid.svelte-external.js +7 -7
- package/dist/chart-export.js +8 -8
- package/dist/editing.js +22 -13
- package/dist/row-drag-touch.d.ts +5 -1
- package/dist/row-drag-touch.js +15 -4
- package/dist/row-drag.js +7 -3
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +181 -181
- package/src/SvAutoComplete.svelte +169 -169
- 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 -279
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +378 -378
- 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.types.ts +2092 -2092
- package/src/SvGridCellEditor.svelte +748 -744
- 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 -270
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +334 -334
- 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 -293
- package/src/SvNumberInput.svelte +172 -172
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +203 -203
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +115 -115
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +239 -239
- 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/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 -148
- package/src/chart-export.ts +202 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2321 -2321
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.ts +78 -78
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1865 -1865
- 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/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 -974
- package/src/editing.ts +618 -609
- 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/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 -325
- 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/headless.ts +87 -87
- 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 -179
- package/src/menus.ts +597 -597
- package/src/merge-objects.ts +48 -48
- package/src/overlays.test.ts +90 -90
- package/src/positioning.ts +268 -268
- package/src/render-component.ts +28 -28
- package/src/row-drag-touch.ts +226 -211
- package/src/row-drag.test.ts +401 -353
- package/src/row-drag.ts +419 -415
- package/src/row-resize.test.ts +524 -524
- package/src/row-resize.ts +228 -228
- 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.test.ts +885 -885
- 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 +382 -377
- 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.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.wrapper.test.ts +63 -63
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/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/src/row-drag-touch.ts
CHANGED
|
@@ -1,211 +1,226 @@
|
|
|
1
|
-
// Touch / pointer row dragging (#66).
|
|
2
|
-
//
|
|
3
|
-
// HTML5 drag events do not exist for touch input on iOS Safari or Android
|
|
4
|
-
// Chrome - `dragstart` never fires - so the drag path in `row-drag.ts` is dead
|
|
5
|
-
// on a phone. This is a second INPUT path onto the same model: it populates the
|
|
6
|
-
// same cross-grid `bus` and the same `rowDropIndex` / `rowDropSide`, and commits
|
|
7
|
-
// through the same `commitDropOnRow`, so reorder, cross-grid hand-off and the
|
|
8
|
-
// `onRowDragEnd` payload cannot drift between mouse and touch.
|
|
9
|
-
//
|
|
10
|
-
// Loaded with `import()` on the first touch that lands on a draggable row, so
|
|
11
|
-
// desktop users never download it. The 350 ms long press below
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
/**
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
touch.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
deps.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
1
|
+
// Touch / pointer row dragging (#66).
|
|
2
|
+
//
|
|
3
|
+
// HTML5 drag events do not exist for touch input on iOS Safari or Android
|
|
4
|
+
// Chrome - `dragstart` never fires - so the drag path in `row-drag.ts` is dead
|
|
5
|
+
// on a phone. This is a second INPUT path onto the same model: it populates the
|
|
6
|
+
// same cross-grid `bus` and the same `rowDropIndex` / `rowDropSide`, and commits
|
|
7
|
+
// through the same `commitDropOnRow`, so reorder, cross-grid hand-off and the
|
|
8
|
+
// `onRowDragEnd` payload cannot drift between mouse and touch.
|
|
9
|
+
//
|
|
10
|
+
// Loaded with `import()` on the first touch that lands on a draggable row, so
|
|
11
|
+
// desktop users never download it. The 350 ms long press below absorbs the
|
|
12
|
+
// fetch rather than following it: `start` takes the pointerdown timestamp and
|
|
13
|
+
// subtracts the time already spent waiting, so a fetch that finishes inside the
|
|
14
|
+
// press costs the user nothing.
|
|
15
|
+
//
|
|
16
|
+
// A touch that starts on a row must still be able to SCROLL the grid, so the
|
|
17
|
+
// drag only begins after a long press; moving before that threshold hands the
|
|
18
|
+
// gesture back to the browser.
|
|
19
|
+
|
|
20
|
+
/** Everything the touch path needs from the `createRowDrag` closure. */
|
|
21
|
+
export type TouchDragDeps = {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
ctx: any;
|
|
24
|
+
managed: () => boolean;
|
|
25
|
+
originalAt: (rowIndex: number) => unknown;
|
|
26
|
+
commitDropOnRow: (rowIndex: number, side: "before" | "after") => void;
|
|
27
|
+
clearIndicators: () => void;
|
|
28
|
+
cancelPendingLeave: () => void;
|
|
29
|
+
/** Publish the dragged row on the shared cross-grid bus. */
|
|
30
|
+
openBus: (row: unknown) => void;
|
|
31
|
+
closeBus: () => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const LONG_PRESS_MS = 350;
|
|
35
|
+
/** Movement (px) before the long press is treated as a scroll instead. */
|
|
36
|
+
const SCROLL_SLOP_PX = 10;
|
|
37
|
+
/** Distance from an edge (px) at which the grid starts auto-scrolling. */
|
|
38
|
+
const EDGE_PX = 48;
|
|
39
|
+
const EDGE_SPEED_PX = 12;
|
|
40
|
+
|
|
41
|
+
type TouchState = {
|
|
42
|
+
pointerId: number;
|
|
43
|
+
rowIndex: number;
|
|
44
|
+
startX: number;
|
|
45
|
+
startY: number;
|
|
46
|
+
started: boolean;
|
|
47
|
+
timer: ReturnType<typeof setTimeout> | null;
|
|
48
|
+
scrollFrame: number | null;
|
|
49
|
+
scroller: HTMLElement | null;
|
|
50
|
+
root: HTMLElement | null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** What {@link createTouchDrag} returns. */
|
|
54
|
+
export type TouchDragHandle = {
|
|
55
|
+
/**
|
|
56
|
+
* @param pressStartedAt `performance.now()` at the pointerdown that began
|
|
57
|
+
* this press. Defaults to now. See the long-press comment in `start`.
|
|
58
|
+
*/
|
|
59
|
+
start: (e: PointerEvent, rowIndex: number, pressStartedAt?: number) => void;
|
|
60
|
+
destroy: () => void;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function createTouchDrag(deps: TouchDragDeps): TouchDragHandle {
|
|
64
|
+
const { ctx } = deps;
|
|
65
|
+
let touch: TouchState | null = null;
|
|
66
|
+
|
|
67
|
+
/** Resolve the row under a viewport point, and which half of it. */
|
|
68
|
+
function rowAtPoint(x: number, y: number): { index: number; side: "before" | "after" } | null {
|
|
69
|
+
const el = document.elementFromPoint(x, y) as HTMLElement | null;
|
|
70
|
+
const tr = el?.closest?.("tr.sv-grid-row") as HTMLElement | null;
|
|
71
|
+
if (!tr) return null;
|
|
72
|
+
const marker = tr.querySelector<HTMLElement>("[data-svgrid-row]");
|
|
73
|
+
const raw = marker?.dataset.svgridRow;
|
|
74
|
+
if (raw == null) return null;
|
|
75
|
+
const index = Number(raw);
|
|
76
|
+
if (!Number.isFinite(index)) return null;
|
|
77
|
+
const rect = tr.getBoundingClientRect();
|
|
78
|
+
return { index, side: y < rect.top + rect.height / 2 ? "before" : "after" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function stopEdgeScroll() {
|
|
82
|
+
if (touch?.scrollFrame != null) {
|
|
83
|
+
cancelAnimationFrame(touch.scrollFrame);
|
|
84
|
+
touch.scrollFrame = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Keep scrolling while the finger is parked near the top or bottom edge. */
|
|
89
|
+
function edgeScroll(clientY: number) {
|
|
90
|
+
const scroller = touch?.scroller;
|
|
91
|
+
if (!touch || !scroller) return;
|
|
92
|
+
const rect = scroller.getBoundingClientRect();
|
|
93
|
+
let dy = 0;
|
|
94
|
+
if (clientY < rect.top + EDGE_PX) dy = -EDGE_SPEED_PX;
|
|
95
|
+
else if (clientY > rect.bottom - EDGE_PX) dy = EDGE_SPEED_PX;
|
|
96
|
+
stopEdgeScroll();
|
|
97
|
+
if (dy === 0) return;
|
|
98
|
+
const step = () => {
|
|
99
|
+
if (!touch?.started) return;
|
|
100
|
+
scroller.scrollTop += dy;
|
|
101
|
+
// Re-hit-test as rows move under a stationary finger, otherwise the
|
|
102
|
+
// indicator freezes on whichever row happened to be there at touch time.
|
|
103
|
+
const hit = rowAtPoint(touch.startX, clientY);
|
|
104
|
+
if (hit) {
|
|
105
|
+
ctx.rowDropIndex = hit.index;
|
|
106
|
+
ctx.rowDropSide = hit.side;
|
|
107
|
+
}
|
|
108
|
+
touch.scrollFrame = requestAnimationFrame(step);
|
|
109
|
+
};
|
|
110
|
+
touch.scrollFrame = requestAnimationFrame(step);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// `pointermove.preventDefault()` does NOT stop iOS scrolling; only a
|
|
114
|
+
// non-passive `touchmove` does. Registered for the life of the drag.
|
|
115
|
+
function blockTouchScroll(e: TouchEvent) {
|
|
116
|
+
if (touch?.started) e.preventDefault();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function detach() {
|
|
120
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
121
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
122
|
+
window.removeEventListener("pointercancel", onPointerCancel);
|
|
123
|
+
window.removeEventListener("touchmove", blockTouchScroll);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function end(commit: boolean) {
|
|
127
|
+
if (!touch) return;
|
|
128
|
+
const t = touch;
|
|
129
|
+
if (t.timer) clearTimeout(t.timer);
|
|
130
|
+
stopEdgeScroll();
|
|
131
|
+
touch = null;
|
|
132
|
+
detach();
|
|
133
|
+
t.root?.classList.remove("sv-grid-row-drag-active");
|
|
134
|
+
if (!t.started) return;
|
|
135
|
+
if (commit && ctx.rowDropIndex != null) {
|
|
136
|
+
deps.commitDropOnRow(
|
|
137
|
+
ctx.rowDropIndex as number,
|
|
138
|
+
(ctx.rowDropSide ?? "before") as "before" | "after",
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
deps.clearIndicators();
|
|
142
|
+
deps.closeBus();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function onPointerMove(e: PointerEvent) {
|
|
147
|
+
if (!touch || e.pointerId !== touch.pointerId) return;
|
|
148
|
+
if (!touch.started) {
|
|
149
|
+
// Still inside the long press: a real move means the user is scrolling.
|
|
150
|
+
const moved = Math.abs(e.clientY - touch.startY) + Math.abs(e.clientX - touch.startX);
|
|
151
|
+
if (moved > SCROLL_SLOP_PX) end(false);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
e.preventDefault();
|
|
155
|
+
const hit = rowAtPoint(e.clientX, e.clientY);
|
|
156
|
+
if (hit) {
|
|
157
|
+
deps.cancelPendingLeave();
|
|
158
|
+
ctx.rowDropIndex = hit.index;
|
|
159
|
+
ctx.rowDropSide = hit.side;
|
|
160
|
+
}
|
|
161
|
+
touch.startX = e.clientX;
|
|
162
|
+
edgeScroll(e.clientY);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function onPointerUp(e: PointerEvent) {
|
|
166
|
+
if (!touch || e.pointerId !== touch.pointerId) return;
|
|
167
|
+
end(true);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function onPointerCancel(e: PointerEvent) {
|
|
171
|
+
if (!touch || e.pointerId !== touch.pointerId) return;
|
|
172
|
+
end(false);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
/** Begin a press that may become a drag. Caller has already checked touch. */
|
|
177
|
+
start(e: PointerEvent, rowIndex: number, pressStartedAt?: number) {
|
|
178
|
+
if (!deps.managed() || touch) return;
|
|
179
|
+
const row = deps.originalAt(rowIndex);
|
|
180
|
+
if (row == null) return;
|
|
181
|
+
const target = e.currentTarget as HTMLElement | null;
|
|
182
|
+
touch = {
|
|
183
|
+
pointerId: e.pointerId,
|
|
184
|
+
rowIndex,
|
|
185
|
+
startX: e.clientX,
|
|
186
|
+
startY: e.clientY,
|
|
187
|
+
started: false,
|
|
188
|
+
timer: null,
|
|
189
|
+
scrollFrame: null,
|
|
190
|
+
scroller: target?.closest?.<HTMLElement>(".sv-grid-container") ?? null,
|
|
191
|
+
root: target?.closest?.<HTMLElement>(".sv-grid-root") ?? null,
|
|
192
|
+
};
|
|
193
|
+
window.addEventListener("pointermove", onPointerMove, { passive: false });
|
|
194
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
195
|
+
window.addEventListener("pointercancel", onPointerCancel);
|
|
196
|
+
window.addEventListener("touchmove", blockTouchScroll, { passive: false });
|
|
197
|
+
// The long press is measured from when the finger LANDED, not from when
|
|
198
|
+
// this module finished loading. `row-drag.ts` fetches it with import() on
|
|
199
|
+
// the first touch, and that fetch is not free - measured at ~190 ms cold.
|
|
200
|
+
// Starting the clock here would make the real cost `fetch + 350 ms`, so a
|
|
201
|
+
// user on a slow connection would have to hold noticeably longer for
|
|
202
|
+
// their first drag than for every one after it. Subtracting the elapsed
|
|
203
|
+
// time keeps it at 350 ms from the finger's point of view, which is what
|
|
204
|
+
// the comment at the top of this file has always claimed.
|
|
205
|
+
const waited = pressStartedAt == null ? 0 : Math.max(0, performance.now() - pressStartedAt);
|
|
206
|
+
touch.timer = setTimeout(() => {
|
|
207
|
+
if (!touch) return;
|
|
208
|
+
touch.started = true;
|
|
209
|
+
touch.timer = null;
|
|
210
|
+
touch.root?.classList.add("sv-grid-row-drag-active");
|
|
211
|
+
try {
|
|
212
|
+
target?.setPointerCapture?.(e.pointerId);
|
|
213
|
+
} catch {
|
|
214
|
+
// Not every environment implements pointer capture (tests, old WebViews).
|
|
215
|
+
}
|
|
216
|
+
deps.openBus(row);
|
|
217
|
+
ctx.rowDragActive = true;
|
|
218
|
+
ctx.rowDropIndex = rowIndex;
|
|
219
|
+
ctx.rowDropSide = "before";
|
|
220
|
+
}, Math.max(0, LONG_PRESS_MS - waited));
|
|
221
|
+
},
|
|
222
|
+
destroy() {
|
|
223
|
+
end(false);
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|