@svgrid/grid 1.1.0 → 1.1.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/dist/GridMenus.svelte +205 -127
- package/dist/SvGrid.controller.svelte.d.ts +44 -1
- package/dist/SvGrid.controller.svelte.js +223 -38
- package/dist/SvGrid.css +2012 -1709
- package/dist/SvGrid.helpers.test.d.ts +1 -0
- package/dist/SvGrid.helpers.test.js +298 -0
- package/dist/SvGrid.svelte +2346 -2043
- package/dist/SvGrid.types.d.ts +91 -4
- package/dist/aligned-grids.d.ts +6 -0
- package/dist/aligned-grids.js +84 -0
- package/dist/aligned-grids.test.d.ts +1 -0
- package/dist/aligned-grids.test.js +75 -0
- package/dist/build-api.coverage.test.d.ts +20 -0
- package/dist/build-api.coverage.test.js +505 -0
- package/dist/build-api.js +59 -29
- package/dist/cell-render.test.d.ts +1 -0
- package/dist/cell-render.test.js +338 -0
- package/dist/chart-export.test.d.ts +1 -0
- package/dist/chart-export.test.js +302 -0
- package/dist/chart.coverage.test.d.ts +1 -0
- package/dist/chart.coverage.test.js +748 -0
- package/dist/clipboard.js +88 -24
- package/dist/clipboard.test.d.ts +1 -0
- package/dist/clipboard.test.js +700 -0
- package/dist/collaboration.coverage.test.d.ts +1 -0
- package/dist/collaboration.coverage.test.js +200 -0
- package/dist/column-groups.d.ts +19 -0
- package/dist/column-groups.js +62 -0
- package/dist/column-groups.test.d.ts +1 -0
- package/dist/column-groups.test.js +56 -0
- package/dist/column-types.d.ts +10 -0
- package/dist/column-types.js +63 -0
- package/dist/column-types.test.d.ts +1 -0
- package/dist/column-types.test.js +62 -0
- package/dist/columns.test.d.ts +1 -0
- package/dist/columns.test.js +625 -0
- package/dist/core.d.ts +85 -0
- package/dist/editing.d.ts +7 -0
- package/dist/editing.js +191 -5
- package/dist/editing.test.d.ts +1 -0
- package/dist/editing.test.js +732 -0
- package/dist/editors/cell-editors.coverage.test.d.ts +1 -0
- package/dist/editors/cell-editors.coverage.test.js +139 -0
- package/dist/facet-buckets.test.d.ts +1 -0
- package/dist/facet-buckets.test.js +296 -0
- package/dist/filter-operators.test.d.ts +1 -0
- package/dist/filter-operators.test.js +135 -0
- package/dist/hyperformula-adapter.test.d.ts +1 -0
- package/dist/hyperformula-adapter.test.js +205 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/keyboard-handlers.coverage.test.d.ts +1 -0
- package/dist/keyboard-handlers.coverage.test.js +495 -0
- package/dist/keyboard-handlers.js +7 -2
- package/dist/menus.js +1 -0
- package/dist/menus.test.d.ts +1 -0
- package/dist/menus.test.js +560 -0
- package/dist/named-views.coverage.test.d.ts +1 -0
- package/dist/named-views.coverage.test.js +180 -0
- package/dist/row-drag.d.ts +49 -0
- package/dist/row-drag.js +221 -0
- package/dist/row-drag.test.d.ts +1 -0
- package/dist/row-drag.test.js +142 -0
- package/dist/row-resize.test.d.ts +1 -0
- package/dist/row-resize.test.js +329 -0
- package/dist/scroll-sync.js +3 -0
- package/dist/scroll-sync.test.d.ts +1 -0
- package/dist/scroll-sync.test.js +290 -0
- package/dist/selection.d.ts +7 -1
- package/dist/selection.js +76 -36
- package/dist/selection.multi-range.test.d.ts +1 -0
- package/dist/selection.multi-range.test.js +55 -0
- package/dist/selection.test.d.ts +1 -0
- package/dist/selection.test.js +647 -0
- package/dist/server-data-source.coverage.test.d.ts +1 -0
- package/dist/server-data-source.coverage.test.js +154 -0
- package/dist/spreadsheet.d.ts +30 -0
- package/dist/spreadsheet.js +48 -0
- package/dist/spreadsheet.test.d.ts +1 -0
- package/dist/spreadsheet.test.js +446 -0
- package/dist/sv-grid-scrollbar.js +13 -1
- package/dist/svgrid-wrapper.types.d.ts +19 -0
- package/dist/svgrid.behavior.test.js +20 -0
- package/dist/svgrid.interaction.test.js +31 -0
- package/dist/svgrid.new-features.wrapper.test.js +34 -2
- package/dist/test-setup.js +9 -3
- package/dist/virtualization/scroll-scaling.d.ts +17 -0
- package/dist/virtualization/scroll-scaling.js +35 -0
- package/dist/virtualization/scroll-scaling.test.js +42 -1
- package/package.json +2 -1
- package/src/GridMenus.svelte +205 -127
- package/src/SvGrid.controller.svelte.ts +2352 -2195
- package/src/SvGrid.css +2012 -1747
- package/src/SvGrid.svelte +2346 -2047
- package/src/SvGrid.types.ts +537 -456
- package/src/aligned-grids.test.ts +80 -0
- package/src/aligned-grids.ts +87 -0
- package/src/build-api.ts +683 -663
- package/src/clipboard.test.ts +49 -0
- package/src/clipboard.ts +51 -23
- package/src/column-groups.test.ts +59 -0
- package/src/column-groups.ts +80 -0
- package/src/column-types.test.ts +68 -0
- package/src/column-types.ts +82 -0
- package/src/core.ts +78 -0
- package/src/editing.ts +669 -513
- package/src/index.ts +12 -0
- package/src/menus.ts +1 -0
- package/src/row-drag.test.ts +168 -0
- package/src/row-drag.ts +255 -0
- package/src/scroll-sync.ts +2 -0
- package/src/selection.multi-range.test.ts +61 -0
- package/src/selection.ts +71 -37
- package/src/spreadsheet.test.ts +489 -445
- package/src/spreadsheet.ts +304 -246
- package/src/svgrid-wrapper.types.ts +19 -0
- package/src/svgrid.new-features.wrapper.test.ts +2 -2
package/dist/GridMenus.svelte
CHANGED
|
@@ -108,6 +108,7 @@
|
|
|
108
108
|
const operatorMenuPos = $derived(ctrl.operatorMenuPos);
|
|
109
109
|
const chooseColumnsPos = $derived(ctrl.chooseColumnsPos);
|
|
110
110
|
const showColumnFiltersEffective = $derived(ctrl.showColumnFiltersEffective);
|
|
111
|
+
const columnMenuTabsEnabled = $derived(ctrl.props.columnMenuTabs === true);
|
|
111
112
|
const grid = $derived(ctrl.grid);
|
|
112
113
|
const allColumns = $derived(ctrl.allColumns);
|
|
113
114
|
const isColumnPinned = $derived(ctrl.isColumnPinned);
|
|
@@ -123,6 +124,15 @@
|
|
|
123
124
|
const operatorsForColumn = $derived(ctrl.operatorsForColumn);
|
|
124
125
|
const defaultOperatorFor = $derived(ctrl.defaultOperatorFor);
|
|
125
126
|
const operatorLabelFor = $derived(ctrl.operatorLabelFor);
|
|
127
|
+
|
|
128
|
+
// Patch a column's menu filter (used for the optional 2nd condition + join
|
|
129
|
+
// of multi-condition filtering). Merges into the existing entry, creating a
|
|
130
|
+
// base entry from the column's default operator when none exists yet.
|
|
131
|
+
function patchFilter(columnId: string, patch: Record<string, unknown>) {
|
|
132
|
+
const col = allColumns.find((c) => c.id === columnId);
|
|
133
|
+
const cur = ctrl.filterMenuValues[columnId] ?? { operator: defaultOperatorFor(col), value: "" };
|
|
134
|
+
ctrl.filterMenuValues = { ...ctrl.filterMenuValues, [columnId]: { ...cur, ...patch } };
|
|
135
|
+
}
|
|
126
136
|
const isColumnFiltered = $derived(ctrl.isColumnFiltered);
|
|
127
137
|
const closeMenus = $derived(ctrl.closeMenus);
|
|
128
138
|
const autosizeColumn = $derived(ctrl.autosizeColumn);
|
|
@@ -169,15 +179,190 @@
|
|
|
169
179
|
></div>
|
|
170
180
|
{/if}
|
|
171
181
|
|
|
182
|
+
<!-- Shared filter UI: rendered both in the funnel popover and in the column
|
|
183
|
+
menu's Filter tab, so the two never diverge. -->
|
|
184
|
+
{#snippet filterPanelBody(colId: string)}
|
|
185
|
+
{@const menuColumn = allColumns.find((c) => c.id === colId)}
|
|
186
|
+
{@const menuOperatorOptions = operatorsForColumn(menuColumn)}
|
|
187
|
+
{@const menuActiveOperator = filterMenuValues[colId]?.operator ?? defaultOperatorFor(menuColumn)}
|
|
188
|
+
{@const menuInputType = getEditorInputType((menuColumn?.columnDef.editorType ?? "text") as CellEditorType)}
|
|
189
|
+
<div class="sv-grid-menu-filter">
|
|
190
|
+
<div class="sv-grid-menu-filter-head">
|
|
191
|
+
<span class="sv-grid-header-icon">{@render icon("filter")}</span> Filter
|
|
192
|
+
condition
|
|
193
|
+
</div>
|
|
194
|
+
<select
|
|
195
|
+
class="sv-grid-menu-operator-select"
|
|
196
|
+
aria-label="Filter condition"
|
|
197
|
+
value={menuActiveOperator}
|
|
198
|
+
onchange={(event) =>
|
|
199
|
+
updateFilterOperator(colId, (event.currentTarget as HTMLSelectElement).value as FilterOperator)}
|
|
200
|
+
>
|
|
201
|
+
{#each menuOperatorOptions as option (option.value)}
|
|
202
|
+
<option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
|
|
203
|
+
{/each}
|
|
204
|
+
</select>
|
|
205
|
+
{#if menuActiveOperator !== "isBlank"}
|
|
206
|
+
<input
|
|
207
|
+
class="sv-grid-menu-condition-value"
|
|
208
|
+
type={menuInputType}
|
|
209
|
+
value={filterMenuValues[colId]?.value ?? ""}
|
|
210
|
+
placeholder={menuActiveOperator === "between" ? "From" : "Filter value..."}
|
|
211
|
+
oninput={(event) => updateFilterMenuValue(colId, (event.currentTarget as HTMLInputElement).value)}
|
|
212
|
+
/>
|
|
213
|
+
{#if menuActiveOperator === "between"}
|
|
214
|
+
<input
|
|
215
|
+
class="sv-grid-menu-condition-value"
|
|
216
|
+
type={menuInputType}
|
|
217
|
+
value={filterMenuValues[colId]?.valueTo ?? ""}
|
|
218
|
+
placeholder="To"
|
|
219
|
+
oninput={(event) => updateFilterMenuValueTo(colId, (event.currentTarget as HTMLInputElement).value)}
|
|
220
|
+
/>
|
|
221
|
+
{/if}
|
|
222
|
+
{@const mf = filterMenuValues[colId]}
|
|
223
|
+
{#if mf?.operator2}
|
|
224
|
+
{@const op2 = mf.operator2 ?? defaultOperatorFor(menuColumn)}
|
|
225
|
+
<div class="sv-grid-menu-join" role="radiogroup" aria-label="Combine conditions">
|
|
226
|
+
<button type="button" class:is-on={(mf.join ?? "AND") === "AND"}
|
|
227
|
+
onclick={() => patchFilter(colId, { join: "AND" })}>AND</button>
|
|
228
|
+
<button type="button" class:is-on={mf.join === "OR"}
|
|
229
|
+
onclick={() => patchFilter(colId, { join: "OR" })}>OR</button>
|
|
230
|
+
<button type="button" class="sv-grid-menu-join-x" title="Remove second condition"
|
|
231
|
+
aria-label="Remove second condition"
|
|
232
|
+
onclick={() => patchFilter(colId, { operator2: undefined, value2: undefined, valueTo2: undefined, join: undefined })}>×</button>
|
|
233
|
+
</div>
|
|
234
|
+
<select
|
|
235
|
+
class="sv-grid-menu-operator-select"
|
|
236
|
+
aria-label="Second filter condition"
|
|
237
|
+
value={op2}
|
|
238
|
+
onchange={(event) =>
|
|
239
|
+
patchFilter(colId, { operator2: (event.currentTarget as HTMLSelectElement).value as FilterOperator })}
|
|
240
|
+
>
|
|
241
|
+
{#each menuOperatorOptions as option (option.value)}
|
|
242
|
+
<option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
|
|
243
|
+
{/each}
|
|
244
|
+
</select>
|
|
245
|
+
{#if op2 !== "isBlank"}
|
|
246
|
+
<input
|
|
247
|
+
class="sv-grid-menu-condition-value"
|
|
248
|
+
type={menuInputType}
|
|
249
|
+
value={mf.value2 ?? ""}
|
|
250
|
+
placeholder={op2 === "between" ? "From" : "Filter value..."}
|
|
251
|
+
oninput={(event) => patchFilter(colId, { value2: (event.currentTarget as HTMLInputElement).value })}
|
|
252
|
+
/>
|
|
253
|
+
{#if op2 === "between"}
|
|
254
|
+
<input
|
|
255
|
+
class="sv-grid-menu-condition-value"
|
|
256
|
+
type={menuInputType}
|
|
257
|
+
value={mf.valueTo2 ?? ""}
|
|
258
|
+
placeholder="To"
|
|
259
|
+
oninput={(event) => patchFilter(colId, { valueTo2: (event.currentTarget as HTMLInputElement).value })}
|
|
260
|
+
/>
|
|
261
|
+
{/if}
|
|
262
|
+
{/if}
|
|
263
|
+
{:else}
|
|
264
|
+
<button type="button" class="sv-grid-menu-add-cond"
|
|
265
|
+
onclick={() => patchFilter(colId, { operator2: defaultOperatorFor(menuColumn), value2: "", join: "AND" })}>
|
|
266
|
+
+ Add condition
|
|
267
|
+
</button>
|
|
268
|
+
{/if}
|
|
269
|
+
{/if}
|
|
270
|
+
<div class="sv-grid-menu-sep"></div>
|
|
271
|
+
<div class="sv-grid-menu-filter-head">Values</div>
|
|
272
|
+
<input
|
|
273
|
+
class="sv-grid-menu-search"
|
|
274
|
+
placeholder="Search values..."
|
|
275
|
+
bind:value={ctrl.columnMenuSearch}
|
|
276
|
+
/>
|
|
277
|
+
<label class="sv-grid-facet sv-grid-facet-all">
|
|
278
|
+
<input type="checkbox" checked={isAllFacetsChecked(colId)} onchange={() => toggleAllFacets(colId)} />
|
|
279
|
+
<span class="sv-grid-facet-label">(Select all)</span>
|
|
280
|
+
</label>
|
|
281
|
+
<div class="sv-grid-facet-list">
|
|
282
|
+
{#each columnMenuVisibleFacets as value (value)}
|
|
283
|
+
<label class="sv-grid-facet">
|
|
284
|
+
<input type="checkbox" checked={isFacetChecked(colId, value)} onchange={() => toggleFacetValue(colId, value)} />
|
|
285
|
+
<span class="sv-grid-facet-label">{value === "" ? "(Blanks)" : value}</span>
|
|
286
|
+
</label>
|
|
287
|
+
{:else}
|
|
288
|
+
<div class="sv-grid-facet-empty">No values</div>
|
|
289
|
+
{/each}
|
|
290
|
+
</div>
|
|
291
|
+
{#if columnMenuFacetValues.length > columnMenuVisibleFacets.length}
|
|
292
|
+
<div class="sv-grid-facet-note">
|
|
293
|
+
Showing {columnMenuVisibleFacets.length} of {columnMenuFacetValues.length}
|
|
294
|
+
</div>
|
|
295
|
+
{/if}
|
|
296
|
+
<div class="sv-grid-menu-actions">
|
|
297
|
+
<button
|
|
298
|
+
type="button"
|
|
299
|
+
class="sv-grid-menu-btn"
|
|
300
|
+
disabled={!isColumnFiltered(colId)}
|
|
301
|
+
onclick={() => clearColumnFilter(colId)}
|
|
302
|
+
>
|
|
303
|
+
Clear filter
|
|
304
|
+
</button>
|
|
305
|
+
<button type="button" class="sv-grid-menu-btn sv-grid-menu-btn-primary" onclick={closeMenus}>
|
|
306
|
+
Done
|
|
307
|
+
</button>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
{/snippet}
|
|
311
|
+
|
|
172
312
|
{#if columnMenuFor}
|
|
173
313
|
{@const menuColumnId = columnMenuFor}
|
|
174
314
|
{@const menuCol = allColumns.find((c) => c.id === menuColumnId)}
|
|
175
315
|
{@const menuCanSort = menuCol?.getCanSort?.() ?? false}
|
|
316
|
+
{@const menuCanFilter = (menuCol?.columnDef.field != null) && (menuCol?.columnDef.filterable !== false)}
|
|
317
|
+
{@const menuTab = ctrl.columnMenuTab}
|
|
176
318
|
<div
|
|
177
319
|
class="sv-grid-menu sv-grid-column-menu"
|
|
178
320
|
role="menu"
|
|
179
|
-
style={`left: ${columnMenuPos.x}px; top: ${columnMenuPos.y}px;`}
|
|
321
|
+
style={`left: ${columnMenuPos.x}px; top: ${columnMenuPos.y}px; max-height: calc(100vh - ${columnMenuPos.y}px - 12px);`}
|
|
180
322
|
>
|
|
323
|
+
{#if columnMenuTabsEnabled}
|
|
324
|
+
<div class="sv-grid-menu-tabs" role="tablist">
|
|
325
|
+
<button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "general"}
|
|
326
|
+
role="tab" aria-selected={menuTab === "general"}
|
|
327
|
+
onclick={() => (ctrl.columnMenuTab = "general")}>General</button>
|
|
328
|
+
{#if menuCanFilter && showColumnFiltersEffective}
|
|
329
|
+
<button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "filter"}
|
|
330
|
+
role="tab" aria-selected={menuTab === "filter"}
|
|
331
|
+
onclick={() => (ctrl.columnMenuTab = "filter")}>Filter</button>
|
|
332
|
+
{/if}
|
|
333
|
+
<button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "columns"}
|
|
334
|
+
role="tab" aria-selected={menuTab === "columns"}
|
|
335
|
+
onclick={() => (ctrl.columnMenuTab = "columns")}>Columns</button>
|
|
336
|
+
</div>
|
|
337
|
+
{/if}
|
|
338
|
+
{#if columnMenuTabsEnabled && menuTab === "filter" && menuCanFilter && showColumnFiltersEffective}
|
|
339
|
+
{@render filterPanelBody(menuColumnId)}
|
|
340
|
+
{:else if columnMenuTabsEnabled && menuTab === "columns"}
|
|
341
|
+
<div class="sv-grid-menu-filter-head">Visible columns</div>
|
|
342
|
+
<div class="sv-grid-facet-list">
|
|
343
|
+
{#each grid.getAllColumns() as column (column.id)}
|
|
344
|
+
<label class="sv-grid-facet">
|
|
345
|
+
<input
|
|
346
|
+
type="checkbox"
|
|
347
|
+
checked={!ctrl.hiddenColumns[column.id]}
|
|
348
|
+
onchange={(event) => {
|
|
349
|
+
const visible = (event.currentTarget as HTMLInputElement).checked;
|
|
350
|
+
if (visible) {
|
|
351
|
+
const next = { ...ctrl.hiddenColumns };
|
|
352
|
+
delete next[column.id];
|
|
353
|
+
ctrl.hiddenColumns = next;
|
|
354
|
+
} else {
|
|
355
|
+
ctrl.hiddenColumns = { ...ctrl.hiddenColumns, [column.id]: true };
|
|
356
|
+
}
|
|
357
|
+
}}
|
|
358
|
+
/>
|
|
359
|
+
<span class="sv-grid-facet-label"
|
|
360
|
+
>{typeof column.columnDef.header === "string" ? column.columnDef.header : column.id}</span
|
|
361
|
+
>
|
|
362
|
+
</label>
|
|
363
|
+
{/each}
|
|
364
|
+
</div>
|
|
365
|
+
{:else}
|
|
181
366
|
{#if menuCanSort}
|
|
182
367
|
<button
|
|
183
368
|
type="button"
|
|
@@ -290,21 +475,23 @@
|
|
|
290
475
|
</button>
|
|
291
476
|
{/if}
|
|
292
477
|
<div class="sv-grid-menu-sep"></div>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<span class="sv-grid-header-icon sv-grid-menu-item-chevron"
|
|
305
|
-
>{@render icon("chevron-down")}</span
|
|
478
|
+
{#if !columnMenuTabsEnabled}
|
|
479
|
+
<!-- Flat menu: reach the column chooser via a submenu (tabbed mode has
|
|
480
|
+
a Columns tab instead). -->
|
|
481
|
+
<button
|
|
482
|
+
type="button"
|
|
483
|
+
class="sv-grid-menu-item"
|
|
484
|
+
class:is-open={chooseColumnsPos !== null}
|
|
485
|
+
role="menuitem"
|
|
486
|
+
aria-haspopup="menu"
|
|
487
|
+
aria-expanded={chooseColumnsPos !== null}
|
|
488
|
+
onclick={(event) => openChooseColumns(event)}
|
|
306
489
|
>
|
|
307
|
-
|
|
490
|
+
<span class="sv-grid-header-icon">{@render icon("columns")}</span>
|
|
491
|
+
Choose columns
|
|
492
|
+
<span class="sv-grid-header-icon sv-grid-menu-item-chevron">{@render icon("chevron-down")}</span>
|
|
493
|
+
</button>
|
|
494
|
+
{/if}
|
|
308
495
|
<button
|
|
309
496
|
type="button"
|
|
310
497
|
class="sv-grid-menu-item"
|
|
@@ -316,126 +503,17 @@
|
|
|
316
503
|
>
|
|
317
504
|
<span class="sv-grid-header-icon">{@render icon("reset")}</span> Reset columns
|
|
318
505
|
</button>
|
|
506
|
+
{/if}
|
|
319
507
|
</div>
|
|
320
508
|
{/if}
|
|
321
509
|
|
|
322
510
|
{#if filterMenuFor && showColumnFiltersEffective}
|
|
323
|
-
{@const menuColumnId = filterMenuFor}
|
|
324
|
-
{@const menuColumn = allColumns.find((c) => c.id === menuColumnId)}
|
|
325
|
-
{@const menuOperatorOptions = operatorsForColumn(menuColumn)}
|
|
326
|
-
{@const menuActiveOperator =
|
|
327
|
-
filterMenuValues[menuColumnId]?.operator ??
|
|
328
|
-
defaultOperatorFor(menuColumn)}
|
|
329
|
-
{@const menuInputType = getEditorInputType(
|
|
330
|
-
(menuColumn?.columnDef.editorType ?? "text") as CellEditorType,
|
|
331
|
-
)}
|
|
332
511
|
<div
|
|
333
512
|
class="sv-grid-menu sv-grid-filter-menu"
|
|
334
513
|
role="menu"
|
|
335
|
-
style={`left: ${filterMenuPos.x}px; top: ${filterMenuPos.y}px;`}
|
|
514
|
+
style={`left: ${filterMenuPos.x}px; top: ${filterMenuPos.y}px; max-height: calc(100vh - ${filterMenuPos.y}px - 12px);`}
|
|
336
515
|
>
|
|
337
|
-
|
|
338
|
-
<div class="sv-grid-menu-filter-head">
|
|
339
|
-
<span class="sv-grid-header-icon">{@render icon("filter")}</span> Filter
|
|
340
|
-
condition
|
|
341
|
-
</div>
|
|
342
|
-
<select
|
|
343
|
-
class="sv-grid-menu-operator-select"
|
|
344
|
-
aria-label="Filter condition"
|
|
345
|
-
value={menuActiveOperator}
|
|
346
|
-
onchange={(event) =>
|
|
347
|
-
updateFilterOperator(
|
|
348
|
-
menuColumnId,
|
|
349
|
-
(event.currentTarget as HTMLSelectElement)
|
|
350
|
-
.value as FilterOperator,
|
|
351
|
-
)}
|
|
352
|
-
>
|
|
353
|
-
{#each menuOperatorOptions as option (option.value)}
|
|
354
|
-
<option value={option.value}
|
|
355
|
-
>{operatorLabelFor(option, menuColumn)}</option
|
|
356
|
-
>
|
|
357
|
-
{/each}
|
|
358
|
-
</select>
|
|
359
|
-
{#if menuActiveOperator !== "isBlank"}
|
|
360
|
-
<input
|
|
361
|
-
class="sv-grid-menu-condition-value"
|
|
362
|
-
type={menuInputType}
|
|
363
|
-
value={filterMenuValues[menuColumnId]?.value ?? ""}
|
|
364
|
-
placeholder={menuActiveOperator === "between" ? "From" : "Filter value..."}
|
|
365
|
-
oninput={(event) =>
|
|
366
|
-
updateFilterMenuValue(
|
|
367
|
-
menuColumnId,
|
|
368
|
-
(event.currentTarget as HTMLInputElement).value,
|
|
369
|
-
)}
|
|
370
|
-
/>
|
|
371
|
-
{#if menuActiveOperator === "between"}
|
|
372
|
-
<input
|
|
373
|
-
class="sv-grid-menu-condition-value"
|
|
374
|
-
type={menuInputType}
|
|
375
|
-
value={filterMenuValues[menuColumnId]?.valueTo ?? ""}
|
|
376
|
-
placeholder="To"
|
|
377
|
-
oninput={(event) =>
|
|
378
|
-
updateFilterMenuValueTo(
|
|
379
|
-
menuColumnId,
|
|
380
|
-
(event.currentTarget as HTMLInputElement).value,
|
|
381
|
-
)}
|
|
382
|
-
/>
|
|
383
|
-
{/if}
|
|
384
|
-
{/if}
|
|
385
|
-
<div class="sv-grid-menu-sep"></div>
|
|
386
|
-
<div class="sv-grid-menu-filter-head">Values</div>
|
|
387
|
-
<input
|
|
388
|
-
class="sv-grid-menu-search"
|
|
389
|
-
placeholder="Search values..."
|
|
390
|
-
bind:value={ctrl.columnMenuSearch}
|
|
391
|
-
/>
|
|
392
|
-
<label class="sv-grid-facet sv-grid-facet-all">
|
|
393
|
-
<input
|
|
394
|
-
type="checkbox"
|
|
395
|
-
checked={isAllFacetsChecked(menuColumnId)}
|
|
396
|
-
onchange={() => toggleAllFacets(menuColumnId)}
|
|
397
|
-
/>
|
|
398
|
-
<span class="sv-grid-facet-label">(Select all)</span>
|
|
399
|
-
</label>
|
|
400
|
-
<div class="sv-grid-facet-list">
|
|
401
|
-
{#each columnMenuVisibleFacets as value (value)}
|
|
402
|
-
<label class="sv-grid-facet">
|
|
403
|
-
<input
|
|
404
|
-
type="checkbox"
|
|
405
|
-
checked={isFacetChecked(menuColumnId, value)}
|
|
406
|
-
onchange={() => toggleFacetValue(menuColumnId, value)}
|
|
407
|
-
/>
|
|
408
|
-
<span class="sv-grid-facet-label"
|
|
409
|
-
>{value === "" ? "(Blanks)" : value}</span
|
|
410
|
-
>
|
|
411
|
-
</label>
|
|
412
|
-
{:else}
|
|
413
|
-
<div class="sv-grid-facet-empty">No values</div>
|
|
414
|
-
{/each}
|
|
415
|
-
</div>
|
|
416
|
-
{#if columnMenuFacetValues.length > columnMenuVisibleFacets.length}
|
|
417
|
-
<div class="sv-grid-facet-note">
|
|
418
|
-
Showing {columnMenuVisibleFacets.length} of {columnMenuFacetValues.length}
|
|
419
|
-
</div>
|
|
420
|
-
{/if}
|
|
421
|
-
<div class="sv-grid-menu-actions">
|
|
422
|
-
<button
|
|
423
|
-
type="button"
|
|
424
|
-
class="sv-grid-menu-btn"
|
|
425
|
-
disabled={!isColumnFiltered(menuColumnId)}
|
|
426
|
-
onclick={() => clearColumnFilter(menuColumnId)}
|
|
427
|
-
>
|
|
428
|
-
Clear filter
|
|
429
|
-
</button>
|
|
430
|
-
<button
|
|
431
|
-
type="button"
|
|
432
|
-
class="sv-grid-menu-btn sv-grid-menu-btn-primary"
|
|
433
|
-
onclick={closeMenus}
|
|
434
|
-
>
|
|
435
|
-
Done
|
|
436
|
-
</button>
|
|
437
|
-
</div>
|
|
438
|
-
</div>
|
|
516
|
+
{@render filterPanelBody(filterMenuFor)}
|
|
439
517
|
</div>
|
|
440
518
|
{/if}
|
|
441
519
|
|
|
@@ -25,6 +25,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
25
25
|
operator: FilterOperator;
|
|
26
26
|
value: string;
|
|
27
27
|
valueTo?: string;
|
|
28
|
+
operator2?: FilterOperator;
|
|
29
|
+
value2?: string;
|
|
30
|
+
valueTo2?: string;
|
|
31
|
+
join?: "AND" | "OR";
|
|
28
32
|
}>;
|
|
29
33
|
verticalScrollbarEl: HTMLElement | null;
|
|
30
34
|
horizontalScrollbarEl: HTMLElement | null;
|
|
@@ -35,6 +39,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
35
39
|
pendingScrollLeft: number;
|
|
36
40
|
scrollSyncRaf: number | null;
|
|
37
41
|
selectionRange: SelectionRange;
|
|
42
|
+
selectionRanges: SelectionRange[];
|
|
38
43
|
isDraggingSelection: boolean;
|
|
39
44
|
fillDrag: {
|
|
40
45
|
sourceMinRow: number;
|
|
@@ -49,6 +54,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
49
54
|
colIndex: number;
|
|
50
55
|
} | null;
|
|
51
56
|
editingCell: CellEditState;
|
|
57
|
+
fullRowEdit: {
|
|
58
|
+
rowId: string;
|
|
59
|
+
draft: Record<string, unknown>;
|
|
60
|
+
} | null;
|
|
52
61
|
editedCellValues: Record<string, unknown>;
|
|
53
62
|
readonly UNDO_LIMIT: number;
|
|
54
63
|
history: {
|
|
@@ -100,6 +109,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
100
109
|
readonly DATE_OPERATORS: FilterOperator[];
|
|
101
110
|
readonly CHECKBOX_OPERATORS: FilterOperator[];
|
|
102
111
|
columnMenuFor: string | null;
|
|
112
|
+
columnMenuTab: "filter" | "columns" | "general";
|
|
103
113
|
columnMenuPos: MenuPosition;
|
|
104
114
|
columnMenuSearch: string;
|
|
105
115
|
filterMenuFor: string | null;
|
|
@@ -140,6 +150,8 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
140
150
|
internalData: readonly TData[];
|
|
141
151
|
internalColumns: ColumnDef<TFeatures, TData>[];
|
|
142
152
|
hiddenColumns: Record<string, boolean>;
|
|
153
|
+
readonly toggleColumnGroup: (groupId: string) => void;
|
|
154
|
+
readonly isColumnGroupCollapsed: (groupId: string) => boolean;
|
|
143
155
|
readonly externalSortEnabled: boolean;
|
|
144
156
|
readonly externalFilterEnabled: boolean;
|
|
145
157
|
readonly passthroughSortedRowModel: ({ rows }: {
|
|
@@ -165,6 +177,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
165
177
|
* multi-level value tree). They render as empty cells so the
|
|
166
178
|
* layout stays aligned without showing duplicate labels. */
|
|
167
179
|
isPlaceholder: boolean;
|
|
180
|
+
/** Set when this group cell has a collapse toggle. */
|
|
181
|
+
groupId?: string;
|
|
182
|
+
collapsible: boolean;
|
|
183
|
+
collapsed: boolean;
|
|
168
184
|
}[];
|
|
169
185
|
}[];
|
|
170
186
|
readonly pinnedOffsets: {
|
|
@@ -176,6 +192,17 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
176
192
|
colDragId: string | null;
|
|
177
193
|
colDropOnId: string | null;
|
|
178
194
|
colDropSide: "before" | "after" | null;
|
|
195
|
+
rowDragActive: boolean;
|
|
196
|
+
rowDropIndex: number | null;
|
|
197
|
+
rowDropSide: "before" | "after" | null;
|
|
198
|
+
readonly onRowDragStart: (e: DragEvent, rowIndex: number) => void;
|
|
199
|
+
readonly onRowDragOver: (e: DragEvent, rowIndex: number) => void;
|
|
200
|
+
readonly onRowDragLeave: (rowIndex: number) => void;
|
|
201
|
+
readonly onRowDrop: (e: DragEvent, rowIndex: number) => void;
|
|
202
|
+
readonly onRowsContainerDragOver: (e: DragEvent) => void;
|
|
203
|
+
readonly onRowsContainerDrop: (e: DragEvent) => void;
|
|
204
|
+
readonly onRowDragEnd: () => void;
|
|
205
|
+
readonly broadcastAlignedScroll: (left: number) => void;
|
|
179
206
|
readonly getCurrentColumnOrder: () => string[];
|
|
180
207
|
readonly emitColumnOrder: () => void;
|
|
181
208
|
readonly setColumnOrderInternal: (ids: ReadonlyArray<string>) => void;
|
|
@@ -215,6 +242,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
215
242
|
max: number;
|
|
216
243
|
} | null;
|
|
217
244
|
toolPanelOpen: boolean;
|
|
245
|
+
toolPanelTab: "columns" | "filters";
|
|
218
246
|
readonly toolPanelEnabled: boolean;
|
|
219
247
|
readonly toolPanelColumns: Column<TData>[];
|
|
220
248
|
readonly toolPanelHeaderLabel: typeof toolPanelHeaderLabel;
|
|
@@ -324,7 +352,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
324
352
|
readonly startColumnResize: (event: PointerEvent, columnId: string) => void;
|
|
325
353
|
readonly onColumnResizeMove: (event: PointerEvent) => void;
|
|
326
354
|
readonly endColumnResize: () => void;
|
|
327
|
-
readonly setSelection: (rowIndex: number, colIndex: number) => void;
|
|
355
|
+
readonly setSelection: (rowIndex: number, colIndex: number, additive?: boolean) => void;
|
|
328
356
|
readonly extendSelection: (rowIndex: number, colIndex: number) => void;
|
|
329
357
|
readonly isCellInSelectedRange: (rowIndex: number, colIndex: number) => boolean;
|
|
330
358
|
readonly getCellRangeEdges: (rowIndex: number, colIndex: number) => {
|
|
@@ -333,6 +361,12 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
333
361
|
left: boolean;
|
|
334
362
|
right: boolean;
|
|
335
363
|
} | null;
|
|
364
|
+
readonly getSelectionRects: () => {
|
|
365
|
+
minRow: number;
|
|
366
|
+
maxRow: number;
|
|
367
|
+
minCol: number;
|
|
368
|
+
maxCol: number;
|
|
369
|
+
}[];
|
|
336
370
|
readonly fillHandleCell: {
|
|
337
371
|
rowIndex: any;
|
|
338
372
|
colIndex: any;
|
|
@@ -354,9 +388,18 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
354
388
|
readonly onCellClick: (rowIndex: number, colIndex: number) => void;
|
|
355
389
|
readonly emitCellDoubleClick: (rowIndex: number, colIndex: number) => void;
|
|
356
390
|
readonly copySelectionToClipboard: () => void;
|
|
391
|
+
readonly cutSelectionToClipboard: () => Promise<void>;
|
|
392
|
+
readonly pasteFromClipboard: () => Promise<void>;
|
|
393
|
+
readonly onGridPaste: (event: ClipboardEvent) => void;
|
|
357
394
|
readonly clearSelectedCells: () => boolean;
|
|
358
395
|
readonly onCellDoubleClick: (rowIndex: number, colIndex: number) => void;
|
|
359
396
|
readonly startEditingWithChar: (rowIndex: number, colIndex: number, char: string) => boolean;
|
|
397
|
+
readonly startEditing: (rowIndex: number, columnId: string) => boolean;
|
|
398
|
+
readonly stopEditing: (cancel?: boolean) => boolean;
|
|
399
|
+
readonly startFullRowEdit: (rowIndex: number) => boolean;
|
|
400
|
+
readonly setFullRowDraft: (columnId: string, value: unknown) => void;
|
|
401
|
+
readonly commitFullRowEdit: () => void;
|
|
402
|
+
readonly cancelFullRowEdit: () => void;
|
|
360
403
|
readonly saveEditingCell: () => void;
|
|
361
404
|
readonly applyHistoryStep: (step: {
|
|
362
405
|
rowId: string;
|