@alaarab/ogrid-fluent 1.6.0 → 1.7.2
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/esm/DataGridTable/DataGridTable.js +114 -86
- package/dist/esm/DataGridTable/DataGridTable.module.css +5 -2
- package/dist/esm/FluentDataTable/FluentDataTable.js +2 -4
- package/dist/esm/FluentDataTable/index.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/types/DataGridTable/DataGridTable.d.ts +0 -2
- package/dist/types/FluentDataTable/FluentDataTable.d.ts +0 -4
- package/dist/types/FluentDataTable/index.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +2 -2
|
@@ -9,6 +9,20 @@ import { StatusBar } from './StatusBar';
|
|
|
9
9
|
import { GridContextMenu } from './GridContextMenu';
|
|
10
10
|
import { useDataGridState, getHeaderFilterConfig, getCellRenderDescriptor, buildHeaderRows, MarchingAntsOverlay, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from '@alaarab/ogrid-core';
|
|
11
11
|
import styles from './DataGridTable.module.css';
|
|
12
|
+
// Module-scope stable constants (avoid per-render allocations)
|
|
13
|
+
const gridRootStyle = {
|
|
14
|
+
position: 'relative',
|
|
15
|
+
flex: 1,
|
|
16
|
+
minHeight: 0,
|
|
17
|
+
display: 'flex',
|
|
18
|
+
flexDirection: 'column',
|
|
19
|
+
};
|
|
20
|
+
const CURSOR_CELL_STYLE = { cursor: 'cell' };
|
|
21
|
+
const NUMERIC_STYLE = { justifyContent: 'flex-end', textAlign: 'right' };
|
|
22
|
+
const BOOLEAN_STYLE = { justifyContent: 'center', textAlign: 'center' };
|
|
23
|
+
const EDITABLE_NUMERIC_STYLE = { cursor: 'cell', justifyContent: 'flex-end', textAlign: 'right' };
|
|
24
|
+
const EDITABLE_BOOLEAN_STYLE = { cursor: 'cell', justifyContent: 'center', textAlign: 'center' };
|
|
25
|
+
const PREVENT_DEFAULT = (e) => { e.preventDefault(); };
|
|
12
26
|
function DataGridTableInner(props) {
|
|
13
27
|
const wrapperRef = useRef(null);
|
|
14
28
|
const tableContainerRef = useRef(null);
|
|
@@ -21,7 +35,8 @@ function DataGridTableInner(props) {
|
|
|
21
35
|
const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
|
|
22
36
|
const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid } = viewModels;
|
|
23
37
|
const { items, getRowId, emptyState, layoutMode = 'fill', rowSelection = 'none', freezeRows, freezeCols, suppressHorizontalScroll, isLoading = false, loadingMessage = 'Loading\u2026', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, } = props;
|
|
24
|
-
|
|
38
|
+
// Memoize header rows (recursive tree traversal)
|
|
39
|
+
const headerRows = useMemo(() => buildHeaderRows(props.columns, props.visibleColumns), [props.columns, props.visibleColumns]);
|
|
25
40
|
const hasGroupHeaders = headerRows.length > 1;
|
|
26
41
|
const fitToContent = layoutMode === 'content';
|
|
27
42
|
const columnSizingOptions = useMemo(() => {
|
|
@@ -44,6 +59,52 @@ function DataGridTableInner(props) {
|
|
|
44
59
|
return acc;
|
|
45
60
|
}, [visibleCols, columnSizingOverrides, hasCheckboxCol]);
|
|
46
61
|
const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
|
|
62
|
+
// Pre-compute column class maps (avoids per-cell .filter(Boolean).join(' '))
|
|
63
|
+
const { cellClassMap, headerClassMap, colIndexMap } = useMemo(() => {
|
|
64
|
+
const cm = {};
|
|
65
|
+
const hm = {};
|
|
66
|
+
const im = new Map();
|
|
67
|
+
for (let i = 0; i < visibleCols.length; i++) {
|
|
68
|
+
const col = visibleCols[i];
|
|
69
|
+
const isFreezeCol = freezeCols != null && freezeCols >= 1 && i < freezeCols;
|
|
70
|
+
const isPinnedLeft = col.pinned === 'left';
|
|
71
|
+
const isPinnedRight = col.pinned === 'right';
|
|
72
|
+
const parts = [];
|
|
73
|
+
if (isFreezeCol)
|
|
74
|
+
parts.push(styles.freezeCol);
|
|
75
|
+
if (isFreezeCol && i === 0)
|
|
76
|
+
parts.push(styles.freezeColFirst);
|
|
77
|
+
if (isPinnedLeft) {
|
|
78
|
+
parts.push(styles.pinnedCell);
|
|
79
|
+
parts.push(styles.pinnedLeft);
|
|
80
|
+
}
|
|
81
|
+
if (isPinnedRight) {
|
|
82
|
+
parts.push(styles.pinnedCell);
|
|
83
|
+
parts.push(styles.pinnedRight);
|
|
84
|
+
}
|
|
85
|
+
cm[col.columnId] = parts.join(' ');
|
|
86
|
+
hm[col.columnId] = parts.join(' ');
|
|
87
|
+
im.set(col.columnId, i);
|
|
88
|
+
}
|
|
89
|
+
cm['__selection__'] = styles.selectionCellWrapper;
|
|
90
|
+
hm['__selection__'] = styles.selectionHeaderCellWrapper;
|
|
91
|
+
return { cellClassMap: cm, headerClassMap: hm, colIndexMap: im };
|
|
92
|
+
}, [visibleCols, freezeCols]);
|
|
93
|
+
// Refs for volatile state (read inside fluentColumns render closures without adding to deps)
|
|
94
|
+
const cellDescriptorInputRef = useRef(cellDescriptorInput);
|
|
95
|
+
cellDescriptorInputRef.current = cellDescriptorInput;
|
|
96
|
+
const selectedRowIdsRef = useRef(selectedRowIds);
|
|
97
|
+
selectedRowIdsRef.current = selectedRowIds;
|
|
98
|
+
const activeCellRef = useRef(activeCell);
|
|
99
|
+
activeCellRef.current = activeCell;
|
|
100
|
+
const pendingEditorValueRef = useRef(pendingEditorValue);
|
|
101
|
+
pendingEditorValueRef.current = pendingEditorValue;
|
|
102
|
+
const popoverAnchorElRef = useRef(popoverAnchorEl);
|
|
103
|
+
popoverAnchorElRef.current = popoverAnchorEl;
|
|
104
|
+
const allSelectedRef = useRef(allSelected);
|
|
105
|
+
allSelectedRef.current = allSelected;
|
|
106
|
+
const someSelectedRef = useRef(someSelected);
|
|
107
|
+
someSelectedRef.current = someSelected;
|
|
47
108
|
const fluentColumns = useMemo(() => {
|
|
48
109
|
const dataCols = visibleCols.map((col, colIdx) => createTableColumn({
|
|
49
110
|
columnId: col.columnId,
|
|
@@ -52,16 +113,16 @@ function DataGridTableInner(props) {
|
|
|
52
113
|
renderCell: (item) => {
|
|
53
114
|
const rowId = getRowId(item);
|
|
54
115
|
const rowIndex = rowIndexByRowId.get(rowId) ?? -1;
|
|
55
|
-
const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx,
|
|
116
|
+
const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInputRef.current);
|
|
56
117
|
if (descriptor.mode === 'editing-inline') {
|
|
57
118
|
return _jsx(InlineCellEditor, { ...buildInlineEditorProps(item, col, descriptor, { commitCellEdit, setEditingCell }) });
|
|
58
119
|
}
|
|
59
120
|
if (descriptor.mode === 'editing-popover' && typeof col.cellEditor === 'function') {
|
|
60
|
-
const editorProps = buildPopoverEditorProps(item, col, descriptor,
|
|
121
|
+
const editorProps = buildPopoverEditorProps(item, col, descriptor, pendingEditorValueRef.current, { setPendingEditorValue, commitCellEdit, cancelPopoverEdit });
|
|
61
122
|
const CustomEditor = col.cellEditor;
|
|
62
123
|
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: (el) => { if (el)
|
|
63
|
-
setPopoverAnchorEl(el); }, style: { minHeight: '100%', minWidth: 40 }, "aria-hidden": true }), _jsx(Popover, { open: !!
|
|
64
|
-
cancelPopoverEdit(); }, positioning: { target:
|
|
124
|
+
setPopoverAnchorEl(el); }, style: { minHeight: '100%', minWidth: 40 }, "aria-hidden": true }), _jsx(Popover, { open: !!popoverAnchorElRef.current, onOpenChange: (_, data) => { if (!data.open)
|
|
125
|
+
cancelPopoverEdit(); }, positioning: { target: popoverAnchorElRef.current ?? undefined }, children: _jsx(PopoverSurface, { children: _jsx(CustomEditor, { ...editorProps }) }) })] }));
|
|
65
126
|
}
|
|
66
127
|
const content = resolveCellDisplayContent(col, item, descriptor.displayValue);
|
|
67
128
|
const cellStyle = resolveCellStyle(col, item);
|
|
@@ -77,23 +138,24 @@ function DataGridTableInner(props) {
|
|
|
77
138
|
.join(' ');
|
|
78
139
|
const colType = col.type;
|
|
79
140
|
const interactionProps = getCellInteractionProps(descriptor, col.columnId, { handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu });
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
141
|
+
// Select stable style constant by type + editability
|
|
142
|
+
const computedStyle = descriptor.canEditAny
|
|
143
|
+
? (colType === 'numeric' ? EDITABLE_NUMERIC_STYLE : colType === 'boolean' ? EDITABLE_BOOLEAN_STYLE : CURSOR_CELL_STYLE)
|
|
144
|
+
: (colType === 'numeric' ? NUMERIC_STYLE : colType === 'boolean' ? BOOLEAN_STYLE : undefined);
|
|
145
|
+
return (_jsxs("div", { className: cellClassNames, ...interactionProps, style: computedStyle, children: [styledContent, descriptor.canEditAny && descriptor.isSelectionEndCell && (_jsx("div", { className: styles.fillHandle, onMouseDown: handleFillHandleMouseDown, "aria-label": "Fill handle" }))] }));
|
|
85
146
|
},
|
|
86
147
|
}));
|
|
87
148
|
if (hasCheckboxCol) {
|
|
88
149
|
const checkboxCol = createTableColumn({
|
|
89
150
|
columnId: '__selection__',
|
|
90
151
|
compare: () => 0,
|
|
91
|
-
renderHeaderCell: () => (_jsx("div", { className: styles.selectionHeaderCell, children: _jsx(Checkbox, { checked:
|
|
152
|
+
renderHeaderCell: () => (_jsx("div", { className: styles.selectionHeaderCell, children: _jsx(Checkbox, { checked: allSelectedRef.current ? true : someSelectedRef.current ? 'mixed' : false, onChange: (_, data) => handleSelectAll(!!data.checked), "aria-label": "Select all rows" }) })),
|
|
92
153
|
renderCell: (item) => {
|
|
93
154
|
const rowId = getRowId(item);
|
|
94
155
|
const rowIndex = rowIndexByRowId.get(rowId) ?? -1;
|
|
95
|
-
const isChecked =
|
|
96
|
-
const
|
|
156
|
+
const isChecked = selectedRowIdsRef.current.has(rowId);
|
|
157
|
+
const ac = activeCellRef.current;
|
|
158
|
+
const isActive = ac?.rowIndex === rowIndex && ac?.columnIndex === 0;
|
|
97
159
|
return (_jsx("div", { className: `${styles.selectionCell} ${isActive ? styles.activeCellContent : ''}`, "data-row-index": rowIndex, "data-col-index": 0, onClick: (e) => {
|
|
98
160
|
e.stopPropagation();
|
|
99
161
|
setActiveCell({ rowIndex, columnIndex: 0 });
|
|
@@ -108,18 +170,11 @@ function DataGridTableInner(props) {
|
|
|
108
170
|
}, [
|
|
109
171
|
visibleCols,
|
|
110
172
|
headerFilterInput,
|
|
111
|
-
cellDescriptorInput,
|
|
112
173
|
getRowId,
|
|
113
174
|
rowIndexByRowId,
|
|
114
|
-
pendingEditorValue,
|
|
115
|
-
popoverAnchorEl,
|
|
116
175
|
hasCheckboxCol,
|
|
117
|
-
allSelected,
|
|
118
|
-
someSelected,
|
|
119
|
-
selectedRowIds,
|
|
120
176
|
handleSelectAll,
|
|
121
177
|
handleRowCheckboxChange,
|
|
122
|
-
activeCell,
|
|
123
178
|
handleCellMouseDown,
|
|
124
179
|
handleFillHandleMouseDown,
|
|
125
180
|
handleCellContextMenu,
|
|
@@ -130,6 +185,15 @@ function DataGridTableInner(props) {
|
|
|
130
185
|
commitCellEdit,
|
|
131
186
|
cancelPopoverEdit,
|
|
132
187
|
]);
|
|
188
|
+
// Stable row-click handler
|
|
189
|
+
const handleSingleRowClick = useCallback((rowId) => {
|
|
190
|
+
if (rowSelection !== 'single')
|
|
191
|
+
return;
|
|
192
|
+
const ids = selectedRowIdsRef.current;
|
|
193
|
+
updateSelection(ids.has(rowId) ? new Set() : new Set([rowId]));
|
|
194
|
+
}, [rowSelection, updateSelection]);
|
|
195
|
+
// Stable getRowId wrapper for Fluent DataGrid
|
|
196
|
+
const fluentGetRowId = useCallback((item) => String(getRowId(item)), [getRowId]);
|
|
133
197
|
// Double-click to auto-fit column width
|
|
134
198
|
useEffect(() => {
|
|
135
199
|
const root = wrapperRef.current;
|
|
@@ -172,71 +236,35 @@ function DataGridTableInner(props) {
|
|
|
172
236
|
[String(data.columnId)]: { widthPx: data.width },
|
|
173
237
|
}));
|
|
174
238
|
}, [setColumnSizingOverrides]);
|
|
175
|
-
return (_jsxs("div", { ref: wrapperRef, tabIndex: 0, className: `${styles.tableWrapper} ${rowSelection !== 'none' ? styles.selectableGrid : ''}`, role: "region", "aria-label": ariaLabel ?? (ariaLabelledBy ? undefined : 'Data grid'), "aria-labelledby": ariaLabelledBy, "data-empty": showEmptyInGrid ? 'true' : undefined, "data-auto-fit": layoutMode === 'fill' && !allowOverflowX ? 'true' : undefined, "data-column-count": totalColCount, "data-freeze-rows": freezeRows != null && freezeRows >= 1 ? freezeRows : undefined, "data-freeze-cols": freezeCols != null && freezeCols >= 1 ? freezeCols : undefined, "data-overflow-x": allowOverflowX ? 'true' : 'false', "data-container-width": containerWidth, "data-min-table-width": Math.round(minTableWidth), "data-has-selection": rowSelection !== 'none' ? 'true' : undefined, onContextMenu:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
columnId === '__selection__' ? styles.selectionHeaderCellWrapper : '',
|
|
206
|
-
isFreezeCol ? styles.freezeCol : '',
|
|
207
|
-
isFreezeCol && colIdx === 0 ? styles.freezeColFirst : '',
|
|
208
|
-
isPinnedLeft ? styles.pinnedCell : '',
|
|
209
|
-
isPinnedLeft ? styles.pinnedLeft : '',
|
|
210
|
-
isPinnedRight ? styles.pinnedCell : '',
|
|
211
|
-
isPinnedRight ? styles.pinnedRight : '',
|
|
212
|
-
].filter(Boolean).join(' '), children: renderHeaderCell() }));
|
|
213
|
-
} })] }), _jsx(DataGridBody, { children: ({ item }) => {
|
|
214
|
-
const rowId = getRowId(item);
|
|
215
|
-
const isSelected = selectedRowIds.has(rowId);
|
|
216
|
-
return (_jsx(DataGridRow, { className: `${isSelected ? styles.selectedRow : ''} ${activeCell !== null && (rowIndexByRowId.get(rowId) ?? -1) === activeCell.rowIndex
|
|
217
|
-
? styles.activeRow
|
|
218
|
-
: ''}`, onClick: () => {
|
|
219
|
-
if (rowSelection === 'single') {
|
|
220
|
-
const isCurrentlySelected = selectedRowIds.has(rowId);
|
|
221
|
-
updateSelection(isCurrentlySelected ? new Set() : new Set([rowId]));
|
|
222
|
-
}
|
|
223
|
-
}, children: ({ renderCell, columnId }) => {
|
|
224
|
-
const colIdx = visibleCols.findIndex((c) => c.columnId === columnId);
|
|
225
|
-
const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx >= 0 && colIdx < freezeCols;
|
|
226
|
-
const col = colIdx >= 0 ? visibleCols[colIdx] : undefined;
|
|
227
|
-
const isPinnedLeft = col?.pinned === 'left';
|
|
228
|
-
const isPinnedRight = col?.pinned === 'right';
|
|
229
|
-
return (_jsx(DataGridCell, { className: [
|
|
230
|
-
columnId === '__selection__' ? styles.selectionCellWrapper : '',
|
|
231
|
-
isFreezeCol ? styles.freezeCol : '',
|
|
232
|
-
isFreezeCol && colIdx === 0 ? styles.freezeColFirst : '',
|
|
233
|
-
isPinnedLeft ? styles.pinnedCell : '',
|
|
234
|
-
isPinnedLeft ? styles.pinnedLeft : '',
|
|
235
|
-
isPinnedRight ? styles.pinnedCell : '',
|
|
236
|
-
isPinnedRight ? styles.pinnedRight : '',
|
|
237
|
-
].filter(Boolean).join(' '), children: renderCell(item) }));
|
|
238
|
-
} }, rowId));
|
|
239
|
-
} })] }), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset }), showEmptyInGrid && emptyState && (_jsx("div", { className: styles.emptyStateInGrid, children: _jsx("div", { className: styles.emptyStateInGridMessageSticky, children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.emptyStateInGridIcon, "aria-hidden": true, children: "\uD83D\uDCCB" }), _jsx("div", { className: styles.emptyStateInGridTitle, children: "No results found" }), _jsx("div", { className: styles.emptyStateInGridMessage, children: emptyState.message != null ? (emptyState.message) : emptyState.hasActiveFilters ? (_jsxs(_Fragment, { children: ["No items match your current filters. Try adjusting your search or", ' ', _jsx("button", { type: "button", className: styles.emptyStateInGridLink, onClick: emptyState.onClearAll, children: "clear all filters" }), ' ', "to see all items."] })) : ('There are no items available at this time.') })] })) }) }))] }) }), statusBarConfig && (_jsx(StatusBar, { totalCount: statusBarConfig.totalCount, filteredCount: statusBarConfig.filteredCount, selectedCount: statusBarConfig.selectedCount ?? selectedRowIds.size, selectedCellCount: selectionRange ? (Math.abs(selectionRange.endRow - selectionRange.startRow) + 1) * (Math.abs(selectionRange.endCol - selectionRange.startCol) + 1) : undefined, aggregation: statusBarConfig.aggregation, suppressRowCount: statusBarConfig.suppressRowCount }))] }), menuPosition &&
|
|
240
|
-
createPortal(_jsx(GridContextMenu, { x: menuPosition.x, y: menuPosition.y, hasSelection: hasCellSelection, canUndo: canUndo, canRedo: canRedo, onUndo: onUndo ?? (() => { }), onRedo: onRedo ?? (() => { }), onCopy: handleCopy, onCut: handleCut, onPaste: () => void handlePaste(), onSelectAll: handleSelectAllCells, onClose: closeContextMenu }), document.body)] }));
|
|
239
|
+
return (_jsxs("div", { style: gridRootStyle, children: [_jsxs("div", { ref: wrapperRef, tabIndex: 0, className: `${styles.tableWrapper} ${rowSelection !== 'none' ? styles.selectableGrid : ''}`, role: "region", "aria-label": ariaLabel ?? (ariaLabelledBy ? undefined : 'Data grid'), "aria-labelledby": ariaLabelledBy, "data-empty": showEmptyInGrid ? 'true' : undefined, "data-auto-fit": layoutMode === 'fill' && !allowOverflowX ? 'true' : undefined, "data-column-count": totalColCount, "data-freeze-rows": freezeRows != null && freezeRows >= 1 ? freezeRows : undefined, "data-freeze-cols": freezeCols != null && freezeCols >= 1 ? freezeCols : undefined, "data-overflow-x": allowOverflowX ? 'true' : 'false', "data-container-width": containerWidth, "data-min-table-width": Math.round(minTableWidth), "data-has-selection": rowSelection !== 'none' ? 'true' : undefined, onContextMenu: PREVENT_DEFAULT, style: {
|
|
240
|
+
['--data-table-column-count']: totalColCount,
|
|
241
|
+
['--data-table-width']: showEmptyInGrid
|
|
242
|
+
? '100%'
|
|
243
|
+
: allowOverflowX
|
|
244
|
+
? 'fit-content'
|
|
245
|
+
: fitToContent
|
|
246
|
+
? 'fit-content'
|
|
247
|
+
: '100%',
|
|
248
|
+
['--data-table-min-width']: showEmptyInGrid
|
|
249
|
+
? '100%'
|
|
250
|
+
: allowOverflowX
|
|
251
|
+
? 'max-content'
|
|
252
|
+
: fitToContent
|
|
253
|
+
? 'max-content'
|
|
254
|
+
: '100%',
|
|
255
|
+
}, onKeyDown: handleGridKeyDown, children: [_jsxs("div", { className: styles.tableScrollContent, children: [_jsx("div", { className: isLoading && items.length > 0 ? styles.loadingDimmed : undefined, children: _jsxs("div", { className: styles.tableWidthAnchor, ref: tableContainerRef, children: [_jsxs(DataGrid, { items: items, columns: fluentColumns, resizableColumns: true, resizableColumnsOptions: { autoFitColumns: layoutMode === 'fill' && !allowOverflowX }, columnSizingOptions: columnSizingOptions, onColumnResize: handleColumnResize, getRowId: fluentGetRowId, focusMode: "composite", className: styles.dataGrid, children: [_jsxs(DataGridHeader, { className: styles.stickyHeader, children: [hasGroupHeaders && headerRows.slice(0, -1).map((row, rowIdx) => (_jsxs("tr", { className: styles.groupHeaderRow, children: [rowIdx === 0 && hasCheckboxCol && (_jsx("th", { rowSpan: headerRows.length - 1, style: { width: 48, minWidth: 48 } })), row.map((cell, cellIdx) => {
|
|
256
|
+
if (cell.isGroup) {
|
|
257
|
+
return (_jsx("th", { colSpan: cell.colSpan, className: styles.groupHeaderCell, scope: "colgroup", children: cell.label }, cellIdx));
|
|
258
|
+
}
|
|
259
|
+
return (_jsx("th", { rowSpan: headerRows.length - rowIdx, className: styles.leafHeaderCellSpan, scope: "col", children: cell.columnDef?.name }, cellIdx));
|
|
260
|
+
})] }, `group-${rowIdx}`))), _jsx(DataGridRow, { children: ({ renderHeaderCell, columnId }) => (_jsx(DataGridHeaderCell, { className: headerClassMap[String(columnId)] || undefined, children: renderHeaderCell() })) })] }), _jsx(DataGridBody, { children: ({ item }) => {
|
|
261
|
+
const rowId = getRowId(item);
|
|
262
|
+
const isSelected = selectedRowIdsRef.current.has(rowId);
|
|
263
|
+
const ac = activeCellRef.current;
|
|
264
|
+
return (_jsx(DataGridRow, { className: `${isSelected ? styles.selectedRow : ''} ${ac !== null && (rowIndexByRowId.get(rowId) ?? -1) === ac.rowIndex
|
|
265
|
+
? styles.activeRow
|
|
266
|
+
: ''}`, onClick: () => handleSingleRowClick(rowId), children: ({ renderCell, columnId }) => (_jsx(DataGridCell, { className: cellClassMap[String(columnId)] || undefined, children: renderCell(item) })) }, rowId));
|
|
267
|
+
} })] }), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset }), showEmptyInGrid && emptyState && (_jsx("div", { className: styles.emptyStateInGrid, children: _jsx("div", { className: styles.emptyStateInGridMessageSticky, children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.emptyStateInGridIcon, "aria-hidden": true, children: "\uD83D\uDCCB" }), _jsx("div", { className: styles.emptyStateInGridTitle, children: "No results found" }), _jsx("div", { className: styles.emptyStateInGridMessage, children: emptyState.message != null ? (emptyState.message) : emptyState.hasActiveFilters ? (_jsxs(_Fragment, { children: ["No items match your current filters. Try adjusting your search or", ' ', _jsx("button", { type: "button", className: styles.emptyStateInGridLink, onClick: emptyState.onClearAll, children: "clear all filters" }), ' ', "to see all items."] })) : ('There are no items available at this time.') })] })) }) }))] }) }), statusBarConfig && (_jsx(StatusBar, { totalCount: statusBarConfig.totalCount, filteredCount: statusBarConfig.filteredCount, selectedCount: statusBarConfig.selectedCount ?? selectedRowIds.size, selectedCellCount: selectionRange ? (Math.abs(selectionRange.endRow - selectionRange.startRow) + 1) * (Math.abs(selectionRange.endCol - selectionRange.startCol) + 1) : undefined, aggregation: statusBarConfig.aggregation, suppressRowCount: statusBarConfig.suppressRowCount }))] }), menuPosition &&
|
|
268
|
+
createPortal(_jsx(GridContextMenu, { x: menuPosition.x, y: menuPosition.y, hasSelection: hasCellSelection, canUndo: canUndo, canRedo: canRedo, onUndo: onUndo ?? (() => { }), onRedo: onRedo ?? (() => { }), onCopy: handleCopy, onCut: handleCut, onPaste: () => void handlePaste(), onSelectAll: handleSelectAllCells, onClose: closeContextMenu }), document.body)] }), isLoading && items.length > 0 && (_jsx("div", { className: styles.loadingOverlay, "aria-live": "polite", children: _jsxs("div", { className: styles.loadingOverlayContent, children: [_jsx(Spinner, { size: "small" }), _jsx("span", { className: styles.loadingOverlayText, children: loadingMessage })] }) }))] }));
|
|
241
269
|
}
|
|
242
270
|
export const DataGridTable = React.memo(DataGridTableInner);
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
border: none;
|
|
39
39
|
background-color: var(--colorNeutralBackground1, #ffffff);
|
|
40
40
|
-webkit-overflow-scrolling: touch;
|
|
41
|
+
will-change: scroll-position;
|
|
41
42
|
/* Wide tables: allow horizontal scroll */
|
|
42
43
|
}
|
|
43
44
|
.tableWrapper[data-overflow-x=true] {
|
|
@@ -330,7 +331,7 @@
|
|
|
330
331
|
.stickyHeader {
|
|
331
332
|
position: sticky;
|
|
332
333
|
top: 0;
|
|
333
|
-
z-index:
|
|
334
|
+
z-index: 6;
|
|
334
335
|
background-color: var(--colorSubtleBackgroundSelected, #f3f2f1);
|
|
335
336
|
}
|
|
336
337
|
|
|
@@ -340,6 +341,7 @@
|
|
|
340
341
|
left: 0;
|
|
341
342
|
z-index: 2;
|
|
342
343
|
background-color: var(--colorNeutralBackground1, #ffffff);
|
|
344
|
+
will-change: transform;
|
|
343
345
|
}
|
|
344
346
|
|
|
345
347
|
:global(.fui-DataGridHeader) .freezeColFirst {
|
|
@@ -353,7 +355,7 @@
|
|
|
353
355
|
.stickyHeader .pinnedLeft,
|
|
354
356
|
.stickyHeader .pinnedRight {
|
|
355
357
|
top: 0;
|
|
356
|
-
z-index:
|
|
358
|
+
z-index: 7;
|
|
357
359
|
}
|
|
358
360
|
|
|
359
361
|
.activeRow :global .fui-DataGridCell:not(:has(.activeCellContent)) {
|
|
@@ -364,6 +366,7 @@
|
|
|
364
366
|
position: sticky;
|
|
365
367
|
z-index: 2;
|
|
366
368
|
background-color: inherit;
|
|
369
|
+
will-change: transform;
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
.pinnedLeft {
|
|
@@ -6,13 +6,11 @@ import { ColumnChooser } from '../ColumnChooser/ColumnChooser';
|
|
|
6
6
|
import { PaginationControls } from '../PaginationControls/PaginationControls';
|
|
7
7
|
import { useOGrid, OGridLayout, } from '@alaarab/ogrid-core';
|
|
8
8
|
const OGridInner = forwardRef(function OGridInner(props, ref) {
|
|
9
|
-
const { dataGridProps, page, pageSize, displayTotalCount, setPage, setPageSize, columnChooserColumns, visibleColumns, handleVisibilityChange, toolbar, className, entityLabelPlural, pageSizeOptions, sideBarProps, columnChooserPlacement, } = useOGrid(props, ref);
|
|
10
|
-
return (_jsx(OGridLayout, { className: className, sideBar: sideBarProps, toolbar: toolbar, toolbarEnd: columnChooserPlacement === 'toolbar' ? (_jsx(ColumnChooser, { columns: columnChooserColumns, visibleColumns: visibleColumns, onVisibilityChange: handleVisibilityChange })) : undefined, pagination: _jsx(PaginationControls, { currentPage: page, pageSize: pageSize, totalCount: displayTotalCount, onPageChange: setPage, onPageSizeChange: (size) => {
|
|
9
|
+
const { dataGridProps, page, pageSize, displayTotalCount, setPage, setPageSize, columnChooserColumns, visibleColumns, handleVisibilityChange, toolbar, toolbarBelow, className, entityLabelPlural, pageSizeOptions, sideBarProps, columnChooserPlacement, } = useOGrid(props, ref);
|
|
10
|
+
return (_jsx(OGridLayout, { className: className, sideBar: sideBarProps, toolbar: toolbar, toolbarBelow: toolbarBelow, toolbarEnd: columnChooserPlacement === 'toolbar' ? (_jsx(ColumnChooser, { columns: columnChooserColumns, visibleColumns: visibleColumns, onVisibilityChange: handleVisibilityChange })) : undefined, pagination: _jsx(PaginationControls, { currentPage: page, pageSize: pageSize, totalCount: displayTotalCount, onPageChange: setPage, onPageSizeChange: (size) => {
|
|
11
11
|
setPageSize(size);
|
|
12
12
|
setPage(1);
|
|
13
13
|
}, pageSizeOptions: pageSizeOptions, entityLabelPlural: entityLabelPlural }), children: _jsx(DataGridTable, { ...dataGridProps }) }));
|
|
14
14
|
});
|
|
15
15
|
OGridInner.displayName = 'OGrid';
|
|
16
16
|
export const OGrid = React.memo(OGridInner);
|
|
17
|
-
/** @deprecated Use OGrid and IOGridProps. Kept for backward compatibility. */
|
|
18
|
-
export const FluentDataTable = OGrid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { OGrid
|
|
1
|
+
export { OGrid } from './FluentDataTable';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Components
|
|
2
|
-
export { OGrid
|
|
2
|
+
export { OGrid } from './FluentDataTable';
|
|
3
3
|
export { DataGridTable } from './DataGridTable/DataGridTable';
|
|
4
4
|
export { ColumnChooser } from './ColumnChooser/ColumnChooser';
|
|
5
5
|
export { ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter';
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { IOGridDataGridProps } from '@alaarab/ogrid-core';
|
|
3
|
-
/** @deprecated Use IOGridDataGridProps from @alaarab/ogrid-core for new code. */
|
|
4
|
-
export type IDataGridTableProps<T> = IOGridDataGridProps<T>;
|
|
5
3
|
declare function DataGridTableInner<T>(props: IOGridDataGridProps<T>): React.ReactElement;
|
|
6
4
|
export declare const DataGridTable: typeof DataGridTableInner;
|
|
7
5
|
export {};
|
|
@@ -3,7 +3,3 @@ import { type IOGridProps, type IOGridApi } from '@alaarab/ogrid-core';
|
|
|
3
3
|
export type { IOGridProps } from '@alaarab/ogrid-core';
|
|
4
4
|
declare const OGridInner: React.ForwardRefExoticComponent<IOGridProps<unknown> & React.RefAttributes<IOGridApi<unknown>>>;
|
|
5
5
|
export declare const OGrid: typeof OGridInner;
|
|
6
|
-
/** @deprecated Use OGrid and IOGridProps. Kept for backward compatibility. */
|
|
7
|
-
export declare const FluentDataTable: React.ForwardRefExoticComponent<IOGridProps<unknown> & React.RefAttributes<IOGridApi<unknown>>>;
|
|
8
|
-
/** @deprecated Use IOGridProps. Kept for backward compatibility. */
|
|
9
|
-
export type IFluentDataTableProps<T> = IOGridProps<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { OGrid,
|
|
1
|
+
export { OGrid, type IOGridProps } from './FluentDataTable';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { OGrid, type IOGridProps
|
|
2
|
-
export { DataGridTable
|
|
1
|
+
export { OGrid, type IOGridProps } from './FluentDataTable';
|
|
2
|
+
export { DataGridTable } from './DataGridTable/DataGridTable';
|
|
3
3
|
export { ColumnChooser, type IColumnChooserProps } from './ColumnChooser/ColumnChooser';
|
|
4
4
|
export { ColumnHeaderFilter, type IColumnHeaderFilterProps } from './ColumnHeaderFilter/ColumnHeaderFilter';
|
|
5
5
|
export { PaginationControls, type IPaginationControlsProps } from './PaginationControls/PaginationControls';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-fluent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "OGrid Fluent UI implementation – DataGrid-powered data table with sorting, filtering, pagination, column chooser, and CSV export.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=18"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@alaarab/ogrid-core": "^1.
|
|
43
|
+
"@alaarab/ogrid-core": "^1.7.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@fluentui/react-components": "^9.0.0",
|