@alaarab/ogrid-react-material 2.0.18 → 2.0.21

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,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
3
  import { Popover, Tooltip, IconButton, Box, Typography } from '@mui/material';
4
- import { ArrowUpward as ArrowUpwardIcon, ArrowDownward as ArrowDownwardIcon, SwapVert as SwapVertIcon, FilterList as FilterListIcon, } from '@mui/icons-material';
4
+ import { FilterList as FilterListIcon, } from '@mui/icons-material';
5
5
  import { useColumnHeaderFilterState, getColumnHeaderFilterStateParams, renderFilterContent, } from '@alaarab/ogrid-react';
6
6
  import { TextFilterPopover } from './TextFilterPopover';
7
7
  import { MultiSelectFilterPopover } from './MultiSelectFilterPopover';
@@ -13,18 +13,18 @@ const materialRenderers = {
13
13
  renderDate: (p) => (_jsxs(Box, { sx: { p: 1.5, display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Typography, { variant: "caption", sx: { minWidth: 36 }, children: "From:" }), _jsx("input", { type: "date", value: p.tempDateFrom, onChange: (e) => p.setTempDateFrom(e.target.value), style: { flex: 1, padding: '4px 6px' } })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Typography, { variant: "caption", sx: { minWidth: 36 }, children: "To:" }), _jsx("input", { type: "date", value: p.tempDateTo, onChange: (e) => p.setTempDateTo(e.target.value), style: { flex: 1, padding: '4px 6px' } })] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 1, mt: 0.5 }, children: [_jsx("button", { onClick: p.onClear, disabled: !p.tempDateFrom && !p.tempDateTo, style: { padding: '4px 12px', cursor: 'pointer' }, children: "Clear" }), _jsx("button", { onClick: p.onApply, style: { padding: '4px 12px', cursor: 'pointer' }, children: "Apply" })] })] })),
14
14
  };
