beautiful-grid 1.0.11 → 1.0.13
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 +153 -117
- package/cjs/BGrid.js +193 -39
- package/cjs/components/Table.js +37 -2
- package/cjs/components/TableBodyCell.js +2 -1
- package/cjs/components/TableBodyRow.js +5 -1
- package/cjs/components/TreeCell.js +88 -0
- package/cjs/components/TreeContext.js +42 -0
- package/cjs/utils/exportCsv.js +129 -0
- package/cjs/utils/exportData.js +230 -0
- package/cjs/utils/exportExcel.js +317 -0
- package/cjs/utils/index.js +4 -0
- package/cjs/utils/treeData.js +205 -0
- package/esm/BGrid.js +221 -66
- package/esm/components/Table.js +36 -4
- package/esm/components/TableBodyCell.js +2 -1
- package/esm/components/TableBodyRow.js +5 -1
- package/esm/components/TreeCell.js +49 -0
- package/esm/components/TreeContext.js +5 -0
- package/esm/utils/exportCsv.js +97 -0
- package/esm/utils/exportData.js +154 -0
- package/esm/utils/exportExcel.js +246 -0
- package/esm/utils/index.js +4 -0
- package/esm/utils/treeData.js +174 -0
- package/package.json +1 -1
- package/style.css +125 -43
- package/types/BGrid.d.ts +1 -1
- package/types/components/Table.d.ts +3 -1
- package/types/components/TableBodyCell.d.ts +2 -2
- package/types/components/TreeCell.d.ts +9 -0
- package/types/components/TreeContext.d.ts +16 -0
- package/types/types.d.ts +121 -2
- package/types/utils/exportCsv.d.ts +5 -0
- package/types/utils/exportData.d.ts +16 -0
- package/types/utils/exportExcel.d.ts +5 -0
- package/types/utils/index.d.ts +4 -0
- package/types/utils/treeData.d.ts +22 -0
package/cjs/utils/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __exportStar(require("./number"), exports);
|
|
|
32
32
|
__exportStar(require("./buildHeaderMatrix"), exports);
|
|
33
33
|
__exportStar(require("./getVisibleScrollableRowRange"), exports);
|
|
34
34
|
__exportStar(require("./virtualScrollWindow"), exports);
|
|
35
|
+
__exportStar(require("./treeData"), exports);
|
|
35
36
|
__exportStar(require("./rowHeightMetrics"), exports);
|
|
36
37
|
__exportStar(require("./cellEditState"), exports);
|
|
37
38
|
__exportStar(require("./getCellSelectionAxisState"), exports);
|
|
@@ -42,3 +43,6 @@ __exportStar(require("./rowReorder"), exports);
|
|
|
42
43
|
__exportStar(require("./checkboxEditor"), exports);
|
|
43
44
|
__exportStar(require("./columnVisibility"), exports);
|
|
44
45
|
__exportStar(require("./checkedIndexes"), exports);
|
|
46
|
+
__exportStar(require("./exportData"), exports);
|
|
47
|
+
__exportStar(require("./exportCsv"), exports);
|
|
48
|
+
__exportStar(require("./exportExcel"), exports);
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.projectTreeData = projectTreeData;
|
|
15
|
+
exports.getTreeRowPath = getTreeRowPath;
|
|
16
|
+
var getCellValue_1 = require("./getCellValue");
|
|
17
|
+
function flatFallback(data, diagnostics) {
|
|
18
|
+
var sourceIndexByVisibleIndex = data.map(function (_, index) { return index; });
|
|
19
|
+
return {
|
|
20
|
+
valid: false,
|
|
21
|
+
data: data,
|
|
22
|
+
sourceIndexByVisibleIndex: sourceIndexByVisibleIndex,
|
|
23
|
+
visibleIndexBySourceIndex: new Map(sourceIndexByVisibleIndex.map(function (index) { return [index, index]; })),
|
|
24
|
+
metaByRowKey: new Map(),
|
|
25
|
+
metaBySourceIndex: new Map(),
|
|
26
|
+
diagnostics: Array.from(new Set(diagnostics)),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Projects flat parent-key data into visible pre-order rows without mutating the input. */
|
|
30
|
+
function projectTreeData(params) {
|
|
31
|
+
var e_1, _a;
|
|
32
|
+
var _b;
|
|
33
|
+
var data = params.data, rowKey = params.rowKey, parentRowKey = params.parentRowKey, expandedRowKeys = params.expandedRowKeys, includedSourceIndexes = params.includedSourceIndexes, orderedSourceIndexes = params.orderedSourceIndexes;
|
|
34
|
+
if (data.length === 0) {
|
|
35
|
+
return {
|
|
36
|
+
valid: true,
|
|
37
|
+
data: [],
|
|
38
|
+
sourceIndexByVisibleIndex: [],
|
|
39
|
+
visibleIndexBySourceIndex: new Map(),
|
|
40
|
+
metaByRowKey: new Map(),
|
|
41
|
+
metaBySourceIndex: new Map(),
|
|
42
|
+
diagnostics: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
var diagnostics = [];
|
|
46
|
+
var keys = new Array(data.length);
|
|
47
|
+
var indexByKey = new Map();
|
|
48
|
+
data.forEach(function (item, sourceIndex) {
|
|
49
|
+
var key = (0, getCellValue_1.getCellValueByRowKey)(rowKey, item.values);
|
|
50
|
+
if (key === undefined || key === null) {
|
|
51
|
+
diagnostics.push('missingRowKey');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
keys[sourceIndex] = key;
|
|
55
|
+
if (indexByKey.has(key))
|
|
56
|
+
diagnostics.push('duplicateRowKey');
|
|
57
|
+
else
|
|
58
|
+
indexByKey.set(key, sourceIndex);
|
|
59
|
+
});
|
|
60
|
+
if (diagnostics.length > 0)
|
|
61
|
+
return flatFallback(data, diagnostics);
|
|
62
|
+
var parentIndexes = new Array(data.length);
|
|
63
|
+
var childrenByParentIndex = new Map();
|
|
64
|
+
var roots = [];
|
|
65
|
+
data.forEach(function (item, sourceIndex) {
|
|
66
|
+
var _a;
|
|
67
|
+
var parentKey = (0, getCellValue_1.getCellValueByRowKey)(parentRowKey, item.values);
|
|
68
|
+
var parentIndex = parentKey === undefined || parentKey === null ? undefined : indexByKey.get(parentKey);
|
|
69
|
+
if (parentIndex === undefined) {
|
|
70
|
+
roots.push(sourceIndex);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
parentIndexes[sourceIndex] = parentIndex;
|
|
74
|
+
var children = (_a = childrenByParentIndex.get(parentIndex)) !== null && _a !== void 0 ? _a : [];
|
|
75
|
+
children.push(sourceIndex);
|
|
76
|
+
childrenByParentIndex.set(parentIndex, children);
|
|
77
|
+
});
|
|
78
|
+
// Every node has at most one parent, so iterative parent walking detects all cycles in O(N).
|
|
79
|
+
var visitState = new Uint8Array(data.length);
|
|
80
|
+
for (var start = 0; start < data.length; start++) {
|
|
81
|
+
if (visitState[start] !== 0)
|
|
82
|
+
continue;
|
|
83
|
+
var path = [];
|
|
84
|
+
var current = start;
|
|
85
|
+
while (current !== undefined && visitState[current] === 0) {
|
|
86
|
+
visitState[current] = 1;
|
|
87
|
+
path.push(current);
|
|
88
|
+
current = parentIndexes[current];
|
|
89
|
+
}
|
|
90
|
+
if (current !== undefined && visitState[current] === 1)
|
|
91
|
+
diagnostics.push('cycle');
|
|
92
|
+
path.forEach(function (index) {
|
|
93
|
+
visitState[index] = 2;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (diagnostics.length > 0)
|
|
97
|
+
return flatFallback(data, diagnostics);
|
|
98
|
+
var orderRank = new Map();
|
|
99
|
+
orderedSourceIndexes === null || orderedSourceIndexes === void 0 ? void 0 : orderedSourceIndexes.forEach(function (sourceIndex, rank) { return orderRank.set(sourceIndex, rank); });
|
|
100
|
+
var compareSourceOrder = function (left, right) {
|
|
101
|
+
var _a, _b;
|
|
102
|
+
return ((_a = orderRank.get(left)) !== null && _a !== void 0 ? _a : Number.MAX_SAFE_INTEGER) - ((_b = orderRank.get(right)) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER) ||
|
|
103
|
+
left - right;
|
|
104
|
+
};
|
|
105
|
+
roots.sort(compareSourceOrder);
|
|
106
|
+
childrenByParentIndex.forEach(function (children) { return children.sort(compareSourceOrder); });
|
|
107
|
+
var included = includedSourceIndexes ? new Set(includedSourceIndexes) : undefined;
|
|
108
|
+
var forcedExpanded = new Set();
|
|
109
|
+
if (included) {
|
|
110
|
+
try {
|
|
111
|
+
for (var _c = __values(includedSourceIndexes !== null && includedSourceIndexes !== void 0 ? includedSourceIndexes : []), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
112
|
+
var matchedIndex = _d.value;
|
|
113
|
+
var current = parentIndexes[matchedIndex];
|
|
114
|
+
while (current !== undefined) {
|
|
115
|
+
included.add(current);
|
|
116
|
+
forcedExpanded.add(current);
|
|
117
|
+
current = parentIndexes[current];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
122
|
+
finally {
|
|
123
|
+
try {
|
|
124
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
125
|
+
}
|
|
126
|
+
finally { if (e_1) throw e_1.error; }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
var expanded = new Set(expandedRowKeys);
|
|
130
|
+
var visibleData = [];
|
|
131
|
+
var sourceIndexByVisibleIndex = [];
|
|
132
|
+
var visibleIndexBySourceIndex = new Map();
|
|
133
|
+
var metaByRowKey = new Map();
|
|
134
|
+
var metaBySourceIndex = new Map();
|
|
135
|
+
var stack = roots
|
|
136
|
+
.slice()
|
|
137
|
+
.reverse()
|
|
138
|
+
.map(function (sourceIndex, reverseIndex) { return ({
|
|
139
|
+
sourceIndex: sourceIndex,
|
|
140
|
+
depth: 0,
|
|
141
|
+
visible: true,
|
|
142
|
+
siblingIndex: roots.length - reverseIndex - 1,
|
|
143
|
+
siblingCount: roots.length,
|
|
144
|
+
}); });
|
|
145
|
+
while (stack.length > 0) {
|
|
146
|
+
var entry = stack.pop();
|
|
147
|
+
var sourceIndex = entry.sourceIndex, depth = entry.depth;
|
|
148
|
+
var key = keys[sourceIndex];
|
|
149
|
+
var parentSourceIndex = parentIndexes[sourceIndex];
|
|
150
|
+
var children = (_b = childrenByParentIndex.get(sourceIndex)) !== null && _b !== void 0 ? _b : [];
|
|
151
|
+
var isIncluded = !included || included.has(sourceIndex);
|
|
152
|
+
var isExpanded = expanded.has(key);
|
|
153
|
+
var effectiveExpanded = isExpanded || forcedExpanded.has(sourceIndex);
|
|
154
|
+
var meta = {
|
|
155
|
+
rowKey: key,
|
|
156
|
+
parentRowKey: parentSourceIndex === undefined ? undefined : keys[parentSourceIndex],
|
|
157
|
+
depth: depth,
|
|
158
|
+
hasChildren: children.length > 0,
|
|
159
|
+
expanded: isExpanded,
|
|
160
|
+
sourceIndex: sourceIndex,
|
|
161
|
+
parentSourceIndex: parentSourceIndex,
|
|
162
|
+
siblingIndex: entry.siblingIndex,
|
|
163
|
+
siblingCount: entry.siblingCount,
|
|
164
|
+
};
|
|
165
|
+
metaByRowKey.set(key, meta);
|
|
166
|
+
metaBySourceIndex.set(sourceIndex, meta);
|
|
167
|
+
var visible = entry.visible && isIncluded;
|
|
168
|
+
if (visible) {
|
|
169
|
+
visibleIndexBySourceIndex.set(sourceIndex, visibleData.length);
|
|
170
|
+
visibleData.push(data[sourceIndex]);
|
|
171
|
+
sourceIndexByVisibleIndex.push(sourceIndex);
|
|
172
|
+
}
|
|
173
|
+
// Collapsed subtrees do not need row metadata until they become visible. Skipping
|
|
174
|
+
// them keeps folding work proportional to the visible hierarchy, not all input rows.
|
|
175
|
+
if (!visible || !effectiveExpanded)
|
|
176
|
+
continue;
|
|
177
|
+
for (var childIndex = children.length - 1; childIndex >= 0; childIndex--) {
|
|
178
|
+
stack.push({
|
|
179
|
+
sourceIndex: children[childIndex],
|
|
180
|
+
depth: depth + 1,
|
|
181
|
+
visible: true,
|
|
182
|
+
siblingIndex: childIndex,
|
|
183
|
+
siblingCount: children.length,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
valid: true,
|
|
189
|
+
data: visibleData,
|
|
190
|
+
sourceIndexByVisibleIndex: sourceIndexByVisibleIndex,
|
|
191
|
+
visibleIndexBySourceIndex: visibleIndexBySourceIndex,
|
|
192
|
+
metaByRowKey: metaByRowKey,
|
|
193
|
+
metaBySourceIndex: metaBySourceIndex,
|
|
194
|
+
diagnostics: [],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function getTreeRowPath(metaBySourceIndex, sourceIndex) {
|
|
198
|
+
var path = [];
|
|
199
|
+
var current = metaBySourceIndex.get(sourceIndex);
|
|
200
|
+
while (current) {
|
|
201
|
+
path.push(current.rowKey);
|
|
202
|
+
current = current.parentSourceIndex === undefined ? undefined : metaBySourceIndex.get(current.parentSourceIndex);
|
|
203
|
+
}
|
|
204
|
+
return path.reverse();
|
|
205
|
+
}
|
package/esm/BGrid.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import Table from './components/Table';
|
|
3
|
-
import { createPivotData, createRowHeightMetrics, buildHeaderMatrix, countVisibleCheckedIndexes, findDuplicateColumnIds, getCellValueByRowKey, getColumnId, getColumnKeyToken, getFrozenColumnsWidth, processDataQuery, projectColumnVisibility, resolveStatusOptions, resolvePaginationViewOptions, resolveScrollbarOptions, shouldRenderBottomBar, } from './utils';
|
|
3
|
+
import { createPivotData, createRowHeightMetrics, buildHeaderMatrix, countVisibleCheckedIndexes, findDuplicateColumnIds, getCellValueByRowKey, getColumnId, getColumnKeyToken, getFrozenColumnsWidth, getTreeRowPath, processDataQuery, projectTreeData, projectColumnVisibility, resolveStatusOptions, resolvePaginationViewOptions, resolveScrollbarOptions, shouldRenderBottomBar, } from './utils';
|
|
4
4
|
import { AppStoreProvider } from './store';
|
|
5
|
+
import { TreeContext } from './components/TreeContext';
|
|
5
6
|
function computeModelColumns(columns, frozenColumnIndex, duplicateToolboxColumnIds) {
|
|
6
7
|
let left = 0;
|
|
7
8
|
let previousWidth = 0;
|
|
@@ -21,7 +22,11 @@ function computeModelColumns(columns, frozenColumnIndex, duplicateToolboxColumnI
|
|
|
21
22
|
};
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
|
-
export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, summaryHeight = 30, itemHeight = 15, itemPadding = 7, getRowHeight, columns, columnsGroup, columnGroups, onChangeColumns, frozenColumnIndex = 0, frozenRowCount = 0, data = [], page, scrollbar, status, pagination, bottomBarHeight, scrollTop = 0, scrollLeft = 0, className, style, rowChecked, sort, onClick, loading = false, spinning, disabled, rowKey, selectedRowKey, editable, onChangeData, showLineNumber, msg, getRowClassName, editTrigger = 'dblclick', cellMergeOptions, cellSelectionOptions, cellNavigationOptions, variant, summary, columnSortable, reorder, pivot, dataControl, icons, columnVisibility, searchOptions, contextMenuOptions, }) {
|
|
25
|
+
export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, summaryHeight = 30, itemHeight = 15, itemPadding = 7, getRowHeight, columns, columnsGroup, columnGroups, onChangeColumns, frozenColumnIndex = 0, frozenRowCount = 0, data = [], page, scrollbar, status, pagination, bottomBarHeight, scrollTop = 0, scrollLeft = 0, className, style, rowChecked, sort, onClick, loading = false, spinning, disabled, rowKey, selectedRowKey, editable, onChangeData, showLineNumber, msg, getRowClassName, editTrigger = 'dblclick', cellMergeOptions, cellSelectionOptions, cellNavigationOptions, variant, summary, columnSortable, reorder, pivot, dataControl, icons, columnVisibility, searchOptions, contextMenuOptions, tree, }) {
|
|
26
|
+
const [uncontrolledExpandedRowKeys, setUncontrolledExpandedRowKeys] = React.useState(() => [
|
|
27
|
+
...(tree?.defaultExpandedRowKeys ?? []),
|
|
28
|
+
]);
|
|
29
|
+
const expandedRowKeys = tree?.expandedRowKeys ?? uncontrolledExpandedRowKeys;
|
|
25
30
|
const warnedSearchControlledRef = React.useRef({
|
|
26
31
|
open: false,
|
|
27
32
|
query: false,
|
|
@@ -79,13 +84,15 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
79
84
|
const resolvedDisabled = !!disabled;
|
|
80
85
|
const resolvedOnClick = pivotEnabled || resolvedDisabled ? undefined : onClick;
|
|
81
86
|
const baseOnChangeColumns = pivotEnabled ? undefined : onChangeColumns;
|
|
82
|
-
const
|
|
87
|
+
const baseOnChangeData = pivotEnabled ? undefined : onChangeData;
|
|
83
88
|
const resolvedRowKey = pivotEnabled ? undefined : rowKey;
|
|
84
89
|
const resolvedSelectedRowKey = pivotEnabled ? undefined : selectedRowKey;
|
|
85
90
|
const resolvedEditable = pivotEnabled || resolvedDisabled ? false : editable;
|
|
86
91
|
const resolvedShowLineNumber = pivotEnabled ? false : showLineNumber;
|
|
87
92
|
const resolvedGetRowClassName = pivotEnabled ? undefined : getRowClassName;
|
|
88
|
-
const
|
|
93
|
+
const requestedTreeEnabled = !!tree && tree.enabled !== false;
|
|
94
|
+
const treeEnabled = requestedTreeEnabled && !pivotEnabled && resolvedRowKey !== undefined;
|
|
95
|
+
const resolvedCellMergeOptions = treeEnabled ? undefined : visibilityProjection.cellMergeOptions;
|
|
89
96
|
const resolvedCellSelectionOptions = resolvedDisabled
|
|
90
97
|
? { ...cellSelectionOptions, enabled: false }
|
|
91
98
|
: cellSelectionOptions;
|
|
@@ -97,11 +104,11 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
97
104
|
const resolvedContextMenuOptions = pivotEnabled || resolvedDisabled ? undefined : contextMenuOptions;
|
|
98
105
|
const hasActiveClientQuery = resolvedDataControl?.mode === 'client' &&
|
|
99
106
|
(resolvedDataControl.query.sortParams.length > 0 || resolvedDataControl.query.filterParams.length > 0);
|
|
100
|
-
const resolvedReorder = React.useMemo(() => pivotEnabled || resolvedDisabled
|
|
107
|
+
const resolvedReorder = React.useMemo(() => pivotEnabled || resolvedDisabled || treeEnabled
|
|
101
108
|
? undefined
|
|
102
109
|
: (hasActiveClientQuery || frozenRowCount > 0) && reorder
|
|
103
110
|
? { ...reorder, enabled: false }
|
|
104
|
-
: reorder, [frozenRowCount, hasActiveClientQuery, pivotEnabled, reorder, resolvedDisabled]);
|
|
111
|
+
: reorder, [frozenRowCount, hasActiveClientQuery, pivotEnabled, reorder, resolvedDisabled, treeEnabled]);
|
|
105
112
|
// Development warnings
|
|
106
113
|
React.useEffect(() => {
|
|
107
114
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -130,6 +137,18 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
130
137
|
warnedSearchControlledRef.current.pivotContextMenu = true;
|
|
131
138
|
console.warn('[BGrid] contextMenuOptions is disabled while pivot mode is active.');
|
|
132
139
|
}
|
|
140
|
+
if (requestedTreeEnabled && rowKey === undefined) {
|
|
141
|
+
console.warn('[BGrid] tree requires rowKey. Falling back to flat rows.');
|
|
142
|
+
}
|
|
143
|
+
if (pivotEnabled && requestedTreeEnabled) {
|
|
144
|
+
console.warn('[BGrid] tree is disabled while pivot mode is active.');
|
|
145
|
+
}
|
|
146
|
+
if (treeEnabled && cellMergeOptions) {
|
|
147
|
+
console.warn('[BGrid] cellMergeOptions is disabled while tree mode is active.');
|
|
148
|
+
}
|
|
149
|
+
if (treeEnabled && reorder?.enabled) {
|
|
150
|
+
console.warn('[BGrid] Row reordering is disabled while tree mode is active.');
|
|
151
|
+
}
|
|
133
152
|
if (hasHiddenColumns && columnSortable) {
|
|
134
153
|
console.warn('[BGrid] Column reordering is disabled while columns are hidden.');
|
|
135
154
|
}
|
|
@@ -166,6 +185,10 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
166
185
|
hasHiddenColumns,
|
|
167
186
|
visibilityOptions?.hiddenColumnIds,
|
|
168
187
|
visibilityOptions?.onChange,
|
|
188
|
+
cellMergeOptions,
|
|
189
|
+
requestedTreeEnabled,
|
|
190
|
+
rowKey,
|
|
191
|
+
treeEnabled,
|
|
169
192
|
]);
|
|
170
193
|
const duplicateToolboxColumnIds = React.useMemo(() => {
|
|
171
194
|
const duplicates = findDuplicateColumnIds(resolvedColumns, true);
|
|
@@ -218,9 +241,33 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
218
241
|
resolvedColumns,
|
|
219
242
|
visibilityEnabled,
|
|
220
243
|
]);
|
|
244
|
+
const resolvedTreeColumnId = React.useMemo(() => {
|
|
245
|
+
if (!treeEnabled || visibilityProjection.columns.length === 0)
|
|
246
|
+
return undefined;
|
|
247
|
+
const requestedId = tree?.treeColumnId;
|
|
248
|
+
return requestedId &&
|
|
249
|
+
visibilityProjection.columns.some(column => getColumnId(column) === requestedId)
|
|
250
|
+
? requestedId
|
|
251
|
+
: getColumnId(visibilityProjection.columns[0]);
|
|
252
|
+
}, [tree?.treeColumnId, treeEnabled, visibilityProjection.columns]);
|
|
221
253
|
const computedColumns = React.useMemo(() => {
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
const columns = computeModelColumns(visibilityProjection.columns, resolvedFrozenColumnIndex, duplicateToolboxColumnIds);
|
|
255
|
+
if (!resolvedTreeColumnId)
|
|
256
|
+
return columns;
|
|
257
|
+
return columns.map(column => (column.columnId === resolvedTreeColumnId ? { ...column, treeCell: true } : column));
|
|
258
|
+
}, [duplicateToolboxColumnIds, resolvedFrozenColumnIndex, resolvedTreeColumnId, visibilityProjection.columns]);
|
|
259
|
+
const warnedTreeColumnIdRef = React.useRef(undefined);
|
|
260
|
+
React.useEffect(() => {
|
|
261
|
+
if (process.env.NODE_ENV === 'production' ||
|
|
262
|
+
!treeEnabled ||
|
|
263
|
+
!tree?.treeColumnId ||
|
|
264
|
+
tree.treeColumnId === resolvedTreeColumnId ||
|
|
265
|
+
warnedTreeColumnIdRef.current === tree.treeColumnId) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
warnedTreeColumnIdRef.current = tree.treeColumnId;
|
|
269
|
+
console.warn(`[BGrid] tree.treeColumnId "${tree.treeColumnId}" is not visible. Falling back to the first visible column.`);
|
|
270
|
+
}, [resolvedTreeColumnId, tree?.treeColumnId, treeEnabled]);
|
|
224
271
|
const queryColumns = React.useMemo(() => computeModelColumns(resolvedColumns, pivotEnabled ? 0 : frozenColumnIndex, duplicateToolboxColumnIds), [duplicateToolboxColumnIds, frozenColumnIndex, pivotEnabled, resolvedColumns]);
|
|
225
272
|
const projectedOnChangeColumns = React.useCallback((visibleColumnIndex, info) => {
|
|
226
273
|
if (!baseOnChangeColumns)
|
|
@@ -305,8 +352,9 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
305
352
|
filterParams: [],
|
|
306
353
|
};
|
|
307
354
|
}, [resolvedDataControl, resolvedSort]);
|
|
308
|
-
//
|
|
309
|
-
|
|
355
|
+
// Apply the existing client query first. Tree mode uses its result as sibling order
|
|
356
|
+
// and as the direct-match set, then restores the hierarchy during projection.
|
|
357
|
+
const queryProcessedResult = React.useMemo(() => {
|
|
310
358
|
if (resolvedDataControl?.mode === 'client') {
|
|
311
359
|
return processDataQuery({
|
|
312
360
|
data: resolvedData,
|
|
@@ -323,7 +371,111 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
323
371
|
visibleIndexBySourceIndex: undefined,
|
|
324
372
|
};
|
|
325
373
|
}, [queryColumns, resolvedData, resolvedDataControl?.mode, resolvedDataQuery, resolvedRowKey]);
|
|
374
|
+
const treeProjection = React.useMemo(() => {
|
|
375
|
+
if (!treeEnabled || !tree || resolvedRowKey === undefined)
|
|
376
|
+
return undefined;
|
|
377
|
+
const clientQuery = resolvedDataControl?.mode === 'client';
|
|
378
|
+
return projectTreeData({
|
|
379
|
+
data: resolvedData,
|
|
380
|
+
rowKey: resolvedRowKey,
|
|
381
|
+
parentRowKey: tree.parentRowKey,
|
|
382
|
+
expandedRowKeys,
|
|
383
|
+
includedSourceIndexes: clientQuery && resolvedDataQuery.filterParams.length > 0
|
|
384
|
+
? queryProcessedResult.sourceIndexByVisibleIndex
|
|
385
|
+
: undefined,
|
|
386
|
+
orderedSourceIndexes: clientQuery && resolvedDataQuery.sortParams.length > 0
|
|
387
|
+
? queryProcessedResult.sourceIndexByVisibleIndex
|
|
388
|
+
: undefined,
|
|
389
|
+
});
|
|
390
|
+
}, [
|
|
391
|
+
expandedRowKeys,
|
|
392
|
+
queryProcessedResult.sourceIndexByVisibleIndex,
|
|
393
|
+
resolvedData,
|
|
394
|
+
resolvedDataControl?.mode,
|
|
395
|
+
resolvedDataQuery.filterParams.length,
|
|
396
|
+
resolvedDataQuery.sortParams.length,
|
|
397
|
+
resolvedRowKey,
|
|
398
|
+
tree,
|
|
399
|
+
treeEnabled,
|
|
400
|
+
]);
|
|
401
|
+
React.useEffect(() => {
|
|
402
|
+
if (process.env.NODE_ENV === 'production' || !treeProjection || treeProjection.valid)
|
|
403
|
+
return;
|
|
404
|
+
console.warn(`[BGrid] Invalid tree data (${treeProjection.diagnostics.join(', ')}). Falling back to flat rows.`);
|
|
405
|
+
}, [treeProjection]);
|
|
406
|
+
const resolvedTreeProjection = treeProjection?.valid ? treeProjection : undefined;
|
|
407
|
+
const processedResult = resolvedTreeProjection
|
|
408
|
+
? {
|
|
409
|
+
rows: [],
|
|
410
|
+
data: resolvedTreeProjection.data,
|
|
411
|
+
sourceIndexByVisibleIndex: resolvedTreeProjection.sourceIndexByVisibleIndex,
|
|
412
|
+
visibleIndexBySourceIndex: resolvedTreeProjection.visibleIndexBySourceIndex,
|
|
413
|
+
}
|
|
414
|
+
: queryProcessedResult;
|
|
326
415
|
const displayData = processedResult.data;
|
|
416
|
+
const resolvedOnChangeData = React.useMemo(() => {
|
|
417
|
+
if (!baseOnChangeData)
|
|
418
|
+
return undefined;
|
|
419
|
+
if (!resolvedTreeProjection)
|
|
420
|
+
return baseOnChangeData;
|
|
421
|
+
return (index, columnIndex, item, column, meta) => {
|
|
422
|
+
const treeMeta = resolvedTreeProjection.metaBySourceIndex.get(index);
|
|
423
|
+
if (!meta || !treeMeta) {
|
|
424
|
+
baseOnChangeData(index, columnIndex, item, column, meta);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
baseOnChangeData(index, columnIndex, item, column, {
|
|
428
|
+
...meta,
|
|
429
|
+
tree: {
|
|
430
|
+
rowKey: treeMeta.rowKey,
|
|
431
|
+
parentRowKey: treeMeta.parentRowKey,
|
|
432
|
+
sourceIndex: treeMeta.sourceIndex,
|
|
433
|
+
depth: treeMeta.depth,
|
|
434
|
+
path: getTreeRowPath(resolvedTreeProjection.metaBySourceIndex, index),
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
}, [baseOnChangeData, resolvedTreeProjection]);
|
|
439
|
+
const toggleTreeRow = React.useCallback((meta) => {
|
|
440
|
+
if (resolvedDisabled || !tree || !meta.hasChildren)
|
|
441
|
+
return;
|
|
442
|
+
const nextExpanded = meta.expanded
|
|
443
|
+
? expandedRowKeys.filter(key => !Object.is(key, meta.rowKey))
|
|
444
|
+
: expandedRowKeys.some(key => Object.is(key, meta.rowKey))
|
|
445
|
+
? [...expandedRowKeys]
|
|
446
|
+
: [...expandedRowKeys, meta.rowKey];
|
|
447
|
+
if (tree.expandedRowKeys === undefined)
|
|
448
|
+
setUncontrolledExpandedRowKeys(nextExpanded);
|
|
449
|
+
const item = resolvedData[meta.sourceIndex];
|
|
450
|
+
if (item) {
|
|
451
|
+
tree.onExpandedRowKeysChange?.(nextExpanded, {
|
|
452
|
+
rowKey: meta.rowKey,
|
|
453
|
+
expanded: !meta.expanded,
|
|
454
|
+
item,
|
|
455
|
+
sourceIndex: meta.sourceIndex,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}, [expandedRowKeys, resolvedData, resolvedDisabled, tree]);
|
|
459
|
+
const treeContextValue = React.useMemo(() => {
|
|
460
|
+
if (!resolvedTreeProjection || !resolvedTreeColumnId || resolvedRowKey === undefined || !tree)
|
|
461
|
+
return undefined;
|
|
462
|
+
const indentSize = Number.isFinite(tree.indentSize) && (tree.indentSize ?? -1) >= 0 ? tree.indentSize : 16;
|
|
463
|
+
return {
|
|
464
|
+
treeColumnId: resolvedTreeColumnId,
|
|
465
|
+
indentSize,
|
|
466
|
+
icons: tree.icons,
|
|
467
|
+
expandAriaLabel: tree.expandAriaLabel ?? 'Expand row',
|
|
468
|
+
collapseAriaLabel: tree.collapseAriaLabel ?? 'Collapse row',
|
|
469
|
+
disabled: resolvedDisabled,
|
|
470
|
+
metaByRowKey: resolvedTreeProjection.metaByRowKey,
|
|
471
|
+
metaBySourceIndex: resolvedTreeProjection.metaBySourceIndex,
|
|
472
|
+
getRowKey: values => {
|
|
473
|
+
const key = getCellValueByRowKey(resolvedRowKey, values);
|
|
474
|
+
return key === undefined || key === null ? undefined : key;
|
|
475
|
+
},
|
|
476
|
+
toggle: toggleTreeRow,
|
|
477
|
+
};
|
|
478
|
+
}, [resolvedDisabled, resolvedRowKey, resolvedTreeColumnId, resolvedTreeProjection, toggleTreeRow, tree]);
|
|
327
479
|
const rowHeightMetrics = React.useMemo(() => createRowHeightMetrics(displayData, itemHeight + itemPadding * 2, getRowHeight), [displayData, getRowHeight, itemHeight, itemPadding]);
|
|
328
480
|
const resolvedFrozenRowCount = pivotEnabled
|
|
329
481
|
? 0
|
|
@@ -532,60 +684,63 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
532
684
|
initialShowBottomBar,
|
|
533
685
|
]);
|
|
534
686
|
return (React.createElement(AppStoreProvider, { initialState: initialStoreState },
|
|
535
|
-
React.createElement(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
687
|
+
React.createElement(TreeContext.Provider, { value: treeContextValue },
|
|
688
|
+
React.createElement(Table, { ref: ref, sourceColumns: resolvedColumns,
|
|
689
|
+
columns: computedColumns,
|
|
690
|
+
columnsGroup: visibilityProjection.columnsGroup,
|
|
691
|
+
columnGroups: visibilityProjection.columnGroups,
|
|
692
|
+
onChangeColumns: resolvedOnChangeColumns,
|
|
693
|
+
width,
|
|
694
|
+
height,
|
|
695
|
+
className,
|
|
696
|
+
style,
|
|
697
|
+
loading,
|
|
698
|
+
disabled: resolvedDisabled,
|
|
699
|
+
spinning,
|
|
700
|
+
scrollLeft,
|
|
701
|
+
scrollTop,
|
|
702
|
+
headerHeight,
|
|
703
|
+
footerHeight,
|
|
704
|
+
bottomBarHeight: resolvedBottomBarHeight,
|
|
705
|
+
scrollbar: resolvedScrollbar,
|
|
706
|
+
status: resolvedStatus,
|
|
707
|
+
pagination: resolvedPagination,
|
|
708
|
+
summaryHeight,
|
|
709
|
+
itemHeight,
|
|
710
|
+
itemPadding,
|
|
711
|
+
rowHeightMetrics,
|
|
712
|
+
frozenColumnIndex: resolvedFrozenColumnIndex,
|
|
713
|
+
frozenRowCount: resolvedFrozenRowCount,
|
|
714
|
+
rowChecked: resolvedRowChecked,
|
|
715
|
+
checkedIndexesMap,
|
|
716
|
+
sort: resolvedSort,
|
|
717
|
+
sortParams,
|
|
718
|
+
dataQuery: resolvedDataQuery,
|
|
719
|
+
dataControl: resolvedDataControl,
|
|
720
|
+
icons,
|
|
721
|
+
columnVisibilityState,
|
|
722
|
+
searchOptions: resolvedSearchOptions,
|
|
723
|
+
contextMenuOptions: resolvedContextMenuOptions,
|
|
724
|
+
page: resolvedPage,
|
|
725
|
+
data: displayData,
|
|
726
|
+
sourceData: resolvedData,
|
|
727
|
+
sourceIndexByVisibleIndex: processedResult.sourceIndexByVisibleIndex,
|
|
728
|
+
visibleIndexBySourceIndex: processedResult.visibleIndexBySourceIndex,
|
|
729
|
+
onClick: resolvedOnClick,
|
|
730
|
+
rowKey: resolvedRowKey,
|
|
731
|
+
selectedRowKey: resolvedSelectedRowKey,
|
|
732
|
+
editable: resolvedEditable,
|
|
733
|
+
editTrigger,
|
|
734
|
+
onChangeData: resolvedOnChangeData,
|
|
735
|
+
showLineNumber: resolvedShowLineNumber,
|
|
736
|
+
msg,
|
|
737
|
+
getRowClassName: resolvedGetRowClassName,
|
|
738
|
+
cellMergeOptions: resolvedCellMergeOptions,
|
|
739
|
+
cellSelectionOptions: resolvedCellSelectionOptions,
|
|
740
|
+
cellNavigationOptions,
|
|
741
|
+
variant,
|
|
742
|
+
summary: resolvedSummary,
|
|
743
|
+
columnSortable: resolvedColumnSortable,
|
|
744
|
+
reorder: resolvedReorder,
|
|
745
|
+
treeEnabled: !!treeContextValue }))));
|
|
591
746
|
}
|
package/esm/components/Table.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useCallback, useEffect, useRef } from 'react';
|
|
3
3
|
import { useShallow } from 'zustand/react/shallow';
|
|
4
|
-
import { useAppStore } from '../store';
|
|
4
|
+
import { useAppStore, useAppStoreApi } from '../store';
|
|
5
5
|
import { BGridDataItemStatus, } from '../types';
|
|
6
|
-
import { getCellValueByRowKey, getFrozenColumnsWidth, getRowIndexAtOffset, getVisibleScrollableRowRange, getVirtualScrollWindowMetrics, getVirtualScrollWindowPosition, isCheckboxValueChecked, markCellEdited, markCellValueChanged, rebaseVirtualScrollWindow, resolveLogicalCell, shouldRenderBottomBar, } from '../utils';
|
|
6
|
+
import { getCellValueByRowKey, getFrozenColumnsWidth, getRowIndexAtOffset, getVisibleScrollableRowRange, getVirtualScrollWindowMetrics, getVirtualScrollWindowPosition, isCheckboxValueChecked, markCellEdited, markCellValueChanged, rebaseVirtualScrollWindow, resolveLogicalCell, shouldRenderBottomBar, createGridExportData, serializeGridExportDataToCsv, downloadCsv, } from '../utils';
|
|
7
7
|
import { clamp, ensureCellVisible } from '../utils/coordinate';
|
|
8
8
|
import Loading from './Loading';
|
|
9
9
|
import TableBody, { createRowKeyRegistry } from './TableBody';
|
|
@@ -29,6 +29,7 @@ const LazyGridOptionalSurfaces = React.lazy(() => import('./GridOptionalSurfaces
|
|
|
29
29
|
default: module.GridOptionalSurfaces,
|
|
30
30
|
})));
|
|
31
31
|
function Table(props) {
|
|
32
|
+
const store = useAppStoreApi();
|
|
32
33
|
const { cellSelectionOptions, onChangeData, sourceIndexByVisibleIndex } = props;
|
|
33
34
|
const cellSelectionEnabled = cellSelectionOptions?.enabled ?? true;
|
|
34
35
|
const rowKeyRegistryState = useRef({
|
|
@@ -507,11 +508,42 @@ function Table(props) {
|
|
|
507
508
|
const scrollPropsRef = useRef({ top: props.scrollTop, left: props.scrollLeft });
|
|
508
509
|
const syncedDataPropRef = useRef(props.data);
|
|
509
510
|
const [rowScrollRequest, setRowScrollRequest] = React.useState(null);
|
|
511
|
+
const sourceColumnsRef = useRef(props.sourceColumns);
|
|
512
|
+
sourceColumnsRef.current = props.sourceColumns;
|
|
513
|
+
const getCurrentExportData = useCallback((options) => {
|
|
514
|
+
const state = store.getState();
|
|
515
|
+
return createGridExportData({
|
|
516
|
+
options,
|
|
517
|
+
columns: state.columns,
|
|
518
|
+
sourceColumns: sourceColumnsRef.current,
|
|
519
|
+
data: state.data,
|
|
520
|
+
sourceData: state.sourceData,
|
|
521
|
+
sourceIndexByVisibleIndex: state.sourceIndexByVisibleIndex,
|
|
522
|
+
visibleIndexBySourceIndex: state.visibleIndexBySourceIndex,
|
|
523
|
+
checkedIndexesMap: state.checkedIndexesMap,
|
|
524
|
+
rowKey: state.rowKey,
|
|
525
|
+
});
|
|
526
|
+
}, [store]);
|
|
510
527
|
React.useImperativeHandle(props.ref, () => ({
|
|
511
528
|
scrollToRow(rowIndex, options) {
|
|
512
529
|
setRowScrollRequest({ rowIndex, align: options?.align ?? 'nearest' });
|
|
513
530
|
},
|
|
514
|
-
|
|
531
|
+
getExportData(options) {
|
|
532
|
+
return getCurrentExportData(options);
|
|
533
|
+
},
|
|
534
|
+
exportCsv(options) {
|
|
535
|
+
const exportData = getCurrentExportData(options);
|
|
536
|
+
const csv = serializeGridExportDataToCsv(exportData, options);
|
|
537
|
+
downloadCsv(csv, options?.fileName ?? 'export.csv');
|
|
538
|
+
},
|
|
539
|
+
exportExcel(options) {
|
|
540
|
+
const exportData = getCurrentExportData(options);
|
|
541
|
+
void import('../utils/exportExcel').then(({ serializeGridExportDataToExcel, downloadExcel }) => {
|
|
542
|
+
const excelBytes = serializeGridExportDataToExcel(exportData, options);
|
|
543
|
+
downloadExcel(excelBytes, options?.fileName ?? 'export.xlsx');
|
|
544
|
+
});
|
|
545
|
+
},
|
|
546
|
+
}), [getCurrentExportData]);
|
|
515
547
|
const scrollRafRef = useRef(null);
|
|
516
548
|
const scrollIdleTimerRef = useRef(null);
|
|
517
549
|
const selectionAutoScrollRafRef = useRef(null);
|
|
@@ -2151,7 +2183,7 @@ function Table(props) {
|
|
|
2151
2183
|
};
|
|
2152
2184
|
}, []);
|
|
2153
2185
|
return (React.createElement(EditorPortalContext.Provider, { value: editorPortalContext },
|
|
2154
|
-
React.createElement(Container, { ref: containerRef, role: 'grid', style: {
|
|
2186
|
+
React.createElement(Container, { ref: containerRef, role: props.treeEnabled ? 'treegrid' : 'grid', style: {
|
|
2155
2187
|
...style,
|
|
2156
2188
|
width,
|
|
2157
2189
|
height,
|