@alaarab/ogrid 1.5.0 → 1.7.1
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.
- package/dist/esm/DataGridTable/DataGridTable.js +92 -64
- package/dist/esm/DataGridTable/DataGridTable.module.css +15 -1
- package/dist/esm/OGrid/OGrid.js +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/types/DataGridTable/DataGridTable.d.ts +0 -2
- package/dist/types/index.d.ts +2 -2
- package/package.json +2 -2
|
@@ -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,22 +10,87 @@ 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);
|
|
16
21
|
const state = useDataGridState({ props, wrapperRef });
|
|
17
22
|
const lastMouseShiftRef = useRef(false);
|
|
18
|
-
const {
|
|
23
|
+
const { layout, rowSelection: rowSel, editing, interaction, contextMenu: ctxMenu, viewModels } = state;
|
|
24
|
+
const { visibleCols, totalColCount, hasCheckboxCol, colOffset, containerWidth, minTableWidth, desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides } = layout;
|
|
25
|
+
const { selectedRowIds, updateSelection, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected } = rowSel;
|
|
26
|
+
const { setEditingCell, pendingEditorValue, setPendingEditorValue, commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl } = editing;
|
|
27
|
+
const { setActiveCell, handleCellMouseDown, handleSelectAllCells, selectionRange, hasCellSelection, handleGridKeyDown, handleFillHandleMouseDown, handleCopy, handleCut, handlePaste, cutRange, copyRange, canUndo, canRedo, onUndo, onRedo } = interaction;
|
|
28
|
+
const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
|
|
29
|
+
const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid } = viewModels;
|
|
19
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;
|
|
20
|
-
|
|
31
|
+
// Memoize header rows (recursive tree traversal — avoid recomputing every render)
|
|
32
|
+
const headerRows = useMemo(() => buildHeaderRows(columns, visibleColumns), [columns, visibleColumns]);
|
|
21
33
|
const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
|
|
22
34
|
const fitToContent = layoutMode === 'content';
|
|
23
35
|
const { handleResizeStart, getColumnWidth } = useColumnResize({
|
|
24
36
|
columnSizingOverrides,
|
|
25
37
|
setColumnSizingOverrides,
|
|
26
38
|
});
|
|
27
|
-
const editCallbacks =
|
|
28
|
-
const interactionHandlers =
|
|
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]);
|
|
29
94
|
const renderCellContent = useCallback((item, col, rowIndex, colIdx) => {
|
|
30
95
|
const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInput);
|
|
31
96
|
if (descriptor.mode === 'editing-inline') {
|
|
@@ -48,65 +113,28 @@ function DataGridTableInner(props) {
|
|
|
48
113
|
descriptor.isInCopyRange ? styles.cellCopied : '',
|
|
49
114
|
].filter(Boolean).join(' ');
|
|
50
115
|
const interactionProps = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
|
|
51
|
-
return (_jsxs("div", { className: cellClassNames, ...interactionProps, style: descriptor.canEditAny ?
|
|
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" }))] }));
|
|
52
117
|
}, [cellDescriptorInput, pendingEditorValue, popoverAnchorEl, editCallbacks, interactionHandlers, handleFillHandleMouseDown, setPopoverAnchorEl, cancelPopoverEdit]);
|
|
53
|
-
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:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
isFreezeCol ? styles.freezeCol : '',
|
|
75
|
-
isFreezeCol && colIdx === 0 ? styles.freezeColFirst : '',
|
|
76
|
-
isPinnedLeft ? styles.pinnedColLeft : '',
|
|
77
|
-
isPinnedRight ? styles.pinnedColRight : '',
|
|
78
|
-
].filter(Boolean).join(' '), style: {
|
|
79
|
-
minWidth: col.minWidth ?? 80,
|
|
80
|
-
width: hasExplicitWidth ? columnWidth : undefined,
|
|
81
|
-
maxWidth: hasExplicitWidth ? columnWidth : undefined,
|
|
82
|
-
}, children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx("div", { className: styles.resizeHandle, onMouseDown: (e) => handleResizeStart(e, col), "aria-label": `Resize ${col.name}` })] }, col.columnId));
|
|
83
|
-
})] }, rowIdx))) }), !showEmptyInGrid && (_jsx("tbody", { children: items.map((item, rowIndex) => {
|
|
84
|
-
const rowIdStr = getRowId(item);
|
|
85
|
-
const isSelected = selectedRowIds.has(rowIdStr);
|
|
86
|
-
return (_jsxs("tr", { className: isSelected ? styles.selectedRow : '', onClick: () => {
|
|
87
|
-
if (rowSelection === 'single') {
|
|
88
|
-
const id = getRowId(item);
|
|
89
|
-
updateSelection(selectedRowIds.has(id) ? new Set() : new Set([id]));
|
|
90
|
-
}
|
|
91
|
-
}, 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) => {
|
|
92
|
-
const isFreezeCol = freezeCols != null && freezeCols >= 1 && colIdx < freezeCols;
|
|
93
|
-
const isPinnedLeft = col.pinned === 'left';
|
|
94
|
-
const isPinnedRight = col.pinned === 'right';
|
|
95
|
-
const columnWidth = getColumnWidth(col);
|
|
96
|
-
const hasExplicitWidth = !!(columnSizingOverrides[col.columnId] || col.idealWidth != null || col.defaultWidth != null);
|
|
97
|
-
return (_jsx("td", { className: [
|
|
98
|
-
isFreezeCol ? styles.freezeCol : '',
|
|
99
|
-
isFreezeCol && colIdx === 0 ? styles.freezeColFirst : '',
|
|
100
|
-
isPinnedLeft ? styles.pinnedColLeft : '',
|
|
101
|
-
isPinnedRight ? styles.pinnedColRight : '',
|
|
102
|
-
].filter(Boolean).join(' '), style: {
|
|
103
|
-
minWidth: col.minWidth ?? 80,
|
|
104
|
-
width: hasExplicitWidth ? columnWidth : undefined,
|
|
105
|
-
maxWidth: hasExplicitWidth ? columnWidth : undefined,
|
|
106
|
-
textAlign: col.type === 'numeric' ? 'right' : col.type === 'boolean' ? 'center' : undefined,
|
|
107
|
-
}, children: renderCellContent(item, col, rowIndex, colIdx) }, col.columnId));
|
|
108
|
-
})] }, rowIdStr));
|
|
109
|
-
}) }))] }), _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 }))] }), contextMenu &&
|
|
110
|
-
createPortal(_jsx(GridContextMenu, { x: contextMenu.x, y: contextMenu.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 })] }) }))] }));
|
|
111
139
|
}
|
|
112
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:
|
|
80
|
+
z-index: 6;
|
|
80
81
|
background: var(--ogrid-bg-subtle, #f3f2f1);
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -90,6 +91,17 @@
|
|
|
90
91
|
left: 0;
|
|
91
92
|
z-index: 2;
|
|
92
93
|
background: var(--ogrid-bg, #ffffff);
|
|
94
|
+
will-change: transform;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Freeze/pinned header cells need top: 0 + z-index: 3 so they stick in BOTH
|
|
98
|
+
directions — left (from .freezeCol/.pinnedCol) AND top (from .stickyHeader).
|
|
99
|
+
Without explicit top, the child's own position: sticky overrides the parent's. */
|
|
100
|
+
.dataTable thead.stickyHeader .freezeColFirst,
|
|
101
|
+
.dataTable thead.stickyHeader .pinnedColLeft,
|
|
102
|
+
.dataTable thead.stickyHeader .pinnedColRight {
|
|
103
|
+
top: 0;
|
|
104
|
+
z-index: 7;
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
.dataTable thead .freezeColFirst {
|
|
@@ -104,6 +116,7 @@
|
|
|
104
116
|
left: 0;
|
|
105
117
|
z-index: 2;
|
|
106
118
|
background: var(--ogrid-bg, #ffffff);
|
|
119
|
+
will-change: transform;
|
|
107
120
|
}
|
|
108
121
|
.dataTable .pinnedColLeft::after {
|
|
109
122
|
content: "";
|
|
@@ -125,6 +138,7 @@
|
|
|
125
138
|
right: 0;
|
|
126
139
|
z-index: 2;
|
|
127
140
|
background: var(--ogrid-bg, #ffffff);
|
|
141
|
+
will-change: transform;
|
|
128
142
|
}
|
|
129
143
|
.dataTable .pinnedColRight::before {
|
|
130
144
|
content: "";
|
package/dist/esm/OGrid/OGrid.js
CHANGED
|
@@ -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,
|
|
10
|
-
return (_jsx(OGridLayout, { className: className,
|
|
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 }) }));
|
package/dist/esm/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export { DataGridTable } from './DataGridTable/DataGridTable';
|
|
|
4
4
|
export { ColumnChooser } from './ColumnChooser/ColumnChooser';
|
|
5
5
|
export { ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter';
|
|
6
6
|
export { PaginationControls } from './PaginationControls/PaginationControls';
|
|
7
|
-
// Re-export from core
|
|
8
|
-
export
|
|
7
|
+
// Re-export everything from core
|
|
8
|
+
export * from '@alaarab/ogrid-core';
|
|
@@ -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 {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { OGrid, type IOGridProps } from './OGrid/OGrid';
|
|
2
|
-
export { 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';
|
|
6
|
-
export
|
|
6
|
+
export * from '@alaarab/ogrid-core';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
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.
|
|
44
|
+
"@alaarab/ogrid-core": "^1.7.1",
|
|
45
45
|
"@radix-ui/react-checkbox": "^1.1.2",
|
|
46
46
|
"@radix-ui/react-popover": "^1.1.2"
|
|
47
47
|
},
|