@alaarab/ogrid-fluent 1.8.0 → 1.8.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.
@@ -7,7 +7,7 @@ import { ColumnHeaderFilter } from '../ColumnHeaderFilter';
7
7
  import { InlineCellEditor } from './InlineCellEditor';
8
8
  import { StatusBar } from './StatusBar';
9
9
  import { GridContextMenu } from './GridContextMenu';
10
- import { useDataGridState, getHeaderFilterConfig, getCellRenderDescriptor, buildHeaderRows, MarchingAntsOverlay, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from '@alaarab/ogrid-core';
10
+ import { useDataGridState, getHeaderFilterConfig, getCellRenderDescriptor, buildHeaderRows, MarchingAntsOverlay, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, isRowInRange, } from '@alaarab/ogrid-core';
11
11
  import styles from './DataGridTable.module.css';
12
12
  // Module-scope stable constants (avoid per-render allocations)
13
13
  const gridRootStyle = {
@@ -23,6 +23,66 @@ const BOOLEAN_STYLE = { justifyContent: 'center', textAlign: 'center' };
23
23
  const EDITABLE_NUMERIC_STYLE = { cursor: 'cell', justifyContent: 'flex-end', textAlign: 'right' };
24
24
  const EDITABLE_BOOLEAN_STYLE = { cursor: 'cell', justifyContent: 'center', textAlign: 'center' };
25
25
  const PREVENT_DEFAULT = (e) => { e.preventDefault(); };
26
+ function GridRowInner(props) {
27
+ const { item, rowId, rowIndex, isSelected, cellClassMap, handleSingleRowClick, activeCell } = props;
28
+ const rowClassName = [
29
+ isSelected ? styles.selectedRow : '',
30
+ activeCell !== null && rowIndex === activeCell.rowIndex ? styles.activeRow : '',
31
+ ].filter(Boolean).join(' ') || undefined;
32
+ return (
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ _jsx(DataGridRow, { className: rowClassName, onClick: () => handleSingleRowClick(rowId), children: ({ renderCell, columnId }) => (_jsx(DataGridCell, { className: cellClassMap[String(columnId)] || undefined, children: renderCell(item) })) }));
35
+ }
36
+ function areGridRowPropsEqual(prev, next) {
37
+ // Data / structure changes — always re-render
38
+ if (prev.item !== next.item)
39
+ return false;
40
+ if (prev.isSelected !== next.isSelected)
41
+ return false;
42
+ if (prev.cellClassMap !== next.cellClassMap)
43
+ return false;
44
+ const ri = prev.rowIndex;
45
+ // Editing cell in this row?
46
+ if (prev.editingRowId !== next.editingRowId) {
47
+ if (prev.editingRowId === prev.rowId || next.editingRowId === next.rowId)
48
+ return false;
49
+ }
50
+ // Active cell in this row?
51
+ const prevActive = prev.activeCell?.rowIndex === ri;
52
+ const nextActive = next.activeCell?.rowIndex === ri;
53
+ if (prevActive !== nextActive)
54
+ return false;
55
+ if (prevActive && nextActive && prev.activeCell.columnIndex !== next.activeCell.columnIndex)
56
+ return false;
57
+ // Selection range touches this row?
58
+ const prevInSel = isRowInRange(prev.selectionRange, ri);
59
+ const nextInSel = isRowInRange(next.selectionRange, ri);
60
+ if (prevInSel !== nextInSel)
61
+ return false;
62
+ if (prevInSel && nextInSel) {
63
+ if (prev.selectionRange.startCol !== next.selectionRange.startCol ||
64
+ prev.selectionRange.endCol !== next.selectionRange.endCol)
65
+ return false;
66
+ }
67
+ // Fill handle (selection end row) + isDragging
68
+ const prevIsEnd = prev.selectionRange?.endRow === ri;
69
+ const nextIsEnd = next.selectionRange?.endRow === ri;
70
+ if (prevIsEnd !== nextIsEnd)
71
+ return false;
72
+ if ((prevIsEnd || nextIsEnd) && prev.isDragging !== next.isDragging)
73
+ return false;
74
+ // Cut/copy ranges touch this row?
75
+ if (prev.cutRange !== next.cutRange) {
76
+ if (isRowInRange(prev.cutRange, ri) || isRowInRange(next.cutRange, ri))
77
+ return false;
78
+ }
79
+ if (prev.copyRange !== next.copyRange) {
80
+ if (isRowInRange(prev.copyRange, ri) || isRowInRange(next.copyRange, ri))
81
+ return false;
82
+ }
83
+ return true;
84
+ }
85
+ const GridRow = React.memo(GridRowInner, areGridRowPropsEqual);
26
86
  function DataGridTableInner(props) {
27
87
  const wrapperRef = useRef(null);
28
88
  const tableContainerRef = useRef(null);
@@ -259,11 +319,7 @@ function DataGridTableInner(props) {
259
319
  return (_jsx("th", { rowSpan: headerRows.length - rowIdx, className: styles.leafHeaderCellSpan, scope: "col", children: cell.columnDef?.name }, cellIdx));
260
320
  })] }, `group-${rowIdx}`))), _jsx(DataGridRow, { children: ({ renderHeaderCell, columnId }) => (_jsx(DataGridHeaderCell, { className: headerClassMap[String(columnId)] || undefined, children: renderHeaderCell() })) })] }), _jsx(DataGridBody, { children: ({ item }) => {
261
321
  const rowId = getRowId(item);
262
- const isSelected = selectedRowIdsRef.current.has(rowId);
263
- const ac = activeCellRef.current;
264
- return (_jsx(DataGridRow, { className: `${isSelected ? styles.selectedRow : ''} ${ac !== null && (rowIndexByRowId.get(rowId) ?? -1) === ac.rowIndex
265
- ? styles.activeRow
266
- : ''}`, onClick: () => handleSingleRowClick(rowId), children: ({ renderCell, columnId }) => (_jsx(DataGridCell, { className: cellClassMap[String(columnId)] || undefined, children: renderCell(item) })) }, rowId));
322
+ return (_jsx(GridRow, { item: item, rowId: rowId, rowIndex: rowIndexByRowId.get(rowId) ?? -1, isSelected: selectedRowIds.has(rowId), cellClassMap: cellClassMap, handleSingleRowClick: handleSingleRowClick, selectionRange: selectionRange, activeCell: activeCell, cutRange: cutRange, copyRange: copyRange, isDragging: isDragging, editingRowId: editingCell?.rowId ?? null }, rowId));
267
323
  } })] }), _jsx(MarchingAntsOverlay, { containerRef: tableContainerRef, selectionRange: selectionRange, copyRange: copyRange, cutRange: cutRange, colOffset: colOffset }), showEmptyInGrid && emptyState && (_jsx("div", { className: styles.emptyStateInGrid, children: _jsx("div", { className: styles.emptyStateInGridMessageSticky, children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.emptyStateInGridIcon, "aria-hidden": true, children: "\uD83D\uDCCB" }), _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.') })] })) }) }))] }) }) }), menuPosition &&
268
324
  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)] }), 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 && items.length > 0 && (_jsx("div", { className: styles.loadingOverlay, "aria-live": "polite", children: _jsxs("div", { className: styles.loadingOverlayContent, children: [_jsx(Spinner, { size: "small" }), _jsx("span", { className: styles.loadingOverlayText, children: loadingMessage })] }) }))] }));
269
325
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-fluent",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "OGrid Fluent UI implementation – DataGrid-powered data table with sorting, filtering, pagination, column chooser, and CSV export.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -40,7 +40,7 @@
40
40
  "node": ">=18"
41
41
  },
42
42
  "dependencies": {
43
- "@alaarab/ogrid-core": "^1.8.0"
43
+ "@alaarab/ogrid-core": "^1.8.2"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@fluentui/react-components": "^9.0.0",