@ornery/ui-grid-react 0.1.10 → 1.0.0

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/index.mjs CHANGED
@@ -1,2223 +1,158 @@
1
1
  // src/UiGrid.tsx
2
2
  import React from "react";
3
-
4
- // src/useGridState.ts
5
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
-
7
- // src/gridStateMath.ts
8
- import { gridColumnWidth } from "@ornery/ui-grid-core";
9
- function orderVisibleColumns(columns, order) {
10
- return [...columns].filter((column) => column.visible !== false).sort((left, right) => order.indexOf(left.name) - order.indexOf(right.name));
11
- }
12
- function buildGridTemplateColumns(columns) {
13
- return columns.map((column) => gridColumnWidth(column)).join(" ");
14
- }
15
- function resolveBenchmarkIterations(iterations, configuredIterations) {
16
- return Math.max(1, iterations ?? configuredIterations ?? 25);
17
- }
18
- function formatPaginationSummary(totalItems, firstRowIndex, lastRowIndex) {
19
- if (totalItems === 0) {
20
- return "0-0 of 0";
21
- }
22
- return `${firstRowIndex + 1}-${lastRowIndex + 1} of ${totalItems}`;
23
- }
24
- function computeViewportHeightPx(viewportHeight, autoViewportHeight) {
25
- return `${viewportHeight ?? autoViewportHeight ?? 560}px`;
26
- }
27
- function computeViewportRows(viewportHeight, rowHeight) {
28
- return Math.max(1, Math.ceil((viewportHeight ?? 560) / (rowHeight ?? 44)));
29
- }
30
-
31
- // src/useGridState.ts
32
- import {
33
- createGridApi,
34
- getCellValue,
35
- setPathValue,
36
- SORT_DIRECTIONS,
37
- defaultGridEngine,
38
- resolveGridLabels,
39
- gridColumnWidth as gridColumnWidth2,
40
- headerLabel as coreHeaderLabel,
41
- gridSortButtonLabel,
42
- gridSortAriaSort,
43
- gridGroupingButtonLabel,
44
- gridFilterPlaceholder,
45
- gridGroupDisclosureLabel,
46
- gridEditorInputType,
47
- gridCellIndent,
48
- gridTreeToggleLabelForRow,
49
- gridExpandToggleLabelForRow,
50
- isGridTreeRowExpanded,
51
- isGridColumnSortable,
52
- isGridColumnFilterable,
53
- isGridColumnGrouped,
54
- isGridGroupingEnabled,
55
- isGridFilteringEnabled,
56
- shouldShowGridTreeToggle,
57
- shouldShowGridExpandToggle,
58
- shouldShowGridPaginationControls,
59
- buildGridCellContext,
60
- formatGridCellDisplayValue,
61
- buildGridFocusCellResult,
62
- findNextGridCell,
63
- isPrintableGridKey,
64
- isGridNavigationKey,
65
- isGridCellPosition,
66
- exportCsvRows,
67
- buildGridRows,
68
- resolveGridRowId as coreResolveGridRowId,
69
- findGridRowById as coreFindGridRowById,
70
- getEffectivePageSize as coreGetEffectivePageSize,
71
- getCurrentPageValue as coreGetCurrentPageValue,
72
- getTotalPagesValue as coreGetTotalPagesValue,
73
- getFirstRowIndexValue as coreGetFirstRowIndexValue,
74
- getLastRowIndexValue as coreGetLastRowIndexValue,
75
- buildGridSavedState,
76
- sanitizeDownloadFilename,
77
- parseGridEditedValue,
78
- canGridExpandRows,
79
- areAllGridRowsExpanded,
80
- addGridRowInvisibleReason,
81
- clearGridRowInvisibleReason,
82
- FEATURE_SORTING,
83
- FEATURE_FILTERING,
84
- FEATURE_GROUPING,
85
- FEATURE_PAGINATION,
86
- FEATURE_CELL_EDIT,
87
- FEATURE_EXPANDABLE,
88
- FEATURE_TREE_VIEW,
89
- FEATURE_INFINITE_SCROLL,
90
- FEATURE_COLUMN_MOVING,
91
- FEATURE_CSV_EXPORT,
92
- FEATURE_AUTO_RESIZE,
93
- FEATURE_PINNING,
94
- buildInitialPinnedState,
95
- computePinnedOffset,
96
- isColumnPinnable,
97
- isPinningEnabled,
98
- applyGridSortStateCommand,
99
- updateGridFilterCommand,
100
- clearGridFiltersCommand,
101
- clearGridGroupingCommand,
102
- moveGridColumnCommand,
103
- moveGridVisibleColumnCommand,
104
- seekGridPaginationCommand,
105
- setGridPaginationPageSizeCommand,
106
- sortGridColumnCommand,
107
- toggleGridRowExpansionCommand,
108
- expandAllGridRowsCommand,
109
- collapseAllGridRowsCommand,
110
- toggleGridTreeRowCommand,
111
- setGridTreeRowExpandedCommand,
112
- expandAllGridTreeRowsCommand,
113
- collapseAllGridTreeRowsCommand,
114
- beginGridCellEditCommand,
115
- commitGridCellEditCommand,
116
- cancelGridCellEditCommand,
117
- maybeRequestInfiniteScrollCommand,
118
- completeGridInfiniteScrollDataLoadCommand,
119
- resetGridInfiniteScrollCommand,
120
- saveGridInfiniteScrollPercentageCommand,
121
- setGridInfiniteScrollDirectionsCommand,
122
- restoreGridStateCommand,
123
- pinGridColumnCommand,
124
- raiseGridRenderingComplete,
125
- raiseGridRowsRendered,
126
- raiseGridRowsVisibleChanged,
127
- raiseGridCanvasHeightChanged,
128
- raiseGridDimensionChanged,
129
- raiseGridScrollBegin,
130
- raiseGridScrollEnd,
131
- raiseGridBenchmarkComplete,
132
- downloadGridCsvFile,
133
- observeGridHostSize
134
- } from "@ornery/ui-grid-core";
135
- function escapeCssSelectorValue(value) {
136
- const nativeEscape = globalThis.CSS?.escape;
137
- if (typeof nativeEscape === "function") {
138
- return nativeEscape(value);
139
- }
140
- let output = "";
141
- for (let index = 0; index < value.length; index += 1) {
142
- const codePoint = value.charCodeAt(index);
143
- const character = value.charAt(index);
144
- if (codePoint === 0) {
145
- output += "\uFFFD";
146
- continue;
147
- }
148
- const isControlCharacter = codePoint >= 1 && codePoint <= 31 || codePoint === 127;
149
- const startsWithDigit = index === 0 && codePoint >= 48 && codePoint <= 57;
150
- const secondCharDigitAfterHyphen = index === 1 && codePoint >= 48 && codePoint <= 57 && value.charCodeAt(0) === 45;
151
- if (isControlCharacter || startsWithDigit || secondCharDigitAfterHyphen) {
152
- output += `\\${codePoint.toString(16)} `;
153
- continue;
154
- }
155
- if (index === 0 && value.length === 1 && codePoint === 45) {
156
- output += `\\${character}`;
157
- continue;
158
- }
159
- const isSafeCharacter = codePoint >= 128 || codePoint === 45 || codePoint === 95 || codePoint >= 48 && codePoint <= 57 || codePoint >= 65 && codePoint <= 90 || codePoint >= 97 && codePoint <= 122;
160
- output += isSafeCharacter ? character : `\\${character}`;
161
- }
162
- return output;
163
- }
164
- function useGridState(options, onRegisterApi) {
165
- const [activeFilters, setActiveFilters] = useState({});
166
- const [groupByColumns, setGroupByColumns] = useState([]);
167
- const [collapsedGroups, setCollapsedGroups] = useState({});
168
- const [columnOrder, setColumnOrder] = useState([]);
169
- const [hiddenRowReasons, setHiddenRowReasons] = useState({});
170
- const [sortState, setSortState] = useState({
171
- columnName: null,
172
- direction: SORT_DIRECTIONS.none
173
- });
174
- const [focusedCell, setFocusedCell] = useState(null);
175
- const [editingCell, setEditingCell] = useState(null);
176
- const [editingValue, setEditingValue] = useState("");
177
- const [expandedRows, setExpandedRows] = useState({});
178
- const [expandedTreeRows, setExpandedTreeRows] = useState({});
179
- const [currentPage, setCurrentPage] = useState(1);
180
- const [pageSize, setPageSize] = useState(0);
181
- const [benchmarkResult, setBenchmarkResult] = useState(null);
182
- const [infiniteScrollState, setInfiniteScrollState] = useState({
183
- scrollUp: false,
184
- scrollDown: true,
185
- dataLoading: false,
186
- previousVisibleRows: 0
187
- });
188
- const [autoViewportHeight, setAutoViewportHeight] = useState(null);
189
- const [pinnedColumns, setPinnedColumns] = useState({});
190
- const [columnWidthOverrides, setColumnWidthOverrides] = useState({});
191
- const gridContainerRef = useRef(null);
192
- const initializedGridIdRef = useRef(null);
193
- const lastCanvasHeightRef = useRef(0);
194
- const lastGridHeightRef = useRef(0);
195
- const lastGridWidthRef = useRef(0);
196
- const scrollEndHandleRef = useRef(void 0);
197
- const scrollingRef = useRef(false);
198
- const editorFocusTokenRef = useRef(0);
199
- const renderedCellFocusTokenRef = useRef(0);
200
- const activeFiltersRef = useRef(activeFilters);
201
- activeFiltersRef.current = activeFilters;
202
- const groupByColumnsRef = useRef(groupByColumns);
203
- groupByColumnsRef.current = groupByColumns;
204
- const collapsedGroupsRef = useRef(collapsedGroups);
205
- collapsedGroupsRef.current = collapsedGroups;
206
- const columnOrderRef = useRef(columnOrder);
207
- columnOrderRef.current = columnOrder;
208
- const hiddenRowReasonsRef = useRef(hiddenRowReasons);
209
- hiddenRowReasonsRef.current = hiddenRowReasons;
210
- const sortStateRef = useRef(sortState);
211
- sortStateRef.current = sortState;
212
- const focusedCellRef = useRef(focusedCell);
213
- focusedCellRef.current = focusedCell;
214
- const editingCellRef = useRef(editingCell);
215
- editingCellRef.current = editingCell;
216
- const editingValueRef = useRef(editingValue);
217
- editingValueRef.current = editingValue;
218
- const expandedRowsRef = useRef(expandedRows);
219
- expandedRowsRef.current = expandedRows;
220
- const expandedTreeRowsRef = useRef(expandedTreeRows);
221
- expandedTreeRowsRef.current = expandedTreeRows;
222
- const pinnedColumnsRef = useRef(pinnedColumns);
223
- pinnedColumnsRef.current = pinnedColumns;
224
- const currentPageRef = useRef(currentPage);
225
- currentPageRef.current = currentPage;
226
- const pageSizeRef = useRef(pageSize);
227
- pageSizeRef.current = pageSize;
228
- const setEditingCellState = useCallback((nextEditingCell) => {
229
- editingCellRef.current = nextEditingCell;
230
- setEditingCell(nextEditingCell);
231
- }, []);
232
- const setEditingValueState = useCallback((nextEditingValue) => {
233
- editingValueRef.current = nextEditingValue;
234
- setEditingValue(nextEditingValue);
235
- }, []);
236
- const infiniteScrollStateRef = useRef(infiniteScrollState);
237
- infiniteScrollStateRef.current = infiniteScrollState;
238
- const optionsRef = useRef(options);
3
+ import { createPortal } from "react-dom";
4
+ import { defineStandaloneUiGridElement } from "@ornery/ui-grid-vanilla";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var TAG_NAME = "ui-grid-element";
7
+ var definePromise = null;
8
+ function UiGrid({ options, onRegisterApi, cellRenderers, className }) {
9
+ const containerRef = React.useRef(null);
10
+ const elementRef = React.useRef(null);
11
+ const [slots, setSlots] = React.useState(/* @__PURE__ */ new Map());
12
+ const cellRenderersRef = React.useRef(cellRenderers);
13
+ cellRenderersRef.current = cellRenderers;
14
+ const onRegisterApiRef = React.useRef(onRegisterApi);
15
+ onRegisterApiRef.current = onRegisterApi;
16
+ const optionsRef = React.useRef(options);
239
17
  optionsRef.current = options;
240
- const rowSize = options.rowHeight ?? 44;
241
- const visibleColumns = useMemo(() => {
242
- const orderedColumns = orderVisibleColumns(options.columnDefs, columnOrder);
243
- const applyWidthOverrides = (columns) => columns.map((col) => {
244
- const override = columnWidthOverrides[col.name];
245
- return override == null ? col : { ...col, width: override };
246
- });
247
- const pinnedEntries = Object.entries(pinnedColumns);
248
- if (pinnedEntries.length === 0) {
249
- return applyWidthOverrides(orderedColumns);
250
- }
251
- const columnByName = new Map(orderedColumns.map((column) => [column.name, column]));
252
- const pinnedLeft = pinnedEntries.filter(([, direction]) => direction === "left").map(([columnName]) => columnByName.get(columnName)).filter((column) => column !== void 0);
253
- const pinnedRight = pinnedEntries.filter(([, direction]) => direction === "right").map(([columnName]) => columnByName.get(columnName)).filter((column) => column !== void 0);
254
- const centerColumns = orderedColumns.filter(
255
- (column) => pinnedColumns[column.name] === void 0
256
- );
257
- return applyWidthOverrides([...pinnedLeft, ...centerColumns, ...pinnedRight]);
258
- }, [options.columnDefs, columnOrder, pinnedColumns, columnWidthOverrides]);
259
- const visibleColumnsRef = useRef(visibleColumns);
260
- visibleColumnsRef.current = visibleColumns;
261
- const pipeline = useMemo(() => {
262
- return defaultGridEngine.buildPipeline({
263
- options,
264
- columns: visibleColumns,
265
- activeFilters,
266
- sortState,
267
- groupByColumns,
268
- collapsedGroups,
269
- hiddenRowReasons,
270
- expandedRows,
271
- expandedTreeRows,
272
- currentPage,
273
- pageSize,
274
- rowSize
275
- });
276
- }, [
277
- options,
278
- visibleColumns,
279
- activeFilters,
280
- sortState,
281
- groupByColumns,
282
- collapsedGroups,
283
- hiddenRowReasons,
284
- expandedRows,
285
- expandedTreeRows,
286
- currentPage,
287
- pageSize,
288
- rowSize
289
- ]);
290
- const pipelineRef = useRef(pipeline);
291
- pipelineRef.current = pipeline;
292
- const labels = useMemo(() => resolveGridLabels(options.labels), [options.labels]);
293
- const gridTemplateColumns = useMemo(
294
- () => buildGridTemplateColumns(visibleColumns),
295
- [visibleColumns]
296
- );
297
- const isPinningEnabledFn = useCallback(() => {
298
- return isPinningEnabled(optionsRef.current);
299
- }, []);
300
- const isColumnPinnableFn = useCallback((column) => {
301
- return isColumnPinnable(optionsRef.current, column);
302
- }, []);
303
- const isPinnedFn = useCallback((column) => {
304
- return pinnedColumnsRef.current[column.name] !== void 0;
305
- }, []);
306
- const pinnedOffsetFn = useCallback((column) => {
307
- return computePinnedOffset(visibleColumnsRef.current, pinnedColumnsRef.current, column);
308
- }, []);
309
- const resolveRowId = useCallback((row) => {
310
- return coreResolveGridRowId(optionsRef.current, row);
311
- }, []);
312
- const buildRowsFromData = useCallback((data) => {
313
- return buildGridRows(
314
- { ...optionsRef.current, data },
315
- optionsRef.current.rowHeight ?? 44,
316
- hiddenRowReasonsRef.current,
317
- expandedRowsRef.current
318
- );
319
- }, []);
320
- const findRowById = useCallback(
321
- (rowId) => {
322
- return coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId);
323
- },
324
- [buildRowsFromData]
325
- );
326
- const canExpandRowsFn = useCallback(() => {
327
- return FEATURE_EXPANDABLE && canGridExpandRows(optionsRef.current);
328
- }, []);
329
- const effectivePageSizeFn = useCallback((totalItems) => {
330
- return coreGetEffectivePageSize(optionsRef.current, pageSizeRef.current, totalItems);
331
- }, []);
332
- const getCurrentPageValueFn = useCallback((totalItems) => {
333
- const ti = totalItems ?? pipelineRef.current.totalItems;
334
- return coreGetCurrentPageValue(
335
- optionsRef.current,
336
- currentPageRef.current,
337
- ti,
338
- pageSizeRef.current
339
- );
340
- }, []);
341
- const getTotalPagesValueFn = useCallback((totalItems) => {
342
- const ti = totalItems ?? pipelineRef.current.totalItems;
343
- return coreGetTotalPagesValue(optionsRef.current, ti, pageSizeRef.current);
344
- }, []);
345
- const getFirstRowIndexValueFn = useCallback((totalItems) => {
346
- const ti = totalItems ?? pipelineRef.current.totalItems;
347
- return coreGetFirstRowIndexValue(
348
- optionsRef.current,
349
- currentPageRef.current,
350
- ti,
351
- pageSizeRef.current
352
- );
353
- }, []);
354
- const getLastRowIndexValueFn = useCallback((totalItems) => {
355
- const ti = totalItems ?? pipelineRef.current.totalItems;
356
- return coreGetLastRowIndexValue(
357
- optionsRef.current,
358
- currentPageRef.current,
359
- ti,
360
- pageSizeRef.current
361
- );
362
- }, []);
363
- const isCellEditable = useCallback(
364
- (row, column, triggerEvent) => {
365
- if (!FEATURE_CELL_EDIT) return false;
366
- const editable = column.enableCellEdit ?? optionsRef.current.enableCellEdit ?? false;
367
- if (!editable) return false;
368
- const condition = column.cellEditableCondition ?? optionsRef.current.cellEditableCondition ?? true;
369
- if (typeof condition === "boolean") return condition;
370
- const context = {
371
- row: row.entity,
372
- column,
373
- rowIndex: row.index,
374
- triggerEvent
375
- };
376
- return condition(context);
377
- },
378
- []
379
- );
380
- const shouldEditOnFocusFn = useCallback((column) => {
381
- return column.enableCellEditOnFocus ?? optionsRef.current.enableCellEditOnFocus ?? false;
382
- }, []);
383
- const focusRenderedCell = useCallback((position) => {
384
- const focusToken = ++renderedCellFocusTokenRef.current;
385
- const selector = `.body-cell[data-row-id="${escapeCssSelectorValue(position.rowId)}"][data-col-name="${escapeCssSelectorValue(position.columnName)}"]`;
386
- const doFocus = (retry = true) => {
387
- if (focusToken !== renderedCellFocusTokenRef.current) return;
388
- const container = gridContainerRef.current;
389
- if (!container) return;
390
- const target = container.querySelector(selector);
391
- if (!target) {
392
- if (retry) requestAnimationFrame(() => doFocus(false));
393
- return;
394
- }
395
- target.focus({ preventScroll: true });
396
- if (retry && container.ownerDocument.activeElement !== target) {
397
- requestAnimationFrame(() => doFocus(false));
398
- }
18
+ const currentSlotColumnsRef = React.useRef([]);
19
+ React.useEffect(() => {
20
+ const container = containerRef.current;
21
+ if (!container) return;
22
+ let el = null;
23
+ let disposed = false;
24
+ const mount = async () => {
25
+ if (!definePromise) {
26
+ definePromise = defineStandaloneUiGridElement(TAG_NAME);
27
+ }
28
+ await definePromise;
29
+ if (disposed) return;
30
+ el = document.createElement(TAG_NAME);
31
+ el.style.display = "block";
32
+ el.style.height = "100%";
33
+ el.style.minHeight = "0";
34
+ elementRef.current = el;
35
+ container.appendChild(el);
36
+ el.addEventListener("cellSlotsChanged", handleCellSlotsChanged);
37
+ applyOptions(el, optionsRef.current);
399
38
  };
400
- doFocus(true);
401
- queueMicrotask(() => doFocus(true));
402
- }, []);
403
- const focusEditorInput = useCallback((focusToken) => {
404
- if (focusToken !== editorFocusTokenRef.current) return;
405
- const ec = editingCellRef.current;
406
- if (!ec) return;
407
- const selector = `.cell-editor[data-row-id="${escapeCssSelectorValue(ec.rowId)}"][data-col-name="${escapeCssSelectorValue(ec.columnName)}"]`;
408
- const doFocus = (retry = true) => {
409
- if (focusToken !== editorFocusTokenRef.current) return;
410
- const currentEc = editingCellRef.current;
411
- if (!currentEc || currentEc.rowId !== ec.rowId || currentEc.columnName !== ec.columnName)
412
- return;
413
- const container = gridContainerRef.current;
414
- if (!container) return;
415
- const input = container.querySelector(selector);
416
- if (!input) {
417
- if (retry) requestAnimationFrame(() => doFocus(false));
418
- return;
39
+ void mount();
40
+ return () => {
41
+ disposed = true;
42
+ if (el) {
43
+ el.removeEventListener("cellSlotsChanged", handleCellSlotsChanged);
44
+ el.remove();
45
+ elementRef.current = null;
419
46
  }
420
- input.focus();
421
- input.select();
47
+ setSlots(/* @__PURE__ */ new Map());
422
48
  };
423
- doFocus(true);
424
49
  }, []);
425
- const gridApiRef = useRef(null);
426
- if (!gridApiRef.current) {
427
- const bindings = {
428
- refresh: () => setActiveFilters((current) => ({ ...current })),
429
- getVisibleRows: () => pipelineRef.current.visibleRows,
430
- setRowInvisible: (row, reason = "user") => {
431
- const rowId = coreResolveGridRowId(optionsRef.current, row);
432
- setHiddenRowReasons((current) => addGridRowInvisibleReason(current, rowId, reason));
433
- },
434
- clearRowInvisible: (row, reason = "user") => {
435
- const rowId = coreResolveGridRowId(optionsRef.current, row);
436
- setHiddenRowReasons((current) => clearGridRowInvisibleReason(current, rowId, reason));
437
- },
438
- setFilter: (columnName, value) => {
439
- setActiveFilters((current) => {
440
- const next = { ...current, [columnName]: value };
441
- activeFiltersRef.current = next;
442
- queueMicrotask(() => gridApiRef.current.core.raise.filterChanged(next));
443
- return next;
444
- });
445
- },
446
- clearAllFilters: () => {
447
- const nextFilters = {};
448
- activeFiltersRef.current = nextFilters;
449
- setActiveFilters(nextFilters);
450
- queueMicrotask(() => gridApiRef.current.core.raise.filterChanged(nextFilters));
451
- },
452
- sortColumn: (columnName, direction) => {
453
- sortGridColumnCommand(gridApiRef.current, (s) => setSortState(s), columnName, direction);
454
- },
455
- moveColumn: (fromIndex, toIndex) => {
456
- moveGridColumnCommand(
457
- gridApiRef.current,
458
- FEATURE_COLUMN_MOVING && optionsRef.current.enableColumnMoving === true,
459
- (updater) => setColumnOrder((current) => updater(current)),
460
- fromIndex,
461
- toIndex
462
- );
463
- },
464
- toggleGrouping: (columnName) => {
465
- if (!(FEATURE_GROUPING && isGridGroupingEnabled(optionsRef.current))) return;
466
- const current = groupByColumnsRef.current;
467
- const next = current.includes(columnName) ? current.filter((n) => n !== columnName) : [...current, columnName];
468
- groupByColumnsRef.current = next;
469
- setGroupByColumns(next);
470
- gridApiRef.current.core.raise.groupingChanged(next);
471
- },
472
- clearGrouping: () => {
473
- clearGridGroupingCommand(
474
- gridApiRef.current,
475
- (grouping) => setGroupByColumns(grouping),
476
- false
477
- );
478
- },
479
- benchmark: (iterations) => {
480
- return runBenchmarkFn(iterations);
481
- },
482
- exportCsv: () => {
483
- exportCsvFn();
484
- },
485
- paginationGetPage: () => getCurrentPageValueFn(),
486
- paginationGetTotalPages: () => getTotalPagesValueFn(),
487
- paginationGetFirstRowIndex: () => getFirstRowIndexValueFn(),
488
- paginationGetLastRowIndex: () => getLastRowIndexValueFn(),
489
- paginationNextPage: () => seekPageFn(getCurrentPageValueFn() + 1),
490
- paginationPreviousPage: () => seekPageFn(getCurrentPageValueFn() - 1),
491
- paginationSeek: (page) => seekPageFn(page),
492
- paginationSetPageSize: (ps) => setPaginationPageSizeFn(ps),
493
- toggleRowExpansion: (row) => toggleRowExpansionByRefFn(row),
494
- expandAllRows: () => expandAllRowsFn(),
495
- collapseAllRows: () => {
496
- collapseAllGridRowsCommand((e) => setExpandedRows(e));
497
- },
498
- toggleAllRows: () => toggleAllRowsFn(),
499
- treeExpandAllRows: () => {
500
- expandAllGridTreeRowsCommand(
501
- (data) => buildRowsFromData(data),
502
- optionsRef.current.data,
503
- (e) => setExpandedTreeRows(e)
504
- );
505
- },
506
- treeCollapseAllRows: () => {
507
- collapseAllGridTreeRowsCommand((e) => setExpandedTreeRows(e));
508
- },
509
- treeToggleRow: (row) => toggleTreeRowByRefFn(row),
510
- treeExpandRow: (row) => expandTreeRowByRefFn(row),
511
- treeCollapseRow: (row) => collapseTreeRowByRefFn(row),
512
- treeGetRowChildren: (row) => {
513
- const rowId = coreResolveGridRowId(optionsRef.current, row);
514
- return buildRowsFromData(optionsRef.current.data).filter((r) => r.parentId === rowId);
515
- },
516
- treeGetState: () => expandedTreeRowsRef.current,
517
- treeSetState: (state) => setExpandedTreeRows({ ...state }),
518
- infiniteScrollDataLoaded: (scrollUp, scrollDown) => {
519
- return completeGridInfiniteScrollDataLoadCommand(
520
- infiniteScrollStateRef.current,
521
- (s) => setInfiniteScrollState(s),
522
- scrollUp ?? infiniteScrollStateRef.current.scrollUp,
523
- scrollDown ?? infiniteScrollStateRef.current.scrollDown
524
- );
525
- },
526
- infiniteScrollReset: (scrollUp, scrollDown) => {
527
- resetGridInfiniteScrollCommand(
528
- (s) => setInfiniteScrollState(s),
529
- scrollUp ?? infiniteScrollStateRef.current.scrollUp,
530
- scrollDown ?? infiniteScrollStateRef.current.scrollDown
531
- );
532
- },
533
- infiniteScrollSaveScrollPercentage: () => {
534
- saveGridInfiniteScrollPercentageCommand(
535
- infiniteScrollStateRef.current,
536
- pipelineRef.current.visibleRows.length,
537
- (s) => setInfiniteScrollState(s)
538
- );
539
- },
540
- infiniteScrollDataRemovedTop: (scrollUp, scrollDown) => {
541
- resetGridInfiniteScrollCommand(
542
- (s) => setInfiniteScrollState(s),
543
- scrollUp ?? infiniteScrollStateRef.current.scrollUp,
544
- scrollDown ?? infiniteScrollStateRef.current.scrollDown
545
- );
546
- },
547
- infiniteScrollDataRemovedBottom: (scrollUp, scrollDown) => {
548
- resetGridInfiniteScrollCommand(
549
- (s) => setInfiniteScrollState(s),
550
- scrollUp ?? infiniteScrollStateRef.current.scrollUp,
551
- scrollDown ?? infiniteScrollStateRef.current.scrollDown
552
- );
553
- },
554
- infiniteScrollSetDirections: (scrollUp, scrollDown) => {
555
- setGridInfiniteScrollDirectionsCommand(
556
- infiniteScrollStateRef.current,
557
- (s) => setInfiniteScrollState(s),
558
- scrollUp,
559
- scrollDown
560
- );
561
- },
562
- saveState: () => {
563
- return buildGridSavedState({
564
- columnOrder: columnOrderRef.current,
565
- activeFilters: activeFiltersRef.current,
566
- sortState: sortStateRef.current,
567
- groupByColumns: groupByColumnsRef.current,
568
- currentPage: currentPageRef.current,
569
- pageSize: pageSizeRef.current,
570
- totalItems: pipelineRef.current.totalItems,
571
- expandedRows: expandedRowsRef.current,
572
- expandedTreeRows: expandedTreeRowsRef.current,
573
- pinnedColumns: pinnedColumnsRef.current
574
- });
575
- },
576
- restoreState: (state) => {
577
- restoreGridStateCommand(gridApiRef.current, state, {
578
- setColumnOrder: (order) => setColumnOrder(order),
579
- setActiveFilters: (filters) => setActiveFilters(filters),
580
- setSortState: (s) => setSortState(s),
581
- setGroupByColumns: (grouping) => setGroupByColumns(grouping),
582
- setCurrentPage: (page) => setCurrentPage(page),
583
- setPageSize: (ps) => setPageSize(ps),
584
- setExpandedRows: (e) => setExpandedRows(e),
585
- setExpandedTreeRows: (e) => setExpandedTreeRows(e),
586
- setPinnedColumns: (p) => setPinnedColumns(p),
587
- getEffectivePageSize: () => effectivePageSizeFn(pipelineRef.current.totalItems)
50
+ React.useEffect(() => {
51
+ const el = elementRef.current;
52
+ if (!el) return;
53
+ applyOptions(el, options);
54
+ }, [options]);
55
+ React.useEffect(() => {
56
+ if (slots.size === 0 || !options.data) return;
57
+ const dataById = /* @__PURE__ */ new Map();
58
+ for (const row of options.data) {
59
+ const id = String(row["id"] ?? "");
60
+ if (id) dataById.set(id, row);
61
+ }
62
+ let changed = false;
63
+ const nextSlots = new Map(slots);
64
+ for (const [key, entry] of nextSlots) {
65
+ const row = dataById.get(entry.rowId);
66
+ if (!row) continue;
67
+ const col = options.columnDefs?.find((c) => c.name === entry.columnName);
68
+ const value = col?.field ? getNestedValue(row, col.field) : row[entry.columnName];
69
+ if (entry.context.value !== value || entry.context.row !== row) {
70
+ nextSlots.set(key, {
71
+ ...entry,
72
+ context: { ...entry.context, $implicit: value, value, row }
588
73
  });
589
- },
590
- beginCellEdit: (row, columnName, triggerEvent) => {
591
- const rowId = coreResolveGridRowId(optionsRef.current, row);
592
- const gridRow = coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId);
593
- const column = visibleColumnsRef.current.find((c) => c.name === columnName);
594
- if (!gridRow || !column || !isCellEditable(gridRow, column, triggerEvent)) return;
595
- startCellEditFn(gridRow, column, triggerEvent);
596
- },
597
- endCellEdit: () => commitCellEditFn(),
598
- cancelCellEdit: () => cancelCellEditFn(),
599
- getEditingCell: () => editingCellRef.current,
600
- pinColumn: (columnName, direction) => {
601
- pinGridColumnCommand(
602
- gridApiRef.current,
603
- isPinningEnabledFn(),
604
- (v) => setPinnedColumns(v),
605
- () => pinnedColumnsRef.current,
606
- columnName,
607
- direction
608
- );
609
- }
610
- };
611
- gridApiRef.current = createGridApi(bindings);
612
- }
613
- const gridApi = gridApiRef.current;
614
- const seekPageFn = useCallback(
615
- (page) => {
616
- seekGridPaginationCommand(
617
- gridApiRef.current,
618
- (nextPage) => setCurrentPage(nextPage),
619
- () => getTotalPagesValueFn(),
620
- () => effectivePageSizeFn(pipelineRef.current.totalItems),
621
- page
622
- );
623
- },
624
- [getTotalPagesValueFn, effectivePageSizeFn]
625
- );
626
- const togglePinFn = useCallback((column) => {
627
- const current = pinnedColumnsRef.current[column.name];
628
- const next = current === "left" ? "right" : current === "right" ? "none" : "left";
629
- pinGridColumnCommand(
630
- gridApiRef.current,
631
- isPinningEnabledFn(),
632
- (v) => setPinnedColumns(v),
633
- () => pinnedColumnsRef.current,
634
- column.name,
635
- next
636
- );
637
- }, []);
638
- const setPaginationPageSizeFn = useCallback((ps) => {
639
- setGridPaginationPageSizeCommand(
640
- gridApiRef.current,
641
- (nextPageSize) => setPageSize(nextPageSize),
642
- (nextPage) => setCurrentPage(nextPage),
643
- ps
644
- );
645
- }, []);
646
- const toggleRowExpansionByRefFn = useCallback(
647
- (row) => {
648
- const rowId = coreResolveGridRowId(optionsRef.current, row);
649
- toggleGridRowExpansionCommand(
650
- gridApiRef.current,
651
- FEATURE_EXPANDABLE && canGridExpandRows(optionsRef.current),
652
- expandedRowsRef.current,
653
- rowId,
654
- (e) => setExpandedRows(e),
655
- (resolvedRowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId)
656
- );
657
- },
658
- [buildRowsFromData]
659
- );
660
- const expandAllRowsFn = useCallback(() => {
661
- if (!canGridExpandRows(optionsRef.current)) return;
662
- expandAllGridRowsCommand(
663
- (data) => buildRowsFromData(data),
664
- optionsRef.current.data,
665
- (e) => setExpandedRows(e)
666
- );
667
- }, [buildRowsFromData]);
668
- const toggleAllRowsFn = useCallback(() => {
669
- const allExpanded = areAllGridRowsExpanded(
670
- buildRowsFromData(optionsRef.current.data),
671
- expandedRowsRef.current
672
- );
673
- if (allExpanded) {
674
- collapseAllGridRowsCommand((e) => setExpandedRows(e));
675
- } else {
676
- expandAllRowsFn();
677
- }
678
- }, [buildRowsFromData, expandAllRowsFn]);
679
- const toggleTreeRowByRefFn = useCallback(
680
- (row) => {
681
- const rowId = coreResolveGridRowId(optionsRef.current, row);
682
- toggleGridTreeRowCommand(
683
- gridApiRef.current,
684
- expandedTreeRowsRef.current,
685
- rowId,
686
- (e) => setExpandedTreeRows(e),
687
- (resolvedRowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId)
688
- );
689
- },
690
- [buildRowsFromData]
691
- );
692
- const expandTreeRowByRefFn = useCallback(
693
- (row) => {
694
- const rowId = coreResolveGridRowId(optionsRef.current, row);
695
- setGridTreeRowExpandedCommand(
696
- gridApiRef.current,
697
- expandedTreeRowsRef.current,
698
- rowId,
699
- true,
700
- (e) => setExpandedTreeRows(e),
701
- (resolvedRowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId)
702
- );
703
- },
704
- [buildRowsFromData]
705
- );
706
- const collapseTreeRowByRefFn = useCallback(
707
- (row) => {
708
- const rowId = coreResolveGridRowId(optionsRef.current, row);
709
- setGridTreeRowExpandedCommand(
710
- gridApiRef.current,
711
- expandedTreeRowsRef.current,
712
- rowId,
713
- false,
714
- (e) => setExpandedTreeRows(e),
715
- (resolvedRowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId)
716
- );
717
- },
718
- [buildRowsFromData]
719
- );
720
- const startCellEditFn = useCallback(
721
- (row, column, triggerEvent, initialValue) => {
722
- const currentValue = getCellValue(row.entity, column);
723
- const focusToken = ++editorFocusTokenRef.current;
724
- const ec = beginGridCellEditCommand(
725
- gridApiRef.current,
726
- {
727
- setFocusedCell: (fc) => setFocusedCell(fc),
728
- setEditingCell: setEditingCellState,
729
- setEditingValue: setEditingValueState
730
- },
731
- row,
732
- column,
733
- currentValue,
734
- triggerEvent,
735
- initialValue
736
- );
737
- if (ec) {
738
- queueMicrotask(() => focusEditorInput(focusToken));
739
- }
740
- },
741
- [focusEditorInput, setEditingCellState, setEditingValueState]
742
- );
743
- const commitCellEditFn = useCallback(
744
- (direction, restoreFocus = true) => {
745
- const result = commitGridCellEditCommand(gridApiRef.current, {
746
- getEditingCell: () => editingCellRef.current,
747
- getEditingValue: () => editingValueRef.current,
748
- setEditingCell: setEditingCellState,
749
- setEditingValue: setEditingValueState,
750
- findRowById: (rowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId),
751
- findColumnByName: (columnName) => visibleColumnsRef.current.find((c) => c.name === columnName),
752
- parseEditedValue: (column, value, oldValue) => parseGridEditedValue(column, value, oldValue),
753
- setCellValue: (rowEntity, column, value) => {
754
- const fieldPath = column.editModelField ?? column.field ?? column.name;
755
- setPathValue(rowEntity, fieldPath, value);
74
+ changed = true;
75
+ }
76
+ }
77
+ if (changed) setSlots(nextSlots);
78
+ }, [options.data]);
79
+ function applyOptions(el, opts) {
80
+ const renderers2 = cellRenderersRef.current;
81
+ const cellSlotColumns = [];
82
+ if (renderers2 && opts.columnDefs) {
83
+ for (const col of opts.columnDefs) {
84
+ if (renderers2[col.name]) {
85
+ cellSlotColumns.push(col.name);
756
86
  }
757
- });
758
- if (!result.committed || !result.row || !result.column || !result.focusTarget) return;
759
- editorFocusTokenRef.current += 1;
760
- if (direction) {
761
- const moved = moveFocusFn(result.row, result.column, direction);
762
- if (!moved) focusRenderedCell(result.focusTarget);
763
- } else if (restoreFocus) {
764
- focusRenderedCell(result.focusTarget);
765
- }
766
- },
767
- [buildRowsFromData, focusRenderedCell, setEditingCellState, setEditingValueState]
768
- );
769
- const cancelCellEditFn = useCallback(() => {
770
- const hadEditingCell = editingCellRef.current !== null;
771
- const result = cancelGridCellEditCommand(gridApiRef.current, {
772
- getEditingCell: () => editingCellRef.current,
773
- setEditingCell: setEditingCellState,
774
- setEditingValue: setEditingValueState,
775
- findRowById: (rowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId),
776
- findColumnByName: (columnName) => visibleColumnsRef.current.find((c) => c.name === columnName)
777
- });
778
- if (!hadEditingCell) return;
779
- editorFocusTokenRef.current += 1;
780
- if (result.focusTarget) focusRenderedCell(result.focusTarget);
781
- }, [buildRowsFromData, focusRenderedCell, setEditingCellState, setEditingValueState]);
782
- const moveFocusFn = useCallback(
783
- (row, column, direction, triggerEvent) => {
784
- const nextCell = findNextGridCell({
785
- rows: pipelineRef.current.displayItems.filter((item) => item.kind === "row").map((item) => item.row),
786
- columns: visibleColumnsRef.current,
787
- rowId: row.id,
788
- columnName: column.name,
789
- direction
790
- });
791
- if (!nextCell) return false;
792
- setFocusedCell({ rowId: nextCell.row.id, columnName: nextCell.column.name });
793
- focusRenderedCell({ rowId: nextCell.row.id, columnName: nextCell.column.name });
794
- if (shouldEditOnFocusFn(nextCell.column) && isCellEditable(nextCell.row, nextCell.column, triggerEvent)) {
795
- startCellEditFn(nextCell.row, nextCell.column, triggerEvent);
796
87
  }
797
- return true;
798
- },
799
- [focusRenderedCell, isCellEditable, shouldEditOnFocusFn, startCellEditFn]
800
- );
801
- const runBenchmarkFn = useCallback((iterations) => {
802
- const safeIterations = resolveBenchmarkIterations(
803
- iterations,
804
- optionsRef.current.benchmark?.iterations
805
- );
806
- const startedAt = performance.now();
807
- let lastResult = defaultGridEngine.buildPipeline({
808
- options: optionsRef.current,
809
- columns: visibleColumnsRef.current,
810
- activeFilters: activeFiltersRef.current,
811
- sortState: sortStateRef.current,
812
- groupByColumns: groupByColumnsRef.current,
813
- collapsedGroups: collapsedGroupsRef.current,
814
- hiddenRowReasons: hiddenRowReasonsRef.current,
815
- expandedRows: expandedRowsRef.current,
816
- expandedTreeRows: expandedTreeRowsRef.current,
817
- currentPage: currentPageRef.current,
818
- pageSize: pageSizeRef.current,
819
- rowSize: optionsRef.current.rowHeight ?? 44
820
- });
821
- for (let i = 1; i < safeIterations; i++) {
822
- lastResult = defaultGridEngine.buildPipeline({
823
- options: optionsRef.current,
824
- columns: visibleColumnsRef.current,
825
- activeFilters: activeFiltersRef.current,
826
- sortState: sortStateRef.current,
827
- groupByColumns: groupByColumnsRef.current,
828
- collapsedGroups: collapsedGroupsRef.current,
829
- hiddenRowReasons: hiddenRowReasonsRef.current,
830
- expandedRows: expandedRowsRef.current,
831
- expandedTreeRows: expandedTreeRowsRef.current,
832
- currentPage: currentPageRef.current,
833
- pageSize: pageSizeRef.current,
834
- rowSize: optionsRef.current.rowHeight ?? 44
835
- });
836
88
  }
837
- const totalMs = performance.now() - startedAt;
838
- const result = {
839
- iterations: safeIterations,
840
- totalMs,
841
- averageMs: totalMs / safeIterations,
842
- visibleRows: lastResult.visibleRows.length,
843
- renderedItems: lastResult.displayItems.length
844
- };
845
- setBenchmarkResult(result);
846
- raiseGridBenchmarkComplete(gridApiRef.current, result);
847
- return result;
848
- }, []);
849
- const exportCsvFn = useCallback(() => {
850
- if (!FEATURE_CSV_EXPORT) return;
851
- const columns = visibleColumnsRef.current;
852
- const csv = exportCsvRows(columns, pipelineRef.current.visibleRows);
853
- downloadGridCsvFile(csv, `${sanitizeDownloadFilename(optionsRef.current.id)}.csv`);
854
- }, []);
855
- useEffect(() => {
856
- if (initializedGridIdRef.current === options.id) return;
857
- initializedGridIdRef.current = options.id;
858
- setActiveFilters({});
859
- setHiddenRowReasons({});
860
- setCollapsedGroups({});
861
- setFocusedCell(null);
862
- setEditingCellState(null);
863
- setEditingValueState("");
864
- setExpandedRows({});
865
- setExpandedTreeRows({});
866
- setColumnOrder(options.columnDefs.map((column) => column.name));
867
- setGroupByColumns(options.grouping?.groupBy ?? []);
868
- setPinnedColumns(buildInitialPinnedState(options.columnDefs));
869
- setCurrentPage(options.paginationCurrentPage ?? 1);
870
- setPageSize(coreGetEffectivePageSize(options, 0, options.data.length));
871
- setInfiniteScrollState({
872
- scrollUp: options.infiniteScrollUp === true,
873
- scrollDown: options.infiniteScrollDown !== false,
874
- dataLoading: false,
875
- previousVisibleRows: 0
876
- });
877
- const initialSort = options.columnDefs.find(
878
- (column) => column.sort?.direction && !column.sort.ignoreSort
879
- );
880
- setSortState({
881
- columnName: initialSort?.name ?? null,
882
- direction: initialSort?.sort?.direction ?? SORT_DIRECTIONS.none
883
- });
884
- onRegisterApi?.(gridApi);
885
- raiseGridRenderingComplete(gridApi);
886
- }, [options.id]);
887
- useEffect(() => {
888
- raiseGridRowsRendered(gridApi, pipeline.visibleRows);
889
- raiseGridRowsVisibleChanged(gridApi, pipeline.visibleRows);
890
- const newHeight = pipeline.displayItems.length * rowSize;
891
- if (newHeight !== lastCanvasHeightRef.current) {
892
- raiseGridCanvasHeightChanged(gridApi, lastCanvasHeightRef.current, newHeight);
893
- lastCanvasHeightRef.current = newHeight;
894
- }
895
- }, [pipeline, gridApi, rowSize]);
896
- useEffect(() => {
897
- if (!FEATURE_AUTO_RESIZE) return;
898
- const container = gridContainerRef.current;
899
- if (!container) return;
900
- const observer = observeGridHostSize(container, ({ height: nextHeight, width: nextWidth }) => {
901
- if (nextHeight === lastGridHeightRef.current && nextWidth === lastGridWidthRef.current)
902
- return;
903
- raiseGridDimensionChanged(
904
- gridApi,
905
- lastGridHeightRef.current,
906
- lastGridWidthRef.current,
907
- nextHeight,
908
- nextWidth
909
- );
910
- lastGridHeightRef.current = nextHeight;
911
- lastGridWidthRef.current = nextWidth;
912
- if (!options.viewportHeight && nextHeight > 0) {
913
- setAutoViewportHeight(nextHeight);
89
+ const wrappedOptions = {
90
+ ...opts,
91
+ onRegisterApi: (api) => {
92
+ onRegisterApiRef.current?.(api);
93
+ opts.onRegisterApi?.(api);
914
94
  }
915
- });
916
- if (!observer) return;
917
- return () => observer.disconnect();
918
- }, [options.enableAutoResize, options.viewportHeight, gridApi]);
919
- const totalRows = pipeline.totalItems;
920
- const visibleRowCount = pipeline.visibleRows.length;
921
- const displayItems = pipeline.displayItems;
922
- const virtualizationEnabled = pipeline.virtualizationEnabled;
923
- const pipelineMsVal = pipeline.pipelineMs;
924
- const paginationCurrentPage = getCurrentPageValueFn();
925
- const paginationTotalPages = getTotalPagesValueFn();
926
- const paginationSelectedPageSize = effectivePageSizeFn(pipeline.totalItems);
927
- const viewportHeightPx = computeViewportHeightPx(options.viewportHeight, autoViewportHeight);
928
- const headerLabelFn = useCallback((column) => coreHeaderLabel(column), []);
929
- const isGroupItemFn = useCallback(
930
- (item) => item.kind === "group",
931
- []
932
- );
933
- const isExpandableItemFn = useCallback(
934
- (item) => item.kind === "expandable",
935
- []
936
- );
937
- const isRowItemFn = useCallback((item) => item.kind === "row", []);
938
- const isOddStripedRowFn = useCallback(
939
- (item) => item.kind === "row" && item.visibleIndex % 2 === 0,
940
- []
941
- );
942
- const sortDirectionFn = useCallback((column) => {
943
- return sortStateRef.current.columnName === column.name ? sortStateRef.current.direction : SORT_DIRECTIONS.none;
944
- }, []);
945
- const sortButtonLabelFn = useCallback(
946
- (column) => {
947
- return gridSortButtonLabel(sortDirectionFn(column), labels);
948
- },
949
- [labels, sortDirectionFn]
950
- );
951
- const sortAriaSortFn = useCallback(
952
- (column) => {
953
- return gridSortAriaSort(sortDirectionFn(column));
954
- },
955
- [sortDirectionFn]
956
- );
957
- const groupingButtonLabelFn = useCallback(
958
- (column) => {
959
- return gridGroupingButtonLabel(
960
- isGridColumnGrouped(groupByColumnsRef.current, column),
961
- labels
962
- );
963
- },
964
- [labels]
965
- );
966
- const filterValueFn = useCallback((columnName) => {
967
- return activeFiltersRef.current[columnName] ?? "";
968
- }, []);
969
- const filterPlaceholderFn = useCallback(
970
- (column) => {
971
- return gridFilterPlaceholder(isGridColumnFilterable(optionsRef.current, column), labels);
972
- },
973
- [labels]
974
- );
975
- const isFilterInputDisabledFn = useCallback((column) => {
976
- return !isGridColumnFilterable(optionsRef.current, column);
977
- }, []);
978
- const groupDisclosureLabelFn = useCallback(
979
- (item) => {
980
- return gridGroupDisclosureLabel(item.collapsed, labels);
981
- },
982
- [labels]
983
- );
984
- const cellContextFn = useCallback(
985
- (row, column) => {
986
- return buildGridCellContext(row, column);
987
- },
988
- []
989
- );
990
- const displayValueFn = useCallback(
991
- (row, column) => {
992
- return formatGridCellDisplayValue(cellContextFn(row, column));
993
- },
994
- [cellContextFn]
995
- );
996
- const isFocusedCellFn = useCallback((row, column) => {
997
- return isGridCellPosition(focusedCellRef.current, row.id, column.name);
998
- }, []);
999
- const isFocusedRowFn = useCallback((row) => {
1000
- return focusedCellRef.current?.rowId === row.id || editingCellRef.current?.rowId === row.id;
1001
- }, []);
1002
- const isEditingCellFn = useCallback((row, column) => {
1003
- return isGridCellPosition(editingCellRef.current, row.id, column.name);
1004
- }, []);
1005
- const editorInputTypeFn = useCallback((column) => {
1006
- return gridEditorInputType(column);
1007
- }, []);
1008
- const expandedContextFn = useCallback(
1009
- (row) => {
1010
- return {
1011
- $implicit: row.entity,
1012
- row: row.entity,
1013
- rowIndex: row.index,
1014
- expanded: true,
1015
- ...optionsRef.current.expandableRowScope ?? {}
1016
- };
1017
- },
1018
- []
1019
- );
1020
- const columnWidthFn = useCallback((column) => gridColumnWidth2(column), []);
1021
- const isColumnSortableFn = useCallback((column) => {
1022
- return isGridColumnSortable(optionsRef.current, column);
1023
- }, []);
1024
- const isColumnFilterableFn = useCallback((column) => {
1025
- return isGridColumnFilterable(optionsRef.current, column);
1026
- }, []);
1027
- const cellIndentFn = useCallback((row, column) => {
1028
- return gridCellIndent(optionsRef.current, visibleColumnsRef.current, row, column);
1029
- }, []);
1030
- const treeToggleLabelFn = useCallback(
1031
- (row) => {
1032
- return gridTreeToggleLabelForRow(expandedTreeRowsRef.current, row, labels);
1033
- },
1034
- [labels]
1035
- );
1036
- const isTreeRowExpandedFn = useCallback((row) => {
1037
- return isGridTreeRowExpanded(expandedTreeRowsRef.current, row);
1038
- }, []);
1039
- const expandToggleLabelFn = useCallback(
1040
- (row) => {
1041
- return gridExpandToggleLabelForRow(row, labels);
1042
- },
1043
- [labels]
1044
- );
1045
- const isGroupedFn = useCallback((column) => {
1046
- return isGridColumnGrouped(groupByColumnsRef.current, column);
1047
- }, []);
1048
- const showTreeToggleFn = useCallback((row, column) => {
1049
- return shouldShowGridTreeToggle(optionsRef.current, visibleColumnsRef.current, row, column);
1050
- }, []);
1051
- const showExpandToggleFn = useCallback((row, column) => {
1052
- return shouldShowGridExpandToggle(optionsRef.current, visibleColumnsRef.current, column);
1053
- }, []);
1054
- const showPaginationControlsFn = useCallback(() => {
1055
- return FEATURE_PAGINATION && shouldShowGridPaginationControls(optionsRef.current);
1056
- }, []);
1057
- const paginationSummaryFn = useCallback(() => {
1058
- const ti = pipelineRef.current.totalItems;
1059
- return formatPaginationSummary(ti, getFirstRowIndexValueFn(ti), getLastRowIndexValueFn(ti));
1060
- }, [getFirstRowIndexValueFn, getLastRowIndexValueFn]);
1061
- const pageSizeOptionsFn = useCallback(() => {
1062
- return optionsRef.current.paginationPageSizes ?? [];
1063
- }, []);
1064
- const isGroupingEnabledFn = useCallback(() => {
1065
- return FEATURE_GROUPING && isGridGroupingEnabled(optionsRef.current);
1066
- }, []);
1067
- const isFilteringEnabledFn = useCallback(() => {
1068
- return FEATURE_FILTERING && isGridFilteringEnabled(optionsRef.current);
1069
- }, []);
1070
- const toggleSortFn = useCallback((column) => {
1071
- if (!FEATURE_SORTING || !isGridColumnSortable(optionsRef.current, column)) return;
1072
- const currentDirection = sortStateRef.current.columnName === column.name ? sortStateRef.current.direction : SORT_DIRECTIONS.none;
1073
- const nextDirection = currentDirection === SORT_DIRECTIONS.none ? SORT_DIRECTIONS.asc : currentDirection === SORT_DIRECTIONS.asc ? SORT_DIRECTIONS.desc : SORT_DIRECTIONS.none;
1074
- applyGridSortStateCommand(gridApiRef.current, (state) => setSortState(state), {
1075
- columnName: nextDirection === SORT_DIRECTIONS.none ? null : column.name,
1076
- direction: nextDirection
1077
- });
1078
- }, []);
1079
- const updateFilterFn = useCallback((columnName, value) => {
1080
- updateGridFilterCommand(
1081
- gridApiRef.current,
1082
- (updater) => setActiveFilters((current) => updater(current)),
1083
- () => activeFiltersRef.current,
1084
- columnName,
1085
- value
1086
- );
1087
- }, []);
1088
- const clearAllFiltersFn = useCallback(() => {
1089
- clearGridFiltersCommand(gridApiRef.current, (filters) => setActiveFilters(filters));
1090
- }, []);
1091
- const toggleGroupingFn = useCallback((column, event) => {
1092
- event?.stopPropagation();
1093
- if (!(FEATURE_GROUPING && isGridGroupingEnabled(optionsRef.current))) return;
1094
- const current = groupByColumnsRef.current;
1095
- const next = current.includes(column.name) ? current.filter((n) => n !== column.name) : [...current, column.name];
1096
- groupByColumnsRef.current = next;
1097
- setGroupByColumns(next);
1098
- gridApiRef.current.core.raise.groupingChanged(next);
1099
- }, []);
1100
- const toggleGroupFn = useCallback((item) => {
1101
- setCollapsedGroups((current) => ({
1102
- ...current,
1103
- [item.id]: !current[item.id]
1104
- }));
1105
- }, []);
1106
- const focusCellFn = useCallback(
1107
- (row, column, triggerEvent) => {
1108
- const nextFocusResult = buildGridFocusCellResult({
1109
- currentFocusedCell: focusedCellRef.current,
1110
- currentEditingCell: editingCellRef.current,
1111
- rowId: row.id,
1112
- columnName: column.name,
1113
- shouldEditOnFocus: shouldEditOnFocusFn(column),
1114
- isCellEditable: isCellEditable(row, column, triggerEvent)
1115
- });
1116
- setFocusedCell(nextFocusResult.focusedCell);
1117
- if (nextFocusResult.shouldBeginEdit) {
1118
- startCellEditFn(row, column, triggerEvent);
1119
- }
1120
- },
1121
- [isCellEditable, shouldEditOnFocusFn, startCellEditFn]
1122
- );
1123
- const handleCellKeyDownFn = useCallback(
1124
- (row, column, event) => {
1125
- if (isGridNavigationKey(event.key)) {
1126
- setFocusedCell({ rowId: row.id, columnName: column.name });
1127
- } else {
1128
- focusCellFn(row, column, event.nativeEvent);
1129
- }
1130
- switch (event.key) {
1131
- case "ArrowLeft":
1132
- event.preventDefault();
1133
- event.stopPropagation();
1134
- moveFocusFn(row, column, "left", event.nativeEvent);
1135
- return;
1136
- case "ArrowRight":
1137
- event.preventDefault();
1138
- event.stopPropagation();
1139
- moveFocusFn(row, column, "right", event.nativeEvent);
1140
- return;
1141
- case "ArrowUp":
1142
- event.preventDefault();
1143
- event.stopPropagation();
1144
- moveFocusFn(row, column, "up", event.nativeEvent);
1145
- return;
1146
- case "ArrowDown":
1147
- event.preventDefault();
1148
- event.stopPropagation();
1149
- moveFocusFn(row, column, "down", event.nativeEvent);
1150
- return;
1151
- case "Tab":
1152
- event.preventDefault();
1153
- event.stopPropagation();
1154
- moveFocusFn(row, column, event.shiftKey ? "left" : "right", event.nativeEvent);
1155
- return;
1156
- case "Enter":
1157
- event.preventDefault();
1158
- event.stopPropagation();
1159
- moveFocusFn(row, column, event.shiftKey ? "up" : "down", event.nativeEvent);
1160
- return;
1161
- case "F2":
1162
- event.preventDefault();
1163
- event.stopPropagation();
1164
- if (isCellEditable(row, column, event.nativeEvent)) {
1165
- startCellEditFn(row, column, event.nativeEvent);
1166
- }
1167
- return;
1168
- case "Backspace":
1169
- case "Delete":
1170
- if (isCellEditable(row, column, event.nativeEvent)) {
1171
- event.preventDefault();
1172
- event.stopPropagation();
1173
- startCellEditFn(row, column, event.nativeEvent, "");
1174
- }
1175
- return;
1176
- default:
1177
- break;
1178
- }
1179
- if (isPrintableGridKey(event.key, event.ctrlKey, event.metaKey, event.altKey) && isCellEditable(row, column, event.nativeEvent)) {
1180
- event.preventDefault();
1181
- event.stopPropagation();
1182
- startCellEditFn(row, column, event.nativeEvent, event.key);
1183
- }
1184
- },
1185
- [focusCellFn, moveFocusFn, isCellEditable, startCellEditFn]
1186
- );
1187
- const handleCellDoubleClickFn = useCallback(
1188
- (row, column, event) => {
1189
- focusCellFn(row, column, event.nativeEvent);
1190
- if (isCellEditable(row, column, event.nativeEvent)) {
1191
- startCellEditFn(row, column, event.nativeEvent);
1192
- }
1193
- },
1194
- [focusCellFn, isCellEditable, startCellEditFn]
1195
- );
1196
- const updateEditingValueFn = useCallback((value) => {
1197
- setEditingValueState(value);
1198
- }, [setEditingValueState]);
1199
- const handleEditorKeyDownFn = useCallback(
1200
- (event) => {
1201
- if (event.key === "Escape") {
1202
- event.preventDefault();
1203
- event.stopPropagation();
1204
- cancelCellEditFn();
1205
- return;
1206
- }
1207
- if (event.key === "Enter") {
1208
- event.preventDefault();
1209
- event.stopPropagation();
1210
- commitCellEditFn(event.shiftKey ? "up" : "down");
1211
- return;
1212
- }
1213
- if (event.key === "Tab") {
1214
- event.preventDefault();
1215
- event.stopPropagation();
1216
- commitCellEditFn(event.shiftKey ? "left" : "right");
1217
- return;
1218
- }
1219
- if (event.key === "ArrowUp" || event.key === "ArrowDown") {
1220
- event.preventDefault();
1221
- event.stopPropagation();
1222
- commitCellEditFn(event.key === "ArrowUp" ? "up" : "down");
1223
- }
1224
- },
1225
- [cancelCellEditFn, commitCellEditFn]
1226
- );
1227
- const handleEditorBlurFn = useCallback(
1228
- (event) => {
1229
- const ec = editingCellRef.current;
1230
- const target = event.target;
1231
- if (!ec || !target) return;
1232
- if (target.dataset["rowId"] !== ec.rowId || target.dataset["colName"] !== ec.columnName)
1233
- return;
1234
- commitCellEditFn(void 0, false);
1235
- },
1236
- [commitCellEditFn]
1237
- );
1238
- const toggleRowExpansionFn = useCallback(
1239
- (row, event) => {
1240
- event?.stopPropagation();
1241
- toggleRowExpansionByRefFn(row);
1242
- },
1243
- [toggleRowExpansionByRefFn]
1244
- );
1245
- const toggleTreeRowFn = useCallback(
1246
- (row, event) => {
1247
- event?.stopPropagation();
1248
- toggleTreeRowByRefFn(row);
1249
- },
1250
- [toggleTreeRowByRefFn]
1251
- );
1252
- const moveColumnFn = useCallback((fromIndex, toIndex) => {
1253
- moveGridColumnCommand(
1254
- gridApiRef.current,
1255
- FEATURE_COLUMN_MOVING && optionsRef.current.enableColumnMoving === true,
1256
- (updater) => setColumnOrder((current) => updater(current)),
1257
- fromIndex,
1258
- toIndex
1259
- );
1260
- }, []);
1261
- const moveVisibleColumnFn = useCallback((columnName, targetColumnName) => {
1262
- moveGridVisibleColumnCommand(
1263
- gridApiRef.current,
1264
- FEATURE_COLUMN_MOVING && optionsRef.current.enableColumnMoving === true,
1265
- columnOrderRef.current,
1266
- visibleColumnsRef.current.map((column) => column.name),
1267
- columnName,
1268
- targetColumnName,
1269
- (order) => setColumnOrder(order)
1270
- );
1271
- }, []);
1272
- const nextPageFn = useCallback(() => {
1273
- seekPageFn(getCurrentPageValueFn() + 1);
1274
- }, [seekPageFn, getCurrentPageValueFn]);
1275
- const previousPageFn = useCallback(() => {
1276
- seekPageFn(getCurrentPageValueFn() - 1);
1277
- }, [seekPageFn, getCurrentPageValueFn]);
1278
- const onPageSizeChangeFn = useCallback(
1279
- (value) => {
1280
- setPaginationPageSizeFn(Number(value));
1281
- },
1282
- [setPaginationPageSizeFn]
1283
- );
1284
- const canResizeColumnsFn = useCallback(() => {
1285
- return optionsRef.current.enableColumnResizing !== false;
1286
- }, []);
1287
- const setColumnWidthOverrideFn = useCallback((columnName, widthPx) => {
1288
- const nextWidth = `${Math.max(88, Math.round(widthPx))}px`;
1289
- setColumnWidthOverrides((current) => ({ ...current, [columnName]: nextWidth }));
1290
- }, []);
1291
- const measureAutoColumnWidthFn = useCallback((columnName) => {
1292
- const container = gridContainerRef.current;
1293
- if (container == null) return 176;
1294
- const escaped = CSS.escape ? CSS.escape(columnName) : columnName.replace(/([\\".#:[\](){}+~> ])/g, "\\$1");
1295
- const selectors = [
1296
- `.header-cell[data-col-name="${escaped}"]`,
1297
- `.filter-cell[data-col-name="${escaped}"]`,
1298
- `.body-cell[data-col-name="${escaped}"] .cell-shell`
1299
- ];
1300
- let maxWidth = 0;
1301
- for (const selector of selectors) {
1302
- const elements = container.querySelectorAll(selector);
1303
- for (const element of elements) {
1304
- maxWidth = Math.max(maxWidth, element.scrollWidth);
1305
- }
1306
- }
1307
- return maxWidth + 12;
1308
- }, []);
1309
- const handleHeaderResizeMouseDownFn = useCallback(
1310
- (column, event) => {
1311
- if (!canResizeColumnsFn()) return;
1312
- event.preventDefault();
1313
- event.stopPropagation();
1314
- const headerCell = event.currentTarget.closest(".header-cell");
1315
- if (headerCell == null) return;
1316
- const startX = event.clientX;
1317
- const startWidth = headerCell.getBoundingClientRect().width;
1318
- let lastWidth = startWidth;
1319
- const handleMove = (moveEvent) => {
1320
- lastWidth = Math.max(88, startWidth + (moveEvent.clientX - startX));
1321
- const widthStr = `${Math.round(lastWidth)}px`;
1322
- const newTemplate = buildGridTemplateColumns(
1323
- visibleColumnsRef.current.map(
1324
- (c) => c.name === column.name ? { ...c, width: widthStr } : c
1325
- )
1326
- );
1327
- gridContainerRef.current?.querySelectorAll(".header-grid, .filter-grid, .body-grid").forEach((el) => {
1328
- el.style.gridTemplateColumns = newTemplate;
1329
- });
1330
- };
1331
- const handleUp = () => {
1332
- window.removeEventListener("mousemove", handleMove);
1333
- window.removeEventListener("mouseup", handleUp);
1334
- setColumnWidthOverrideFn(column.name, lastWidth);
1335
- };
1336
- window.addEventListener("mousemove", handleMove);
1337
- window.addEventListener("mouseup", handleUp);
1338
- },
1339
- [canResizeColumnsFn, setColumnWidthOverrideFn]
1340
- );
1341
- const autoSizeColumnFn = useCallback(
1342
- (column, event) => {
1343
- if (!canResizeColumnsFn()) return;
1344
- event.preventDefault();
1345
- event.stopPropagation();
1346
- setColumnWidthOverrideFn(column.name, measureAutoColumnWidthFn(column.name));
1347
- },
1348
- [canResizeColumnsFn, setColumnWidthOverrideFn, measureAutoColumnWidthFn]
1349
- );
1350
- const onViewportScrollFn = useCallback((startIndex) => {
1351
- if (!scrollingRef.current) {
1352
- scrollingRef.current = true;
1353
- raiseGridScrollBegin(gridApiRef.current);
1354
- }
1355
- if (scrollEndHandleRef.current) {
1356
- window.clearTimeout(scrollEndHandleRef.current);
1357
- }
1358
- scrollEndHandleRef.current = window.setTimeout(() => {
1359
- scrollingRef.current = false;
1360
- raiseGridScrollEnd(gridApiRef.current);
1361
- }, 120);
1362
- const isInfiniteScrollEnabled = FEATURE_INFINITE_SCROLL && (optionsRef.current.infiniteScrollRowsFromEnd !== void 0 || optionsRef.current.infiniteScrollUp === true || optionsRef.current.infiniteScrollDown !== void 0);
1363
- maybeRequestInfiniteScrollCommand(gridApiRef.current, {
1364
- enabled: isInfiniteScrollEnabled,
1365
- virtualizationEnabled: pipelineRef.current.virtualizationEnabled,
1366
- state: infiniteScrollStateRef.current,
1367
- startIndex,
1368
- visibleRows: pipelineRef.current.visibleRows.length,
1369
- viewportRows: computeViewportRows(
1370
- optionsRef.current.viewportHeight,
1371
- optionsRef.current.rowHeight
1372
- ),
1373
- threshold: optionsRef.current.infiniteScrollRowsFromEnd ?? 20,
1374
- setState: (state) => setInfiniteScrollState(state)
1375
- });
1376
- }, []);
1377
- return {
1378
- pipeline,
1379
- visibleColumns,
1380
- labels,
1381
- gridTemplateColumns,
1382
- gridApi,
1383
- gridContainerRef,
1384
- activeFilters,
1385
- groupByColumns,
1386
- collapsedGroups,
1387
- sortState,
1388
- focusedCell,
1389
- editingCell,
1390
- editingValue,
1391
- expandedRows,
1392
- expandedTreeRows,
1393
- currentPage,
1394
- pageSize,
1395
- benchmarkResult,
1396
- infiniteScrollState,
1397
- totalRows,
1398
- visibleRowCount,
1399
- displayItems,
1400
- virtualizationEnabled,
1401
- pipelineMs: pipelineMsVal,
1402
- paginationCurrentPage,
1403
- paginationTotalPages,
1404
- paginationSelectedPageSize,
1405
- rowSize,
1406
- viewportHeightPx,
1407
- autoViewportHeight,
1408
- headerLabel: headerLabelFn,
1409
- isGroupItem: isGroupItemFn,
1410
- isExpandableItem: isExpandableItemFn,
1411
- isRowItem: isRowItemFn,
1412
- isOddStripedRow: isOddStripedRowFn,
1413
- sortButtonLabel: sortButtonLabelFn,
1414
- sortAriaSort: sortAriaSortFn,
1415
- sortDirection: sortDirectionFn,
1416
- groupingButtonLabel: groupingButtonLabelFn,
1417
- filterValue: filterValueFn,
1418
- filterPlaceholder: filterPlaceholderFn,
1419
- isFilterInputDisabled: isFilterInputDisabledFn,
1420
- groupDisclosureLabel: groupDisclosureLabelFn,
1421
- displayValue: displayValueFn,
1422
- isFocusedCell: isFocusedCellFn,
1423
- isFocusedRow: isFocusedRowFn,
1424
- isEditingCell: isEditingCellFn,
1425
- editorInputType: editorInputTypeFn,
1426
- cellContext: cellContextFn,
1427
- expandedContext: expandedContextFn,
1428
- columnWidth: columnWidthFn,
1429
- isColumnSortable: isColumnSortableFn,
1430
- isColumnFilterable: isColumnFilterableFn,
1431
- cellIndent: cellIndentFn,
1432
- treeToggleLabel: treeToggleLabelFn,
1433
- isTreeRowExpanded: isTreeRowExpandedFn,
1434
- expandToggleLabel: expandToggleLabelFn,
1435
- isGrouped: isGroupedFn,
1436
- showTreeToggle: showTreeToggleFn,
1437
- showExpandToggle: showExpandToggleFn,
1438
- showPaginationControls: showPaginationControlsFn,
1439
- paginationSummary: paginationSummaryFn,
1440
- pageSizeOptions: pageSizeOptionsFn,
1441
- isCellEditable,
1442
- shouldEditOnFocus: shouldEditOnFocusFn,
1443
- sortingFeature: FEATURE_SORTING,
1444
- filteringFeature: FEATURE_FILTERING,
1445
- groupingFeature: FEATURE_GROUPING,
1446
- paginationFeature: FEATURE_PAGINATION,
1447
- cellEditFeature: FEATURE_CELL_EDIT,
1448
- expandableFeature: FEATURE_EXPANDABLE,
1449
- treeViewFeature: FEATURE_TREE_VIEW,
1450
- infiniteScrollFeature: FEATURE_INFINITE_SCROLL,
1451
- columnMovingFeature: FEATURE_COLUMN_MOVING,
1452
- csvExportFeature: FEATURE_CSV_EXPORT,
1453
- isGroupingEnabled: isGroupingEnabledFn,
1454
- isFilteringEnabled: isFilteringEnabledFn,
1455
- toggleSort: toggleSortFn,
1456
- updateFilter: updateFilterFn,
1457
- clearAllFilters: clearAllFiltersFn,
1458
- toggleGrouping: toggleGroupingFn,
1459
- toggleGroup: toggleGroupFn,
1460
- focusCell: focusCellFn,
1461
- handleCellKeyDown: handleCellKeyDownFn,
1462
- handleCellDoubleClick: handleCellDoubleClickFn,
1463
- updateEditingValue: updateEditingValueFn,
1464
- handleEditorKeyDown: handleEditorKeyDownFn,
1465
- handleEditorBlur: handleEditorBlurFn,
1466
- toggleRowExpansion: toggleRowExpansionFn,
1467
- toggleTreeRow: toggleTreeRowFn,
1468
- moveColumn: moveColumnFn,
1469
- moveVisibleColumn: moveVisibleColumnFn,
1470
- canResizeColumns: canResizeColumnsFn,
1471
- handleHeaderResizeMouseDown: handleHeaderResizeMouseDownFn,
1472
- autoSizeColumn: autoSizeColumnFn,
1473
- nextPage: nextPageFn,
1474
- previousPage: previousPageFn,
1475
- onPageSizeChange: onPageSizeChangeFn,
1476
- runBenchmark: runBenchmarkFn,
1477
- exportCsv: exportCsvFn,
1478
- onViewportScroll: onViewportScrollFn,
1479
- // Pinning
1480
- isPinned: isPinnedFn,
1481
- pinnedOffset: pinnedOffsetFn,
1482
- isPinningEnabled: isPinningEnabledFn,
1483
- isColumnPinnable: isColumnPinnableFn,
1484
- togglePin: togglePinFn,
1485
- pinningFeature: FEATURE_PINNING
1486
- };
1487
- }
1488
-
1489
- // src/useVirtualScroll.ts
1490
- import { useCallback as useCallback2, useRef as useRef2, useState as useState2 } from "react";
1491
-
1492
- // src/virtualScrollMath.ts
1493
- function calculateVirtualWindow(request) {
1494
- const overscan = request.overscan ?? 3;
1495
- if (request.itemCount <= 0 || request.itemSize <= 0) {
1496
- return {
1497
- visibleRange: { start: 0, end: 0 },
1498
- totalHeight: Math.max(0, request.itemCount) * Math.max(0, request.itemSize),
1499
- offsetY: 0
1500
95
  };
1501
- }
1502
- const rawStart = Math.floor(request.scrollTop / request.itemSize) - overscan;
1503
- const start = Math.max(0, rawStart);
1504
- const rawEnd = rawStart + Math.ceil(request.viewportHeight / request.itemSize) + 2 * overscan;
1505
- const end = Math.min(request.itemCount, rawEnd);
1506
- return {
1507
- visibleRange: { start, end },
1508
- totalHeight: request.itemCount * request.itemSize,
1509
- offsetY: start * request.itemSize
1510
- };
1511
- }
1512
-
1513
- // src/useVirtualScroll.ts
1514
- function useVirtualScroll(options) {
1515
- const { itemCount, itemSize, viewportHeight, overscan = 3 } = options;
1516
- const [scrollTop, setScrollTop] = useState2(0);
1517
- const viewportRef = useRef2(null);
1518
- const virtualWindow = calculateVirtualWindow({
1519
- itemCount,
1520
- itemSize,
1521
- viewportHeight,
1522
- overscan,
1523
- scrollTop
1524
- });
1525
- const onScroll = useCallback2((event) => {
1526
- setScrollTop(event.currentTarget.scrollTop);
1527
- }, []);
1528
- return {
1529
- visibleRange: virtualWindow.visibleRange,
1530
- totalHeight: virtualWindow.totalHeight,
1531
- offsetY: virtualWindow.offsetY,
1532
- onScroll,
1533
- setScrollTop,
1534
- viewportRef,
1535
- scrollTop
1536
- };
1537
- }
1538
-
1539
- // src/UiGrid.tsx
1540
- import { jsx, jsxs } from "react/jsx-runtime";
1541
- function UiGrid({
1542
- options,
1543
- onRegisterApi,
1544
- cellRenderer,
1545
- headerRenderer,
1546
- expandableRenderer,
1547
- className
1548
- }) {
1549
- const state = useGridState(options, onRegisterApi);
1550
- const {
1551
- pipeline,
1552
- visibleColumns,
1553
- labels,
1554
- gridTemplateColumns,
1555
- gridContainerRef,
1556
- displayItems,
1557
- virtualizationEnabled,
1558
- rowSize,
1559
- editingValue,
1560
- autoViewportHeight,
1561
- sortingFeature,
1562
- filteringFeature,
1563
- groupingFeature,
1564
- paginationFeature,
1565
- cellEditFeature,
1566
- expandableFeature,
1567
- treeViewFeature,
1568
- columnMovingFeature,
1569
- paginationCurrentPage,
1570
- paginationTotalPages,
1571
- paginationSelectedPageSize
1572
- } = state;
1573
- const headerGridRef = React.useRef(null);
1574
- const filterGridRef = React.useRef(null);
1575
- const [headerStickyHeight, setHeaderStickyHeight] = React.useState(0);
1576
- const [filterStickyHeight, setFilterStickyHeight] = React.useState(0);
1577
- const stickyChromeHeight = headerStickyHeight + filterStickyHeight;
1578
- const resolvedViewportHeight = options.viewportHeight ?? (autoViewportHeight && autoViewportHeight > 0 ? autoViewportHeight : 560);
1579
- const bodyViewportHeight = Math.max(rowSize, resolvedViewportHeight - stickyChromeHeight);
1580
- const virtualScroll = useVirtualScroll({
1581
- itemCount: displayItems.length,
1582
- itemSize: rowSize,
1583
- viewportHeight: bodyViewportHeight,
1584
- overscan: 3
1585
- });
1586
- const [openPinMenuColumn, setOpenPinMenuColumn] = React.useState(null);
1587
- const [draggedColumnName, setDraggedColumnName] = React.useState(null);
1588
- const [dropTargetColumnName, setDropTargetColumnName] = React.useState(null);
1589
- const scrollContainerHeight = `${resolvedViewportHeight}px`;
1590
- function renderHeaderContent(column) {
1591
- const value = state.headerLabel(column);
1592
- const context = {
1593
- $implicit: value,
1594
- value,
1595
- column
1596
- };
1597
- if (headerRenderer) {
1598
- return headerRenderer(context) ?? value;
1599
- }
1600
- if (column.headerRenderer) {
1601
- return column.headerRenderer(context);
96
+ el.options = wrappedOptions;
97
+ const prev = currentSlotColumnsRef.current;
98
+ const columnsChanged = cellSlotColumns.length !== prev.length || cellSlotColumns.some((name, i) => name !== prev[i]);
99
+ if (columnsChanged) {
100
+ currentSlotColumnsRef.current = cellSlotColumns;
101
+ el.setFrameworkRenderedSlots({ cells: cellSlotColumns });
1602
102
  }
1603
- return value;
1604
103
  }
1605
- const eventPathIncludesClass = React.useCallback((event, className2) => {
1606
- const eventPath = typeof event.composedPath === "function" ? event.composedPath() : event.target ? [event.target] : [];
1607
- return eventPath.some((target) => {
1608
- if (!target || typeof target !== "object" || !("classList" in target)) {
1609
- return false;
1610
- }
1611
- const classList = target.classList;
1612
- return classList?.contains(className2) ?? false;
1613
- });
1614
- }, []);
1615
- const isPinMenuOpen = React.useCallback(
1616
- (column) => openPinMenuColumn === column.name,
1617
- [openPinMenuColumn]
1618
- );
1619
- const pinButtonLabel = React.useCallback(
1620
- (column) => state.isPinned(column) ? labels.unpin : labels.pinColumn,
1621
- [labels, state]
1622
- );
1623
- const onPinTrigger = React.useCallback(
1624
- (column, event) => {
1625
- event?.stopPropagation();
1626
- if (state.isPinned(column)) {
1627
- setOpenPinMenuColumn(null);
1628
- state.gridApi.pinning.pinColumn(column.name, "none");
1629
- return;
1630
- }
1631
- setOpenPinMenuColumn((current) => current === column.name ? null : column.name);
1632
- },
1633
- [state]
1634
- );
1635
- const choosePinDirection = React.useCallback(
1636
- (column, direction, event) => {
1637
- event?.stopPropagation();
1638
- setOpenPinMenuColumn(null);
1639
- state.gridApi.pinning.pinColumn(column.name, direction);
1640
- },
1641
- [state]
1642
- );
1643
- const handleHeaderDragStart = React.useCallback(
1644
- (column, event) => {
1645
- if (!columnMovingFeature) {
1646
- event.preventDefault();
1647
- return;
1648
- }
1649
- setDraggedColumnName(column.name);
1650
- setDropTargetColumnName(null);
1651
- event.dataTransfer.effectAllowed = "move";
1652
- event.dataTransfer.setData("text/plain", column.name);
1653
- },
1654
- [columnMovingFeature]
1655
- );
1656
- const handleHeaderDragOver = React.useCallback(
1657
- (column, event) => {
1658
- if (!columnMovingFeature || !draggedColumnName || draggedColumnName === column.name) {
1659
- return;
1660
- }
1661
- event.preventDefault();
1662
- event.dataTransfer.dropEffect = "move";
1663
- setDropTargetColumnName(column.name);
1664
- },
1665
- [columnMovingFeature, draggedColumnName]
1666
- );
1667
- const handleHeaderDrop = React.useCallback(
1668
- (column, event) => {
1669
- event.preventDefault();
1670
- if (!columnMovingFeature) {
1671
- return;
104
+ function handleCellSlotsChanged(event) {
105
+ const detail = event.detail;
106
+ const el = elementRef.current;
107
+ if (!el) return;
108
+ setSlots((prev) => {
109
+ const next = new Map(prev);
110
+ for (const slot of detail.removed) {
111
+ const entry = next.get(slot.slotName);
112
+ if (entry) {
113
+ entry.wrapper.remove();
114
+ next.delete(slot.slotName);
115
+ }
1672
116
  }
1673
- const sourceColumnName = draggedColumnName ?? event.dataTransfer.getData("text/plain");
1674
- setDraggedColumnName(null);
1675
- setDropTargetColumnName(null);
1676
- if (!sourceColumnName || sourceColumnName === column.name) {
1677
- return;
117
+ for (const slot of detail.added) {
118
+ const existing = next.get(slot.slotName);
119
+ if (existing) {
120
+ existing.wrapper.remove();
121
+ }
122
+ const wrapper = document.createElement("span");
123
+ wrapper.setAttribute("slot", slot.slotName);
124
+ el.appendChild(wrapper);
125
+ next.set(slot.slotName, {
126
+ slotName: slot.slotName,
127
+ columnName: slot.columnName,
128
+ rowId: slot.rowId,
129
+ context: slot.context,
130
+ wrapper
131
+ });
1678
132
  }
1679
- state.moveVisibleColumn(sourceColumnName, column.name);
1680
- },
1681
- [columnMovingFeature, draggedColumnName, state]
1682
- );
1683
- const handleHeaderDragEnd = React.useCallback(() => {
1684
- setDraggedColumnName(null);
1685
- setDropTargetColumnName(null);
1686
- }, []);
1687
- React.useLayoutEffect(() => {
1688
- setHeaderStickyHeight(headerGridRef.current?.offsetHeight ?? 0);
1689
- setFilterStickyHeight(filterGridRef.current?.offsetHeight ?? 0);
1690
- }, [visibleColumns, filteringFeature, options.enableFiltering]);
1691
- React.useLayoutEffect(() => {
1692
- const headerElement = headerGridRef.current;
1693
- const filterElement = filterGridRef.current;
1694
- if (typeof ResizeObserver === "undefined" || !headerElement && !filterElement) {
1695
- return;
1696
- }
1697
- const observer = new ResizeObserver(() => {
1698
- setHeaderStickyHeight(headerGridRef.current?.offsetHeight ?? 0);
1699
- setFilterStickyHeight(filterGridRef.current?.offsetHeight ?? 0);
133
+ return next;
1700
134
  });
1701
- if (headerElement) {
1702
- observer.observe(headerElement);
1703
- }
1704
- if (filterElement) {
1705
- observer.observe(filterElement);
1706
- }
1707
- return () => observer.disconnect();
1708
- }, []);
1709
- React.useEffect(() => {
1710
- if (!openPinMenuColumn) {
1711
- return;
1712
- }
1713
- const handleDocumentClick = (event) => {
1714
- if (eventPathIncludesClass(event, "pin-control")) {
1715
- return;
1716
- }
1717
- setOpenPinMenuColumn(null);
1718
- };
1719
- const handleDocumentEscape = (event) => {
1720
- if (event.key === "Escape") {
1721
- setOpenPinMenuColumn(null);
135
+ }
136
+ const portals = [];
137
+ const renderers = cellRenderers;
138
+ if (renderers) {
139
+ for (const [, entry] of slots) {
140
+ const renderer = renderers[entry.columnName];
141
+ if (renderer) {
142
+ portals.push(createPortal(renderer(entry.context), entry.wrapper, entry.slotName));
1722
143
  }
1723
- };
1724
- document.addEventListener("click", handleDocumentClick);
1725
- document.addEventListener("keydown", handleDocumentEscape);
1726
- return () => {
1727
- document.removeEventListener("click", handleDocumentClick);
1728
- document.removeEventListener("keydown", handleDocumentEscape);
1729
- };
1730
- }, [eventPathIncludesClass, openPinMenuColumn]);
1731
- const itemsToRender = virtualizationEnabled ? displayItems.slice(virtualScroll.visibleRange.start, virtualScroll.visibleRange.end) : displayItems;
1732
- const onGridTableScroll = (event) => {
1733
- const bodyScrollTop = Math.max(0, event.currentTarget.scrollTop - stickyChromeHeight);
1734
- virtualScroll.setScrollTop(bodyScrollTop);
1735
- const startIndex = Math.floor(bodyScrollTop / rowSize);
1736
- state.onViewportScroll(startIndex);
1737
- };
1738
- function renderDisplayItem(item) {
1739
- if (groupingFeature && state.isGroupItem(item)) {
1740
- return /* @__PURE__ */ jsxs(
1741
- "button",
1742
- {
1743
- type: "button",
1744
- className: "group-row ui-grid-row ui-grid-group-row",
1745
- "data-part": "group-row",
1746
- role: "row",
1747
- "aria-expanded": !item.collapsed,
1748
- style: { gridColumn: "1 / -1", paddingInlineStart: `${item.depth * 1.25 + 1}rem` },
1749
- onClick: () => state.toggleGroup(item),
1750
- children: [
1751
- /* @__PURE__ */ jsxs("strong", { children: [
1752
- item.field,
1753
- ": ",
1754
- item.label
1755
- ] }),
1756
- /* @__PURE__ */ jsxs("span", { children: [
1757
- item.count,
1758
- " ",
1759
- labels.groupRowsSuffix
1760
- ] }),
1761
- /* @__PURE__ */ jsx(
1762
- "svg",
1763
- {
1764
- className: "toggle-icon group-disclosure-icon",
1765
- viewBox: "0 0 24 24",
1766
- "aria-hidden": "true",
1767
- focusable: false,
1768
- children: /* @__PURE__ */ jsx("path", { d: item.collapsed ? "M10 7l5 5-5 5z" : "M7 10l5 5 5-5z" })
1769
- }
1770
- ),
1771
- /* @__PURE__ */ jsx("span", { className: "sr-only ui-grid-sr-only", children: state.groupDisclosureLabel(item) })
1772
- ]
1773
- },
1774
- item.id
1775
- );
1776
- }
1777
- if (expandableFeature && state.isExpandableItem(item)) {
1778
- const ctx = state.expandedContext(item.row);
1779
- return /* @__PURE__ */ jsx(
1780
- "div",
1781
- {
1782
- className: "expandable-row ui-grid-row ui-grid-expandable-row",
1783
- "data-part": "expandable-row",
1784
- style: { gridColumn: "1 / -1", minHeight: `${item.row.expandedRowHeight}px` },
1785
- children: expandableRenderer?.(ctx)
1786
- },
1787
- item.id
1788
- );
1789
144
  }
1790
- if (item.kind !== "row") return null;
1791
- const rowItem = item;
1792
- return visibleColumns.map((column) => {
1793
- const pinned = state.isPinned(column);
1794
- const pinOffset = pinned ? state.pinnedOffset(column) : null;
1795
- return /* @__PURE__ */ jsx(
1796
- "div",
1797
- {
1798
- className: `${cellClassName(rowItem, column)}${pinned ? " is-pinned" : ""}`,
1799
- "data-part": "body-cell",
1800
- role: "gridcell",
1801
- tabIndex: 0,
1802
- "data-row-id": rowItem.row.id,
1803
- "data-col-name": column.name,
1804
- onFocus: () => state.focusCell(rowItem.row, column),
1805
- onClick: () => state.focusCell(rowItem.row, column),
1806
- onDoubleClick: (e) => state.handleCellDoubleClick(rowItem.row, column, e),
1807
- onKeyDown: (e) => state.handleCellKeyDown(rowItem.row, column, e),
1808
- style: {
1809
- position: pinned ? "sticky" : void 0,
1810
- left: pinOffset?.side === "left" ? pinOffset.offset : void 0,
1811
- right: pinOffset?.side === "right" ? pinOffset.offset : void 0,
1812
- zIndex: pinned ? 2 : void 0
1813
- },
1814
- children: /* @__PURE__ */ jsxs(
1815
- "div",
1816
- {
1817
- className: "cell-shell",
1818
- style: { paddingInlineStart: state.cellIndent(rowItem.row, column) },
1819
- children: [
1820
- treeViewFeature && state.showTreeToggle(rowItem.row, column) && /* @__PURE__ */ jsx(
1821
- "button",
1822
- {
1823
- type: "button",
1824
- className: "row-toggle row-toggle-tree",
1825
- "data-part": "tree-toggle",
1826
- "aria-label": state.treeToggleLabel(rowItem.row),
1827
- "aria-expanded": state.isTreeRowExpanded(rowItem.row),
1828
- onClick: (e) => state.toggleTreeRow(rowItem.row, e),
1829
- children: /* @__PURE__ */ jsx(
1830
- "svg",
1831
- {
1832
- className: "toggle-icon",
1833
- viewBox: "0 0 24 24",
1834
- "aria-hidden": "true",
1835
- focusable: false,
1836
- children: /* @__PURE__ */ jsx(
1837
- "path",
1838
- {
1839
- d: state.isTreeRowExpanded(rowItem.row) ? "M7 10l5 5 5-5z" : "M10 7l5 5-5 5z"
1840
- }
1841
- )
1842
- }
1843
- )
1844
- }
1845
- ),
1846
- expandableFeature && state.showExpandToggle(rowItem.row, column) && /* @__PURE__ */ jsx(
1847
- "button",
1848
- {
1849
- type: "button",
1850
- className: "row-toggle row-toggle-expand",
1851
- "data-part": "expand-toggle",
1852
- "aria-label": state.expandToggleLabel(rowItem.row),
1853
- "aria-expanded": rowItem.row.expanded,
1854
- onClick: (e) => state.toggleRowExpansion(rowItem.row, e),
1855
- children: /* @__PURE__ */ jsx(
1856
- "svg",
1857
- {
1858
- className: "toggle-icon",
1859
- viewBox: "0 0 24 24",
1860
- "aria-hidden": "true",
1861
- focusable: false,
1862
- children: /* @__PURE__ */ jsx("path", { d: rowItem.row.expanded ? "M7 10l5 5 5-5z" : "M10 7l5 5-5 5z" })
1863
- }
1864
- )
1865
- }
1866
- ),
1867
- /* @__PURE__ */ jsx("span", { className: "cell-value", children: cellEditFeature && state.isEditingCell(rowItem.row, column) ? /* @__PURE__ */ jsx(
1868
- "input",
1869
- {
1870
- className: "cell-editor",
1871
- "data-row-id": rowItem.row.id,
1872
- "data-col-name": column.name,
1873
- "aria-label": state.headerLabel(column),
1874
- type: state.editorInputType(column),
1875
- defaultValue: editingValue,
1876
- onChange: (e) => state.updateEditingValue(e.target.value),
1877
- onKeyDown: (e) => state.handleEditorKeyDown(e),
1878
- onBlur: (e) => state.handleEditorBlur(e)
1879
- }
1880
- ) : cellRenderer ? cellRenderer(state.cellContext(rowItem.row, column)) ?? state.displayValue(rowItem.row, column) : state.displayValue(rowItem.row, column) })
1881
- ]
1882
- }
1883
- )
1884
- },
1885
- `${rowItem.row.id}-${column.name}`
1886
- );
1887
- });
1888
145
  }
1889
- function cellClassName(item, column) {
1890
- const classes = ["body-cell", "ui-grid-cell"];
1891
- if (state.isOddStripedRow(item)) classes.push("body-cell-odd");
1892
- if (column.align === "center") classes.push("align-center");
1893
- if (column.align === "end") classes.push("align-end");
1894
- if (state.isFocusedCell(item.row, column)) classes.push("cell-focused");
1895
- if (state.isFocusedRow(item.row)) classes.push("row-focused");
1896
- if (cellEditFeature && state.isEditingCell(item.row, column)) classes.push("cell-editing");
1897
- return classes.join(" ");
146
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className, style: { display: "block", height: "100%", minHeight: 0 }, children: portals });
147
+ }
148
+ function getNestedValue(obj, field) {
149
+ const parts = field.split(".");
150
+ let current = obj;
151
+ for (const part of parts) {
152
+ if (current == null || typeof current !== "object") return void 0;
153
+ current = current[part];
1898
154
  }
1899
- function renderSortIcon(column) {
1900
- const direction = state.sortDirection(column);
1901
- switch (direction) {
1902
- case "asc":
1903
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M12 5l-6 6h4v8h4v-8h4z" }) });
1904
- case "desc":
1905
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M12 19l6-6h-4V5h-4v8H6z" }) });
1906
- default:
1907
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M7 6h10v2H7V6Zm0 5h7v2H7v-2Zm0 5h4v2H7v-2Z" }) });
1908
- }
1909
- }
1910
- return /* @__PURE__ */ jsx("div", { className: `ui-grid-host ${className ?? ""}`, ref: gridContainerRef, children: /* @__PURE__ */ jsxs(
1911
- "section",
1912
- {
1913
- className: "grid-frame ui-grid",
1914
- "data-part": "grid-frame",
1915
- role: "grid",
1916
- "aria-label": options.title ?? "Data grid",
1917
- children: [
1918
- /* @__PURE__ */ jsxs(
1919
- "div",
1920
- {
1921
- className: "grid-table ui-grid-contents-wrapper",
1922
- "data-part": "grid-table",
1923
- style: virtualizationEnabled ? { height: scrollContainerHeight, overflowY: "auto" } : void 0,
1924
- onScroll: virtualizationEnabled ? onGridTableScroll : void 0,
1925
- children: [
1926
- /* @__PURE__ */ jsx(
1927
- "div",
1928
- {
1929
- className: "header-grid ui-grid-header ui-grid-header-canvas",
1930
- "data-part": "header",
1931
- role: "row",
1932
- ref: headerGridRef,
1933
- style: { gridTemplateColumns },
1934
- children: visibleColumns.map((column) => {
1935
- const pinned = state.isPinned(column);
1936
- const pinOffset = pinned ? state.pinnedOffset(column) : null;
1937
- const pinMenuOpen = isPinMenuOpen(column);
1938
- return /* @__PURE__ */ jsxs(
1939
- "div",
1940
- {
1941
- className: `header-cell ui-grid-header-cell${sortingFeature && state.sortDirection(column) !== "none" ? " is-active" : ""}${pinned ? " is-pinned" : ""}${pinMenuOpen ? " is-pin-menu-open" : ""}${draggedColumnName === column.name ? " is-dragging" : ""}${dropTargetColumnName === column.name ? " is-drag-target" : ""}`,
1942
- "data-part": "header-cell",
1943
- "data-col-name": column.name,
1944
- "aria-sort": sortingFeature ? state.sortAriaSort(column) : void 0,
1945
- draggable: columnMovingFeature,
1946
- onDragStart: (event) => handleHeaderDragStart(column, event),
1947
- onDragOver: (event) => handleHeaderDragOver(column, event),
1948
- onDrop: (event) => handleHeaderDrop(column, event),
1949
- onDragEnd: handleHeaderDragEnd,
1950
- onDragLeave: () => {
1951
- if (dropTargetColumnName === column.name) {
1952
- setDropTargetColumnName(null);
1953
- }
1954
- },
1955
- style: {
1956
- position: pinned ? "sticky" : void 0,
1957
- left: pinOffset?.side === "left" ? pinOffset.offset : void 0,
1958
- right: pinOffset?.side === "right" ? pinOffset.offset : void 0,
1959
- zIndex: pinMenuOpen ? 8 : pinned ? 2 : void 0
1960
- },
1961
- children: [
1962
- /* @__PURE__ */ jsx("span", { className: "header-label", children: renderHeaderContent(column) }),
1963
- /* @__PURE__ */ jsxs("div", { className: "header-actions", children: [
1964
- sortingFeature && /* @__PURE__ */ jsxs(
1965
- "button",
1966
- {
1967
- type: "button",
1968
- className: `header-action${!state.isColumnSortable(column) ? " header-action-disabled" : ""}`,
1969
- disabled: !state.isColumnSortable(column),
1970
- "aria-label": state.sortButtonLabel(column),
1971
- title: state.sortButtonLabel(column),
1972
- onClick: () => state.toggleSort(column),
1973
- children: [
1974
- renderSortIcon(column),
1975
- /* @__PURE__ */ jsx("span", { className: "sr-only ui-grid-sr-only", children: state.sortButtonLabel(column) })
1976
- ]
1977
- }
1978
- ),
1979
- groupingFeature && state.isGroupingEnabled() && column.enableGrouping !== false && /* @__PURE__ */ jsxs(
1980
- "button",
1981
- {
1982
- type: "button",
1983
- className: `chip-action${state.isGrouped(column) ? " chip-action-active" : ""}`,
1984
- "data-part": "group-toggle",
1985
- "aria-label": state.groupingButtonLabel(column),
1986
- title: state.groupingButtonLabel(column),
1987
- onClick: (e) => state.toggleGrouping(column, e),
1988
- children: [
1989
- /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M4 6h8v4H4V6Zm0 8h8v4H4v-4Zm10-8h6v4h-6V6Zm0 8h6v4h-6v-4Z" }) }),
1990
- /* @__PURE__ */ jsx("span", { className: "sr-only ui-grid-sr-only", children: state.groupingButtonLabel(column) })
1991
- ]
1992
- }
1993
- ),
1994
- state.pinningFeature && state.isPinningEnabled() && state.isColumnPinnable(column) && /* @__PURE__ */ jsxs(
1995
- "div",
1996
- {
1997
- className: `pin-control${pinMenuOpen ? " pin-control-open" : ""}`,
1998
- onClick: (event) => event.stopPropagation(),
1999
- children: [
2000
- /* @__PURE__ */ jsxs(
2001
- "button",
2002
- {
2003
- type: "button",
2004
- className: `chip-action pin-trigger${pinned || pinMenuOpen ? " chip-action-active" : ""}`,
2005
- "data-part": "pin-toggle",
2006
- "aria-label": pinButtonLabel(column),
2007
- title: pinButtonLabel(column),
2008
- "aria-haspopup": pinned ? void 0 : "menu",
2009
- "aria-expanded": pinned ? void 0 : pinMenuOpen,
2010
- onClick: (event) => onPinTrigger(column, event),
2011
- children: [
2012
- /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M16 12V4h1V2H7v2h1v8l-2 2v2h5v6l1 1 1-1v-6h5v-2l-2-2z" }) }),
2013
- /* @__PURE__ */ jsx("span", { className: "sr-only ui-grid-sr-only", children: pinButtonLabel(column) })
2014
- ]
2015
- }
2016
- ),
2017
- /* @__PURE__ */ jsxs(
2018
- "div",
2019
- {
2020
- className: "pin-menu",
2021
- "data-part": "pin-menu",
2022
- role: "menu",
2023
- "aria-label": "Pin options",
2024
- "aria-hidden": !pinMenuOpen,
2025
- children: [
2026
- /* @__PURE__ */ jsxs(
2027
- "button",
2028
- {
2029
- type: "button",
2030
- className: "pin-menu-action",
2031
- "data-part": "pin-left-action",
2032
- role: "menuitem",
2033
- "aria-label": labels.pinLeft,
2034
- title: labels.pinLeft,
2035
- tabIndex: pinMenuOpen ? 0 : -1,
2036
- onClick: (event) => choosePinDirection(column, "left", event),
2037
- children: [
2038
- /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M10 6 4 12l6 6v-4h10v-4H10V6z" }) }),
2039
- /* @__PURE__ */ jsx("span", { className: "sr-only ui-grid-sr-only", children: labels.pinLeft })
2040
- ]
2041
- }
2042
- ),
2043
- /* @__PURE__ */ jsxs(
2044
- "button",
2045
- {
2046
- type: "button",
2047
- className: "pin-menu-action",
2048
- "data-part": "pin-right-action",
2049
- role: "menuitem",
2050
- "aria-label": labels.pinRight,
2051
- title: labels.pinRight,
2052
- tabIndex: pinMenuOpen ? 0 : -1,
2053
- onClick: (event) => choosePinDirection(column, "right", event),
2054
- children: [
2055
- /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M14 6v4H4v4h10v4l6-6-6-6z" }) }),
2056
- /* @__PURE__ */ jsx("span", { className: "sr-only ui-grid-sr-only", children: labels.pinRight })
2057
- ]
2058
- }
2059
- )
2060
- ]
2061
- }
2062
- )
2063
- ]
2064
- }
2065
- )
2066
- ] }),
2067
- state.canResizeColumns() && /* @__PURE__ */ jsx(
2068
- "button",
2069
- {
2070
- type: "button",
2071
- className: "column-resizer",
2072
- "data-col-name": column.name,
2073
- "aria-label": `Resize ${state.headerLabel(column)} column`,
2074
- title: "Drag to resize, double-click to auto fit",
2075
- onMouseDown: (event) => state.handleHeaderResizeMouseDown(column, event),
2076
- onDoubleClick: (event) => state.autoSizeColumn(column, event)
2077
- }
2078
- )
2079
- ]
2080
- },
2081
- column.name
2082
- );
2083
- })
2084
- }
2085
- ),
2086
- filteringFeature && state.isFilteringEnabled() && /* @__PURE__ */ jsx(
2087
- "div",
2088
- {
2089
- className: "filter-grid ui-grid-header",
2090
- "data-part": "filters",
2091
- ref: filterGridRef,
2092
- style: {
2093
- gridTemplateColumns,
2094
- ["--ui-grid-header-sticky-top"]: `${headerStickyHeight}px`
2095
- },
2096
- children: visibleColumns.map((column) => {
2097
- const pinned = state.isPinned(column);
2098
- const pinOffset = pinned ? state.pinnedOffset(column) : null;
2099
- return /* @__PURE__ */ jsxs(
2100
- "label",
2101
- {
2102
- className: `filter-cell ui-grid-filter-container${pinned ? " is-pinned" : ""}`,
2103
- "data-part": "filter-cell",
2104
- style: {
2105
- position: pinned ? "sticky" : void 0,
2106
- left: pinOffset?.side === "left" ? pinOffset.offset : void 0,
2107
- right: pinOffset?.side === "right" ? pinOffset.offset : void 0,
2108
- zIndex: pinned ? 2 : void 0
2109
- },
2110
- children: [
2111
- /* @__PURE__ */ jsxs("span", { className: "sr-only ui-grid-sr-only", children: [
2112
- labels.filterColumn,
2113
- " ",
2114
- state.headerLabel(column)
2115
- ] }),
2116
- /* @__PURE__ */ jsx(
2117
- "input",
2118
- {
2119
- className: "ui-grid-filter-input",
2120
- type: "text",
2121
- defaultValue: state.filterValue(column.name),
2122
- placeholder: state.filterPlaceholder(column),
2123
- disabled: state.isFilterInputDisabled(column),
2124
- onChange: (e) => state.updateFilter(column.name, e.target.value)
2125
- }
2126
- )
2127
- ]
2128
- },
2129
- column.name
2130
- );
2131
- })
2132
- }
2133
- ),
2134
- displayItems.length > 0 ? virtualizationEnabled ? /* @__PURE__ */ jsx("div", { className: "grid-virtual-spacer", style: { height: `${virtualScroll.totalHeight}px` }, children: /* @__PURE__ */ jsx(
2135
- "div",
2136
- {
2137
- className: "body-grid ui-grid-canvas grid-virtual-body",
2138
- "data-part": "body",
2139
- role: "rowgroup",
2140
- style: {
2141
- gridTemplateColumns,
2142
- position: "absolute",
2143
- top: `${virtualScroll.offsetY}px`,
2144
- left: 0
2145
- },
2146
- children: itemsToRender.map(renderDisplayItem)
2147
- }
2148
- ) }) : /* @__PURE__ */ jsx("div", { className: "body-grid ui-grid-canvas", "data-part": "body", role: "rowgroup", style: { gridTemplateColumns }, children: displayItems.map(renderDisplayItem) }) : /* @__PURE__ */ jsxs("div", { className: "empty-state ui-grid-no-row-overlay", "data-part": "empty-state", children: [
2149
- /* @__PURE__ */ jsx("strong", { children: options.emptyMessage ?? labels.emptyHeading }),
2150
- /* @__PURE__ */ jsx("p", { children: labels.emptyDescription })
2151
- ] })
2152
- ]
2153
- }
2154
- ),
2155
- paginationFeature && state.showPaginationControls() && /* @__PURE__ */ jsxs(
2156
- "footer",
2157
- {
2158
- className: "pagination-bar ui-grid-pagination",
2159
- "data-part": "pagination",
2160
- role: "navigation",
2161
- "aria-label": labels.paginationPage,
2162
- children: [
2163
- /* @__PURE__ */ jsx("p", { children: state.paginationSummary() }),
2164
- /* @__PURE__ */ jsxs("div", { className: "pagination-controls", children: [
2165
- /* @__PURE__ */ jsxs(
2166
- "button",
2167
- {
2168
- type: "button",
2169
- className: "action action-secondary pagination-button",
2170
- "aria-label": labels.paginationPrevious,
2171
- disabled: paginationCurrentPage <= 1,
2172
- onClick: () => state.previousPage(),
2173
- children: [
2174
- /* @__PURE__ */ jsx("svg", { className: "pagination-icon", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }) }),
2175
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.paginationPrevious })
2176
- ]
2177
- }
2178
- ),
2179
- /* @__PURE__ */ jsxs("span", { children: [
2180
- labels.paginationPage,
2181
- " ",
2182
- paginationCurrentPage,
2183
- " ",
2184
- labels.paginationOf,
2185
- " ",
2186
- paginationTotalPages
2187
- ] }),
2188
- /* @__PURE__ */ jsxs(
2189
- "button",
2190
- {
2191
- type: "button",
2192
- className: "action action-secondary pagination-button",
2193
- "aria-label": labels.paginationNext,
2194
- disabled: paginationCurrentPage >= paginationTotalPages,
2195
- onClick: () => state.nextPage(),
2196
- children: [
2197
- /* @__PURE__ */ jsx("svg", { className: "pagination-icon", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: false, children: /* @__PURE__ */ jsx("path", { d: "M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z" }) }),
2198
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.paginationNext })
2199
- ]
2200
- }
2201
- ),
2202
- state.pageSizeOptions().length > 0 && /* @__PURE__ */ jsxs("label", { className: "pagination-size", children: [
2203
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.paginationRows }),
2204
- /* @__PURE__ */ jsx(
2205
- "select",
2206
- {
2207
- "aria-label": labels.paginationRows,
2208
- value: paginationSelectedPageSize,
2209
- onChange: (e) => state.onPageSizeChange(e.target.value),
2210
- children: state.pageSizeOptions().map((size) => /* @__PURE__ */ jsx("option", { value: size, children: size }, size))
2211
- }
2212
- )
2213
- ] })
2214
- ] })
2215
- ]
2216
- }
2217
- )
2218
- ]
2219
- }
2220
- ) });
155
+ return current;
2221
156
  }
2222
157
 
2223
158
  // src/mountUiGrid.tsx
@@ -2238,6 +173,24 @@ function styledCell(text, color, extraStyle) {
2238
173
  text
2239
174
  );
2240
175
  }
176
+ function datePickerCell(value, onChange, extraStyle) {
177
+ return React2.createElement("input", {
178
+ type: "date",
179
+ value: value || "",
180
+ onChange: onChange ? (e) => onChange(e.target.value) : void 0,
181
+ style: {
182
+ font: "inherit",
183
+ fontSize: "0.85rem",
184
+ border: "1px solid color-mix(in srgb, currentColor 20%, transparent)",
185
+ borderRadius: "6px",
186
+ padding: "0.2rem 0.4rem",
187
+ background: "var(--ui-grid-surface, white)",
188
+ color: "inherit",
189
+ cursor: "pointer",
190
+ ...extraStyle
191
+ }
192
+ });
193
+ }
2241
194
 
2242
195
  // src/vanillaAdapter.ts
2243
196
  async function mountUiGridCustomElement(container, mountOptions) {
@@ -2258,43 +211,14 @@ async function mountUiGridCustomElement(container, mountOptions) {
2258
211
  };
2259
212
  }
2260
213
 
2261
- // src/rustWasmGridEngine.ts
2262
- import { registerRustWasmGridEngine } from "@ornery/ui-grid-core";
2263
- var uiGridWasmModulePath = "../../../dist/ui-grid-wasm-web/ui_grid_wasm.js";
2264
- var uiGridWasmBinaryPath = "/dist/ui-grid-wasm-web/ui_grid_wasm_bg.wasm";
2265
- function registerReactUiGridWasmEngineFromModule(module) {
2266
- registerRustWasmGridEngine({
2267
- buildPipeline(context) {
2268
- return module.build_pipeline_js(context);
2269
- }
2270
- });
2271
- }
2272
- async function enableReactUiGridWasmEngine() {
2273
- const module = await import(
2274
- /* @vite-ignore */
2275
- uiGridWasmModulePath
2276
- );
2277
- await module.default(uiGridWasmBinaryPath);
2278
- registerReactUiGridWasmEngineFromModule(module);
2279
- }
2280
-
2281
214
  // src/index.ts
2282
215
  import { DEFAULT_GRID_LABELS } from "@ornery/ui-grid-core";
2283
216
  export {
2284
217
  DEFAULT_GRID_LABELS,
2285
218
  UiGrid,
2286
- buildGridTemplateColumns,
2287
- computeViewportHeightPx,
2288
- computeViewportRows,
2289
- enableReactUiGridWasmEngine,
2290
- formatPaginationSummary,
219
+ datePickerCell,
2291
220
  mountUiGrid,
2292
221
  mountUiGridCustomElement,
2293
- orderVisibleColumns,
2294
- registerReactUiGridWasmEngineFromModule,
2295
- resolveBenchmarkIterations,
2296
222
  styledCell,
2297
- updateUiGrid,
2298
- useGridState,
2299
- useVirtualScroll
223
+ updateUiGrid
2300
224
  };