beautiful-grid 1.0.13 → 1.0.15
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 +121 -6
- package/cjs/BGrid.js +388 -44
- package/cjs/components/CellNavigationDomSync.js +8 -0
- package/cjs/components/CellTextEditorGateway.js +13 -9
- package/cjs/components/ColResizer.js +13 -7
- package/cjs/components/MasterDetailCell.js +145 -0
- package/cjs/components/MasterDetailContext.js +42 -0
- package/cjs/components/Table.js +162 -52
- package/cjs/components/TableBody.js +7 -3
- package/cjs/components/TableBodyCell.js +2 -1
- package/cjs/components/TableSummary.js +25 -34
- package/cjs/components/TableSummaryFronzen.js +36 -34
- package/cjs/store/createAppStore.js +2 -0
- package/cjs/utils/cellSelectionGeometry.js +32 -6
- package/cjs/utils/columnVisibility.js +17 -14
- package/cjs/utils/index.js +1 -0
- package/cjs/utils/rowHeightMetrics.js +47 -6
- package/cjs/utils/summary.js +55 -0
- package/cjs/utils/useRowReorderController.js +5 -1
- package/esm/BGrid.js +387 -72
- package/esm/components/CellNavigationDomSync.js +8 -0
- package/esm/components/CellTextEditorGateway.js +12 -9
- package/esm/components/ColResizer.js +8 -2
- package/esm/components/MasterDetailCell.js +101 -0
- package/esm/components/MasterDetailContext.js +5 -0
- package/esm/components/Table.js +151 -42
- package/esm/components/TableBody.js +6 -2
- package/esm/components/TableBodyCell.js +2 -1
- package/esm/components/TableSummary.js +26 -29
- package/esm/components/TableSummaryFronzen.js +26 -29
- package/esm/store/createAppStore.js +2 -0
- package/esm/utils/cellSelectionGeometry.js +31 -5
- package/esm/utils/columnVisibility.js +21 -18
- package/esm/utils/index.js +1 -0
- package/esm/utils/rowHeightMetrics.js +46 -6
- package/esm/utils/summary.js +35 -0
- package/esm/utils/useRowReorderController.js +5 -1
- package/package.json +1 -1
- package/style.css +164 -14
- package/types/BGrid.d.ts +1 -1
- package/types/components/MasterDetailCell.d.ts +21 -0
- package/types/components/MasterDetailContext.d.ts +20 -0
- package/types/components/Table.d.ts +1 -0
- package/types/components/TableBody.d.ts +2 -1
- package/types/store/createAppStore.d.ts +1 -1
- package/types/types.d.ts +70 -4
- package/types/utils/cellSelectionGeometry.d.ts +3 -1
- package/types/utils/index.d.ts +1 -0
- package/types/utils/rowHeightMetrics.d.ts +15 -1
- package/types/utils/summary.d.ts +13 -0
package/esm/BGrid.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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, getTreeRowPath, processDataQuery, projectTreeData, 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, computeSummaryHeight, DEFAULT_SUMMARY_ROW_HEIGHT, shouldRenderBottomBar, } from './utils';
|
|
4
4
|
import { AppStoreProvider } from './store';
|
|
5
5
|
import { TreeContext } from './components/TreeContext';
|
|
6
|
+
import { MasterDetailContext } from './components/MasterDetailContext';
|
|
6
7
|
function computeModelColumns(columns, frozenColumnIndex, duplicateToolboxColumnIds) {
|
|
7
8
|
let left = 0;
|
|
8
9
|
let previousWidth = 0;
|
|
@@ -22,11 +23,19 @@ function computeModelColumns(columns, frozenColumnIndex, duplicateToolboxColumnI
|
|
|
22
23
|
};
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
function encodeMasterDetailRowKey(rowKey) {
|
|
27
|
+
const codePoints = Array.from(String(rowKey), character => character.codePointAt(0).toString(16));
|
|
28
|
+
return `${typeof rowKey}-${codePoints.join('-')}`;
|
|
29
|
+
}
|
|
30
|
+
export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, summaryHeight: explicitSummaryHeight, summaryRowHeight = DEFAULT_SUMMARY_ROW_HEIGHT, 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, masterDetail, }) {
|
|
26
31
|
const [uncontrolledExpandedRowKeys, setUncontrolledExpandedRowKeys] = React.useState(() => [
|
|
27
32
|
...(tree?.defaultExpandedRowKeys ?? []),
|
|
28
33
|
]);
|
|
29
34
|
const expandedRowKeys = tree?.expandedRowKeys ?? uncontrolledExpandedRowKeys;
|
|
35
|
+
const [uncontrolledMasterDetailExpandedKeys, setUncontrolledMasterDetailExpandedKeys] = React.useState(() => masterDetail?.defaultExpandedRowKeys ?? []);
|
|
36
|
+
const masterDetailExpandedKeys = masterDetail?.expandedRowKeys !== undefined
|
|
37
|
+
? masterDetail.expandedRowKeys
|
|
38
|
+
: uncontrolledMasterDetailExpandedKeys;
|
|
30
39
|
const warnedSearchControlledRef = React.useRef({
|
|
31
40
|
open: false,
|
|
32
41
|
query: false,
|
|
@@ -92,7 +101,13 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
92
101
|
const resolvedGetRowClassName = pivotEnabled ? undefined : getRowClassName;
|
|
93
102
|
const requestedTreeEnabled = !!tree && tree.enabled !== false;
|
|
94
103
|
const treeEnabled = requestedTreeEnabled && !pivotEnabled && resolvedRowKey !== undefined;
|
|
95
|
-
const
|
|
104
|
+
const requestedMasterDetailEnabled = !!masterDetail && masterDetail.enabled !== false;
|
|
105
|
+
const masterDetailEnabled = requestedMasterDetailEnabled &&
|
|
106
|
+
!pivotEnabled &&
|
|
107
|
+
!treeEnabled &&
|
|
108
|
+
resolvedRowKey !== undefined &&
|
|
109
|
+
frozenRowCount <= 0;
|
|
110
|
+
const resolvedCellMergeOptions = treeEnabled || masterDetailEnabled ? undefined : visibilityProjection.cellMergeOptions;
|
|
96
111
|
const resolvedCellSelectionOptions = resolvedDisabled
|
|
97
112
|
? { ...cellSelectionOptions, enabled: false }
|
|
98
113
|
: cellSelectionOptions;
|
|
@@ -104,11 +119,11 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
104
119
|
const resolvedContextMenuOptions = pivotEnabled || resolvedDisabled ? undefined : contextMenuOptions;
|
|
105
120
|
const hasActiveClientQuery = resolvedDataControl?.mode === 'client' &&
|
|
106
121
|
(resolvedDataControl.query.sortParams.length > 0 || resolvedDataControl.query.filterParams.length > 0);
|
|
107
|
-
const resolvedReorder = React.useMemo(() => pivotEnabled || resolvedDisabled || treeEnabled
|
|
122
|
+
const resolvedReorder = React.useMemo(() => pivotEnabled || resolvedDisabled || treeEnabled || masterDetailEnabled
|
|
108
123
|
? undefined
|
|
109
124
|
: (hasActiveClientQuery || frozenRowCount > 0) && reorder
|
|
110
125
|
? { ...reorder, enabled: false }
|
|
111
|
-
: reorder, [frozenRowCount, hasActiveClientQuery, pivotEnabled, reorder, resolvedDisabled, treeEnabled]);
|
|
126
|
+
: reorder, [frozenRowCount, hasActiveClientQuery, masterDetailEnabled, pivotEnabled, reorder, resolvedDisabled, treeEnabled]);
|
|
112
127
|
// Development warnings
|
|
113
128
|
React.useEffect(() => {
|
|
114
129
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -149,6 +164,24 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
149
164
|
if (treeEnabled && reorder?.enabled) {
|
|
150
165
|
console.warn('[BGrid] Row reordering is disabled while tree mode is active.');
|
|
151
166
|
}
|
|
167
|
+
if (requestedMasterDetailEnabled && rowKey === undefined) {
|
|
168
|
+
console.warn('[BGrid] masterDetail requires rowKey. Falling back to flat rows.');
|
|
169
|
+
}
|
|
170
|
+
if (pivotEnabled && requestedMasterDetailEnabled) {
|
|
171
|
+
console.warn('[BGrid] masterDetail is disabled while pivot mode is active.');
|
|
172
|
+
}
|
|
173
|
+
if (treeEnabled && requestedMasterDetailEnabled) {
|
|
174
|
+
console.warn('[BGrid] masterDetail is disabled while tree mode is active.');
|
|
175
|
+
}
|
|
176
|
+
if (masterDetailEnabled && cellMergeOptions) {
|
|
177
|
+
console.warn('[BGrid] cellMergeOptions is disabled while masterDetail mode is active.');
|
|
178
|
+
}
|
|
179
|
+
if (masterDetailEnabled && reorder?.enabled) {
|
|
180
|
+
console.warn('[BGrid] Row reordering is disabled while masterDetail mode is active.');
|
|
181
|
+
}
|
|
182
|
+
if (requestedMasterDetailEnabled && frozenRowCount > 0) {
|
|
183
|
+
console.warn('[BGrid] masterDetail is disabled when frozenRowCount > 0.');
|
|
184
|
+
}
|
|
152
185
|
if (hasHiddenColumns && columnSortable) {
|
|
153
186
|
console.warn('[BGrid] Column reordering is disabled while columns are hidden.');
|
|
154
187
|
}
|
|
@@ -186,6 +219,8 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
186
219
|
visibilityOptions?.hiddenColumnIds,
|
|
187
220
|
visibilityOptions?.onChange,
|
|
188
221
|
cellMergeOptions,
|
|
222
|
+
masterDetailEnabled,
|
|
223
|
+
requestedMasterDetailEnabled,
|
|
189
224
|
requestedTreeEnabled,
|
|
190
225
|
rowKey,
|
|
191
226
|
treeEnabled,
|
|
@@ -250,12 +285,31 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
250
285
|
? requestedId
|
|
251
286
|
: getColumnId(visibilityProjection.columns[0]);
|
|
252
287
|
}, [tree?.treeColumnId, treeEnabled, visibilityProjection.columns]);
|
|
288
|
+
const resolvedExpandColumnId = React.useMemo(() => {
|
|
289
|
+
if (!masterDetailEnabled || visibilityProjection.columns.length === 0)
|
|
290
|
+
return undefined;
|
|
291
|
+
const requestedId = masterDetail?.expandColumnId;
|
|
292
|
+
return requestedId &&
|
|
293
|
+
visibilityProjection.columns.some(column => getColumnId(column) === requestedId)
|
|
294
|
+
? requestedId
|
|
295
|
+
: getColumnId(visibilityProjection.columns[0]);
|
|
296
|
+
}, [masterDetail?.expandColumnId, masterDetailEnabled, visibilityProjection.columns]);
|
|
253
297
|
const computedColumns = React.useMemo(() => {
|
|
254
|
-
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
298
|
+
let columns = computeModelColumns(visibilityProjection.columns, resolvedFrozenColumnIndex, duplicateToolboxColumnIds);
|
|
299
|
+
if (resolvedTreeColumnId) {
|
|
300
|
+
columns = columns.map(column => (column.columnId === resolvedTreeColumnId ? { ...column, treeCell: true } : column));
|
|
301
|
+
}
|
|
302
|
+
if (resolvedExpandColumnId) {
|
|
303
|
+
columns = columns.map(column => column.columnId === resolvedExpandColumnId ? { ...column, masterDetailCell: true } : column);
|
|
304
|
+
}
|
|
305
|
+
return columns;
|
|
306
|
+
}, [
|
|
307
|
+
duplicateToolboxColumnIds,
|
|
308
|
+
resolvedExpandColumnId,
|
|
309
|
+
resolvedFrozenColumnIndex,
|
|
310
|
+
resolvedTreeColumnId,
|
|
311
|
+
visibilityProjection.columns,
|
|
312
|
+
]);
|
|
259
313
|
const warnedTreeColumnIdRef = React.useRef(undefined);
|
|
260
314
|
React.useEffect(() => {
|
|
261
315
|
if (process.env.NODE_ENV === 'production' ||
|
|
@@ -268,6 +322,18 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
268
322
|
warnedTreeColumnIdRef.current = tree.treeColumnId;
|
|
269
323
|
console.warn(`[BGrid] tree.treeColumnId "${tree.treeColumnId}" is not visible. Falling back to the first visible column.`);
|
|
270
324
|
}, [resolvedTreeColumnId, tree?.treeColumnId, treeEnabled]);
|
|
325
|
+
const warnedExpandColumnIdRef = React.useRef(undefined);
|
|
326
|
+
React.useEffect(() => {
|
|
327
|
+
if (process.env.NODE_ENV === 'production' ||
|
|
328
|
+
!masterDetailEnabled ||
|
|
329
|
+
!masterDetail?.expandColumnId ||
|
|
330
|
+
masterDetail.expandColumnId === resolvedExpandColumnId ||
|
|
331
|
+
warnedExpandColumnIdRef.current === masterDetail.expandColumnId) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
warnedExpandColumnIdRef.current = masterDetail.expandColumnId;
|
|
335
|
+
console.warn(`[BGrid] masterDetail.expandColumnId "${masterDetail.expandColumnId}" is not visible. Falling back to the first visible column.`);
|
|
336
|
+
}, [masterDetail?.expandColumnId, masterDetailEnabled, resolvedExpandColumnId]);
|
|
271
337
|
const queryColumns = React.useMemo(() => computeModelColumns(resolvedColumns, pivotEnabled ? 0 : frozenColumnIndex, duplicateToolboxColumnIds), [duplicateToolboxColumnIds, frozenColumnIndex, pivotEnabled, resolvedColumns]);
|
|
272
338
|
const projectedOnChangeColumns = React.useCallback((visibleColumnIndex, info) => {
|
|
273
339
|
if (!baseOnChangeColumns)
|
|
@@ -476,7 +542,247 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
476
542
|
toggle: toggleTreeRow,
|
|
477
543
|
};
|
|
478
544
|
}, [resolvedDisabled, resolvedRowKey, resolvedTreeColumnId, resolvedTreeProjection, toggleTreeRow, tree]);
|
|
479
|
-
const
|
|
545
|
+
const rawGridId = React.useId();
|
|
546
|
+
const gridInstanceId = React.useMemo(() => rawGridId.replace(/[^a-zA-Z0-9_-]/g, '_'), [rawGridId]);
|
|
547
|
+
const masterDetailRowKeysMeta = React.useMemo(() => {
|
|
548
|
+
if (!masterDetailEnabled || resolvedRowKey === undefined)
|
|
549
|
+
return undefined;
|
|
550
|
+
const validKeys = new Set();
|
|
551
|
+
const rowsByKey = new Map();
|
|
552
|
+
const duplicateKeys = new Set();
|
|
553
|
+
const missingKeyIndexes = [];
|
|
554
|
+
for (let i = 0; i < resolvedData.length; i++) {
|
|
555
|
+
const key = getCellValueByRowKey(resolvedRowKey, resolvedData[i].values);
|
|
556
|
+
if (key === undefined || key === null) {
|
|
557
|
+
missingKeyIndexes.push(i);
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
const k = key;
|
|
561
|
+
if (validKeys.has(k)) {
|
|
562
|
+
duplicateKeys.add(k);
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
validKeys.add(k);
|
|
566
|
+
rowsByKey.set(k, { item: resolvedData[i], sourceIndex: i });
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return { validKeys, rowsByKey, duplicateKeys, missingKeyIndexes };
|
|
571
|
+
}, [masterDetailEnabled, resolvedData, resolvedRowKey]);
|
|
572
|
+
React.useEffect(() => {
|
|
573
|
+
if (process.env.NODE_ENV === 'production' || !masterDetailRowKeysMeta)
|
|
574
|
+
return;
|
|
575
|
+
if (masterDetailRowKeysMeta.missingKeyIndexes.length > 0) {
|
|
576
|
+
console.warn(`[BGrid] masterDetail found ${masterDetailRowKeysMeta.missingKeyIndexes.length} rows with missing or null rowKey at indexes: [${masterDetailRowKeysMeta.missingKeyIndexes.slice(0, 5).join(', ')}${masterDetailRowKeysMeta.missingKeyIndexes.length > 5 ? ', ...' : ''}]. These rows cannot be expanded.`);
|
|
577
|
+
}
|
|
578
|
+
if (masterDetailRowKeysMeta.duplicateKeys.size > 0) {
|
|
579
|
+
console.warn(`[BGrid] masterDetail found duplicate rowKey values: [${Array.from(masterDetailRowKeysMeta.duplicateKeys).slice(0, 5).join(', ')}]. These rows cannot be expanded.`);
|
|
580
|
+
}
|
|
581
|
+
}, [masterDetailRowKeysMeta]);
|
|
582
|
+
const expandMode = masterDetail?.expandMode ?? 'multiple';
|
|
583
|
+
const masterDetailHasDetail = masterDetail?.hasDetail;
|
|
584
|
+
const effectiveMasterDetailExpandedKeysSet = React.useMemo(() => {
|
|
585
|
+
if (!masterDetailEnabled || !masterDetailRowKeysMeta)
|
|
586
|
+
return new Set();
|
|
587
|
+
const set = new Set();
|
|
588
|
+
for (const key of masterDetailExpandedKeys) {
|
|
589
|
+
if (masterDetailRowKeysMeta.duplicateKeys.has(key))
|
|
590
|
+
continue;
|
|
591
|
+
const row = masterDetailRowKeysMeta.rowsByKey.get(key);
|
|
592
|
+
if (!row)
|
|
593
|
+
continue;
|
|
594
|
+
if (masterDetailHasDetail) {
|
|
595
|
+
try {
|
|
596
|
+
if (!masterDetailHasDetail(row.item, row.sourceIndex))
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
catch {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
set.add(key);
|
|
604
|
+
if (expandMode === 'single')
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
return set;
|
|
608
|
+
}, [expandMode, masterDetailHasDetail, masterDetailEnabled, masterDetailExpandedKeys, masterDetailRowKeysMeta]);
|
|
609
|
+
const toggleMasterDetailRow = React.useCallback((rowKey, item, sourceIndex) => {
|
|
610
|
+
if (resolvedDisabled || !masterDetailEnabled || !masterDetail)
|
|
611
|
+
return;
|
|
612
|
+
const isExpanded = effectiveMasterDetailExpandedKeysSet.has(rowKey);
|
|
613
|
+
let nextKeys;
|
|
614
|
+
if (expandMode === 'single') {
|
|
615
|
+
nextKeys = isExpanded ? [] : [rowKey];
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
if (isExpanded) {
|
|
619
|
+
nextKeys = masterDetailExpandedKeys.filter(k => !Object.is(k, rowKey));
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
const exists = masterDetailExpandedKeys.some(k => Object.is(k, rowKey));
|
|
623
|
+
nextKeys = exists ? [...masterDetailExpandedKeys] : [...masterDetailExpandedKeys, rowKey];
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (masterDetail.expandedRowKeys === undefined) {
|
|
627
|
+
setUncontrolledMasterDetailExpandedKeys(nextKeys);
|
|
628
|
+
}
|
|
629
|
+
masterDetail.onExpandedRowKeysChange?.(nextKeys, {
|
|
630
|
+
rowKey,
|
|
631
|
+
expanded: !isExpanded,
|
|
632
|
+
item,
|
|
633
|
+
sourceIndex,
|
|
634
|
+
});
|
|
635
|
+
}, [
|
|
636
|
+
effectiveMasterDetailExpandedKeysSet,
|
|
637
|
+
expandMode,
|
|
638
|
+
masterDetail,
|
|
639
|
+
masterDetailEnabled,
|
|
640
|
+
masterDetailExpandedKeys,
|
|
641
|
+
resolvedDisabled,
|
|
642
|
+
]);
|
|
643
|
+
const collapseMasterDetailRow = React.useCallback((rowKey) => {
|
|
644
|
+
if (resolvedDisabled || !masterDetailEnabled || !masterDetail)
|
|
645
|
+
return;
|
|
646
|
+
if (!effectiveMasterDetailExpandedKeysSet.has(rowKey))
|
|
647
|
+
return;
|
|
648
|
+
let nextKeys;
|
|
649
|
+
if (expandMode === 'single') {
|
|
650
|
+
nextKeys = [];
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
nextKeys = masterDetailExpandedKeys.filter(k => !Object.is(k, rowKey));
|
|
654
|
+
}
|
|
655
|
+
if (masterDetail.expandedRowKeys === undefined) {
|
|
656
|
+
setUncontrolledMasterDetailExpandedKeys(nextKeys);
|
|
657
|
+
}
|
|
658
|
+
const sourceIndex = resolvedData.findIndex(d => Object.is(getCellValueByRowKey(resolvedRowKey, d.values), rowKey));
|
|
659
|
+
const item = resolvedData[sourceIndex];
|
|
660
|
+
if (item) {
|
|
661
|
+
masterDetail.onExpandedRowKeysChange?.(nextKeys, {
|
|
662
|
+
rowKey,
|
|
663
|
+
expanded: false,
|
|
664
|
+
item,
|
|
665
|
+
sourceIndex: sourceIndex >= 0 ? sourceIndex : 0,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}, [
|
|
669
|
+
effectiveMasterDetailExpandedKeysSet,
|
|
670
|
+
expandMode,
|
|
671
|
+
masterDetail,
|
|
672
|
+
masterDetailEnabled,
|
|
673
|
+
masterDetailExpandedKeys,
|
|
674
|
+
resolvedData,
|
|
675
|
+
resolvedDisabled,
|
|
676
|
+
resolvedRowKey,
|
|
677
|
+
]);
|
|
678
|
+
const getToggleElementId = React.useCallback((rowKey) => `${gridInstanceId}-detail-toggle-${encodeMasterDetailRowKey(rowKey)}`, [gridInstanceId]);
|
|
679
|
+
const getDetailElementId = React.useCallback((rowKey) => `${gridInstanceId}-detail-region-${encodeMasterDetailRowKey(rowKey)}`, [gridInstanceId]);
|
|
680
|
+
const masterDetailContextValue = React.useMemo(() => {
|
|
681
|
+
if (!masterDetailEnabled || !resolvedExpandColumnId || resolvedRowKey === undefined || !masterDetail) {
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
gridId: gridInstanceId,
|
|
686
|
+
expandColumnId: resolvedExpandColumnId,
|
|
687
|
+
icons: masterDetail.icons,
|
|
688
|
+
expandAriaLabel: masterDetail.expandAriaLabel ?? 'Expand row',
|
|
689
|
+
collapseAriaLabel: masterDetail.collapseAriaLabel ?? 'Collapse row',
|
|
690
|
+
disabled: resolvedDisabled,
|
|
691
|
+
expandedKeysSet: effectiveMasterDetailExpandedKeysSet,
|
|
692
|
+
getRowKey: values => {
|
|
693
|
+
const key = getCellValueByRowKey(resolvedRowKey, values);
|
|
694
|
+
return key === undefined || key === null ? undefined : key;
|
|
695
|
+
},
|
|
696
|
+
hasDetail: (item, sourceIndex) => {
|
|
697
|
+
const rowKey = getCellValueByRowKey(resolvedRowKey, item.values);
|
|
698
|
+
if (rowKey === undefined || rowKey === null)
|
|
699
|
+
return false;
|
|
700
|
+
if (masterDetailRowKeysMeta?.duplicateKeys.has(rowKey))
|
|
701
|
+
return false;
|
|
702
|
+
if (masterDetail.hasDetail) {
|
|
703
|
+
try {
|
|
704
|
+
return masterDetail.hasDetail(item, sourceIndex);
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return true;
|
|
711
|
+
},
|
|
712
|
+
detailRender: masterDetail.detailRender,
|
|
713
|
+
toggle: toggleMasterDetailRow,
|
|
714
|
+
collapse: collapseMasterDetailRow,
|
|
715
|
+
getToggleElementId,
|
|
716
|
+
getDetailElementId,
|
|
717
|
+
};
|
|
718
|
+
}, [
|
|
719
|
+
collapseMasterDetailRow,
|
|
720
|
+
effectiveMasterDetailExpandedKeysSet,
|
|
721
|
+
getDetailElementId,
|
|
722
|
+
getToggleElementId,
|
|
723
|
+
gridInstanceId,
|
|
724
|
+
masterDetail,
|
|
725
|
+
masterDetailEnabled,
|
|
726
|
+
masterDetailRowKeysMeta?.duplicateKeys,
|
|
727
|
+
resolvedDisabled,
|
|
728
|
+
resolvedExpandColumnId,
|
|
729
|
+
resolvedRowKey,
|
|
730
|
+
toggleMasterDetailRow,
|
|
731
|
+
]);
|
|
732
|
+
const rowHeightMetrics = React.useMemo(() => {
|
|
733
|
+
const fallbackHeight = itemHeight + itemPadding * 2;
|
|
734
|
+
if (!masterDetailEnabled || !masterDetail) {
|
|
735
|
+
return createRowHeightMetrics(displayData, fallbackHeight, getRowHeight);
|
|
736
|
+
}
|
|
737
|
+
return createRowHeightMetrics(displayData, fallbackHeight, getRowHeight, {
|
|
738
|
+
expandedKeysSet: effectiveMasterDetailExpandedKeysSet,
|
|
739
|
+
getRowKey: values => {
|
|
740
|
+
const key = getCellValueByRowKey(resolvedRowKey, values);
|
|
741
|
+
return key === undefined || key === null ? undefined : key;
|
|
742
|
+
},
|
|
743
|
+
getDetailHeight: (item, sourceIndex) => {
|
|
744
|
+
if (typeof masterDetail.detailRowHeight === 'function') {
|
|
745
|
+
try {
|
|
746
|
+
return masterDetail.detailRowHeight(item, sourceIndex);
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
return 200;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return typeof masterDetail.detailRowHeight === 'number' && masterDetail.detailRowHeight > 0
|
|
753
|
+
? masterDetail.detailRowHeight
|
|
754
|
+
: 200;
|
|
755
|
+
},
|
|
756
|
+
hasDetail: (item, sourceIndex) => {
|
|
757
|
+
const rowKey = getCellValueByRowKey(resolvedRowKey, item.values);
|
|
758
|
+
if (rowKey === undefined || rowKey === null)
|
|
759
|
+
return false;
|
|
760
|
+
if (masterDetailRowKeysMeta?.duplicateKeys.has(rowKey))
|
|
761
|
+
return false;
|
|
762
|
+
if (masterDetail.hasDetail) {
|
|
763
|
+
try {
|
|
764
|
+
return masterDetail.hasDetail(item, sourceIndex);
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return true;
|
|
771
|
+
},
|
|
772
|
+
sourceIndexByVisibleIndex: processedResult.sourceIndexByVisibleIndex,
|
|
773
|
+
});
|
|
774
|
+
}, [
|
|
775
|
+
displayData,
|
|
776
|
+
effectiveMasterDetailExpandedKeysSet,
|
|
777
|
+
getRowHeight,
|
|
778
|
+
itemHeight,
|
|
779
|
+
itemPadding,
|
|
780
|
+
masterDetail,
|
|
781
|
+
masterDetailEnabled,
|
|
782
|
+
masterDetailRowKeysMeta?.duplicateKeys,
|
|
783
|
+
processedResult.sourceIndexByVisibleIndex,
|
|
784
|
+
resolvedRowKey,
|
|
785
|
+
]);
|
|
480
786
|
const resolvedFrozenRowCount = pivotEnabled
|
|
481
787
|
? 0
|
|
482
788
|
: Math.min(Math.max(Math.floor(frozenRowCount), 0), displayData.length);
|
|
@@ -535,6 +841,11 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
535
841
|
console.warn('[BGrid] Both bottomBarHeight and footerHeight were provided. bottomBarHeight takes precedence.');
|
|
536
842
|
}
|
|
537
843
|
}, [bottomBarHeight, footerHeight]);
|
|
844
|
+
const resolvedSummaryHeight = React.useMemo(() => computeSummaryHeight({
|
|
845
|
+
summary: resolvedSummary,
|
|
846
|
+
summaryHeight: explicitSummaryHeight,
|
|
847
|
+
summaryRowHeight,
|
|
848
|
+
}), [resolvedSummary, explicitSummaryHeight, summaryRowHeight]);
|
|
538
849
|
const initialStoreState = React.useMemo(() => {
|
|
539
850
|
const initialWidth = width !== undefined ? Math.max(width, 100) : 0;
|
|
540
851
|
const initialHeight = height !== undefined ? Math.max(height, 100) : 0;
|
|
@@ -543,7 +854,7 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
543
854
|
? Math.max(initialHeight -
|
|
544
855
|
headerHeight -
|
|
545
856
|
(initialShowBottomBar ? resolvedBottomBarHeight : 0) -
|
|
546
|
-
(resolvedSummary ?
|
|
857
|
+
(resolvedSummary ? resolvedSummaryHeight : 0) -
|
|
547
858
|
containerBorderWidth * 2, 0)
|
|
548
859
|
: 0;
|
|
549
860
|
const displayItemCount = contentBodyHeight > 0 ? Math.ceil(contentBodyHeight / (itemHeight + itemPadding * 2)) : 0;
|
|
@@ -564,7 +875,8 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
564
875
|
scrollbar: resolvedScrollbar,
|
|
565
876
|
status: resolvedStatus,
|
|
566
877
|
pagination: resolvedPagination,
|
|
567
|
-
summaryHeight,
|
|
878
|
+
summaryHeight: resolvedSummaryHeight,
|
|
879
|
+
summaryRowHeight,
|
|
568
880
|
itemHeight,
|
|
569
881
|
itemPadding,
|
|
570
882
|
frozenColumnIndex: resolvedFrozenColumnIndex,
|
|
@@ -635,7 +947,8 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
635
947
|
resolvedScrollbar,
|
|
636
948
|
resolvedStatus,
|
|
637
949
|
resolvedPagination,
|
|
638
|
-
|
|
950
|
+
resolvedSummaryHeight,
|
|
951
|
+
summaryRowHeight,
|
|
639
952
|
itemHeight,
|
|
640
953
|
itemPadding,
|
|
641
954
|
rowHeightMetrics,
|
|
@@ -685,62 +998,64 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
|
|
|
685
998
|
]);
|
|
686
999
|
return (React.createElement(AppStoreProvider, { initialState: initialStoreState },
|
|
687
1000
|
React.createElement(TreeContext.Provider, { value: treeContextValue },
|
|
688
|
-
React.createElement(
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1001
|
+
React.createElement(MasterDetailContext.Provider, { value: masterDetailContextValue },
|
|
1002
|
+
React.createElement(Table, { ref: ref, sourceColumns: resolvedColumns,
|
|
1003
|
+
columns: computedColumns,
|
|
1004
|
+
columnsGroup: visibilityProjection.columnsGroup,
|
|
1005
|
+
columnGroups: visibilityProjection.columnGroups,
|
|
1006
|
+
onChangeColumns: resolvedOnChangeColumns,
|
|
1007
|
+
width,
|
|
1008
|
+
height,
|
|
1009
|
+
className,
|
|
1010
|
+
style,
|
|
1011
|
+
loading,
|
|
1012
|
+
disabled: resolvedDisabled,
|
|
1013
|
+
spinning,
|
|
1014
|
+
scrollLeft,
|
|
1015
|
+
scrollTop,
|
|
1016
|
+
headerHeight,
|
|
1017
|
+
footerHeight,
|
|
1018
|
+
bottomBarHeight: resolvedBottomBarHeight,
|
|
1019
|
+
scrollbar: resolvedScrollbar,
|
|
1020
|
+
status: resolvedStatus,
|
|
1021
|
+
pagination: resolvedPagination,
|
|
1022
|
+
summaryHeight: resolvedSummaryHeight,
|
|
1023
|
+
summaryRowHeight,
|
|
1024
|
+
itemHeight,
|
|
1025
|
+
itemPadding,
|
|
1026
|
+
rowHeightMetrics,
|
|
1027
|
+
frozenColumnIndex: resolvedFrozenColumnIndex,
|
|
1028
|
+
frozenRowCount: resolvedFrozenRowCount,
|
|
1029
|
+
rowChecked: resolvedRowChecked,
|
|
1030
|
+
checkedIndexesMap,
|
|
1031
|
+
sort: resolvedSort,
|
|
1032
|
+
sortParams,
|
|
1033
|
+
dataQuery: resolvedDataQuery,
|
|
1034
|
+
dataControl: resolvedDataControl,
|
|
1035
|
+
icons,
|
|
1036
|
+
columnVisibilityState,
|
|
1037
|
+
searchOptions: resolvedSearchOptions,
|
|
1038
|
+
contextMenuOptions: resolvedContextMenuOptions,
|
|
1039
|
+
page: resolvedPage,
|
|
1040
|
+
data: displayData,
|
|
1041
|
+
sourceData: resolvedData,
|
|
1042
|
+
sourceIndexByVisibleIndex: processedResult.sourceIndexByVisibleIndex,
|
|
1043
|
+
visibleIndexBySourceIndex: processedResult.visibleIndexBySourceIndex,
|
|
1044
|
+
onClick: resolvedOnClick,
|
|
1045
|
+
rowKey: resolvedRowKey,
|
|
1046
|
+
selectedRowKey: resolvedSelectedRowKey,
|
|
1047
|
+
editable: resolvedEditable,
|
|
1048
|
+
editTrigger,
|
|
1049
|
+
onChangeData: resolvedOnChangeData,
|
|
1050
|
+
showLineNumber: resolvedShowLineNumber,
|
|
1051
|
+
msg,
|
|
1052
|
+
getRowClassName: resolvedGetRowClassName,
|
|
1053
|
+
cellMergeOptions: resolvedCellMergeOptions,
|
|
1054
|
+
cellSelectionOptions: resolvedCellSelectionOptions,
|
|
1055
|
+
cellNavigationOptions,
|
|
1056
|
+
variant,
|
|
1057
|
+
summary: resolvedSummary,
|
|
1058
|
+
columnSortable: resolvedColumnSortable,
|
|
1059
|
+
reorder: resolvedReorder,
|
|
1060
|
+
treeEnabled: !!treeContextValue })))));
|
|
746
1061
|
}
|
|
@@ -12,6 +12,8 @@ export function CellNavigationDomSync({ containerRef, activeCell, cellSelectionR
|
|
|
12
12
|
container
|
|
13
13
|
.querySelectorAll('[data-bgrid-cell-active="true"], [data-bgrid-cell-active-multi-selection="true"]')
|
|
14
14
|
.forEach(cell => {
|
|
15
|
+
if (cell.closest('[role="grid"], [role="treegrid"]') !== container)
|
|
16
|
+
return;
|
|
15
17
|
cell.classList.remove('bgrid-cell-active', 'bgrid-cell-active-multi-selection');
|
|
16
18
|
cell.removeAttribute('data-bgrid-cell-active');
|
|
17
19
|
cell.removeAttribute('data-bgrid-cell-active-multi-selection');
|
|
@@ -20,6 +22,8 @@ export function CellNavigationDomSync({ containerRef, activeCell, cellSelectionR
|
|
|
20
22
|
container
|
|
21
23
|
.querySelectorAll(`td[data-bgrid-cell="true"][data-bgrid-logical-row-index="${activeCell.rowIndex}"][data-column-index="${activeCell.columnIndex}"]`)
|
|
22
24
|
.forEach(cell => {
|
|
25
|
+
if (cell.closest('[role="grid"], [role="treegrid"]') !== container)
|
|
26
|
+
return;
|
|
23
27
|
cell.classList.add('bgrid-cell-active');
|
|
24
28
|
cell.setAttribute('data-bgrid-cell-active', 'true');
|
|
25
29
|
if (hasMultiCellSelection) {
|
|
@@ -33,6 +37,8 @@ export function CellNavigationDomSync({ containerRef, activeCell, cellSelectionR
|
|
|
33
37
|
const wholeRowSelection = coversEveryColumn && !coversEveryRow;
|
|
34
38
|
const wholeColumnSelection = coversEveryRow && !coversEveryColumn;
|
|
35
39
|
container.querySelectorAll('.bgrid-line-number-cell[data-row-index]').forEach(cell => {
|
|
40
|
+
if (cell.closest('[role="grid"], [role="treegrid"]') !== container)
|
|
41
|
+
return;
|
|
36
42
|
const rowIndex = Number(cell.dataset.rowIndex);
|
|
37
43
|
const active = !wholeColumnSelection &&
|
|
38
44
|
Number.isFinite(rowIndex) &&
|
|
@@ -44,6 +50,8 @@ export function CellNavigationDomSync({ containerRef, activeCell, cellSelectionR
|
|
|
44
50
|
cell.removeAttribute('data-bgrid-row-axis-active');
|
|
45
51
|
});
|
|
46
52
|
container.querySelectorAll('[data-header-cell-type][data-column-index]').forEach(cell => {
|
|
53
|
+
if (cell.closest('[role="grid"], [role="treegrid"]') !== container)
|
|
54
|
+
return;
|
|
47
55
|
const columnIndex = Number(cell.dataset.columnIndex);
|
|
48
56
|
const active = cell.dataset.headerCellType === 'column' &&
|
|
49
57
|
!wholeRowSelection &&
|
|
@@ -98,14 +98,14 @@ export function CellTextEditorGateway({ containerRef }) {
|
|
|
98
98
|
input.style.removeProperty('text-align');
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
-
const target = container.
|
|
101
|
+
const target = Array.from(container.querySelectorAll(`td[data-bgrid-cell="true"][data-row-index="${cellEditSession.hostCell.rowIndex}"][data-column-index="${cellEditSession.hostCell.columnIndex}"]`)).find(el => el.closest('[role="grid"], [role="treegrid"]') === container);
|
|
102
102
|
if (!(target instanceof HTMLElement)) {
|
|
103
103
|
input.style.visibility = 'hidden';
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
const containerRect = container.getBoundingClientRect();
|
|
107
107
|
const targetRect = target.getBoundingClientRect();
|
|
108
|
-
const bodyViewport = container.
|
|
108
|
+
const bodyViewport = Array.from(container.querySelectorAll('.bgrid-body-viewport')).find(el => el.closest('[role="grid"], [role="treegrid"]') === container);
|
|
109
109
|
const bodyViewportRect = bodyViewport instanceof HTMLElement ? bodyViewport.getBoundingClientRect() : containerRect;
|
|
110
110
|
const editorBox = resolveVisibleTextEditorVerticalBox({
|
|
111
111
|
targetTop: targetRect.top,
|
|
@@ -118,9 +118,12 @@ export function CellTextEditorGateway({ containerRef }) {
|
|
|
118
118
|
input.style.visibility = 'hidden';
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
// Convert viewport pixels back to local pixels during modal scale animations.
|
|
122
|
+
const scaleX = container.offsetWidth && containerRect.width ? containerRect.width / container.offsetWidth : 1;
|
|
123
|
+
const scaleY = container.offsetHeight && containerRect.height ? containerRect.height / container.offsetHeight : 1;
|
|
124
|
+
input.style.transform = `translate(${(targetRect.left - containerRect.left) / scaleX - container.clientLeft}px, ${(editorBox.top - containerRect.top) / scaleY - container.clientTop}px)`;
|
|
125
|
+
input.style.width = `${targetRect.width / scaleX}px`;
|
|
126
|
+
input.style.height = `${editorBox.height / scaleY}px`;
|
|
124
127
|
if (column?.align) {
|
|
125
128
|
input.style.textAlign = column.align;
|
|
126
129
|
}
|
|
@@ -216,11 +219,11 @@ export function CellTextEditorGateway({ containerRef }) {
|
|
|
216
219
|
return;
|
|
217
220
|
endCellEdit(cellEditSession.id);
|
|
218
221
|
}, [cellEditSession, endCellEdit]);
|
|
219
|
-
const startReplace = React.useCallback(() => {
|
|
220
|
-
if (!activeCell || !canStartText || cellEditSession)
|
|
222
|
+
const startReplace = React.useCallback((preserveInput = false) => {
|
|
223
|
+
if (!activeCell || !canStartText || cellEditSession || nativeStartRef.current)
|
|
221
224
|
return;
|
|
222
225
|
nativeStartRef.current = true;
|
|
223
|
-
if (inputRef.current)
|
|
226
|
+
if (inputRef.current && !preserveInput)
|
|
224
227
|
inputRef.current.value = '';
|
|
225
228
|
beginCellEdit(activeCell, 'replace');
|
|
226
229
|
}, [activeCell, beginCellEdit, canStartText, cellEditSession]);
|
|
@@ -233,7 +236,7 @@ export function CellTextEditorGateway({ containerRef }) {
|
|
|
233
236
|
: '셀 텍스트 편집'
|
|
234
237
|
: textEditor.ariaLabel ?? `행 ${cell.rowIndex + 1}, 열 ${cell.columnIndex + 1} 텍스트 편집`
|
|
235
238
|
: 'DataGrid 키보드 탐색';
|
|
236
|
-
return (React.createElement("input", { ref: inputRef, "data-bgrid-text-editor-gateway": 'true', className: isTextEditing ? 'bgrid-text-editor-gateway bgrid-text-editor-active' : 'bgrid-text-editor-gateway', "aria-label": ariaLabel, "aria-invalid": invalid || undefined, readOnly: !canStartText && !isTextEditing, ...inputProps, onBeforeInput: () => startReplace(), onInput: () => startReplace(), onCompositionStart: () => {
|
|
239
|
+
return (React.createElement("input", { ref: inputRef, "data-bgrid-text-editor-gateway": 'true', className: isTextEditing ? 'bgrid-text-editor-gateway bgrid-text-editor-active' : 'bgrid-text-editor-gateway', "aria-label": ariaLabel, "aria-invalid": invalid || undefined, readOnly: !canStartText && !isTextEditing, ...inputProps, onBeforeInput: () => startReplace(), onInput: () => startReplace(true), onCompositionStart: () => {
|
|
237
240
|
composingRef.current = true;
|
|
238
241
|
startReplace();
|
|
239
242
|
}, onCompositionEnd: () => {
|