@gridsuite/commons-ui 0.198.0 → 0.200.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/components/network-modification-table/network-modifications-table.js +15 -7
- package/dist/components/network-modification-table/renderers/select-cell.js +21 -32
- package/dist/components/network-modification-table/renderers/select-header-cell.js +1 -1
- package/dist/components/network-modification-table/use-modifications-selection.d.ts +15 -0
- package/dist/components/network-modification-table/use-modifications-selection.js +80 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { useVirtualizer } from "@tanstack/react-virtual";
|
|
|
7
7
|
import { MODIFICATION_ROW_HEIGHT, networkModificationTableStyles, createHeaderCellStyle } from "./network-modification-table-styles.js";
|
|
8
8
|
import { AUTO_EXTENSIBLE_COLUMNS } from "./columns-definition.js";
|
|
9
9
|
import { useModificationsDragAndDrop } from "./use-modifications-drag-and-drop.js";
|
|
10
|
+
import { useModificationsSelection } from "./use-modifications-selection.js";
|
|
10
11
|
import { formatToComposedModification, mergeSubModificationsIntoTree, findAllLoadedCompositeModifications, fetchSubModificationsForExpandedRows, isCompositeModification } from "./utils.js";
|
|
11
12
|
import "@mui/icons-material";
|
|
12
13
|
import "react-intl";
|
|
@@ -33,11 +34,14 @@ function NetworkModificationsTable({
|
|
|
33
34
|
}) {
|
|
34
35
|
const theme = useTheme();
|
|
35
36
|
const containerRef = useRef(null);
|
|
36
|
-
const lastClickedIndex = useRef(null);
|
|
37
37
|
const [expanded, setExpanded] = useState({});
|
|
38
38
|
const [composedModifications, setComposedModifications] = useState(
|
|
39
39
|
formatToComposedModification(modifications)
|
|
40
40
|
);
|
|
41
|
+
const { rowSelection, onRowSelectionChange, lastClickedRowId, emitSelection } = useModificationsSelection({
|
|
42
|
+
modifications: composedModifications,
|
|
43
|
+
onRowSelected
|
|
44
|
+
});
|
|
41
45
|
const columns = useMemo(
|
|
42
46
|
() => createAllColumns(
|
|
43
47
|
isRowDragDisabled ?? false,
|
|
@@ -77,19 +81,23 @@ function NetworkModificationsTable({
|
|
|
77
81
|
const table = useReactTable({
|
|
78
82
|
data: composedModifications,
|
|
79
83
|
columns,
|
|
80
|
-
state: { expanded },
|
|
84
|
+
state: { expanded, rowSelection },
|
|
81
85
|
getCoreRowModel: getCoreRowModel(),
|
|
82
86
|
getExpandedRowModel: getExpandedRowModel(),
|
|
83
87
|
getSubRows: (row) => row.subModifications,
|
|
84
88
|
getRowId: (row) => row.uuid,
|
|
85
89
|
getRowCanExpand: (row) => isCompositeModification(row.original),
|
|
86
90
|
enableRowSelection: true,
|
|
87
|
-
enableSubRowSelection:
|
|
91
|
+
enableSubRowSelection: true,
|
|
88
92
|
enableExpanding: true,
|
|
89
93
|
onExpandedChange: handleExpandRow,
|
|
90
|
-
|
|
94
|
+
onRowSelectionChange,
|
|
95
|
+
meta: { lastClickedRowId, onRowSelected }
|
|
91
96
|
});
|
|
92
|
-
const { rows } = table.getRowModel();
|
|
97
|
+
const { rows, flatRows } = table.getRowModel();
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
emitSelection(flatRows);
|
|
100
|
+
}, [rowSelection, flatRows, emitSelection]);
|
|
93
101
|
const virtualizer = useVirtualizer({
|
|
94
102
|
count: rows.length,
|
|
95
103
|
getScrollElement: () => containerRef.current,
|
|
@@ -109,8 +117,8 @@ function NetworkModificationsTable({
|
|
|
109
117
|
useEffect(() => {
|
|
110
118
|
table.resetRowSelection();
|
|
111
119
|
table.resetExpanded();
|
|
112
|
-
|
|
113
|
-
}, [table]);
|
|
120
|
+
lastClickedRowId.current = null;
|
|
121
|
+
}, [lastClickedRowId, table, currentNodeId]);
|
|
114
122
|
useEffect(() => {
|
|
115
123
|
if (highlightedModificationUuid && containerRef.current) {
|
|
116
124
|
const rowIndex = rows.findIndex((row) => row.original.uuid === highlightedModificationUuid);
|
|
@@ -1,54 +1,43 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback
|
|
2
|
+
import { useCallback } from "react";
|
|
3
3
|
import { Checkbox } from "@mui/material";
|
|
4
4
|
import { networkModificationTableStyles } from "../network-modification-table-styles.js";
|
|
5
|
+
function toggleRange(rows, from, to, targetSelected) {
|
|
6
|
+
const [start, end] = from <= to ? [from, to] : [to, from];
|
|
7
|
+
rows.slice(start, end + 1).forEach((r) => {
|
|
8
|
+
if (r.getCanSelect()) {
|
|
9
|
+
r.toggleSelected(targetSelected);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
5
13
|
function SelectCell({ row, table }) {
|
|
6
14
|
const { meta } = table.options;
|
|
15
|
+
const isSelected = row.getIsSelected();
|
|
16
|
+
const isIndeterminate = !isSelected && row.getIsSomeSelected();
|
|
7
17
|
const handleChange = useCallback(
|
|
8
18
|
(event) => {
|
|
9
19
|
const rows = table.getRowModel().flatRows;
|
|
10
20
|
const currentIndex = rows.indexOf(row);
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
rows.slice(from, to + 1).forEach((r) => {
|
|
17
|
-
if (r.getCanSelect()) {
|
|
18
|
-
r.toggleSelected(!isRowSelected);
|
|
19
|
-
if (isRowSelected) {
|
|
20
|
-
delete nextSelection[r.id];
|
|
21
|
-
} else {
|
|
22
|
-
nextSelection[r.id] = true;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
21
|
+
const anchorRowId = meta?.lastClickedRowId.current;
|
|
22
|
+
const anchorIndex = anchorRowId == null ? null : rows.findIndex((candidate) => candidate.id === anchorRowId);
|
|
23
|
+
const targetSelected = !isSelected;
|
|
24
|
+
if (event.shiftKey && anchorIndex != null && anchorIndex !== -1) {
|
|
25
|
+
toggleRange(rows, anchorIndex, currentIndex, targetSelected);
|
|
26
26
|
} else {
|
|
27
|
-
row.toggleSelected();
|
|
28
|
-
if (row.getIsSelected()) {
|
|
29
|
-
delete nextSelection[row.id];
|
|
30
|
-
} else {
|
|
31
|
-
nextSelection[row.id] = true;
|
|
32
|
-
}
|
|
27
|
+
row.toggleSelected(targetSelected);
|
|
33
28
|
}
|
|
34
29
|
if (meta) {
|
|
35
|
-
meta.
|
|
36
|
-
const selectedRows = rows.filter((r) => nextSelection[r.id]).map((r) => r.original);
|
|
37
|
-
meta.onRowSelected?.(selectedRows);
|
|
30
|
+
meta.lastClickedRowId.current = row.id;
|
|
38
31
|
}
|
|
39
32
|
},
|
|
40
|
-
[table, row, meta]
|
|
41
|
-
);
|
|
42
|
-
const hasPartiallySelectedSubRows = useMemo(
|
|
43
|
-
() => row.subRows.some((subRow) => subRow.getIsSelected()) && !row.getIsSelected(),
|
|
44
|
-
[row]
|
|
33
|
+
[table, row, meta, isSelected]
|
|
45
34
|
);
|
|
46
35
|
return /* @__PURE__ */ jsx(
|
|
47
36
|
Checkbox,
|
|
48
37
|
{
|
|
49
38
|
size: "small",
|
|
50
|
-
checked:
|
|
51
|
-
indeterminate:
|
|
39
|
+
checked: isSelected,
|
|
40
|
+
indeterminate: isIndeterminate,
|
|
52
41
|
disabled: !row.getCanSelect(),
|
|
53
42
|
onClick: handleChange,
|
|
54
43
|
sx: networkModificationTableStyles.selectCheckBox
|
|
@@ -7,7 +7,7 @@ function SelectHeaderCell({ table }) {
|
|
|
7
7
|
if (meta) {
|
|
8
8
|
const nextSelectedRows = table.getIsAllRowsSelected() ? [] : table.getCoreRowModel().rows.map((r) => r.original);
|
|
9
9
|
meta.onRowSelected?.(nextSelectedRows);
|
|
10
|
-
meta.
|
|
10
|
+
meta.lastClickedRowId.current = null;
|
|
11
11
|
}
|
|
12
12
|
table.toggleAllRowsSelected();
|
|
13
13
|
}, [table]);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { Row, RowSelectionState, Updater } from '@tanstack/react-table';
|
|
3
|
+
import { ComposedModificationMetadata } from '../../utils';
|
|
4
|
+
interface UseModificationsSelectionParams {
|
|
5
|
+
modifications: ComposedModificationMetadata[];
|
|
6
|
+
onRowSelected: (selectedRows: ComposedModificationMetadata[]) => void;
|
|
7
|
+
}
|
|
8
|
+
interface UseModificationsSelectionResult {
|
|
9
|
+
rowSelection: RowSelectionState;
|
|
10
|
+
onRowSelectionChange: (updater: Updater<RowSelectionState>) => void;
|
|
11
|
+
lastClickedRowId: RefObject<string | null>;
|
|
12
|
+
emitSelection: (flatRows: Row<ComposedModificationMetadata>[]) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function useModificationsSelection({ modifications, onRowSelected, }: UseModificationsSelectionParams): UseModificationsSelectionResult;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
2
|
+
function normalizeCompositeSelection(rawSelection, roots) {
|
|
3
|
+
const next = { ...rawSelection };
|
|
4
|
+
const visit = (node) => {
|
|
5
|
+
const children = node.subModifications;
|
|
6
|
+
if (!children || children.length === 0) {
|
|
7
|
+
return next[node.uuid];
|
|
8
|
+
}
|
|
9
|
+
const everyChildSelected = children.map((child) => visit(child)).every(Boolean);
|
|
10
|
+
if (everyChildSelected) {
|
|
11
|
+
next[node.uuid] = true;
|
|
12
|
+
} else {
|
|
13
|
+
delete next[node.uuid];
|
|
14
|
+
}
|
|
15
|
+
return everyChildSelected;
|
|
16
|
+
};
|
|
17
|
+
roots.forEach((root) => visit(root));
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
function collectSelection(flatRows) {
|
|
21
|
+
const acc = [];
|
|
22
|
+
let skipBelowDepth = -1;
|
|
23
|
+
flatRows.forEach((row) => {
|
|
24
|
+
if (skipBelowDepth >= 0 && row.depth > skipBelowDepth) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
skipBelowDepth = -1;
|
|
28
|
+
if (row.getIsSelected()) {
|
|
29
|
+
acc.push(row.original);
|
|
30
|
+
skipBelowDepth = row.depth;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return acc;
|
|
34
|
+
}
|
|
35
|
+
function propagateSelectionToLoadedDescendants(selection, roots) {
|
|
36
|
+
let next = selection;
|
|
37
|
+
let mutated = false;
|
|
38
|
+
const visit = (node, ancestorSelected) => {
|
|
39
|
+
const effectiveSelected = ancestorSelected || next[node.uuid];
|
|
40
|
+
if (effectiveSelected && !next[node.uuid]) {
|
|
41
|
+
if (!mutated) {
|
|
42
|
+
next = { ...selection };
|
|
43
|
+
mutated = true;
|
|
44
|
+
}
|
|
45
|
+
next[node.uuid] = true;
|
|
46
|
+
}
|
|
47
|
+
node.subModifications?.forEach((child) => visit(child, effectiveSelected));
|
|
48
|
+
};
|
|
49
|
+
roots.forEach((root) => visit(root, false));
|
|
50
|
+
return mutated ? next : selection;
|
|
51
|
+
}
|
|
52
|
+
function useModificationsSelection({
|
|
53
|
+
modifications,
|
|
54
|
+
onRowSelected
|
|
55
|
+
}) {
|
|
56
|
+
const [rowSelection, setRowSelection] = useState({});
|
|
57
|
+
const lastClickedRowId = useRef(null);
|
|
58
|
+
const onRowSelectionChange = useCallback(
|
|
59
|
+
(updater) => {
|
|
60
|
+
setRowSelection((prev) => {
|
|
61
|
+
const raw = typeof updater === "function" ? updater(prev) : updater;
|
|
62
|
+
return normalizeCompositeSelection(raw, modifications);
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
[modifications]
|
|
66
|
+
);
|
|
67
|
+
const emitSelection = useCallback(
|
|
68
|
+
(flatRows) => {
|
|
69
|
+
onRowSelected(collectSelection(flatRows));
|
|
70
|
+
},
|
|
71
|
+
[onRowSelected]
|
|
72
|
+
);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
setRowSelection((prev) => propagateSelectionToLoadedDescendants(prev, modifications));
|
|
75
|
+
}, [modifications]);
|
|
76
|
+
return { rowSelection, onRowSelectionChange, lastClickedRowId, emitSelection };
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
useModificationsSelection
|
|
80
|
+
};
|