@jielga/tmdatagrid 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +947 -64
- package/dist/index.js +2474 -1423
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/skills/columns/SKILL.md +1 -1
- package/skills/features/SKILL.md +61 -11
- package/skills/getting-started/SKILL.md +1 -1
- package/skills/options/SKILL.md +12 -2
- package/skills/server-side/SKILL.md +1 -1
- package/src/tmdatagrid/TMDataGridContext.ts +26 -0
- package/src/tmdatagrid/components/TMDataGrid.module.css +10 -0
- package/src/tmdatagrid/components/TMDataGrid.tsx +23 -1
- package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +5 -5
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.module.css +33 -0
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +160 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +111 -33
- package/src/tmdatagrid/components/TMDataGridFilterPills.tsx +5 -4
- package/src/tmdatagrid/components/TMDataGridFooter.tsx +11 -19
- package/src/tmdatagrid/components/TMDataGridGroupColumn.tsx +23 -11
- package/src/tmdatagrid/components/TMDataGridHeaderCell.module.css +15 -3
- package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +181 -52
- package/src/tmdatagrid/components/TMDataGridSearch.tsx +122 -0
- package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +23 -13
- package/src/tmdatagrid/components/TMDataGridTable.module.css +127 -6
- package/src/tmdatagrid/components/TMDataGridTable.tsx +1003 -30
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +30 -7
- package/src/tmdatagrid/components/icons.ts +1 -0
- package/src/tmdatagrid/components/sticky.module.css +77 -16
- package/src/tmdatagrid/core/autosize.ts +125 -0
- package/src/tmdatagrid/core/capabilities.ts +41 -0
- package/src/tmdatagrid/core/cellExport.ts +321 -0
- package/src/tmdatagrid/core/cellNavigation.ts +103 -0
- package/src/tmdatagrid/core/cellRange.ts +118 -0
- package/src/tmdatagrid/core/columnOptions.ts +123 -0
- package/src/tmdatagrid/core/columnUtils.ts +18 -0
- package/src/tmdatagrid/core/expanding.ts +93 -0
- package/src/tmdatagrid/core/filterOperators.ts +176 -15
- package/src/tmdatagrid/core/labels.ts +211 -0
- package/src/tmdatagrid/core/labelsSv.ts +108 -0
- package/src/tmdatagrid/core/summary.ts +35 -0
- package/src/tmdatagrid/index.ts +70 -0
- package/src/tmdatagrid/useTMDataGrid.tsx +328 -12
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionIcon, Popover, Text, Tooltip } from "@mantine/core";
|
|
1
|
+
import { ActionIcon, Loader, Popover, Text, Tooltip } from "@mantine/core";
|
|
2
2
|
import { useSelector } from "@tanstack/react-store";
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import classes from "./TMDataGridToolbar.module.css";
|
|
@@ -19,6 +19,29 @@ export function TMDataGridToolbarSpacer() {
|
|
|
19
19
|
return <div className={classes.toolbarSpacer} />;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* A small spinner shown while `meta.loading` is true, and nothing otherwise.
|
|
24
|
+
*
|
|
25
|
+
* The body only shows its loading state while the grid is *empty* — a
|
|
26
|
+
* server-driven grid refetching with rows on screen keeps showing them, which
|
|
27
|
+
* is right, but leaves nothing saying a fetch is running. This is that signal,
|
|
28
|
+
* and the consumer decides where it sits by placing it in the toolbar:
|
|
29
|
+
*
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <TMDataGrid.Toolbar>
|
|
32
|
+
* <TMDataGrid.SummaryCount />
|
|
33
|
+
* <TMDataGrid.Spacer />
|
|
34
|
+
* <TMDataGrid.LoadingIndicator />
|
|
35
|
+
* <TMDataGrid.FilterButton />
|
|
36
|
+
* </TMDataGrid.Toolbar>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function TMDataGridLoadingIndicator() {
|
|
40
|
+
const { table, labels } = useTMDataGridContext();
|
|
41
|
+
if (table.options.meta?.loading !== true) return null;
|
|
42
|
+
return <Loader size="xs" aria-label={labels.loading} />;
|
|
43
|
+
}
|
|
44
|
+
|
|
22
45
|
/**
|
|
23
46
|
* Visible rows over total rows. On a server-driven grid, set
|
|
24
47
|
* `meta.totalRowCount` for the denominator — the client never sees every row.
|
|
@@ -52,7 +75,7 @@ export function TMDataGridSummaryCount({ children }: { children?: ReactNode }) {
|
|
|
52
75
|
* Renders nothing when no column can be hidden (`enableHiding: false`).
|
|
53
76
|
*/
|
|
54
77
|
export function TMDataGridColumnsButton() {
|
|
55
|
-
const { table, ui, features, controlSize } = useTMDataGridContext();
|
|
78
|
+
const { table, ui, features, labels, controlSize } = useTMDataGridContext();
|
|
56
79
|
const opened = useSelector(ui, (state) => state.columnsPanelOpen);
|
|
57
80
|
|
|
58
81
|
if (!getGridCapabilities(table, features).canHideAny) return null;
|
|
@@ -68,12 +91,12 @@ export function TMDataGridColumnsButton() {
|
|
|
68
91
|
trapFocus
|
|
69
92
|
>
|
|
70
93
|
<Popover.Target>
|
|
71
|
-
<Tooltip label=
|
|
94
|
+
<Tooltip label={labels.manageColumns} openDelay={400}>
|
|
72
95
|
<ActionIcon
|
|
73
96
|
variant="subtle"
|
|
74
97
|
color="gray"
|
|
75
98
|
size={controlSize}
|
|
76
|
-
aria-label=
|
|
99
|
+
aria-label={labels.manageColumns}
|
|
77
100
|
onClick={ui.actions.toggleColumnsPanel}
|
|
78
101
|
>
|
|
79
102
|
<BurgerIcon size={18} stroke={1.6} />
|
|
@@ -93,7 +116,7 @@ export function TMDataGridColumnsButton() {
|
|
|
93
116
|
*/
|
|
94
117
|
export function TMDataGridFilterButton() {
|
|
95
118
|
const api = useTMDataGridContext();
|
|
96
|
-
const { table, ui, features, controlSize } = api;
|
|
119
|
+
const { table, ui, features, labels, controlSize } = api;
|
|
97
120
|
const opened = useSelector(ui, (state) => state.filterPanelOpen);
|
|
98
121
|
const columnFilters = useSelector(
|
|
99
122
|
table.store,
|
|
@@ -106,12 +129,12 @@ export function TMDataGridFilterButton() {
|
|
|
106
129
|
if (!getGridCapabilities(table, features).canFilterAny) return null;
|
|
107
130
|
|
|
108
131
|
return (
|
|
109
|
-
<Tooltip label=
|
|
132
|
+
<Tooltip label={labels.filters} openDelay={400}>
|
|
110
133
|
<ActionIcon
|
|
111
134
|
variant={opened || activeCount > 0 ? "light" : "subtle"}
|
|
112
135
|
color={activeCount > 0 ? undefined : "gray"}
|
|
113
136
|
size={controlSize}
|
|
114
|
-
aria-label=
|
|
137
|
+
aria-label={labels.filters}
|
|
115
138
|
// Marks this as the panel's own toggle, so the panel's click-away
|
|
116
139
|
// handler leaves it alone and the button stays a toggle.
|
|
117
140
|
data-dg-filter-toggle
|
|
@@ -1,36 +1,38 @@
|
|
|
1
1
|
/* Boundary of a pinned column group. Shared, because the edge is cast by both
|
|
2
|
-
the header cell and every body cell in the same column — the two have to
|
|
3
|
-
|
|
2
|
+
the header cell and every body cell in the same column — the two have to draw
|
|
3
|
+
an identical band or the seam shows at the header.
|
|
4
4
|
|
|
5
5
|
A box-shadow is drawn per cell, and its blur fades in at each cell's own top
|
|
6
6
|
and bottom edge — stacked down a column that reads as banding at every row
|
|
7
|
-
seam. Instead: a
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
.stickyLeft {
|
|
12
|
-
border-right: 1px solid var(--mantine-color-default-border);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.stickyRight {
|
|
16
|
-
border-left: 1px solid var(--mantine-color-default-border);
|
|
17
|
-
}
|
|
7
|
+
seam. Instead: a gradient pseudo element pinned to `top: 0; bottom: 0` so
|
|
8
|
+
consecutive cells butt together into one unbroken band. Mantine sets
|
|
9
|
+
`color-scheme` on the root, so light-dark() resolves without a second
|
|
10
|
+
selector.
|
|
18
11
|
|
|
12
|
+
No border. A 1px line the colour of the grid's own borders drew a hard rule
|
|
13
|
+
between the pinned lanes and the data, so the pinned columns read as a
|
|
14
|
+
separate table rather than as part of this one — and at the system lanes,
|
|
15
|
+
which are only a control wide, it framed them like a sidebar. */
|
|
19
16
|
.stickyLeft::after,
|
|
20
17
|
.stickyRight::after {
|
|
21
18
|
content: "";
|
|
22
19
|
position: absolute;
|
|
23
20
|
top: 0;
|
|
24
21
|
bottom: 0;
|
|
25
|
-
width:
|
|
22
|
+
width: 8px;
|
|
26
23
|
pointer-events: none;
|
|
27
24
|
}
|
|
28
25
|
|
|
26
|
+
/* Three stops rather than two. A straight linear ramp to `transparent` fades
|
|
27
|
+
fastest at the start, so its own outer edge stays visible as a faint line —
|
|
28
|
+
the sharpness a hard border was blamed for. Front-loading the falloff puts
|
|
29
|
+
the density against the pinned cell and lets the tail reach zero unnoticed. */
|
|
29
30
|
.stickyLeft::after {
|
|
30
31
|
left: 100%;
|
|
31
32
|
background: linear-gradient(
|
|
32
33
|
to right,
|
|
33
|
-
light-dark(rgba(0, 0, 0, 0.
|
|
34
|
+
light-dark(rgba(0, 0, 0, 0.07), rgba(0, 0, 0, 0.26)),
|
|
35
|
+
light-dark(rgba(0, 0, 0, 0.025), rgba(0, 0, 0, 0.09)) 45%,
|
|
34
36
|
transparent
|
|
35
37
|
);
|
|
36
38
|
}
|
|
@@ -39,7 +41,66 @@
|
|
|
39
41
|
right: 100%;
|
|
40
42
|
background: linear-gradient(
|
|
41
43
|
to left,
|
|
42
|
-
light-dark(rgba(0, 0, 0, 0.
|
|
44
|
+
light-dark(rgba(0, 0, 0, 0.07), rgba(0, 0, 0, 0.26)),
|
|
45
|
+
light-dark(rgba(0, 0, 0, 0.025), rgba(0, 0, 0, 0.09)) 45%,
|
|
43
46
|
transparent
|
|
44
47
|
);
|
|
45
48
|
}
|
|
49
|
+
|
|
50
|
+
/* The band says "there is more table under this lane". So it should only be
|
|
51
|
+
there while that is true: nothing is hidden at either end of the scroll, and
|
|
52
|
+
nothing at all is hidden by a grid narrow enough not to scroll.
|
|
53
|
+
|
|
54
|
+
Driven by the scroll position itself rather than by a listener writing a
|
|
55
|
+
class. A scroll-driven animation is evaluated on the compositor, so the band
|
|
56
|
+
tracks the scroll frame for frame without a single React render — and the
|
|
57
|
+
body re-renders on every scroll frame already, from virtualization.
|
|
58
|
+
|
|
59
|
+
`scroll(nearest inline)` resolves to `.scrollContainer`, the one horizontal
|
|
60
|
+
scroller above these cells. An inactive timeline — which is what a container
|
|
61
|
+
with nothing to scroll gives — leaves the base style in place, so `opacity:
|
|
62
|
+
0` inside this block is also the answer for a grid that does not scroll. */
|
|
63
|
+
@supports (animation-timeline: scroll()) {
|
|
64
|
+
.stickyLeft::after,
|
|
65
|
+
.stickyRight::after {
|
|
66
|
+
/* Distance over which the band arrives. Short enough to read as an edge
|
|
67
|
+
appearing under the lane rather than as a fade. */
|
|
68
|
+
--dg-sticky-edge-range: 20px;
|
|
69
|
+
opacity: 0;
|
|
70
|
+
animation-timeline: scroll(nearest inline);
|
|
71
|
+
animation-timing-function: linear;
|
|
72
|
+
animation-fill-mode: both;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.stickyLeft::after {
|
|
76
|
+
animation-name: stickyEdgeIn;
|
|
77
|
+
animation-range: 0 var(--dg-sticky-edge-range);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* The mirror: on at rest, gone as the last column arrives. The range is
|
|
81
|
+
measured from the end, and `both` holds the first value everywhere before
|
|
82
|
+
it — so the band stays fully on until the scroller is within 20px of home.
|
|
83
|
+
*/
|
|
84
|
+
.stickyRight::after {
|
|
85
|
+
animation-name: stickyEdgeOut;
|
|
86
|
+
animation-range: calc(100% - var(--dg-sticky-edge-range)) 100%;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@keyframes stickyEdgeIn {
|
|
91
|
+
from {
|
|
92
|
+
opacity: 0;
|
|
93
|
+
}
|
|
94
|
+
to {
|
|
95
|
+
opacity: 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@keyframes stickyEdgeOut {
|
|
100
|
+
from {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
to {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { RowData } from "@tanstack/react-table";
|
|
2
|
+
import type { TMDataGridRowData } from "../TMDataGridContext";
|
|
3
|
+
import type { TMDataGridTable } from "../useTMDataGrid";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Room the header keeps for its hover-revealed actions (sort arrow, menu) and
|
|
7
|
+
* the resize divider, which measure zero while idle. Without it a column sized
|
|
8
|
+
* exactly to its header text puts the actions on top of the title.
|
|
9
|
+
*/
|
|
10
|
+
const HEADER_ACTIONS_ALLOWANCE = 30;
|
|
11
|
+
|
|
12
|
+
/** Breathing room so the widest value does not touch the next divider. */
|
|
13
|
+
const CONTENT_ALLOWANCE = 2;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* What the content inside one cell actually spans, in px.
|
|
17
|
+
*
|
|
18
|
+
* A Range over the children rather than the span's `scrollWidth`, because the
|
|
19
|
+
* two disagree in opposite directions: a left-aligned cell stretches its
|
|
20
|
+
* content span to fill the cell (`flex: 1 1 auto`), so `scrollWidth` reports
|
|
21
|
+
* the stretched box and autosize could never *shrink* a column — while for
|
|
22
|
+
* clipped content both see past the `overflow: hidden`. The Range measures
|
|
23
|
+
* the laid-out text and elements themselves, whatever box they sit in.
|
|
24
|
+
*
|
|
25
|
+
* `scrollWidth` stays as the fallback for environments whose Ranges do not
|
|
26
|
+
* measure (jsdom reports 0 there).
|
|
27
|
+
*/
|
|
28
|
+
function contentSpanWidth(content: HTMLElement): number {
|
|
29
|
+
const range = document.createRange();
|
|
30
|
+
range.selectNodeContents(content);
|
|
31
|
+
// jsdom's Range has no getBoundingClientRect at all, hence the guard
|
|
32
|
+
// rather than a zero-check alone.
|
|
33
|
+
const width =
|
|
34
|
+
typeof range.getBoundingClientRect === "function"
|
|
35
|
+
? range.getBoundingClientRect().width
|
|
36
|
+
: 0;
|
|
37
|
+
return width > 0 ? width : content.scrollWidth;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Widest rendered content of one column, in px, measured from the DOM.
|
|
42
|
+
*
|
|
43
|
+
* Mounted cells only: under virtualization the unmounted rows do not exist to
|
|
44
|
+
* be measured, so this reads the visible window plus overscan — the same
|
|
45
|
+
* trade AG Grid's autosize makes by default.
|
|
46
|
+
*/
|
|
47
|
+
export function measureColumnContentWidth({
|
|
48
|
+
container,
|
|
49
|
+
columnId,
|
|
50
|
+
}: {
|
|
51
|
+
/** The grid's scroll container — anything enclosing the column's cells. */
|
|
52
|
+
container: HTMLElement;
|
|
53
|
+
columnId: string;
|
|
54
|
+
}): number {
|
|
55
|
+
let widest = 0;
|
|
56
|
+
|
|
57
|
+
const cells = container.querySelectorAll<HTMLElement>(
|
|
58
|
+
`[role="cell"][data-column-id="${CSS.escape(columnId)}"],` +
|
|
59
|
+
`[role="gridcell"][data-column-id="${CSS.escape(columnId)}"]`,
|
|
60
|
+
);
|
|
61
|
+
for (const cell of cells) {
|
|
62
|
+
const content = cell.firstElementChild;
|
|
63
|
+
if (!(content instanceof HTMLElement)) continue;
|
|
64
|
+
const styles = getComputedStyle(cell);
|
|
65
|
+
const padding =
|
|
66
|
+
(Number.parseFloat(styles.paddingLeft) || 0) +
|
|
67
|
+
(Number.parseFloat(styles.paddingRight) || 0);
|
|
68
|
+
widest = Math.max(
|
|
69
|
+
widest,
|
|
70
|
+
contentSpanWidth(content) + padding + CONTENT_ALLOWANCE,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const title = container.querySelector<HTMLElement>(
|
|
75
|
+
`[data-testid="dg-header-${CSS.escape(columnId)}"] [data-dg-header-title]`,
|
|
76
|
+
);
|
|
77
|
+
if (title) {
|
|
78
|
+
const header = title.closest<HTMLElement>('[role="columnheader"]');
|
|
79
|
+
const styles = header ? getComputedStyle(header) : null;
|
|
80
|
+
const padding = styles
|
|
81
|
+
? (Number.parseFloat(styles.paddingLeft) || 0) +
|
|
82
|
+
(Number.parseFloat(styles.paddingRight) || 0)
|
|
83
|
+
: 0;
|
|
84
|
+
widest = Math.max(
|
|
85
|
+
widest,
|
|
86
|
+
contentSpanWidth(title) + padding + HEADER_ACTIONS_ALLOWANCE,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return widest;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Sizes a column to its widest mounted content — what double-clicking the
|
|
95
|
+
* resize divider does, exported so a menu item or consumer code can trigger
|
|
96
|
+
* the same thing.
|
|
97
|
+
*
|
|
98
|
+
* The width lands in `columnSizing`, so it persists with the other widths and
|
|
99
|
+
* a later resize drag takes over from it. Clamped to the column's
|
|
100
|
+
* `minSize`/`maxSize`; a no-op (returning `false`) for a column that cannot
|
|
101
|
+
* be resized or has nothing mounted to measure.
|
|
102
|
+
*/
|
|
103
|
+
export function autosizeColumn<TData extends RowData>({
|
|
104
|
+
table,
|
|
105
|
+
columnId,
|
|
106
|
+
container,
|
|
107
|
+
}: {
|
|
108
|
+
table: TMDataGridTable<TData>;
|
|
109
|
+
columnId: string;
|
|
110
|
+
container: HTMLElement;
|
|
111
|
+
}): boolean {
|
|
112
|
+
const erased = table as unknown as TMDataGridTable<TMDataGridRowData>;
|
|
113
|
+
const column = erased.getColumn(columnId);
|
|
114
|
+
if (!column || !column.getCanResize()) return false;
|
|
115
|
+
|
|
116
|
+
const measured = measureColumnContentWidth({ container, columnId });
|
|
117
|
+
if (measured <= 0) return false;
|
|
118
|
+
|
|
119
|
+
const minSize = column.columnDef.minSize ?? 80;
|
|
120
|
+
const maxSize = column.columnDef.maxSize ?? Number.POSITIVE_INFINITY;
|
|
121
|
+
const width = Math.round(Math.min(Math.max(measured, minSize), maxSize));
|
|
122
|
+
|
|
123
|
+
erased.setColumnSizing((previous) => ({ ...previous, [columnId]: width }));
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
@@ -38,9 +38,27 @@ export type TMDataGridSelectionMode =
|
|
|
38
38
|
/** No selection at all — a row click only highlights. Master-detail. */
|
|
39
39
|
| "highlight";
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* How cells are selected — see `cellSelection` on {@link UseTMDataGridOptions}.
|
|
43
|
+
*
|
|
44
|
+
* A mode rather than a pair of booleans, for the reason
|
|
45
|
+
* {@link TMDataGridSelectionMode} is one: "cells can be selected" and "more
|
|
46
|
+
* than one at a time" are not independent, and a range that cannot be anchored
|
|
47
|
+
* anywhere is not a state worth being able to write down.
|
|
48
|
+
*/
|
|
49
|
+
export type TMDataGridCellSelectionMode =
|
|
50
|
+
/** No cell cursor at all. The body's tab stop stays on the row. */
|
|
51
|
+
| "none"
|
|
52
|
+
/** One cell takes the focus and the arrow keys move it. */
|
|
53
|
+
| "single"
|
|
54
|
+
/** As `"single"`, plus a rectangle: drag, Shift+click, Shift+arrows, Ctrl+C. */
|
|
55
|
+
| "range";
|
|
56
|
+
|
|
41
57
|
export type TMDataGridFeatureFlags = {
|
|
42
58
|
sorting: boolean;
|
|
43
59
|
filtering: boolean;
|
|
60
|
+
/** Whether the quick search may filter — `enableGlobalFilter`. */
|
|
61
|
+
globalFiltering: boolean;
|
|
44
62
|
hiding: boolean;
|
|
45
63
|
pinning: boolean;
|
|
46
64
|
resizing: boolean;
|
|
@@ -70,6 +88,17 @@ export type TMDataGridFeatureFlags = {
|
|
|
70
88
|
* what lets it coexist with a checkbox multi-selection.
|
|
71
89
|
*/
|
|
72
90
|
highlightRow: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Whether cells can be selected at all. Off unless `cellSelection` asks for
|
|
93
|
+
* it — it takes the body's tab stop off the row and puts it on a cell, and
|
|
94
|
+
* reports the grid to assistive technology as a `grid` rather than a `table`,
|
|
95
|
+
* which is a different promise about what the keyboard does.
|
|
96
|
+
*/
|
|
97
|
+
cellSelection: boolean;
|
|
98
|
+
/** Defaults to `"none"`. */
|
|
99
|
+
cellSelectionMode: TMDataGridCellSelectionMode;
|
|
100
|
+
/** Whether a rectangle of cells can be selected. Only under `"range"`. */
|
|
101
|
+
cellRangeSelection: boolean;
|
|
73
102
|
/**
|
|
74
103
|
* The one default-off flag: pagination must be asked for, either with the
|
|
75
104
|
* grid's `enablePagination` or implicitly by declaring `manualPagination`.
|
|
@@ -95,6 +124,7 @@ export function readFeatureFlags<TData extends RowData>(
|
|
|
95
124
|
UseTMDataGridOptions<TData>,
|
|
96
125
|
| "enableSorting"
|
|
97
126
|
| "enableColumnFilters"
|
|
127
|
+
| "enableGlobalFilter"
|
|
98
128
|
| "enableHiding"
|
|
99
129
|
| "enableColumnPinning"
|
|
100
130
|
| "enableColumnResizing"
|
|
@@ -103,12 +133,14 @@ export function readFeatureFlags<TData extends RowData>(
|
|
|
103
133
|
| "enableMultiRowSelection"
|
|
104
134
|
| "selectionMode"
|
|
105
135
|
| "showSelectedBackground"
|
|
136
|
+
| "cellSelection"
|
|
106
137
|
| "enablePagination"
|
|
107
138
|
| "manualPagination"
|
|
108
139
|
| "enableGrouping"
|
|
109
140
|
>,
|
|
110
141
|
): TMDataGridFeatureFlags {
|
|
111
142
|
const selectionMode = options.selectionMode ?? "checkbox";
|
|
143
|
+
const cellSelectionMode = options.cellSelection ?? "none";
|
|
112
144
|
// `"highlight"` is the master-detail mode: no selection to speak of, so the
|
|
113
145
|
// checkbox column and the click-to-select behaviour both fall away. Otherwise
|
|
114
146
|
// `enableRowSelection` still has the final say, including its predicate form.
|
|
@@ -118,6 +150,7 @@ export function readFeatureFlags<TData extends RowData>(
|
|
|
118
150
|
return {
|
|
119
151
|
sorting: options.enableSorting !== false,
|
|
120
152
|
filtering: options.enableColumnFilters !== false,
|
|
153
|
+
globalFiltering: options.enableGlobalFilter !== false,
|
|
121
154
|
hiding: options.enableHiding !== false,
|
|
122
155
|
pinning: options.enableColumnPinning !== false,
|
|
123
156
|
resizing: options.enableColumnResizing !== false,
|
|
@@ -135,6 +168,9 @@ export function readFeatureFlags<TData extends RowData>(
|
|
|
135
168
|
options.showSelectedBackground ?? selectionMode === "row",
|
|
136
169
|
highlightRow:
|
|
137
170
|
selectionMode === "checkboxAndHighlight" || selectionMode === "highlight",
|
|
171
|
+
cellSelection: cellSelectionMode !== "none",
|
|
172
|
+
cellSelectionMode,
|
|
173
|
+
cellRangeSelection: cellSelectionMode === "range",
|
|
138
174
|
pagination:
|
|
139
175
|
options.enablePagination === true || options.manualPagination === true,
|
|
140
176
|
grouping: options.enableGrouping ?? options.manualPagination !== true,
|
|
@@ -192,6 +228,8 @@ export type TMDataGridCapabilities = {
|
|
|
192
228
|
canGroupAny: boolean;
|
|
193
229
|
canSelectRows: boolean;
|
|
194
230
|
canPaginate: boolean;
|
|
231
|
+
/** At least one leaf column takes part in the global quick search. */
|
|
232
|
+
canSearch: boolean;
|
|
195
233
|
};
|
|
196
234
|
|
|
197
235
|
export function getGridCapabilities(
|
|
@@ -211,5 +249,8 @@ export function getGridCapabilities(
|
|
|
211
249
|
canGroupAny: any((c) => c.canGroup),
|
|
212
250
|
canSelectRows: features.rowSelection,
|
|
213
251
|
canPaginate: features.pagination,
|
|
252
|
+
canSearch:
|
|
253
|
+
features.globalFiltering &&
|
|
254
|
+
columns.some((column) => column.getCanGlobalFilter()),
|
|
214
255
|
};
|
|
215
256
|
}
|