@alaarab/ogrid-react-material 2.0.17 → 2.0.18

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,9 +1,17 @@
1
1
  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
- export const MultiSelectFilterPopover = ({ searchText, onSearchChange, options, filteredOptions, selected, onOptionToggle, onSelectAll, onClearSelection, onApply, isLoading, }) => (_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: {
5
- input: {
6
- startAdornment: (_jsx(InputAdornment, { position: "start", children: _jsx(SearchIcon, { fontSize: "small" }) })),
7
- },
8
- } }), _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, { 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" })) : (filteredOptions.map((option) => (_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: { display: 'flex', mx: 0, '& .MuiFormControlLabel-label': { flex: 1, minWidth: 0 } } }, option)))) }), _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" })] })] }));
4
+ import { useListVirtualizer } from '@alaarab/ogrid-react';
5
+ const ITEM_HEIGHT = 36;
6
+ export const MultiSelectFilterPopover = ({ searchText, onSearchChange, options, filteredOptions, selected, onOptionToggle, onSelectAll, onClearSelection, onApply, isLoading, }) => {
7
+ const virt = useListVirtualizer({ count: filteredOptions.length, itemHeight: ITEM_HEIGHT, containerHeight: 240 });
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: {
9
+ input: {
10
+ startAdornment: (_jsx(InputAdornment, { position: "start", children: _jsx(SearchIcon, { fontSize: "small" }) })),
11
+ },
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
+ 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));
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
+ };
9
17
  MultiSelectFilterPopover.displayName = 'MultiSelectFilterPopover';
@@ -202,18 +202,17 @@ function GridRowInner(props) {
202
202
  const GridRow = React.memo(GridRowInner, areGridRowPropsEqual);
203
203
  function DataGridTableInner(props) {
204
204
  const o = useDataGridTableOrchestration({ props });
205
- const { wrapperRef, tableContainerRef, lastMouseShiftRef, interaction, pinning, handleResizeStart, getColumnWidth, isReorderDragging, dropIndicatorX, handleHeaderMouseDown, virtualScrollEnabled, visibleRange, items, getRowId, emptyState, freezeRows, freezeCols, 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;
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;
206
206
  // Density-aware cell padding
207
207
  const densityPadding = useMemo(() => getDensityPadding(density), [density]);
208
208
  const _cellSx = useMemo(() => ({ ...CELL_CONTENT_BASE_SX, ...densityPadding }), [densityPadding]);
209
209
  const headerCellSx = useMemo(() => ({ px: densityPadding.px, py: densityPadding.py }), [densityPadding]);
210
210
  // Pre-compute per-column layout (tdSx, widths) so GridRow doesn't recalculate per-cell
211
- const columnLayouts = useMemo(() => visibleCols.map((col, colIdx) => {
212
- const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx < freezeCols;
211
+ const columnLayouts = useMemo(() => visibleCols.map((col) => {
213
212
  const isPinnedLeft = pinning.pinnedColumns[col.columnId] === 'left';
214
213
  const isPinnedRight = pinning.pinnedColumns[col.columnId] === 'right';
215
214
  const columnWidth = getColumnWidth(col);
216
- const baseTdSx = isPinnedLeft || (isFreezeCol && colIdx === 0) ? CELL_TD_PINNED_LEFT_SX : isPinnedRight ? CELL_TD_PINNED_RIGHT_SX : CELL_TD_BASE_SX;
215
+ const baseTdSx = isPinnedLeft ? CELL_TD_PINNED_LEFT_SX : isPinnedRight ? CELL_TD_PINNED_RIGHT_SX : CELL_TD_BASE_SX;
217
216
  // Override sticky offset for pinned columns (supports multiple pinned columns)
218
217
  const tdSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null
219
218
  ? { ...baseTdSx, left: pinning.leftOffsets[col.columnId] }
@@ -227,7 +226,7 @@ function DataGridTableInner(props) {
227
226
  const baseMinWidth = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
228
227
  const effectiveMinWidth = hasResizeOverride ? columnWidth : Math.max(baseMinWidth, measuredW ?? 0);
229
228
  return { col, tdSx, minWidth: effectiveMinWidth, width: columnWidth, maxWidth: columnWidth };
230
- }), [visibleCols, freezeCols, getColumnWidth, columnSizingOverrides, measuredColumnWidths, pinning.pinnedColumns, pinning.leftOffsets, pinning.rightOffsets]);
229
+ }), [visibleCols, getColumnWidth, columnSizingOverrides, measuredColumnWidths, pinning.pinnedColumns, pinning.leftOffsets, pinning.rightOffsets]);
231
230
  // Wrapper sx (depends on dynamic values — memoize to avoid recreation)
232
231
  const wrapperSx = useMemo(() => ({
233
232
  position: 'relative',
@@ -270,7 +269,7 @@ function DataGridTableInner(props) {
270
269
  },
271
270
  // eslint-disable-next-line react-hooks/exhaustive-deps -- *Ref vars are stable refs from useLatestRef
272
271
  [editCallbacks, interactionHandlers, handleFillHandleMouseDown, setPopoverAnchorEl, cancelPopoverEdit, getRowId, onCellError]);
273
- 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(TableContainer, { sx: { 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 }, "data-freeze-rows": freezeRows != null && freezeRows >= 1 ? freezeRows : undefined, "data-freeze-cols": freezeCols != null && freezeCols >= 1 ? freezeCols : undefined, 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, { ...{
272
+ 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(TableContainer, { sx: { 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, { ...{
274
273
  component: "th",
275
274
  scope: "col",
276
275
  rowSpan: headerRows.length > 1 ? 1 : undefined,
@@ -307,12 +306,10 @@ function DataGridTableInner(props) {
307
306
  }
308
307
  // Leaf cell
309
308
  const col = cell.columnDef;
310
- const colIdx = visibleCols.indexOf(col);
311
- const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx < freezeCols;
312
309
  const isPinnedLeft = pinning.pinnedColumns[col.columnId] === 'left';
313
310
  const isPinnedRight = pinning.pinnedColumns[col.columnId] === 'right';
314
311
  const columnWidth = getColumnWidth(col);
315
- const baseHeaderSx = isPinnedLeft || (isFreezeCol && colIdx === 0) ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX;
312
+ const baseHeaderSx = isPinnedLeft ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX;
316
313
  // Override sticky offset for pinned columns (supports multiple pinned columns)
317
314
  const headerSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null
318
315
  ? { ...baseHeaderSx, left: pinning.leftOffsets[col.columnId] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react-material",
3
- "version": "2.0.17",
3
+ "version": "2.0.18",
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.17"
42
+ "@alaarab/ogrid-react": "2.0.18"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@emotion/react": "^11.0.0",