@alaarab/ogrid-react-material 2.1.3 → 2.1.4
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/index.css +118 -0
- package/dist/esm/index.js +1577 -11
- package/dist/types/DataGridTable/DataGridTable.d.ts +1 -0
- package/dist/types/OGrid/OGrid.d.ts +1 -1
- package/dist/types/index.d.ts +40 -1
- package/package.json +4 -4
- package/dist/esm/ColumnChooser/ColumnChooser.js +0 -53
- package/dist/esm/ColumnHeaderFilter/ColumnHeaderFilter.js +0 -34
- package/dist/esm/ColumnHeaderFilter/MultiSelectFilterPopover.js +0 -17
- package/dist/esm/ColumnHeaderFilter/PeopleFilterPopover.js +0 -17
- package/dist/esm/ColumnHeaderFilter/TextFilterPopover.js +0 -15
- package/dist/esm/ColumnHeaderFilter/index.js +0 -1
- package/dist/esm/ColumnHeaderMenu/ColumnHeaderMenu.js +0 -61
- package/dist/esm/ColumnHeaderMenu/index.js +0 -1
- package/dist/esm/DataGridTable/DataGridTable.js +0 -416
- package/dist/esm/DataGridTable/DropIndicator.js +0 -15
- package/dist/esm/DataGridTable/EmptyState.js +0 -6
- package/dist/esm/DataGridTable/GridContextMenu.js +0 -25
- package/dist/esm/DataGridTable/InlineCellEditor.js +0 -6
- package/dist/esm/DataGridTable/LoadingOverlay.js +0 -14
- package/dist/esm/DataGridTable/StatusBar.js +0 -13
- package/dist/esm/OGrid/OGrid.js +0 -31
- package/dist/esm/OGrid/index.js +0 -1
- package/dist/esm/PaginationControls/PaginationControls.js +0 -35
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { useCallback, useMemo } from 'react';
|
|
4
|
-
import { createPortal } from 'react-dom';
|
|
5
|
-
import { Box, Popover, Checkbox, Table, TableHead, TableBody, TableRow, TableCell, } from '@mui/material';
|
|
6
|
-
import { ColumnHeaderFilter } from '../ColumnHeaderFilter';
|
|
7
|
-
import { ColumnHeaderMenu } from '../ColumnHeaderMenu';
|
|
8
|
-
import { InlineCellEditor } from './InlineCellEditor';
|
|
9
|
-
import { StatusBar } from './StatusBar';
|
|
10
|
-
import { GridContextMenu } from './GridContextMenu';
|
|
11
|
-
import { EmptyState } from './EmptyState';
|
|
12
|
-
import { LoadingOverlay } from './LoadingOverlay';
|
|
13
|
-
import { DropIndicator } from './DropIndicator';
|
|
14
|
-
import { useDataGridTableOrchestration, useColumnMeta, getHeaderFilterConfig, getCellRenderDescriptor, MarchingAntsOverlay, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, areGridRowPropsEqual, CellErrorBoundary, CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, PREVENT_DEFAULT, NOOP, STOP_PROPAGATION, } from '@alaarab/ogrid-react';
|
|
15
|
-
// ── Module-scope stable styles (avoid per-render Emotion resolutions) ──
|
|
16
|
-
const gridRootSx = { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' };
|
|
17
|
-
// Editing cell wrapper (plain div, not MUI)
|
|
18
|
-
const EDITING_CELL_STYLE = {
|
|
19
|
-
width: '100%',
|
|
20
|
-
height: '100%',
|
|
21
|
-
display: 'flex',
|
|
22
|
-
alignItems: 'center',
|
|
23
|
-
boxSizing: 'border-box',
|
|
24
|
-
outline: '2px solid var(--ogrid-selection-color, #217346)',
|
|
25
|
-
outlineOffset: '-1px',
|
|
26
|
-
zIndex: 2,
|
|
27
|
-
position: 'relative',
|
|
28
|
-
background: 'var(--ogrid-bg, #fff)',
|
|
29
|
-
overflow: 'visible',
|
|
30
|
-
padding: 0,
|
|
31
|
-
};
|
|
32
|
-
// Row
|
|
33
|
-
const ROW_HOVER_SX = { '&:hover': { bgcolor: 'action.hover' } };
|
|
34
|
-
// Checkbox column
|
|
35
|
-
const CHECKBOX_CELL_SX = { width: CHECKBOX_COLUMN_WIDTH, minWidth: CHECKBOX_COLUMN_WIDTH, maxWidth: CHECKBOX_COLUMN_WIDTH, textAlign: 'center' };
|
|
36
|
-
const CHECKBOX_WRAPPER_SX = { display: 'flex', alignItems: 'center', justifyContent: 'center' };
|
|
37
|
-
const CHECKBOX_PLACEHOLDER_SX = { width: CHECKBOX_COLUMN_WIDTH, minWidth: CHECKBOX_COLUMN_WIDTH, p: 0 };
|
|
38
|
-
// Header — use opaque var(--ogrid-header-bg) (not semi-transparent action.hover) so sticky
|
|
39
|
-
// headers fully occlude pinned-column content scrolling beneath them.
|
|
40
|
-
// The CSS variable is theme-aware: light=#f5f5f5, dark=#2c2c2c (set by each UI package).
|
|
41
|
-
const HEADER_BG = 'var(--ogrid-header-bg, #f5f5f5)';
|
|
42
|
-
const STICKY_HEADER_SX = {
|
|
43
|
-
/* Removed position: 'sticky', top: 0 - breaks horizontal sticky on pinned columns.
|
|
44
|
-
Instead, apply sticky to individual header cells (HEADER_BASE_SX). */
|
|
45
|
-
zIndex: 8,
|
|
46
|
-
bgcolor: HEADER_BG,
|
|
47
|
-
'& th': { bgcolor: HEADER_BG }
|
|
48
|
-
};
|
|
49
|
-
const HEADER_ROW_SX = { bgcolor: HEADER_BG };
|
|
50
|
-
const GROUP_HEADER_CELL_SX = { textAlign: 'center', fontWeight: 600, borderBottom: 2, borderColor: 'divider', py: 0.75 };
|
|
51
|
-
// Density padding helper
|
|
52
|
-
function getDensityPadding(density) {
|
|
53
|
-
switch (density) {
|
|
54
|
-
case 'compact': return { px: '8px', py: '4px' };
|
|
55
|
-
case 'comfortable': return { px: '16px', py: '12px' };
|
|
56
|
-
default: return { px: '10px', py: '6px' };
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// Cell content base variants (selected by column type + editability)
|
|
60
|
-
const CELL_CONTENT_BASE_SX = {
|
|
61
|
-
width: '100%', height: '100%', display: 'flex', alignItems: 'center', minWidth: 0,
|
|
62
|
-
px: '10px', py: '6px', boxSizing: 'border-box', overflow: 'hidden',
|
|
63
|
-
textOverflow: 'ellipsis', whiteSpace: 'nowrap', userSelect: 'none', outline: 'none',
|
|
64
|
-
'&:focus-visible': { outline: '2px solid', outlineColor: 'primary.main', outlineOffset: '-2px', zIndex: 3 },
|
|
65
|
-
};
|
|
66
|
-
const CELL_CONTENT_NUMERIC_SX = { ...CELL_CONTENT_BASE_SX, justifyContent: 'flex-end', textAlign: 'right' };
|
|
67
|
-
const CELL_CONTENT_BOOLEAN_SX = { ...CELL_CONTENT_BASE_SX, justifyContent: 'center', textAlign: 'center' };
|
|
68
|
-
const CELL_CONTENT_EDITABLE_SX = { ...CELL_CONTENT_BASE_SX, cursor: 'cell' };
|
|
69
|
-
const CELL_CONTENT_NUMERIC_EDITABLE_SX = { ...CELL_CONTENT_NUMERIC_SX, cursor: 'cell' };
|
|
70
|
-
const CELL_CONTENT_BOOLEAN_EDITABLE_SX = { ...CELL_CONTENT_BOOLEAN_SX, cursor: 'cell' };
|
|
71
|
-
// Cell overlay states (only applied to the few active/selected cells)
|
|
72
|
-
// Active cell: theme-aware bg so dark mode doesn't show white (MUI action.hover adapts to theme)
|
|
73
|
-
const CELL_ACTIVE_SX = { outline: '2px solid var(--ogrid-selection, #217346)', outlineOffset: '-1px', zIndex: 2, position: 'relative', overflow: 'visible', bgcolor: 'action.hover', '&:focus-visible': { outline: '2px solid var(--ogrid-selection, #217346)', outlineOffset: '-1px' } };
|
|
74
|
-
const CELL_IN_RANGE_SX = { bgcolor: 'var(--ogrid-bg-range, rgba(33, 115, 70, 0.12))', '&:focus-visible': { outline: 'none' } };
|
|
75
|
-
const CELL_CUT_RANGE_SX = { bgcolor: 'action.hover', opacity: 0.7 };
|
|
76
|
-
// Pre-computed overlay variant arrays (avoid per-cell array allocation + filter)
|
|
77
|
-
// Key: `${base}_${overlay}` where overlay is 'active' | 'range' | 'cut'
|
|
78
|
-
const OVERLAY_VARIANTS = {
|
|
79
|
-
base_active: [CELL_CONTENT_BASE_SX, CELL_ACTIVE_SX],
|
|
80
|
-
base_range: [CELL_CONTENT_BASE_SX, CELL_IN_RANGE_SX],
|
|
81
|
-
base_cut: [CELL_CONTENT_BASE_SX, CELL_IN_RANGE_SX, CELL_CUT_RANGE_SX],
|
|
82
|
-
editable_active: [CELL_CONTENT_EDITABLE_SX, CELL_ACTIVE_SX],
|
|
83
|
-
editable_range: [CELL_CONTENT_EDITABLE_SX, CELL_IN_RANGE_SX],
|
|
84
|
-
editable_cut: [CELL_CONTENT_EDITABLE_SX, CELL_IN_RANGE_SX, CELL_CUT_RANGE_SX],
|
|
85
|
-
numeric_active: [CELL_CONTENT_NUMERIC_SX, CELL_ACTIVE_SX],
|
|
86
|
-
numeric_range: [CELL_CONTENT_NUMERIC_SX, CELL_IN_RANGE_SX],
|
|
87
|
-
numeric_cut: [CELL_CONTENT_NUMERIC_SX, CELL_IN_RANGE_SX, CELL_CUT_RANGE_SX],
|
|
88
|
-
numeric_editable_active: [CELL_CONTENT_NUMERIC_EDITABLE_SX, CELL_ACTIVE_SX],
|
|
89
|
-
numeric_editable_range: [CELL_CONTENT_NUMERIC_EDITABLE_SX, CELL_IN_RANGE_SX],
|
|
90
|
-
numeric_editable_cut: [CELL_CONTENT_NUMERIC_EDITABLE_SX, CELL_IN_RANGE_SX, CELL_CUT_RANGE_SX],
|
|
91
|
-
boolean_active: [CELL_CONTENT_BOOLEAN_SX, CELL_ACTIVE_SX],
|
|
92
|
-
boolean_range: [CELL_CONTENT_BOOLEAN_SX, CELL_IN_RANGE_SX],
|
|
93
|
-
boolean_cut: [CELL_CONTENT_BOOLEAN_SX, CELL_IN_RANGE_SX, CELL_CUT_RANGE_SX],
|
|
94
|
-
boolean_editable_active: [CELL_CONTENT_BOOLEAN_EDITABLE_SX, CELL_ACTIVE_SX],
|
|
95
|
-
boolean_editable_range: [CELL_CONTENT_BOOLEAN_EDITABLE_SX, CELL_IN_RANGE_SX],
|
|
96
|
-
boolean_editable_cut: [CELL_CONTENT_BOOLEAN_EDITABLE_SX, CELL_IN_RANGE_SX, CELL_CUT_RANGE_SX],
|
|
97
|
-
};
|
|
98
|
-
/** Select pre-computed sx for a cell based on column type, editability, and overlay state. */
|
|
99
|
-
function getCellSx(colType, canEdit, isActive, isInRange, isInCutRange) {
|
|
100
|
-
// Determine base key
|
|
101
|
-
let baseKey;
|
|
102
|
-
if (colType === 'numeric')
|
|
103
|
-
baseKey = canEdit ? 'numeric_editable' : 'numeric';
|
|
104
|
-
else if (colType === 'boolean')
|
|
105
|
-
baseKey = canEdit ? 'boolean_editable' : 'boolean';
|
|
106
|
-
else
|
|
107
|
-
baseKey = canEdit ? 'editable' : 'base';
|
|
108
|
-
// Determine overlay
|
|
109
|
-
if (isInCutRange)
|
|
110
|
-
return OVERLAY_VARIANTS[`${baseKey}_cut`];
|
|
111
|
-
if (isInRange)
|
|
112
|
-
return OVERLAY_VARIANTS[`${baseKey}_range`];
|
|
113
|
-
if (isActive)
|
|
114
|
-
return OVERLAY_VARIANTS[`${baseKey}_active`];
|
|
115
|
-
// No overlay — return the base sx directly
|
|
116
|
-
if (colType === 'numeric')
|
|
117
|
-
return canEdit ? CELL_CONTENT_NUMERIC_EDITABLE_SX : CELL_CONTENT_NUMERIC_SX;
|
|
118
|
-
if (colType === 'boolean')
|
|
119
|
-
return canEdit ? CELL_CONTENT_BOOLEAN_EDITABLE_SX : CELL_CONTENT_BOOLEAN_SX;
|
|
120
|
-
return canEdit ? CELL_CONTENT_EDITABLE_SX : CELL_CONTENT_BASE_SX;
|
|
121
|
-
}
|
|
122
|
-
// Fill handle
|
|
123
|
-
const FILL_HANDLE_SX = {
|
|
124
|
-
position: 'absolute', right: -3, bottom: -3, width: 7, height: 7,
|
|
125
|
-
bgcolor: 'var(--ogrid-selection, #217346)', border: '1px solid var(--ogrid-bg, #fff)', borderRadius: '1px',
|
|
126
|
-
cursor: 'crosshair', pointerEvents: 'auto', zIndex: 3,
|
|
127
|
-
};
|
|
128
|
-
// Cell <td> positioning variants
|
|
129
|
-
const CELL_TD_BASE_SX = { position: 'relative', p: 0, height: '1px' };
|
|
130
|
-
const CELL_TD_PINNED_LEFT_SX = {
|
|
131
|
-
...CELL_TD_BASE_SX, position: 'sticky', left: 0, zIndex: 6,
|
|
132
|
-
bgcolor: 'background.paper', willChange: 'transform',
|
|
133
|
-
borderRight: '1px solid', borderRightColor: 'divider',
|
|
134
|
-
boxShadow: '2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
135
|
-
};
|
|
136
|
-
const CELL_TD_PINNED_RIGHT_SX = {
|
|
137
|
-
...CELL_TD_BASE_SX, position: 'sticky', right: 0, zIndex: 6,
|
|
138
|
-
bgcolor: 'background.paper', willChange: 'transform',
|
|
139
|
-
borderLeft: '1px solid', borderLeftColor: 'divider',
|
|
140
|
-
boxShadow: '-2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
141
|
-
};
|
|
142
|
-
// Header cell positioning variants (sticky)
|
|
143
|
-
// Use opaque HEADER_BG so headers fully occlude content scrolling beneath them.
|
|
144
|
-
const HEADER_BASE_SX = {
|
|
145
|
-
fontWeight: 600,
|
|
146
|
-
position: 'sticky', /* Enables vertical sticky for all headers */
|
|
147
|
-
top: 0, /* Sticky vertically */
|
|
148
|
-
zIndex: 8, /* Stack above body cells */
|
|
149
|
-
bgcolor: HEADER_BG /* Opaque — required for sticky overlap */
|
|
150
|
-
};
|
|
151
|
-
const HEADER_PINNED_LEFT_SX = {
|
|
152
|
-
...HEADER_BASE_SX, position: 'sticky', left: 0, top: 0,
|
|
153
|
-
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
154
|
-
borderRight: '1px solid', borderRightColor: 'divider',
|
|
155
|
-
boxShadow: '2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
156
|
-
};
|
|
157
|
-
const HEADER_PINNED_RIGHT_SX = {
|
|
158
|
-
...HEADER_BASE_SX, position: 'sticky', right: 0, top: 0,
|
|
159
|
-
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
160
|
-
borderLeft: '1px solid', borderLeftColor: 'divider',
|
|
161
|
-
boxShadow: '-2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
162
|
-
};
|
|
163
|
-
// Header cell variants (non-sticky — stickyHeader=false)
|
|
164
|
-
const HEADER_BASE_NO_STICKY_SX = {
|
|
165
|
-
fontWeight: 600,
|
|
166
|
-
zIndex: 8,
|
|
167
|
-
bgcolor: HEADER_BG,
|
|
168
|
-
};
|
|
169
|
-
const HEADER_PINNED_LEFT_NO_STICKY_SX = {
|
|
170
|
-
...HEADER_BASE_NO_STICKY_SX, position: 'sticky', left: 0,
|
|
171
|
-
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
172
|
-
borderRight: '1px solid', borderRightColor: 'divider',
|
|
173
|
-
boxShadow: '2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
174
|
-
};
|
|
175
|
-
const HEADER_PINNED_RIGHT_NO_STICKY_SX = {
|
|
176
|
-
...HEADER_BASE_NO_STICKY_SX, position: 'sticky', right: 0,
|
|
177
|
-
zIndex: 10, bgcolor: HEADER_BG, willChange: 'transform',
|
|
178
|
-
borderLeft: '1px solid', borderLeftColor: 'divider',
|
|
179
|
-
boxShadow: '-2px 0 4px -1px rgba(0,0,0,0.1)',
|
|
180
|
-
};
|
|
181
|
-
// Resize handle
|
|
182
|
-
const RESIZE_HANDLE_SX = {
|
|
183
|
-
position: 'absolute', top: 0, right: '-3px', bottom: 0, width: '8px',
|
|
184
|
-
cursor: 'col-resize', userSelect: 'none',
|
|
185
|
-
'&::after': { content: '""', position: 'absolute', top: 0, right: '3px', bottom: 0, width: '2px' },
|
|
186
|
-
'&:hover::after': { bgcolor: 'primary.main' },
|
|
187
|
-
'&:active::after': { bgcolor: 'primary.dark' },
|
|
188
|
-
};
|
|
189
|
-
// Popover
|
|
190
|
-
const POPOVER_ANCHOR_SX = { minHeight: '100%', minWidth: 40 };
|
|
191
|
-
const POPOVER_CONTENT_SX = { p: 1 };
|
|
192
|
-
// Wrapper
|
|
193
|
-
const WRAPPER_SCROLL_SX = { display: 'flex', flexDirection: 'column', minHeight: '100%' };
|
|
194
|
-
// Header cell content wrapper
|
|
195
|
-
const HEADER_CONTENT_FLEX_SX = { display: 'flex', alignItems: 'center', gap: 0.5 };
|
|
196
|
-
// Column options button
|
|
197
|
-
const COLUMN_OPTIONS_BUTTON_SX = {
|
|
198
|
-
background: 'transparent',
|
|
199
|
-
border: 'none',
|
|
200
|
-
cursor: 'pointer',
|
|
201
|
-
padding: '2px 4px',
|
|
202
|
-
fontSize: '16px',
|
|
203
|
-
lineHeight: 1,
|
|
204
|
-
color: 'text.secondary',
|
|
205
|
-
opacity: 1,
|
|
206
|
-
transition: 'background-color 0.15s',
|
|
207
|
-
borderRadius: '3px',
|
|
208
|
-
display: 'flex',
|
|
209
|
-
alignItems: 'center',
|
|
210
|
-
justifyContent: 'center',
|
|
211
|
-
minWidth: '20px',
|
|
212
|
-
height: '20px',
|
|
213
|
-
'&:hover': {
|
|
214
|
-
bgcolor: 'action.hover',
|
|
215
|
-
},
|
|
216
|
-
};
|
|
217
|
-
// Table wrapper
|
|
218
|
-
const TABLE_WRAPPER_SX = { position: 'relative', opacity: 1 };
|
|
219
|
-
const TABLE_WRAPPER_LOADING_SX = { position: 'relative', opacity: 0.6 };
|
|
220
|
-
// TableBody — remove bottom border from last row so DataGridTable has no outer border
|
|
221
|
-
// (the OGridLayout container provides the border/radius)
|
|
222
|
-
const TABLE_BODY_SX = { '& tr:last-child td': { borderBottom: 'none' } };
|
|
223
|
-
function GridRowInner(props) {
|
|
224
|
-
const { item, rowIndex, rowId, isSelected, columnLayouts, renderCellContent, handleSingleRowClick, handleRowCheckboxChange, lastMouseShiftRef, hasCheckboxCol, hasRowNumbersCol, rowNumberOffset, } = props;
|
|
225
|
-
return (_jsxs(TableRow, { selected: isSelected, "data-row-id": rowId, onClick: handleSingleRowClick, sx: ROW_HOVER_SX, children: [hasCheckboxCol && (_jsx(TableCell, { padding: "checkbox", sx: CHECKBOX_CELL_SX, children: _jsx(Box, { "data-row-index": rowIndex, "data-col-index": 0, onClick: STOP_PROPAGATION, sx: CHECKBOX_WRAPPER_SX, children: _jsx(Checkbox, { checked: isSelected, onChange: (_, checked) => handleRowCheckboxChange(rowId, checked, rowIndex, lastMouseShiftRef.current), size: "small", "aria-label": `Select row ${rowIndex + 1}` }) }) })), hasRowNumbersCol && (_jsx(TableCell, { sx: {
|
|
226
|
-
width: ROW_NUMBER_COLUMN_WIDTH,
|
|
227
|
-
minWidth: ROW_NUMBER_COLUMN_WIDTH,
|
|
228
|
-
maxWidth: ROW_NUMBER_COLUMN_WIDTH,
|
|
229
|
-
textAlign: 'center',
|
|
230
|
-
fontWeight: 600,
|
|
231
|
-
fontVariantNumeric: 'tabular-nums',
|
|
232
|
-
color: 'text.secondary',
|
|
233
|
-
backgroundColor: 'action.hover',
|
|
234
|
-
position: 'sticky',
|
|
235
|
-
left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
|
|
236
|
-
zIndex: 3,
|
|
237
|
-
}, children: rowNumberOffset + rowIndex + 1 })), columnLayouts.map((cl, colIdx) => (_jsx(TableCell, { "data-column-id": cl.col.columnId, sx: [cl.tdSx, { minWidth: cl.minWidth, width: cl.width, maxWidth: cl.maxWidth }], children: renderCellContent(item, cl.col, rowIndex, colIdx) }, cl.col.columnId)))] }));
|
|
238
|
-
}
|
|
239
|
-
const GridRow = React.memo(GridRowInner, areGridRowPropsEqual);
|
|
240
|
-
function DataGridTableInner(props) {
|
|
241
|
-
const o = useDataGridTableOrchestration({ props });
|
|
242
|
-
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;
|
|
243
|
-
// Density-aware cell padding
|
|
244
|
-
const densityPadding = useMemo(() => getDensityPadding(density), [density]);
|
|
245
|
-
const headerCellSx = useMemo(() => ({ px: densityPadding.px, py: densityPadding.py }), [densityPadding]);
|
|
246
|
-
// Shared width/minWidth computation (deduped with Radix/Fluent via useColumnMeta)
|
|
247
|
-
const columnMeta = useColumnMeta({
|
|
248
|
-
visibleCols,
|
|
249
|
-
getColumnWidth,
|
|
250
|
-
columnSizingOverrides,
|
|
251
|
-
measuredColumnWidths,
|
|
252
|
-
pinnedColumns: pinning.pinnedColumns,
|
|
253
|
-
leftOffsets: pinning.leftOffsets,
|
|
254
|
-
rightOffsets: pinning.rightOffsets,
|
|
255
|
-
pinnedColLeftClass: '',
|
|
256
|
-
pinnedColRightClass: '',
|
|
257
|
-
});
|
|
258
|
-
// Pre-compute per-column layout (tdSx + widths from columnMeta) so GridRow doesn't recalculate per-cell
|
|
259
|
-
const columnLayouts = useMemo(() => visibleCols.map((col) => {
|
|
260
|
-
const isPinnedLeft = pinning.pinnedColumns[col.columnId] === 'left';
|
|
261
|
-
const isPinnedRight = pinning.pinnedColumns[col.columnId] === 'right';
|
|
262
|
-
const baseTdSx = isPinnedLeft ? CELL_TD_PINNED_LEFT_SX : isPinnedRight ? CELL_TD_PINNED_RIGHT_SX : CELL_TD_BASE_SX;
|
|
263
|
-
// Override sticky offset for pinned columns (supports multiple pinned columns)
|
|
264
|
-
const tdSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null
|
|
265
|
-
? { ...baseTdSx, left: pinning.leftOffsets[col.columnId] }
|
|
266
|
-
: isPinnedRight && pinning.rightOffsets[col.columnId] != null
|
|
267
|
-
? { ...baseTdSx, right: pinning.rightOffsets[col.columnId] }
|
|
268
|
-
: baseTdSx;
|
|
269
|
-
const cellStyle = columnMeta.cellStyles[col.columnId];
|
|
270
|
-
return {
|
|
271
|
-
col,
|
|
272
|
-
tdSx,
|
|
273
|
-
minWidth: cellStyle?.minWidth ?? 0,
|
|
274
|
-
width: cellStyle?.width ?? getColumnWidth(col),
|
|
275
|
-
maxWidth: cellStyle?.maxWidth ?? getColumnWidth(col),
|
|
276
|
-
};
|
|
277
|
-
}), [visibleCols, columnMeta, pinning.pinnedColumns, pinning.leftOffsets, pinning.rightOffsets, getColumnWidth]);
|
|
278
|
-
// Wrapper sx (depends on dynamic values — memoize to avoid recreation)
|
|
279
|
-
const wrapperSx = useMemo(() => ({
|
|
280
|
-
position: 'relative',
|
|
281
|
-
flex: 1,
|
|
282
|
-
minHeight: isLoading && items.length === 0 ? 200 : 0,
|
|
283
|
-
width: fitToContent ? 'fit-content' : '100%',
|
|
284
|
-
maxWidth: '100%',
|
|
285
|
-
overflowX: suppressHorizontalScroll ? 'hidden' : allowOverflowX ? 'auto' : 'hidden',
|
|
286
|
-
overflowY: 'auto',
|
|
287
|
-
bgcolor: 'background.paper',
|
|
288
|
-
willChange: 'scroll-position',
|
|
289
|
-
'& [data-drag-range]': { bgcolor: 'rgba(33, 115, 70, 0.12) !important' },
|
|
290
|
-
'& [data-drag-anchor]': { bgcolor: 'background.paper !important' },
|
|
291
|
-
...(rowHeight ? { '& tbody tr': { height: rowHeight } } : {}),
|
|
292
|
-
}), [fitToContent, suppressHorizontalScroll, allowOverflowX, isLoading, items.length, rowHeight]);
|
|
293
|
-
const renderCellContent = useCallback((item, col, rowIndex, colIdx) => {
|
|
294
|
-
const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInputRef.current);
|
|
295
|
-
const rowId = getRowId(item);
|
|
296
|
-
let cellContent;
|
|
297
|
-
if (descriptor.mode === 'editing-inline') {
|
|
298
|
-
cellContent = (_jsx("div", { style: EDITING_CELL_STYLE, children: _jsx(InlineCellEditor, { ...buildInlineEditorProps(item, col, descriptor, editCallbacks) }) }));
|
|
299
|
-
}
|
|
300
|
-
else if (descriptor.mode === 'editing-popover' && typeof col.cellEditor === 'function') {
|
|
301
|
-
const editorProps = buildPopoverEditorProps(item, col, descriptor, pendingEditorValueRef.current, editCallbacks);
|
|
302
|
-
const CustomEditor = col.cellEditor;
|
|
303
|
-
cellContent = (_jsxs(_Fragment, { children: [_jsx(Box, { ref: (el) => { if (el)
|
|
304
|
-
setPopoverAnchorEl(el); }, sx: POPOVER_ANCHOR_SX, "aria-hidden": true }), _jsx(Popover, { open: !!popoverAnchorElRef.current, anchorEl: popoverAnchorElRef.current, onClose: cancelPopoverEdit, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, transformOrigin: { vertical: 'top', horizontal: 'left' }, children: _jsx(Box, { sx: POPOVER_CONTENT_SX, children: _jsx(CustomEditor, { ...editorProps }) }) })] }));
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
const content = resolveCellDisplayContent(col, item, descriptor.displayValue);
|
|
308
|
-
const cellStyle = resolveCellStyle(col, item);
|
|
309
|
-
const styledContent = cellStyle ? _jsx(Box, { component: "span", sx: cellStyle, children: content }) : content;
|
|
310
|
-
// Select pre-computed sx variant (module-scope = no per-cell allocation)
|
|
311
|
-
const cellSx = getCellSx(col.type, descriptor.canEditAny, descriptor.isActive && !descriptor.isInRange, descriptor.isInRange, descriptor.isInCutRange);
|
|
312
|
-
const interactionProps = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
|
|
313
|
-
cellContent = (_jsxs(Box, { component: "div", ...interactionProps, sx: Array.isArray(cellSx) ? [...cellSx, densityPadding] : { ...cellSx, ...densityPadding }, children: [styledContent, descriptor.canEditAny && descriptor.isSelectionEndCell && (_jsx(Box, { component: "div", onMouseDown: handleFillHandleMouseDown, "aria-label": "Fill handle", sx: FILL_HANDLE_SX }))] }));
|
|
314
|
-
}
|
|
315
|
-
return (_jsx(CellErrorBoundary, { onError: onCellError, children: cellContent }, `${rowId}-${col.columnId}`));
|
|
316
|
-
}, [editCallbacks, interactionHandlers, handleFillHandleMouseDown, setPopoverAnchorEl, cancelPopoverEdit, getRowId, onCellError, cellDescriptorInputRef, densityPadding, pendingEditorValueRef, popoverAnchorElRef]);
|
|
317
|
-
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("div", { style: { 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, { ...{
|
|
318
|
-
component: "th",
|
|
319
|
-
scope: "col",
|
|
320
|
-
rowSpan: headerRows.length > 1 ? 1 : undefined,
|
|
321
|
-
sx: {
|
|
322
|
-
width: ROW_NUMBER_COLUMN_WIDTH,
|
|
323
|
-
minWidth: ROW_NUMBER_COLUMN_WIDTH,
|
|
324
|
-
maxWidth: ROW_NUMBER_COLUMN_WIDTH,
|
|
325
|
-
textAlign: 'center',
|
|
326
|
-
fontWeight: 600,
|
|
327
|
-
backgroundColor: HEADER_BG,
|
|
328
|
-
position: 'sticky',
|
|
329
|
-
left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
|
|
330
|
-
zIndex: 4,
|
|
331
|
-
...headerCellSx,
|
|
332
|
-
}
|
|
333
|
-
}, children: "#" })), rowIdx === 0 && rowIdx < headerRows.length - 1 && hasRowNumbersCol && (_jsx(TableCell, { ...{
|
|
334
|
-
rowSpan: headerRows.length - 1,
|
|
335
|
-
sx: {
|
|
336
|
-
width: ROW_NUMBER_COLUMN_WIDTH,
|
|
337
|
-
minWidth: ROW_NUMBER_COLUMN_WIDTH,
|
|
338
|
-
position: 'sticky',
|
|
339
|
-
left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
|
|
340
|
-
zIndex: 4,
|
|
341
|
-
backgroundColor: 'background.paper',
|
|
342
|
-
}
|
|
343
|
-
} })), row.map((cell, cellIdx) => {
|
|
344
|
-
if (cell.isGroup) {
|
|
345
|
-
return (_jsx(TableCell, { ...{
|
|
346
|
-
colSpan: cell.colSpan,
|
|
347
|
-
component: "th",
|
|
348
|
-
scope: "colgroup",
|
|
349
|
-
sx: GROUP_HEADER_CELL_SX
|
|
350
|
-
}, children: cell.label }, cellIdx));
|
|
351
|
-
}
|
|
352
|
-
// Leaf cell
|
|
353
|
-
if (!cell.columnDef)
|
|
354
|
-
return null;
|
|
355
|
-
const col = cell.columnDef;
|
|
356
|
-
const isPinnedLeft = pinning.pinnedColumns[col.columnId] === 'left';
|
|
357
|
-
const isPinnedRight = pinning.pinnedColumns[col.columnId] === 'right';
|
|
358
|
-
const baseHeaderSx = o.stickyHeader
|
|
359
|
-
? (isPinnedLeft ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX)
|
|
360
|
-
: (isPinnedLeft ? HEADER_PINNED_LEFT_NO_STICKY_SX : isPinnedRight ? HEADER_PINNED_RIGHT_NO_STICKY_SX : HEADER_BASE_NO_STICKY_SX);
|
|
361
|
-
// Override sticky offset for pinned columns (supports multiple pinned columns)
|
|
362
|
-
const headerSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null
|
|
363
|
-
? { ...baseHeaderSx, left: pinning.leftOffsets[col.columnId] }
|
|
364
|
-
: isPinnedRight && pinning.rightOffsets[col.columnId] != null
|
|
365
|
-
? { ...baseHeaderSx, right: pinning.rightOffsets[col.columnId] }
|
|
366
|
-
: baseHeaderSx;
|
|
367
|
-
// Width/minWidth from shared useColumnMeta (avoids duplicate calculation)
|
|
368
|
-
const hdrStyle = columnMeta.hdrStyles[col.columnId];
|
|
369
|
-
// Determine aria-sort value for sorted columns
|
|
370
|
-
const isSorted = props.sortBy === col.columnId;
|
|
371
|
-
const ariaSort = isSorted
|
|
372
|
-
? (props.sortDirection === 'asc' ? 'ascending' : 'descending')
|
|
373
|
-
: undefined;
|
|
374
|
-
return (_jsxs(TableCell, { ...{
|
|
375
|
-
component: "th",
|
|
376
|
-
scope: "col",
|
|
377
|
-
'data-column-id': col.columnId,
|
|
378
|
-
rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : undefined,
|
|
379
|
-
'aria-sort': ariaSort,
|
|
380
|
-
sx: {
|
|
381
|
-
...headerSx,
|
|
382
|
-
...headerCellSx,
|
|
383
|
-
minWidth: hdrStyle?.minWidth,
|
|
384
|
-
width: hdrStyle?.width,
|
|
385
|
-
maxWidth: hdrStyle?.maxWidth,
|
|
386
|
-
...(columnReorder ? { cursor: isReorderDragging ? 'grabbing' : 'grab' } : {}),
|
|
387
|
-
'&:focus-visible': {
|
|
388
|
-
outline: '2px solid',
|
|
389
|
-
outlineColor: 'primary.main',
|
|
390
|
-
outlineOffset: '-2px',
|
|
391
|
-
zIndex: 11,
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
onMouseDown: columnReorder ? (e) => handleHeaderMouseDown(col.columnId, e) : undefined
|
|
395
|
-
}, children: [_jsxs(Box, { sx: HEADER_CONTENT_FLEX_SX, children: [_jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }), _jsx(Box, { component: "button", onClick: (e) => {
|
|
396
|
-
e.stopPropagation();
|
|
397
|
-
headerMenu.open(col.columnId, e.currentTarget);
|
|
398
|
-
}, "aria-label": "Column options", title: "Column options", sx: COLUMN_OPTIONS_BUTTON_SX, children: "\u22EE" })] }), _jsx(Box, { onMouseDown: (e) => {
|
|
399
|
-
setActiveCell(null);
|
|
400
|
-
interaction.setSelectionRange(null);
|
|
401
|
-
wrapperRef.current?.focus({ preventScroll: true });
|
|
402
|
-
handleResizeStart(e, col);
|
|
403
|
-
}, sx: RESIZE_HANDLE_SX })] }, col.columnId));
|
|
404
|
-
})] }, rowIdx))) }), !showEmptyInGrid && (_jsxs(TableBody, { sx: TABLE_BODY_SX, children: [virtualScrollEnabled && visibleRange.offsetTop > 0 && (_jsx(TableRow, { style: { height: visibleRange.offsetTop }, "aria-hidden": true })), (virtualScrollEnabled
|
|
405
|
-
? items.slice(visibleRange.startIndex, visibleRange.endIndex + 1).map((item, i) => {
|
|
406
|
-
const rowIndex = visibleRange.startIndex + i;
|
|
407
|
-
const rowIdStr = getRowId(item);
|
|
408
|
-
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));
|
|
409
|
-
})
|
|
410
|
-
: items.map((item, rowIndex) => {
|
|
411
|
-
const rowIdStr = getRowId(item);
|
|
412
|
-
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));
|
|
413
|
-
})), 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 &&
|
|
414
|
-
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 }))] }));
|
|
415
|
-
}
|
|
416
|
-
export const DataGridTable = React.memo(DataGridTableInner);
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from '@mui/material';
|
|
3
|
-
export function DropIndicator({ dropIndicatorX, wrapperLeft }) {
|
|
4
|
-
return (_jsx(Box, { sx: {
|
|
5
|
-
position: 'absolute',
|
|
6
|
-
top: 0,
|
|
7
|
-
bottom: 0,
|
|
8
|
-
width: 3,
|
|
9
|
-
bgcolor: 'var(--ogrid-primary, #217346)',
|
|
10
|
-
pointerEvents: 'none',
|
|
11
|
-
zIndex: 100,
|
|
12
|
-
transition: 'left 0.05s',
|
|
13
|
-
left: dropIndicatorX - wrapperLeft,
|
|
14
|
-
} }));
|
|
15
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Typography, Button } from '@mui/material';
|
|
3
|
-
const EMPTY_STATE_SX = { py: 4, px: 2, textAlign: 'center', borderTop: 1, borderColor: 'divider', bgcolor: 'action.hover' };
|
|
4
|
-
export function EmptyState({ emptyState }) {
|
|
5
|
-
return (_jsx(Box, { sx: EMPTY_STATE_SX, children: emptyState.render ? (emptyState.render()) : (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: "No results found" }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: emptyState.message != null ? (emptyState.message) : emptyState.hasActiveFilters ? (_jsxs(_Fragment, { children: ["No items match your current filters. Try adjusting your search or", ' ', _jsx(Button, { variant: "text", size: "small", onClick: emptyState.onClearAll, children: "clear all filters" }), ' ', "to see all items."] })) : ('There are no items available at this time.') })] })) }));
|
|
6
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Menu, MenuItem, Divider } from '@mui/material';
|
|
4
|
-
import { GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from '@alaarab/ogrid-react';
|
|
5
|
-
export function GridContextMenu(props) {
|
|
6
|
-
const { x, y, hasSelection, canUndo, canRedo, onClose } = props;
|
|
7
|
-
const handlers = React.useMemo(() => getContextMenuHandlers(props), [props]);
|
|
8
|
-
const isDisabled = React.useCallback((item) => {
|
|
9
|
-
if (item.disabledWhenNoSelection && !hasSelection)
|
|
10
|
-
return true;
|
|
11
|
-
if (item.id === 'undo' && !canUndo)
|
|
12
|
-
return true;
|
|
13
|
-
if (item.id === 'redo' && !canRedo)
|
|
14
|
-
return true;
|
|
15
|
-
return false;
|
|
16
|
-
}, [hasSelection, canUndo, canRedo]);
|
|
17
|
-
return (_jsx(Menu, { open: true, onClose: onClose, anchorReference: "anchorPosition", anchorPosition: { top: y, left: x }, MenuListProps: { dense: true, 'aria-label': 'Grid context menu' }, children: GRID_CONTEXT_MENU_ITEMS.flatMap((item) => {
|
|
18
|
-
const elements = [];
|
|
19
|
-
if (item.dividerBefore) {
|
|
20
|
-
elements.push(_jsx(Divider, {}, `${item.id}-divider`));
|
|
21
|
-
}
|
|
22
|
-
elements.push(_jsxs(MenuItem, { onClick: handlers[item.id], disabled: isDisabled(item), children: [_jsx("span", { style: { flex: 1 }, children: item.label }), item.shortcut && (_jsx("span", { style: { marginLeft: 24, color: 'var(--ogrid-fg-muted, rgba(0,0,0,0.4))', fontSize: '0.8em' }, children: formatShortcut(item.shortcut) }))] }, item.id));
|
|
23
|
-
return elements;
|
|
24
|
-
}) }));
|
|
25
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Checkbox } from '@mui/material';
|
|
3
|
-
import { BaseInlineCellEditor } from '@alaarab/ogrid-react';
|
|
4
|
-
export function InlineCellEditor(props) {
|
|
5
|
-
return (_jsx(BaseInlineCellEditor, { ...props, renderCheckbox: (checked, onCommit, onCancel) => (_jsx(Checkbox, { checked: checked, onChange: (_, c) => onCommit(c), onKeyDown: (e) => e.key === 'Escape' && (e.preventDefault(), onCancel()), size: "small" })) }));
|
|
6
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, CircularProgress, Typography } from '@mui/material';
|
|
3
|
-
const LOADING_OVERLAY_SX = {
|
|
4
|
-
position: 'absolute', inset: 0, zIndex: 2,
|
|
5
|
-
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
6
|
-
background: 'var(--ogrid-loading-bg, rgba(255,255,255,0.7))',
|
|
7
|
-
};
|
|
8
|
-
const LOADING_INNER_SX = {
|
|
9
|
-
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1,
|
|
10
|
-
p: 2, bgcolor: 'background.paper', border: 1, borderColor: 'divider', borderRadius: 1,
|
|
11
|
-
};
|
|
12
|
-
export function LoadingOverlay({ message }) {
|
|
13
|
-
return (_jsx(Box, { sx: LOADING_OVERLAY_SX, children: _jsxs(Box, { sx: LOADING_INNER_SX, children: [_jsx(CircularProgress, { size: 24 }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: message })] }) }));
|
|
14
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Typography } from '@mui/material';
|
|
3
|
-
import { getStatusBarParts } from '@alaarab/ogrid-react';
|
|
4
|
-
const partSx = (isLast) => ({
|
|
5
|
-
display: 'inline-flex',
|
|
6
|
-
alignItems: 'center',
|
|
7
|
-
gap: 0.5,
|
|
8
|
-
...(isLast ? {} : { mr: 2, '&::after': { content: '"|"', ml: 2, color: 'divider' } }),
|
|
9
|
-
});
|
|
10
|
-
export function StatusBar(props) {
|
|
11
|
-
const parts = getStatusBarParts(props);
|
|
12
|
-
return (_jsx(Box, { role: "status", "aria-live": "polite", sx: { mt: 'auto', px: 1.5, py: 0.75, borderTop: 1, borderColor: 'divider', bgcolor: 'action.hover' }, children: parts.map((p, i) => (_jsxs(Typography, { component: "span", variant: "body2", sx: partSx(i === parts.length - 1), children: [_jsx(Typography, { component: "span", color: "text.secondary", children: p.label }), _jsx(Typography, { component: "span", fontWeight: 600, children: p.value.toLocaleString() })] }, p.key))) }));
|
|
13
|
-
}
|
package/dist/esm/OGrid/OGrid.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Box, useTheme } from '@mui/material';
|
|
4
|
-
import { createOGrid } from '@alaarab/ogrid-react';
|
|
5
|
-
import { DataGridTable } from '../DataGridTable/DataGridTable';
|
|
6
|
-
import { ColumnChooser } from '../ColumnChooser/ColumnChooser';
|
|
7
|
-
import { PaginationControls } from '../PaginationControls/PaginationControls';
|
|
8
|
-
/**
|
|
9
|
-
* MUI theme bridge: reads the MUI palette and sets --ogrid-* CSS variables
|
|
10
|
-
* so the shared OGridLayout and DataGridTable styles adapt to the MUI theme.
|
|
11
|
-
*/
|
|
12
|
-
const MuiThemeContainer = React.forwardRef(function MuiThemeContainer(props, ref) {
|
|
13
|
-
const theme = useTheme();
|
|
14
|
-
const sx = React.useMemo(() => ({
|
|
15
|
-
display: 'flex', flexDirection: 'column', gap: 1,
|
|
16
|
-
'--ogrid-bg': theme.palette.background.default,
|
|
17
|
-
'--ogrid-border': theme.palette.divider,
|
|
18
|
-
'--ogrid-header-bg': theme.palette.mode === 'dark' ? theme.palette.grey[800] : theme.palette.grey[100],
|
|
19
|
-
'--ogrid-fg': theme.palette.text.primary,
|
|
20
|
-
'--ogrid-fg-secondary': theme.palette.text.secondary,
|
|
21
|
-
'--ogrid-fg-muted': theme.palette.text.disabled,
|
|
22
|
-
'--ogrid-hover-bg': theme.palette.action.hover,
|
|
23
|
-
}), [theme]);
|
|
24
|
-
return _jsx(Box, { ref: ref, sx: sx, ...props });
|
|
25
|
-
});
|
|
26
|
-
export const OGrid = createOGrid({
|
|
27
|
-
DataGridTable: DataGridTable,
|
|
28
|
-
ColumnChooser: ColumnChooser,
|
|
29
|
-
PaginationControls,
|
|
30
|
-
containerComponent: MuiThemeContainer,
|
|
31
|
-
});
|
package/dist/esm/OGrid/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { OGrid } from './OGrid';
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { IconButton, Button, Select, MenuItem, Box, Typography, } from '@mui/material';
|
|
4
|
-
import { FirstPage as FirstPageIcon, LastPage as LastPageIcon, ChevronLeft as ChevronLeftIcon, ChevronRight as ChevronRightIcon, } from '@mui/icons-material';
|
|
5
|
-
import { usePaginationControls } from '@alaarab/ogrid-react';
|
|
6
|
-
export const PaginationControls = React.memo((props) => {
|
|
7
|
-
const { currentPage, pageSize, totalCount, onPageChange, onPageSizeChange, pageSizeOptions, entityLabelPlural, className, } = props;
|
|
8
|
-
const { labelPlural, vm, handlePageSizeChange } = usePaginationControls({
|
|
9
|
-
currentPage,
|
|
10
|
-
pageSize,
|
|
11
|
-
totalCount,
|
|
12
|
-
onPageChange,
|
|
13
|
-
onPageSizeChange,
|
|
14
|
-
pageSizeOptions,
|
|
15
|
-
entityLabelPlural,
|
|
16
|
-
});
|
|
17
|
-
const handlePageSizeChangeEvent = (event) => {
|
|
18
|
-
handlePageSizeChange(Number(event.target.value));
|
|
19
|
-
};
|
|
20
|
-
if (!vm) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
const { pageNumbers, showStartEllipsis, showEndEllipsis, totalPages, startItem, endItem } = vm;
|
|
24
|
-
return (_jsxs(Box, { className: className, role: "navigation", "aria-label": "Pagination", sx: {
|
|
25
|
-
display: 'flex',
|
|
26
|
-
alignItems: 'center',
|
|
27
|
-
justifyContent: 'space-between',
|
|
28
|
-
flexWrap: 'wrap',
|
|
29
|
-
gap: 2,
|
|
30
|
-
px: 1.5,
|
|
31
|
-
width: '100%',
|
|
32
|
-
minWidth: 0,
|
|
33
|
-
boxSizing: 'border-box',
|
|
34
|
-
}, children: [_jsxs(Typography, { variant: "body2", color: "text.secondary", children: ["Showing ", startItem, " to ", endItem, " of ", totalCount.toLocaleString(), " ", labelPlural] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.5 }, children: [_jsx(IconButton, { size: "small", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "First page", children: _jsx(FirstPageIcon, { fontSize: "small" }) }), _jsx(IconButton, { size: "small", onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, "aria-label": "Previous page", children: _jsx(ChevronLeftIcon, { fontSize: "small" }) }), showStartEllipsis && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outlined", size: "small", onClick: () => onPageChange(1), "aria-label": "Page 1", sx: { minWidth: 32, px: 0.5 }, children: "1" }), _jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mx: 0.5 }, "aria-hidden": true, children: "\u2026" })] })), pageNumbers.map((pageNum) => (_jsx(Button, { variant: currentPage === pageNum ? 'contained' : 'outlined', size: "small", onClick: () => onPageChange(pageNum), "aria-label": `Page ${pageNum}`, "aria-current": currentPage === pageNum ? 'page' : undefined, sx: { minWidth: 32, px: 0.5 }, children: pageNum }, pageNum))), showEndEllipsis && (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mx: 0.5 }, "aria-hidden": true, children: "\u2026" }), _jsx(Button, { variant: "outlined", size: "small", onClick: () => onPageChange(totalPages), "aria-label": `Page ${totalPages}`, sx: { minWidth: 32, px: 0.5 }, children: totalPages })] })), _jsx(IconButton, { size: "small", onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages, "aria-label": "Next page", children: _jsx(ChevronRightIcon, { fontSize: "small" }) }), _jsx(IconButton, { size: "small", onClick: () => onPageChange(totalPages), disabled: currentPage >= totalPages, "aria-label": "Last page", children: _jsx(LastPageIcon, { fontSize: "small" }) })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Typography, { variant: "body2", color: "text.secondary", children: "Rows" }), _jsx(Select, { value: pageSize, onChange: handlePageSizeChangeEvent, size: "small", "aria-label": "Rows per page", sx: { minWidth: 70 }, children: vm.pageSizeOptions.map((n) => (_jsx(MenuItem, { value: n, children: n }, n))) })] })] }));
|
|
35
|
-
});
|