15
15
  export const ColumnHeaderFilter = React.memo((props) => {
16
- const { columnName, filterType, isSorted = false, isSortedDescending = false, onSort, options = [], isLoadingOptions = false, selectedUser, } = props;
16
+ const { columnName, filterType, options = [], isLoadingOptions = false, selectedUser, } = props;
17
17
  const state = useColumnHeaderFilterState(getColumnHeaderFilterStateParams(props));
18
18
  const { headerRef, isFilterOpen, setFilterOpen, hasActiveFilter, popoverPosition, handlers, } = state;
19
- return (_jsxs(Box, { ref: headerRef, sx: { display: 'flex', alignItems: 'center', width: '100%', minWidth: 0 }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0, overflow: 'hidden' }, children: _jsx(Tooltip, { title: columnName, arrow: true, children: _jsx(Typography, { variant: "body2", fontWeight: 600, noWrap: true, "data-header-label": true, sx: { lineHeight: 1.4 }, children: columnName }) }) }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', ml: 0.5, flexShrink: 0 }, children: [onSort && (_jsx(IconButton, { size: "small", onClick: handlers.handleSortClick, "aria-label": `Sort by ${columnName}`, title: isSorted ? (isSortedDescending ? 'Sorted descending' : 'Sorted ascending') : 'Sort', color: isSorted ? 'primary' : 'default', sx: { p: 0.25 }, children: isSorted ? (isSortedDescending ? (_jsx(ArrowDownwardIcon, { sx: { fontSize: 16 } })) : (_jsx(ArrowUpwardIcon, { sx: { fontSize: 16 } }))) : (_jsx(SwapVertIcon, { sx: { fontSize: 16 } })) })), filterType !== 'none' && (_jsxs(IconButton, { size: "small", onClick: handlers.handleFilterIconClick, "aria-label": `Filter ${columnName}`, title: `Filter ${columnName}`, color: hasActiveFilter || isFilterOpen ? 'primary' : 'default', sx: { p: 0.25, position: 'relative' }, children: [_jsx(FilterListIcon, { sx: { fontSize: 16 } }), hasActiveFilter && (_jsx(Box, { sx: {
20
- position: 'absolute',
21
- top: 2,
22
- right: 2,
23
- width: 6,
24
- height: 6,
25
- borderRadius: '50%',
26
- bgcolor: 'primary.main',
27
- } }))] }))] }), _jsxs(Popover, { open: isFilterOpen && filterType !== 'none', onClose: () => setFilterOpen(false), anchorReference: "anchorPosition", anchorPosition: popoverPosition ?? { top: 0, left: 0 }, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, transformOrigin: { vertical: 'top', horizontal: 'left' }, slotProps: {
19
+ return (_jsxs(Box, { ref: headerRef, sx: { display: 'flex', alignItems: 'center', width: '100%', minWidth: 0 }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0, overflow: 'hidden' }, children: _jsx(Tooltip, { title: columnName, arrow: true, children: _jsx(Typography, { variant: "body2", fontWeight: 600, noWrap: true, "data-header-label": true, sx: { lineHeight: 1.4 }, children: columnName }) }) }), _jsx(Box, { sx: { display: 'flex', alignItems: 'center', ml: 0.5, flexShrink: 0 }, children: filterType !== 'none' && (_jsxs(IconButton, { size: "small", onClick: handlers.handleFilterIconClick, "aria-label": `Filter ${columnName}`, title: `Filter ${columnName}`, color: hasActiveFilter || isFilterOpen ? 'primary' : 'default', sx: { p: 0.25, position: 'relative' }, children: [_jsx(FilterListIcon, { sx: { fontSize: 16 } }), hasActiveFilter && (_jsx(Box, { sx: {
20
+ position: 'absolute',
21
+ top: 2,
22
+ right: 2,
23
+ width: 6,
24
+ height: 6,
25
+ borderRadius: '50%',
26
+ bgcolor: 'primary.main',
27
+ } }))] })) }), _jsxs(Popover, { open: isFilterOpen && filterType !== 'none', onClose: () => setFilterOpen(false), anchorReference: "anchorPosition", anchorPosition: popoverPosition ?? { top: 0, left: 0 }, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, transformOrigin: { vertical: 'top', horizontal: 'left' }, slotProps: {
28
28
  paper: {
29
29
  sx: { mt: 0.5, overflow: 'visible' },
30
30
  onClick: (e) => e.stopPropagation(),
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { TextField, Checkbox, CircularProgress, Button, Box, Typography, InputAdornment, FormControlLabel, } from '@mui/material';
3
3
  import { Search as SearchIcon } from '@mui/icons-material';
4
4
  import { useListVirtualizer } from '@alaarab/ogrid-react';
5
- const ITEM_HEIGHT = 36;
5
+ const ITEM_HEIGHT = 40;
6
6
  export const MultiSelectFilterPopover = ({ searchText, onSearchChange, options, filteredOptions, selected, onOptionToggle, onSelectAll, onClearSelection, onApply, isLoading, }) => {
7
7
  const virt = useListVirtualizer({ count: filteredOptions.length, itemHeight: ITEM_HEIGHT, containerHeight: 240 });
8
8
  return (_jsxs(Box, { sx: { width: 280 }, children: [_jsxs(Box, { sx: { p: 1.5, pb: 0.5 }, children: [_jsx(TextField, { placeholder: "Search...", value: searchText, onChange: (e) => onSearchChange(e.target.value), onKeyDown: (e) => e.stopPropagation(), autoComplete: "off", size: "small", fullWidth: true, slotProps: {
@@ -11,7 +11,7 @@ export const MultiSelectFilterPopover = ({ searchText, onSearchChange, options,
11
11
  },
12
12
  } }), _jsxs(Typography, { variant: "caption", color: "text.secondary", sx: { mt: 0.5, display: 'block' }, children: [filteredOptions.length, " of ", options.length, " options"] })] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'space-between', px: 1.5, py: 0.5 }, children: [_jsxs(Button, { size: "small", onClick: onSelectAll, children: ["Select All (", filteredOptions.length, ")"] }), _jsx(Button, { size: "small", onClick: onClearSelection, children: "Clear" })] }), _jsx(Box, { ref: virt.containerRef, onScroll: virt.onScroll, sx: { maxHeight: 240, overflowY: 'auto', px: 0.5 }, children: isLoading ? (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 2 }, children: _jsx(CircularProgress, { size: 24 }) })) : filteredOptions.length === 0 ? (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { py: 2, textAlign: 'center' }, children: "No options found" })) : (_jsx(Box, { sx: { height: virt.totalHeight, position: 'relative' }, children: virt.visibleItems.map(({ index, offsetTop }) => {
13
13
  const option = filteredOptions[index];
14
- return (_jsx(FormControlLabel, { control: _jsx(Checkbox, { size: "small", checked: selected.has(option), onChange: (e) => onOptionToggle(option, e.target.checked) }), label: _jsx(Typography, { variant: "body2", children: option }), sx: { position: 'absolute', top: offsetTop, width: '100%', boxSizing: 'border-box', display: 'flex', mx: 0, '& .MuiFormControlLabel-label': { flex: 1, minWidth: 0 } } }, option));
14
+ return (_jsx(FormControlLabel, { control: _jsx(Checkbox, { size: "small", checked: selected.has(option), onChange: (e) => onOptionToggle(option, e.target.checked) }), label: _jsx(Typography, { variant: "body2", children: option }), sx: { position: 'absolute', top: offsetTop, width: '100%', height: ITEM_HEIGHT, boxSizing: 'border-box', display: 'flex', alignItems: 'center', mx: 0, '& .MuiFormControlLabel-label': { flex: 1, minWidth: 0 } } }, option));
15
15
  }) })) }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 1, p: 1.5, pt: 1, borderTop: 1, borderColor: 'divider' }, children: [_jsx(Button, { size: "small", onClick: onClearSelection, children: "Clear" }), _jsx(Button, { size: "small", variant: "contained", onClick: onApply, children: "Apply" })] })] }));
16
16
  };
