@cdx-ui/data-grid 0.3.12 → 0.3.13

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.
Files changed (67) hide show
  1. package/coverage.svg +1 -1
  2. package/lib/commonjs/DataGrid.js +279 -40
  3. package/lib/commonjs/DataGrid.js.map +1 -1
  4. package/lib/commonjs/DataGridFilterPanel.js +136 -9
  5. package/lib/commonjs/DataGridFilterPanel.js.map +1 -1
  6. package/lib/commonjs/adapter/columns.js +16 -8
  7. package/lib/commonjs/adapter/columns.js.map +1 -1
  8. package/lib/commonjs/adapter/pinning.js +5 -1
  9. package/lib/commonjs/adapter/pinning.js.map +1 -1
  10. package/lib/commonjs/adapter/selectColumn.js +11 -0
  11. package/lib/commonjs/adapter/selectColumn.js.map +1 -1
  12. package/lib/commonjs/adapter/treeData.js +195 -0
  13. package/lib/commonjs/adapter/treeData.js.map +1 -0
  14. package/lib/commonjs/styles.js +48 -2
  15. package/lib/commonjs/styles.js.map +1 -1
  16. package/lib/commonjs/types.js +11 -1
  17. package/lib/commonjs/types.js.map +1 -1
  18. package/lib/commonjs/useDataGridTable.js +143 -12
  19. package/lib/commonjs/useDataGridTable.js.map +1 -1
  20. package/lib/module/DataGrid.js +285 -46
  21. package/lib/module/DataGrid.js.map +1 -1
  22. package/lib/module/DataGridFilterPanel.js +139 -13
  23. package/lib/module/DataGridFilterPanel.js.map +1 -1
  24. package/lib/module/adapter/columns.js +17 -9
  25. package/lib/module/adapter/columns.js.map +1 -1
  26. package/lib/module/adapter/pinning.js +5 -1
  27. package/lib/module/adapter/pinning.js.map +1 -1
  28. package/lib/module/adapter/selectColumn.js +11 -1
  29. package/lib/module/adapter/selectColumn.js.map +1 -1
  30. package/lib/module/adapter/treeData.js +187 -0
  31. package/lib/module/adapter/treeData.js.map +1 -0
  32. package/lib/module/styles.js +47 -1
  33. package/lib/module/styles.js.map +1 -1
  34. package/lib/module/types.js +10 -0
  35. package/lib/module/types.js.map +1 -1
  36. package/lib/module/useDataGridTable.js +147 -16
  37. package/lib/module/useDataGridTable.js.map +1 -1
  38. package/lib/typescript/DataGrid.d.ts.map +1 -1
  39. package/lib/typescript/DataGridFilterPanel.d.ts +39 -4
  40. package/lib/typescript/DataGridFilterPanel.d.ts.map +1 -1
  41. package/lib/typescript/adapter/columns.d.ts +1 -1
  42. package/lib/typescript/adapter/columns.d.ts.map +1 -1
  43. package/lib/typescript/adapter/pinning.d.ts +1 -0
  44. package/lib/typescript/adapter/pinning.d.ts.map +1 -1
  45. package/lib/typescript/adapter/selectColumn.d.ts +8 -0
  46. package/lib/typescript/adapter/selectColumn.d.ts.map +1 -1
  47. package/lib/typescript/adapter/treeData.d.ts +36 -0
  48. package/lib/typescript/adapter/treeData.d.ts.map +1 -0
  49. package/lib/typescript/index.d.ts +1 -1
  50. package/lib/typescript/index.d.ts.map +1 -1
  51. package/lib/typescript/styles.d.ts +12 -1
  52. package/lib/typescript/styles.d.ts.map +1 -1
  53. package/lib/typescript/types.d.ts +68 -0
  54. package/lib/typescript/types.d.ts.map +1 -1
  55. package/lib/typescript/useDataGridTable.d.ts +25 -3
  56. package/lib/typescript/useDataGridTable.d.ts.map +1 -1
  57. package/package.json +5 -5
  58. package/src/DataGrid.tsx +399 -59
  59. package/src/DataGridFilterPanel.tsx +188 -17
  60. package/src/adapter/columns.ts +21 -7
  61. package/src/adapter/pinning.ts +10 -1
  62. package/src/adapter/selectColumn.ts +12 -1
  63. package/src/adapter/treeData.ts +228 -0
  64. package/src/index.ts +4 -0
  65. package/src/styles.ts +57 -1
  66. package/src/types.ts +76 -0
  67. package/src/useDataGridTable.ts +217 -15
