@svgrid/grid 2.6.21 → 2.6.23
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 +62 -0
- package/README.md +22 -0
- package/dist/GridMenus.svelte +201 -18
- package/dist/SvAutoComplete.svelte +4 -0
- package/dist/SvComboBox.svelte +5 -1
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDropDownList.svelte +4 -0
- package/dist/SvForm.svelte +8 -8
- package/dist/SvGrid.controller.svelte.d.ts +61 -9
- package/dist/SvGrid.controller.svelte.js +363 -123
- package/dist/SvGrid.css +222 -1
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +255 -56
- package/dist/SvGrid.types.d.ts +146 -1
- package/dist/SvGridCellEditor.svelte +99 -17
- package/dist/SvGridSelect.svelte +6 -2
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvListBox.svelte +8 -0
- package/dist/SvMultiSelect.svelte +6 -2
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +4 -0
- package/dist/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- package/dist/SvTextArea.svelte +5 -1
- package/dist/SvTreeSelect.svelte +6 -2
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/builtin-editors.d.ts +2 -2
- package/dist/builtin-editors.js +19 -3
- package/dist/cdn/GridMenus-BzWXQjv3.js +600 -0
- package/dist/cdn/GridMenus-xr_rHx8F.js +601 -0
- package/dist/cdn/SvDateRangeInput-CaOuMs8O.js +198 -0
- package/dist/cdn/SvDateRangeInput-DMLKmGEc.js +199 -0
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +816 -0
- package/dist/cdn/SvDateTimePicker-sonaH0oh.js +812 -0
- package/dist/cdn/SvGridCellEditor-BAPSC7EL.js +550 -0
- package/dist/cdn/SvGridCellEditor-BwzmUWtL.js +549 -0
- package/dist/cdn/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/date-format-BNii4zeD.js +1393 -0
- package/dist/cdn/date-format-BnnHlqGw.js +1395 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-DBel9wRZ.js → src-DGo7IHug.js} +10017 -9488
- package/dist/cdn/{src-BYq-qyrp.js → src-skQN5eqh.js} +7829 -7300
- package/dist/cdn/svgrid.js +14 -10
- package/dist/cdn/svgrid.svelte-external.js +14 -10
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- package/dist/column-groups.js +1 -1
- package/dist/column-resize.d.ts +46 -0
- package/dist/column-resize.js +205 -0
- package/dist/columns.d.ts +0 -3
- package/dist/columns.js +0 -57
- package/dist/core.d.ts +19 -4
- package/dist/core.js +460 -119
- package/dist/editing.js +12 -7
- package/dist/editors/cell-editors.d.ts +11 -0
- package/dist/editors/cell-editors.js +18 -1
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +41 -8
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/sanitize-html.d.ts +37 -0
- package/dist/sanitize-html.js +234 -0
- package/dist/selection-bar-view.svelte.d.ts +25 -0
- package/dist/selection-bar-view.svelte.js +13 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +133 -8
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/package.json +1 -1
- package/src/GridMenus.svelte +201 -18
- package/src/SvAutoComplete.svelte +4 -0
- package/src/SvComboBox.svelte +5 -1
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDropDownList.svelte +4 -0
- package/src/SvForm.svelte +8 -8
- package/src/SvGrid.controller.svelte.ts +404 -133
- package/src/SvGrid.css +222 -1
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +255 -56
- package/src/SvGrid.types.ts +153 -1
- package/src/SvGridCellEditor.svelte +99 -17
- package/src/SvGridSelect.svelte +6 -2
- package/src/SvListBox.svelte +8 -0
- package/src/SvMultiSelect.svelte +6 -2
- package/src/SvNumberInput.svelte +4 -0
- package/src/SvRichCell.svelte +125 -0
- package/src/SvTextArea.svelte +5 -1
- package/src/SvTreeSelect.svelte +6 -2
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-values.ts +30 -0
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/column-groups.ts +1 -1
- package/src/column-resize.test.ts +381 -0
- package/src/column-resize.ts +227 -0
- package/src/columns.test.ts +0 -103
- package/src/columns.ts +0 -58
- package/src/core.aggregate.test.ts +134 -0
- package/src/core.filter.test.ts +156 -0
- package/src/core.grouping.test.ts +146 -0
- package/src/core.row-shape.test.ts +119 -0
- package/src/core.rowmodel-cache.test.ts +121 -0
- package/src/core.sort.test.ts +293 -0
- package/src/core.ts +516 -119
- package/src/date-string-column.test.ts +87 -0
- package/src/editing.test.ts +25 -0
- package/src/editing.ts +12 -9
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editors/cell-editors.ts +27 -1
- package/src/filtering/excel-filters.ts +30 -0
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/grid-messages.ts +34 -0
- package/src/group-display.ts +1 -1
- package/src/index.ts +25 -1
- package/src/list-option.ts +6 -0
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +38 -8
- package/src/move-cells.test.ts +850 -0
- package/src/number-editor-literal.test.ts +84 -0
- package/src/number-editor.grid.test.ts +160 -0
- package/src/resize-props.test.ts +361 -0
- package/src/row-resize.test.ts +31 -0
- package/src/row-resize.ts +21 -3
- package/src/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +157 -0
- package/src/selection.ts +135 -8
- package/src/spreadsheet.ts +1 -1
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +56 -10
- package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.upsell-license.test.ts +117 -0
- package/dist/cdn/GridMenus-BfTAKn84.js +0 -488
- package/dist/cdn/GridMenus-C3bJd7w8.js +0 -489
- package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
- package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
- package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
- package/dist/cdn/SvGridCellEditor-G8IMWIws.js +0 -522
package/src/SvGrid.svelte
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
} from "./index";
|
|
16
16
|
import "./sv-grid-scrollbar";
|
|
17
17
|
import "./SvGrid.css";
|
|
18
|
+
import type { RowResizeOptions } from "./row-resize";
|
|
19
|
+
import type { ColumnResizeOptions } from "./column-resize";
|
|
18
20
|
import {
|
|
19
21
|
RenderSnippetConfig,
|
|
20
22
|
RenderComponentConfig,
|
|
@@ -31,6 +33,9 @@
|
|
|
31
33
|
import {
|
|
32
34
|
cfTextStyle,
|
|
33
35
|
getEditorInputType,
|
|
36
|
+
usesRichDateFilter,
|
|
37
|
+
toIsoDateLocal,
|
|
38
|
+
fromIsoDateLocal,
|
|
34
39
|
getOptionLabel,
|
|
35
40
|
getOptionColor,
|
|
36
41
|
colorfulChipStyle,
|
|
@@ -46,6 +51,7 @@
|
|
|
46
51
|
// SvGridChartPanel (charting) and SvGridBoard (Kanban) are heavy, prop-gated
|
|
47
52
|
// views - lazy-loaded below so they stay out of the base <SvGrid> bundle.
|
|
48
53
|
import { getSchedulerView } from "./scheduler-view.svelte";
|
|
54
|
+
import { getSelectionBarView } from "./selection-bar-view.svelte";
|
|
49
55
|
import { getBoardView } from "./board-view.svelte";
|
|
50
56
|
import { getChartView } from "./chart-view.svelte";
|
|
51
57
|
let props: Props<TFeatures, TData> = $props();
|
|
@@ -97,6 +103,37 @@
|
|
|
97
103
|
import("./SvGridChartView.svelte").then((m) => (ChartView = m.default));
|
|
98
104
|
});
|
|
99
105
|
let MenusOverlay = $state<typeof import("./GridMenus.svelte").default | null>(null);
|
|
106
|
+
// The grid's own date picker, for date FILTER inputs. Lazy for the same
|
|
107
|
+
// reason the cell editor's copy is: most grids never show a filter row, and
|
|
108
|
+
// most that do have no date column.
|
|
109
|
+
let FilterDatePicker = $state<typeof import("./SvDateTimePicker.svelte").default | null>(null);
|
|
110
|
+
$effect(() => {
|
|
111
|
+
if (!showFilterRowEffective && !ctrl.filterMenuFor) return;
|
|
112
|
+
if (FilterDatePicker) return;
|
|
113
|
+
const wantsPicker = ctrl.allColumns.some((c: any) =>
|
|
114
|
+
usesRichDateFilter(c.columnDef?.editorType),
|
|
115
|
+
);
|
|
116
|
+
if (wantsPicker)
|
|
117
|
+
import("./SvDateTimePicker.svelte").then((m) => (FilterDatePicker = m.default));
|
|
118
|
+
});
|
|
119
|
+
// `between` on a date column filters with a RANGE control rather than two
|
|
120
|
+
// single-date fields: one field, one popover, both ends picked in a single
|
|
121
|
+
// gesture - and it fits a narrow date column, which two pickers plus their
|
|
122
|
+
// buttons do not. Loaded only once a date column is actually set to Between,
|
|
123
|
+
// so a grid that never uses the operator never pays for the chunk.
|
|
124
|
+
let FilterRangePicker = $state<typeof import("./SvDateRangeInput.svelte").default | null>(null);
|
|
125
|
+
$effect(() => {
|
|
126
|
+
if (!showFilterRowEffective) return;
|
|
127
|
+
if (FilterRangePicker) return;
|
|
128
|
+
const wantsRange = ctrl.allColumns.some(
|
|
129
|
+
(c: any) =>
|
|
130
|
+
usesRichDateFilter(c.columnDef?.editorType) &&
|
|
131
|
+
(ctrl.filterMenuValues[c.id]?.operator ??
|
|
132
|
+
ctrl.defaultOperatorFor(c)) === "between",
|
|
133
|
+
);
|
|
134
|
+
if (wantsRange)
|
|
135
|
+
import("./SvDateRangeInput.svelte").then((m) => (FilterRangePicker = m.default));
|
|
136
|
+
});
|
|
100
137
|
$effect(() => {
|
|
101
138
|
if (ctrl.chartingEnabled && ctrl.chartPanelOpen && !ChartPanelView)
|
|
102
139
|
import("./SvGridChartPanel.svelte").then((m) => (ChartPanelView = m.default));
|
|
@@ -167,7 +204,9 @@
|
|
|
167
204
|
const hideTooltip = $derived(ctrl.hideTooltip);
|
|
168
205
|
const findHits = $derived(ctrl.findHits);
|
|
169
206
|
const headerHeight = $derived(ctrl.headerHeight);
|
|
170
|
-
|
|
207
|
+
// Column resizing has always been unconditional; `columnResize={false}` is
|
|
208
|
+
// the opt-out, so the default has to stay on when the prop is absent.
|
|
209
|
+
const columnResizeEnabled = $derived(opt.columnResize === true);
|
|
171
210
|
const selectionColumnWidth = $derived(ctrl.selectionColumnWidth);
|
|
172
211
|
const rowNumberColumnWidth = $derived(ctrl.rowNumberColumnWidth);
|
|
173
212
|
const showRowNumbersEffective = $derived(ctrl.showRowNumbersEffective);
|
|
@@ -264,6 +303,23 @@
|
|
|
264
303
|
// Row-grouping display modes: group state lives in synthetic columns instead
|
|
265
304
|
// of a full-width banner row.
|
|
266
305
|
const groupColumnMode = $derived(ctrl.groupColumnMode);
|
|
306
|
+
/**
|
|
307
|
+
* True when a cell's content needs none of the wrapper snippets.
|
|
308
|
+
*
|
|
309
|
+
* A cell body normally renders through `cellBodyWithFormat` ->
|
|
310
|
+
* `cellBodyFormatted` -> `cellBody`, and with no grouping, no tree data and
|
|
311
|
+
* no conditional formats the first two are pure pass-throughs: three snippet
|
|
312
|
+
* renders and two `{#if}` blocks per cell to reach content that one render
|
|
313
|
+
* produces. Measured by ablation, that chain is 4.2 ms of a 17.4 ms mount at
|
|
314
|
+
* 28 rows x 9 columns - about a quarter of the whole mount, and the largest
|
|
315
|
+
* single per-cell cost.
|
|
316
|
+
*
|
|
317
|
+
* Computed once per render rather than per cell, so the fast path costs one
|
|
318
|
+
* boolean read at each of the ~250 cells instead of two snippet frames.
|
|
319
|
+
*/
|
|
320
|
+
const plainCellBody = $derived(
|
|
321
|
+
!groupColumnMode && !treeData && !hasConditionalFormats,
|
|
322
|
+
);
|
|
267
323
|
const autoGroupCell = $derived(ctrl.autoGroupCell);
|
|
268
324
|
const isAutoGroupColumn = (id: string): boolean =>
|
|
269
325
|
id === "__autoGroup" || id.startsWith("__group_");
|
|
@@ -280,7 +336,60 @@
|
|
|
280
336
|
// The fixed row height, matching what the virtualized path takes from the virtualizer.
|
|
281
337
|
// The non-virtualized (`virtualization={false}`) body must apply this too, else its rows
|
|
282
338
|
// fall back to content height and look shorter than a virtualized grid's.
|
|
339
|
+
/**
|
|
340
|
+
* `rowResize` and `columnResize` are both opt-in and both default to OFF, so
|
|
341
|
+
* neither module is imported statically - a grid that leaves them alone must
|
|
342
|
+
* not carry their bytes. This wraps an action so its module is fetched on
|
|
343
|
+
* first enable and never otherwise. The handles appear a microtask after the
|
|
344
|
+
* first paint, which is invisible for a drag affordance.
|
|
345
|
+
*
|
|
346
|
+
* One helper for both: two hand-rolled copies of this bookkeeping would cost
|
|
347
|
+
* more base bundle than the deferral saves.
|
|
348
|
+
*/
|
|
349
|
+
type LazyHandle<O> = { update(o: O): void; destroy(): void };
|
|
350
|
+
function lazyAction<O extends { disabled?: boolean }>(
|
|
351
|
+
load: () => Promise<(node: HTMLElement, opts: O) => LazyHandle<O>>,
|
|
352
|
+
) {
|
|
353
|
+
return (node: HTMLElement, opts: O): LazyHandle<O> => {
|
|
354
|
+
let handle: LazyHandle<O> | null = null;
|
|
355
|
+
let current = opts;
|
|
356
|
+
let destroyed = false;
|
|
357
|
+
const ensure = () => {
|
|
358
|
+
if (handle || destroyed || current.disabled) return;
|
|
359
|
+
void load().then((make) => {
|
|
360
|
+
// The prop can be switched back off, or the grid unmounted, while
|
|
361
|
+
// the chunk is still in flight.
|
|
362
|
+
if (destroyed || current.disabled || handle) return;
|
|
363
|
+
handle = make(node, current);
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
ensure();
|
|
367
|
+
return {
|
|
368
|
+
update(next: O) {
|
|
369
|
+
current = next;
|
|
370
|
+
if (handle) handle.update(next);
|
|
371
|
+
else ensure();
|
|
372
|
+
},
|
|
373
|
+
destroy() {
|
|
374
|
+
destroyed = true;
|
|
375
|
+
handle?.destroy();
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const lazyRowResize = lazyAction<RowResizeOptions>(() =>
|
|
382
|
+
import("./row-resize").then((m) => m.rowResize),
|
|
383
|
+
);
|
|
384
|
+
const lazyColumnResize = lazyAction<ColumnResizeOptions>(() =>
|
|
385
|
+
import("./column-resize").then((m) => m.columnResize),
|
|
386
|
+
);
|
|
387
|
+
|
|
283
388
|
const rowSizePx = (i: number): number => {
|
|
389
|
+
// A height the user dragged wins over the declared one, so the row stays
|
|
390
|
+
// where they put it across re-renders and virtualization recycling.
|
|
391
|
+
const dragged = ctrl.rowResizeHeightPx(i);
|
|
392
|
+
if (dragged != null) return dragged;
|
|
284
393
|
const rh = opt.rowHeight;
|
|
285
394
|
return typeof rh === "function" ? rh(i) : (rh ?? 30);
|
|
286
395
|
};
|
|
@@ -358,26 +467,10 @@
|
|
|
358
467
|
return "sv-grid-cell-flash";
|
|
359
468
|
}
|
|
360
469
|
|
|
361
|
-
// Keyboard-accessible column resize (#79): focus a resize handle and use the
|
|
362
|
-
// arrow keys (Shift = fine 1px step). Complements the pointer-drag resize.
|
|
363
|
-
function resizeColumnByKeyboard(e: KeyboardEvent, columnId: string) {
|
|
364
|
-
let delta = 0;
|
|
365
|
-
const step = e.shiftKey ? 1 : 10;
|
|
366
|
-
if (e.key === "ArrowLeft") delta = -step;
|
|
367
|
-
else if (e.key === "ArrowRight") delta = step;
|
|
368
|
-
else return;
|
|
369
|
-
e.preventDefault();
|
|
370
|
-
const current = ctrl.getColumnWidth(columnId);
|
|
371
|
-
ctrl.columnWidths = {
|
|
372
|
-
...ctrl.columnWidths,
|
|
373
|
-
[columnId]: Math.max(40, current + delta),
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
|
|
377
470
|
// ---- Full-row editing -------------------------------------------------
|
|
378
471
|
const fullRowEdit = $derived(ctrl.fullRowEdit);
|
|
379
472
|
// Commit the whole row when the user clicks away from its editors (Excel /
|
|
380
|
-
//
|
|
473
|
+
// spreadsheet feel). Clicking within any full-row editor keeps editing.
|
|
381
474
|
// Stays here rather than in SvGridCellEditor: that component is instantiated
|
|
382
475
|
// once PER editable cell during a full-row edit, so a document listener there
|
|
383
476
|
// would be attached N times and commit N times.
|
|
@@ -429,7 +522,6 @@
|
|
|
429
522
|
const setActiveCell = $derived(ctrl.setActiveCell);
|
|
430
523
|
const scrollActiveCellIntoView = $derived(ctrl.scrollActiveCellIntoView);
|
|
431
524
|
const getColumnWidth = $derived(ctrl.getColumnWidth);
|
|
432
|
-
const startColumnResize = $derived(ctrl.startColumnResize);
|
|
433
525
|
const getCellRangeEdges = $derived(ctrl.getCellRangeEdges);
|
|
434
526
|
const fillHandleCell = $derived(ctrl.fillHandleCell);
|
|
435
527
|
const isInFillPreview = $derived(ctrl.isInFillPreview);
|
|
@@ -624,6 +716,24 @@
|
|
|
624
716
|
onpointermove={onWindowPointerMove}
|
|
625
717
|
/>
|
|
626
718
|
|
|
719
|
+
<!--
|
|
720
|
+
The licensing line every Enterprise upsell note carries. One snippet rather
|
|
721
|
+
than four copies: four gates worded four ways is how a product ends up
|
|
722
|
+
explaining its own licensing inconsistently.
|
|
723
|
+
|
|
724
|
+
Enterprise is SOFT-gated - the feature runs without a key and the grid shows
|
|
725
|
+
an unlicensed watermark - so this says exactly that rather than implying the
|
|
726
|
+
feature is inert without one.
|
|
727
|
+
-->
|
|
728
|
+
{#snippet enterpriseLicenseNote()}
|
|
729
|
+
<p class="sv-grid-upsell-license">
|
|
730
|
+
{messages.upsellLicense}
|
|
731
|
+
<a href="https://svgrid.com/pricing/" target="_blank" rel="noopener noreferrer">
|
|
732
|
+
{messages.upsellLicenseLink}
|
|
733
|
+
</a>
|
|
734
|
+
</p>
|
|
735
|
+
{/snippet}
|
|
736
|
+
|
|
627
737
|
{#if opt.loading && !opt.loadingOverlay && !hasMeasured}
|
|
628
738
|
<!-- Full-screen loading state only on the *initial* load, before the grid
|
|
629
739
|
has ever rendered. Once measured, a `loading` flip (from a server-mode
|
|
@@ -680,6 +790,7 @@
|
|
|
680
790
|
<code>@svgrid/enterprise</code> and call <code>enableBoardView()</code>
|
|
681
791
|
to render it.
|
|
682
792
|
</p>
|
|
793
|
+
{@render enterpriseLicenseNote()}
|
|
683
794
|
</div>
|
|
684
795
|
{/if}
|
|
685
796
|
</div>
|
|
@@ -727,6 +838,7 @@
|
|
|
727
838
|
<code>@svgrid/enterprise</code> and call <code>enableSchedulerView()</code>
|
|
728
839
|
to render it.
|
|
729
840
|
</p>
|
|
841
|
+
{@render enterpriseLicenseNote()}
|
|
730
842
|
</div>
|
|
731
843
|
{/if}
|
|
732
844
|
</div>
|
|
@@ -809,6 +921,7 @@
|
|
|
809
921
|
<div class="sv-grid-scheduler-upsell sv-grid-pivot-upsell" role="note">
|
|
810
922
|
<strong>{messages.pivotUpsellTitle}</strong>
|
|
811
923
|
<p>{messages.pivotUpsellBody}</p>
|
|
924
|
+
{@render enterpriseLicenseNote()}
|
|
812
925
|
</div>
|
|
813
926
|
{/if}
|
|
814
927
|
</div>
|
|
@@ -866,6 +979,13 @@
|
|
|
866
979
|
<path d="M8 5l9 7-9 7" />
|
|
867
980
|
{:else if name === "op-lessThan"}
|
|
868
981
|
<path d="M16 5l-9 7 9 7" />
|
|
982
|
+
{:else if name === "op-between"}
|
|
983
|
+
<!-- Two bounds with the span between them. The catalogue has always
|
|
984
|
+
named this icon; the snippet had no case for it, so the Between
|
|
985
|
+
row in the operator menu drew an empty <svg>. -->
|
|
986
|
+
<path d="M5 5v14" />
|
|
987
|
+
<path d="M19 5v14" />
|
|
988
|
+
<path d="M9 12h6" />
|
|
869
989
|
{:else if name === "op-isBlank"}
|
|
870
990
|
<circle cx="12" cy="12" r="8" />
|
|
871
991
|
<path d="M6.5 6.5l11 11" />
|
|
@@ -1294,6 +1414,32 @@
|
|
|
1294
1414
|
class="sv-grid-root"
|
|
1295
1415
|
class:sv-grid-root-fill={opt.containerHeight === "100%"}
|
|
1296
1416
|
style={chartDockReserveStyle}
|
|
1417
|
+
data-move-grab={ctrl.moveGrabHover || ctrl.moveDrag ? "true" : undefined}
|
|
1418
|
+
data-selbar={
|
|
1419
|
+
ctrl.selectionBarOn && ctrl.selectionBarTarget.ids.length > 0
|
|
1420
|
+
? (getSelectionBarView() ? ctrl.selectionBarPosition : `upsell-${ctrl.selectionBarPosition}`)
|
|
1421
|
+
: undefined
|
|
1422
|
+
}
|
|
1423
|
+
use:lazyRowResize={{
|
|
1424
|
+
disabled: !ctrl.rowResizeOn,
|
|
1425
|
+
// No gutter column on most grids, so fall back to the first body cell -
|
|
1426
|
+
// otherwise `rowResize` would be a prop that silently does nothing.
|
|
1427
|
+
anchor: "row",
|
|
1428
|
+
onResize: (index, height) => ctrl.setRowResizeHeight(index, height),
|
|
1429
|
+
}}
|
|
1430
|
+
use:lazyColumnResize={{
|
|
1431
|
+
disabled: !columnResizeEnabled,
|
|
1432
|
+
getWidth: (columnId) => ctrl.getColumnWidth(columnId),
|
|
1433
|
+
onResize: (columnId, width) => {
|
|
1434
|
+
ctrl.columnWidths = { ...ctrl.columnWidths, [columnId]: width };
|
|
1435
|
+
},
|
|
1436
|
+
label: (columnId) => {
|
|
1437
|
+
const col = ctrl.findColumnById(columnId);
|
|
1438
|
+
return col ? toolPanelHeaderLabel(col) : columnId;
|
|
1439
|
+
},
|
|
1440
|
+
canResize: (columnId) => ctrl.columnResizable(columnId),
|
|
1441
|
+
onAutosize: (columnId) => ctrl.autosizeColumn(columnId),
|
|
1442
|
+
}}
|
|
1297
1443
|
>
|
|
1298
1444
|
{#if showGlobalFilterEffective}
|
|
1299
1445
|
<label class="sv-grid-global-filter">
|
|
@@ -1747,32 +1893,6 @@
|
|
|
1747
1893
|
}}
|
|
1748
1894
|
/>
|
|
1749
1895
|
{/if}
|
|
1750
|
-
<div
|
|
1751
|
-
class="sv-grid-resize-handle"
|
|
1752
|
-
class:is-resizing={resizingColumnId ===
|
|
1753
|
-
header.column.id}
|
|
1754
|
-
role="separator"
|
|
1755
|
-
aria-orientation="vertical"
|
|
1756
|
-
aria-label={`Resize ${toolPanelHeaderLabel(header.column)}`}
|
|
1757
|
-
tabindex="0"
|
|
1758
|
-
{...(() => {
|
|
1759
|
-
// A focusable separator is a widget, so ARIA requires
|
|
1760
|
-
// aria-valuenow. The value it exposes is the column
|
|
1761
|
-
// width the arrow keys change; 40 is the floor
|
|
1762
|
-
// enforced in resizeColumnByKeyboard.
|
|
1763
|
-
const w = Math.round(ctrl.getColumnWidth(header.column.id));
|
|
1764
|
-
return {
|
|
1765
|
-
"aria-valuenow": w,
|
|
1766
|
-
"aria-valuemin": 40,
|
|
1767
|
-
"aria-valuetext": `${w} pixels`,
|
|
1768
|
-
};
|
|
1769
|
-
})()}
|
|
1770
|
-
onpointerdown={(event) =>
|
|
1771
|
-
startColumnResize(event, header.column.id)}
|
|
1772
|
-
onkeydown={(event) =>
|
|
1773
|
-
resizeColumnByKeyboard(event, header.column.id)}
|
|
1774
|
-
ondblclick={(event) => event.stopPropagation()}
|
|
1775
|
-
></div>
|
|
1776
1896
|
{/if}
|
|
1777
1897
|
</th>
|
|
1778
1898
|
{/if}
|
|
@@ -1900,6 +2020,57 @@
|
|
|
1900
2020
|
}}
|
|
1901
2021
|
/>
|
|
1902
2022
|
</div>
|
|
2023
|
+
{:else if activeOperator !== "isBlank" && activeOperator !== "isNotBlank" && activeOperator !== "regex" && FilterDatePicker && usesRichDateFilter(rendered.column.columnDef.editorType)}
|
|
2024
|
+
<!-- Same picker the cell editor uses, so a date
|
|
2025
|
+
column looks the same wherever you touch it.
|
|
2026
|
+
`regex` keeps the text box - a pattern is not
|
|
2027
|
+
a date. -->
|
|
2028
|
+
{#if activeOperator === "between"}
|
|
2029
|
+
<!-- `between` needs BOTH bounds - `condActive`
|
|
2030
|
+
ignores the filter until `valueTo` is set too -
|
|
2031
|
+
so this is one range field rather than two
|
|
2032
|
+
single-date pickers: both ends come from a
|
|
2033
|
+
single gesture in one popover, and it still
|
|
2034
|
+
fits a narrow date column. It writes the same
|
|
2035
|
+
pair of stores the plain text inputs use. -->
|
|
2036
|
+
{#if FilterRangePicker}
|
|
2037
|
+
{@const RangePicker = FilterRangePicker}
|
|
2038
|
+
{@const from = fromIsoDateLocal(
|
|
2039
|
+
filterRowValues[rendered.column.id],
|
|
2040
|
+
)}
|
|
2041
|
+
{@const to = fromIsoDateLocal(
|
|
2042
|
+
filterMenuValues[rendered.column.id]?.valueTo,
|
|
2043
|
+
)}
|
|
2044
|
+
<RangePicker
|
|
2045
|
+
value={from && to ? [from, to] : null}
|
|
2046
|
+
formatString="yyyy-MM-dd"
|
|
2047
|
+
block
|
|
2048
|
+
placeholder="Range…"
|
|
2049
|
+
onChange={(range) => {
|
|
2050
|
+
updateFilterRow(
|
|
2051
|
+
rendered.column.id,
|
|
2052
|
+
range ? toIsoDateLocal(range[0]) : "",
|
|
2053
|
+
);
|
|
2054
|
+
updateFilterMenuValueTo(
|
|
2055
|
+
rendered.column.id,
|
|
2056
|
+
range ? toIsoDateLocal(range[1]) : "",
|
|
2057
|
+
);
|
|
2058
|
+
}}
|
|
2059
|
+
/>
|
|
2060
|
+
{/if}
|
|
2061
|
+
{:else}
|
|
2062
|
+
{@const FilterPicker = FilterDatePicker}
|
|
2063
|
+
<FilterPicker
|
|
2064
|
+
value={(filterRowValues[rendered.column.id] ?? "") || null}
|
|
2065
|
+
formatString="yyyy-MM-dd"
|
|
2066
|
+
dropDownDisplayMode="calendar"
|
|
2067
|
+
block
|
|
2068
|
+
nullable
|
|
2069
|
+
placeholder="Filter…"
|
|
2070
|
+
onChange={(d) =>
|
|
2071
|
+
updateFilterRow(rendered.column.id, toIsoDateLocal(d))}
|
|
2072
|
+
/>
|
|
2073
|
+
{/if}
|
|
1903
2074
|
{:else if activeOperator !== "isBlank" && activeOperator !== "isNotBlank"}
|
|
1904
2075
|
{@const frType = getEditorInputType(
|
|
1905
2076
|
rendered.column.columnDef.editorType ?? "text",
|
|
@@ -2177,12 +2348,11 @@
|
|
|
2177
2348
|
rendered.column.columnDef.cellFlash,
|
|
2178
2349
|
),
|
|
2179
2350
|
}}
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
})}
|
|
2351
|
+
role="gridcell"
|
|
2352
|
+
id={getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex)}
|
|
2353
|
+
aria-colindex={colIndex + 1}
|
|
2354
|
+
aria-rowindex={rowIndex + 1}
|
|
2355
|
+
aria-selected={isRowSelected(row.id)}
|
|
2186
2356
|
>
|
|
2187
2357
|
{#if inRowEdit || isEditing}
|
|
2188
2358
|
<!-- The editing cell stays empty until the lazy
|
|
@@ -2194,11 +2364,15 @@
|
|
|
2194
2364
|
<CellEditor {ctrl} column={rendered.column} {row} fullRow={inRowEdit} />
|
|
2195
2365
|
{/if}
|
|
2196
2366
|
{:else}
|
|
2197
|
-
{
|
|
2198
|
-
row,
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2367
|
+
{#if plainCellBody}
|
|
2368
|
+
{@render cellBody(row, rendered.column, cellValue)}
|
|
2369
|
+
{:else}
|
|
2370
|
+
{@render cellBodyWithFormat(
|
|
2371
|
+
row,
|
|
2372
|
+
rendered.column,
|
|
2373
|
+
cellValue,
|
|
2374
|
+
)}
|
|
2375
|
+
{/if}
|
|
2202
2376
|
{/if}
|
|
2203
2377
|
{#if !isEditing && fillHandleCell && fillHandleCell.rowIndex === rowIndex && fillHandleCell.colIndex === colIndex}
|
|
2204
2378
|
<!-- Excel-style fill handle: drag down/right to
|
|
@@ -2948,6 +3122,31 @@
|
|
|
2948
3122
|
{#if ctrl.chartingEnabled && ctrl.chartPanelOpen && ChartPanelView}
|
|
2949
3123
|
<ChartPanelView {ctrl} />
|
|
2950
3124
|
{/if}
|
|
3125
|
+
|
|
3126
|
+
<!-- Bulk-action bar. INSIDE the root, unlike GridMenus: it is positioned
|
|
3127
|
+
`absolute` against it, so moving it outside would anchor it to the
|
|
3128
|
+
page. The renderer is Enterprise and arrives through the
|
|
3129
|
+
selection-bar-view seam; without it, a short note in its place. -->
|
|
3130
|
+
{#if ctrl.selectionBarOn && ctrl.selectionBarTarget.ids.length > 0}
|
|
3131
|
+
{#if getSelectionBarView()}
|
|
3132
|
+
{@const SelectionBarView = getSelectionBarView()}
|
|
3133
|
+
<SelectionBarView {ctrl} />
|
|
3134
|
+
{:else}
|
|
3135
|
+
<div
|
|
3136
|
+
class="sv-grid-scheduler-upsell sv-grid-selection-bar-upsell"
|
|
3137
|
+
data-position={ctrl.selectionBarPosition}
|
|
3138
|
+
role="note"
|
|
3139
|
+
>
|
|
3140
|
+
<strong>Bulk-action bar</strong>
|
|
3141
|
+
<p>
|
|
3142
|
+
The selection bar is an Enterprise feature. Install
|
|
3143
|
+
<code>@svgrid/enterprise</code> and call <code>enableSelectionBar()</code>
|
|
3144
|
+
to render it.
|
|
3145
|
+
</p>
|
|
3146
|
+
{@render enterpriseLicenseNote()}
|
|
3147
|
+
</div>
|
|
3148
|
+
{/if}
|
|
3149
|
+
{/if}
|
|
2951
3150
|
</div>
|
|
2952
3151
|
<!-- /.sv-grid-root -->
|
|
2953
3152
|
|
package/src/SvGrid.types.ts
CHANGED
|
@@ -217,6 +217,78 @@ export type ContextMenuItem<TData extends RowData = RowData> =
|
|
|
217
217
|
action: (target: ContextMenuTarget<TData>) => void;
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
+
/** What a {@link SelectionBarAction} is handed when it runs. */
|
|
221
|
+
export type SelectionBarTarget<TData extends RowData = RowData> = {
|
|
222
|
+
/** The selected rows, in the grid's current display order. */
|
|
223
|
+
rows: TData[];
|
|
224
|
+
/** Their row ids, same order as `rows`. */
|
|
225
|
+
ids: string[];
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* One button on the selection bar. Same shape as {@link ContextMenuItem}'s
|
|
230
|
+
* object form - key, label, optional hidden/disabled predicates, and an action -
|
|
231
|
+
* except everything here is handed the whole SELECTION rather than one cell.
|
|
232
|
+
*/
|
|
233
|
+
export type SelectionBarAction<TData extends RowData = RowData> = {
|
|
234
|
+
key: string;
|
|
235
|
+
label: string;
|
|
236
|
+
/**
|
|
237
|
+
* Inline SVG path data (the `d` of a single 24x24 path) drawn to the left of
|
|
238
|
+
* the label. A string rather than a component so an action array stays plain
|
|
239
|
+
* data that can live in a `.ts` module next to the columns.
|
|
240
|
+
*/
|
|
241
|
+
icon?: string;
|
|
242
|
+
/** Render in the destructive style (red). Does not add a confirmation. */
|
|
243
|
+
danger?: boolean;
|
|
244
|
+
/** Hide the button entirely for this selection. */
|
|
245
|
+
hidden?: (target: SelectionBarTarget<TData>) => boolean;
|
|
246
|
+
/** Render greyed-out and non-clickable for this selection. */
|
|
247
|
+
disabled?: (target: SelectionBarTarget<TData>) => boolean;
|
|
248
|
+
/** Invoked on click. The bar stays open; clear the selection yourself if the
|
|
249
|
+
* action should dismiss it. */
|
|
250
|
+
action: (target: SelectionBarTarget<TData>) => void;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* A built-in bar button, named by key - the same convention
|
|
255
|
+
* {@link ContextMenuItem} uses for its string form.
|
|
256
|
+
*
|
|
257
|
+
* - `'selectAll'` selects every row in the current view.
|
|
258
|
+
* - `'editFields'` opens the bulk-edit dialog: pick a field, set one value,
|
|
259
|
+
* apply it to every selected row in a single undo step.
|
|
260
|
+
* - `'separator'` draws a divider.
|
|
261
|
+
*/
|
|
262
|
+
export type SelectionBarBuiltin = "selectAll" | "editFields" | "separator";
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Full form of the {@link SvGridProps.selectionBar} prop, for when the array
|
|
266
|
+
* shorthand is not enough.
|
|
267
|
+
*/
|
|
268
|
+
export type SelectionBarConfig<TData extends RowData = RowData> = {
|
|
269
|
+
/**
|
|
270
|
+
* The buttons, left to right. Strings are {@link SelectionBarBuiltin} keys,
|
|
271
|
+
* objects are your own actions - so a Jira-shaped bar is
|
|
272
|
+
* `['selectAll', 'editFields', 'separator', myAction]`.
|
|
273
|
+
*/
|
|
274
|
+
actions?: ReadonlyArray<SelectionBarAction<TData> | SelectionBarBuiltin>;
|
|
275
|
+
/**
|
|
276
|
+
* Which edge of the grid the bar floats against. Default `'bottom'`, where
|
|
277
|
+
* the eye is already going after ticking a checkbox and where it covers the
|
|
278
|
+
* rows a user is least likely to be reading.
|
|
279
|
+
*/
|
|
280
|
+
position?: "bottom" | "top";
|
|
281
|
+
/**
|
|
282
|
+
* How many actions stay on the bar before the rest collapse into an overflow
|
|
283
|
+
* menu. Default 6, which fits a full issue-tracker set (select all, edit,
|
|
284
|
+
* status, watch, delete) without collapsing. A bar wide enough to need
|
|
285
|
+
* horizontal scrolling is a bar whose last button nobody finds.
|
|
286
|
+
*/
|
|
287
|
+
maxVisible?: number;
|
|
288
|
+
/** Hide the trailing clear (x) button. Default false. */
|
|
289
|
+
hideClear?: boolean;
|
|
290
|
+
};
|
|
291
|
+
|
|
220
292
|
/** A single Kanban lane (board column). {@link BoardConfig}. */
|
|
221
293
|
export type BoardLane = {
|
|
222
294
|
/** Lane id - equals the `groupBy` field value of the cards it holds. */
|
|
@@ -1003,6 +1075,29 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1003
1075
|
* browser menu shows instead).
|
|
1004
1076
|
*/
|
|
1005
1077
|
contextMenu?: boolean | ReadonlyArray<ContextMenuItem<TData>>;
|
|
1078
|
+
/**
|
|
1079
|
+
* Floating bar that appears over the grid while rows are selected, showing
|
|
1080
|
+
* the count and the actions that apply to the whole selection - the pattern
|
|
1081
|
+
* an issue tracker uses for bulk edit.
|
|
1082
|
+
*
|
|
1083
|
+
* `true` gives the count and a Clear button. An array is the shorthand for
|
|
1084
|
+
* `{ actions }`. Pass {@link SelectionBarConfig} to also set `position`
|
|
1085
|
+
* (`'bottom'` default, or `'top'`), `maxVisible` and `hideClear`. Omitted /
|
|
1086
|
+
* `false` is off.
|
|
1087
|
+
*
|
|
1088
|
+
* Default **off**. This is chrome that floats over your content, not a
|
|
1089
|
+
* gesture: it overlaps a row, and what belongs on it is specific to the app.
|
|
1090
|
+
*
|
|
1091
|
+
* **The prop and its types are free; the bar itself is an Enterprise
|
|
1092
|
+
* feature.** Install `@svgrid/enterprise` and call `enableSelectionBar()`
|
|
1093
|
+
* (or `installEnterprise`, which does it for you) to render it - without
|
|
1094
|
+
* that the grid shows a short upsell note in its place, the same way the
|
|
1095
|
+
* scheduler and board views do.
|
|
1096
|
+
*/
|
|
1097
|
+
selectionBar?:
|
|
1098
|
+
| boolean
|
|
1099
|
+
| ReadonlyArray<SelectionBarAction<TData> | SelectionBarBuiltin>
|
|
1100
|
+
| SelectionBarConfig<TData>;
|
|
1006
1101
|
/**
|
|
1007
1102
|
* The feature set built with `tableFeatures({ ... })`. Optional - the
|
|
1008
1103
|
* `sortable` / `filterable` / `groupable` shortcuts below inject the
|
|
@@ -1329,6 +1424,28 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1329
1424
|
* supplying per-row heights).
|
|
1330
1425
|
*/
|
|
1331
1426
|
autoRowHeight?: boolean;
|
|
1427
|
+
/**
|
|
1428
|
+
* Let the user drag a row's bottom edge to change its height. **Off by
|
|
1429
|
+
* default**, because a resizable row needs somewhere to grab and most grids
|
|
1430
|
+
* do not want a drag target on every row.
|
|
1431
|
+
*
|
|
1432
|
+
* The grid remembers the heights itself, so this works as a bare boolean -
|
|
1433
|
+
* no `rowHeight` function required. Drag, or focus the grip and use Up/Down
|
|
1434
|
+
* (Shift for 1px steps).
|
|
1435
|
+
*
|
|
1436
|
+
* Turning this on also turns on the **row header column** ({@link
|
|
1437
|
+
* showRowNumbers}), because that is where the grip lives and where a
|
|
1438
|
+
* spreadsheet puts it - a drag target on the edge of a data cell works but
|
|
1439
|
+
* reads as an accident. An explicit `showRowNumbers={false}` still wins; the
|
|
1440
|
+
* grip then falls back to the row's first cell, and a column of your own
|
|
1441
|
+
* carrying `cellClass: 'sv-row-gutter'` is used ahead of either.
|
|
1442
|
+
*
|
|
1443
|
+
* Ignored under `autoRowHeight`, where the content decides the height and a
|
|
1444
|
+
* manual one would immediately be overwritten. For full control of the
|
|
1445
|
+
* heights - persisting them, sharing them between grids - pass a
|
|
1446
|
+
* function-valued {@link rowHeight} and use the `rowResize` action directly.
|
|
1447
|
+
*/
|
|
1448
|
+
rowResize?: boolean;
|
|
1332
1449
|
/**
|
|
1333
1450
|
* Height (px) of a single column-header level row. With multi-level
|
|
1334
1451
|
* (grouped) headers the total header height is `levels * headerHeight`,
|
|
@@ -1387,6 +1504,24 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1387
1504
|
* win once they happen.
|
|
1388
1505
|
*/
|
|
1389
1506
|
fitColumns?: boolean;
|
|
1507
|
+
/**
|
|
1508
|
+
* Let the user drag a column's edge to change its width. **Off by default**,
|
|
1509
|
+
* so `width` means the width you asked for until you say otherwise.
|
|
1510
|
+
*
|
|
1511
|
+
* Turn it on for grids the reader is meant to arrange - a spreadsheet, a
|
|
1512
|
+
* dense report, anything with columns whose content varies in length. Leave
|
|
1513
|
+
* it off for layouts you control, and for columns that must keep their size:
|
|
1514
|
+
* a row-number gutter, a checkbox column, an icon column.
|
|
1515
|
+
*
|
|
1516
|
+
* It is grid-wide rather than per-column: there is no `resizable: false` on a
|
|
1517
|
+
* single column definition. `api.autosizeColumn()`, `fitColumns` and
|
|
1518
|
+
* programmatic width changes work either way - this only governs the drag
|
|
1519
|
+
* handle.
|
|
1520
|
+
*
|
|
1521
|
+
* The handles come from the `columnResize` action, loaded on demand: a grid
|
|
1522
|
+
* that leaves this off never fetches that code.
|
|
1523
|
+
*/
|
|
1524
|
+
columnResize?: boolean;
|
|
1390
1525
|
/**
|
|
1391
1526
|
* Make the grid usable on narrow screens. When the grid's own width drops
|
|
1392
1527
|
* below the breakpoint (default `640`px), pinned columns are un-pinned so the
|
|
@@ -1415,6 +1550,23 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1415
1550
|
* is the shortcut alias and wins over it.
|
|
1416
1551
|
*/
|
|
1417
1552
|
enableCellSelection?: boolean;
|
|
1553
|
+
/**
|
|
1554
|
+
* Excel-style drag-and-drop of a selected cell range: grab the range's
|
|
1555
|
+
* border and drag it somewhere else to MOVE the values there, or hold
|
|
1556
|
+
* Ctrl (Cmd on macOS) to COPY them instead. The modifier is read at drop
|
|
1557
|
+
* time, so it can be pressed or released mid-drag.
|
|
1558
|
+
*
|
|
1559
|
+
* On by default whenever cell selection is on, matching the fill handle.
|
|
1560
|
+
* Set `false` to keep a border pointerdown starting a fresh selection, which
|
|
1561
|
+
* is what it did before this existed.
|
|
1562
|
+
*
|
|
1563
|
+
* The grab strip is the outer 4px of the range border, so a pointerdown
|
|
1564
|
+
* anywhere further inside still starts a new selection. A drop is refused
|
|
1565
|
+
* outright - nothing changes - when the destination would fall outside the
|
|
1566
|
+
* grid, or when any source or destination cell is read-only. It does not
|
|
1567
|
+
* move the part that fits and silently drop the rest.
|
|
1568
|
+
*/
|
|
1569
|
+
moveCells?: boolean;
|
|
1418
1570
|
/**
|
|
1419
1571
|
* Highlight the row under the pointer. Default **false** - the hover tint can
|
|
1420
1572
|
* compete with the cell selection / fill marquee. Set `true` to opt in; when
|
|
@@ -1498,7 +1650,7 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1498
1650
|
charting?: boolean | ChartingConfig<TData>;
|
|
1499
1651
|
/**
|
|
1500
1652
|
* Render the header column menu (⋮) as a tabbed popover - **General**,
|
|
1501
|
-
* **Filter**, and **Columns** tabs (the
|
|
1653
|
+
* **Filter**, and **Columns** tabs (the tabbed layout). Defaults to `false`,
|
|
1502
1654
|
* which keeps the flat menu (actions list + "Choose columns" submenu).
|
|
1503
1655
|
*/
|
|
1504
1656
|
columnMenuTabs?: boolean;
|