@alaarab/ogrid 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.
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
- import { useCallback, useRef } from 'react';
3
+ import { useCallback, useRef, useMemo } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
5
  import * as Popover from '@radix-ui/react-popover';
6
6
  import * as Checkbox from '@radix-ui/react-checkbox';
@@ -10,6 +10,11 @@ import { StatusBar } from './StatusBar';
10
10
  import { GridContextMenu } from './GridContextMenu';
11
11
  import { useDataGridState, useColumnResize, getHeaderFilterConfig, getCellRenderDescriptor, buildHeaderRows, MarchingAntsOverlay, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from '@alaarab/ogrid-core';
12
12
  import styles from './DataGridTable.module.css';
13
+ // Module-scope stable constants (avoid per-render allocations)
14
+ const GRID_ROOT_STYLE = { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' };
15
+ const CURSOR_CELL_STYLE = { cursor: 'cell' };
16
+ const STOP_PROPAGATION = (e) => e.stopPropagation();
17
+ const PREVENT_DEFAULT = (e) => { e.preventDefault(); };
13
18
  function DataGridTableInner(props) {
14
19
  const wrapperRef = useRef(null);
15
20
  const tableContainerRef = useRef(null);
@@ -23,15 +28,69 @@ function DataGridTableInner(props) {
23
28
  const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
24
29
  const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid } = viewModels;
25
30
  const { items, columns, getRowId, emptyState, layoutMode = 'fill', rowSelection = 'none', freezeRows, freezeCols, suppressHorizontalScroll, isLoading = false, loadingMessage = 'Loading\u2026', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, visibleColumns, } = props;
26
- const headerRows = buildHeaderRows(columns, visibleColumns);
31
+ // Memoize header rows (recursive tree traversal — avoid recomputing every render)
32
+ const headerRows = useMemo(() => buildHeaderRows(columns, visibleColumns), [columns, visibleColumns]);
27
33
  const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
28
34
  const fitToContent = layoutMode === 'content';
29
35
  const { handleResizeStart, getColumnWidth } = useColumnResize({
30
36
  columnSizingOverrides,
31
37
  setColumnSizingOverrides,
32
38
  });
33
- const editCallbacks = React.useMemo(() => ({ commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit }), [commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit]);
34
- const interactionHandlers = React.useMemo(() => ({ handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu }), [handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu]);
39
+ const editCallbacks = useMemo(() => ({ commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit }), [commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit]);
40
+ const interactionHandlers = useMemo(() => ({ handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu }), [handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu]);
41
+ // Pre-compute column styles and classNames (avoids per-cell object creation in the row loop)
42
+ const columnMeta = useMemo(() => {
43
+ const cellStyles = {};
44
+ const cellClasses = {};
45
+ const hdrStyles = {};
46
+ const hdrClasses = {};
47
+ for (let i = 0; i < visibleCols.length; i++) {
48
+ const col = visibleCols[i];
49
+ const columnWidth = getColumnWidth(col);
50
+ const hasExplicitWidth = !!(columnSizingOverrides[col.columnId] || col.idealWidth != null || col.defaultWidth != null);
51
+ const isFreezeCol = freezeCols != null && freezeCols >= 1 && i < freezeCols;
52
+ const isPinnedLeft = col.pinned === 'left';
53
+ const isPinnedRight = col.pinned === 'right';
54
+ cellStyles[col.columnId] = {
55
+ minWidth: col.minWidth ?? 80,
56
+ width: hasExplicitWidth ? columnWidth : undefined,
57
+ maxWidth: hasExplicitWidth ? columnWidth : undefined,
58
+ textAlign: col.type === 'numeric' ? 'right' : col.type === 'boolean' ? 'center' : undefined,
59
+ };
60
+ hdrStyles[col.columnId] = {
61
+ minWidth: col.minWidth ?? 80,
62
+ width: hasExplicitWidth ? columnWidth : undefined,
63
+ maxWidth: hasExplicitWidth ? columnWidth : undefined,
64
+ };
65
+ const parts = [];
66
+ if (isFreezeCol)
67
+ parts.push(styles.freezeCol);
68
+ if (isFreezeCol && i === 0)
69
+ parts.push(styles.freezeColFirst);
70
+ if (isPinnedLeft)
71
+ parts.push(styles.pinnedColLeft);
72
+ if (isPinnedRight)
73
+ parts.push(styles.pinnedColRight);
74
+ const cn = parts.join(' ');
75
+ cellClasses[col.columnId] = cn;
76
+ hdrClasses[col.columnId] = cn;
77
+ }
78
+ return { cellStyles, cellClasses, hdrStyles, hdrClasses };
79
+ }, [visibleCols, getColumnWidth, columnSizingOverrides, freezeCols]);
80
+ // Stable row-click handler (avoids creating a new arrow function per row)
81
+ const selectedRowIdsRef = useRef(selectedRowIds);
82
+ selectedRowIdsRef.current = selectedRowIds;
83
+ const handleSingleRowClick = useCallback((e) => {
84
+ if (rowSelection !== 'single')
85
+ return;
86
+ const rowId = e.currentTarget.dataset.rowId;
87
+ if (!rowId)
88
+ return;
89
+ const ids = selectedRowIdsRef.current;
90
+ updateSelection(ids.has(rowId) ? new Set() : new Set([rowId]));
91
+ }, [rowSelection, updateSelection]);
92
+ // Stable header select-all handler
93
+ const handleSelectAllChecked = useCallback((c) => handleSelectAll(!!c), [handleSelectAll]);
35
94
  const renderCellContent = useCallback((item, col, rowIndex, colIdx) => {
36
95
  const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInput);
37
96
  if (descriptor.mode === 'editing-inline') {
@@ -54,65 +113,28 @@ function DataGridTableInner(props) {
54
113
  descriptor.isInCopyRange ? styles.cellCopied : '',
55
114
  ].filter(Boolean).join(' ');
56
115
  const interactionProps = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
57
- return (_jsxs("div", { className: cellClassNames, ...interactionProps, style: descriptor.canEditAny ? { cursor: 'cell' } : undefined, children: [styledContent, descriptor.canEditAny && descriptor.isSelectionEndCell && (_jsx("div", { className: styles.fillHandle, onMouseDown: handleFillHandleMouseDown, "aria-label": "Fill handle" }))] }));
116
+ return (_jsxs("div", { className: cellClassNames, ...interactionProps, style: descriptor.canEditAny ? CURSOR_CELL_STYLE : undefined, children: [styledContent, descriptor.canEditAny && descriptor.isSelectionEndCell && (_jsx("div", { className: styles.fillHandle, onMouseDown: handleFillHandleMouseDown, "aria-label": "Fill handle" }))] }));
58
117
  }, [cellDescriptorInput, pendingEditorValue, popoverAnchorEl, editCallbacks, interactionHandlers, handleFillHandleMouseDown, setPopoverAnchorEl, cancelPopoverEdit]);
59
- return (_jsxs("div", { ref: wrapperRef, tabIndex: 0, onMouseDown: (e) => { lastMouseShiftRef.current = e.shiftKey; }, 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-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: (e) => { e.preventDefault(); }, onKeyDown: handleGridKeyDown, style: {
60
- ['--data-table-column-count']: totalColCount,
61
- ['--data-table-width']: showEmptyInGrid ? '100%' : allowOverflowX ? 'fit-content' : fitToContent ? 'fit-content' : '100%',
62
- ['--data-table-min-width']: showEmptyInGrid ? '100%' : allowOverflowX ? 'max-content' : fitToContent ? 'max-content' : '100%',
63
- ['--data-table-total-min-width']: `${minTableWidth}px`,
64
- }, children: [isLoading && items.length > 0 && (_jsx("div", { className: styles.loadingOverlay, "aria-live": "polite", children: _jsxs("div", { className: styles.loadingOverlayContent, children: [_jsx("div", { className: styles.spinner }), _jsx("span", { className: styles.loadingOverlayText, children: loadingMessage })] }) })), _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("table", { className: styles.dataTable, children: [_jsx("thead", { className: styles.stickyHeader, children: headerRows.map((row, rowIdx) => (_jsxs("tr", { children: [rowIdx === headerRows.length - 1 && hasCheckboxCol && (_jsx("th", { className: styles.selectionHeaderCell, scope: "col", rowSpan: 1, children: _jsx("div", { className: styles.selectionHeaderCellInner, children: _jsx(Checkbox.Root, { className: styles.rowCheckbox, checked: allSelected ? true : someSelected ? 'indeterminate' : false, onCheckedChange: (c) => handleSelectAll(!!c), "aria-label": "Select all rows", children: _jsx(Checkbox.Indicator, { className: styles.rowCheckboxIndicator, children: someSelected && !allSelected ? '–' : '✓' }) }) }) })), rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol && (_jsx("th", { rowSpan: headerRows.length - 1 })), row.map((cell, cellIdx) => {
65
- if (cell.isGroup) {
66
- return (_jsx("th", { colSpan: cell.colSpan, className: styles.groupHeaderCell, scope: "colgroup", children: cell.label }, cellIdx));
67
- }
68
- // Leaf cell
69
- const col = cell.columnDef;
70
- const colIdx = visibleCols.indexOf(col);
71
- const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx < freezeCols;
72
- const isPinnedLeft = col.pinned === 'left';
73
- const isPinnedRight = col.pinned === 'right';
74
- const columnWidth = getColumnWidth(col);
75
- const hasExplicitWidth = !!(columnSizingOverrides[col.columnId] || col.idealWidth != null || col.defaultWidth != null);
76
- const leafRowSpan = headerRows.length > 1 && rowIdx < headerRows.length - 1
77
- ? headerRows.length - rowIdx
78
- : undefined;
79
- return (_jsxs("th", { scope: "col", "data-column-id": col.columnId, rowSpan: leafRowSpan, className: [
80
- isFreezeCol ? styles.freezeCol : '',
81
- isFreezeCol && colIdx === 0 ? styles.freezeColFirst : '',
82
- isPinnedLeft ? styles.pinnedColLeft : '',
83
- isPinnedRight ? styles.pinnedColRight : '',
84
- ].filter(Boolean).join(' '), style: {
85
- minWidth: col.minWidth ?? 80,
86
- width: hasExplicitWidth ? columnWidth : undefined,
87
- maxWidth: hasExplicitWidth ? columnWidth : undefined,
88
- }, children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx("div", { className: styles.resizeHandle, onMouseDown: (e) => handleResizeStart(e, col), "aria-label": `Resize ${col.name}` })] }, col.columnId));
89
- })] }, rowIdx))) }), !showEmptyInGrid && (_jsx("tbody", { children: items.map((item, rowIndex) => {
90
- const rowIdStr = getRowId(item);
91
- const isSelected = selectedRowIds.has(rowIdStr);
92
- return (_jsxs("tr", { className: isSelected ? styles.selectedRow : '', onClick: () => {
93
- if (rowSelection === 'single') {
94
- const id = getRowId(item);
95
- updateSelection(selectedRowIds.has(id) ? new Set() : new Set([id]));
96
- }
97
- }, children: [hasCheckboxCol && (_jsx("td", { className: styles.selectionCell, children: _jsx("div", { className: styles.selectionCellInner, "data-row-index": rowIndex, "data-col-index": 0, onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox.Root, { className: styles.rowCheckbox, checked: selectedRowIds.has(rowIdStr), onCheckedChange: (c) => handleRowCheckboxChange(rowIdStr, !!c, rowIndex, lastMouseShiftRef.current), "aria-label": `Select row ${rowIndex + 1}`, children: _jsx(Checkbox.Indicator, { className: styles.rowCheckboxIndicator, children: "\u2713" }) }) }) })), visibleCols.map((col, colIdx) => {
98
- const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx < freezeCols;
99
- const isPinnedLeft = col.pinned === 'left';
100
- const isPinnedRight = col.pinned === 'right';
101
- const columnWidth = getColumnWidth(col);
102
- const hasExplicitWidth = !!(columnSizingOverrides[col.columnId] || col.idealWidth != null || col.defaultWidth != null);
103
- return (_jsx("td", { className: [
104
- isFreezeCol ? styles.freezeCol : '',
105
- isFreezeCol && colIdx === 0 ? styles.freezeColFirst : '',
106
- isPinnedLeft ? styles.pinnedColLeft : '',
107
- isPinnedRight ? styles.pinnedColRight : '',
108
- ].filter(Boolean).join(' '), style: {
109
- minWidth: col.minWidth ?? 80,
110
- width: hasExplicitWidth ? columnWidth : undefined,
111
- maxWidth: hasExplicitWidth ? columnWidth : undefined,
112
- textAlign: col.type === 'numeric' ? 'right' : col.type === 'boolean' ? 'center' : undefined,
113
- }, children: renderCellContent(item, col, rowIndex, colIdx) }, col.columnId));
114
- })] }, rowIdStr));
115
- }) }))] }), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset }), showEmptyInGrid && emptyState && (_jsx("div", { className: styles.emptyStateInGrid, children: _jsx("div", { children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_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 &&
116
- 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)] }));
118
+ return (_jsxs("div", { style: GRID_ROOT_STYLE, children: [_jsxs("div", { ref: wrapperRef, tabIndex: 0, onMouseDown: (e) => { lastMouseShiftRef.current = e.shiftKey; }, 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-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, onKeyDown: handleGridKeyDown, style: {
119
+ ['--data-table-column-count']: totalColCount,
120
+ ['--data-table-width']: showEmptyInGrid ? '100%' : allowOverflowX ? 'fit-content' : fitToContent ? 'fit-content' : '100%',
121
+ ['--data-table-min-width']: showEmptyInGrid ? '100%' : allowOverflowX ? 'max-content' : fitToContent ? 'max-content' : '100%',
122
+ ['--data-table-total-min-width']: `${minTableWidth}px`,
123
+ }, 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("table", { className: styles.dataTable, children: [_jsx("thead", { className: styles.stickyHeader, children: headerRows.map((row, rowIdx) => (_jsxs("tr", { children: [rowIdx === headerRows.length - 1 && hasCheckboxCol && (_jsx("th", { className: styles.selectionHeaderCell, scope: "col", rowSpan: 1, children: _jsx("div", { className: styles.selectionHeaderCellInner, children: _jsx(Checkbox.Root, { className: styles.rowCheckbox, checked: allSelected ? true : someSelected ? 'indeterminate' : false, onCheckedChange: handleSelectAllChecked, "aria-label": "Select all rows", children: _jsx(Checkbox.Indicator, { className: styles.rowCheckboxIndicator, children: someSelected && !allSelected ? '–' : '✓' }) }) }) })), rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol && (_jsx("th", { rowSpan: headerRows.length - 1 })), row.map((cell, cellIdx) => {
124
+ if (cell.isGroup) {
125
+ return (_jsx("th", { colSpan: cell.colSpan, className: styles.groupHeaderCell, scope: "colgroup", children: cell.label }, cellIdx));
126
+ }
127
+ // Leaf cell
128
+ const col = cell.columnDef;
129
+ const leafRowSpan = headerRows.length > 1 && rowIdx < headerRows.length - 1
130
+ ? headerRows.length - rowIdx
131
+ : undefined;
132
+ return (_jsxs("th", { scope: "col", "data-column-id": col.columnId, rowSpan: leafRowSpan, className: columnMeta.hdrClasses[col.columnId] || undefined, style: columnMeta.hdrStyles[col.columnId], children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx("div", { className: styles.resizeHandle, onMouseDown: (e) => handleResizeStart(e, col), "aria-label": `Resize ${col.name}` })] }, col.columnId));
133
+ })] }, rowIdx))) }), !showEmptyInGrid && (_jsx("tbody", { children: items.map((item, rowIndex) => {
134
+ const rowIdStr = getRowId(item);
135
+ const isSelected = selectedRowIds.has(rowIdStr);
136
+ return (_jsxs("tr", { className: isSelected ? styles.selectedRow : '', "data-row-id": rowIdStr, onClick: handleSingleRowClick, children: [hasCheckboxCol && (_jsx("td", { className: styles.selectionCell, children: _jsx("div", { className: styles.selectionCellInner, "data-row-index": rowIndex, "data-col-index": 0, onClick: STOP_PROPAGATION, children: _jsx(Checkbox.Root, { className: styles.rowCheckbox, checked: selectedRowIds.has(rowIdStr), onCheckedChange: (c) => handleRowCheckboxChange(rowIdStr, !!c, rowIndex, lastMouseShiftRef.current), "aria-label": `Select row ${rowIndex + 1}`, children: _jsx(Checkbox.Indicator, { className: styles.rowCheckboxIndicator, children: "\u2713" }) }) }) })), visibleCols.map((col, colIdx) => (_jsx("td", { className: columnMeta.cellClasses[col.columnId] || undefined, style: columnMeta.cellStyles[col.columnId], children: renderCellContent(item, col, rowIndex, colIdx) }, col.columnId)))] }, rowIdStr));
137
+ }) }))] }), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset }), showEmptyInGrid && emptyState && (_jsx("div", { className: styles.emptyStateInGrid, children: _jsx("div", { children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_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 &&
138
+ 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("div", { className: styles.spinner }), _jsx("span", { className: styles.loadingOverlayText, children: loadingMessage })] }) }))] }));
117
139
  }