package/coverage.svg CHANGED
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="108" height="20" role="img" aria-label="coverage: 87.1%"><title>coverage: 87.1%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="108" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="47" height="20" fill="#4b0"/><rect width="108" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="835" y="150" fill-opacity=".8" filter="url(#blur)" textLength="370">87.1%</text><text x="835" y="150" fill-opacity=".3" textLength="370">87.1%</text></g><text x="835" y="140" textLength="370">87.1%</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="108" height="20" role="img" aria-label="coverage: 88.3%"><title>coverage: 88.3%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="108" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="47" height="20" fill="#4b0"/><rect width="108" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="835" y="150" fill-opacity=".8" filter="url(#blur)" textLength="370">88.3%</text><text x="835" y="150" fill-opacity=".3" textLength="370">88.3%</text></g><text x="835" y="140" textLength="370">88.3%</text></g></g></svg>
@@ -19,6 +19,9 @@ var _types = require("./types");
19
19
  var _useDataGridTable = require("./useDataGridTable");
20
20
  var _jsxRuntime = require("react/jsx-runtime");
21
21
  const MAX_SKELETON_ROW_COUNT = 10;
22
+ /** Matches the detail panel cell's `px-4`. */
23
+ const DETAIL_PANEL_INSET = 16;
24
+ const TREE_INDENT = 24;
22
25
  const DEFAULT_WEB_COLUMN_WIDTH = 100;
23
26
  const WEB_TABLE_CONTAINER_STYLE = {
24
27
  overflow: 'visible'
@@ -53,7 +56,7 @@ function effectiveFlex(column, pinned) {
53
56
  }
54
57
  return Math.max(column.flex ?? 0, 0);
55
58
  }
