@alaarab/ogrid-react-material 2.0.2 → 2.0.3
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.
|
@@ -7,7 +7,7 @@ import { ColumnHeaderFilter } from '../ColumnHeaderFilter';
|
|
|
7
7
|
import { InlineCellEditor } from './InlineCellEditor';
|
|
8
8
|
import { StatusBar } from './StatusBar';
|
|
9
9
|
import { GridContextMenu } from './GridContextMenu';
|
|
10
|
-
import { useDataGridState, useColumnResize, useLatestRef, getHeaderFilterConfig, getCellRenderDescriptor, MarchingAntsOverlay, buildHeaderRows, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, areGridRowPropsEqual, CellErrorBoundary, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, } from '@alaarab/ogrid-react';
|
|
10
|
+
import { useDataGridState, useColumnResize, useColumnReorder, useVirtualScroll, useLatestRef, getHeaderFilterConfig, getCellRenderDescriptor, MarchingAntsOverlay, buildHeaderRows, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, areGridRowPropsEqual, CellErrorBoundary, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, } from '@alaarab/ogrid-react';
|
|
11
11
|
// ── Module-scope stable styles (avoid per-render Emotion resolutions) ──
|
|
12
12
|
const gridRootSx = { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' };
|
|
13
13
|
// Row
|
|
@@ -117,7 +117,7 @@ const EMPTY_STATE_SX = { py: 4, px: 2, textAlign: 'center', borderTop: 1, border
|
|
|
117
117
|
const LOADING_OVERLAY_SX = {
|
|
118
118
|
position: 'absolute', inset: 0, zIndex: 2,
|
|
119
119
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
120
|
-
|
|
120
|
+
background: 'var(--ogrid-loading-bg, rgba(255,255,255,0.7))',
|
|
121
121
|
};
|
|
122
122
|
const LOADING_INNER_SX = {
|
|
123
123
|
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1,
|
|
@@ -145,7 +145,7 @@ function DataGridTableInner(props) {
|
|
|
145
145
|
const handlePasteVoid = useCallback(() => { void handlePaste(); }, [handlePaste]);
|
|
146
146
|
const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
|
|
147
147
|
const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError } = viewModels;
|
|
148
|
-
const { items, getRowId, emptyState, layoutMode = 'fill', rowSelection = 'none', freezeRows, freezeCols, suppressHorizontalScroll, isLoading = false, loadingMessage = 'Loading\u2026', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, } = props;
|
|
148
|
+
const { items, getRowId, emptyState, layoutMode = 'fill', rowSelection = 'none', freezeRows, freezeCols, suppressHorizontalScroll, isLoading = false, loadingMessage = 'Loading\u2026', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, columnOrder, onColumnOrderChange, columnReorder, virtualScroll, pinnedColumns, } = props;
|
|
149
149
|
const fitToContent = layoutMode === 'content';
|
|
150
150
|
const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
|
|
151
151
|
// Memoize header rows (recursive tree traversal)
|
|
@@ -154,6 +154,23 @@ function DataGridTableInner(props) {
|
|
|
154
154
|
columnSizingOverrides,
|
|
155
155
|
setColumnSizingOverrides,
|
|
156
156
|
});
|
|
157
|
+
const { isDragging: isReorderDragging, dropIndicatorX, handleHeaderMouseDown } = useColumnReorder({
|
|
158
|
+
columns: visibleCols,
|
|
159
|
+
columnOrder,
|
|
160
|
+
onColumnOrderChange,
|
|
161
|
+
enabled: columnReorder === true,
|
|
162
|
+
pinnedColumns,
|
|
163
|
+
wrapperRef,
|
|
164
|
+
});
|
|
165
|
+
const virtualScrollEnabled = virtualScroll?.enabled === true;
|
|
166
|
+
const virtualRowHeight = virtualScroll?.rowHeight ?? 36;
|
|
167
|
+
const { visibleRange } = useVirtualScroll({
|
|
168
|
+
totalRows: items.length,
|
|
169
|
+
rowHeight: virtualRowHeight,
|
|
170
|
+
enabled: virtualScrollEnabled,
|
|
171
|
+
overscan: virtualScroll?.overscan,
|
|
172
|
+
containerRef: wrapperRef,
|
|
173
|
+
});
|
|
157
174
|
// Pre-compute per-column layout (tdSx, widths) so GridRow doesn't recalculate per-cell
|
|
158
175
|
const columnLayouts = useMemo(() => visibleCols.map((col, colIdx) => {
|
|
159
176
|
const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx < freezeCols;
|
|
@@ -233,11 +250,32 @@ function DataGridTableInner(props) {
|
|
|
233
250
|
const isPinnedRight = col.pinned === 'right';
|
|
234
251
|
const columnWidth = getColumnWidth(col);
|
|
235
252
|
const headerSx = isPinnedLeft || (isFreezeCol && colIdx === 0) ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX;
|
|
236
|
-
return (_jsxs(TableCell, { component: "th", scope: "col", rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : undefined, sx: headerSx, style: {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
253
|
+
return (_jsxs(TableCell, { component: "th", scope: "col", "data-column-id": col.columnId, rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : undefined, sx: headerSx, style: {
|
|
254
|
+
minWidth: col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH,
|
|
255
|
+
width: columnWidth,
|
|
256
|
+
maxWidth: columnWidth,
|
|
257
|
+
...(columnReorder ? { cursor: isReorderDragging ? 'grabbing' : 'grab' } : undefined),
|
|
258
|
+
}, onMouseDown: columnReorder ? (e) => handleHeaderMouseDown(col.columnId, e) : undefined, children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx(Box, { onMouseDown: (e) => handleResizeStart(e, col), sx: RESIZE_HANDLE_SX })] }, col.columnId));
|
|
259
|
+
})] }, rowIdx))) }), !showEmptyInGrid && (_jsxs(TableBody, { children: [virtualScrollEnabled && visibleRange.offsetTop > 0 && (_jsx(TableRow, { style: { height: visibleRange.offsetTop }, "aria-hidden": true })), (virtualScrollEnabled
|
|
260
|
+
? items.slice(visibleRange.startIndex, visibleRange.endIndex + 1).map((item, i) => {
|
|
261
|
+
const rowIndex = visibleRange.startIndex + i;
|
|
262
|
+
const rowIdStr = getRowId(item);
|
|
263
|
+
return (_jsx(GridRow, { item: item, rowIndex: rowIndex, rowId: rowIdStr, isSelected: selectedRowIds.has(rowIdStr), columnLayouts: columnLayouts, renderCellContent: renderCellContent, handleSingleRowClick: handleSingleRowClick, handleRowCheckboxChange: handleRowCheckboxChange, lastMouseShiftRef: lastMouseShiftRef, hasCheckboxCol: hasCheckboxCol, selectionRange: selectionRange, activeCell: interaction.activeCell, cutRange: cutRange, copyRange: copyRange, isDragging: isDragging, editingRowId: editingCell?.rowId ?? null }, rowIdStr));
|
|
264
|
+
})
|
|
265
|
+
: items.map((item, rowIndex) => {
|
|
266
|
+
const rowIdStr = getRowId(item);
|
|
267
|
+
return (_jsx(GridRow, { item: item, rowIndex: rowIndex, rowId: rowIdStr, isSelected: selectedRowIds.has(rowIdStr), columnLayouts: columnLayouts, renderCellContent: renderCellContent, handleSingleRowClick: handleSingleRowClick, handleRowCheckboxChange: handleRowCheckboxChange, lastMouseShiftRef: lastMouseShiftRef, hasCheckboxCol: hasCheckboxCol, selectionRange: selectionRange, activeCell: interaction.activeCell, cutRange: cutRange, copyRange: copyRange, isDragging: isDragging, editingRowId: editingCell?.rowId ?? null }, rowIdStr));
|
|
268
|
+
})), virtualScrollEnabled && visibleRange.offsetBottom > 0 && (_jsx(TableRow, { style: { height: visibleRange.offsetBottom }, "aria-hidden": true }))] }))] }), isReorderDragging && dropIndicatorX != null && (_jsx(Box, { sx: {
|
|
269
|
+
position: 'absolute',
|
|
270
|
+
top: 0,
|
|
271
|
+
bottom: 0,
|
|
272
|
+
width: 3,
|
|
273
|
+
bgcolor: 'var(--ogrid-primary, #217346)',
|
|
274
|
+
pointerEvents: 'none',
|
|
275
|
+
zIndex: 100,
|
|
276
|
+
transition: 'left 0.05s',
|
|
277
|
+
left: dropIndicatorX - (wrapperRef.current?.getBoundingClientRect().left ?? 0),
|
|
278
|
+
} })), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset }), showEmptyInGrid && emptyState && (_jsx(Box, { sx: EMPTY_STATE_SX, children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: "No results found" }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: emptyState.message != null ? (emptyState.message) : emptyState.hasActiveFilters ? (_jsxs(_Fragment, { children: ["No items match your current filters. Try adjusting your search or", ' ', _jsx(Button, { variant: "text", size: "small", onClick: emptyState.onClearAll, children: "clear all filters" }), ' ', "to see all items."] })) : ('There are no items available at this time.') })] })) }))] }) }) }), menuPosition &&
|
|
241
279
|
createPortal(_jsx(GridContextMenu, { x: menuPosition.x, y: menuPosition.y, hasSelection: hasCellSelection, canUndo: canUndo, canRedo: canRedo, onUndo: onUndo ?? NOOP, onRedo: onRedo ?? NOOP, onCopy: handleCopy, onCut: handleCut, onPaste: handlePasteVoid, onSelectAll: handleSelectAllCells, onClose: closeContextMenu }), document.body)] }), 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 })), isLoading && (_jsx(Box, { sx: LOADING_OVERLAY_SX, children: _jsxs(Box, { sx: LOADING_INNER_SX, children: [_jsx(CircularProgress, { size: 24 }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: loadingMessage })] }) }))] }));
|
|
242
280
|
}
|
|
243
281
|
export const DataGridTable = React.memo(DataGridTableInner);
|
|
@@ -14,5 +14,5 @@ export function GridContextMenu(props) {
|
|
|
14
14
|
return true;
|
|
15
15
|
return false;
|
|
16
16
|
}, [hasSelection, canUndo, canRedo]);
|
|
17
|
-
return (_jsx(Menu, { open: true, onClose: onClose, anchorReference: "anchorPosition", anchorPosition: { top: y, left: x }, MenuListProps: { dense: true, 'aria-label': 'Grid context menu' }, children: GRID_CONTEXT_MENU_ITEMS.map((item) => (_jsxs(React.Fragment, { children: [item.dividerBefore && _jsx(Divider, {}), _jsxs(MenuItem, { onClick: handlers[item.id], disabled: isDisabled(item), children: [_jsx("span", { style: { flex: 1 }, children: item.label }), item.shortcut && (_jsx("span", { style: { marginLeft: 24, color: 'rgba(0,0,0,0.4)', fontSize: '0.8em' }, children: formatShortcut(item.shortcut) }))] })] }, item.id))) }));
|
|
17
|
+
return (_jsx(Menu, { open: true, onClose: onClose, anchorReference: "anchorPosition", anchorPosition: { top: y, left: x }, MenuListProps: { dense: true, 'aria-label': 'Grid context menu' }, children: GRID_CONTEXT_MENU_ITEMS.map((item) => (_jsxs(React.Fragment, { children: [item.dividerBefore && _jsx(Divider, {}), _jsxs(MenuItem, { onClick: handlers[item.id], disabled: isDisabled(item), children: [_jsx("span", { style: { flex: 1 }, children: item.label }), item.shortcut && (_jsx("span", { style: { marginLeft: 24, color: 'var(--ogrid-fg-muted, rgba(0,0,0,0.4))', fontSize: '0.8em' }, children: formatShortcut(item.shortcut) }))] })] }, item.id))) }));
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-react-material",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "OGrid Material UI implementation – MUI Table–based data grid with sorting, filtering, pagination, column chooser, spreadsheet selection, and CSV export.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"node": ">=18"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@alaarab/ogrid-react": "2.0.
|
|
41
|
+
"@alaarab/ogrid-react": "2.0.3"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@emotion/react": "^11.0.0",
|