118
140
  export const DataGridTable = React.memo(DataGridTableInner);
@@ -19,6 +19,7 @@
19
19
  max-width: 100%;
20
20
  box-sizing: border-box;
21
21
  background: var(--ogrid-bg, #fff);
22
+ will-change: scroll-position;
22
23
  }
23
24
  .tableWrapper[data-overflow-x=true] {
24
25
  overflow-x: auto;
@@ -76,7 +77,7 @@
76
77
  .dataTable thead.stickyHeader {
77
78
  position: sticky;
78
79
  top: 0;
79
- z-index: 2;
80
+ z-index: 6;
80
81
  background: var(--ogrid-bg-subtle, #f3f2f1);
81
82
  }
82
83
 
@@ -90,6 +91,7 @@
90
91
  left: 0;
91
92
  z-index: 2;
92
93
  background: var(--ogrid-bg, #ffffff);
94
+ will-change: transform;
93
95
  }
94
96
 
95
97
  /* Freeze/pinned header cells need top: 0 + z-index: 3 so they stick in BOTH
@@ -99,7 +101,7 @@
99
101
  .dataTable thead.stickyHeader .pinnedColLeft,
100
102
  .dataTable thead.stickyHeader .pinnedColRight {
101
103
  top: 0;
102
- z-index: 3;
104
+ z-index: 7;
103
105
  }
104
106
 
105
107
  .dataTable thead .freezeColFirst {
@@ -114,6 +116,7 @@
114
116
  left: 0;
115
117
  z-index: 2;
116
118
  background: var(--ogrid-bg, #ffffff);
119
+ will-change: transform;
117
120
  }
118
121
  .dataTable .pinnedColLeft::after {
119
122
  content: "";
@@ -135,6 +138,7 @@
135
138
  right: 0;
136
139
  z-index: 2;
137
140
  background: var(--ogrid-bg, #ffffff);
141
+ will-change: transform;
138
142
  }
139
143
  .dataTable .pinnedColRight::before {
140
144
  content: "";
@@ -6,8 +6,8 @@ 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, columnChooserPlacement, toolbar, className, entityLabelPlural, pageSizeOptions, sideBarProps, } = 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, columnChooserPlacement, toolbar, toolbarBelow, className, entityLabelPlural, pageSizeOptions, sideBarProps, } = 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 }) }));
@@ -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 {};
@@ -1,5 +1,5 @@
1
1
  export { OGrid, type IOGridProps } from './OGrid/OGrid';
2
- export { DataGridTable, type IDataGridTableProps } from './DataGridTable/DataGridTable';
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",
3
- "version": "1.6.0",
3
+ "version": "1.7.2",
4
4
  "description": "OGrid default (Radix) – Data grid with sorting, filtering, pagination, column chooser, and CSV export. Packed with Radix UI; no Fluent or Material required.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -41,7 +41,7 @@
41
41
  "node": ">=18"
42
42
  },
43
43
  "dependencies": {
44
- "@alaarab/ogrid-core": "^1.6.0",
44
+ "@alaarab/ogrid-core": "^1.7.2",
45
45
  "@radix-ui/react-checkbox": "^1.1.2",
46
46
  "@radix-ui/react-popover": "^1.1.2"
47
47
  },