17
17
  MultiSelectFilterPopover.displayName = 'MultiSelectFilterPopover';
@@ -30,7 +30,7 @@ export function ColumnHeaderMenu(props) {
30
30
  isResizable,
31
31
  }), [canPinLeft, canPinRight, canUnpin, currentSort, isSortable, isResizable]);
32
32
  const items = useMemo(() => getColumnHeaderMenuItems(menuInput), [menuInput]);
33
- const handlers = {
33
+ const handlers = useMemo(() => ({
34
34
  pinLeft: onPinLeft,
35
35
  pinRight: onPinRight,
36
36
  unpin: onUnpin,
@@ -39,7 +39,7 @@ export function ColumnHeaderMenu(props) {
39
39
  clearSort: onClearSort,
40
40
  autosizeThis: onAutosizeThis,
41
41
  autosizeAll: onAutosizeAll,
42
- };
42
+ }), [onPinLeft, onPinRight, onUnpin, onSortAsc, onSortDesc, onClearSort, onAutosizeThis, onAutosizeAll]);
43
43
  return (_jsx(Menu, { open: isOpen && !!anchorPosition, onClose: onClose, anchorReference: "anchorPosition", anchorPosition: anchorPosition, slotProps: {
44
44
  paper: {
45
45
  sx: {
@@ -177,6 +177,29 @@ const POPOVER_ANCHOR_SX = { minHeight: '100%', minWidth: 40 };
177
177
  const POPOVER_CONTENT_SX = { p: 1 };
178
178
  // Wrapper
179
179
  const WRAPPER_SCROLL_SX = { display: 'flex', flexDirection: 'column', minHeight: '100%' };
180
+ // Header cell content wrapper
181
+ const HEADER_CONTENT_FLEX_SX = { display: 'flex', alignItems: 'center', gap: 0.5 };
182
+ // Column options button
183
+ const COLUMN_OPTIONS_BUTTON_SX = {
184
+ background: 'transparent',
185
+ border: 'none',
186
+ cursor: 'pointer',
187
+ padding: '2px 4px',
188
+ fontSize: '16px',
189
+ lineHeight: 1,
190
+ color: 'text.secondary',
191
+ opacity: 1,
192
+ transition: 'background-color 0.15s',
193
+ borderRadius: '3px',
194
+ display: 'flex',
195
+ alignItems: 'center',
196
+ justifyContent: 'center',
197
+ minWidth: '20px',
198
+ height: '20px',
199
+ '&:hover': {
200
+ bgcolor: 'action.hover',
201
+ },
202
+ };
180
203
  // Table wrapper
181
204
  const TABLE_WRAPPER_SX = { position: 'relative', opacity: 1 };
182
205
  const TABLE_WRAPPER_LOADING_SX = { position: 'relative', opacity: 0.6 };
@@ -202,10 +225,9 @@ function GridRowInner(props) {
202
225
  const GridRow = React.memo(GridRowInner, areGridRowPropsEqual);
203
226
  function DataGridTableInner(props) {
204
227
  const o = useDataGridTableOrchestration({ props });
205
- const { wrapperRef, tableContainerRef, lastMouseShiftRef, interaction, pinning, handleResizeStart, getColumnWidth, isReorderDragging, dropIndicatorX, handleHeaderMouseDown, virtualScrollEnabled, visibleRange, items, getRowId, emptyState, suppressHorizontalScroll, isLoading, loadingMessage, ariaLabel, ariaLabelledBy, columnReorder, density, rowNumberOffset, headerRows, allowOverflowX, fitToContent, editCallbacks, interactionHandlers, cellDescriptorInputRef, pendingEditorValueRef, popoverAnchorElRef, handleSingleRowClick, handlePasteVoid, visibleCols, hasCheckboxCol, hasRowNumbersCol, colOffset, minTableWidth, columnSizingOverrides, measuredColumnWidths, selectedRowIds, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected, editingCell, setPopoverAnchorEl, cancelPopoverEdit, setActiveCell, selectionRange, hasCellSelection, handleGridKeyDown, handleFillHandleMouseDown, handleCopy, handleCut, cutRange, copyRange, canUndo, canRedo, onUndo, onRedo, isDragging, menuPosition, closeContextMenu, headerFilterInput, statusBarConfig, showEmptyInGrid, onCellError, } = o;
228
+ const { wrapperRef, tableContainerRef, lastMouseShiftRef, interaction, pinning, handleResizeStart, getColumnWidth, isReorderDragging, dropIndicatorX, handleHeaderMouseDown, virtualScrollEnabled, visibleRange, items, getRowId, emptyState, suppressHorizontalScroll, isLoading, loadingMessage, ariaLabel, ariaLabelledBy, columnReorder, density, rowHeight, rowNumberOffset, headerRows, allowOverflowX, fitToContent, editCallbacks, interactionHandlers, cellDescriptorInputRef, pendingEditorValueRef, popoverAnchorElRef, handleSingleRowClick, handlePasteVoid, visibleCols, hasCheckboxCol, hasRowNumbersCol, colOffset, minTableWidth, columnSizingOverrides, measuredColumnWidths, selectedRowIds, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected, editingCell, setPopoverAnchorEl, cancelPopoverEdit, setActiveCell, selectionRange, hasCellSelection, handleGridKeyDown, handleFillHandleMouseDown, handleCopy, handleCut, cutRange, copyRange, canUndo, canRedo, onUndo, onRedo, isDragging, menuPosition, closeContextMenu, headerFilterInput, statusBarConfig, showEmptyInGrid, onCellError, headerMenu, } = o;
206
229
  // Density-aware cell padding
207
230
  const densityPadding = useMemo(() => getDensityPadding(density), [density]);
208
- const _cellSx = useMemo(() => ({ ...CELL_CONTENT_BASE_SX, ...densityPadding }), [densityPadding]);
209
231
  const headerCellSx = useMemo(() => ({ px: densityPadding.px, py: densityPadding.py }), [densityPadding]);
210
232
  // Pre-compute per-column layout (tdSx, widths) so GridRow doesn't recalculate per-cell
211
233
  const columnLayouts = useMemo(() => visibleCols.map((col) => {
@@ -240,7 +262,8 @@ function DataGridTableInner(props) {
240
262
  willChange: 'scroll-position',
241
263
  '& [data-drag-range]': { bgcolor: 'rgba(33, 115, 70, 0.12) !important' },
242
264
  '& [data-drag-anchor]': { bgcolor: 'background.paper !important' },
243
- }), [fitToContent, suppressHorizontalScroll, allowOverflowX, isLoading, items.length]);
265
+ ...(rowHeight ? { '& tbody tr': { height: rowHeight } } : {}),
266
+ }), [fitToContent, suppressHorizontalScroll, allowOverflowX, isLoading, items.length, rowHeight]);
244
267
  const renderCellContent = useCallback((item, col, rowIndex, colIdx) => {
245
268
  const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInputRef.current);
246
269
  const rowId = getRowId(item);
@@ -342,29 +365,10 @@ function DataGridTableInner(props) {
342
365
  },
343
366
  },
344
367
  onMouseDown: columnReorder ? (e) => handleHeaderMouseDown(col.columnId, e) : undefined
345
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.5 }, children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx(Box, { component: "button", onClick: (e) => {
368
+ }, children: [_jsxs(Box, { sx: HEADER_CONTENT_FLEX_SX, children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx(Box, { component: "button", onClick: (e) => {
346
369
  e.stopPropagation();
347
- pinning.headerMenu.open(col.columnId, e.currentTarget);
348
- }, "aria-label": "Column options", title: "Column options", sx: {
349
- background: 'transparent',
350
- border: 'none',
351
- cursor: 'pointer',
352
- padding: '2px 4px',
353
- fontSize: '16px',
354
- lineHeight: 1,
355
- color: 'text.secondary',
356
- opacity: 1,
357
- transition: 'background-color 0.15s',
358
- borderRadius: '3px',
359
- display: 'flex',
360
- alignItems: 'center',
361
- justifyContent: 'center',
362
- minWidth: '20px',
363
- height: '20px',
364
- '&:hover': {
365
- bgcolor: 'action.hover',
366
- },
367
- }, children: "\u22EE" })] }), _jsx(Box, { onMouseDown: (e) => {
370
+ headerMenu.open(col.columnId, e.currentTarget);
371
+ }, "aria-label": "Column options", title: "Column options", sx: COLUMN_OPTIONS_BUTTON_SX, children: "\u22EE" })] }), _jsx(Box, { onMouseDown: (e) => {
368
372
  setActiveCell(null);
369
373
  interaction.setSelectionRange(null);
370
374
  wrapperRef.current?.focus({ preventScroll: true });
@@ -380,6 +384,6 @@ function DataGridTableInner(props) {
380
384
  const rowIdStr = getRowId(item);
381
385
  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, hasRowNumbersCol: hasRowNumbersCol, rowNumberOffset: rowNumberOffset, selectionRange: selectionRange, activeCell: interaction.activeCell, cutRange: cutRange, copyRange: copyRange, isDragging: isDragging, editingRowId: editingCell?.rowId ?? null }, rowIdStr));
382
386
  })), virtualScrollEnabled && visibleRange.offsetBottom > 0 && (_jsx(TableRow, { style: { height: visibleRange.offsetBottom }, "aria-hidden": true }))] }))] }), isReorderDragging && dropIndicatorX != null && (_jsx(DropIndicator, { dropIndicatorX: dropIndicatorX, wrapperLeft: wrapperRef.current?.getBoundingClientRect().left ?? 0 })), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset, items: items, visibleColumns: props.visibleColumns, columnSizingOverrides: columnSizingOverrides, columnOrder: props.columnOrder }), showEmptyInGrid && emptyState && (_jsx(EmptyState, { emptyState: emptyState }))] }) }) }), menuPosition &&
