@svgrid/grid 2.2.25 → 2.2.27
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 -145
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +179 -179
- package/dist/GridMenus.svelte +734 -728
- package/dist/SvAutoComplete.svelte +165 -165
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +101 -101
- 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 +250 -250
- 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 +2723 -2719
- package/dist/SvGrid.svelte +3333 -3333
- package/dist/SvGridChart.svelte +1724 -1724
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +696 -696
- package/dist/SvGridSelect.svelte +259 -259
- package/dist/SvGroupCell.svelte +109 -109
- package/dist/SvListBox.svelte +287 -287
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +281 -281
- package/dist/SvNumberInput.svelte +168 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +131 -131
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +200 -200
- package/dist/SvSwitchButton.svelte +107 -107
- 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 +204 -204
- package/dist/SvTree.svelte +437 -437
- package/dist/SvTreeSelect.svelte +233 -233
- package/dist/a11y/dismissable.d.ts +11 -0
- package/dist/a11y/dismissable.js +21 -0
- package/dist/ai.js +3 -3
- package/dist/cdn/GridMenus-BzxEYNtW.js +421 -0
- package/dist/cdn/GridMenus-Dr-nGEXn.js +417 -0
- package/dist/cdn/{SvDateTimePicker-CYC7gRER.js → SvDateTimePicker-B1sDc9Pz.js} +451 -444
- package/dist/cdn/{SvDateTimePicker-DtQKd4Ns.js → SvDateTimePicker-Bh0FTs1E.js} +114 -107
- package/dist/cdn/{SvGridChart-BhnFdLd0.js → SvGridChart-BKQsO5fZ.js} +1 -1
- package/dist/cdn/{SvGridChart-BcLCYvw9.js → SvGridChart-Bgk4hDs8.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DvjwxLvK.js → SvGridChartPanel-DSkHIQDd.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-Cmx9DHAS.js → SvGridChartPanel-DkCc2Z7b.js} +1 -1
- package/dist/cdn/{SvGridChartView-B40DTJSX.js → SvGridChartView-BrbVaELm.js} +1 -1
- package/dist/cdn/{SvGridChartView-PwqI4s02.js → SvGridChartView-i9rWvSKg.js} +1 -1
- package/dist/cdn/{SvGridDropdown-hpnHvOGt.js → SvGridDropdown-CGxxopQ4.js} +1 -1
- package/dist/cdn/{SvGridDropdown-DjALc_4f.js → SvGridDropdown-D08sVmAy.js} +1 -1
- package/dist/cdn/{src-DysW9qbk.js → src-Cw8d9U8j.js} +5753 -5753
- package/dist/cdn/{src-bYfr_7aR.js → src-cgs1wcUS.js} +8102 -8102
- 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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +30 -13
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +179 -179
- package/src/GridMenus.svelte +734 -728
- package/src/SvAutoComplete.svelte +165 -165
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +101 -101
- 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 +250 -250
- 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 +3239 -3239
- package/src/SvGrid.css +2723 -2719
- package/src/SvGrid.svelte +3333 -3333
- package/src/SvGrid.types.ts +1613 -1613
- package/src/SvGridChart.svelte +1724 -1724
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +696 -696
- package/src/SvGridSelect.svelte +259 -259
- package/src/SvGroupCell.svelte +109 -109
- package/src/SvListBox.svelte +287 -287
- 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 +281 -281
- package/src/SvNumberInput.svelte +168 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +131 -131
- package/src/SvPopover.svelte +197 -197
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +200 -200
- package/src/SvSwitchButton.svelte +107 -107
- 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 +204 -204
- package/src/SvTree.svelte +437 -437
- package/src/SvTreeSelect.svelte +233 -233
- package/src/a11y/dismissable.test.ts +119 -95
- package/src/a11y/dismissable.ts +114 -94
- 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 +895 -895
- package/src/builtin-editors.grid.test.ts +85 -85
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.ts +469 -469
- package/src/chart-export.ts +201 -201
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2296 -2296
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1111 -1111
- 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/createMenu.svelte.ts +225 -225
- 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 +859 -859
- package/src/editing.ts +675 -675
- package/src/editor-contract.ts +157 -157
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +598 -598
- package/src/filter-operators.ts +157 -157
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/index.ts +713 -712
- package/src/js-scroller.svelte.ts +173 -173
- 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.test.ts +700 -700
- package/src/menus.ts +652 -652
- package/src/merge-objects.ts +48 -48
- package/src/overlays.test.ts +90 -90
- package/src/render-component.ts +28 -28
- 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 +600 -600
- 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 +489 -489
- 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 +524 -524
- package/src/svgrid.behavior.test.ts +706 -706
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-menu-scroll.test.ts +102 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +215 -215
- package/src/toast-store.svelte.ts +246 -246
- package/src/toast-store.test.ts +147 -147
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
- package/themes/ember.css +71 -71
- package/dist/cdn/GridMenus-GfCAHgkZ.js +0 -420
- package/dist/cdn/GridMenus-GiVNSeDU.js +0 -416
package/src/GridMenus.svelte
CHANGED
|
@@ -1,728 +1,734 @@
|
|
|
1
|
-
<script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
|
|
2
|
-
import {
|
|
3
|
-
applyExcelFilter,
|
|
4
|
-
normalizeForFilter,
|
|
5
|
-
splitInTokens,
|
|
6
|
-
createColumnVirtualizer,
|
|
7
|
-
createCoreRowModel,
|
|
8
|
-
createExpandedRowModel,
|
|
9
|
-
createFilteredRowModel,
|
|
10
|
-
createGroupedRowModel,
|
|
11
|
-
createPaginatedRowModel,
|
|
12
|
-
createSvelteVirtualizer,
|
|
13
|
-
createSortedRowModel,
|
|
14
|
-
createSvGrid,
|
|
15
|
-
filterFns,
|
|
16
|
-
getGridCellA11yProps,
|
|
17
|
-
getGridCellDomId,
|
|
18
|
-
getGridHeaderA11yProps,
|
|
19
|
-
getGridRootA11yProps,
|
|
20
|
-
getGridRowA11yProps,
|
|
21
|
-
parseEditorValue,
|
|
22
|
-
normalizeEditorOptions,
|
|
23
|
-
sortFns,
|
|
24
|
-
tableFeatures,
|
|
25
|
-
rowSortingFeature,
|
|
26
|
-
columnFilteringFeature,
|
|
27
|
-
columnGroupingFeature,
|
|
28
|
-
type CellContext,
|
|
29
|
-
type EditorContext,
|
|
30
|
-
type CellEditorOption,
|
|
31
|
-
type CellEditorType,
|
|
32
|
-
type CellFormatter,
|
|
33
|
-
type CellFormatConfig,
|
|
34
|
-
type Column,
|
|
35
|
-
type ColumnDef,
|
|
36
|
-
type Row,
|
|
37
|
-
type RowData,
|
|
38
|
-
type SvGridApi,
|
|
39
|
-
type TableFeatures,
|
|
40
|
-
} from "./index";
|
|
41
|
-
import "./sv-grid-scrollbar";
|
|
42
|
-
import
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
type
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
import
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
const
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
<
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
>
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
class="sv-grid-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
class="sv-grid-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
<div
|
|
676
|
-
class="sv-grid-menu
|
|
677
|
-
role="
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
>
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
1
|
+
<script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
|
|
2
|
+
import {
|
|
3
|
+
applyExcelFilter,
|
|
4
|
+
normalizeForFilter,
|
|
5
|
+
splitInTokens,
|
|
6
|
+
createColumnVirtualizer,
|
|
7
|
+
createCoreRowModel,
|
|
8
|
+
createExpandedRowModel,
|
|
9
|
+
createFilteredRowModel,
|
|
10
|
+
createGroupedRowModel,
|
|
11
|
+
createPaginatedRowModel,
|
|
12
|
+
createSvelteVirtualizer,
|
|
13
|
+
createSortedRowModel,
|
|
14
|
+
createSvGrid,
|
|
15
|
+
filterFns,
|
|
16
|
+
getGridCellA11yProps,
|
|
17
|
+
getGridCellDomId,
|
|
18
|
+
getGridHeaderA11yProps,
|
|
19
|
+
getGridRootA11yProps,
|
|
20
|
+
getGridRowA11yProps,
|
|
21
|
+
parseEditorValue,
|
|
22
|
+
normalizeEditorOptions,
|
|
23
|
+
sortFns,
|
|
24
|
+
tableFeatures,
|
|
25
|
+
rowSortingFeature,
|
|
26
|
+
columnFilteringFeature,
|
|
27
|
+
columnGroupingFeature,
|
|
28
|
+
type CellContext,
|
|
29
|
+
type EditorContext,
|
|
30
|
+
type CellEditorOption,
|
|
31
|
+
type CellEditorType,
|
|
32
|
+
type CellFormatter,
|
|
33
|
+
type CellFormatConfig,
|
|
34
|
+
type Column,
|
|
35
|
+
type ColumnDef,
|
|
36
|
+
type Row,
|
|
37
|
+
type RowData,
|
|
38
|
+
type SvGridApi,
|
|
39
|
+
type TableFeatures,
|
|
40
|
+
} from "./index";
|
|
41
|
+
import "./sv-grid-scrollbar";
|
|
42
|
+
import { onScrollOutside } from "./a11y/dismissable";
|
|
43
|
+
import type { Snippet } from "svelte";
|
|
44
|
+
import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
|
|
45
|
+
import {
|
|
46
|
+
formatNumericWithConfig,
|
|
47
|
+
getDateFormatter,
|
|
48
|
+
resolveDatePattern,
|
|
49
|
+
} from "./cell-formatting";
|
|
50
|
+
import {
|
|
51
|
+
RenderSnippetConfig,
|
|
52
|
+
RenderComponentConfig,
|
|
53
|
+
} from "./render-component";
|
|
54
|
+
import { buildFillPattern } from "./fill-patterns";
|
|
55
|
+
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
56
|
+
import {
|
|
57
|
+
resolveCellFormat,
|
|
58
|
+
computeColumnStat,
|
|
59
|
+
formatsNeedingStats,
|
|
60
|
+
type ColumnStat,
|
|
61
|
+
type ResolvedCellFormat,
|
|
62
|
+
} from "./conditional-formatting";
|
|
63
|
+
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
64
|
+
import type {
|
|
65
|
+
Props,
|
|
66
|
+
SelectionPoint,
|
|
67
|
+
SelectionRange,
|
|
68
|
+
CellEditState,
|
|
69
|
+
FilterOperator,
|
|
70
|
+
FilterOption,
|
|
71
|
+
MenuPosition,
|
|
72
|
+
} from "./SvGrid.types";
|
|
73
|
+
import {
|
|
74
|
+
cfTextStyle,
|
|
75
|
+
fmtStat,
|
|
76
|
+
getCellKey,
|
|
77
|
+
resolveClassList,
|
|
78
|
+
toDateInputValue,
|
|
79
|
+
toDateTimeLocalInputValue,
|
|
80
|
+
getEditableInputValue,
|
|
81
|
+
getEditorInputType,
|
|
82
|
+
toValueArray,
|
|
83
|
+
getOptionLabel,
|
|
84
|
+
getOptionColor,
|
|
85
|
+
colorfulChipStyle,
|
|
86
|
+
getEditorClass,
|
|
87
|
+
asDate,
|
|
88
|
+
clampMenuX,
|
|
89
|
+
cssEscape,
|
|
90
|
+
rawToNumber,
|
|
91
|
+
formatFacetNumber,
|
|
92
|
+
formatFacetDate,
|
|
93
|
+
} from "./SvGrid.helpers";
|
|
94
|
+
|
|
95
|
+
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
96
|
+
|
|
97
|
+
let { ctrl, icon }: { ctrl: SvGridController<TFeatures, TData>; icon: Snippet<[string]> } = $props();
|
|
98
|
+
|
|
99
|
+
// View facade: re-bind the controller's reactive members so the markup
|
|
100
|
+
// (moved verbatim from SvGrid.svelte) stays identical.
|
|
101
|
+
const groupingControlsEnabled = $derived(ctrl.groupingControlsEnabled);
|
|
102
|
+
const filterMenuValues = $derived(ctrl.filterMenuValues);
|
|
103
|
+
const tooltip = $derived(ctrl.tooltip);
|
|
104
|
+
const showTooltipFor = $derived(ctrl.showTooltipFor);
|
|
105
|
+
const columnMenuFor = $derived(ctrl.columnMenuFor);
|
|
106
|
+
const columnMenuPos = $derived(ctrl.columnMenuPos);
|
|
107
|
+
const filterMenuFor = $derived(ctrl.filterMenuFor);
|
|
108
|
+
const filterMenuPos = $derived(ctrl.filterMenuPos);
|
|
109
|
+
const operatorMenuFor = $derived(ctrl.operatorMenuFor);
|
|
110
|
+
const operatorMenuPos = $derived(ctrl.operatorMenuPos);
|
|
111
|
+
const chooseColumnsPos = $derived(ctrl.chooseColumnsPos);
|
|
112
|
+
const showColumnFiltersEffective = $derived(ctrl.showColumnFiltersEffective);
|
|
113
|
+
const columnMenuTabsEnabled = $derived(ctrl.props.columnMenuTabs === true);
|
|
114
|
+
const grid = $derived(ctrl.grid);
|
|
115
|
+
const allColumns = $derived(ctrl.allColumns);
|
|
116
|
+
const isColumnPinned = $derived(ctrl.isColumnPinned);
|
|
117
|
+
const pinColumnLeft = $derived(ctrl.pinColumnLeft);
|
|
118
|
+
const pinColumnRight = $derived(ctrl.pinColumnRight);
|
|
119
|
+
const unpinColumn = $derived(ctrl.unpinColumn);
|
|
120
|
+
const sortDirectionByColumn = $derived(ctrl.sortDirectionByColumn);
|
|
121
|
+
const groupingColumns = $derived(ctrl.groupingColumns);
|
|
122
|
+
const columnVirtualizationEnabled = $derived(ctrl.columnVirtualizationEnabled);
|
|
123
|
+
const updateFilterOperator = $derived(ctrl.updateFilterOperator);
|
|
124
|
+
const updateFilterMenuValue = $derived(ctrl.updateFilterMenuValue);
|
|
125
|
+
const updateFilterMenuValueTo = $derived(ctrl.updateFilterMenuValueTo);
|
|
126
|
+
const operatorsForColumn = $derived(ctrl.operatorsForColumn);
|
|
127
|
+
const defaultOperatorFor = $derived(ctrl.defaultOperatorFor);
|
|
128
|
+
const operatorLabelFor = $derived(ctrl.operatorLabelFor);
|
|
129
|
+
const filterRowValues = $derived(ctrl.filterRowValues);
|
|
130
|
+
const inSuggestFor = $derived(ctrl.inSuggestFor);
|
|
131
|
+
const inSuggestPos = $derived(ctrl.inSuggestPos);
|
|
132
|
+
const inSuggestValues = $derived(ctrl.inSuggestValues);
|
|
133
|
+
const toggleFilterToken = $derived(ctrl.toggleFilterToken);
|
|
134
|
+
const facetValuesForColumn = $derived(ctrl.facetValuesForColumn);
|
|
135
|
+
|
|
136
|
+
// Patch a column's menu filter (used for the optional 2nd condition + join
|
|
137
|
+
// of multi-condition filtering). Merges into the existing entry, creating a
|
|
138
|
+
// base entry from the column's default operator when none exists yet.
|
|
139
|
+
function patchFilter(columnId: string, patch: Record<string, unknown>) {
|
|
140
|
+
const col = allColumns.find((c) => c.id === columnId);
|
|
141
|
+
const cur = ctrl.filterMenuValues[columnId] ?? { operator: defaultOperatorFor(col), value: "" };
|
|
142
|
+
ctrl.filterMenuValues = { ...ctrl.filterMenuValues, [columnId]: { ...cur, ...patch } };
|
|
143
|
+
}
|
|
144
|
+
const isColumnFiltered = $derived(ctrl.isColumnFiltered);
|
|
145
|
+
const closeMenus = $derived(ctrl.closeMenus);
|
|
146
|
+
|
|
147
|
+
// Column/filter/operator menus are position:fixed and anchored to their
|
|
148
|
+
// trigger by a one-time measurement taken when they open. On scroll they'd
|
|
149
|
+
// otherwise detach and float over the wrong place (#88), so close them when
|
|
150
|
+
// anything OUTSIDE them scrolls, or on resize. Scrolling the menu's own body
|
|
151
|
+
// or its facet list has to keep it open - hence onScrollOutside rather than a
|
|
152
|
+
// bare capture-phase listener.
|
|
153
|
+
$effect(() => {
|
|
154
|
+
if (!(columnMenuFor || filterMenuFor || operatorMenuFor || chooseColumnsPos)) return;
|
|
155
|
+
const close = () => closeMenus();
|
|
156
|
+
const offScroll = onScrollOutside(
|
|
157
|
+
() => Array.from(document.querySelectorAll<HTMLElement>('.sv-grid-menu')),
|
|
158
|
+
close,
|
|
159
|
+
);
|
|
160
|
+
window.addEventListener('resize', close);
|
|
161
|
+
return () => {
|
|
162
|
+
offScroll();
|
|
163
|
+
window.removeEventListener('resize', close);
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const autosizeColumn = $derived(ctrl.autosizeColumn);
|
|
168
|
+
const autosizeAllColumns = $derived(ctrl.autosizeAllColumns);
|
|
169
|
+
const resetColumns = $derived(ctrl.resetColumns);
|
|
170
|
+
const openChooseColumns = $derived(ctrl.openChooseColumns);
|
|
171
|
+
const sortColumnFromMenu = $derived(ctrl.sortColumnFromMenu);
|
|
172
|
+
const clearColumnSort = $derived(ctrl.clearColumnSort);
|
|
173
|
+
const groupByColumnFromMenu = $derived(ctrl.groupByColumnFromMenu);
|
|
174
|
+
const clearGroupingFromMenu = $derived(ctrl.clearGroupingFromMenu);
|
|
175
|
+
const columnMenuFacetValues = $derived(ctrl.columnMenuFacetValues);
|
|
176
|
+
const columnMenuVisibleFacets = $derived(ctrl.columnMenuVisibleFacets);
|
|
177
|
+
const isFacetChecked = $derived(ctrl.isFacetChecked);
|
|
178
|
+
const toggleFacetValue = $derived(ctrl.toggleFacetValue);
|
|
179
|
+
const isAllFacetsChecked = $derived(ctrl.isAllFacetsChecked);
|
|
180
|
+
const toggleAllFacets = $derived(ctrl.toggleAllFacets);
|
|
181
|
+
const clearColumnFilter = $derived(ctrl.clearColumnFilter);
|
|
182
|
+
const contextMenuFor = $derived(ctrl.contextMenuFor);
|
|
183
|
+
const contextMenuPos = $derived(ctrl.contextMenuPos);
|
|
184
|
+
const contextMenuItems = $derived(ctrl.contextMenuItems);
|
|
185
|
+
const closeContextMenu = $derived(ctrl.closeContextMenu);
|
|
186
|
+
const commentEditFor = $derived(ctrl.commentEditFor);
|
|
187
|
+
const saveComment = $derived(ctrl.saveComment);
|
|
188
|
+
const removeComment = $derived(ctrl.removeComment);
|
|
189
|
+
const closeCommentEditor = $derived(ctrl.closeCommentEditor);
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
{#if tooltip}
|
|
193
|
+
<!-- Custom hover tooltip. Floats above the grid; positioned by
|
|
194
|
+
showTooltipFor(). Notes and column tooltips both surface here. -->
|
|
195
|
+
<div
|
|
196
|
+
class="sv-grid-tooltip"
|
|
197
|
+
class:sv-grid-tooltip-above={!tooltip.below}
|
|
198
|
+
style={`left: ${tooltip.x}px; top: ${tooltip.y}px; ${tooltip.below ? '' : 'transform: translateY(-100%);'}`}
|
|
199
|
+
role="tooltip"
|
|
200
|
+
>{tooltip.text}</div>
|
|
201
|
+
{/if}
|
|
202
|
+
|
|
203
|
+
{#if columnMenuFor || filterMenuFor || operatorMenuFor || chooseColumnsPos}
|
|
204
|
+
<div
|
|
205
|
+
class="sv-grid-menu-backdrop"
|
|
206
|
+
role="presentation"
|
|
207
|
+
onclick={closeMenus}
|
|
208
|
+
></div>
|
|
209
|
+
{/if}
|
|
210
|
+
|
|
211
|
+
<!-- Shared filter UI: rendered both in the funnel popover and in the column
|
|
212
|
+
menu's Filter tab, so the two never diverge. -->
|
|
213
|
+
{#snippet filterPanelBody(colId: string)}
|
|
214
|
+
{@const menuColumn = allColumns.find((c) => c.id === colId)}
|
|
215
|
+
{@const menuOperatorOptions = operatorsForColumn(menuColumn)}
|
|
216
|
+
{@const menuActiveOperator = filterMenuValues[colId]?.operator ?? defaultOperatorFor(menuColumn)}
|
|
217
|
+
{@const menuInputType = getEditorInputType((menuColumn?.columnDef.editorType ?? "text") as CellEditorType)}
|
|
218
|
+
<div class="sv-grid-menu-filter">
|
|
219
|
+
<div class="sv-grid-menu-filter-head">
|
|
220
|
+
<span class="sv-grid-header-icon">{@render icon("filter")}</span> Filter
|
|
221
|
+
condition
|
|
222
|
+
</div>
|
|
223
|
+
<select
|
|
224
|
+
class="sv-grid-menu-operator-select"
|
|
225
|
+
aria-label="Filter condition"
|
|
226
|
+
value={menuActiveOperator}
|
|
227
|
+
onchange={(event) =>
|
|
228
|
+
updateFilterOperator(colId, (event.currentTarget as HTMLSelectElement).value as FilterOperator)}
|
|
229
|
+
>
|
|
230
|
+
{#each menuOperatorOptions as option (option.value)}
|
|
231
|
+
<option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
|
|
232
|
+
{/each}
|
|
233
|
+
</select>
|
|
234
|
+
{#if menuActiveOperator !== "isBlank" && menuActiveOperator !== "isNotBlank"}
|
|
235
|
+
{@const isSetOp =
|
|
236
|
+
menuActiveOperator === "in" || menuActiveOperator === "notIn"}
|
|
237
|
+
<input
|
|
238
|
+
class="sv-grid-menu-condition-value"
|
|
239
|
+
type={menuActiveOperator === "regex" ? "text" : menuInputType}
|
|
240
|
+
list={isSetOp ? `sv-in-list-${colId}` : undefined}
|
|
241
|
+
value={filterMenuValues[colId]?.value ?? ""}
|
|
242
|
+
placeholder={menuActiveOperator === "between"
|
|
243
|
+
? "From"
|
|
244
|
+
: isSetOp
|
|
245
|
+
? "value, value..."
|
|
246
|
+
: menuActiveOperator === "regex"
|
|
247
|
+
? "Pattern..."
|
|
248
|
+
: "Filter value..."}
|
|
249
|
+
oninput={(event) => updateFilterMenuValue(colId, (event.currentTarget as HTMLInputElement).value)}
|
|
250
|
+
/>
|
|
251
|
+
{#if isSetOp}
|
|
252
|
+
<datalist id={`sv-in-list-${colId}`}>
|
|
253
|
+
{#each facetValuesForColumn(colId).slice(0, 200) as v (v)}
|
|
254
|
+
<option value={v}></option>
|
|
255
|
+
{/each}
|
|
256
|
+
</datalist>
|
|
257
|
+
{/if}
|
|
258
|
+
{#if menuActiveOperator === "between"}
|
|
259
|
+
<input
|
|
260
|
+
class="sv-grid-menu-condition-value"
|
|
261
|
+
type={menuInputType}
|
|
262
|
+
value={filterMenuValues[colId]?.valueTo ?? ""}
|
|
263
|
+
placeholder="To"
|
|
264
|
+
oninput={(event) => updateFilterMenuValueTo(colId, (event.currentTarget as HTMLInputElement).value)}
|
|
265
|
+
/>
|
|
266
|
+
{/if}
|
|
267
|
+
{@const mf = filterMenuValues[colId]}
|
|
268
|
+
{#if mf?.operator2}
|
|
269
|
+
{@const op2 = mf.operator2 ?? defaultOperatorFor(menuColumn)}
|
|
270
|
+
<div class="sv-grid-menu-join" role="radiogroup" aria-label="Combine conditions">
|
|
271
|
+
<button type="button" class:is-on={(mf.join ?? "AND") === "AND"}
|
|
272
|
+
onclick={() => patchFilter(colId, { join: "AND" })}>AND</button>
|
|
273
|
+
<button type="button" class:is-on={mf.join === "OR"}
|
|
274
|
+
onclick={() => patchFilter(colId, { join: "OR" })}>OR</button>
|
|
275
|
+
<button type="button" class="sv-grid-menu-join-x" title="Remove second condition"
|
|
276
|
+
aria-label="Remove second condition"
|
|
277
|
+
onclick={() => patchFilter(colId, { operator2: undefined, value2: undefined, valueTo2: undefined, join: undefined })}>×</button>
|
|
278
|
+
</div>
|
|
279
|
+
<select
|
|
280
|
+
class="sv-grid-menu-operator-select"
|
|
281
|
+
aria-label="Second filter condition"
|
|
282
|
+
value={op2}
|
|
283
|
+
onchange={(event) =>
|
|
284
|
+
patchFilter(colId, { operator2: (event.currentTarget as HTMLSelectElement).value as FilterOperator })}
|
|
285
|
+
>
|
|
286
|
+
{#each menuOperatorOptions as option (option.value)}
|
|
287
|
+
<option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
|
|
288
|
+
{/each}
|
|
289
|
+
</select>
|
|
290
|
+
{#if op2 !== "isBlank"}
|
|
291
|
+
<input
|
|
292
|
+
class="sv-grid-menu-condition-value"
|
|
293
|
+
type={menuInputType}
|
|
294
|
+
value={mf.value2 ?? ""}
|
|
295
|
+
placeholder={op2 === "between" ? "From" : "Filter value..."}
|
|
296
|
+
oninput={(event) => patchFilter(colId, { value2: (event.currentTarget as HTMLInputElement).value })}
|
|
297
|
+
/>
|
|
298
|
+
{#if op2 === "between"}
|
|
299
|
+
<input
|
|
300
|
+
class="sv-grid-menu-condition-value"
|
|
301
|
+
type={menuInputType}
|
|
302
|
+
value={mf.valueTo2 ?? ""}
|
|
303
|
+
placeholder="To"
|
|
304
|
+
oninput={(event) => patchFilter(colId, { valueTo2: (event.currentTarget as HTMLInputElement).value })}
|
|
305
|
+
/>
|
|
306
|
+
{/if}
|
|
307
|
+
{/if}
|
|
308
|
+
{:else}
|
|
309
|
+
<button type="button" class="sv-grid-menu-add-cond"
|
|
310
|
+
onclick={() => patchFilter(colId, { operator2: defaultOperatorFor(menuColumn), value2: "", join: "AND" })}>
|
|
311
|
+
+ Add condition
|
|
312
|
+
</button>
|
|
313
|
+
{/if}
|
|
314
|
+
{/if}
|
|
315
|
+
<div class="sv-grid-menu-sep"></div>
|
|
316
|
+
<div class="sv-grid-menu-filter-head">Values</div>
|
|
317
|
+
<input
|
|
318
|
+
class="sv-grid-menu-search"
|
|
319
|
+
placeholder="Search values..."
|
|
320
|
+
bind:value={ctrl.columnMenuSearch}
|
|
321
|
+
/>
|
|
322
|
+
<label class="sv-grid-facet sv-grid-facet-all">
|
|
323
|
+
<input type="checkbox" checked={isAllFacetsChecked(colId)} onchange={() => toggleAllFacets(colId)} />
|
|
324
|
+
<span class="sv-grid-facet-label">(Select all)</span>
|
|
325
|
+
</label>
|
|
326
|
+
<div class="sv-grid-facet-list">
|
|
327
|
+
{#each columnMenuVisibleFacets as value (value)}
|
|
328
|
+
<label class="sv-grid-facet">
|
|
329
|
+
<input type="checkbox" checked={isFacetChecked(colId, value)} onchange={() => toggleFacetValue(colId, value)} />
|
|
330
|
+
<span class="sv-grid-facet-label">{value === "" ? "(Blanks)" : value}</span>
|
|
331
|
+
</label>
|
|
332
|
+
{:else}
|
|
333
|
+
<div class="sv-grid-facet-empty">No values</div>
|
|
334
|
+
{/each}
|
|
335
|
+
</div>
|
|
336
|
+
{#if columnMenuFacetValues.length > columnMenuVisibleFacets.length}
|
|
337
|
+
<div class="sv-grid-facet-note">
|
|
338
|
+
Showing {columnMenuVisibleFacets.length} of {columnMenuFacetValues.length}
|
|
339
|
+
</div>
|
|
340
|
+
{/if}
|
|
341
|
+
<div class="sv-grid-menu-actions">
|
|
342
|
+
<button
|
|
343
|
+
type="button"
|
|
344
|
+
class="sv-grid-menu-btn"
|
|
345
|
+
disabled={!isColumnFiltered(colId)}
|
|
346
|
+
onclick={() => clearColumnFilter(colId)}
|
|
347
|
+
>
|
|
348
|
+
Clear filter
|
|
349
|
+
</button>
|
|
350
|
+
<button type="button" class="sv-grid-menu-btn sv-grid-menu-btn-primary" onclick={closeMenus}>
|
|
351
|
+
Done
|
|
352
|
+
</button>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
{/snippet}
|
|
356
|
+
|
|
357
|
+
{#if columnMenuFor}
|
|
358
|
+
{@const menuColumnId = columnMenuFor}
|
|
359
|
+
{@const menuCol = allColumns.find((c) => c.id === menuColumnId)}
|
|
360
|
+
{@const menuCanSort = menuCol?.getCanSort?.() ?? false}
|
|
361
|
+
{@const menuCanFilter = (menuCol?.columnDef.field != null) && (menuCol?.columnDef.filterable !== false)}
|
|
362
|
+
{@const menuTab = ctrl.columnMenuTab}
|
|
363
|
+
<div
|
|
364
|
+
class="sv-grid-menu sv-grid-column-menu"
|
|
365
|
+
role="menu"
|
|
366
|
+
style={`left: ${columnMenuPos.x}px; top: ${columnMenuPos.y}px; max-height: calc(100vh - ${columnMenuPos.y}px - 12px);`}
|
|
367
|
+
>
|
|
368
|
+
{#if columnMenuTabsEnabled}
|
|
369
|
+
<div class="sv-grid-menu-tabs" role="tablist">
|
|
370
|
+
<button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "general"}
|
|
371
|
+
role="tab" aria-selected={menuTab === "general"}
|
|
372
|
+
onclick={() => (ctrl.columnMenuTab = "general")}>General</button>
|
|
373
|
+
{#if menuCanFilter && showColumnFiltersEffective}
|
|
374
|
+
<button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "filter"}
|
|
375
|
+
role="tab" aria-selected={menuTab === "filter"}
|
|
376
|
+
onclick={() => (ctrl.columnMenuTab = "filter")}>Filter</button>
|
|
377
|
+
{/if}
|
|
378
|
+
<button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "columns"}
|
|
379
|
+
role="tab" aria-selected={menuTab === "columns"}
|
|
380
|
+
onclick={() => (ctrl.columnMenuTab = "columns")}>Columns</button>
|
|
381
|
+
</div>
|
|
382
|
+
{/if}
|
|
383
|
+
{#if columnMenuTabsEnabled && menuTab === "filter" && menuCanFilter && showColumnFiltersEffective}
|
|
384
|
+
{@render filterPanelBody(menuColumnId)}
|
|
385
|
+
{:else if columnMenuTabsEnabled && menuTab === "columns"}
|
|
386
|
+
<div class="sv-grid-menu-filter-head">Visible columns</div>
|
|
387
|
+
<div class="sv-grid-facet-list">
|
|
388
|
+
{#each grid.getAllColumns() as column (column.id)}
|
|
389
|
+
<label class="sv-grid-facet">
|
|
390
|
+
<input
|
|
391
|
+
type="checkbox"
|
|
392
|
+
checked={!ctrl.hiddenColumns[column.id]}
|
|
393
|
+
onchange={(event) => {
|
|
394
|
+
const visible = (event.currentTarget as HTMLInputElement).checked;
|
|
395
|
+
if (visible) {
|
|
396
|
+
const next = { ...ctrl.hiddenColumns };
|
|
397
|
+
delete next[column.id];
|
|
398
|
+
ctrl.hiddenColumns = next;
|
|
399
|
+
} else {
|
|
400
|
+
ctrl.hiddenColumns = { ...ctrl.hiddenColumns, [column.id]: true };
|
|
401
|
+
}
|
|
402
|
+
}}
|
|
403
|
+
/>
|
|
404
|
+
<span class="sv-grid-facet-label"
|
|
405
|
+
>{typeof column.columnDef.header === "string" ? column.columnDef.header : column.id}</span
|
|
406
|
+
>
|
|
407
|
+
</label>
|
|
408
|
+
{/each}
|
|
409
|
+
</div>
|
|
410
|
+
{:else}
|
|
411
|
+
{#if menuCanSort}
|
|
412
|
+
<button
|
|
413
|
+
type="button"
|
|
414
|
+
class="sv-grid-menu-item"
|
|
415
|
+
role="menuitem"
|
|
416
|
+
onclick={() => sortColumnFromMenu(menuColumnId, false)}
|
|
417
|
+
>
|
|
418
|
+
<span class="sv-grid-header-icon">{@render icon("sort-asc")}</span> Sort
|
|
419
|
+
ascending
|
|
420
|
+
</button>
|
|
421
|
+
<button
|
|
422
|
+
type="button"
|
|
423
|
+
class="sv-grid-menu-item"
|
|
424
|
+
role="menuitem"
|
|
425
|
+
onclick={() => sortColumnFromMenu(menuColumnId, true)}
|
|
426
|
+
>
|
|
427
|
+
<span class="sv-grid-header-icon">{@render icon("sort-desc")}</span> Sort
|
|
428
|
+
descending
|
|
429
|
+
</button>
|
|
430
|
+
<button
|
|
431
|
+
type="button"
|
|
432
|
+
class="sv-grid-menu-item"
|
|
433
|
+
role="menuitem"
|
|
434
|
+
disabled={!sortDirectionByColumn[menuColumnId]}
|
|
435
|
+
onclick={() => clearColumnSort(menuColumnId)}
|
|
436
|
+
>
|
|
437
|
+
<span class="sv-grid-header-icon">{@render icon("x")}</span> Remove sort
|
|
438
|
+
</button>
|
|
439
|
+
{/if}
|
|
440
|
+
{#if !columnVirtualizationEnabled}
|
|
441
|
+
{@const menuPinSide = isColumnPinned(menuColumnId)}
|
|
442
|
+
<div class="sv-grid-menu-sep"></div>
|
|
443
|
+
<button
|
|
444
|
+
type="button"
|
|
445
|
+
class="sv-grid-menu-item"
|
|
446
|
+
role="menuitem"
|
|
447
|
+
disabled={menuPinSide === "left"}
|
|
448
|
+
onclick={() => pinColumnLeft(menuColumnId)}
|
|
449
|
+
>
|
|
450
|
+
<span class="sv-grid-header-icon"
|
|
451
|
+
>{@render icon("op-startsWith")}</span
|
|
452
|
+
> Pin to left
|
|
453
|
+
</button>
|
|
454
|
+
<button
|
|
455
|
+
type="button"
|
|
456
|
+
class="sv-grid-menu-item"
|
|
457
|
+
role="menuitem"
|
|
458
|
+
disabled={menuPinSide === "right"}
|
|
459
|
+
onclick={() => pinColumnRight(menuColumnId)}
|
|
460
|
+
>
|
|
461
|
+
<span class="sv-grid-header-icon"
|
|
462
|
+
>{@render icon("op-greaterThan")}</span
|
|
463
|
+
> Pin to right
|
|
464
|
+
</button>
|
|
465
|
+
<button
|
|
466
|
+
type="button"
|
|
467
|
+
class="sv-grid-menu-item"
|
|
468
|
+
role="menuitem"
|
|
469
|
+
disabled={!menuPinSide}
|
|
470
|
+
onclick={() => unpinColumn(menuColumnId)}
|
|
471
|
+
>
|
|
472
|
+
<span class="sv-grid-header-icon">{@render icon("x")}</span> Unpin column
|
|
473
|
+
</button>
|
|
474
|
+
{/if}
|
|
475
|
+
<div class="sv-grid-menu-sep"></div>
|
|
476
|
+
<button
|
|
477
|
+
type="button"
|
|
478
|
+
class="sv-grid-menu-item"
|
|
479
|
+
role="menuitem"
|
|
480
|
+
onclick={() => {
|
|
481
|
+
autosizeColumn(menuColumnId);
|
|
482
|
+
closeMenus();
|
|
483
|
+
}}
|
|
484
|
+
>
|
|
485
|
+
<span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
|
|
486
|
+
this column
|
|
487
|
+
</button>
|
|
488
|
+
<button
|
|
489
|
+
type="button"
|
|
490
|
+
class="sv-grid-menu-item"
|
|
491
|
+
role="menuitem"
|
|
492
|
+
onclick={() => {
|
|
493
|
+
autosizeAllColumns();
|
|
494
|
+
closeMenus();
|
|
495
|
+
}}
|
|
496
|
+
>
|
|
497
|
+
<span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
|
|
498
|
+
all columns
|
|
499
|
+
</button>
|
|
500
|
+
{#if groupingControlsEnabled}
|
|
501
|
+
<div class="sv-grid-menu-sep"></div>
|
|
502
|
+
<button
|
|
503
|
+
type="button"
|
|
504
|
+
class="sv-grid-menu-item"
|
|
505
|
+
role="menuitem"
|
|
506
|
+
disabled={groupingColumns.includes(menuColumnId)}
|
|
507
|
+
onclick={() => groupByColumnFromMenu(menuColumnId)}
|
|
508
|
+
>
|
|
509
|
+
<span class="sv-grid-header-icon">{@render icon("group")}</span> Group
|
|
510
|
+
by this column
|
|
511
|
+
</button>
|
|
512
|
+
<button
|
|
513
|
+
type="button"
|
|
514
|
+
class="sv-grid-menu-item"
|
|
515
|
+
role="menuitem"
|
|
516
|
+
disabled={!groupingColumns.includes(menuColumnId)}
|
|
517
|
+
onclick={() => clearGroupingFromMenu(menuColumnId)}
|
|
518
|
+
>
|
|
519
|
+
<span class="sv-grid-header-icon">{@render icon("x")}</span> Remove grouping
|
|
520
|
+
</button>
|
|
521
|
+
{/if}
|
|
522
|
+
<div class="sv-grid-menu-sep"></div>
|
|
523
|
+
{#if !columnMenuTabsEnabled}
|
|
524
|
+
<!-- Flat menu: reach the column chooser via a submenu (tabbed mode has
|
|
525
|
+
a Columns tab instead). -->
|
|
526
|
+
<button
|
|
527
|
+
type="button"
|
|
528
|
+
class="sv-grid-menu-item"
|
|
529
|
+
class:is-open={chooseColumnsPos !== null}
|
|
530
|
+
role="menuitem"
|
|
531
|
+
aria-haspopup="menu"
|
|
532
|
+
aria-expanded={chooseColumnsPos !== null}
|
|
533
|
+
onclick={(event) => openChooseColumns(event)}
|
|
534
|
+
>
|
|
535
|
+
<span class="sv-grid-header-icon">{@render icon("columns")}</span>
|
|
536
|
+
Choose columns
|
|
537
|
+
<span class="sv-grid-header-icon sv-grid-menu-item-chevron">{@render icon("chevron-down")}</span>
|
|
538
|
+
</button>
|
|
539
|
+
{/if}
|
|
540
|
+
<button
|
|
541
|
+
type="button"
|
|
542
|
+
class="sv-grid-menu-item"
|
|
543
|
+
role="menuitem"
|
|
544
|
+
onclick={() => {
|
|
545
|
+
resetColumns();
|
|
546
|
+
closeMenus();
|
|
547
|
+
}}
|
|
548
|
+
>
|
|
549
|
+
<span class="sv-grid-header-icon">{@render icon("reset")}</span> Reset columns
|
|
550
|
+
</button>
|
|
551
|
+
{/if}
|
|
552
|
+
</div>
|
|
553
|
+
{/if}
|
|
554
|
+
|
|
555
|
+
{#if filterMenuFor && showColumnFiltersEffective}
|
|
556
|
+
<div
|
|
557
|
+
class="sv-grid-menu sv-grid-filter-menu"
|
|
558
|
+
role="menu"
|
|
559
|
+
style={`left: ${filterMenuPos.x}px; top: ${filterMenuPos.y}px; max-height: calc(100vh - ${filterMenuPos.y}px - 12px);`}
|
|
560
|
+
>
|
|
561
|
+
{@render filterPanelBody(filterMenuFor)}
|
|
562
|
+
</div>
|
|
563
|
+
{/if}
|
|
564
|
+
|
|
565
|
+
{#if chooseColumnsPos}
|
|
566
|
+
{@const everyColumn = grid.getAllColumns()}
|
|
567
|
+
<div
|
|
568
|
+
class="sv-grid-menu sv-grid-choose-columns-menu"
|
|
569
|
+
role="menu"
|
|
570
|
+
style={`left: ${chooseColumnsPos.x}px; top: ${chooseColumnsPos.y}px;`}
|
|
571
|
+
>
|
|
572
|
+
<div class="sv-grid-menu-filter-head">Visible columns</div>
|
|
573
|
+
<div class="sv-grid-facet-list">
|
|
574
|
+
{#each everyColumn as column (column.id)}
|
|
575
|
+
<label class="sv-grid-facet">
|
|
576
|
+
<input
|
|
577
|
+
type="checkbox"
|
|
578
|
+
checked={!ctrl.hiddenColumns[column.id]}
|
|
579
|
+
onchange={(event) => {
|
|
580
|
+
const visible = (event.currentTarget as HTMLInputElement)
|
|
581
|
+
.checked;
|
|
582
|
+
if (visible) {
|
|
583
|
+
const next = { ...ctrl.hiddenColumns };
|
|
584
|
+
delete next[column.id];
|
|
585
|
+
ctrl.hiddenColumns = next;
|
|
586
|
+
} else {
|
|
587
|
+
ctrl.hiddenColumns = { ...ctrl.hiddenColumns, [column.id]: true };
|
|
588
|
+
}
|
|
589
|
+
}}
|
|
590
|
+
/>
|
|
591
|
+
<span class="sv-grid-facet-label"
|
|
592
|
+
>{typeof column.columnDef.header === "string"
|
|
593
|
+
? column.columnDef.header
|
|
594
|
+
: column.id}</span
|
|
595
|
+
>
|
|
596
|
+
</label>
|
|
597
|
+
{/each}
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
600
|
+
{/if}
|
|
601
|
+
|
|
602
|
+
{#if operatorMenuFor}
|
|
603
|
+
{@const opColumnId = operatorMenuFor}
|
|
604
|
+
{@const opColumn = allColumns.find((c) => c.id === opColumnId)}
|
|
605
|
+
{@const opOptions = operatorsForColumn(opColumn)}
|
|
606
|
+
{@const opActive =
|
|
607
|
+
filterMenuValues[opColumnId]?.operator ?? defaultOperatorFor(opColumn)}
|
|
608
|
+
<div
|
|
609
|
+
class="sv-grid-menu sv-grid-operator-menu"
|
|
610
|
+
role="menu"
|
|
611
|
+
style={`left: ${operatorMenuPos.x}px; top: ${operatorMenuPos.y}px;`}
|
|
612
|
+
>
|
|
613
|
+
{#each opOptions as option (option.value)}
|
|
614
|
+
<button
|
|
615
|
+
type="button"
|
|
616
|
+
class="sv-grid-menu-item"
|
|
617
|
+
role="menuitemradio"
|
|
618
|
+
aria-checked={opActive === option.value}
|
|
619
|
+
onclick={() => {
|
|
620
|
+
updateFilterOperator(opColumnId, option.value);
|
|
621
|
+
closeMenus();
|
|
622
|
+
}}
|
|
623
|
+
>
|
|
624
|
+
<span class="sv-grid-header-icon"
|
|
625
|
+
>{@render icon(option.iconName)}</span
|
|
626
|
+
>
|
|
627
|
+
{operatorLabelFor(option, opColumn)}
|
|
628
|
+
</button>
|
|
629
|
+
{/each}
|
|
630
|
+
</div>
|
|
631
|
+
{/if}
|
|
632
|
+
|
|
633
|
+
{#if inSuggestFor}
|
|
634
|
+
{@const suggestColId = inSuggestFor}
|
|
635
|
+
{@const selectedTokens = new Set(
|
|
636
|
+
splitInTokens(filterRowValues[suggestColId] ?? ""),
|
|
637
|
+
)}
|
|
638
|
+
<!-- Value suggestions for an `in` / `notIn` chip input. Items use
|
|
639
|
+
`onmousedown` (preventDefault) so clicking one keeps focus in the
|
|
640
|
+
chip input and the dropdown stays open for multi-select; the input's
|
|
641
|
+
own blur handler closes it when focus truly leaves. -->
|
|
642
|
+
<div
|
|
643
|
+
class="sv-grid-menu sv-grid-in-suggest"
|
|
644
|
+
role="listbox"
|
|
645
|
+
aria-multiselectable="true"
|
|
646
|
+
aria-label="Filter values"
|
|
647
|
+
style={`left: ${inSuggestPos.x}px; top: ${inSuggestPos.y}px;`}
|
|
648
|
+
>
|
|
649
|
+
{#each inSuggestValues as value (value)}
|
|
650
|
+
{@const checked = selectedTokens.has(value)}
|
|
651
|
+
<button
|
|
652
|
+
type="button"
|
|
653
|
+
class="sv-grid-menu-item sv-grid-in-suggest-item"
|
|
654
|
+
role="option"
|
|
655
|
+
aria-selected={checked}
|
|
656
|
+
onmousedown={(event) => {
|
|
657
|
+
event.preventDefault();
|
|
658
|
+
toggleFilterToken(suggestColId, value);
|
|
659
|
+
}}
|
|
660
|
+
>
|
|
661
|
+
<span class="sv-grid-in-suggest-check" aria-hidden="true"
|
|
662
|
+
>{checked ? "✓" : ""}</span
|
|
663
|
+
>
|
|
664
|
+
<span class="sv-grid-in-suggest-label"
|
|
665
|
+
>{value === "" ? "(Blanks)" : value}</span
|
|
666
|
+
>
|
|
667
|
+
</button>
|
|
668
|
+
{:else}
|
|
669
|
+
<div class="sv-grid-facet-empty">No values</div>
|
|
670
|
+
{/each}
|
|
671
|
+
</div>
|
|
672
|
+
{/if}
|
|
673
|
+
|
|
674
|
+
{#if contextMenuFor}
|
|
675
|
+
<div
|
|
676
|
+
class="sv-grid-menu-backdrop"
|
|
677
|
+
role="presentation"
|
|
678
|
+
oncontextmenu={(e) => { e.preventDefault(); closeContextMenu(); }}
|
|
679
|
+
onclick={closeContextMenu}
|
|
680
|
+
></div>
|
|
681
|
+
<div
|
|
682
|
+
class="sv-grid-menu sv-grid-context-menu"
|
|
683
|
+
role="menu"
|
|
684
|
+
style={`left: ${contextMenuPos.x}px; top: ${contextMenuPos.y}px;`}
|
|
685
|
+
>
|
|
686
|
+
{#each contextMenuItems() as item (item.key)}
|
|
687
|
+
{#if item.separator}
|
|
688
|
+
<div class="sv-grid-menu-sep"></div>
|
|
689
|
+
{:else}
|
|
690
|
+
<button
|
|
691
|
+
type="button"
|
|
692
|
+
class="sv-grid-menu-item"
|
|
693
|
+
role="menuitem"
|
|
694
|
+
disabled={item.disabled}
|
|
695
|
+
onclick={() => { item.run?.(); closeContextMenu(); }}
|
|
696
|
+
>{item.label}</button>
|
|
697
|
+
{/if}
|
|
698
|
+
{/each}
|
|
699
|
+
</div>
|
|
700
|
+
{/if}
|
|
701
|
+
|
|
702
|
+
{#if commentEditFor}
|
|
703
|
+
<div
|
|
704
|
+
class="sv-grid-menu-backdrop"
|
|
705
|
+
role="presentation"
|
|
706
|
+
onclick={closeCommentEditor}
|
|
707
|
+
></div>
|
|
708
|
+
<div
|
|
709
|
+
class="sv-grid-menu sv-grid-comment-editor"
|
|
710
|
+
role="dialog"
|
|
711
|
+
aria-label="Edit comment"
|
|
712
|
+
style={`left: ${commentEditFor.x}px; top: ${commentEditFor.y}px;`}
|
|
713
|
+
>
|
|
714
|
+
<!-- svelte-ignore a11y_autofocus -->
|
|
715
|
+
<textarea
|
|
716
|
+
class="sv-grid-comment-textarea"
|
|
717
|
+
rows="3"
|
|
718
|
+
placeholder="Add a comment…"
|
|
719
|
+
autofocus
|
|
720
|
+
bind:value={ctrl.commentDraft}
|
|
721
|
+
onkeydown={(e) => {
|
|
722
|
+
e.stopPropagation();
|
|
723
|
+
if (e.key === "Escape") { e.preventDefault(); closeCommentEditor(); }
|
|
724
|
+
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); saveComment(); }
|
|
725
|
+
}}
|
|
726
|
+
></textarea>
|
|
727
|
+
<div class="sv-grid-comment-actions">
|
|
728
|
+
<button type="button" class="sv-grid-comment-remove" onclick={removeComment}>Remove</button>
|
|
729
|
+
<span class="sv-grid-comment-spacer"></span>
|
|
730
|
+
<button type="button" class="sv-grid-comment-cancel" onclick={closeCommentEditor}>Cancel</button>
|
|
731
|
+
<button type="button" class="sv-grid-comment-save" onclick={saveComment}>Save</button>
|
|
732
|
+
</div>
|
|
733
|
+
</div>
|
|
734
|
+
{/if}
|