56
- function webColumnWidths(columns, checkboxSelection, pinned = new Set()) {
59
+ function webColumnWidths(columns, leadingWidth, pinned = new Set()) {
57
60
  const totalFlex = columns.reduce((total, column) => total + effectiveFlex(column, pinned), 0);
58
61
  const widths = new Map();
59
62
  if (totalFlex === 0) {
@@ -66,7 +69,7 @@ function webColumnWidths(columns, checkboxSelection, pinned = new Set()) {
66
69
  }
67
70
  return widths;
68
71
  }
69
- const fixedWidth = (checkboxSelection ? _pinning.SELECT_COLUMN_WIDTH : 0) + columns.reduce((total, column) => {
72
+ const fixedWidth = leadingWidth + columns.reduce((total, column) => {
70
73
  if (effectiveFlex(column, pinned) > 0) {
71
74
  return total;
72
75
  }
@@ -156,7 +159,7 @@ function defaultCellContent(value) {
156
159
  }
157
160
  function DefaultLoadingRows({
158
161
  columns,
159
- checkboxSelection,
162
+ leadingColumnIds,
160
163
  rowCount,
161
164
  columnWidths,
162
165
  orderedColumnIds,
@@ -164,7 +167,7 @@ function DefaultLoadingRows({
164
167
  offsets
165
168
  }) {
166
169
  const colByField = new Map(columns.map(column => [column.field, column]));
167
- const ids = orderedColumnIds ?? (checkboxSelection ? [_types.SELECT_COLUMN_ID, ...columns.map(column => column.field)] : columns.map(column => column.field));
170
+ const ids = orderedColumnIds ?? [...leadingColumnIds, ...columns.map(column => column.field)];
168
171
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
169
172
  children: Array.from({
170
173
  length: rowCount
@@ -183,6 +186,12 @@ function DefaultLoadingRows({
183
186
  })
184
187
  }, columnId);
185
188
  }
189
+ if (columnId === _types.DETAIL_PANEL_TOGGLE_ID) {
190
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
191
+ className: (0, _utils.cn)((0, _styles.dataGridDetailToggleCellVariants)(), pinnedClassName(pin, false)),
192
+ style: mergePinnedStyle(undefined, pin, offsets, columnId, false)
193
+ }, columnId);
194
+ }
186
195
  const column = colByField.get(columnId);
187
196
  if (!column) {
188
197
  return null;
@@ -229,6 +238,7 @@ function DataGridInner(props) {
229
238
  showToolbar = false,
230
239
  headerFilters = false,
231
240
  pinnedColumns,
241
+ columnVisibilityModel,
232
242
  pageSizeOptions = _types.DEFAULT_PAGE_SIZE_OPTIONS,
233
243
  noRowsOverlay,
234
244
  loadingOverlay,
@@ -237,8 +247,49 @@ function DataGridInner(props) {
237
247
  style,
238
248
  label,
239
249
  'aria-label': ariaLabel,
240
- 'aria-labelledby': ariaLabelledBy
250
+ 'aria-labelledby': ariaLabelledBy,
251
+ treeData = false,
252
+ getTreeDataPath,
253
+ groupingColDef,
254
+ defaultGroupingExpansionDepth,
255
+ isGroupExpandedByDefault,
256
+ rowSelectionPropagation,
257
+ getDetailPanelContent
241
258
  } = props;
259
+ const treeActive = treeData && getTreeDataPath !== undefined;
260
+ const isColumnVisible = (0, _react.useCallback)(column => columnVisibilityModel?.[column.field] !== false, [columnVisibilityModel]);
261
+ const treeField = treeActive ? columns.find(isColumnVisible)?.field : undefined;
262
+ const gridColumns = (0, _react.useMemo)(() => {
263
+ if (treeField === undefined || groupingColDef === undefined) {
264
+ return columns;
265
+ }
266
+ return columns.map(column => column.field === treeField ? {
267
+ ...column,
268
+ ...groupingColDef,
269
+ field: column.field
270
+ } : column);
271
+ }, [columns, groupingColDef, treeField]);
272
+ const visibleColumns = (0, _react.useMemo)(() => gridColumns.filter(isColumnVisible), [gridColumns, isColumnVisible]);
273
+ const selectColumn = checkboxSelection && !treeActive;
274
+ const treeCheckbox = checkboxSelection && treeActive;
275
+ const detailPanel = getDetailPanelContent !== undefined;
276
+ const leadingColumnIds = (0, _react.useMemo)(() => [...(selectColumn ? [_types.SELECT_COLUMN_ID] : []), ...(detailPanel ? [_types.DETAIL_PANEL_TOGGLE_ID] : [])], [detailPanel, selectColumn]);
277
+ const leadingWidth = (selectColumn ? _pinning.SELECT_COLUMN_WIDTH : 0) + (detailPanel ? _pinning.DETAIL_PANEL_TOGGLE_WIDTH : 0);
278
+ const [detailPanelExpandedRowIds, setDetailPanelExpandedRowIds] = (0, _utils.useControllableState)({
279
+ prop: props.detailPanelExpandedRowIds,
280
+ defaultProp: props.defaultDetailPanelExpandedRowIds ?? _types.EMPTY_ROW_ID_SET,
281
+ onChange: ids => props.onDetailPanelExpandedRowIdsChange?.(new Set(ids))
282
+ });
283
+ const resolvedDetailIds = detailPanelExpandedRowIds ?? _types.EMPTY_ROW_ID_SET;
284
+ const toggleDetailPanel = id => {
285
+ const next = new Set(resolvedDetailIds);
286
+ if (next.has(id)) {
287
+ next.delete(id);
288
+ } else {
289
+ next.add(id);
290
+ }
291
+ setDetailPanelExpandedRowIds(next);
292
+ };
242
293
  const [sortModel, setSortModel] = (0, _utils.useControllableState)({
243
294
  prop: props.sortModel,
244
295
  defaultProp: props.defaultSortModel ?? [],
@@ -276,18 +327,25 @@ function DataGridInner(props) {
276
327
  const resolvedQuickFilter = quickFilter ?? '';
277
328
  const resolvedSelection = rowSelectionModel ?? _types.EMPTY_SELECTION;
278
329
  const knownFields = (0, _react.useMemo)(() => new Set(columns.map(column => column.field)), [columns]);
279
- const columnPinning = (0, _react.useMemo)(() => (0, _columns.normalizePinnedColumns)(pinnedColumns, knownFields, checkboxSelection), [pinnedColumns, knownFields, checkboxSelection]);
330
+ const columnPinning = (0, _react.useMemo)(() => (0, _columns.normalizePinnedColumns)(pinnedColumns, knownFields, selectColumn, detailPanel), [pinnedColumns, knownFields, selectColumn, detailPanel]);
280
331
  const pinningActive = (0, _pinning.isColumnPinningActive)(columnPinning);
281
332
  const pinnedFields = (0, _react.useMemo)(() => (0, _pinning.pinnedFieldSet)(columnPinning), [columnPinning]);
282
333
  const {
283
334
  table,
284
335
  setQuickFilter: setQuickFilterWithPageReset,
285
336
  setColumnFilter,
286
- setFilterModel: setFilterModelWithPageReset
337
+ setFilterModel: setFilterModelWithPageReset,
338
+ tree
287
339
  } = (0, _useDataGridTable.useDataGridTable)({
288
340
  rows,
289
- columns,
341
+ columns: gridColumns,
290
342
  getRowId,
343
+ detailPanel,
344
+ treeData: treeActive,
345
+ getTreeDataPath,
346
+ defaultGroupingExpansionDepth,
347
+ isGroupExpandedByDefault,
348
+ rowSelectionPropagation,
291
349
  checkboxSelection,
292
350
  disableColumnSorting,
293
351
  paginationMode,
@@ -305,18 +363,19 @@ function DataGridInner(props) {
305
363
  onQuickFilterChange: value => setQuickFilter(value),
306
364
  rowSelectionModel: resolvedSelection,
307
365
  onRowSelectionModelChange: model => setRowSelectionModel(model),
308
- columnPinning
366
+ columnPinning,
367
+ columnVisibility: columnVisibilityModel
309
368
  });
310
369
  const colByField = (0, _react.useMemo)(() => {
311
370
  const map = new Map();
312
- for (const col of columns) {
371
+ for (const col of gridColumns) {
313
372
  map.set(col.field, col);
314
373
  }
315
374
  return map;
316
- }, [columns]);
317
- (0, _pinning.warnPinnedFlexColumns)(columns, pinnedFields);
318
- const columnWidths = (0, _react.useMemo)(() => webColumnWidths(columns, checkboxSelection, pinnedFields), [checkboxSelection, columns, pinnedFields]);
319
- const hasFlexColumns = (0, _react.useMemo)(() => columns.some(column => effectiveFlex(column, pinnedFields) > 0), [columns, pinnedFields]);
375
+ }, [gridColumns]);
376
+ (0, _pinning.warnPinnedFlexColumns)(visibleColumns, pinnedFields);
377
+ const columnWidths = (0, _react.useMemo)(() => webColumnWidths(visibleColumns, leadingWidth, pinnedFields), [leadingWidth, visibleColumns, pinnedFields]);
378
+ const hasFlexColumns = (0, _react.useMemo)(() => visibleColumns.some(column => effectiveFlex(column, pinnedFields) > 0), [visibleColumns, pinnedFields]);
320
379
  const pinnedOffsets = (0, _react.useMemo)(() => pinningActive ? (0, _pinning.buildPinnedOffsets)(columnPinning, colByField) : null, [pinningActive, columnPinning, colByField]);
321
380
  const orderedLeafColumnIds = (0, _react.useMemo)(() => {
322
381
  if (!pinningActive) {
@@ -344,7 +403,7 @@ function DataGridInner(props) {
344
403
  const pageCount = Math.max(table.getPageCount(), 1);
345
404
  const selectedCount = table.getFilteredSelectedRowModel().rows.length;
346
405
  const filterableCount = table.getFilteredRowModel().rows.length;
347
- const headerSelectState = selectedCount === 0 ? {
406
+ const headerSelectState = tree?.headerSelection ?? (selectedCount === 0 ? {
348
407
  checked: false,
349
408
  indeterminate: false
350
409
  } : selectedCount === filterableCount ? {
@@ -353,7 +412,8 @@ function DataGridInner(props) {
353
412
  } : {
354
413
  checked: false,
355
414
  indeterminate: true
356
- };
415
+ });
416
+ const leafColumnCount = table.getVisibleLeafColumns().length;
357
417
  const accessibleName = ariaLabel ?? label;
358
418
  const totalCount = paginationMode === 'server' ? rowCount ?? rows.length : filterableCount;
359
419
  const filterValueFor = field => {
@@ -361,14 +421,65 @@ function DataGridInner(props) {
361
421
  return typeof value === 'string' ? value : '';
362
422
  };
363
423
  const showToolbarBar = showToolbar || toolbarActions != null;
364
- const filterPanelColumns = (0, _react.useMemo)(() => columns.filter(column => column.filterable !== false), [columns]);
365
- const [filterPanelOpen, setFilterPanelOpen] = (0, _react.useState)(false);
424
+ const filterPanelColumns = (0, _react.useMemo)(() => gridColumns.filter(column => column.filterable !== false), [gridColumns]);
425
+ // Header filter cells and the column menu open the panel through `filterPanel.openForField`.
426
+ const filterPanel = (0, _DataGridFilterPanel.useDataGridFilterPanel)({
427
+ columns: filterPanelColumns,
428
+ filterModel: resolvedFilterModel,
429
+ onFilterModelChange: setFilterModel
430
+ });
431
+ const filterPanelOpen = filterPanel.opener !== null && filterPanelColumns.length > 0;
432
+ const filterPanelAnchored = filterPanel.opener?.field !== undefined;
366
433
  const filterPanelButtonRef = (0, _react.useRef)(null);
367
434
  const showFilterPanelButton = showToolbar && filterPanelColumns.length > 0;
435
+ const mainRef = (0, _react.useRef)(null);
436
+ const [viewportWidth, setViewportWidth] = (0, _react.useState)(undefined);
437
+ (0, _react.useEffect)(() => {
438
+ const main = mainRef.current;
439
+ if (_reactNative.Platform.OS !== 'web' || !detailPanel || main === null || typeof ResizeObserver === 'undefined') {
440
+ return undefined;
441
+ }
442
+ const observer = new ResizeObserver(() => setViewportWidth(main.clientWidth));
443
+ observer.observe(main);
444
+ return () => observer.disconnect();
445
+ }, [detailPanel]);
446
+ const detailPanelContentStyle = (0, _react.useMemo)(() => viewportWidth === undefined ? undefined : {
447
+ position: 'sticky',
448
+ insetInlineStart: DETAIL_PANEL_INSET,
449
+ width: viewportWidth - DETAIL_PANEL_INSET * 2
450
+ }, [viewportWidth]);
451
+ const rootRef = (0, _react.useRef)(null);
452
+ const searchRef = (0, _react.useRef)(null);
453
+ const bodyFocusRef = (0, _react.useRef)(null);
454
+ (0, _react.useEffect)(() => {
455
+ if (_reactNative.Platform.OS !== 'web') {
456
+ return undefined;
457
+ }
458
+ const root = rootRef.current;
459
+ const onFocusIn = event => {
460
+ const target = event.target instanceof Element ? event.target : null;
461
+ bodyFocusRef.current = target !== null && root?.contains(target.closest('tbody')) === true ? target : null;
462
+ };
463
+ document.addEventListener('focusin', onFocusIn);
464
+ return () => document.removeEventListener('focusin', onFocusIn);
465
+ }, []);
466
+ // Only when the focused row control was removed: focus that left the grid stays where it went.
467
+ (0, _react.useEffect)(() => {
468
+ const focused = bodyFocusRef.current;
469
+ if (_reactNative.Platform.OS !== 'web' || focused === null || focused.isConnected) {
470
+ return;
471
+ }
472
+ bodyFocusRef.current = null;
473
+ const active = document.activeElement;
474
+ if (active === null || active === document.body) {
475
+ searchRef.current?.focus();
476
+ }
477
+ }, [visibleRows]);
368
478
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
479
+ ref: rootRef,
369
480
  className: (0, _utils.cn)((0, _styles.dataGridRootVariants)({
370
481
  autoHeight
371
- }), className, filterPanelOpen && 'overflow-visible'),
482
+ }), className, filterPanelOpen && !filterPanelAnchored && 'overflow-visible'),
372
483
  style: style,
373
484
  ...(0, _primitives.mergeDataAttributes)({}, (0, _primitives.dataAttributes)({
374
485
  slot: 'data-grid'
@@ -381,6 +492,7 @@ function DataGridInner(props) {
381
492
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Input.Icon, {
382
493
  as: _icons.Search
383
494
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Input.Field, {
495
+ ref: searchRef,
384
496
  value: resolvedQuickFilter,
385
497
  onChangeText: setQuickFilterWithPageReset,
386
498
  placeholder: "Search",
@@ -396,12 +508,12 @@ function DataGridInner(props) {
396
508
  accessibilityState: {
397
509
  expanded: filterPanelOpen
398
510
  },
399
- onPress: () => setFilterPanelOpen(open => !open)
511
+ onPress: () => filterPanel.toggle(filterPanelButtonRef)
400
512
  }) : null, toolbarActions ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
401
513
  className: (0, _styles.dataGridToolbarActionsVariants)(),
402
514
  children: toolbarActions
403
515
  }) : null]
404
- }) : null, showFilterPanelButton && filterPanelOpen ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_DataGridFilterPanel.DataGridFilterPanel, {
516
+ }) : null, filterPanelOpen && filterPanel.opener ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_DataGridFilterPanel.DataGridFilterPanel, {
405
517
  columns: filterPanelColumns,
406
518
  filterModel: resolvedFilterModel,
407
519
  onFilterModelChange: setFilterModelWithPageReset,
@@ -415,9 +527,10 @@ function DataGridInner(props) {
415
527
  });
416
528
  }
417
529
  },
418
- onClose: () => setFilterPanelOpen(false),
419
- triggerRef: filterPanelButtonRef
530
+ onClose: filterPanel.close,
531
+ opener: filterPanel.opener
420
532
  }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
533
+ ref: mainRef,
421
534
  className: (0, _styles.dataGridMainVariants)({
422
535
  autoHeight,
423
536
  overlay: isEmpty || loading && loadingOverlay != null
@@ -435,10 +548,14 @@ function DataGridInner(props) {
435
548
  children: [label ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Caption, {
436
549
  children: label
437
550
  }) : null, _reactNative.Platform.OS === 'web' && hasFlexColumns ? /*#__PURE__*/(0, _jsxRuntime.jsx)("colgroup", {
438
- children: (orderedLeafColumnIds ?? (checkboxSelection ? [_types.SELECT_COLUMN_ID, ...columns.map(column => column.field)] : columns.map(column => column.field))).map(columnId => columnId === _types.SELECT_COLUMN_ID ? /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
551
+ children: (orderedLeafColumnIds ?? [...leadingColumnIds, ...visibleColumns.map(column => column.field)]).map(columnId => columnId === _types.SELECT_COLUMN_ID ? /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
439
552
  style: {
440
553
  width: _pinning.SELECT_COLUMN_WIDTH
441
554
  }
555
+ }, columnId) : columnId === _types.DETAIL_PANEL_TOGGLE_ID ? /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
556
+ style: {
557
+ width: _pinning.DETAIL_PANEL_TOGGLE_WIDTH
558
+ }
442
559
  }, columnId) : /*#__PURE__*/(0, _jsxRuntime.jsx)("col", {
443
560
  style: {
444
561
  width: columnWidths.get(columnId),
@@ -468,7 +585,14 @@ function DataGridInner(props) {
468
585
  })
469
586
  }, header.id);
470
587
  }
588
+ if (header.id === _types.DETAIL_PANEL_TOGGLE_ID) {
589
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
590
+ className: (0, _utils.cn)((0, _styles.dataGridHeadVariants)(), (0, _styles.dataGridDetailToggleCellVariants)(), pinnedClassName(pin, true)),
591
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, header.id, true)
592
+ }, header.id);
593
+ }
471
594
  const col = colByField.get(header.column.id);
595
+ const isTreeColumn = header.column.id === treeField;
472
596
  const sorted = header.column.getIsSorted();
473
597
  const canSort = !disableColumnSorting && header.column.getCanSort();
474
598
  const headerAlign = (0, _columns.resolveColumnHeaderAlign)(col);
@@ -489,6 +613,13 @@ function DataGridInner(props) {
489
613
  sorted: sorted !== false
490
614
  })
491
615
  }) : null;
616
+ const headerLabel = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
617
+ children: [headerAlign === 'end' ? sortButton : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Text, {
618
+ numberOfLines: 1,
619
+ className: (0, _styles.dataGridHeaderLabelVariants)(),
620
+ children: col?.renderHeader ? col.renderHeader() : headerName
621
+ }), headerAlign === 'end' ? null : sortButton]
622
+ });
492
623
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Head, {
493
624
  align: headerAlign,
494
625
  className: (0, _utils.cn)((0, _styles.dataGridHeadVariants)(), pinnedClassName(pin, true)),
@@ -499,26 +630,39 @@ function DataGridInner(props) {
499
630
  'aria-sort': sorted === 'asc' ? 'ascending' : sorted === 'desc' ? 'descending' : 'none'
500
631
  } : {})
501
632
  } : {}),
502
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
633
+ children: isTreeColumn ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
634
+ className: (0, _styles.dataGridTreeCellInnerVariants)(),
635
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
636
+ className: (0, _styles.dataGridTreeToggleSlotVariants)()
637
+ }), treeCheckbox ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Checkbox, {
638
+ value: "select-all",
639
+ isChecked: headerSelectState.checked,
640
+ isIndeterminate: headerSelectState.indeterminate,
641
+ onChange: () => tree?.toggleAllRowsSelected(),
642
+ accessibilityLabel: "Select all",
643
+ "aria-label": "Select all"
644
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
645
+ className: (0, _utils.cn)((0, _styles.dataGridHeaderCellInnerVariants)({
646
+ align: headerAlign
647
+ }), 'flex-1'),
648
+ children: headerLabel
649
+ })]
650
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
503
651
  className: (0, _styles.dataGridHeaderCellInnerVariants)({
504
652
  align: headerAlign,
505
653
  fitToColumn: _reactNative.Platform.OS === 'web' && columnWidths.has(header.column.id)
506
654
  }),