383
- 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: o.interaction.handleSelectAllCells, onClose: closeContextMenu }), document.body), _jsx(ColumnHeaderMenu, { columnId: pinning.headerMenu.openForColumn || '', isOpen: pinning.headerMenu.isOpen, anchorElement: pinning.headerMenu.anchorElement, onClose: pinning.headerMenu.close, onPinLeft: pinning.headerMenu.handlePinLeft, onPinRight: pinning.headerMenu.handlePinRight, onUnpin: pinning.headerMenu.handleUnpin, onSortAsc: pinning.headerMenu.handleSortAsc, onSortDesc: pinning.headerMenu.handleSortDesc, onClearSort: pinning.headerMenu.handleClearSort, onAutosizeThis: pinning.headerMenu.handleAutosizeThis, onAutosizeAll: pinning.headerMenu.handleAutosizeAll, canPinLeft: pinning.headerMenu.canPinLeft, canPinRight: pinning.headerMenu.canPinRight, canUnpin: pinning.headerMenu.canUnpin, currentSort: pinning.headerMenu.currentSort, isSortable: pinning.headerMenu.isSortable, isResizable: pinning.headerMenu.isResizable })] }), 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(LoadingOverlay, { message: loadingMessage }))] }));
387
+ 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: o.interaction.handleSelectAllCells, onClose: closeContextMenu }), document.body), _jsx(ColumnHeaderMenu, { isOpen: headerMenu.isOpen, anchorElement: headerMenu.anchorElement, onClose: headerMenu.close, onPinLeft: headerMenu.handlePinLeft, onPinRight: headerMenu.handlePinRight, onUnpin: headerMenu.handleUnpin, onSortAsc: headerMenu.handleSortAsc, onSortDesc: headerMenu.handleSortDesc, onClearSort: headerMenu.handleClearSort, onAutosizeThis: headerMenu.handleAutosizeThis, onAutosizeAll: headerMenu.handleAutosizeAll, canPinLeft: headerMenu.canPinLeft, canPinRight: headerMenu.canPinRight, canUnpin: headerMenu.canUnpin, currentSort: headerMenu.currentSort, isSortable: headerMenu.isSortable, isResizable: headerMenu.isResizable })] }), 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(LoadingOverlay, { message: loadingMessage }))] }));
384
388
  }
