@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.js CHANGED
@@ -1,11 +1,1577 @@
1
- // Components
2
- export { OGrid } from './OGrid';
3
- export { DataGridTable } from './DataGridTable/DataGridTable';
4
- export { ColumnChooser } from './ColumnChooser/ColumnChooser';
5
- export { ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter';
6
- export { PaginationControls } from './PaginationControls/PaginationControls';
7
- export { ColumnHeaderMenu } from './ColumnHeaderMenu/ColumnHeaderMenu';
8
- // Re-export all from base package for consumer convenience.
9
- // Note: This prevents tree-shaking of unused utilities.
10
- // Consider explicit named exports in a future major version.
11
- export * from '@alaarab/ogrid-react';
1
+ import * as React3 from 'react';
2
+ import { useMemo, useCallback, useState, useRef, useEffect } from 'react';
3
+ import { Box, Tooltip, Typography, IconButton, Popover, Button, Select, MenuItem, useTheme, Checkbox, Table, TableHead, TableRow, TableCell, FormControlLabel, Avatar, TextField, InputAdornment, CircularProgress, Menu, Divider } from '@mui/material';
4
+ import { useColumnHeaderFilterState, getColumnHeaderFilterStateParams, renderFilterContent, areGridRowPropsEqual, usePaginationControls, createOGrid, CHECKBOX_COLUMN_WIDTH, STOP_PROPAGATION, ROW_NUMBER_COLUMN_WIDTH, useDataGridTableOrchestration, useColumnMeta, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, getCellInteractionProps, CellErrorBoundary, PREVENT_DEFAULT, getHeaderFilterConfig, MarchingAntsOverlay, NOOP, useColumnChooserState, useListVirtualizer, BaseInlineCellEditor, getContextMenuHandlers, GRID_CONTEXT_MENU_ITEMS, formatShortcut, getColumnHeaderMenuItems, getStatusBarParts } from '@alaarab/ogrid-react';
5
+ export { BaseColumnHeaderMenu, BaseDropIndicator, BaseEmptyState, BaseInlineCellEditor, BaseLoadingOverlay, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CURSOR_CELL_STYLE, CellErrorBoundary, DEFAULT_MIN_COLUMN_WIDTH, DateFilterContent, EmptyState, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, GRID_ROOT_STYLE, GridContextMenu, MAX_PAGE_BUTTONS, MarchingAntsOverlay, NOOP, OGridLayout, PAGE_SIZE_OPTIONS, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, ROW_NUMBER_COLUMN_WIDTH, STOP_PROPAGATION, SideBar, StatusBar, UndoRedoStack, areGridRowPropsEqual, booleanParser, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, clampSelectionToBounds, computeAggregations, computeAutoScrollSpeed, computeTabNavigation, createOGrid, currencyParser, dateParser, deriveFilterOptionsFromData, editorInputStyle, editorWrapperStyle, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellInteractionProps, getCellRenderDescriptor, getCellValue, getColumnHeaderFilterStateParams, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getDateFilterContentProps, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getStatusBarParts, isInSelectionRange, isRowInRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, processClientSideData, rangesEqual, renderFilterContent, resolveCellDisplayContent, resolveCellStyle, richSelectDropdownStyle, richSelectNoMatchesStyle, richSelectOptionHighlightedStyle, richSelectOptionStyle, richSelectWrapperStyle, selectChevronStyle, selectDisplayStyle, selectEditorStyle, toUserLike, triggerCsvDownload, useActiveCell, useCellEditing, useCellSelection, useClipboard, useColumnChooserState, useColumnHeaderFilterState, useColumnMeta, useColumnReorder, useColumnResize, useContextMenu, useDataGridState, useDataGridTableOrchestration, useDateFilterState, useDebounce, useFillHandle, useFilterOptions, useInlineCellEditorState, useKeyboardNavigation, useLatestRef, useListVirtualizer, useMultiSelectFilterState, useOGrid, usePaginationControls, usePeopleFilterState, useRichSelectState, useRowSelection, useSelectState, useSideBarState, useTableLayout, useTextFilterState, useUndoRedo, useVirtualScroll } from '@alaarab/ogrid-react';
6
+ import { createPortal } from 'react-dom';
7
+ import { FilterList, FirstPage, ChevronLeft, ChevronRight, LastPage, ExpandLess, ExpandMore, ViewColumn, Clear, Search } from '@mui/icons-material';
8
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
+
10
+ // src/OGrid/OGrid.tsx
11
+ var TextFilterPopover = ({
12
+ value,
13
+ onValueChange,
14
+ onApply,
15
+ onClear
16
+ }) => /* @__PURE__ */ jsxs(Box, { sx: { width: 260 }, children: [
17
+ /* @__PURE__ */ jsx(Box, { sx: { p: 1.5 }, children: /* @__PURE__ */ jsx(
18
+ TextField,
19
+ {
20
+ placeholder: "Enter search term...",
21
+ value,
22
+ onChange: (e) => onValueChange(e.target.value),
23
+ onKeyDown: (e) => {
24
+ e.stopPropagation();
25
+ if (e.key === "Enter") {
26
+ e.preventDefault();
27
+ onApply();
28
+ }
29
+ },
30
+ autoComplete: "off",
31
+ size: "small",
32
+ fullWidth: true,
33
+ slotProps: {
34
+ input: {
35
+ startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(Search, { fontSize: "small" }) })
36
+ }
37
+ }
38
+ }
39
+ ) }),
40
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "flex-end", gap: 1, p: 1.5, pt: 0 }, children: [
41
+ /* @__PURE__ */ jsx(Button, { size: "small", disabled: !value, onClick: onClear, children: "Clear" }),
42
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "contained", onClick: onApply, children: "Apply" })
43
+ ] })
44
+ ] });
45
+ TextFilterPopover.displayName = "TextFilterPopover";
46
+ var ITEM_HEIGHT = 40;
47
+ var MultiSelectFilterPopover = ({
48
+ searchText,
49
+ onSearchChange,
50
+ options,
51
+ filteredOptions,
52
+ selected,
53
+ onOptionToggle,
54
+ onSelectAll,
55
+ onClearSelection,
56
+ onApply,
57
+ isLoading
58
+ }) => {
59
+ const virt = useListVirtualizer({ count: filteredOptions.length, itemHeight: ITEM_HEIGHT, containerHeight: 240 });
60
+ return /* @__PURE__ */ jsxs(Box, { sx: { width: 280 }, children: [
61
+ /* @__PURE__ */ jsxs(Box, { sx: { p: 1.5, pb: 0.5 }, children: [
62
+ /* @__PURE__ */ jsx(
63
+ TextField,
64
+ {
65
+ placeholder: "Search...",
66
+ value: searchText,
67
+ onChange: (e) => onSearchChange(e.target.value),
68
+ onKeyDown: (e) => e.stopPropagation(),
69
+ autoComplete: "off",
70
+ size: "small",
71
+ fullWidth: true,
72
+ slotProps: {
73
+ input: {
74
+ startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(Search, { fontSize: "small" }) })
75
+ }
76
+ }
77
+ }
78
+ ),
79
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "text.secondary", sx: { mt: 0.5, display: "block" }, children: [
80
+ filteredOptions.length,
81
+ " of ",
82
+ options.length,
83
+ " options"
84
+ ] })
85
+ ] }),
86
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "space-between", px: 1.5, py: 0.5 }, children: [
87
+ /* @__PURE__ */ jsxs(Button, { size: "small", onClick: onSelectAll, children: [
88
+ "Select All (",
89
+ filteredOptions.length,
90
+ ")"
91
+ ] }),
92
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: onClearSelection, children: "Clear" })
93
+ ] }),
94
+ /* @__PURE__ */ jsx(Box, { ref: virt.containerRef, onScroll: virt.onScroll, sx: { maxHeight: 240, overflowY: "auto", px: 0.5 }, children: isLoading ? /* @__PURE__ */ jsx(Box, { sx: { display: "flex", justifyContent: "center", py: 2 }, children: /* @__PURE__ */ jsx(CircularProgress, { size: 24 }) }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { py: 2, textAlign: "center" }, children: "No options found" }) : /* @__PURE__ */ jsx(Box, { sx: { height: virt.totalHeight, position: "relative" }, children: virt.visibleItems.map(({ index, offsetTop }) => {
95
+ const option = filteredOptions[index];
96
+ return /* @__PURE__ */ jsx(
97
+ FormControlLabel,
98
+ {
99
+ control: /* @__PURE__ */ jsx(
100
+ Checkbox,
101
+ {
102
+ size: "small",
103
+ checked: selected.has(option),
104
+ onChange: (e) => onOptionToggle(option, e.target.checked)
105
+ }
106
+ ),
107
+ label: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: option }),
108
+ sx: { position: "absolute", top: offsetTop, width: "100%", height: ITEM_HEIGHT, boxSizing: "border-box", display: "flex", alignItems: "center", mx: 0, "& .MuiFormControlLabel-label": { flex: 1, minWidth: 0 } }
109
+ },
110
+ option
111
+ );
112
+ }) }) }),
113
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "flex-end", gap: 1, p: 1.5, pt: 1, borderTop: 1, borderColor: "divider" }, children: [
114
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: onClearSelection, children: "Clear" }),
115
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "contained", onClick: onApply, children: "Apply" })
116
+ ] })
117
+ ] });
118
+ };
119
+ MultiSelectFilterPopover.displayName = "MultiSelectFilterPopover";
120
+ var PeopleFilterPopover = ({
121
+ selectedUser,
122
+ searchText,
123
+ onSearchChange,
124
+ suggestions,
125
+ isLoading,
126
+ onUserSelect,
127
+ onClearUser,
128
+ inputRef
129
+ }) => /* @__PURE__ */ jsxs(Box, { sx: { width: 300 }, children: [
130
+ selectedUser && /* @__PURE__ */ jsxs(Box, { sx: { p: 1.5, pb: 1, borderBottom: 1, borderColor: "divider" }, children: [
131
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Currently filtered by:" }),
132
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mt: 0.5 }, children: [
133
+ /* @__PURE__ */ jsx(Avatar, { src: selectedUser.photo, alt: selectedUser.displayName, sx: { width: 32, height: 32 }, children: selectedUser.displayName?.[0] }),
134
+ /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
135
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", noWrap: true, children: selectedUser.displayName }),
136
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", noWrap: true, children: selectedUser.email })
137
+ ] }),
138
+ /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onClearUser, "aria-label": "Remove filter", children: /* @__PURE__ */ jsx(Clear, { fontSize: "small" }) })
139
+ ] })
140
+ ] }),
141
+ /* @__PURE__ */ jsx(Box, { sx: { p: 1.5, pb: 0.5 }, children: /* @__PURE__ */ jsx(
142
+ TextField,
143
+ {
144
+ inputRef,
145
+ placeholder: "Search for a person...",
146
+ value: searchText,
147
+ onChange: (e) => onSearchChange(e.target.value),
148
+ onKeyDown: (e) => e.stopPropagation(),
149
+ autoComplete: "off",
150
+ size: "small",
151
+ fullWidth: true,
152
+ slotProps: {
153
+ input: {
154
+ startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(Search, { fontSize: "small" }) })
155
+ }
156
+ }
157
+ }
158
+ ) }),
159
+ /* @__PURE__ */ jsx(Box, { sx: { maxHeight: 240, overflowY: "auto" }, children: isLoading && searchText.trim() ? /* @__PURE__ */ jsx(Box, { sx: { display: "flex", justifyContent: "center", py: 2 }, children: /* @__PURE__ */ jsx(CircularProgress, { size: 24 }) }) : suggestions.length === 0 && searchText.trim() ? /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { py: 2, textAlign: "center" }, children: "No results found" }) : searchText.trim() ? suggestions.map((user) => /* @__PURE__ */ jsxs(
160
+ Box,
161
+ {
162
+ onClick: () => onUserSelect(user),
163
+ sx: {
164
+ display: "flex",
165
+ alignItems: "center",
166
+ gap: 1,
167
+ px: 1.5,
168
+ py: 1,
169
+ cursor: "pointer",
170
+ "&:hover": { bgcolor: "action.hover" }
171
+ },
172
+ children: [
173
+ /* @__PURE__ */ jsx(Avatar, { src: user.photo, alt: user.displayName, sx: { width: 32, height: 32 }, children: user.displayName?.[0] }),
174
+ /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
175
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", noWrap: true, children: user.displayName }),
176
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", noWrap: true, children: user.email })
177
+ ] })
178
+ ]
179
+ },
180
+ user.id || user.email || user.displayName
181
+ )) : /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { py: 2, textAlign: "center" }, children: "Type to search..." }) }),
182
+ selectedUser && /* @__PURE__ */ jsx(Box, { sx: { p: 1.5, pt: 1, borderTop: 1, borderColor: "divider" }, children: /* @__PURE__ */ jsx(Button, { size: "small", fullWidth: true, onClick: onClearUser, children: "Clear Filter" }) })
183
+ ] });
184
+ PeopleFilterPopover.displayName = "PeopleFilterPopover";
185
+ var materialRenderers = {
186
+ renderMultiSelect: (p) => /* @__PURE__ */ jsx(
187
+ MultiSelectFilterPopover,
188
+ {
189
+ searchText: p.searchText,
190
+ onSearchChange: p.onSearchChange,
191
+ options: p.options,
192
+ filteredOptions: p.filteredOptions,
193
+ selected: p.selected,
194
+ onOptionToggle: p.onOptionToggle,
195
+ onSelectAll: p.onSelectAll,
196
+ onClearSelection: p.onClearSelection,
197
+ onApply: p.onApply,
198
+ isLoading: p.isLoading
199
+ }
200
+ ),
201
+ renderText: (p) => /* @__PURE__ */ jsx(
202
+ TextFilterPopover,
203
+ {
204
+ value: p.value,
205
+ onValueChange: p.onValueChange,
206
+ onApply: p.onApply,
207
+ onClear: p.onClear
208
+ }
209
+ ),
210
+ renderPeople: (p) => /* @__PURE__ */ jsx(
211
+ PeopleFilterPopover,
212
+ {
213
+ selectedUser: p.selectedUser,
214
+ searchText: p.searchText,
215
+ onSearchChange: p.onSearchChange,
216
+ suggestions: p.suggestions,
217
+ isLoading: p.isLoading,
218
+ onUserSelect: p.onUserSelect,
219
+ onClearUser: p.onClearUser,
220
+ inputRef: p.inputRef
221
+ }
222
+ ),
223
+ renderDate: (p) => /* @__PURE__ */ jsxs(Box, { sx: { p: 1.5, display: "flex", flexDirection: "column", gap: 1 }, children: [
224
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
225
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { minWidth: 36 }, children: "From:" }),
226
+ /* @__PURE__ */ jsx("input", { type: "date", value: p.tempDateFrom, onChange: (e) => p.setTempDateFrom(e.target.value), style: { flex: 1, padding: "4px 6px" } })
227
+ ] }),
228
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
229
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { minWidth: 36 }, children: "To:" }),
230
+ /* @__PURE__ */ jsx("input", { type: "date", value: p.tempDateTo, onChange: (e) => p.setTempDateTo(e.target.value), style: { flex: 1, padding: "4px 6px" } })
231
+ ] }),
232
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "flex-end", gap: 1, mt: 0.5 }, children: [
233
+ /* @__PURE__ */ jsx("button", { onClick: p.onClear, disabled: !p.tempDateFrom && !p.tempDateTo, style: { padding: "4px 12px", cursor: "pointer" }, children: "Clear" }),
234
+ /* @__PURE__ */ jsx("button", { onClick: p.onApply, style: { padding: "4px 12px", cursor: "pointer" }, children: "Apply" })
235
+ ] })
236
+ ] })
237
+ };
238
+ var ColumnHeaderFilter = React3.memo((props) => {
239
+ const {
240
+ columnName,
241
+ filterType,
242
+ options = [],
243
+ isLoadingOptions = false,
244
+ selectedUser
245
+ } = props;
246
+ const state = useColumnHeaderFilterState(getColumnHeaderFilterStateParams(props));
247
+ const {
248
+ headerRef,
249
+ isFilterOpen,
250
+ setFilterOpen,
251
+ hasActiveFilter,
252
+ popoverPosition,
253
+ handlers
254
+ } = state;
255
+ return /* @__PURE__ */ jsxs(Box, { ref: headerRef, sx: { display: "flex", alignItems: "center", width: "100%", minWidth: 0 }, children: [
256
+ /* @__PURE__ */ jsx(Box, { sx: { flex: 1, minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsx(Tooltip, { title: columnName, arrow: true, children: /* @__PURE__ */ jsx(
257
+ Typography,
258
+ {
259
+ variant: "body2",
260
+ fontWeight: 600,
261
+ noWrap: true,
262
+ "data-header-label": true,
263
+ sx: { lineHeight: 1.4 },
264
+ children: columnName
265
+ }
266
+ ) }) }),
267
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", ml: 0.5, flexShrink: 0 }, children: filterType !== "none" && /* @__PURE__ */ jsxs(
268
+ IconButton,
269
+ {
270
+ size: "small",
271
+ onClick: handlers.handleFilterIconClick,
272
+ "aria-label": `Filter ${columnName}`,
273
+ title: `Filter ${columnName}`,
274
+ color: hasActiveFilter || isFilterOpen ? "primary" : "default",
275
+ sx: { p: 0.25, position: "relative" },
276
+ children: [
277
+ /* @__PURE__ */ jsx(FilterList, { sx: { fontSize: 16 } }),
278
+ hasActiveFilter && /* @__PURE__ */ jsx(
279
+ Box,
280
+ {
281
+ sx: {
282
+ position: "absolute",
283
+ top: 2,
284
+ right: 2,
285
+ width: 6,
286
+ height: 6,
287
+ borderRadius: "50%",
288
+ bgcolor: "primary.main"
289
+ }
290
+ }
291
+ )
292
+ ]
293
+ }
294
+ ) }),
295
+ /* @__PURE__ */ jsxs(
296
+ Popover,
297
+ {
298
+ open: isFilterOpen && filterType !== "none",
299
+ onClose: () => setFilterOpen(false),
300
+ anchorReference: "anchorPosition",
301
+ anchorPosition: popoverPosition ?? { top: 0, left: 0 },
302
+ anchorOrigin: { vertical: "bottom", horizontal: "left" },
303
+ transformOrigin: { vertical: "top", horizontal: "left" },
304
+ slotProps: {
305
+ paper: {
306
+ sx: { mt: 0.5, overflow: "visible" },
307
+ onClick: (e) => e.stopPropagation()
308
+ }
309
+ },
310
+ children: [
311
+ /* @__PURE__ */ jsx(Box, { sx: { borderBottom: 1, borderColor: "divider", px: 1.5, py: 1 }, children: /* @__PURE__ */ jsxs(Typography, { variant: "subtitle2", children: [
312
+ "Filter: ",
313
+ columnName
314
+ ] }) }),
315
+ renderFilterContent(filterType, state, options ?? [], isLoadingOptions, selectedUser, materialRenderers)
316
+ ]
317
+ }
318
+ )
319
+ ] });
320
+ });
321
+ ColumnHeaderFilter.displayName = "ColumnHeaderFilter";
322
+ function ColumnHeaderMenu(props) {
323
+ const {
324
+ isOpen,
325
+ anchorElement,
326
+ onClose,
327
+ onPinLeft,
328
+ onPinRight,
329
+ onUnpin,
330
+ onSortAsc,
331
+ onSortDesc,
332
+ onClearSort,
333
+ onAutosizeThis,
334
+ onAutosizeAll,
335
+ canPinLeft,
336
+ canPinRight,
337
+ canUnpin,
338
+ currentSort,
339
+ isSortable,
340
+ isResizable
341
+ } = props;
342
+ const [anchorPosition, setAnchorPosition] = useState(void 0);
343
+ useEffect(() => {
344
+ if (isOpen && anchorElement) {
345
+ const rect = anchorElement.getBoundingClientRect();
346
+ setAnchorPosition({
347
+ top: rect.bottom + 4,
348
+ left: rect.left
349
+ });
350
+ } else {
351
+ setAnchorPosition(void 0);
352
+ }
353
+ }, [isOpen, anchorElement]);
354
+ const menuInput = useMemo(
355
+ () => ({
356
+ canPinLeft,
357
+ canPinRight,
358
+ canUnpin,
359
+ currentSort,
360
+ isSortable,
361
+ isResizable
362
+ }),
363
+ [canPinLeft, canPinRight, canUnpin, currentSort, isSortable, isResizable]
364
+ );
365
+ const items = useMemo(() => getColumnHeaderMenuItems(menuInput), [menuInput]);
366
+ const handlers = useMemo(
367
+ () => ({
368
+ pinLeft: onPinLeft,
369
+ pinRight: onPinRight,
370
+ unpin: onUnpin,
371
+ sortAsc: onSortAsc,
372
+ sortDesc: onSortDesc,
373
+ clearSort: onClearSort,
374
+ autosizeThis: onAutosizeThis,
375
+ autosizeAll: onAutosizeAll
376
+ }),
377
+ [onPinLeft, onPinRight, onUnpin, onSortAsc, onSortDesc, onClearSort, onAutosizeThis, onAutosizeAll]
378
+ );
379
+ return /* @__PURE__ */ jsx(
380
+ Menu,
381
+ {
382
+ open: isOpen && !!anchorPosition,
383
+ onClose,
384
+ anchorReference: "anchorPosition",
385
+ anchorPosition,
386
+ slotProps: {
387
+ paper: {
388
+ sx: {
389
+ minWidth: 140
390
+ }
391
+ }
392
+ },
393
+ children: items.flatMap((item, idx) => {
394
+ const elements = [
395
+ /* @__PURE__ */ jsx(
396
+ MenuItem,
397
+ {
398
+ disabled: item.disabled,
399
+ onClick: () => {
400
+ handlers[item.id]();
401
+ onClose();
402
+ },
403
+ children: item.label
404
+ },
405
+ item.id
406
+ )
407
+ ];
408
+ if (item.divider && idx < items.length - 1) {
409
+ elements.push(/* @__PURE__ */ jsx(Divider, {}, `${item.id}-divider`));
410
+ }
411
+ return elements;
412
+ })
413
+ }
414
+ );
415
+ }
416
+ function InlineCellEditor(props) {
417
+ return /* @__PURE__ */ jsx(
418
+ BaseInlineCellEditor,
419
+ {
420
+ ...props,
421
+ renderCheckbox: (checked, onCommit, onCancel) => /* @__PURE__ */ jsx(
422
+ Checkbox,
423
+ {
424
+ checked,
425
+ onChange: (_, c) => onCommit(c),
426
+ onKeyDown: (e) => e.key === "Escape" && (e.preventDefault(), onCancel()),
427
+ size: "small"
428
+ }
429
+ )
430
+ }
431
+ );
432
+ }
433
+ var partSx = (isLast) => ({
434
+ display: "inline-flex",
435
+ alignItems: "center",
436
+ gap: 0.5,
437
+ ...isLast ? {} : { mr: 2, "&::after": { content: '"|"', ml: 2, color: "divider" } }
438
+ });
439
+ function StatusBar(props) {
440
+ const parts = getStatusBarParts(props);
441
+ return /* @__PURE__ */ 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) => /* @__PURE__ */ jsxs(Typography, { component: "span", variant: "body2", sx: partSx(i === parts.length - 1), children: [
442
+ /* @__PURE__ */ jsx(Typography, { component: "span", color: "text.secondary", children: p.label }),
443
+ /* @__PURE__ */ jsx(Typography, { component: "span", fontWeight: 600, children: p.value.toLocaleString() })
444
+ ] }, p.key)) });
445
+ }
446
+ function GridContextMenu(props) {
447
+ const { x, y, hasSelection, canUndo, canRedo, onClose } = props;
448
+ const handlers = React3.useMemo(() => getContextMenuHandlers(props), [props]);
449
+ const isDisabled = React3.useCallback(
450
+ (item) => {
451
+ if (item.disabledWhenNoSelection && !hasSelection) return true;
452
+ if (item.id === "undo" && !canUndo) return true;
453
+ if (item.id === "redo" && !canRedo) return true;
454
+ return false;
455
+ },
456
+ [hasSelection, canUndo, canRedo]
457
+ );
458
+ return /* @__PURE__ */ jsx(
459
+ Menu,
460
+ {
461
+ open: true,
462
+ onClose,
463
+ anchorReference: "anchorPosition",
464
+ anchorPosition: { top: y, left: x },
465
+ MenuListProps: { dense: true, "aria-label": "Grid context menu" },
466
+ children: GRID_CONTEXT_MENU_ITEMS.flatMap((item) => {
467
+ const elements = [];
468
+ if (item.dividerBefore) {
469
+ elements.push(/* @__PURE__ */ jsx(Divider, {}, `${item.id}-divider`));
470
+ }
471
+ elements.push(
472
+ /* @__PURE__ */ jsxs(
473
+ MenuItem,
474
+ {
475
+ onClick: handlers[item.id],
476
+ disabled: isDisabled(item),
477
+ children: [
478
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: item.label }),
479
+ item.shortcut && /* @__PURE__ */ jsx("span", { style: { marginLeft: 24, color: "var(--ogrid-fg-muted, rgba(0,0,0,0.4))", fontSize: "0.8em" }, children: formatShortcut(item.shortcut) })
480
+ ]
481
+ },
482
+ item.id
483
+ )
484
+ );
485
+ return elements;
486
+ })
487
+ }
488
+ );
489
+ }
490
+ var EMPTY_STATE_SX = { py: 4, px: 2, textAlign: "center", borderTop: 1, borderColor: "divider", bgcolor: "action.hover" };
491
+ function EmptyState({ emptyState }) {
492
+ return /* @__PURE__ */ jsx(Box, { sx: EMPTY_STATE_SX, children: emptyState.render ? emptyState.render() : /* @__PURE__ */ jsxs(Fragment, { children: [
493
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", gutterBottom: true, children: "No results found" }),
494
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: emptyState.message != null ? emptyState.message : emptyState.hasActiveFilters ? /* @__PURE__ */ jsxs(Fragment, { children: [
495
+ "No items match your current filters. Try adjusting your search or",
496
+ " ",
497
+ /* @__PURE__ */ jsx(Button, { variant: "text", size: "small", onClick: emptyState.onClearAll, children: "clear all filters" }),
498
+ " ",
499
+ "to see all items."
500
+ ] }) : "There are no items available at this time." })
501
+ ] }) });
502
+ }
503
+ var LOADING_OVERLAY_SX = {
504
+ position: "absolute",
505
+ inset: 0,
506
+ zIndex: 2,
507
+ display: "flex",
508
+ alignItems: "center",
509
+ justifyContent: "center",
510
+ background: "var(--ogrid-loading-bg, rgba(255,255,255,0.7))"
511
+ };
512
+ var LOADING_INNER_SX = {
513
+ display: "flex",
514
+ flexDirection: "column",
515
+ alignItems: "center",
516
+ gap: 1,
517
+ p: 2,
518
+ bgcolor: "background.paper",
519
+ border: 1,
520
+ borderColor: "divider",
521
+ borderRadius: 1
522
+ };
523
+ function LoadingOverlay({ message }) {
524
+ return /* @__PURE__ */ jsx(Box, { sx: LOADING_OVERLAY_SX, children: /* @__PURE__ */ jsxs(Box, { sx: LOADING_INNER_SX, children: [
525
+ /* @__PURE__ */ jsx(CircularProgress, { size: 24 }),
526
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: message })
527
+ ] }) });
528
+ }
529
+ function DropIndicator({ dropIndicatorX, wrapperLeft }) {
530
+ return /* @__PURE__ */ jsx(
531
+ Box,
532
+ {
533
+ sx: {
534
+ position: "absolute",
535
+ top: 0,
536
+ bottom: 0,
537
+ width: 3,
538
+ bgcolor: "var(--ogrid-primary, #217346)",
539
+ pointerEvents: "none",
540
+ zIndex: 100,
541
+ transition: "left 0.05s",
542
+ left: dropIndicatorX - wrapperLeft
543
+ }
544
+ }
545
+ );
546
+ }
547
+ var gridRootSx = { position: "relative", flex: 1, minHeight: 0, display: "flex", flexDirection: "column" };
548
+ var EDITING_CELL_STYLE = {
549
+ width: "100%",
550
+ height: "100%",
551
+ display: "flex",
552
+ alignItems: "center",
553
+ boxSizing: "border-box",
554
+ outline: "2px solid var(--ogrid-selection-color, #217346)",
555
+ outlineOffset: "-1px",
556
+ zIndex: 2,
557
+ position: "relative",
558
+ background: "var(--ogrid-bg, #fff)",
559
+ overflow: "visible",
560
+ padding: 0
561
+ };
562
+ var CHECKBOX_CELL_SX = { width: CHECKBOX_COLUMN_WIDTH, minWidth: CHECKBOX_COLUMN_WIDTH, maxWidth: CHECKBOX_COLUMN_WIDTH, textAlign: "center" };
563
+ var CHECKBOX_PLACEHOLDER_SX = { width: CHECKBOX_COLUMN_WIDTH, minWidth: CHECKBOX_COLUMN_WIDTH, p: 0 };
564
+ var CHECKBOX_TD_STYLE = {
565
+ width: CHECKBOX_COLUMN_WIDTH,
566
+ minWidth: CHECKBOX_COLUMN_WIDTH,
567
+ maxWidth: CHECKBOX_COLUMN_WIDTH,
568
+ textAlign: "center",
569
+ padding: 0,
570
+ position: "relative",
571
+ height: "1px",
572
+ borderBottom: "1px solid var(--ogrid-border, rgba(224,224,224,1))"
573
+ };
574
+ var HEADER_BG = "var(--ogrid-header-bg, #f5f5f5)";
575
+ var STICKY_HEADER_SX = {
576
+ /* Removed position: 'sticky', top: 0 - breaks horizontal sticky on pinned columns.
577
+ Instead, apply sticky to individual header cells (HEADER_BASE_SX). */
578
+ zIndex: 8,
579
+ bgcolor: HEADER_BG,
580
+ "& th": { bgcolor: HEADER_BG }
581
+ };
582
+ var HEADER_ROW_SX = { bgcolor: HEADER_BG };
583
+ var GROUP_HEADER_CELL_SX = { textAlign: "center", fontWeight: 600, borderBottom: 2, borderColor: "divider", py: 0.75 };
584
+ function getDensityPadding(density) {
585
+ switch (density) {
586
+ case "compact":
587
+ return { px: "8px", py: "4px" };
588
+ case "comfortable":
589
+ return { px: "16px", py: "12px" };
590
+ default:
591
+ return { px: "10px", py: "6px" };
592
+ }
593
+ }
594
+ var DENSITY_CELL_STYLES = {
595
+ compact: { padding: "4px 8px" },
596
+ normal: { padding: "6px 10px" },
597
+ comfortable: { padding: "12px 16px" }
598
+ };
599
+ var HEADER_BASE_SX = {
600
+ fontWeight: 600,
601
+ position: "sticky",
602
+ /* Enables vertical sticky for all headers */
603
+ top: 0,
604
+ /* Sticky vertically */
605
+ zIndex: 8,
606
+ /* Stack above body cells */
607
+ bgcolor: HEADER_BG
608
+ /* Opaque — required for sticky overlap */
609
+ };
610
+ var HEADER_PINNED_LEFT_SX = {
611
+ ...HEADER_BASE_SX,
612
+ position: "sticky",
613
+ left: 0,
614
+ top: 0,
615
+ zIndex: 10,
616
+ bgcolor: HEADER_BG,
617
+ willChange: "transform",
618
+ borderRight: "1px solid",
619
+ borderRightColor: "divider",
620
+ boxShadow: "2px 0 4px -1px rgba(0,0,0,0.1)"
621
+ };
622
+ var HEADER_PINNED_RIGHT_SX = {
623
+ ...HEADER_BASE_SX,
624
+ position: "sticky",
625
+ right: 0,
626
+ top: 0,
627
+ zIndex: 10,
628
+ bgcolor: HEADER_BG,
629
+ willChange: "transform",
630
+ borderLeft: "1px solid",
631
+ borderLeftColor: "divider",
632
+ boxShadow: "-2px 0 4px -1px rgba(0,0,0,0.1)"
633
+ };
634
+ var HEADER_BASE_NO_STICKY_SX = {
635
+ fontWeight: 600,
636
+ zIndex: 8,
637
+ bgcolor: HEADER_BG
638
+ };
639
+ var HEADER_PINNED_LEFT_NO_STICKY_SX = {
640
+ ...HEADER_BASE_NO_STICKY_SX,
641
+ position: "sticky",
642
+ left: 0,
643
+ zIndex: 10,
644
+ bgcolor: HEADER_BG,
645
+ willChange: "transform",
646
+ borderRight: "1px solid",
647
+ borderRightColor: "divider",
648
+ boxShadow: "2px 0 4px -1px rgba(0,0,0,0.1)"
649
+ };
650
+ var HEADER_PINNED_RIGHT_NO_STICKY_SX = {
651
+ ...HEADER_BASE_NO_STICKY_SX,
652
+ position: "sticky",
653
+ right: 0,
654
+ zIndex: 10,
655
+ bgcolor: HEADER_BG,
656
+ willChange: "transform",
657
+ borderLeft: "1px solid",
658
+ borderLeftColor: "divider",
659
+ boxShadow: "-2px 0 4px -1px rgba(0,0,0,0.1)"
660
+ };
661
+ var RESIZE_HANDLE_SX = {
662
+ position: "absolute",
663
+ top: 0,
664
+ right: "-3px",
665
+ bottom: 0,
666
+ width: "8px",
667
+ cursor: "col-resize",
668
+ userSelect: "none",
669
+ "&::after": { content: '""', position: "absolute", top: 0, right: "3px", bottom: 0, width: "2px" },
670
+ "&:hover::after": { bgcolor: "primary.main" },
671
+ "&:active::after": { bgcolor: "primary.dark" }
672
+ };
673
+ var POPOVER_ANCHOR_SX = { minHeight: "100%", minWidth: 40 };
674
+ var POPOVER_CONTENT_SX = { p: 1 };
675
+ var WRAPPER_SCROLL_SX = { display: "flex", flexDirection: "column", minHeight: "100%" };
676
+ var HEADER_CONTENT_FLEX_SX = { display: "flex", alignItems: "center", gap: 0.5 };
677
+ var COLUMN_OPTIONS_BUTTON_SX = {
678
+ background: "transparent",
679
+ border: "none",
680
+ cursor: "pointer",
681
+ padding: "2px 4px",
682
+ fontSize: "16px",
683
+ lineHeight: 1,
684
+ color: "text.secondary",
685
+ opacity: 1,
686
+ transition: "background-color 0.15s",
687
+ borderRadius: "3px",
688
+ display: "flex",
689
+ alignItems: "center",
690
+ justifyContent: "center",
691
+ minWidth: "20px",
692
+ height: "20px",
693
+ "&:hover": {
694
+ bgcolor: "action.hover"
695
+ }
696
+ };
697
+ var TABLE_WRAPPER_SX = { position: "relative", opacity: 1 };
698
+ var TABLE_WRAPPER_LOADING_SX = { position: "relative", opacity: 0.6 };
699
+ function GridRowInner(props) {
700
+ const {
701
+ item,
702
+ rowIndex,
703
+ rowId,
704
+ isSelected,
705
+ columnLayouts,
706
+ renderCellContent,
707
+ handleSingleRowClick,
708
+ handleRowCheckboxChange,
709
+ lastMouseShiftRef,
710
+ hasCheckboxCol,
711
+ hasRowNumbersCol,
712
+ rowNumberOffset,
713
+ rowHeight
714
+ } = props;
715
+ return /* @__PURE__ */ jsxs(
716
+ "tr",
717
+ {
718
+ "data-row-id": rowId,
719
+ onClick: handleSingleRowClick,
720
+ "aria-selected": isSelected || void 0,
721
+ className: `ogrid-mat-row${isSelected ? " ogrid-mat-row--selected" : ""}`,
722
+ style: rowHeight ? { height: rowHeight } : void 0,
723
+ children: [
724
+ hasCheckboxCol && /* @__PURE__ */ jsx("td", { style: CHECKBOX_TD_STYLE, children: /* @__PURE__ */ jsx(
725
+ "div",
726
+ {
727
+ "data-row-index": rowIndex,
728
+ "data-col-index": 0,
729
+ onClick: STOP_PROPAGATION,
730
+ className: "ogrid-mat-checkbox-wrapper",
731
+ children: /* @__PURE__ */ jsx(
732
+ Checkbox,
733
+ {
734
+ checked: isSelected,
735
+ onChange: (_, checked) => handleRowCheckboxChange(rowId, checked, rowIndex, lastMouseShiftRef.current),
736
+ size: "small",
737
+ "aria-label": `Select row ${rowIndex + 1}`
738
+ }
739
+ )
740
+ }
741
+ ) }),
742
+ hasRowNumbersCol && /* @__PURE__ */ jsx(
743
+ "td",
744
+ {
745
+ className: "ogrid-mat-td ogrid-mat-row-number",
746
+ style: {
747
+ width: ROW_NUMBER_COLUMN_WIDTH,
748
+ minWidth: ROW_NUMBER_COLUMN_WIDTH,
749
+ maxWidth: ROW_NUMBER_COLUMN_WIDTH,
750
+ left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
751
+ borderBottom: "1px solid var(--ogrid-border, rgba(224,224,224,1))"
752
+ },
753
+ children: rowNumberOffset + rowIndex + 1
754
+ }
755
+ ),
756
+ columnLayouts.map((cl, colIdx) => /* @__PURE__ */ jsx(
757
+ "td",
758
+ {
759
+ "data-column-id": cl.col.columnId,
760
+ className: cl.tdClassName,
761
+ style: { ...cl.tdStyle, minWidth: cl.minWidth, width: cl.width, maxWidth: cl.maxWidth },
762
+ children: renderCellContent(item, cl.col, rowIndex, colIdx)
763
+ },
764
+ cl.col.columnId
765
+ ))
766
+ ]
767
+ }
768
+ );
769
+ }
770
+ var GridRow = React3.memo(GridRowInner, areGridRowPropsEqual);
771
+ function DataGridTableInner(props) {
772
+ const o = useDataGridTableOrchestration({ props });
773
+ const {
774
+ wrapperRef,
775
+ tableContainerRef,
776
+ lastMouseShiftRef,
777
+ interaction,
778
+ pinning,
779
+ handleResizeStart,
780
+ getColumnWidth,
781
+ isReorderDragging,
782
+ dropIndicatorX,
783
+ handleHeaderMouseDown,
784
+ virtualScrollEnabled,
785
+ visibleRange,
786
+ items,
787
+ getRowId,
788
+ emptyState,
789
+ suppressHorizontalScroll,
790
+ isLoading,
791
+ loadingMessage,
792
+ ariaLabel,
793
+ ariaLabelledBy,
794
+ columnReorder,
795
+ density,
796
+ rowHeight,
797
+ rowNumberOffset,
798
+ headerRows,
799
+ allowOverflowX,
800
+ fitToContent,
801
+ editCallbacks,
802
+ interactionHandlers,
803
+ cellDescriptorInputRef,
804
+ pendingEditorValueRef,
805
+ popoverAnchorElRef,
806
+ handleSingleRowClick,
807
+ handlePasteVoid,
808
+ visibleCols,
809
+ hasCheckboxCol,
810
+ hasRowNumbersCol,
811
+ colOffset,
812
+ minTableWidth,
813
+ columnSizingOverrides,
814
+ measuredColumnWidths,
815
+ selectedRowIds,
816
+ handleRowCheckboxChange,
817
+ handleSelectAll,
818
+ allSelected,
819
+ someSelected,
820
+ editingCell,
821
+ setPopoverAnchorEl,
822
+ cancelPopoverEdit,
823
+ setActiveCell,
824
+ selectionRange,
825
+ hasCellSelection,
826
+ handleGridKeyDown,
827
+ handleFillHandleMouseDown,
828
+ handleCopy,
829
+ handleCut,
830
+ cutRange,
831
+ copyRange,
832
+ canUndo,
833
+ canRedo,
834
+ onUndo,
835
+ onRedo,
836
+ isDragging,
837
+ menuPosition,
838
+ closeContextMenu,
839
+ headerFilterInput,
840
+ statusBarConfig,
841
+ showEmptyInGrid,
842
+ onCellError,
843
+ headerMenu
844
+ } = o;
845
+ const densityPadding = useMemo(() => getDensityPadding(density), [density]);
846
+ const headerCellSx = useMemo(() => ({ px: densityPadding.px, py: densityPadding.py }), [densityPadding]);
847
+ const columnMeta = useColumnMeta({
848
+ visibleCols,
849
+ getColumnWidth,
850
+ columnSizingOverrides,
851
+ measuredColumnWidths,
852
+ pinnedColumns: pinning.pinnedColumns,
853
+ leftOffsets: pinning.leftOffsets,
854
+ rightOffsets: pinning.rightOffsets,
855
+ pinnedColLeftClass: "",
856
+ pinnedColRightClass: ""
857
+ });
858
+ const columnLayouts = useMemo(
859
+ () => visibleCols.map((col) => {
860
+ const isPinnedLeft = pinning.pinnedColumns[col.columnId] === "left";
861
+ const isPinnedRight = pinning.pinnedColumns[col.columnId] === "right";
862
+ let tdClassName = "ogrid-mat-td";
863
+ const tdStyle = {};
864
+ if (isPinnedLeft) {
865
+ tdClassName += " ogrid-mat-td--pinned-left";
866
+ if (pinning.leftOffsets[col.columnId] != null) tdStyle.left = pinning.leftOffsets[col.columnId];
867
+ } else if (isPinnedRight) {
868
+ tdClassName += " ogrid-mat-td--pinned-right";
869
+ if (pinning.rightOffsets[col.columnId] != null) tdStyle.right = pinning.rightOffsets[col.columnId];
870
+ }
871
+ const cellMeta = columnMeta.cellStyles[col.columnId];
872
+ return {
873
+ col,
874
+ tdClassName,
875
+ tdStyle,
876
+ minWidth: cellMeta?.minWidth ?? 0,
877
+ width: cellMeta?.width ?? getColumnWidth(col),
878
+ maxWidth: cellMeta?.maxWidth ?? getColumnWidth(col)
879
+ };
880
+ }),
881
+ [visibleCols, columnMeta, pinning.pinnedColumns, pinning.leftOffsets, pinning.rightOffsets, getColumnWidth]
882
+ );
883
+ const wrapperSx = useMemo(() => ({
884
+ position: "relative",
885
+ flex: 1,
886
+ minHeight: isLoading && items.length === 0 ? 200 : 0,
887
+ width: fitToContent ? "fit-content" : "100%",
888
+ maxWidth: "100%",
889
+ overflowX: suppressHorizontalScroll ? "hidden" : allowOverflowX ? "auto" : "hidden",
890
+ overflowY: "auto",
891
+ bgcolor: "background.paper",
892
+ willChange: "scroll-position"
893
+ }), [fitToContent, suppressHorizontalScroll, allowOverflowX, isLoading, items.length]);
894
+ const cellDensityStyle = DENSITY_CELL_STYLES[density] ?? DENSITY_CELL_STYLES.normal;
895
+ const renderCellContent = useCallback(
896
+ (item, col, rowIndex, colIdx) => {
897
+ const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInputRef.current);
898
+ const rowId = getRowId(item);
899
+ let cellContent;
900
+ if (descriptor.mode === "editing-inline") {
901
+ cellContent = /* @__PURE__ */ jsx("div", { style: EDITING_CELL_STYLE, children: /* @__PURE__ */ jsx(InlineCellEditor, { ...buildInlineEditorProps(item, col, descriptor, editCallbacks) }) });
902
+ } else if (descriptor.mode === "editing-popover" && typeof col.cellEditor === "function") {
903
+ const editorProps = buildPopoverEditorProps(item, col, descriptor, pendingEditorValueRef.current, editCallbacks);
904
+ const CustomEditor = col.cellEditor;
905
+ cellContent = /* @__PURE__ */ jsxs(Fragment, { children: [
906
+ /* @__PURE__ */ jsx(Box, { ref: (el) => {
907
+ if (el) setPopoverAnchorEl(el);
908
+ }, sx: POPOVER_ANCHOR_SX, "aria-hidden": true }),
909
+ /* @__PURE__ */ jsx(
910
+ Popover,
911
+ {
912
+ open: !!popoverAnchorElRef.current,
913
+ anchorEl: popoverAnchorElRef.current,
914
+ onClose: cancelPopoverEdit,
915
+ anchorOrigin: { vertical: "bottom", horizontal: "left" },
916
+ transformOrigin: { vertical: "top", horizontal: "left" },
917
+ children: /* @__PURE__ */ jsx(Box, { sx: POPOVER_CONTENT_SX, children: /* @__PURE__ */ jsx(CustomEditor, { ...editorProps }) })
918
+ }
919
+ )
920
+ ] });
921
+ } else {
922
+ const content = resolveCellDisplayContent(col, item, descriptor.displayValue);
923
+ const cellStyle = resolveCellStyle(col, item);
924
+ const styledContent = cellStyle ? /* @__PURE__ */ jsx("span", { style: cellStyle, children: content }) : content;
925
+ let cls = "ogrid-mat-cell";
926
+ if (col.type === "numeric") cls += " ogrid-mat-cell--numeric";
927
+ else if (col.type === "boolean") cls += " ogrid-mat-cell--boolean";
928
+ if (descriptor.canEditAny) cls += " ogrid-mat-cell--editable";
929
+ if (descriptor.isActive && !descriptor.isInRange) cls += " ogrid-mat-cell--active";
930
+ if (descriptor.isInRange) cls += " ogrid-mat-cell--range";
931
+ if (descriptor.isInCutRange) cls += " ogrid-mat-cell--cut";
932
+ const interactionProps = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
933
+ cellContent = /* @__PURE__ */ jsxs(
934
+ "div",
935
+ {
936
+ className: cls,
937
+ ...interactionProps,
938
+ style: cellDensityStyle,
939
+ children: [
940
+ styledContent,
941
+ descriptor.canEditAny && descriptor.isSelectionEndCell && /* @__PURE__ */ jsx("div", { className: "ogrid-mat-fill-handle", onMouseDown: handleFillHandleMouseDown, "aria-label": "Fill handle" })
942
+ ]
943
+ }
944
+ );
945
+ }
946
+ return /* @__PURE__ */ jsx(CellErrorBoundary, { onError: onCellError, children: cellContent }, `${rowId}-${col.columnId}`);
947
+ },
948
+ [editCallbacks, interactionHandlers, handleFillHandleMouseDown, setPopoverAnchorEl, cancelPopoverEdit, getRowId, onCellError, cellDescriptorInputRef, cellDensityStyle, pendingEditorValueRef, popoverAnchorElRef]
949
+ );
950
+ return /* @__PURE__ */ jsxs(Box, { sx: gridRootSx, children: [
951
+ /* @__PURE__ */ jsxs(
952
+ Box,
953
+ {
954
+ ref: wrapperRef,
955
+ tabIndex: 0,
956
+ role: "region",
957
+ "aria-label": ariaLabel ?? (ariaLabelledBy ? void 0 : "Data grid"),
958
+ "aria-labelledby": ariaLabelledBy,
959
+ onMouseDown: (e) => {
960
+ lastMouseShiftRef.current = e.shiftKey;
961
+ },
962
+ onKeyDown: handleGridKeyDown,
963
+ onContextMenu: PREVENT_DEFAULT,
964
+ "data-overflow-x": allowOverflowX ? "true" : "false",
965
+ "data-density": density,
966
+ className: "ogrid-mat-wrapper",
967
+ sx: wrapperSx,
968
+ children: [
969
+ /* @__PURE__ */ jsx(Box, { sx: WRAPPER_SCROLL_SX, children: /* @__PURE__ */ jsx("div", { style: { minWidth: allowOverflowX ? minTableWidth : void 0 }, children: /* @__PURE__ */ jsxs(Box, { ref: tableContainerRef, sx: isLoading && items.length > 0 ? TABLE_WRAPPER_LOADING_SX : TABLE_WRAPPER_SX, children: [
970
+ /* @__PURE__ */ jsxs(
971
+ Table,
972
+ {
973
+ size: "small",
974
+ sx: { minWidth: minTableWidth, borderCollapse: "separate", borderSpacing: 0 },
975
+ children: [
976
+ /* @__PURE__ */ jsx(TableHead, { sx: STICKY_HEADER_SX, children: headerRows.map((row, rowIdx) => /* @__PURE__ */ jsxs(TableRow, { sx: HEADER_ROW_SX, children: [
977
+ rowIdx === headerRows.length - 1 && hasCheckboxCol && /* @__PURE__ */ jsx(
978
+ TableCell,
979
+ {
980
+ ...{ padding: "checkbox", rowSpan: headerRows.length > 1 ? 1 : void 0, sx: CHECKBOX_CELL_SX },
981
+ children: /* @__PURE__ */ jsx(
982
+ Checkbox,
983
+ {
984
+ checked: allSelected,
985
+ indeterminate: someSelected,
986
+ onChange: (_, c) => handleSelectAll(!!c),
987
+ size: "small",
988
+ "aria-label": "Select all rows"
989
+ }
990
+ )
991
+ }
992
+ ),
993
+ rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol && /* @__PURE__ */ jsx(TableCell, { ...{ rowSpan: headerRows.length - 1, sx: CHECKBOX_PLACEHOLDER_SX } }),
994
+ rowIdx === headerRows.length - 1 && hasRowNumbersCol && /* @__PURE__ */ jsx(
995
+ TableCell,
996
+ {
997
+ ...{
998
+ component: "th",
999
+ scope: "col",
1000
+ rowSpan: headerRows.length > 1 ? 1 : void 0,
1001
+ sx: {
1002
+ width: ROW_NUMBER_COLUMN_WIDTH,
1003
+ minWidth: ROW_NUMBER_COLUMN_WIDTH,
1004
+ maxWidth: ROW_NUMBER_COLUMN_WIDTH,
1005
+ textAlign: "center",
1006
+ fontWeight: 600,
1007
+ backgroundColor: HEADER_BG,
1008
+ position: "sticky",
1009
+ left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
1010
+ zIndex: 4,
1011
+ ...headerCellSx
1012
+ }
1013
+ },
1014
+ children: "#"
1015
+ }
1016
+ ),
1017
+ rowIdx === 0 && rowIdx < headerRows.length - 1 && hasRowNumbersCol && /* @__PURE__ */ jsx(
1018
+ TableCell,
1019
+ {
1020
+ ...{
1021
+ rowSpan: headerRows.length - 1,
1022
+ sx: {
1023
+ width: ROW_NUMBER_COLUMN_WIDTH,
1024
+ minWidth: ROW_NUMBER_COLUMN_WIDTH,
1025
+ position: "sticky",
1026
+ left: hasCheckboxCol ? CHECKBOX_COLUMN_WIDTH : 0,
1027
+ zIndex: 4,
1028
+ backgroundColor: "background.paper"
1029
+ }
1030
+ }
1031
+ }
1032
+ ),
1033
+ row.map((cell, cellIdx) => {
1034
+ if (cell.isGroup) {
1035
+ return /* @__PURE__ */ jsx(
1036
+ TableCell,
1037
+ {
1038
+ ...{
1039
+ colSpan: cell.colSpan,
1040
+ component: "th",
1041
+ scope: "colgroup",
1042
+ sx: GROUP_HEADER_CELL_SX
1043
+ },
1044
+ children: cell.label
1045
+ },
1046
+ cellIdx
1047
+ );
1048
+ }
1049
+ if (!cell.columnDef) return null;
1050
+ const col = cell.columnDef;
1051
+ const isPinnedLeft = pinning.pinnedColumns[col.columnId] === "left";
1052
+ const isPinnedRight = pinning.pinnedColumns[col.columnId] === "right";
1053
+ const baseHeaderSx = o.stickyHeader ? isPinnedLeft ? HEADER_PINNED_LEFT_SX : isPinnedRight ? HEADER_PINNED_RIGHT_SX : HEADER_BASE_SX : isPinnedLeft ? HEADER_PINNED_LEFT_NO_STICKY_SX : isPinnedRight ? HEADER_PINNED_RIGHT_NO_STICKY_SX : HEADER_BASE_NO_STICKY_SX;
1054
+ const headerSx = isPinnedLeft && pinning.leftOffsets[col.columnId] != null ? { ...baseHeaderSx, left: pinning.leftOffsets[col.columnId] } : isPinnedRight && pinning.rightOffsets[col.columnId] != null ? { ...baseHeaderSx, right: pinning.rightOffsets[col.columnId] } : baseHeaderSx;
1055
+ const hdrStyle = columnMeta.hdrStyles[col.columnId];
1056
+ const isSorted = props.sortBy === col.columnId;
1057
+ const ariaSort = isSorted ? props.sortDirection === "asc" ? "ascending" : "descending" : void 0;
1058
+ return /* @__PURE__ */ jsxs(
1059
+ TableCell,
1060
+ {
1061
+ ...{
1062
+ component: "th",
1063
+ scope: "col",
1064
+ "data-column-id": col.columnId,
1065
+ rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : void 0,
1066
+ "aria-sort": ariaSort,
1067
+ sx: {
1068
+ ...headerSx,
1069
+ ...headerCellSx,
1070
+ minWidth: hdrStyle?.minWidth,
1071
+ width: hdrStyle?.width,
1072
+ maxWidth: hdrStyle?.maxWidth,
1073
+ ...columnReorder ? { cursor: isReorderDragging ? "grabbing" : "grab" } : {},
1074
+ "&:focus-visible": {
1075
+ outline: "2px solid",
1076
+ outlineColor: "primary.main",
1077
+ outlineOffset: "-2px",
1078
+ zIndex: 11
1079
+ }
1080
+ },
1081
+ onMouseDown: columnReorder ? (e) => handleHeaderMouseDown(col.columnId, e) : void 0
1082
+ },
1083
+ children: [
1084
+ /* @__PURE__ */ jsxs(Box, { sx: HEADER_CONTENT_FLEX_SX, children: [
1085
+ /* @__PURE__ */ jsx(ColumnHeaderFilter, { ...getHeaderFilterConfig(col, headerFilterInput) }),
1086
+ /* @__PURE__ */ jsx(
1087
+ Box,
1088
+ {
1089
+ component: "button",
1090
+ onClick: (e) => {
1091
+ e.stopPropagation();
1092
+ headerMenu.open(col.columnId, e.currentTarget);
1093
+ },
1094
+ "aria-label": "Column options",
1095
+ title: "Column options",
1096
+ sx: COLUMN_OPTIONS_BUTTON_SX,
1097
+ children: "\u22EE"
1098
+ }
1099
+ )
1100
+ ] }),
1101
+ /* @__PURE__ */ jsx(Box, { onMouseDown: (e) => {
1102
+ setActiveCell(null);
1103
+ interaction.setSelectionRange(null);
1104
+ wrapperRef.current?.focus({ preventScroll: true });
1105
+ handleResizeStart(e, col);
1106
+ }, sx: RESIZE_HANDLE_SX })
1107
+ ]
1108
+ },
1109
+ col.columnId
1110
+ );
1111
+ })
1112
+ ] }, rowIdx)) }),
1113
+ !showEmptyInGrid && /* @__PURE__ */ jsxs("tbody", { className: "ogrid-mat-tbody", children: [
1114
+ virtualScrollEnabled && visibleRange.offsetTop > 0 && /* @__PURE__ */ jsx("tr", { style: { height: visibleRange.offsetTop }, "aria-hidden": true }),
1115
+ virtualScrollEnabled ? items.slice(visibleRange.startIndex, visibleRange.endIndex + 1).map((item, i) => {
1116
+ const rowIndex = visibleRange.startIndex + i;
1117
+ const rowIdStr = getRowId(item);
1118
+ return /* @__PURE__ */ jsx(
1119
+ GridRow,
1120
+ {
1121
+ item,
1122
+ rowIndex,
1123
+ rowId: rowIdStr,
1124
+ isSelected: selectedRowIds.has(rowIdStr),
1125
+ columnLayouts,
1126
+ renderCellContent,
1127
+ handleSingleRowClick,
1128
+ handleRowCheckboxChange,
1129
+ lastMouseShiftRef,
1130
+ hasCheckboxCol,
1131
+ hasRowNumbersCol,
1132
+ rowNumberOffset,
1133
+ selectionRange,
1134
+ activeCell: interaction.activeCell,
1135
+ cutRange,
1136
+ copyRange,
1137
+ isDragging,
1138
+ rowHeight,
1139
+ editingRowId: editingCell?.rowId ?? null
1140
+ },
1141
+ rowIdStr
1142
+ );
1143
+ }) : items.map((item, rowIndex) => {
1144
+ const rowIdStr = getRowId(item);
1145
+ return /* @__PURE__ */ jsx(
1146
+ GridRow,
1147
+ {
1148
+ item,
1149
+ rowIndex,
1150
+ rowId: rowIdStr,
1151
+ isSelected: selectedRowIds.has(rowIdStr),
1152
+ columnLayouts,
1153
+ renderCellContent,
1154
+ handleSingleRowClick,
1155
+ handleRowCheckboxChange,
1156
+ lastMouseShiftRef,
1157
+ hasCheckboxCol,
1158
+ hasRowNumbersCol,
1159
+ rowNumberOffset,
1160
+ selectionRange,
1161
+ activeCell: interaction.activeCell,
1162
+ cutRange,
1163
+ copyRange,
1164
+ isDragging,
1165
+ rowHeight,
1166
+ editingRowId: editingCell?.rowId ?? null
1167
+ },
1168
+ rowIdStr
1169
+ );
1170
+ }),
1171
+ virtualScrollEnabled && visibleRange.offsetBottom > 0 && /* @__PURE__ */ jsx("tr", { style: { height: visibleRange.offsetBottom }, "aria-hidden": true })
1172
+ ] })
1173
+ ]
1174
+ }
1175
+ ),
1176
+ isReorderDragging && dropIndicatorX != null && /* @__PURE__ */ jsx(DropIndicator, { dropIndicatorX, wrapperLeft: wrapperRef.current?.getBoundingClientRect().left ?? 0 }),
1177
+ /* @__PURE__ */ jsx(
1178
+ MarchingAntsOverlay,
1179
+ {
1180
+ containerRef: tableContainerRef,
1181
+ selectionRange,
1182
+ copyRange,
1183
+ cutRange,
1184
+ colOffset,
1185
+ items,
1186
+ visibleColumns: props.visibleColumns,
1187
+ columnSizingOverrides,
1188
+ columnOrder: props.columnOrder
1189
+ }
1190
+ ),
1191
+ showEmptyInGrid && emptyState && /* @__PURE__ */ jsx(EmptyState, { emptyState })
1192
+ ] }) }) }),
1193
+ menuPosition && createPortal(
1194
+ /* @__PURE__ */ jsx(
1195
+ GridContextMenu,
1196
+ {
1197
+ x: menuPosition.x,
1198
+ y: menuPosition.y,
1199
+ hasSelection: hasCellSelection,
1200
+ canUndo,
1201
+ canRedo,
1202
+ onUndo: onUndo ?? NOOP,
1203
+ onRedo: onRedo ?? NOOP,
1204
+ onCopy: handleCopy,
1205
+ onCut: handleCut,
1206
+ onPaste: handlePasteVoid,
1207
+ onSelectAll: o.interaction.handleSelectAllCells,
1208
+ onClose: closeContextMenu
1209
+ }
1210
+ ),
1211
+ document.body
1212
+ ),
1213
+ /* @__PURE__ */ jsx(
1214
+ ColumnHeaderMenu,
1215
+ {
1216
+ isOpen: headerMenu.isOpen,
1217
+ anchorElement: headerMenu.anchorElement,
1218
+ onClose: headerMenu.close,
1219
+ onPinLeft: headerMenu.handlePinLeft,
1220
+ onPinRight: headerMenu.handlePinRight,
1221
+ onUnpin: headerMenu.handleUnpin,
1222
+ onSortAsc: headerMenu.handleSortAsc,
1223
+ onSortDesc: headerMenu.handleSortDesc,
1224
+ onClearSort: headerMenu.handleClearSort,
1225
+ onAutosizeThis: headerMenu.handleAutosizeThis,
1226
+ onAutosizeAll: headerMenu.handleAutosizeAll,
1227
+ canPinLeft: headerMenu.canPinLeft,
1228
+ canPinRight: headerMenu.canPinRight,
1229
+ canUnpin: headerMenu.canUnpin,
1230
+ currentSort: headerMenu.currentSort,
1231
+ isSortable: headerMenu.isSortable,
1232
+ isResizable: headerMenu.isResizable
1233
+ }
1234
+ )
1235
+ ]
1236
+ }
1237
+ ),
1238
+ statusBarConfig && /* @__PURE__ */ jsx(
1239
+ StatusBar,
1240
+ {
1241
+ totalCount: statusBarConfig.totalCount,
1242
+ filteredCount: statusBarConfig.filteredCount,
1243
+ selectedCount: statusBarConfig.selectedCount ?? selectedRowIds.size,
1244
+ selectedCellCount: selectionRange ? (Math.abs(selectionRange.endRow - selectionRange.startRow) + 1) * (Math.abs(selectionRange.endCol - selectionRange.startCol) + 1) : void 0,
1245
+ aggregation: statusBarConfig.aggregation,
1246
+ suppressRowCount: statusBarConfig.suppressRowCount
1247
+ }
1248
+ ),
1249
+ isLoading && /* @__PURE__ */ jsx(LoadingOverlay, { message: loadingMessage })
1250
+ ] });
1251
+ }
1252
+ var DataGridTable = React3.memo(DataGridTableInner);
1253
+ var ColumnChooser = (props) => {
1254
+ const { columns, visibleColumns, onVisibilityChange, onSetVisibleColumns, className } = props;
1255
+ const [anchorEl, setAnchorEl] = useState(null);
1256
+ const buttonRef = useRef(null);
1257
+ const {
1258
+ open: isOpen,
1259
+ setOpen,
1260
+ handleClose,
1261
+ handleCheckboxChange: setColumnVisible,
1262
+ handleSelectAll,
1263
+ handleClearAll,
1264
+ visibleCount,
1265
+ totalCount
1266
+ } = useColumnChooserState({ columns, visibleColumns, onVisibilityChange, onSetVisibleColumns });
1267
+ const handleToggle = (e) => {
1268
+ if (isOpen) {
1269
+ handleClose();
1270
+ setAnchorEl(null);
1271
+ } else {
1272
+ setAnchorEl(e.currentTarget);
1273
+ setOpen(true);
1274
+ }
1275
+ };
1276
+ const handlePopoverClose = () => {
1277
+ handleClose();
1278
+ setAnchorEl(null);
1279
+ };
1280
+ const handleCheckboxChange = (columnKey) => (ev) => {
1281
+ ev.stopPropagation();
1282
+ setColumnVisible(columnKey)(ev.target.checked);
1283
+ };
1284
+ return /* @__PURE__ */ jsxs(Box, { className, sx: { display: "inline-flex" }, children: [
1285
+ /* @__PURE__ */ jsxs(
1286
+ Button,
1287
+ {
1288
+ ref: buttonRef,
1289
+ variant: "outlined",
1290
+ size: "small",
1291
+ color: "inherit",
1292
+ startIcon: /* @__PURE__ */ jsx(ViewColumn, {}),
1293
+ endIcon: isOpen ? /* @__PURE__ */ jsx(ExpandLess, {}) : /* @__PURE__ */ jsx(ExpandMore, {}),
1294
+ onClick: handleToggle,
1295
+ "aria-expanded": isOpen,
1296
+ "aria-haspopup": "listbox",
1297
+ sx: {
1298
+ textTransform: "none",
1299
+ fontWeight: 600,
1300
+ borderColor: isOpen ? "primary.main" : "divider"
1301
+ },
1302
+ children: [
1303
+ "Column Visibility (",
1304
+ visibleCount,
1305
+ " of ",
1306
+ totalCount,
1307
+ ")"
1308
+ ]
1309
+ }
1310
+ ),
1311
+ /* @__PURE__ */ jsxs(
1312
+ Popover,
1313
+ {
1314
+ open: isOpen,
1315
+ anchorEl,
1316
+ onClose: handlePopoverClose,
1317
+ anchorOrigin: { vertical: "bottom", horizontal: "right" },
1318
+ transformOrigin: { vertical: "top", horizontal: "right" },
1319
+ slotProps: {
1320
+ paper: {
1321
+ sx: { mt: 0.5, minWidth: 220 }
1322
+ }
1323
+ },
1324
+ children: [
1325
+ /* @__PURE__ */ jsx(
1326
+ Box,
1327
+ {
1328
+ sx: {
1329
+ px: 1.5,
1330
+ py: 1,
1331
+ borderBottom: 1,
1332
+ borderColor: "divider",
1333
+ bgcolor: "action.hover"
1334
+ },
1335
+ children: /* @__PURE__ */ jsxs(Typography, { variant: "subtitle2", fontWeight: 600, children: [
1336
+ "Select Columns (",
1337
+ visibleCount,
1338
+ " of ",
1339
+ totalCount,
1340
+ ")"
1341
+ ] })
1342
+ }
1343
+ ),
1344
+ /* @__PURE__ */ jsx(Box, { sx: { maxHeight: 320, overflowY: "auto", py: 0.5 }, children: columns.map((column) => /* @__PURE__ */ jsx(Box, { sx: { px: 1.5, minHeight: 32, display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx(
1345
+ FormControlLabel,
1346
+ {
1347
+ control: /* @__PURE__ */ jsx(
1348
+ Checkbox,
1349
+ {
1350
+ size: "small",
1351
+ checked: visibleColumns.has(column.columnId),
1352
+ onChange: handleCheckboxChange(column.columnId),
1353
+ disabled: column.required === true
1354
+ }
1355
+ ),
1356
+ label: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: column.name }),
1357
+ sx: { m: 0 }
1358
+ }
1359
+ ) }, column.columnId)) }),
1360
+ /* @__PURE__ */ jsxs(
1361
+ Box,
1362
+ {
1363
+ sx: {
1364
+ display: "flex",
1365
+ justifyContent: "flex-end",
1366
+ gap: 1,
1367
+ px: 1.5,
1368
+ py: 1,
1369
+ borderTop: 1,
1370
+ borderColor: "divider",
1371
+ bgcolor: "action.hover"
1372
+ },
1373
+ children: [
1374
+ /* @__PURE__ */ jsx(Button, { size: "small", onClick: handleClearAll, sx: { textTransform: "none" }, children: "Clear All" }),
1375
+ /* @__PURE__ */ jsx(
1376
+ Button,
1377
+ {
1378
+ size: "small",
1379
+ variant: "contained",
1380
+ onClick: handleSelectAll,
1381
+ sx: { textTransform: "none" },
1382
+ children: "Select All"
1383
+ }
1384
+ )
1385
+ ]
1386
+ }
1387
+ )
1388
+ ]
1389
+ }
1390
+ )
1391
+ ] });
1392
+ };
1393
+ var PaginationControls = React3.memo((props) => {
1394
+ const {
1395
+ currentPage,
1396
+ pageSize,
1397
+ totalCount,
1398
+ onPageChange,
1399
+ onPageSizeChange,
1400
+ pageSizeOptions,
1401
+ entityLabelPlural,
1402
+ className
1403
+ } = props;
1404
+ const { labelPlural, vm, handlePageSizeChange } = usePaginationControls({
1405
+ currentPage,
1406
+ pageSize,
1407
+ totalCount,
1408
+ onPageChange,
1409
+ onPageSizeChange,
1410
+ pageSizeOptions,
1411
+ entityLabelPlural
1412
+ });
1413
+ const handlePageSizeChangeEvent = (event) => {
1414
+ handlePageSizeChange(Number(event.target.value));
1415
+ };
1416
+ if (!vm) {
1417
+ return null;
1418
+ }
1419
+ const { pageNumbers, showStartEllipsis, showEndEllipsis, totalPages, startItem, endItem } = vm;
1420
+ return /* @__PURE__ */ jsxs(
1421
+ Box,
1422
+ {
1423
+ className,
1424
+ role: "navigation",
1425
+ "aria-label": "Pagination",
1426
+ sx: {
1427
+ display: "flex",
1428
+ alignItems: "center",
1429
+ justifyContent: "space-between",
1430
+ flexWrap: "wrap",
1431
+ gap: 2,
1432
+ px: 1.5,
1433
+ width: "100%",
1434
+ minWidth: 0,
1435
+ boxSizing: "border-box"
1436
+ },
1437
+ children: [
1438
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
1439
+ "Showing ",
1440
+ startItem,
1441
+ " to ",
1442
+ endItem,
1443
+ " of ",
1444
+ totalCount.toLocaleString(),
1445
+ " ",
1446
+ labelPlural
1447
+ ] }),
1448
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
1449
+ /* @__PURE__ */ jsx(
1450
+ IconButton,
1451
+ {
1452
+ size: "small",
1453
+ onClick: () => onPageChange(1),
1454
+ disabled: currentPage === 1,
1455
+ "aria-label": "First page",
1456
+ children: /* @__PURE__ */ jsx(FirstPage, { fontSize: "small" })
1457
+ }
1458
+ ),
1459
+ /* @__PURE__ */ jsx(
1460
+ IconButton,
1461
+ {
1462
+ size: "small",
1463
+ onClick: () => onPageChange(currentPage - 1),
1464
+ disabled: currentPage === 1,
1465
+ "aria-label": "Previous page",
1466
+ children: /* @__PURE__ */ jsx(ChevronLeft, { fontSize: "small" })
1467
+ }
1468
+ ),
1469
+ showStartEllipsis && /* @__PURE__ */ jsxs(Fragment, { children: [
1470
+ /* @__PURE__ */ jsx(
1471
+ Button,
1472
+ {
1473
+ variant: "outlined",
1474
+ size: "small",
1475
+ onClick: () => onPageChange(1),
1476
+ "aria-label": "Page 1",
1477
+ sx: { minWidth: 32, px: 0.5 },
1478
+ children: "1"
1479
+ }
1480
+ ),
1481
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mx: 0.5 }, "aria-hidden": true, children: "\u2026" })
1482
+ ] }),
1483
+ pageNumbers.map((pageNum) => /* @__PURE__ */ jsx(
1484
+ Button,
1485
+ {
1486
+ variant: currentPage === pageNum ? "contained" : "outlined",
1487
+ size: "small",
1488
+ onClick: () => onPageChange(pageNum),
1489
+ "aria-label": `Page ${pageNum}`,
1490
+ "aria-current": currentPage === pageNum ? "page" : void 0,
1491
+ sx: { minWidth: 32, px: 0.5 },
1492
+ children: pageNum
1493
+ },
1494
+ pageNum
1495
+ )),
1496
+ showEndEllipsis && /* @__PURE__ */ jsxs(Fragment, { children: [
1497
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mx: 0.5 }, "aria-hidden": true, children: "\u2026" }),
1498
+ /* @__PURE__ */ jsx(
1499
+ Button,
1500
+ {
1501
+ variant: "outlined",
1502
+ size: "small",
1503
+ onClick: () => onPageChange(totalPages),
1504
+ "aria-label": `Page ${totalPages}`,
1505
+ sx: { minWidth: 32, px: 0.5 },
1506
+ children: totalPages
1507
+ }
1508
+ )
1509
+ ] }),
1510
+ /* @__PURE__ */ jsx(
1511
+ IconButton,
1512
+ {
1513
+ size: "small",
1514
+ onClick: () => onPageChange(currentPage + 1),
1515
+ disabled: currentPage >= totalPages,
1516
+ "aria-label": "Next page",
1517
+ children: /* @__PURE__ */ jsx(ChevronRight, { fontSize: "small" })
1518
+ }
1519
+ ),
1520
+ /* @__PURE__ */ jsx(
1521
+ IconButton,
1522
+ {
1523
+ size: "small",
1524
+ onClick: () => onPageChange(totalPages),
1525
+ disabled: currentPage >= totalPages,
1526
+ "aria-label": "Last page",
1527
+ children: /* @__PURE__ */ jsx(LastPage, { fontSize: "small" })
1528
+ }
1529
+ )
1530
+ ] }),
1531
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
1532
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "Rows" }),
1533
+ /* @__PURE__ */ jsx(
1534
+ Select,
1535
+ {
1536
+ value: pageSize,
1537
+ onChange: handlePageSizeChangeEvent,
1538
+ size: "small",
1539
+ "aria-label": "Rows per page",
1540
+ sx: { minWidth: 70 },
1541
+ children: vm.pageSizeOptions.map((n) => /* @__PURE__ */ jsx(MenuItem, { value: n, children: n }, n))
1542
+ }
1543
+ )
1544
+ ] })
1545
+ ]
1546
+ }
1547
+ );
1548
+ });
1549
+ var MuiThemeContainer = React3.forwardRef(
1550
+ function MuiThemeContainer2(props, ref) {
1551
+ const theme = useTheme();
1552
+ const sx = React3.useMemo(() => ({
1553
+ display: "flex",
1554
+ flexDirection: "column",
1555
+ gap: 1,
1556
+ "--ogrid-bg": theme.palette.background.default,
1557
+ "--ogrid-border": theme.palette.divider,
1558
+ "--ogrid-header-bg": theme.palette.mode === "dark" ? theme.palette.grey[800] : theme.palette.grey[100],
1559
+ "--ogrid-fg": theme.palette.text.primary,
1560
+ "--ogrid-fg-secondary": theme.palette.text.secondary,
1561
+ "--ogrid-fg-muted": theme.palette.text.disabled,
1562
+ "--ogrid-hover-bg": theme.palette.action.hover,
1563
+ "--ogrid-paper-bg": theme.palette.background.paper,
1564
+ "--ogrid-primary": theme.palette.primary.main,
1565
+ "--ogrid-selection-bg": theme.palette.action.selected
1566
+ }), [theme]);
1567
+ return /* @__PURE__ */ jsx(Box, { ref, sx, ...props });
1568
+ }
1569
+ );
1570
+ var OGrid = createOGrid({
1571
+ DataGridTable,
1572
+ ColumnChooser,
1573
+ PaginationControls,
1574
+ containerComponent: MuiThemeContainer
1575
+ });
1576
+
1577
+ export { ColumnChooser, ColumnHeaderFilter, ColumnHeaderMenu, DataGridTable, OGrid, PaginationControls };