@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
@@ -1,20 +1,23 @@
1
1
  "use strict";
2
2
 
3
- import { useMemo, useRef, useState } from 'react';
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { I18nManager, Platform, View } from 'react-native';
5
5
  import { Checkbox, Icon, IconButton, Input, Skeleton, Table, TablePagination, Text } from '@cdx-ui/components';
6
- import { Check, Close, FilterList, KeyboardArrowDown, KeyboardArrowUp, Search } from '@cdx-ui/icons';
6
+ import { Check, Close, FilterList, KeyboardArrowDown, KeyboardArrowRight, KeyboardArrowUp, Search } from '@cdx-ui/icons';
7
7
  import { dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
8
8
  import { cn, useControllableState } from '@cdx-ui/utils';
9
9
  import { normalizePinnedColumns, resolveColumnAlign, resolveColumnHeaderAlign } from './adapter/columns';
10
- import { DEFAULT_PINNED_COLUMN_WIDTH, SELECT_COLUMN_WIDTH, buildPinnedOffsets, isColumnPinningActive, pinnedCellStyle, pinnedEdgeGuidesImage, pinnedFieldSet, warnPinnedFlexColumns } from './adapter/pinning';
10
+ import { DEFAULT_PINNED_COLUMN_WIDTH, DETAIL_PANEL_TOGGLE_WIDTH, SELECT_COLUMN_WIDTH, buildPinnedOffsets, isColumnPinningActive, pinnedCellStyle, pinnedEdgeGuidesImage, pinnedFieldSet, warnPinnedFlexColumns } from './adapter/pinning';
11
11
  import { getTypedCellText } from './adapter/columnTypes';
12
- import { DataGridFilterPanel } from './DataGridFilterPanel';
13
- import { dataGridBodyVariants, dataGridCellVariants, dataGridEmptyOverlayVariants, dataGridFilterInputVariants, dataGridFooterVariants, dataGridHeadVariants, dataGridHeaderCellInnerVariants, dataGridHeaderLabelVariants, dataGridHeaderVariants, dataGridMainVariants, dataGridOverlayVariants, dataGridPaginationRangeVariants, dataGridPaginationVariants, dataGridPinnedEdgeVariants, dataGridPinnedSurfaceVariants, dataGridRootVariants, dataGridSelectCellVariants, dataGridSelectHeadVariants, dataGridSortButtonVariants, dataGridTableVariants, dataGridToolbarActionsVariants, dataGridToolbarSearchVariants, dataGridToolbarVariants } from './styles';
14
- import { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAGINATION_MODEL, EMPTY_SELECTION, SELECT_COLUMN_ID } from './types';
12
+ import { DataGridFilterPanel, useDataGridFilterPanel } from './DataGridFilterPanel';
13
+ import { dataGridBodyVariants, dataGridCellTextVariants, dataGridCellVariants, dataGridDetailPanelCellVariants, dataGridDetailToggleCellVariants, dataGridEmptyOverlayVariants, dataGridFilterInputVariants, dataGridFooterVariants, dataGridHeadVariants, dataGridHeaderCellInnerVariants, dataGridHeaderLabelVariants, dataGridHeaderVariants, dataGridMainVariants, dataGridOverlayVariants, dataGridPaginationRangeVariants, dataGridPaginationVariants, dataGridPinnedEdgeVariants, dataGridPinnedSurfaceVariants, dataGridRootVariants, dataGridSelectCellVariants, dataGridSelectHeadVariants, dataGridSortButtonVariants, dataGridTableVariants, dataGridToolbarActionsVariants, dataGridToolbarSearchVariants, dataGridToolbarVariants, dataGridTreeCellInnerVariants, dataGridTreeContentVariants, dataGridTreeToggleSlotVariants } from './styles';
14
+ import { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAGINATION_MODEL, DETAIL_PANEL_TOGGLE_ID, EMPTY_ROW_ID_SET, EMPTY_SELECTION, SELECT_COLUMN_ID } from './types';
15
15
  import { resolveRowId, useDataGridTable } from './useDataGridTable';
16
16
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
17
17
  const MAX_SKELETON_ROW_COUNT = 10;
18
+ /** Matches the detail panel cell's `px-4`. */
19
+ const DETAIL_PANEL_INSET = 16;
20
+ const TREE_INDENT = 24;
18
21
  const DEFAULT_WEB_COLUMN_WIDTH = 100;
19
22
  const WEB_TABLE_CONTAINER_STYLE = {
20
23
  overflow: 'visible'
@@ -49,7 +52,7 @@ function effectiveFlex(column, pinned) {
49
52
  }
50
53
  return Math.max(column.flex ?? 0, 0);
51
54
  }
52
- function webColumnWidths(columns, checkboxSelection, pinned = new Set()) {
55
+ function webColumnWidths(columns, leadingWidth, pinned = new Set()) {
53
56
  const totalFlex = columns.reduce((total, column) => total + effectiveFlex(column, pinned), 0);
54
57
  const widths = new Map();
55
58
  if (totalFlex === 0) {
@@ -62,7 +65,7 @@ function webColumnWidths(columns, checkboxSelection, pinned = new Set()) {
62
65
  }
63
66
  return widths;
64
67
  }
65
- const fixedWidth = (checkboxSelection ? SELECT_COLUMN_WIDTH : 0) + columns.reduce((total, column) => {
68
+ const fixedWidth = leadingWidth + columns.reduce((total, column) => {
66
69
  if (effectiveFlex(column, pinned) > 0) {
67
70
  return total;
68
71
  }
@@ -152,7 +155,7 @@ function defaultCellContent(value) {
152
155
  }
153
156
  function DefaultLoadingRows({
154
157
  columns,
155
- checkboxSelection,
158
+ leadingColumnIds,
156
159
  rowCount,
157
160
  columnWidths,
158
161
  orderedColumnIds,
@@ -160,7 +163,7 @@ function DefaultLoadingRows({
160
163
  offsets
161
164
  }) {
162
165
  const colByField = new Map(columns.map(column => [column.field, column]));
163
- const ids = orderedColumnIds ?? (checkboxSelection ? [SELECT_COLUMN_ID, ...columns.map(column => column.field)] : columns.map(column => column.field));
166
+ const ids = orderedColumnIds ?? [...leadingColumnIds, ...columns.map(column => column.field)];
164
167
  return /*#__PURE__*/_jsx(_Fragment, {
165
168
  children: Array.from({
166
169
  length: rowCount
@@ -179,6 +182,12 @@ function DefaultLoadingRows({
179
182
  })
180
183
  }, columnId);
181
184
  }
185
+ if (columnId === DETAIL_PANEL_TOGGLE_ID) {
186
+ return /*#__PURE__*/_jsx(Table.Cell, {
187
+ className: cn(dataGridDetailToggleCellVariants(), pinnedClassName(pin, false)),
188
+ style: mergePinnedStyle(undefined, pin, offsets, columnId, false)
189
+ }, columnId);
190
+ }
182
191
  const column = colByField.get(columnId);
183
192
  if (!column) {
184
193
  return null;
@@ -225,6 +234,7 @@ function DataGridInner(props) {
225
234
  showToolbar = false,
226
235
  headerFilters = false,
227
236
  pinnedColumns,
237
+ columnVisibilityModel,
228
238
  pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
229
239
  noRowsOverlay,
230
240
  loadingOverlay,
@@ -233,8 +243,49 @@ function DataGridInner(props) {
233
243
  style,
234
244
  label,
235
245
  'aria-label': ariaLabel,
236
- 'aria-labelledby': ariaLabelledBy
246
+ 'aria-labelledby': ariaLabelledBy,
247
+ treeData = false,
248
+ getTreeDataPath,
249
+ groupingColDef,
250
+ defaultGroupingExpansionDepth,
251
+ isGroupExpandedByDefault,
252
+ rowSelectionPropagation,
253
+ getDetailPanelContent
237
254
  } = props;
255
+ const treeActive = treeData && getTreeDataPath !== undefined;
256
+ const isColumnVisible = useCallback(column => columnVisibilityModel?.[column.field] !== false, [columnVisibilityModel]);
257
+ const treeField = treeActive ? columns.find(isColumnVisible)?.field : undefined;
258
+ const gridColumns = useMemo(() => {
259
+ if (treeField === undefined || groupingColDef === undefined) {
260
+ return columns;
261
+ }
262
+ return columns.map(column => column.field === treeField ? {
263
+ ...column,
264
+ ...groupingColDef,
265
+ field: column.field
266
+ } : column);
267
+ }, [columns, groupingColDef, treeField]);
268
+ const visibleColumns = useMemo(() => gridColumns.filter(isColumnVisible), [gridColumns, isColumnVisible]);
269
+ const selectColumn = checkboxSelection && !treeActive;
270
+ const treeCheckbox = checkboxSelection && treeActive;
271
+ const detailPanel = getDetailPanelContent !== undefined;
272
+ const leadingColumnIds = useMemo(() => [...(selectColumn ? [SELECT_COLUMN_ID] : []), ...(detailPanel ? [DETAIL_PANEL_TOGGLE_ID] : [])], [detailPanel, selectColumn]);
273
+ const leadingWidth = (selectColumn ? SELECT_COLUMN_WIDTH : 0) + (detailPanel ? DETAIL_PANEL_TOGGLE_WIDTH : 0);
274
+ const [detailPanelExpandedRowIds, setDetailPanelExpandedRowIds] = useControllableState({
275
+ prop: props.detailPanelExpandedRowIds,
276
+ defaultProp: props.defaultDetailPanelExpandedRowIds ?? EMPTY_ROW_ID_SET,
277
+ onChange: ids => props.onDetailPanelExpandedRowIdsChange?.(new Set(ids))
278
+ });
279
+ const resolvedDetailIds = detailPanelExpandedRowIds ?? EMPTY_ROW_ID_SET;
280
+ const toggleDetailPanel = id => {
281
+ const next = new Set(resolvedDetailIds);
282
+ if (next.has(id)) {
283
+ next.delete(id);
284
+ } else {
285
+ next.add(id);
286
+ }
287
+ setDetailPanelExpandedRowIds(next);
288
+ };
238
289
  const [sortModel, setSortModel] = useControllableState({
239
290
  prop: props.sortModel,
240
291
  defaultProp: props.defaultSortModel ?? [],
@@ -272,18 +323,25 @@ function DataGridInner(props) {
272
323
  const resolvedQuickFilter = quickFilter ?? '';
273
324
  const resolvedSelection = rowSelectionModel ?? EMPTY_SELECTION;
274
325
  const knownFields = useMemo(() => new Set(columns.map(column => column.field)), [columns]);
275
- const columnPinning = useMemo(() => normalizePinnedColumns(pinnedColumns, knownFields, checkboxSelection), [pinnedColumns, knownFields, checkboxSelection]);
326
+ const columnPinning = useMemo(() => normalizePinnedColumns(pinnedColumns, knownFields, selectColumn, detailPanel), [pinnedColumns, knownFields, selectColumn, detailPanel]);
276
327
  const pinningActive = isColumnPinningActive(columnPinning);
277
328
  const pinnedFields = useMemo(() => pinnedFieldSet(columnPinning), [columnPinning]);
278
329
  const {
279
330
  table,
280
331
  setQuickFilter: setQuickFilterWithPageReset,
281
332
  setColumnFilter,
282
- setFilterModel: setFilterModelWithPageReset
333
+ setFilterModel: setFilterModelWithPageReset,
334
+ tree
283
335
  } = useDataGridTable({
284
336
  rows,
285
- columns,
337
+ columns: gridColumns,
286
338
  getRowId,
339
+ detailPanel,
340
+ treeData: treeActive,
341
+ getTreeDataPath,
342
+ defaultGroupingExpansionDepth,
343
+ isGroupExpandedByDefault,
344
+ rowSelectionPropagation,
287
345
  checkboxSelection,
288
346
  disableColumnSorting,
289
347
  paginationMode,
@@ -301,18 +359,19 @@ function DataGridInner(props) {
301
359
  onQuickFilterChange: value => setQuickFilter(value),
302
360
  rowSelectionModel: resolvedSelection,
303
361
  onRowSelectionModelChange: model => setRowSelectionModel(model),
304
- columnPinning
362
+ columnPinning,
363
+ columnVisibility: columnVisibilityModel
305
364
  });
306
365
  const colByField = useMemo(() => {
307
366
  const map = new Map();
308
- for (const col of columns) {
367
+ for (const col of gridColumns) {
309
368
  map.set(col.field, col);
310
369
  }
311
370
  return map;
312
- }, [columns]);
313
- warnPinnedFlexColumns(columns, pinnedFields);
314
- const columnWidths = useMemo(() => webColumnWidths(columns, checkboxSelection, pinnedFields), [checkboxSelection, columns, pinnedFields]);
315
- const hasFlexColumns = useMemo(() => columns.some(column => effectiveFlex(column, pinnedFields) > 0), [columns, pinnedFields]);
371
+ }, [gridColumns]);
372
+ warnPinnedFlexColumns(visibleColumns, pinnedFields);
373
+ const columnWidths = useMemo(() => webColumnWidths(visibleColumns, leadingWidth, pinnedFields), [leadingWidth, visibleColumns, pinnedFields]);
374
+ const hasFlexColumns = useMemo(() => visibleColumns.some(column => effectiveFlex(column, pinnedFields) > 0), [visibleColumns, pinnedFields]);
316
375
  const pinnedOffsets = useMemo(() => pinningActive ? buildPinnedOffsets(columnPinning, colByField) : null, [pinningActive, columnPinning, colByField]);
317
376
  const orderedLeafColumnIds = useMemo(() => {
318
377
  if (!pinningActive) {
@@ -340,7 +399,7 @@ function DataGridInner(props) {
340
399
  const pageCount = Math.max(table.getPageCount(), 1);
341
400
  const selectedCount = table.getFilteredSelectedRowModel().rows.length;
342
401
  const filterableCount = table.getFilteredRowModel().rows.length;
343
- const headerSelectState = selectedCount === 0 ? {
402
+ const headerSelectState = tree?.headerSelection ?? (selectedCount === 0 ? {
344
403
  checked: false,
345
404
  indeterminate: false
346
405
  } : selectedCount === filterableCount ? {
@@ -349,7 +408,8 @@ function DataGridInner(props) {
349
408
  } : {
350
409
  checked: false,
351
410
  indeterminate: true
352
- };
411
+ });
412
+ const leafColumnCount = table.getVisibleLeafColumns().length;
353
413
  const accessibleName = ariaLabel ?? label;
354
414
  const totalCount = paginationMode === 'server' ? rowCount ?? rows.length : filterableCount;
355
415
  const filterValueFor = field => {
@@ -357,14 +417,65 @@ function DataGridInner(props) {
357
417
  return typeof value === 'string' ? value : '';
358
418
  };
359
419
  const showToolbarBar = showToolbar || toolbarActions != null;
360
- const filterPanelColumns = useMemo(() => columns.filter(column => column.filterable !== false), [columns]);
361
- const [filterPanelOpen, setFilterPanelOpen] = useState(false);
420
+ const filterPanelColumns = useMemo(() => gridColumns.filter(column => column.filterable !== false), [gridColumns]);
421
+ // Header filter cells and the column menu open the panel through `filterPanel.openForField`.
422
+ const filterPanel = useDataGridFilterPanel({
423
+ columns: filterPanelColumns,
424
+ filterModel: resolvedFilterModel,
425
+ onFilterModelChange: setFilterModel
426
+ });
427
+ const filterPanelOpen = filterPanel.opener !== null && filterPanelColumns.length > 0;
428
+ const filterPanelAnchored = filterPanel.opener?.field !== undefined;
362
429
  const filterPanelButtonRef = useRef(null);
363
430
  const showFilterPanelButton = showToolbar && filterPanelColumns.length > 0;
431
+ const mainRef = useRef(null);
432
+ const [viewportWidth, setViewportWidth] = useState(undefined);
433
+ useEffect(() => {
434
+ const main = mainRef.current;
435
+ if (Platform.OS !== 'web' || !detailPanel || main === null || typeof ResizeObserver === 'undefined') {
436
+ return undefined;
437
+ }
438
+ const observer = new ResizeObserver(() => setViewportWidth(main.clientWidth));
439
+ observer.observe(main);
440
+ return () => observer.disconnect();
441
+ }, [detailPanel]);
442
+ const detailPanelContentStyle = useMemo(() => viewportWidth === undefined ? undefined : {
443
+ position: 'sticky',
444
+ insetInlineStart: DETAIL_PANEL_INSET,
445
+ width: viewportWidth - DETAIL_PANEL_INSET * 2
446
+ }, [viewportWidth]);
447
+ const rootRef = useRef(null);
448
+ const searchRef = useRef(null);
449
+ const bodyFocusRef = useRef(null);
450
+ useEffect(() => {
451
+ if (Platform.OS !== 'web') {
452
+ return undefined;
453
+ }
454
+ const root = rootRef.current;
455
+ const onFocusIn = event => {
456
+ const target = event.target instanceof Element ? event.target : null;
457
+ bodyFocusRef.current = target !== null && root?.contains(target.closest('tbody')) === true ? target : null;
458
+ };
459
+ document.addEventListener('focusin', onFocusIn);
460
+ return () => document.removeEventListener('focusin', onFocusIn);
461
+ }, []);
462
+ // Only when the focused row control was removed: focus that left the grid stays where it went.
463
+ useEffect(() => {
464
+ const focused = bodyFocusRef.current;
465
+ if (Platform.OS !== 'web' || focused === null || focused.isConnected) {
466
+ return;
467
+ }
468
+ bodyFocusRef.current = null;
469
+ const active = document.activeElement;
470
+ if (active === null || active === document.body) {
471
+ searchRef.current?.focus();
472
+ }
473
+ }, [visibleRows]);
364
474
  return /*#__PURE__*/_jsxs(View, {
475
+ ref: rootRef,
365
476
  className: cn(dataGridRootVariants({
366
477
  autoHeight
367
- }), className, filterPanelOpen && 'overflow-visible'),
478
+ }), className, filterPanelOpen && !filterPanelAnchored && 'overflow-visible'),
368
479
  style: style,
369
480
  ...mergeDataAttributes({}, dataAttributes({
370
481
  slot: 'data-grid'
@@ -377,6 +488,7 @@ function DataGridInner(props) {
377
488
  children: [/*#__PURE__*/_jsx(Input.Icon, {
378
489
  as: Search
379
490
  }), /*#__PURE__*/_jsx(Input.Field, {
491
+ ref: searchRef,
380
492
  value: resolvedQuickFilter,
381
493
  onChangeText: setQuickFilterWithPageReset,
382
494
  placeholder: "Search",
@@ -392,12 +504,12 @@ function DataGridInner(props) {
392
504
  accessibilityState: {
393
505
  expanded: filterPanelOpen
394
506
  },
395
- onPress: () => setFilterPanelOpen(open => !open)
507
+ onPress: () => filterPanel.toggle(filterPanelButtonRef)
396
508
  }) : null, toolbarActions ? /*#__PURE__*/_jsx(View, {
397
509
  className: dataGridToolbarActionsVariants(),
398
510
  children: toolbarActions
399
511
  }) : null]
400
- }) : null, showFilterPanelButton && filterPanelOpen ? /*#__PURE__*/_jsx(DataGridFilterPanel, {
512
+ }) : null, filterPanelOpen && filterPanel.opener ? /*#__PURE__*/_jsx(DataGridFilterPanel, {
401
513
  columns: filterPanelColumns,
402
514
  filterModel: resolvedFilterModel,
403
515
  onFilterModelChange: setFilterModelWithPageReset,
@@ -411,9 +523,10 @@ function DataGridInner(props) {
411
523
  });
412
524
  }
413
525
  },
414
- onClose: () => setFilterPanelOpen(false),
415
- triggerRef: filterPanelButtonRef
526
+ onClose: filterPanel.close,
527
+ opener: filterPanel.opener
416
528
  }) : null, /*#__PURE__*/_jsxs(View, {
529
+ ref: mainRef,
417
530
  className: dataGridMainVariants({
418
531
  autoHeight,
419
532
  overlay: isEmpty || loading && loadingOverlay != null
@@ -431,10 +544,14 @@ function DataGridInner(props) {
431
544
  children: [label ? /*#__PURE__*/_jsx(Table.Caption, {
432
545
  children: label
433
546
  }) : null, Platform.OS === 'web' && hasFlexColumns ? /*#__PURE__*/_jsx("colgroup", {
434
- children: (orderedLeafColumnIds ?? (checkboxSelection ? [SELECT_COLUMN_ID, ...columns.map(column => column.field)] : columns.map(column => column.field))).map(columnId => columnId === SELECT_COLUMN_ID ? /*#__PURE__*/_jsx("col", {
547
+ children: (orderedLeafColumnIds ?? [...leadingColumnIds, ...visibleColumns.map(column => column.field)]).map(columnId => columnId === SELECT_COLUMN_ID ? /*#__PURE__*/_jsx("col", {
435
548
  style: {
436
549
  width: SELECT_COLUMN_WIDTH
437
550
  }
551
+ }, columnId) : columnId === DETAIL_PANEL_TOGGLE_ID ? /*#__PURE__*/_jsx("col", {
552
+ style: {
553
+ width: DETAIL_PANEL_TOGGLE_WIDTH
554
+ }
438
555
  }, columnId) : /*#__PURE__*/_jsx("col", {
439
556
  style: {
440
557
  width: columnWidths.get(columnId),
@@ -464,7 +581,14 @@ function DataGridInner(props) {
464
581
  })
465
582
  }, header.id);
466
583
  }
584
+ if (header.id === DETAIL_PANEL_TOGGLE_ID) {
585
+ return /*#__PURE__*/_jsx(Table.Cell, {
586
+ className: cn(dataGridHeadVariants(), dataGridDetailToggleCellVariants(), pinnedClassName(pin, true)),
587
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, header.id, true)
588
+ }, header.id);
589
+ }
467
590
  const col = colByField.get(header.column.id);
591
+ const isTreeColumn = header.column.id === treeField;
468
592
  const sorted = header.column.getIsSorted();
469
593
  const canSort = !disableColumnSorting && header.column.getCanSort();
470
594
  const headerAlign = resolveColumnHeaderAlign(col);
@@ -485,6 +609,13 @@ function DataGridInner(props) {
485
609
  sorted: sorted !== false
486
610
  })
487
611
  }) : null;
612
+ const headerLabel = /*#__PURE__*/_jsxs(_Fragment, {
613
+ children: [headerAlign === 'end' ? sortButton : null, /*#__PURE__*/_jsx(Text, {
614
+ numberOfLines: 1,
615
+ className: dataGridHeaderLabelVariants(),
616
+ children: col?.renderHeader ? col.renderHeader() : headerName
617
+ }), headerAlign === 'end' ? null : sortButton]
618
+ });
488
619
  return /*#__PURE__*/_jsx(Table.Head, {
489
620
  align: headerAlign,
490
621
  className: cn(dataGridHeadVariants(), pinnedClassName(pin, true)),
@@ -495,26 +626,39 @@ function DataGridInner(props) {
495
626
  'aria-sort': sorted === 'asc' ? 'ascending' : sorted === 'desc' ? 'descending' : 'none'
496
627
  } : {})
497
628
  } : {}),
498
- children: /*#__PURE__*/_jsxs(View, {
629
+ children: isTreeColumn ? /*#__PURE__*/_jsxs(View, {
630
+ className: dataGridTreeCellInnerVariants(),
631
+ children: [/*#__PURE__*/_jsx(View, {
632
+ className: dataGridTreeToggleSlotVariants()
633
+ }), treeCheckbox ? /*#__PURE__*/_jsx(Checkbox, {
634
+ value: "select-all",
635
+ isChecked: headerSelectState.checked,
636
+ isIndeterminate: headerSelectState.indeterminate,
637
+ onChange: () => tree?.toggleAllRowsSelected(),
638
+ accessibilityLabel: "Select all",
639
+ "aria-label": "Select all"
640
+ }) : null, /*#__PURE__*/_jsx(View, {
641
+ className: cn(dataGridHeaderCellInnerVariants({
642
+ align: headerAlign
643
+ }), 'flex-1'),
644
+ children: headerLabel
645
+ })]
646
+ }) : /*#__PURE__*/_jsx(View, {
499
647
  className: dataGridHeaderCellInnerVariants({
500
648
  align: headerAlign,
501
649
  fitToColumn: Platform.OS === 'web' && columnWidths.has(header.column.id)
502
650
  }),
503
- children: [headerAlign === 'end' ? sortButton : null, /*#__PURE__*/_jsx(Text, {
504
- numberOfLines: 1,
505
- className: dataGridHeaderLabelVariants(),
506
- children: col?.renderHeader ? col.renderHeader() : headerName
507
- }), headerAlign === 'end' ? null : sortButton]
651
+ children: headerLabel
508
652
  })
509
653
  }, header.id);
510
654
  })
511
655
  }, headerGroup.id)), headerFilters ? /*#__PURE__*/_jsx(Table.Row, {
512
- children: (pinningActive ? [...table.getStartVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns(), ...table.getEndVisibleLeafColumns()] : table.getAllLeafColumns()).map(column => {
656
+ children: (pinningActive ? [...table.getStartVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns(), ...table.getEndVisibleLeafColumns()] : table.getVisibleLeafColumns()).map(column => {
513
657
  const pin = pinMetaFor(column.id, columnPinning);
514
- if (column.id === SELECT_COLUMN_ID) {
658
+ if (column.id === SELECT_COLUMN_ID || column.id === DETAIL_PANEL_TOGGLE_ID) {
515
659
  return /*#__PURE__*/_jsx(Table.Cell, {
516
660
  align: "center",
517
- className: cn(dataGridSelectHeadVariants(), pinnedClassName(pin, true)),
661
+ className: cn(dataGridSelectHeadVariants(), column.id === DETAIL_PANEL_TOGGLE_ID && 'w-12 px-2', pinnedClassName(pin, true)),
518
662
  style: mergePinnedStyle(undefined, pin, pinnedOffsets, column.id, true)
519
663
  }, column.id);
520
664
  }
@@ -546,19 +690,47 @@ function DataGridInner(props) {
546
690
  }), /*#__PURE__*/_jsx(Table.Body, {
547
691
  className: dataGridBodyVariants(),
548
692
  children: loading && loadingOverlay == null ? /*#__PURE__*/_jsx(DefaultLoadingRows, {
549
- columns: columns,
550
- checkboxSelection: checkboxSelection,
693
+ columns: visibleColumns,
694
+ leadingColumnIds: leadingColumnIds,
551
695
  rowCount: Math.min(resolvedPagination.pageSize, MAX_SKELETON_ROW_COUNT),
552
696
  columnWidths: columnWidths,
553
697
  orderedColumnIds: orderedLeafColumnIds,
554
698
  pinning: columnPinning,
555
699
  offsets: pinnedOffsets
556
- }) : isEmpty ? null : visibleRows.map(row => {
700
+ }) : isEmpty ? null : visibleRows.flatMap(row => {
557
701
  const rowId = resolveRowId(row.original, getRowId);
558
- const cells = pinningActive ? [...row.getStartVisibleCells(), ...row.getCenterVisibleCells(), ...row.getEndVisibleCells()] : row.getAllCells();
559
- return /*#__PURE__*/_jsx(Table.Row, {
702
+ const cells = pinningActive ? [...row.getStartVisibleCells(), ...row.getCenterVisibleCells(), ...row.getEndVisibleCells()] : row.getVisibleCells();
703
+ const detailContent = getDetailPanelContent?.({
704
+ id: rowId,
705
+ row: row.original
706
+ });
707
+ const hasDetailPanel = detailContent != null;
708
+ const detailOpen = hasDetailPanel && resolvedDetailIds.has(rowId);
709
+ const treeLabel = treeActive ? [...getTreeDataPath(row.original)].reverse().join(', ') : '';
710
+ const dataRow = /*#__PURE__*/_jsx(Table.Row, {
711
+ className: detailOpen ? 'web:[&>*]:border-b-0' : undefined,
560
712
  children: cells.map(cell => {
561
713
  const pin = pinMetaFor(cell.column.id, columnPinning);
714
+ if (cell.column.id === DETAIL_PANEL_TOGGLE_ID) {
715
+ return /*#__PURE__*/_jsx(Table.Cell, {
716
+ align: "center",
717
+ className: cn(dataGridDetailToggleCellVariants(), pinnedClassName(pin, false)),
718
+ style: mergePinnedStyle(undefined, pin, pinnedOffsets, cell.column.id, false),
719
+ children: hasDetailPanel ? /*#__PURE__*/_jsx(IconButton, {
720
+ as: detailOpen ? KeyboardArrowDown : KeyboardArrowRight,
721
+ variant: "ghost",
722
+ color: "neutral",
723
+ size: "small",
724
+ iconClassName: detailOpen ? undefined : 'rtl:-scale-x-100',
725
+ accessibilityLabel: `${detailOpen ? 'Collapse' : 'Expand'} row ${String(rowId)}`,
726
+ accessibilityState: {
727
+ expanded: detailOpen
728
+ },
729
+ "aria-expanded": detailOpen,
730
+ onPress: () => toggleDetailPanel(rowId)
731
+ }) : null
732
+ }, cell.id);
733
+ }
562
734
  if (cell.column.id === SELECT_COLUMN_ID) {
563
735
  return /*#__PURE__*/_jsx(Table.Cell, {
564
736
  align: "center",
@@ -594,14 +766,81 @@ function DataGridInner(props) {
594
766
  const typedText = getTypedCellText(col, value, row.original);
595
767
  content = typedText !== undefined ? typedText : defaultCellContent(value);
596
768
  }
769
+ if (tree && cell.column.id === treeField) {
770
+ const canExpand = row.subRows.length > 0;
771
+ const expanded = canExpand && tree.isRowExpanded(row.id);
772
+ const status = tree.selectionStatus(row.id);
773
+ content = /*#__PURE__*/_jsxs(View, {
774
+ className: dataGridTreeCellInnerVariants(),
775
+ style: {
776
+ paddingStart: row.depth * TREE_INDENT
777
+ },
778
+ children: [/*#__PURE__*/_jsx(View, {
779
+ className: dataGridTreeToggleSlotVariants(),
780
+ children: canExpand ? /*#__PURE__*/_jsx(IconButton, {
781
+ as: expanded ? KeyboardArrowDown : KeyboardArrowRight,
782
+ variant: "ghost",
783
+ color: "neutral",
784
+ size: "small",
785
+ iconClassName: expanded ? undefined : 'rtl:-scale-x-100',
786
+ accessibilityLabel: `${expanded ? 'Collapse' : 'Expand'} ${treeLabel}`,
787
+ accessibilityState: {
788
+ expanded
789
+ },
790
+ "aria-expanded": expanded,
791
+ onPress: () => tree.toggleRowExpanded(row.id)
792
+ }) : null
793
+ }), treeCheckbox ? /*#__PURE__*/_jsx(Checkbox, {
794
+ value: String(rowId),
795
+ isChecked: status === 'all',
796
+ isIndeterminate: status === 'some',
797
+ onChange: () => tree.toggleRowSelected(row),
798
+ accessibilityLabel: `Select ${treeLabel}`,
799
+ "aria-label": `Select ${treeLabel}`
800
+ }) : null, /*#__PURE__*/_jsx(View, {
801
+ className: dataGridTreeContentVariants(),
802
+ children: typeof content === 'string' || typeof content === 'number' ? /*#__PURE__*/_jsx(Text, {
803
+ numberOfLines: 1,
804
+ children: content
805
+ }) : content
806
+ })]
807
+ });
808
+ }
809
+ const align = resolveColumnAlign(col);
810
+ const cellText = !col?.renderCell && (typeof content === 'string' || typeof content === 'number') ? String(content) : undefined;
597
811
  return /*#__PURE__*/_jsx(Table.Cell, {
598
- align: resolveColumnAlign(col),
812
+ align: align,
813
+ ...(Platform.OS === 'web' && cellText ? {
814
+ title: cellText
815
+ } : {}),
599
816
  className: cn(dataGridCellVariants(), pinnedClassName(pin, false)),
600
817
  style: mergePinnedStyle(cellStyle(col, columnWidths.get(cell.column.id)), pin, pinnedOffsets, cell.column.id, false),
601
- children: content
818
+ children: cellText !== undefined ? /*#__PURE__*/_jsx(Text, {
819
+ numberOfLines: 1,
820
+ className: dataGridCellTextVariants({
821
+ align,
822
+ fitToColumn: Platform.OS === 'web' && columnWidths.has(cell.column.id)
823
+ }),
824
+ children: cellText
825
+ }) : content
602
826
  }, cell.id);
603
827
  })
604
828
  }, row.id);
829
+ if (!detailOpen) {
830
+ return [dataRow];
831
+ }
832
+ return [dataRow, /*#__PURE__*/_jsx(Table.Row, {
833
+ className: "web:hover:bg-transparent",
834
+ testID: `data-grid-detail-panel-${String(rowId)}`,
835
+ children: /*#__PURE__*/_jsx(Table.Cell, {
836
+ colSpan: leafColumnCount,
837
+ className: dataGridDetailPanelCellVariants(),
838
+ children: /*#__PURE__*/_jsx(View, {
839
+ style: detailPanelContentStyle,
840
+ children: detailContent
841
+ })
842
+ })
843
+ }, `${row.id}:detail`)];
605
844
  })
606
845
  })]
607
846
  }), isEmpty ? /*#__PURE__*/_jsx(View, {