385
389
  export const DataGridTable = React.memo(DataGridTableInner);
@@ -10,7 +10,7 @@ const OGridInner = forwardRef(function OGridInner(props, ref) {
10
10
  const { dataGridProps, pagination, columnChooser, layout } = useOGrid(props, ref);
11
11
  const theme = useTheme();
12
12
  // Set --ogrid-* CSS variables so the shared OGridLayout adapts to MUI theme (both modes)
13
- const containerSx = {
13
+ const containerSx = React.useMemo(() => ({
14
14
  display: 'flex', flexDirection: 'column', gap: 1,
15
15
  '--ogrid-bg': theme.palette.background.default,
16
16
  '--ogrid-border': theme.palette.divider,
@@ -19,7 +19,7 @@ const OGridInner = forwardRef(function OGridInner(props, ref) {
19
19
  '--ogrid-fg-secondary': theme.palette.text.secondary,
20
20
  '--ogrid-fg-muted': theme.palette.text.disabled,
21
21
  '--ogrid-hover-bg': theme.palette.action.hover,
22
- };
22
+ }), [theme]);
23
23
  return (_jsx(OGridLayout, { containerComponent: Box, containerProps: { sx: containerSx }, className: layout.className, sideBar: layout.sideBarProps, toolbar: layout.toolbar, toolbarBelow: layout.toolbarBelow, toolbarEnd: columnChooser.placement === 'toolbar' ? (_jsx(ColumnChooser, { columns: columnChooser.columns, visibleColumns: columnChooser.visibleColumns, onVisibilityChange: columnChooser.onVisibilityChange })) : undefined, pagination: _jsx(PaginationControls, { currentPage: pagination.page, pageSize: pagination.pageSize, totalCount: pagination.displayTotalCount, onPageChange: pagination.setPage, onPageSizeChange: (size) => {
24
24
  pagination.setPageSize(size);
25
25
  pagination.setPage(1);
package/dist/esm/index.js CHANGED
@@ -5,5 +5,7 @@ export { ColumnChooser } from './ColumnChooser/ColumnChooser';
5
5
  export { ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter';
6
6
  export { PaginationControls } from './PaginationControls/PaginationControls';
7
7
  export { ColumnHeaderMenu } from './ColumnHeaderMenu/ColumnHeaderMenu';
8
- // Re-export everything from core
8
+ // Re-export all from base package for consumer convenience.
9
+ // Note: This prevents tree-shaking of unused utilities.
10
+ // Consider explicit named exports in a future major version.
9
11
  export * from '@alaarab/ogrid-react';
@@ -1,5 +1,4 @@
1
1
  export interface ColumnHeaderMenuProps {
2
- columnId: string;
3
2
  isOpen: boolean;
4
3
  anchorElement: HTMLElement | null;
5
4
  onClose: () => void;
@@ -1,12 +1,4 @@
1
1
  import * as React from 'react';
2
- import type { IColumnDef } from '@alaarab/ogrid-react';
3
- export interface InlineCellEditorProps<T> {
4
- value: unknown;
5
- item: T;
6
- column: IColumnDef<T>;
7
- rowIndex: number;
8
- editorType: 'text' | 'select' | 'checkbox' | 'richSelect' | 'date';
9
- onCommit: (value: unknown) => void;
10
- onCancel: () => void;
11
- }
2
+ import type { InlineCellEditorProps } from '@alaarab/ogrid-react';
3
+ export type { InlineCellEditorProps } from '@alaarab/ogrid-react';
12
4
  export declare function InlineCellEditor<T>(props: InlineCellEditorProps<T>): React.ReactElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react-material",
3
- "version": "2.0.18",
3
+ "version": "2.0.21",
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.0.18"
42
+ "@alaarab/ogrid-react": "2.0.21"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@emotion/react": "^11.0.0",