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