@alaarab/ogrid-react-material 2.1.1 → 2.1.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.
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useEffect, useState } from 'react';
|
|
3
3
|
import { Menu, MenuItem, Divider } from '@mui/material';
|
|
4
4
|
import { getColumnHeaderMenuItems } from '@alaarab/ogrid-react';
|
|
5
5
|
/**
|
|
@@ -46,8 +46,16 @@ export function ColumnHeaderMenu(props) {
|
|
|
46
46
|
minWidth: 140,
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
|
-
}, children: items.
|
|
49
|
+
}, children: items.flatMap((item, idx) => {
|
|
50
|
+
const elements = [
|
|
51
|
+
_jsx(MenuItem, { disabled: item.disabled, onClick: () => {
|
|
50
52
|
handlers[item.id]();
|
|
51
53
|
onClose();
|
|
52
|
-
}, children: item.label }
|
|
54
|
+
}, children: item.label }, item.id),
|
|
55
|
+
];
|
|
56
|
+
if (item.divider && idx < items.length - 1) {
|
|
57
|
+
elements.push(_jsx(Divider, {}, `${item.id}-divider`));
|
|
58
|
+
}
|
|
59
|
+
return elements;
|
|
60
|
+
}) }));
|
|
53
61
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { useCallback, useMemo } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
|
-
import { Box, Popover, Checkbox, Table, TableHead, TableBody, TableRow, TableCell,
|
|
5
|
+
import { Box, Popover, Checkbox, Table, TableHead, TableBody, TableRow, TableCell, } from '@mui/material';
|
|
6
6
|
import { ColumnHeaderFilter } from '../ColumnHeaderFilter';
|
|
7
7
|
import { ColumnHeaderMenu } from '../ColumnHeaderMenu';
|
|
8
8
|
import { InlineCellEditor } from './InlineCellEditor';
|
|
@@ -35,15 +35,18 @@ const ROW_HOVER_SX = { '&:hover': { bgcolor: 'action.hover' } };
|
|
|
35
35
|
const CHECKBOX_CELL_SX = { width: CHECKBOX_COLUMN_WIDTH, minWidth: CHECKBOX_COLUMN_WIDTH, maxWidth: CHECKBOX_COLUMN_WIDTH, textAlign: 'center' };
|
|
36
36
|
const CHECKBOX_WRAPPER_SX = { display: 'flex', alignItems: 'center', justifyContent: 'center' };
|
|
37
37
|
const CHECKBOX_PLACEHOLDER_SX = { width: CHECKBOX_COLUMN_WIDTH, minWidth: CHECKBOX_COLUMN_WIDTH, p: 0 };
|
|
38
|
-
// Header
|
|
38
|
+
// Header — use opaque var(--ogrid-header-bg) (not semi-transparent action.hover) so sticky
|
|
39
|
+
// headers fully occlude pinned-column content scrolling beneath them.
|
|
40
|
+
// The CSS variable is theme-aware: light=#f5f5f5, dark=#2c2c2c (set by each UI package).
|
|
41
|
+
const HEADER_BG = 'var(--ogrid-header-bg, #f5f5f5)';
|
|
39
42
|
const STICKY_HEADER_SX = {
|
|
40
43
|
/* Removed position: 'sticky', top: 0 - breaks horizontal sticky on pinned columns.
|
|
41
44
|
Instead, apply sticky to individual header cells (HEADER_BASE_SX). */
|
|
42
45
|
zIndex: 8,
|
|
43
|
-
bgcolor:
|
|
44
|
-
'& th': { bgcolor:
|
|
46
|
+
bgcolor: HEADER_BG,
|
|
47
|
+
'& th': { bgcolor: HEADER_BG }
|
|
45
48
|
};
|
|
46
|
-
const HEADER_ROW_SX = { bgcolor:
|
|
49
|
+
const HEADER_ROW_SX = { bgcolor: HEADER_BG };
|
|
47
50
|
const GROUP_HEADER_CELL_SX = { textAlign: 'center', fontWeight: 600, borderBottom: 2, borderColor: 'divider', py: 0.75 };
|
|
48
51
|
// Density padding helper
|
|
49
52
|
function getDensityPadding(density) {
|
|
@@ -127,42 +130,53 @@ const CELL_TD_BASE_SX = { position: 'relative', p: 0, height: '1px' };
|
|
|
127
130
|
const CELL_TD_PINNED_LEFT_SX = {
|
|
128
131
|
...CELL_TD_BASE_SX, position: 'sticky', left: 0, zIndex: 6,
|
|
129
132
|
bgcolor: 'background.paper', willChange: 'transform',
|
|
130
|
-
'
|
|
131
|
-
|
|
132
|
-
width: '4px', background: 'linear-gradient(to right, rgba(0,0,0,0.12), transparent)', pointerEvents: 'none',
|
|
133
|
-
},
|
|
133
|
+
borderRight: '1px solid', borderRightColor: 'divider',
|
|
134
|
+
boxShadow: '2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
134
135
|
};
|
|
135
136
|
const CELL_TD_PINNED_RIGHT_SX = {
|
|
136
137
|
...CELL_TD_BASE_SX, position: 'sticky', right: 0, zIndex: 6,
|
|
137
138
|
bgcolor: 'background.paper', willChange: 'transform',
|
|
138
|
-
'
|
|
139
|
-
|
|
140
|
-
width: '4px', background: 'linear-gradient(to left, rgba(0,0,0,0.12), transparent)', pointerEvents: 'none',
|
|
141
|
-
},
|
|
139
|
+
borderLeft: '1px solid', borderLeftColor: 'divider',
|
|
140
|
+
boxShadow: '-2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
142
141
|
};
|
|
143
|
-
// Header cell positioning variants
|
|
142
|
+
// Header cell positioning variants (sticky)
|
|
143
|
+
// Use opaque HEADER_BG so headers fully occlude content scrolling beneath them.
|
|
144
144
|
const HEADER_BASE_SX = {
|
|
145
145
|
fontWeight: 600,
|
|
146
146
|
position: 'sticky', /* Enables vertical sticky for all headers */
|
|
147
147
|
top: 0, /* Sticky vertically */
|
|
148
148
|
zIndex: 8, /* Stack above body cells */
|
|
149
|
-
bgcolor:
|
|
149
|
+
bgcolor: HEADER_BG /* Opaque — required for sticky overlap */
|
|
150
150
|
};
|
|
151
151
|
const HEADER_PINNED_LEFT_SX = {
|
|
152
152
|
...HEADER_BASE_SX, position: 'sticky', left: 0, top: 0,
|
|
153
|
-
zIndex: 10, bgcolor:
|
|
154
|
-
'
|
|
155
|
-
|
|
156
|
-
width: '4px', background: 'linear-gradient(to right, rgba(0,0,0,0.12), transparent)', pointerEvents: 'none',
|
|
157
|
-
},
|
|
153
|
+
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
154
|
+
borderRight: '1px solid', borderRightColor: 'divider',
|
|
155
|
+
boxShadow: '2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
158
156
|
};
|
|
159
157
|
const HEADER_PINNED_RIGHT_SX = {
|
|
160
158
|
...HEADER_BASE_SX, position: 'sticky', right: 0, top: 0,
|
|
161
|
-
zIndex: 10, bgcolor:
|
|
162
|
-
'
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
160
|
+
borderLeft: '1px solid', borderLeftColor: 'divider',
|
|
161
|
+
boxShadow: '-2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
162
|
+
};
|
|
163
|
+
// Header cell variants (non-sticky — stickyHeader=false)
|
|
164
|
+
const HEADER_BASE_NO_STICKY_SX = {
|
|
165
|
+
fontWeight: 600,
|
|
166
|
+
zIndex: 8,
|
|
167
|
+
bgcolor: HEADER_BG,
|
|
168
|
+
};
|
|
169
|
+
const HEADER_PINNED_LEFT_NO_STICKY_SX = {
|
|
170
|
+
...HEADER_BASE_NO_STICKY_SX, position: 'sticky', left: 0,
|
|
171
|
+
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
172
|
+
borderRight: '1px solid', borderRightColor: 'divider',
|
|
173
|
+
boxShadow: '2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
174
|
+
};
|
|
175
|
+
const HEADER_PINNED_RIGHT_NO_STICKY_SX = {
|
|
176
|
+
...HEADER_BASE_NO_STICKY_SX, position: 'sticky', right: 0,
|
|
177
|
+
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
178
|
+
borderLeft: '1px solid', borderLeftColor: 'divider',
|
|
179
|
+
boxShadow: '-2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
166
180
|
};
|
|
167
181
|
// Resize handle
|
|
168
182
|
const RESIZE_HANDLE_SX = {
|
|
@@ -300,7 +314,7 @@ function DataGridTableInner(props) {
|
|
|
300
314
|
}
|
|
301
315
|
return (_jsx(CellErrorBoundary, { onError: onCellError, children: cellContent }, `${rowId}-${col.columnId}`));
|
|
302
316
|
}, [editCallbacks, interactionHandlers, handleFillHandleMouseDown, setPopoverAnchorEl, cancelPopoverEdit, getRowId, onCellError, cellDescriptorInputRef, densityPadding, pendingEditorValueRef, popoverAnchorElRef]);
|
|
303
|
-
return (_jsxs(Box, { sx: gridRootSx, children: [_jsxs(Box, { ref: wrapperRef, tabIndex: 0, role: "region", "aria-label": ariaLabel ?? (ariaLabelledBy ? undefined : 'Data grid'), "aria-labelledby": ariaLabelledBy, onMouseDown: (e) => { lastMouseShiftRef.current = e.shiftKey; }, onKeyDown: handleGridKeyDown, onContextMenu: PREVENT_DEFAULT, "data-overflow-x": allowOverflowX ? 'true' : 'false', "data-density": density, sx: wrapperSx, children: [_jsx(Box, { sx: WRAPPER_SCROLL_SX, children: _jsx(
|
|
317
|
+
return (_jsxs(Box, { sx: gridRootSx, children: [_jsxs(Box, { ref: wrapperRef, tabIndex: 0, role: "region", "aria-label": ariaLabel ?? (ariaLabelledBy ? undefined : 'Data grid'), "aria-labelledby": ariaLabelledBy, onMouseDown: (e) => { lastMouseShiftRef.current = e.shiftKey; }, onKeyDown: handleGridKeyDown, onContextMenu: PREVENT_DEFAULT, "data-overflow-x": allowOverflowX ? 'true' : 'false', "data-density": density, sx: wrapperSx, children: [_jsx(Box, { sx: WRAPPER_SCROLL_SX, children: _jsx("div", { style: { minWidth: allowOverflowX ? minTableWidth : undefined }, children: _jsxs(Box, { ref: tableContainerRef, sx: isLoading && items.length > 0 ? TABLE_WRAPPER_LOADING_SX : TABLE_WRAPPER_SX, children: [_jsxs(Table, { size: "small", sx: { minWidth: minTableWidth, borderCollapse: 'separate', borderSpacing: 0 }, children: [_jsx(TableHead, { sx: STICKY_HEADER_SX, children: headerRows.map((row, rowIdx) => (_jsxs(TableRow, { sx: HEADER_ROW_SX, children: [rowIdx === headerRows.length - 1 && hasCheckboxCol && (_jsx(TableCell, { ...{ padding: "checkbox", rowSpan: headerRows.length > 1 ? 1 : undefined, sx: CHECKBOX_CELL_SX }, children: _jsx(Checkbox, { checked: allSelected, indeterminate: someSelected, onChange: (_, c) => handleSelectAll(!!c), size: "small", "aria-label": "Select all rows" }) })), rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol && (_jsx(TableCell, { ...{ rowSpan: headerRows.length - 1, sx: CHECKBOX_PLACEHOLDER_SX } })), rowIdx === headerRows.length - 1 && hasRowNumbersCol && (_jsx(TableCell, { ...{
|
|
304
318
|
component: "th",
|
|
305
319
|
scope: "col",
|
|
306
320
|
rowSpan: headerRows.length > 1 ? 1 : undefined,
|
|
@@ -310,7 +324,7 @@ function DataGridTableInner(props) {
|
|
|
310
324
|
maxWidth: ROW_NUMBER_COLUMN_WIDTH,
|
|
311
325
|
textAlign: 'center',
|
|
312
326
|
fontWeight: 600,
|
|
313
|
-
backgroundColor:
|
|
327
|
+
backgroundColor: HEADER_BG,
|
|
314
328
|
position: 'sticky',
|
|
315
329
|
left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
|
|
316
330
|
zIndex: 4,
|
|
@@ -341,7 +355,9 @@ function DataGridTableInner(props) {
|
|
|
341
355
|
const col = cell.columnDef;
|
|
342
356
|
const isPinnedLeft = pinning.pinnedColumns[col.columnId] === 'left';
|
|
343
357
|
const isPinnedRight = pinning.pinnedColumns[col.columnId] === 'right';
|
|
344
|
-
const baseHeaderSx =
|
|
358
|
+
const baseHeaderSx = o.stickyHeader
|
|
359
|
+
? (isPinnedLeft ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX)
|
|
360
|
+
: (isPinnedLeft ? HEADER_PINNED_LEFT_NO_STICKY_SX : isPinnedRight ? HEADER_PINNED_RIGHT_NO_STICKY_SX : HEADER_BASE_NO_STICKY_SX);
|
|
345
361
|
// Override sticky offset for pinned columns (supports multiple pinned columns)
|
|
346
362
|
const headerSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null
|
|
347
363
|
? { ...baseHeaderSx, left: pinning.leftOffsets[col.columnId] }
|
|
@@ -14,5 +14,12 @@ 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.
|
|
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.flatMap((item) => {
|
|
18
|
+
const elements = [];
|
|
19
|
+
if (item.dividerBefore) {
|
|
20
|
+
elements.push(_jsx(Divider, {}, `${item.id}-divider`));
|
|
21
|
+
}
|
|
22
|
+
elements.push(_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));
|
|
23
|
+
return elements;
|
|
24
|
+
}) }));
|
|
18
25
|
}
|
package/dist/esm/OGrid/OGrid.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { forwardRef } from 'react';
|
|
4
3
|
import { Box, useTheme } from '@mui/material';
|
|
4
|
+
import { createOGrid } from '@alaarab/ogrid-react';
|
|
5
5
|
import { DataGridTable } from '../DataGridTable/DataGridTable';
|
|
6
6
|
import { ColumnChooser } from '../ColumnChooser/ColumnChooser';
|
|
7
7
|
import { PaginationControls } from '../PaginationControls/PaginationControls';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* MUI theme bridge: reads the MUI palette and sets --ogrid-* CSS variables
|
|
10
|
+
* so the shared OGridLayout and DataGridTable styles adapt to the MUI theme.
|
|
11
|
+
*/
|
|
12
|
+
const MuiThemeContainer = React.forwardRef(function MuiThemeContainer(props, ref) {
|
|
11
13
|
const theme = useTheme();
|
|
12
|
-
|
|
13
|
-
const containerSx = React.useMemo(() => ({
|
|
14
|
+
const sx = React.useMemo(() => ({
|
|
14
15
|
display: 'flex', flexDirection: 'column', gap: 1,
|
|
15
16
|
'--ogrid-bg': theme.palette.background.default,
|
|
16
17
|
'--ogrid-border': theme.palette.divider,
|
|
17
|
-
'--ogrid-header-bg': theme.palette.
|
|
18
|
+
'--ogrid-header-bg': theme.palette.mode === 'dark' ? theme.palette.grey[800] : theme.palette.grey[100],
|
|
18
19
|
'--ogrid-fg': theme.palette.text.primary,
|
|
19
20
|
'--ogrid-fg-secondary': theme.palette.text.secondary,
|
|
20
21
|
'--ogrid-fg-muted': theme.palette.text.disabled,
|
|
21
22
|
'--ogrid-hover-bg': theme.palette.action.hover,
|
|
22
23
|
}), [theme]);
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
return _jsx(Box, { ref: ref, sx: sx, ...props });
|
|
25
|
+
});
|
|
26
|
+
export const OGrid = createOGrid({
|
|
27
|
+
DataGridTable: DataGridTable,
|
|
28
|
+
ColumnChooser: ColumnChooser,
|
|
29
|
+
PaginationControls,
|
|
30
|
+
containerComponent: MuiThemeContainer,
|
|
26
31
|
});
|
|
27
|
-
OGridInner.displayName = 'OGrid';
|
|
28
|
-
export const OGrid = React.memo(OGridInner);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type IOGridProps, type IOGridApi } from '@alaarab/ogrid-react';
|
|
3
2
|
export type { IOGridProps } from '@alaarab/ogrid-react';
|
|
4
|
-
declare const
|
|
5
|
-
export declare const OGrid: typeof OGridInner;
|
|
3
|
+
export declare const OGrid: React.ForwardRefExoticComponent<import("@alaarab/ogrid-react").IOGridProps<unknown> & React.RefAttributes<import("@alaarab/ogrid-react").IOGridApi<unknown>>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-react-material",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "OGrid React Material 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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"node": ">=18"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@alaarab/ogrid-react": "2.1.
|
|
42
|
+
"@alaarab/ogrid-react": "2.1.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@emotion/react": "^11.0.0",
|