507
- children: [headerAlign === 'end' ? sortButton : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Text, {
508
- numberOfLines: 1,
509
- className: (0, _styles.dataGridHeaderLabelVariants)(),
510
- children: col?.renderHeader ? col.renderHeader() : headerName
511
- }), headerAlign === 'end' ? null : sortButton]
655
+ children: headerLabel
512
656
  })
513
657
  }, header.id);
514
658
  })
515
659
  }, headerGroup.id)), headerFilters ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
516
- children: (pinningActive ? [...table.getStartVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns(), ...table.getEndVisibleLeafColumns()] : table.getAllLeafColumns()).map(column => {
660
+ children: (pinningActive ? [...table.getStartVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns(), ...table.getEndVisibleLeafColumns()] : table.getVisibleLeafColumns()).map(column => {
517
661
  const pin = pinMetaFor(column.id, columnPinning);
518
- if (column.id === _types.SELECT_COLUMN_ID) {
662
+ if (column.id === _types.SELECT_COLUMN_ID || column.id === _types.DETAIL_PANEL_TOGGLE_ID) {
519
663
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
520
664
  align: "center",
521
- className: (0, _utils.cn)((0, _styles.dataGridSelectHeadVariants)(), pinnedClassName(pin, true)),
665
+ className: (0, _utils.cn)((0, _styles.dataGridSelectHeadVariants)(), column.id === _types.DETAIL_PANEL_TOGGLE_ID && 'w-12 px-2', pinnedClassName(pin, true)),
522
666
  style: mergePinnedStyle(undefined, pin, pinnedOffsets, column.id, true)
523
667
  }, column.id);
524
668
  }
@@ -550,19 +694,47 @@ function DataGridInner(props) {
550
694
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Body, {
551
695
  className: (0, _styles.dataGridBodyVariants)(),
552
696
  children: loading && loadingOverlay == null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(DefaultLoadingRows, {
553
- columns: columns,
554
- checkboxSelection: checkboxSelection,
697
+ columns: visibleColumns,
698
+ leadingColumnIds: leadingColumnIds,
555
699
  rowCount: Math.min(resolvedPagination.pageSize, MAX_SKELETON_ROW_COUNT),
556
700
  columnWidths: columnWidths,
557
701
  orderedColumnIds: orderedLeafColumnIds,
558
702
  pinning: columnPinning,
559
703
  offsets: pinnedOffsets
560
- }) : isEmpty ? null : visibleRows.map(row => {
704
+ }) : isEmpty ? null : visibleRows.flatMap(row => {
561
705
  const rowId = (0, _useDataGridTable.resolveRowId)(row.original, getRowId);
562
- const cells = pinningActive ? [...row.getStartVisibleCells(), ...row.getCenterVisibleCells(), ...row.getEndVisibleCells()] : row.getAllCells();
563
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
706
+ const cells = pinningActive ? [...row.getStartVisibleCells(), ...row.getCenterVisibleCells(), ...row.getEndVisibleCells()] : row.getVisibleCells();
707
+ const detailContent = getDetailPanelContent?.({
708
+ id: rowId,
709
+ row: row.original
710
+ });
711
+ const hasDetailPanel = detailContent != null;
712
+ const detailOpen = hasDetailPanel && resolvedDetailIds.has(rowId);
713
+ const treeLabel = treeActive ? [...getTreeDataPath(row.original)].reverse().join(', ') : '';
714
+ const dataRow = /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
715
+ className: detailOpen ? 'web:[&>*]:border-b-0' : undefined,
564
716
  children: cells.map(cell => {
565
717
  const pin = pinMetaFor(cell.column.id, columnPinning);
718
+ if (cell.column.id === _types.DETAIL_PANEL_TOGGLE_ID) {
719
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
720
+ align: "center",
721
+ className: (0, _utils.cn)((0, _styles.dataGridDetailToggleCellVariants)(), pinnedClassName(pin, false)),
722
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, cell.column.id, false),
723
+ children: hasDetailPanel ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.IconButton, {
724
+ as: detailOpen ? _icons.KeyboardArrowDown : _icons.KeyboardArrowRight,
725
+ variant: "ghost",
726
+ color: "neutral",
727
+ size: "small",
728
+ iconClassName: detailOpen ? undefined : 'rtl:-scale-x-100',
729
+ accessibilityLabel: `${detailOpen ? 'Collapse' : 'Expand'} row ${String(rowId)}`,
730
+ accessibilityState: {
731
+ expanded: detailOpen
732
+ },
733
+ "aria-expanded": detailOpen,
734
+ onPress: () => toggleDetailPanel(rowId)
735
+ }) : null
736
+ }, cell.id);
737
+ }
566
738
  if (cell.column.id === _types.SELECT_COLUMN_ID) {
567
739
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
568
740
  align: "center",
@@ -598,14 +770,81 @@ function DataGridInner(props) {
598
770
  const typedText = (0, _columnTypes.getTypedCellText)(col, value, row.original);
599
771
  content = typedText !== undefined ? typedText : defaultCellContent(value);
600
772
  }
773
+ if (tree && cell.column.id === treeField) {
774
+ const canExpand = row.subRows.length > 0;
775
+ const expanded = canExpand && tree.isRowExpanded(row.id);
776
+ const status = tree.selectionStatus(row.id);
777
+ content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
778
+ className: (0, _styles.dataGridTreeCellInnerVariants)(),
779
+ style: {
780
+ paddingStart: row.depth * TREE_INDENT
781
+ },
782
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
783
+ className: (0, _styles.dataGridTreeToggleSlotVariants)(),
784
+ children: canExpand ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.IconButton, {
785
+ as: expanded ? _icons.KeyboardArrowDown : _icons.KeyboardArrowRight,
786
+ variant: "ghost",
787
+ color: "neutral",
788
+ size: "small",
789
+ iconClassName: expanded ? undefined : 'rtl:-scale-x-100',
790
+ accessibilityLabel: `${expanded ? 'Collapse' : 'Expand'} ${treeLabel}`,
791
+ accessibilityState: {
792
+ expanded
793
+ },
794
+ "aria-expanded": expanded,
795
+ onPress: () => tree.toggleRowExpanded(row.id)
796
+ }) : null
797
+ }), treeCheckbox ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Checkbox, {
798
+ value: String(rowId),
799
+ isChecked: status === 'all',
800
+ isIndeterminate: status === 'some',
801
+ onChange: () => tree.toggleRowSelected(row),
802
+ accessibilityLabel: `Select ${treeLabel}`,
803
+ "aria-label": `Select ${treeLabel}`
804
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
805
+ className: (0, _styles.dataGridTreeContentVariants)(),
806
+ children: typeof content === 'string' || typeof content === 'number' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Text, {
807
+ numberOfLines: 1,
808
+ children: content
809
+ }) : content
810
+ })]
811
+ });
812
+ }
813
+ const align = (0, _columns.resolveColumnAlign)(col);
814
+ const cellText = !col?.renderCell && (typeof content === 'string' || typeof content === 'number') ? String(content) : undefined;
601
815
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
602
- align: (0, _columns.resolveColumnAlign)(col),
816
+ align: align,
817
+ ...(_reactNative.Platform.OS === 'web' && cellText ? {
818
+ title: cellText
819
+ } : {}),
603
820
  className: (0, _utils.cn)((0, _styles.dataGridCellVariants)(), pinnedClassName(pin, false)),
604
821
  style: mergePinnedStyle(cellStyle(col, columnWidths.get(cell.column.id)), pin, pinnedOffsets, cell.column.id, false),
605
- children: content
822
+ children: cellText !== undefined ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Text, {
823
+ numberOfLines: 1,
824
+ className: (0, _styles.dataGridCellTextVariants)({
825
+ align,
826
+ fitToColumn: _reactNative.Platform.OS === 'web' && columnWidths.has(cell.column.id)
827
+ }),
828
+ children: cellText
829
+ }) : content
606
830
  }, cell.id);
607
831
  })
608
832
  }, row.id);
833
+ if (!detailOpen) {
834
+ return [dataRow];
835
+ }
836
+ return [dataRow, /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Row, {
837
+ className: "web:hover:bg-transparent",
838
+ testID: `data-grid-detail-panel-${String(rowId)}`,
839
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Table.Cell, {
840
+ colSpan: leafColumnCount,
841
+ className: (0, _styles.dataGridDetailPanelCellVariants)(),
842
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
843
+ style: detailPanelContentStyle,
844
+ children: detailContent
845
+ })
846
+ })
847
+ }, `${row.id}:detail`)];
609
848
  })
610
849
  })]
611
850
  }), isEmpty ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {