@jielga/tmdatagrid 2.0.0-beta.9 → 2.0.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/README.md +5 -212
- package/dist/index.d.ts +1281 -768
- package/dist/index.js +4607 -3250
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/adding-rows.md +132 -0
- package/docs/anatomy.md +119 -0
- package/docs/card-view.md +108 -0
- package/docs/cell-selection.md +194 -0
- package/docs/column-layout.md +182 -0
- package/docs/column-menu.md +66 -0
- package/docs/columns.md +268 -0
- package/docs/components.md +311 -0
- package/docs/draft-store.md +242 -0
- package/docs/editing.md +303 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +319 -0
- package/docs/filtering.md +362 -0
- package/docs/getting-started.md +123 -0
- package/docs/grouping.md +165 -0
- package/docs/loading-and-empty.md +92 -0
- package/docs/localization.md +79 -0
- package/docs/menu.md +143 -0
- package/docs/migrating-to-2.md +163 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +114 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +179 -0
- package/docs/quick-search.md +84 -0
- package/docs/row-details.md +115 -0
- package/docs/row-interaction.md +149 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +136 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +112 -0
- package/docs/server-query.md +246 -0
- package/docs/server-side.md +206 -0
- package/docs/sorting.md +101 -0
- package/docs/styling.md +126 -0
- package/docs/summary-row.md +76 -0
- package/docs/testing.md +744 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +22 -46
- package/skills/appearance/SKILL.md +72 -19
- package/skills/cell-selection/SKILL.md +46 -47
- package/skills/columns/SKILL.md +90 -34
- package/skills/data/SKILL.md +86 -16
- package/skills/editing/SKILL.md +67 -40
- package/skills/editing/references/common-mistakes.md +77 -69
- package/skills/editing/references/editing-api.md +22 -19
- package/skills/editing/references/editors-and-validation.md +24 -17
- package/skills/filtering/SKILL.md +148 -40
- package/skills/getting-started/SKILL.md +17 -15
- package/skills/grouping/SKILL.md +31 -16
- package/skills/options/SKILL.md +7 -7
- package/skills/rows/SKILL.md +22 -18
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +150 -32
- package/skills/testing-components/SKILL.md +230 -0
- package/skills/testing-editing/SKILL.md +240 -0
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
- package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +5 -51
- package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
- package/src/components/TMDataGridExportPicker.module.css +77 -0
- package/src/components/TMDataGridExportPicker.tsx +234 -0
- package/src/components/TMDataGridFilterPanel.module.css +54 -0
- package/src/components/TMDataGridFilterPanel.tsx +348 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +15 -8
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +357 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
- package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -0
- package/src/components/TMDataGridToolbar.tsx +181 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/editorShared.ts +16 -2
- package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
- package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
- package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
- package/src/components/filters/controlLayout.ts +32 -0
- package/src/components/filters/filterControlFor.ts +65 -0
- package/src/components/generatedColumns.tsx +187 -0
- package/src/{tmdatagrid/components → components}/icons.ts +1 -0
- package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
- package/src/components/useHideableColumns.ts +52 -0
- package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
- package/src/{tmdatagrid/core → core}/columnOptions.ts +60 -8
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
- package/src/core/export.ts +704 -0
- package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
- package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
- package/src/core/filterSurface.ts +99 -0
- package/src/{tmdatagrid/core → core}/grouping.ts +21 -0
- package/src/{tmdatagrid/core → core}/labels.ts +51 -6
- package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
- package/src/core/pageReset.ts +120 -0
- package/src/core/pagination.ts +81 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/{tmdatagrid/core → core}/summary.ts +20 -4
- package/src/{tmdatagrid/index.ts → index.ts} +69 -35
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
- /package/src/{tmdatagrid/core → core}/capabilities.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
- /package/src/{tmdatagrid/core → core}/controlledState.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
- /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
- /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
- /package/src/{tmdatagrid/core → core}/resizePreview.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
- /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
- /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
|
@@ -18,13 +18,19 @@ import {
|
|
|
18
18
|
useCallback,
|
|
19
19
|
useEffect,
|
|
20
20
|
useLayoutEffect,
|
|
21
|
+
useMemo,
|
|
21
22
|
useRef,
|
|
22
23
|
useState,
|
|
23
24
|
} from "react";
|
|
24
25
|
import classes from "./TMDataGridTable.module.css";
|
|
25
26
|
import sticky from "./sticky.module.css";
|
|
26
27
|
import { type TMDataGridRowData, useTMDataGridContext } from "../TMDataGridContext";
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
TMDataGridFilterPopup,
|
|
30
|
+
TMDataGridFilterSidebar,
|
|
31
|
+
} from "./TMDataGridFilterSurface";
|
|
32
|
+
import { TMDataGridHeaderFilterRow } from "./TMDataGridHeaderFilterRow";
|
|
33
|
+
import { getGridCapabilities } from "../core/capabilities";
|
|
28
34
|
import {
|
|
29
35
|
TMDataGridHeaderCell,
|
|
30
36
|
type TMDataGridColumnMenuItemsRenderer,
|
|
@@ -34,12 +40,17 @@ import {
|
|
|
34
40
|
type TMDataGridCellEditorClose,
|
|
35
41
|
} from "./TMDataGridCellEditor";
|
|
36
42
|
import { TMDataGridEntryRows } from "./TMDataGridEntryRows";
|
|
37
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
getColumnAlign,
|
|
45
|
+
isControlColumn,
|
|
46
|
+
isGeneratedColumn,
|
|
47
|
+
} from "../core/columnUtils";
|
|
38
48
|
import {
|
|
39
49
|
resizePreview,
|
|
40
50
|
type TMDataGridColumnTrack,
|
|
41
51
|
} from "../core/resizePreview";
|
|
42
52
|
import { useSettledTableState } from "../core/useSettledTableState";
|
|
53
|
+
import { isInputElement } from "../core/dom";
|
|
43
54
|
import { draftCellContext } from "../core/draftCellContext";
|
|
44
55
|
import {
|
|
45
56
|
getEditFieldName,
|
|
@@ -58,14 +69,12 @@ import {
|
|
|
58
69
|
resolveRangeBounds,
|
|
59
70
|
} from "../core/cellRange";
|
|
60
71
|
import {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
downloadTextFile,
|
|
72
|
+
buildExportData,
|
|
73
|
+
resolveExportOptions,
|
|
64
74
|
toClipboardText,
|
|
65
|
-
toExcelCsv,
|
|
66
75
|
writeClipboardText,
|
|
67
|
-
|
|
68
|
-
} from "../core/
|
|
76
|
+
writeExportFile,
|
|
77
|
+
} from "../core/export";
|
|
69
78
|
import { autosizeColumn, hasMountedCells } from "../core/autosize";
|
|
70
79
|
import {
|
|
71
80
|
getDisplayedRows,
|
|
@@ -321,16 +330,16 @@ function TMDataGridBodyCell({
|
|
|
321
330
|
// selectors also require `data-cell`, so they are unaffected.
|
|
322
331
|
data-row-id={cell.row.id}
|
|
323
332
|
data-column-id={cell.column.id}
|
|
324
|
-
data-focused={nav?.focused}
|
|
333
|
+
data-focused={nav?.focused || undefined}
|
|
325
334
|
aria-selected={nav?.selected}
|
|
326
|
-
data-selected={nav?.selected}
|
|
335
|
+
data-selected={nav?.selected || undefined}
|
|
327
336
|
// One attribute per side rather than a class per combination: the
|
|
328
337
|
// stylesheet draws the outline edge by edge, and sixteen classes for the
|
|
329
338
|
// sixteen corners of a rectangle is not a stylesheet anyone can read.
|
|
330
|
-
data-edge-top={nav?.edges?.top}
|
|
331
|
-
data-edge-bottom={nav?.edges?.bottom}
|
|
332
|
-
data-edge-left={nav?.edges?.left}
|
|
333
|
-
data-edge-right={nav?.edges?.right}
|
|
339
|
+
data-edge-top={nav?.edges?.top || undefined}
|
|
340
|
+
data-edge-bottom={nav?.edges?.bottom || undefined}
|
|
341
|
+
data-edge-left={nav?.edges?.left || undefined}
|
|
342
|
+
data-edge-right={nav?.edges?.right || undefined}
|
|
334
343
|
// Bubbles, so focus landing on a control inside the cell counts as
|
|
335
344
|
// landing on the cell - the ring follows the user into the checkbox
|
|
336
345
|
// rather than being left behind on whichever cell they came from.
|
|
@@ -345,7 +354,7 @@ function TMDataGridBodyCell({
|
|
|
345
354
|
data-align={getColumnAlign(cell.column)}
|
|
346
355
|
// A control lane is a fixed track, so it cannot take the cell padding the
|
|
347
356
|
// scale grows for text. See isControlColumn.
|
|
348
|
-
data-control-column={isControl}
|
|
357
|
+
data-control-column={isControl || undefined}
|
|
349
358
|
onContextMenu={onContextMenu}
|
|
350
359
|
className={[
|
|
351
360
|
classes.bodyCell,
|
|
@@ -669,12 +678,6 @@ export type TMDataGridTableProps<TData extends RowData> = {
|
|
|
669
678
|
* `position`, `transitionProps` and the rest. Its open state is the grid's.
|
|
670
679
|
*/
|
|
671
680
|
rowContextMenuProps?: Omit<MenuProps, "opened" | "onChange" | "children">;
|
|
672
|
-
/**
|
|
673
|
-
* How Ctrl+C and the export item write values, under
|
|
674
|
-
* `cellSelection: "range"`. Defaults to the Nordic Excel conventions - see
|
|
675
|
-
* {@link TMDataGridCellExportOptions}.
|
|
676
|
-
*/
|
|
677
|
-
cellExport?: TMDataGridCellExportOptions;
|
|
678
681
|
/**
|
|
679
682
|
* Called when the scroll approaches the last row - the infinite-scroll
|
|
680
683
|
* hook-in. Append the next page to `data` and the virtualizer keeps its
|
|
@@ -726,7 +729,6 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
726
729
|
renderRowContextMenu,
|
|
727
730
|
renderColumnMenuItems,
|
|
728
731
|
rowContextMenuProps,
|
|
729
|
-
cellExport,
|
|
730
732
|
onReachEnd,
|
|
731
733
|
reachEndThreshold = 10,
|
|
732
734
|
"aria-label": ariaLabel,
|
|
@@ -737,6 +739,8 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
737
739
|
ui,
|
|
738
740
|
edit,
|
|
739
741
|
features,
|
|
742
|
+
filters,
|
|
743
|
+
exportOptions: gridExportOptions,
|
|
740
744
|
labels,
|
|
741
745
|
rowHeight,
|
|
742
746
|
controlSize,
|
|
@@ -783,6 +787,24 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
783
787
|
edit.store,
|
|
784
788
|
(state) => state.committedRowIds,
|
|
785
789
|
);
|
|
790
|
+
// The same lists by id: the body asks them once per rendered row, and an
|
|
791
|
+
// import runs each of them to thousands.
|
|
792
|
+
const editOpenRowIdSet = useMemo(
|
|
793
|
+
() => new Set(editOpenRowIds),
|
|
794
|
+
[editOpenRowIds],
|
|
795
|
+
);
|
|
796
|
+
const editDirtyRowIdSet = useMemo(
|
|
797
|
+
() => new Set(editDirtyRowIds),
|
|
798
|
+
[editDirtyRowIds],
|
|
799
|
+
);
|
|
800
|
+
const editDraftRowIdSet = useMemo(
|
|
801
|
+
() => new Set(editDraftRowIds),
|
|
802
|
+
[editDraftRowIds],
|
|
803
|
+
);
|
|
804
|
+
const newRowIdSet = useMemo(
|
|
805
|
+
() => new Set(newRows.map((newRow) => newRow.tempId)),
|
|
806
|
+
[newRows],
|
|
807
|
+
);
|
|
786
808
|
|
|
787
809
|
const { loading, noResultsLabel = labels.noResults } =
|
|
788
810
|
table.options.meta ?? {};
|
|
@@ -900,6 +922,22 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
900
922
|
* virtualizer needs its height from the first paint.
|
|
901
923
|
*/
|
|
902
924
|
const gridElementRef = useRef<HTMLDivElement>(null);
|
|
925
|
+
/**
|
|
926
|
+
* Header filters only render where something can be filtered - the row would
|
|
927
|
+
* otherwise be a band of empty cells on a grid with `enableColumnFilters:
|
|
928
|
+
* false`. Derived up here because the header measurement below depends on
|
|
929
|
+
* whether the row is in the DOM.
|
|
930
|
+
*/
|
|
931
|
+
const hasHeaderFilterRow =
|
|
932
|
+
filters.inHeader && getGridCapabilities(table, features).canFilterAny;
|
|
933
|
+
/**
|
|
934
|
+
* How many rows deep the header is. In the measurement's dependencies
|
|
935
|
+
* because the rows are found once, by query: a `columns` change that adds or
|
|
936
|
+
* drops a group level changes which elements have to be measured and given
|
|
937
|
+
* their sticky inset, and nothing else in the list moves when it does.
|
|
938
|
+
*/
|
|
939
|
+
const headerDepth = table.getCenterHeaderGroups().length;
|
|
940
|
+
const tableWrapperRef = useRef<HTMLDivElement>(null);
|
|
903
941
|
const [bodyOffsetTop, setBodyOffsetTop] = useState(0);
|
|
904
942
|
const [stickyCoverTop, setStickyCoverTop] = useState(0);
|
|
905
943
|
const [stickyCoverBottom, setStickyCoverBottom] = useState(0);
|
|
@@ -917,10 +955,28 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
917
955
|
);
|
|
918
956
|
const measure = () => {
|
|
919
957
|
let header = 0;
|
|
920
|
-
for (const headerRow of headerRows)
|
|
958
|
+
for (const headerRow of headerRows) {
|
|
959
|
+
// Every header row is sticky, so each one needs the height of the rows
|
|
960
|
+
// above it as its own inset - otherwise a stacked group and the filter
|
|
961
|
+
// row all pin to the top edge and paint over each other. Written on the
|
|
962
|
+
// element because only JS knows where the row before it ended; the
|
|
963
|
+
// stylesheet's `top` is the pre-measurement fallback.
|
|
964
|
+
headerRow.style.top = `${header}px`;
|
|
965
|
+
header += headerRow.offsetHeight;
|
|
966
|
+
}
|
|
921
967
|
if (newRowCount > 0 || pinnedTopRows.length > 0) {
|
|
922
968
|
grid.style.setProperty("--dg-header-height", `${header}px`);
|
|
923
969
|
}
|
|
970
|
+
// The whole header's height, on the wrapper rather than on the grid,
|
|
971
|
+
// because the popup is a sibling of the scroll container and never sees
|
|
972
|
+
// a property set inside it. A second name rather than reusing
|
|
973
|
+
// `--dg-header-height`: the header cells read that one for their own
|
|
974
|
+
// `min-height`, so publishing a stacked total there would feed a
|
|
975
|
+
// two-row header back in as the height of each of its rows.
|
|
976
|
+
tableWrapperRef.current?.style.setProperty(
|
|
977
|
+
"--dg-header-stack-height",
|
|
978
|
+
`${header}px`,
|
|
979
|
+
);
|
|
924
980
|
let cover = header;
|
|
925
981
|
for (const block of stickyBlocks) cover += block.offsetHeight;
|
|
926
982
|
let offset = cover;
|
|
@@ -934,7 +990,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
934
990
|
observer.observe(element);
|
|
935
991
|
}
|
|
936
992
|
return () => observer.disconnect();
|
|
937
|
-
}, [newRowCount, pinnedTopRows.length]);
|
|
993
|
+
}, [newRowCount, pinnedTopRows.length, hasHeaderFilterRow, headerDepth]);
|
|
938
994
|
|
|
939
995
|
/**
|
|
940
996
|
* The entry block's height, published as `--dg-entry-height` so the pinned
|
|
@@ -958,32 +1014,6 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
958
1014
|
};
|
|
959
1015
|
}, [newRowCount, pinnedTopRows.length]);
|
|
960
1016
|
|
|
961
|
-
/**
|
|
962
|
-
* The summary row's height, published as `--dg-summary-height` so the
|
|
963
|
-
* pinned bottom block sticks above it rather than underneath it.
|
|
964
|
-
*/
|
|
965
|
-
useEffect(() => {
|
|
966
|
-
if (pinnedBottomRows.length === 0) return;
|
|
967
|
-
const grid = gridElementRef.current;
|
|
968
|
-
if (grid === null) return;
|
|
969
|
-
const summaryRow = grid.querySelector<HTMLElement>(
|
|
970
|
-
'[data-dg-part="summary-row"]',
|
|
971
|
-
);
|
|
972
|
-
if (summaryRow === null) return;
|
|
973
|
-
const measure = () =>
|
|
974
|
-
grid.style.setProperty(
|
|
975
|
-
"--dg-summary-height",
|
|
976
|
-
`${summaryRow.offsetHeight}px`,
|
|
977
|
-
);
|
|
978
|
-
measure();
|
|
979
|
-
const observer = new ResizeObserver(measure);
|
|
980
|
-
observer.observe(summaryRow);
|
|
981
|
-
return () => {
|
|
982
|
-
observer.disconnect();
|
|
983
|
-
grid.style.removeProperty("--dg-summary-height");
|
|
984
|
-
};
|
|
985
|
-
}, [pinnedBottomRows.length]);
|
|
986
|
-
|
|
987
1017
|
// A persisted pageIndex can outlive the data that produced it; TanStack only
|
|
988
1018
|
// auto-resets on live filter/sort/data changes, not on restored state. The
|
|
989
1019
|
// guard makes the dependency-free effect idempotent.
|
|
@@ -1122,14 +1152,14 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1122
1152
|
onReachEnd();
|
|
1123
1153
|
});
|
|
1124
1154
|
|
|
1125
|
-
// The *Visible* variants throughout: `
|
|
1155
|
+
// The *Visible* variants throughout: `getStartLeafColumns()` and friends
|
|
1126
1156
|
// include hidden columns, while the cells below come from
|
|
1127
|
-
// `row.
|
|
1157
|
+
// `row.getStartVisibleCells()`, which does not. Mixing the two would lay down
|
|
1128
1158
|
// a grid track for a column that renders no cell, shifting every column after
|
|
1129
1159
|
// it out of its header. The tree column is hidden exactly this way while
|
|
1130
1160
|
// nothing is grouped.
|
|
1131
|
-
const leftLeafColumns = table.
|
|
1132
|
-
const rightLeafColumns = table.
|
|
1161
|
+
const leftLeafColumns = table.getStartVisibleLeafColumns();
|
|
1162
|
+
const rightLeafColumns = table.getEndVisibleLeafColumns();
|
|
1133
1163
|
const centerLeafColumns = table.getCenterVisibleLeafColumns();
|
|
1134
1164
|
|
|
1135
1165
|
const orderedColumns = [
|
|
@@ -1141,8 +1171,8 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1141
1171
|
// Headers and cells must follow the same left → center → right order as the
|
|
1142
1172
|
// column tracks, otherwise pinning a column would shuffle it out of its lane.
|
|
1143
1173
|
const centerHeaderGroups = table.getCenterHeaderGroups();
|
|
1144
|
-
const leftHeaderGroups = table.
|
|
1145
|
-
const rightHeaderGroups = table.
|
|
1174
|
+
const leftHeaderGroups = table.getStartHeaderGroups();
|
|
1175
|
+
const rightHeaderGroups = table.getEndHeaderGroups();
|
|
1146
1176
|
const headerGroups = centerHeaderGroups.map((group, index) => ({
|
|
1147
1177
|
id: group.id,
|
|
1148
1178
|
headers: [
|
|
@@ -1308,14 +1338,51 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1308
1338
|
const hasSummaryRow = orderedColumns.some(
|
|
1309
1339
|
(column) => column.columnDef.footer !== undefined,
|
|
1310
1340
|
);
|
|
1341
|
+
/**
|
|
1342
|
+
* Rows above the first body row, which is what every `aria-rowindex` in the
|
|
1343
|
+
* body counts from and what `aria-rowcount` adds up. The header groups plus
|
|
1344
|
+
* the filter row, when there is one.
|
|
1345
|
+
*/
|
|
1346
|
+
const headerRowCount = headerGroups.length + (hasHeaderFilterRow ? 1 : 0);
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* The summary row's height, published as `--dg-summary-height`, which two
|
|
1350
|
+
* things read: the pinned bottom block sticks above the summary row rather
|
|
1351
|
+
* than underneath it, and the summary row's own `top` inset holds it on the
|
|
1352
|
+
* bottom edge of a body too short to scroll. A layout effect, so the first
|
|
1353
|
+
* paint already has the measurement - `top` would otherwise resolve to 100%
|
|
1354
|
+
* for a frame and take an overflowing grid's summary row out of view. Down
|
|
1355
|
+
* here rather than beside the other measurements because `hasSummaryRow` is
|
|
1356
|
+
* only known this far into the render.
|
|
1357
|
+
*/
|
|
1358
|
+
useLayoutEffect(() => {
|
|
1359
|
+
if (!hasSummaryRow) return;
|
|
1360
|
+
const grid = gridElementRef.current;
|
|
1361
|
+
if (grid === null) return;
|
|
1362
|
+
const summaryRow = grid.querySelector<HTMLElement>(
|
|
1363
|
+
'[data-dg-part="summary-row"]',
|
|
1364
|
+
);
|
|
1365
|
+
if (summaryRow === null) return;
|
|
1366
|
+
const measure = () =>
|
|
1367
|
+
grid.style.setProperty(
|
|
1368
|
+
"--dg-summary-height",
|
|
1369
|
+
`${summaryRow.offsetHeight}px`,
|
|
1370
|
+
);
|
|
1371
|
+
measure();
|
|
1372
|
+
const observer = new ResizeObserver(measure);
|
|
1373
|
+
observer.observe(summaryRow);
|
|
1374
|
+
return () => {
|
|
1375
|
+
observer.disconnect();
|
|
1376
|
+
grid.style.removeProperty("--dg-summary-height");
|
|
1377
|
+
};
|
|
1378
|
+
}, [hasSummaryRow]);
|
|
1311
1379
|
|
|
1312
1380
|
/**
|
|
1313
1381
|
* The other end of the same story as `stickyCoverTop`: the pinned bottom
|
|
1314
1382
|
* block and the summary row stick to the container's bottom edge, so the
|
|
1315
1383
|
* virtualizer takes their height as `scrollPaddingEnd` and stops scrolling a
|
|
1316
|
-
* row to just underneath them. Down here
|
|
1317
|
-
*
|
|
1318
|
-
* render.
|
|
1384
|
+
* row to just underneath them. Down here for the same reason as the
|
|
1385
|
+
* measurement above.
|
|
1319
1386
|
*/
|
|
1320
1387
|
useEffect(() => {
|
|
1321
1388
|
const grid = gridElementRef.current;
|
|
@@ -1579,24 +1646,23 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1579
1646
|
? 0
|
|
1580
1647
|
: orderedColumns
|
|
1581
1648
|
.slice(selectionBounds.left, selectionBounds.right + 1)
|
|
1582
|
-
.filter(
|
|
1649
|
+
.filter(
|
|
1650
|
+
(column) =>
|
|
1651
|
+
!isGeneratedColumn(column.id) &&
|
|
1652
|
+
column.columnDef.meta?.enableExport !== false,
|
|
1653
|
+
).length;
|
|
1583
1654
|
|
|
1584
|
-
const exportOptions =
|
|
1655
|
+
const exportOptions = resolveExportOptions(gridExportOptions);
|
|
1585
1656
|
// Sticky across menus, the way a checkbox in a dialog is: a user who exports
|
|
1586
1657
|
// with headers once almost always wants them the next time too.
|
|
1587
1658
|
const [exportIncludesHeaders, setExportIncludesHeaders] = useState(
|
|
1588
1659
|
exportOptions.includeHeaders,
|
|
1589
1660
|
);
|
|
1590
|
-
|
|
1661
|
+
// The rectangle's values, over the displayed rows the bounds index into.
|
|
1662
|
+
const buildSelectionData = () =>
|
|
1591
1663
|
selectionBounds === null
|
|
1592
|
-
?
|
|
1593
|
-
:
|
|
1594
|
-
rows,
|
|
1595
|
-
columns: orderedColumns,
|
|
1596
|
-
bounds: selectionBounds,
|
|
1597
|
-
includeHeaders,
|
|
1598
|
-
decimalComma: exportOptions.decimalComma,
|
|
1599
|
-
});
|
|
1664
|
+
? null
|
|
1665
|
+
: buildExportData({ table, rows, bounds: selectionBounds });
|
|
1600
1666
|
|
|
1601
1667
|
/**
|
|
1602
1668
|
* Ctrl+C. Values only, no header row - Excel's own copy does not include one
|
|
@@ -1605,9 +1671,13 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1605
1671
|
* because a file is a document and a clipboard is a fragment.
|
|
1606
1672
|
*/
|
|
1607
1673
|
const copySelection = async () => {
|
|
1608
|
-
const
|
|
1609
|
-
if (
|
|
1610
|
-
await writeClipboardText(
|
|
1674
|
+
const data = buildSelectionData();
|
|
1675
|
+
if (data === null || data.columnIds.length === 0) return;
|
|
1676
|
+
await writeClipboardText(
|
|
1677
|
+
toClipboardText(data, {
|
|
1678
|
+
decimalComma: exportOptions.format.decimalComma,
|
|
1679
|
+
}),
|
|
1680
|
+
);
|
|
1611
1681
|
};
|
|
1612
1682
|
|
|
1613
1683
|
/**
|
|
@@ -1621,12 +1691,15 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1621
1691
|
|
|
1622
1692
|
// The drag ends wherever the button comes up, which is routinely outside the
|
|
1623
1693
|
// grid - over the scrollbar, or past the window edge. Only the window hears
|
|
1624
|
-
// that, so only the window can end it.
|
|
1694
|
+
// that, so only the window can end it. The grid's own window, not the
|
|
1695
|
+
// global one: rendered through a portal into a window opened with
|
|
1696
|
+
// `window.open`, the global is the opener and never hears the release.
|
|
1625
1697
|
useEffect(() => {
|
|
1626
1698
|
if (!isDraggingRange) return;
|
|
1627
1699
|
const stop = () => setIsDraggingRange(false);
|
|
1628
|
-
|
|
1629
|
-
|
|
1700
|
+
const view = gridElementRef.current?.ownerDocument.defaultView ?? window;
|
|
1701
|
+
view.addEventListener("mouseup", stop);
|
|
1702
|
+
return () => view.removeEventListener("mouseup", stop);
|
|
1630
1703
|
}, [isDraggingRange]);
|
|
1631
1704
|
|
|
1632
1705
|
const startRangeDrag = (
|
|
@@ -1640,12 +1713,13 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1640
1713
|
if (!extend) setIsDraggingRange(true);
|
|
1641
1714
|
};
|
|
1642
1715
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1716
|
+
/** The export item: the rectangle, in the grid's export format. */
|
|
1717
|
+
const exportSelection = () => {
|
|
1718
|
+
const data = buildSelectionData();
|
|
1719
|
+
if (data === null || data.columnIds.length === 0) return;
|
|
1720
|
+
void writeExportFile(data, {
|
|
1721
|
+
...exportOptions,
|
|
1722
|
+
includeHeaders: exportIncludesHeaders,
|
|
1649
1723
|
});
|
|
1650
1724
|
};
|
|
1651
1725
|
|
|
@@ -1695,9 +1769,9 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1695
1769
|
const row = rows.find((candidate) => candidate.id === rowId);
|
|
1696
1770
|
if (row === undefined) return null;
|
|
1697
1771
|
const cells = [
|
|
1698
|
-
...row.
|
|
1772
|
+
...row.getStartVisibleCells(),
|
|
1699
1773
|
...row.getCenterVisibleCells(),
|
|
1700
|
-
...row.
|
|
1774
|
+
...row.getEndVisibleCells(),
|
|
1701
1775
|
];
|
|
1702
1776
|
return (
|
|
1703
1777
|
cells.find((cell) => edit.canEditCell(row, cell.column))?.column.id ??
|
|
@@ -1904,7 +1978,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
1904
1978
|
event.preventDefault();
|
|
1905
1979
|
nextControl.focus();
|
|
1906
1980
|
// What Tab does on a text field, and what the caret otherwise loses.
|
|
1907
|
-
if (nextControl
|
|
1981
|
+
if (isInputElement(nextControl)) nextControl.select();
|
|
1908
1982
|
return;
|
|
1909
1983
|
}
|
|
1910
1984
|
const rowIndex = rows.findIndex(
|
|
@@ -2119,13 +2193,15 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2119
2193
|
return;
|
|
2120
2194
|
}
|
|
2121
2195
|
|
|
2196
|
+
// The grid's own document, not the global one: in a window opened with
|
|
2197
|
+
// `window.open` the global document is the opener's, and its focus is
|
|
2198
|
+
// never in this grid.
|
|
2199
|
+
const activeElement = container.ownerDocument.activeElement;
|
|
2200
|
+
|
|
2122
2201
|
// Never take the focus from elsewhere on the page. A consumer moving the
|
|
2123
2202
|
// cell while the user is typing in a form somewhere means "move the ring",
|
|
2124
2203
|
// not "move the caret" - the scroll above already did the visible part.
|
|
2125
|
-
if (
|
|
2126
|
-
!wantsCellFocusRef.current &&
|
|
2127
|
-
!container.contains(document.activeElement)
|
|
2128
|
-
) {
|
|
2204
|
+
if (!wantsCellFocusRef.current && !container.contains(activeElement)) {
|
|
2129
2205
|
return;
|
|
2130
2206
|
}
|
|
2131
2207
|
// Mid-hop: Tab parked the focus on a guard and the browser is about to
|
|
@@ -2136,8 +2212,8 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2136
2212
|
// the cell, and that intent is honoured.
|
|
2137
2213
|
if (
|
|
2138
2214
|
!wantsCellFocusRef.current &&
|
|
2139
|
-
|
|
2140
|
-
|
|
2215
|
+
activeElement !== null &&
|
|
2216
|
+
activeElement.closest('[data-dg-part="tab-guard"]') !== null
|
|
2141
2217
|
) {
|
|
2142
2218
|
return;
|
|
2143
2219
|
}
|
|
@@ -2148,7 +2224,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2148
2224
|
wantsCellFocusRef.current = false;
|
|
2149
2225
|
// Focus is already inside - on the control Enter stepped into. The cell is
|
|
2150
2226
|
// where the ring belongs, so there is nothing to move.
|
|
2151
|
-
if (cellElement.contains(
|
|
2227
|
+
if (cellElement.contains(activeElement)) return;
|
|
2152
2228
|
// An open editor outranks the ring, wherever it is. Row mode opens a whole
|
|
2153
2229
|
// row from one gesture, so the caret legitimately sits in a cell other
|
|
2154
2230
|
// than the one that gesture landed the ring on - the lane's pencil is the
|
|
@@ -2156,8 +2232,8 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2156
2232
|
// the caret back onto that cell would leave the row open with the caret in
|
|
2157
2233
|
// none of its editors, and the user with nothing to type into.
|
|
2158
2234
|
if (
|
|
2159
|
-
|
|
2160
|
-
|
|
2235
|
+
activeElement !== null &&
|
|
2236
|
+
activeElement.closest('[data-dg-part="editor"]') !== null
|
|
2161
2237
|
) {
|
|
2162
2238
|
return;
|
|
2163
2239
|
}
|
|
@@ -2256,9 +2332,9 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2256
2332
|
</Menu.Item>
|
|
2257
2333
|
<Menu.Item
|
|
2258
2334
|
disabled={selectedDataColumnCount === 0}
|
|
2259
|
-
onClick={
|
|
2335
|
+
onClick={exportSelection}
|
|
2260
2336
|
>
|
|
2261
|
-
{labels.
|
|
2337
|
+
{labels.exportCells}
|
|
2262
2338
|
</Menu.Item>
|
|
2263
2339
|
{/* Stays open on click: it is the setting the item above reads, and
|
|
2264
2340
|
reopening the menu to change your mind about headers is a worse
|
|
@@ -2379,7 +2455,11 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2379
2455
|
: undefined;
|
|
2380
2456
|
|
|
2381
2457
|
return (
|
|
2382
|
-
<div
|
|
2458
|
+
<div
|
|
2459
|
+
ref={tableWrapperRef}
|
|
2460
|
+
className={classes.tableWrapper}
|
|
2461
|
+
data-dg-filter-sidebar={filters.surface === "sidebar" || undefined}
|
|
2462
|
+
>
|
|
2383
2463
|
<div
|
|
2384
2464
|
ref={scrollContainerRef}
|
|
2385
2465
|
className={classes.scrollContainer}
|
|
@@ -2417,7 +2497,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2417
2497
|
aria-rowcount={
|
|
2418
2498
|
rows.length +
|
|
2419
2499
|
pinnedRowCount +
|
|
2420
|
-
|
|
2500
|
+
headerRowCount +
|
|
2421
2501
|
(hasSummaryRow ? 1 : 0)
|
|
2422
2502
|
}
|
|
2423
2503
|
aria-colcount={orderedColumns.length}
|
|
@@ -2437,11 +2517,16 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2437
2517
|
data-dg-header-row
|
|
2438
2518
|
// The scrolled-under shadow belongs to the boundary between
|
|
2439
2519
|
// header and body - the last header row, not every stacked
|
|
2440
|
-
// group row above it
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2520
|
+
// group row above it, and not this one at all when the filter
|
|
2521
|
+
// row is below it. See sticky.module.css.
|
|
2522
|
+
className={[
|
|
2523
|
+
classes.headerRow,
|
|
2524
|
+
!hasHeaderFilterRow && groupIndex === headerGroups.length - 1
|
|
2525
|
+
? sticky.headerBoundary
|
|
2526
|
+
: "",
|
|
2527
|
+
]
|
|
2528
|
+
.filter(Boolean)
|
|
2529
|
+
.join(" ")}
|
|
2445
2530
|
>
|
|
2446
2531
|
{headerGroup.headers.map((header) => (
|
|
2447
2532
|
<TMDataGridHeaderCell
|
|
@@ -2453,6 +2538,14 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2453
2538
|
))}
|
|
2454
2539
|
</div>
|
|
2455
2540
|
))}
|
|
2541
|
+
|
|
2542
|
+
{hasHeaderFilterRow && (
|
|
2543
|
+
<TMDataGridHeaderFilterRow
|
|
2544
|
+
leafHeaders={leafHeaders}
|
|
2545
|
+
layoutFor={layoutFor}
|
|
2546
|
+
ariaRowIndex={headerRowCount}
|
|
2547
|
+
/>
|
|
2548
|
+
)}
|
|
2456
2549
|
</div>
|
|
2457
2550
|
|
|
2458
2551
|
{/* The leading tab guard, below the header controls and above the
|
|
@@ -2511,9 +2604,9 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2511
2604
|
const isGroupRow = row.getIsGrouped();
|
|
2512
2605
|
const isInteractive = rowGesturesFor(row);
|
|
2513
2606
|
const rowCells = [
|
|
2514
|
-
...row.
|
|
2607
|
+
...row.getStartVisibleCells(),
|
|
2515
2608
|
...row.getCenterVisibleCells(),
|
|
2516
|
-
...row.
|
|
2609
|
+
...row.getEndVisibleCells(),
|
|
2517
2610
|
];
|
|
2518
2611
|
// Row mode opens every editable cell of the row at once, and
|
|
2519
2612
|
// rows accumulate: a second row opening leaves the first one
|
|
@@ -2529,9 +2622,9 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2529
2622
|
// reopening takes it back to the entry block.
|
|
2530
2623
|
const rowEditing =
|
|
2531
2624
|
features.editMode === "row" &&
|
|
2532
|
-
|
|
2533
|
-
!
|
|
2534
|
-
!
|
|
2625
|
+
editOpenRowIdSet.has(row.id) &&
|
|
2626
|
+
!editDraftRowIdSet.has(row.id) &&
|
|
2627
|
+
!newRowIdSet.has(row.id);
|
|
2535
2628
|
// Cell selection takes the body's tab stop off the row and puts
|
|
2536
2629
|
// it on a cell - two stops per row would make Tab a way of
|
|
2537
2630
|
// walking the grid, which is what the arrow keys are for. Space
|
|
@@ -2568,7 +2661,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2568
2661
|
// The tree's own rows. `data-grouped` rather than a class, to
|
|
2569
2662
|
// match how the rest of the row's state is published - and so
|
|
2570
2663
|
// a consumer can restyle them without reaching into modules.
|
|
2571
|
-
data-grouped={isGroupRow}
|
|
2664
|
+
data-grouped={isGroupRow || undefined}
|
|
2572
2665
|
data-depth={row.depth}
|
|
2573
2666
|
// Which edge block the row sits in, for styling hooks.
|
|
2574
2667
|
data-pinned={pinnedAt}
|
|
@@ -2577,13 +2670,16 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2577
2670
|
// still hold ids from before the mode changed - TanStack never
|
|
2578
2671
|
// prunes it - and a row must not report itself selected in a
|
|
2579
2672
|
// mode where selecting is not a thing.
|
|
2580
|
-
data-selected={
|
|
2673
|
+
data-selected={
|
|
2674
|
+
(features.rowSelection && row.getIsSelected()) || undefined
|
|
2675
|
+
}
|
|
2581
2676
|
// Being selected is state; painting it is a display choice, so
|
|
2582
2677
|
// the two are separate attributes.
|
|
2583
2678
|
data-selected-bg={
|
|
2584
|
-
features.rowSelection &&
|
|
2585
|
-
|
|
2586
|
-
|
|
2679
|
+
(features.rowSelection &&
|
|
2680
|
+
features.showSelectedBackground &&
|
|
2681
|
+
row.getIsSelected()) ||
|
|
2682
|
+
undefined
|
|
2587
2683
|
}
|
|
2588
2684
|
// The highlighted row is its own concept, so its own attribute
|
|
2589
2685
|
// pair - `data-highlighted` / `aria-current` against
|
|
@@ -2595,42 +2691,40 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2595
2691
|
// so a consumer styling or querying rows by it would cast far
|
|
2596
2692
|
// wider than they meant to.
|
|
2597
2693
|
data-highlighted={
|
|
2598
|
-
features.highlightRow && row.id === highlightedRowId
|
|
2694
|
+
(features.highlightRow && row.id === highlightedRowId) ||
|
|
2695
|
+
undefined
|
|
2599
2696
|
}
|
|
2600
2697
|
// Marked deleted under draft: struck through and inert
|
|
2601
|
-
// until
|
|
2698
|
+
// until saveDrafts reports it, or the mark is toggled back.
|
|
2602
2699
|
data-deleted={
|
|
2603
|
-
deletedRowIds.length > 0 && deletedRowIds.includes(row.id)
|
|
2700
|
+
(deletedRowIds.length > 0 && deletedRowIds.includes(row.id)) ||
|
|
2701
|
+
undefined
|
|
2604
2702
|
}
|
|
2605
2703
|
// Carrying a dirty draft - the row-level face of the cells'
|
|
2606
2704
|
// own data-dirty markers, for row-scoped styling.
|
|
2607
|
-
data-dirty={
|
|
2608
|
-
editDirtyRowIds.length > 0 &&
|
|
2609
|
-
editDirtyRowIds.includes(row.id)
|
|
2610
|
-
}
|
|
2705
|
+
data-dirty={editDirtyRowIdSet.has(row.id) || undefined}
|
|
2611
2706
|
// Committed into the draft store, waiting for Save. A
|
|
2612
2707
|
// committed entry row is one too - it is in the body
|
|
2613
2708
|
// because it is committed - and carries `data-new` besides.
|
|
2614
2709
|
data-draft={
|
|
2615
|
-
(
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
newRows.some((newRow) => newRow.tempId === row.id))
|
|
2710
|
+
editDraftRowIdSet.has(row.id) ||
|
|
2711
|
+
newRowIdSet.has(row.id) ||
|
|
2712
|
+
undefined
|
|
2619
2713
|
}
|
|
2620
2714
|
// An entered row not yet in `data`, committed into the draft
|
|
2621
2715
|
// store and sorted, filtered and grouped with the rest.
|
|
2622
|
-
data-new={
|
|
2623
|
-
newRowCount > 0 &&
|
|
2624
|
-
newRows.some((newRow) => newRow.tempId === row.id)
|
|
2625
|
-
}
|
|
2716
|
+
data-new={newRowIdSet.has(row.id) || undefined}
|
|
2626
2717
|
// The menu is anchored to the rowgroup, so Mantine's own
|
|
2627
2718
|
// `data-expanded` lands there rather than on a row. This is
|
|
2628
2719
|
// what says which row the open menu is about.
|
|
2629
2720
|
data-context-menu={
|
|
2630
|
-
contextMenuContent !== null &&
|
|
2631
|
-
|
|
2721
|
+
(contextMenuContent !== null &&
|
|
2722
|
+
contextMenuTarget?.rowId === row.id) ||
|
|
2723
|
+
undefined
|
|
2724
|
+
}
|
|
2725
|
+
data-selects-on-click={
|
|
2726
|
+
(selectsOnRowClick && !isGroupRow) || undefined
|
|
2632
2727
|
}
|
|
2633
|
-
data-selects-on-click={selectsOnRowClick && !isGroupRow}
|
|
2634
2728
|
aria-selected={
|
|
2635
2729
|
features.rowSelection ? row.getIsSelected() : undefined
|
|
2636
2730
|
}
|
|
@@ -2642,7 +2736,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2642
2736
|
// From the row's position in the whole view, so the stripes
|
|
2643
2737
|
// survive the virtualizer's moving window - see the prop.
|
|
2644
2738
|
data-striped={
|
|
2645
|
-
striped && viewIndex >= 0
|
|
2739
|
+
(striped && viewIndex >= 0 && viewIndex % 2 === 1) || undefined
|
|
2646
2740
|
}
|
|
2647
2741
|
className={[classes.bodyRow, rowClassNameFor(row)]
|
|
2648
2742
|
.filter(Boolean)
|
|
@@ -2909,7 +3003,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2909
3003
|
renderBodyRow(
|
|
2910
3004
|
row,
|
|
2911
3005
|
-1,
|
|
2912
|
-
|
|
3006
|
+
headerRowCount + index + 1,
|
|
2913
3007
|
"top",
|
|
2914
3008
|
),
|
|
2915
3009
|
)}
|
|
@@ -2930,7 +3024,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2930
3024
|
row,
|
|
2931
3025
|
virtualItem.index,
|
|
2932
3026
|
virtualItem.index +
|
|
2933
|
-
|
|
3027
|
+
headerRowCount +
|
|
2934
3028
|
pinnedTopRows.length +
|
|
2935
3029
|
1,
|
|
2936
3030
|
);
|
|
@@ -2953,7 +3047,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
2953
3047
|
renderBodyRow(
|
|
2954
3048
|
row,
|
|
2955
3049
|
-1,
|
|
2956
|
-
|
|
3050
|
+
headerRowCount +
|
|
2957
3051
|
pinnedTopRows.length +
|
|
2958
3052
|
rows.length +
|
|
2959
3053
|
index +
|
|
@@ -3009,7 +3103,7 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
3009
3103
|
<div
|
|
3010
3104
|
role="row"
|
|
3011
3105
|
aria-rowindex={
|
|
3012
|
-
rows.length + pinnedRowCount +
|
|
3106
|
+
rows.length + pinnedRowCount + headerRowCount + 1
|
|
3013
3107
|
}
|
|
3014
3108
|
// Also what the height measurement above looks for. One
|
|
3015
3109
|
// attribute serves both now that the part name *is* the
|
|
@@ -3025,7 +3119,9 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
3025
3119
|
role="cell"
|
|
3026
3120
|
data-column-id={header.column.id}
|
|
3027
3121
|
data-align={getColumnAlign(header.column)}
|
|
3028
|
-
data-control-column={
|
|
3122
|
+
data-control-column={
|
|
3123
|
+
isControlColumn(header.column.id) || undefined
|
|
3124
|
+
}
|
|
3029
3125
|
className={[
|
|
3030
3126
|
classes.summaryCell,
|
|
3031
3127
|
layout.isBoundary && layout.pinnedAt === "left"
|
|
@@ -3079,7 +3175,13 @@ export function TMDataGridTable<TData extends RowData = TMDataGridRowData>({
|
|
|
3079
3175
|
</div>
|
|
3080
3176
|
</div>
|
|
3081
3177
|
|
|
3082
|
-
|
|
3178
|
+
{/* The automatic filter surfaces. The popup floats over the first body
|
|
3179
|
+
rows, anchored to the wrapper; the sidebar is a second column of it,
|
|
3180
|
+
which is why the wrapper turns into a flex row for one. Under
|
|
3181
|
+
`filters.surface: "none"` neither renders and the consumer's own
|
|
3182
|
+
`TMDataGrid.FilterPanel` is the only panel on the page. */}
|
|
3183
|
+
{filters.surface === "popup" && <TMDataGridFilterPopup />}
|
|
3184
|
+
{filters.surface === "sidebar" && <TMDataGridFilterSidebar />}
|
|
3083
3185
|
</div>
|
|
3084
3186
|
);
|
|
3085
3187
|
}
|