react-glide-table 1.6.0 → 1.7.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/compound.cjs CHANGED
@@ -44,6 +44,7 @@ var ROW_HOVER_CLASS = "row-hoverable";
44
44
  var ROW_HOVERED_BG_CLASS = "row-hovered";
45
45
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
46
46
  var DATA_TABLE_ROW_HEIGHT = 44;
47
+ var DATA_TABLE_HEADER_ROW_HEIGHT = 40;
47
48
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
48
49
  var DATA_TABLE_COLUMN_SIZE = 150;
49
50
  var DATA_TABLE_COLUMN_MIN_SIZE = 40;
@@ -497,7 +498,7 @@ function getColumnFreezeStyle(offset, options) {
497
498
  position: "sticky",
498
499
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
499
500
  zIndex: zBase + offset.stack,
500
- ...options?.isHeader ? { top: 0 } : {}
501
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
501
502
  };
502
503
  }
503
504
 
@@ -1721,6 +1722,38 @@ function DataTableToolbar({
1721
1722
  ] });
1722
1723
  }
1723
1724
 
1725
+ // src/components/ui/table/features/column-groups/mergeHeaderGroups.ts
1726
+ function getMergedHeaderGroups(headerGroups) {
1727
+ if (headerGroups.length <= 1) {
1728
+ return headerGroups.map((group) => ({
1729
+ ...group,
1730
+ headers: group.headers.map((header) => ({
1731
+ ...header,
1732
+ mergedRowSpan: 1
1733
+ }))
1734
+ }));
1735
+ }
1736
+ const seenColumnIds = /* @__PURE__ */ new Set();
1737
+ const fullDepth = headerGroups.length;
1738
+ return headerGroups.map((group, depth) => ({
1739
+ ...group,
1740
+ headers: group.headers.filter((header) => !seenColumnIds.has(header.column.id)).map((header) => {
1741
+ seenColumnIds.add(header.column.id);
1742
+ if (header.isPlaceholder) {
1743
+ return {
1744
+ ...header,
1745
+ isPlaceholder: false,
1746
+ mergedRowSpan: fullDepth - depth
1747
+ };
1748
+ }
1749
+ return {
1750
+ ...header,
1751
+ mergedRowSpan: 1
1752
+ };
1753
+ })
1754
+ }));
1755
+ }
1756
+
1724
1757
  // src/core/useGlideTable.ts
1725
1758
  var import_react_table2 = require("@tanstack/react-table");
1726
1759
  var import_react_virtual = require("@tanstack/react-virtual");
@@ -3349,6 +3382,7 @@ function DataTable({
3349
3382
  const PendingSlot = slots?.Pending ?? DefaultPending;
3350
3383
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
3351
3384
  const freezeOffsets = rowContextValue.columnFreeze.offsets;
3385
+ const headerGroups = getMergedHeaderGroups(table.getHeaderGroups());
3352
3386
  const contextValue = (0, import_react8.useMemo)(
3353
3387
  () => ({ ...rowContextValue, classNames }),
3354
3388
  [rowContextValue, classNames]
@@ -3418,7 +3452,7 @@ function DataTable({
3418
3452
  style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
3419
3453
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
3420
3454
  children: [
3421
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3455
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { className: cn("data-table-head", classNames?.head), children: headerGroups.map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3422
3456
  "tr",
3423
3457
  {
3424
3458
  className: cn("data-table-head-row", classNames?.headRow),
@@ -3432,7 +3466,8 @@ function DataTable({
3432
3466
  });
3433
3467
  const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
3434
3468
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
3435
- isHeader: true
3469
+ isHeader: true,
3470
+ headerTop: header.depth * DATA_TABLE_HEADER_ROW_HEIGHT
3436
3471
  });
3437
3472
  const headerStyle = {
3438
3473
  ...sizeStyle,
@@ -3441,6 +3476,8 @@ function DataTable({
3441
3476
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3442
3477
  "th",
3443
3478
  {
3479
+ colSpan: header.colSpan,
3480
+ rowSpan: header.mergedRowSpan,
3444
3481
  "data-resizing": header.column.getIsResizing() ? "" : void 0,
3445
3482
  "data-frozen": freezeOffset?.side,
3446
3483
  "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
@@ -3646,6 +3683,46 @@ function buildColumnDef(props, sort, onSort) {
3646
3683
  }
3647
3684
  };
3648
3685
  }
3686
+ function resolveGroupId(props, index) {
3687
+ if (props.id) return props.id;
3688
+ if (typeof props.header === "string" || typeof props.header === "number") {
3689
+ return `group:${props.header}:${index}`;
3690
+ }
3691
+ return `group:${index}`;
3692
+ }
3693
+ function buildColumnDefsFromTree(nodes, sort, onSort) {
3694
+ return nodes.map((node, index) => {
3695
+ if (node.type === "leaf") {
3696
+ return buildColumnDef(node.props, sort, onSort);
3697
+ }
3698
+ const childDefs = buildColumnDefsFromTree(node.columns, sort, onSort);
3699
+ const { header, align, headerClassName } = node.props;
3700
+ return {
3701
+ id: resolveGroupId(node.props, index),
3702
+ header: (
3703
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
3704
+ () => header
3705
+ ),
3706
+ columns: childDefs,
3707
+ enableResizing: false,
3708
+ meta: {
3709
+ align,
3710
+ headerClassName
3711
+ }
3712
+ };
3713
+ });
3714
+ }
3715
+ function countLeafColumns(nodes) {
3716
+ let count = 0;
3717
+ for (const node of nodes) {
3718
+ if (node.type === "leaf") {
3719
+ count += 1;
3720
+ } else {
3721
+ count += countLeafColumns(node.columns);
3722
+ }
3723
+ }
3724
+ return count;
3725
+ }
3649
3726
 
3650
3727
  // src/components/ui/table/components/Table/parseTableChildren.ts
3651
3728
  var import_react10 = require("react");
@@ -3655,6 +3732,7 @@ var import_react9 = require("react");
3655
3732
  var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
3656
3733
  var TABLE_BODY_DISPLAY_NAME = "Table.Body";
3657
3734
  var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
3735
+ var TABLE_COLUMN_GROUP_DISPLAY_NAME = "Table.ColumnGroup";
3658
3736
  var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
3659
3737
  function getComponentDisplayName(type) {
3660
3738
  if (typeof type === "function" || typeof type === "object" && type !== null) {
@@ -3671,6 +3749,9 @@ function isTableBodyElement(child) {
3671
3749
  function isTableColumnElement(child) {
3672
3750
  return (0, import_react9.isValidElement)(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
3673
3751
  }
3752
+ function isTableColumnGroupElement(child) {
3753
+ return (0, import_react9.isValidElement)(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_GROUP_DISPLAY_NAME;
3754
+ }
3674
3755
  function isTablePaginationElement(child) {
3675
3756
  return (0, import_react9.isValidElement)(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
3676
3757
  }
@@ -3697,26 +3778,38 @@ function parseTableChildren(children) {
3697
3778
  }
3698
3779
  return slots;
3699
3780
  }
3700
- function flattenColumnElements(children) {
3781
+ function walkColumnTreeNodes(children) {
3701
3782
  const result = [];
3702
3783
  for (const child of import_react10.Children.toArray(children)) {
3703
3784
  if (isTableColumnElement(child)) {
3704
- result.push(child);
3785
+ result.push({
3786
+ type: "leaf",
3787
+ props: child.props
3788
+ });
3789
+ continue;
3790
+ }
3791
+ if (isTableColumnGroupElement(child)) {
3792
+ const groupProps = child.props;
3793
+ result.push({
3794
+ type: "group",
3795
+ props: groupProps,
3796
+ columns: walkColumnTreeNodes(groupProps.children)
3797
+ });
3705
3798
  continue;
3706
3799
  }
3707
3800
  if ((0, import_react10.isValidElement)(child)) {
3708
3801
  const nested = child.props.children;
3709
3802
  if (nested != null) {
3710
- result.push(...flattenColumnElements(nested));
3803
+ result.push(...walkColumnTreeNodes(nested));
3711
3804
  }
3712
3805
  }
3713
3806
  }
3714
3807
  return result;
3715
3808
  }
3716
- function extractColumnElements(header) {
3809
+ function extractColumnTree(header) {
3717
3810
  if (!header) return [];
3718
3811
  const { children } = header.props;
3719
- return flattenColumnElements(children);
3812
+ return walkColumnTreeNodes(children);
3720
3813
  }
3721
3814
 
3722
3815
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -3732,6 +3825,13 @@ function TableColumn(props) {
3732
3825
  }
3733
3826
  TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
3734
3827
 
3828
+ // src/components/ui/table/components/Table/TableColumnGroup.tsx
3829
+ function TableColumnGroup(props) {
3830
+ void props;
3831
+ return null;
3832
+ }
3833
+ TableColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3834
+
3735
3835
  // src/components/ui/table/components/Table/tableDataPipeline.ts
3736
3836
  function sortTableData(data, sort) {
3737
3837
  if (!sort) return data;
@@ -3839,11 +3939,11 @@ function TableRoot({
3839
3939
  return null;
3840
3940
  });
3841
3941
  }, []);
3842
- const columns = (0, import_react11.useMemo)(() => {
3843
- return extractColumnElements(header).map(
3844
- (columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
3845
- );
3846
- }, [header, sort, handleSort]);
3942
+ const columnTree = (0, import_react11.useMemo)(() => extractColumnTree(header), [header]);
3943
+ const columns = (0, import_react11.useMemo)(
3944
+ () => buildColumnDefsFromTree(columnTree, sort, handleSort),
3945
+ [columnTree, sort, handleSort]
3946
+ );
3847
3947
  const paginationProps = paginationElement?.props;
3848
3948
  const pageSize = paginationProps?.pageSize ?? 10;
3849
3949
  const page = paginationProps?.page ?? 1;
@@ -3853,7 +3953,7 @@ function TableRoot({
3853
3953
  if (!paginationProps) return sortedData;
3854
3954
  return paginateTableData(sortedData, page, pageSize);
3855
3955
  }, [data, sort, paginationProps, page, pageSize]);
3856
- if (columns.length === 0) {
3956
+ if (countLeafColumns(columnTree) === 0) {
3857
3957
  console.warn("[Table] Declare at least one Table.Column inside Table.Header.");
3858
3958
  }
3859
3959
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "TableJSX", children: [
@@ -3886,6 +3986,11 @@ function createTable() {
3886
3986
  return null;
3887
3987
  }
3888
3988
  Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
3989
+ function ColumnGroup(props) {
3990
+ void props;
3991
+ return null;
3992
+ }
3993
+ ColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3889
3994
  return Object.assign(
3890
3995
  function BoundTable(props) {
3891
3996
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TableRoot, { ...props });
@@ -3893,6 +3998,7 @@ function createTable() {
3893
3998
  {
3894
3999
  Header: TableHeader,
3895
4000
  Column,
4001
+ ColumnGroup,
3896
4002
  Body: TableBody,
3897
4003
  Pagination: TablePagination
3898
4004
  }
@@ -3901,6 +4007,7 @@ function createTable() {
3901
4007
  var Table = Object.assign(TableRoot, {
3902
4008
  Header: TableHeader,
3903
4009
  Column: TableColumn,
4010
+ ColumnGroup: TableColumnGroup,
3904
4011
  Body: TableBody,
3905
4012
  Pagination: TablePagination
3906
4013
  });
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { h as DataTableProps, T as TableColumnProps, n as TableProps } from './types-tkOzBZ4V.cjs';
4
- export { b as ColumnFreezeMeta, d as ColumnFreezeSide, e as DataTableClassNames, g as DataTableLabels, i as DataTableScrollSlotProps, j as DataTableSlots, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, l as SearchResultItem } from './types-tkOzBZ4V.cjs';
3
+ import { h as DataTableProps, n as TableColumnProps, T as TableColumnGroupProps, o as TableProps } from './types-Cs9MiZs1.cjs';
4
+ export { b as ColumnFreezeMeta, d as ColumnFreezeSide, e as DataTableClassNames, g as DataTableLabels, i as DataTableScrollSlotProps, j as DataTableSlots, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, l as SearchResultItem } from './types-Cs9MiZs1.cjs';
5
5
  export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
6
6
 
7
7
  /**
@@ -25,6 +25,12 @@ declare namespace TableColumn {
25
25
  var displayName: string;
26
26
  }
27
27
 
28
+ /** Used only inside Table.Header. Groups leaf columns under a multi-row header; does not render DOM. */
29
+ declare function TableColumnGroup(props: TableColumnGroupProps): null;
30
+ declare namespace TableColumnGroup {
31
+ var displayName: string;
32
+ }
33
+
28
34
  type TableHeaderProps = {
29
35
  children: ReactNode;
30
36
  };
@@ -49,6 +55,7 @@ declare namespace TablePagination {
49
55
  type TableCompoundComponent<T extends Record<string, unknown>> = ((props: TableProps<T>) => ReactElement) & {
50
56
  Header: typeof TableHeader;
51
57
  Column: <K extends string>(props: TableColumnProps<T, K>) => null;
58
+ ColumnGroup: (props: TableColumnGroupProps) => null;
52
59
  Body: typeof TableBody;
53
60
  Pagination: typeof TablePagination;
54
61
  };
@@ -57,8 +64,9 @@ declare function createTable<T extends Record<string, unknown>>(): TableCompound
57
64
  declare const Table: typeof TableRoot & {
58
65
  Header: typeof TableHeader;
59
66
  Column: typeof TableColumn;
67
+ ColumnGroup: typeof TableColumnGroup;
60
68
  Body: typeof TableBody;
61
69
  Pagination: typeof TablePagination;
62
70
  };
63
71
 
64
- export { DataTable, DataTableProps, Table, TableColumnProps, type TableCompoundComponent, TableProps, createTable };
72
+ export { DataTable, DataTableProps, Table, TableColumnGroupProps, TableColumnProps, type TableCompoundComponent, TableProps, createTable };
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { h as DataTableProps, T as TableColumnProps, n as TableProps } from './types-tkOzBZ4V.js';
4
- export { b as ColumnFreezeMeta, d as ColumnFreezeSide, e as DataTableClassNames, g as DataTableLabels, i as DataTableScrollSlotProps, j as DataTableSlots, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, l as SearchResultItem } from './types-tkOzBZ4V.js';
3
+ import { h as DataTableProps, n as TableColumnProps, T as TableColumnGroupProps, o as TableProps } from './types-Cs9MiZs1.js';
4
+ export { b as ColumnFreezeMeta, d as ColumnFreezeSide, e as DataTableClassNames, g as DataTableLabels, i as DataTableScrollSlotProps, j as DataTableSlots, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, l as SearchResultItem } from './types-Cs9MiZs1.js';
5
5
  export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
6
6
 
7
7
  /**
@@ -25,6 +25,12 @@ declare namespace TableColumn {
25
25
  var displayName: string;
26
26
  }
27
27
 
28
+ /** Used only inside Table.Header. Groups leaf columns under a multi-row header; does not render DOM. */
29
+ declare function TableColumnGroup(props: TableColumnGroupProps): null;
30
+ declare namespace TableColumnGroup {
31
+ var displayName: string;
32
+ }
33
+
28
34
  type TableHeaderProps = {
29
35
  children: ReactNode;
30
36
  };
@@ -49,6 +55,7 @@ declare namespace TablePagination {
49
55
  type TableCompoundComponent<T extends Record<string, unknown>> = ((props: TableProps<T>) => ReactElement) & {
50
56
  Header: typeof TableHeader;
51
57
  Column: <K extends string>(props: TableColumnProps<T, K>) => null;
58
+ ColumnGroup: (props: TableColumnGroupProps) => null;
52
59
  Body: typeof TableBody;
53
60
  Pagination: typeof TablePagination;
54
61
  };
@@ -57,8 +64,9 @@ declare function createTable<T extends Record<string, unknown>>(): TableCompound
57
64
  declare const Table: typeof TableRoot & {
58
65
  Header: typeof TableHeader;
59
66
  Column: typeof TableColumn;
67
+ ColumnGroup: typeof TableColumnGroup;
60
68
  Body: typeof TableBody;
61
69
  Pagination: typeof TablePagination;
62
70
  };
63
71
 
64
- export { DataTable, DataTableProps, Table, TableColumnProps, type TableCompoundComponent, TableProps, createTable };
72
+ export { DataTable, DataTableProps, Table, TableColumnGroupProps, TableColumnProps, type TableCompoundComponent, TableProps, createTable };
package/dist/compound.js CHANGED
@@ -16,6 +16,7 @@ var ROW_HOVER_CLASS = "row-hoverable";
16
16
  var ROW_HOVERED_BG_CLASS = "row-hovered";
17
17
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
18
18
  var DATA_TABLE_ROW_HEIGHT = 44;
19
+ var DATA_TABLE_HEADER_ROW_HEIGHT = 40;
19
20
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
20
21
  var DATA_TABLE_COLUMN_SIZE = 150;
21
22
  var DATA_TABLE_COLUMN_MIN_SIZE = 40;
@@ -469,7 +470,7 @@ function getColumnFreezeStyle(offset, options) {
469
470
  position: "sticky",
470
471
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
471
472
  zIndex: zBase + offset.stack,
472
- ...options?.isHeader ? { top: 0 } : {}
473
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
473
474
  };
474
475
  }
475
476
 
@@ -1693,6 +1694,38 @@ function DataTableToolbar({
1693
1694
  ] });
1694
1695
  }
1695
1696
 
1697
+ // src/components/ui/table/features/column-groups/mergeHeaderGroups.ts
1698
+ function getMergedHeaderGroups(headerGroups) {
1699
+ if (headerGroups.length <= 1) {
1700
+ return headerGroups.map((group) => ({
1701
+ ...group,
1702
+ headers: group.headers.map((header) => ({
1703
+ ...header,
1704
+ mergedRowSpan: 1
1705
+ }))
1706
+ }));
1707
+ }
1708
+ const seenColumnIds = /* @__PURE__ */ new Set();
1709
+ const fullDepth = headerGroups.length;
1710
+ return headerGroups.map((group, depth) => ({
1711
+ ...group,
1712
+ headers: group.headers.filter((header) => !seenColumnIds.has(header.column.id)).map((header) => {
1713
+ seenColumnIds.add(header.column.id);
1714
+ if (header.isPlaceholder) {
1715
+ return {
1716
+ ...header,
1717
+ isPlaceholder: false,
1718
+ mergedRowSpan: fullDepth - depth
1719
+ };
1720
+ }
1721
+ return {
1722
+ ...header,
1723
+ mergedRowSpan: 1
1724
+ };
1725
+ })
1726
+ }));
1727
+ }
1728
+
1696
1729
  // src/core/useGlideTable.ts
1697
1730
  import {
1698
1731
  getCoreRowModel,
@@ -3339,6 +3372,7 @@ function DataTable({
3339
3372
  const PendingSlot = slots?.Pending ?? DefaultPending;
3340
3373
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
3341
3374
  const freezeOffsets = rowContextValue.columnFreeze.offsets;
3375
+ const headerGroups = getMergedHeaderGroups(table.getHeaderGroups());
3342
3376
  const contextValue = useMemo4(
3343
3377
  () => ({ ...rowContextValue, classNames }),
3344
3378
  [rowContextValue, classNames]
@@ -3408,7 +3442,7 @@ function DataTable({
3408
3442
  style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
3409
3443
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
3410
3444
  children: [
3411
- /* @__PURE__ */ jsx6("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx6(
3445
+ /* @__PURE__ */ jsx6("thead", { className: cn("data-table-head", classNames?.head), children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx6(
3412
3446
  "tr",
3413
3447
  {
3414
3448
  className: cn("data-table-head-row", classNames?.headRow),
@@ -3422,7 +3456,8 @@ function DataTable({
3422
3456
  });
3423
3457
  const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
3424
3458
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
3425
- isHeader: true
3459
+ isHeader: true,
3460
+ headerTop: header.depth * DATA_TABLE_HEADER_ROW_HEIGHT
3426
3461
  });
3427
3462
  const headerStyle = {
3428
3463
  ...sizeStyle,
@@ -3431,6 +3466,8 @@ function DataTable({
3431
3466
  return /* @__PURE__ */ jsxs5(
3432
3467
  "th",
3433
3468
  {
3469
+ colSpan: header.colSpan,
3470
+ rowSpan: header.mergedRowSpan,
3434
3471
  "data-resizing": header.column.getIsResizing() ? "" : void 0,
3435
3472
  "data-frozen": freezeOffset?.side,
3436
3473
  "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
@@ -3636,6 +3673,46 @@ function buildColumnDef(props, sort, onSort) {
3636
3673
  }
3637
3674
  };
3638
3675
  }
3676
+ function resolveGroupId(props, index) {
3677
+ if (props.id) return props.id;
3678
+ if (typeof props.header === "string" || typeof props.header === "number") {
3679
+ return `group:${props.header}:${index}`;
3680
+ }
3681
+ return `group:${index}`;
3682
+ }
3683
+ function buildColumnDefsFromTree(nodes, sort, onSort) {
3684
+ return nodes.map((node, index) => {
3685
+ if (node.type === "leaf") {
3686
+ return buildColumnDef(node.props, sort, onSort);
3687
+ }
3688
+ const childDefs = buildColumnDefsFromTree(node.columns, sort, onSort);
3689
+ const { header, align, headerClassName } = node.props;
3690
+ return {
3691
+ id: resolveGroupId(node.props, index),
3692
+ header: (
3693
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
3694
+ () => header
3695
+ ),
3696
+ columns: childDefs,
3697
+ enableResizing: false,
3698
+ meta: {
3699
+ align,
3700
+ headerClassName
3701
+ }
3702
+ };
3703
+ });
3704
+ }
3705
+ function countLeafColumns(nodes) {
3706
+ let count = 0;
3707
+ for (const node of nodes) {
3708
+ if (node.type === "leaf") {
3709
+ count += 1;
3710
+ } else {
3711
+ count += countLeafColumns(node.columns);
3712
+ }
3713
+ }
3714
+ return count;
3715
+ }
3639
3716
 
3640
3717
  // src/components/ui/table/components/Table/parseTableChildren.ts
3641
3718
  import { Children, isValidElement as isValidElement2 } from "react";
@@ -3645,6 +3722,7 @@ import { isValidElement } from "react";
3645
3722
  var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
3646
3723
  var TABLE_BODY_DISPLAY_NAME = "Table.Body";
3647
3724
  var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
3725
+ var TABLE_COLUMN_GROUP_DISPLAY_NAME = "Table.ColumnGroup";
3648
3726
  var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
3649
3727
  function getComponentDisplayName(type) {
3650
3728
  if (typeof type === "function" || typeof type === "object" && type !== null) {
@@ -3661,6 +3739,9 @@ function isTableBodyElement(child) {
3661
3739
  function isTableColumnElement(child) {
3662
3740
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
3663
3741
  }
3742
+ function isTableColumnGroupElement(child) {
3743
+ return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_GROUP_DISPLAY_NAME;
3744
+ }
3664
3745
  function isTablePaginationElement(child) {
3665
3746
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
3666
3747
  }
@@ -3687,26 +3768,38 @@ function parseTableChildren(children) {
3687
3768
  }
3688
3769
  return slots;
3689
3770
  }
3690
- function flattenColumnElements(children) {
3771
+ function walkColumnTreeNodes(children) {
3691
3772
  const result = [];
3692
3773
  for (const child of Children.toArray(children)) {
3693
3774
  if (isTableColumnElement(child)) {
3694
- result.push(child);
3775
+ result.push({
3776
+ type: "leaf",
3777
+ props: child.props
3778
+ });
3779
+ continue;
3780
+ }
3781
+ if (isTableColumnGroupElement(child)) {
3782
+ const groupProps = child.props;
3783
+ result.push({
3784
+ type: "group",
3785
+ props: groupProps,
3786
+ columns: walkColumnTreeNodes(groupProps.children)
3787
+ });
3695
3788
  continue;
3696
3789
  }
3697
3790
  if (isValidElement2(child)) {
3698
3791
  const nested = child.props.children;
3699
3792
  if (nested != null) {
3700
- result.push(...flattenColumnElements(nested));
3793
+ result.push(...walkColumnTreeNodes(nested));
3701
3794
  }
3702
3795
  }
3703
3796
  }
3704
3797
  return result;
3705
3798
  }
3706
- function extractColumnElements(header) {
3799
+ function extractColumnTree(header) {
3707
3800
  if (!header) return [];
3708
3801
  const { children } = header.props;
3709
- return flattenColumnElements(children);
3802
+ return walkColumnTreeNodes(children);
3710
3803
  }
3711
3804
 
3712
3805
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -3722,6 +3815,13 @@ function TableColumn(props) {
3722
3815
  }
3723
3816
  TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
3724
3817
 
3818
+ // src/components/ui/table/components/Table/TableColumnGroup.tsx
3819
+ function TableColumnGroup(props) {
3820
+ void props;
3821
+ return null;
3822
+ }
3823
+ TableColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3824
+
3725
3825
  // src/components/ui/table/components/Table/tableDataPipeline.ts
3726
3826
  function sortTableData(data, sort) {
3727
3827
  if (!sort) return data;
@@ -3829,11 +3929,11 @@ function TableRoot({
3829
3929
  return null;
3830
3930
  });
3831
3931
  }, []);
3832
- const columns = useMemo5(() => {
3833
- return extractColumnElements(header).map(
3834
- (columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
3835
- );
3836
- }, [header, sort, handleSort]);
3932
+ const columnTree = useMemo5(() => extractColumnTree(header), [header]);
3933
+ const columns = useMemo5(
3934
+ () => buildColumnDefsFromTree(columnTree, sort, handleSort),
3935
+ [columnTree, sort, handleSort]
3936
+ );
3837
3937
  const paginationProps = paginationElement?.props;
3838
3938
  const pageSize = paginationProps?.pageSize ?? 10;
3839
3939
  const page = paginationProps?.page ?? 1;
@@ -3843,7 +3943,7 @@ function TableRoot({
3843
3943
  if (!paginationProps) return sortedData;
3844
3944
  return paginateTableData(sortedData, page, pageSize);
3845
3945
  }, [data, sort, paginationProps, page, pageSize]);
3846
- if (columns.length === 0) {
3946
+ if (countLeafColumns(columnTree) === 0) {
3847
3947
  console.warn("[Table] Declare at least one Table.Column inside Table.Header.");
3848
3948
  }
3849
3949
  return /* @__PURE__ */ jsxs8("div", { className: "TableJSX", children: [
@@ -3876,6 +3976,11 @@ function createTable() {
3876
3976
  return null;
3877
3977
  }
3878
3978
  Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
3979
+ function ColumnGroup(props) {
3980
+ void props;
3981
+ return null;
3982
+ }
3983
+ ColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3879
3984
  return Object.assign(
3880
3985
  function BoundTable(props) {
3881
3986
  return /* @__PURE__ */ jsx9(TableRoot, { ...props });
@@ -3883,6 +3988,7 @@ function createTable() {
3883
3988
  {
3884
3989
  Header: TableHeader,
3885
3990
  Column,
3991
+ ColumnGroup,
3886
3992
  Body: TableBody,
3887
3993
  Pagination: TablePagination
3888
3994
  }
@@ -3891,6 +3997,7 @@ function createTable() {
3891
3997
  var Table = Object.assign(TableRoot, {
3892
3998
  Header: TableHeader,
3893
3999
  Column: TableColumn,
4000
+ ColumnGroup: TableColumnGroup,
3894
4001
  Body: TableBody,
3895
4002
  Pagination: TablePagination
3896
4003
  });
package/dist/core.cjs CHANGED
@@ -1189,7 +1189,7 @@ function getColumnFreezeStyle(offset, options) {
1189
1189
  position: "sticky",
1190
1190
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
1191
1191
  zIndex: zBase + offset.stack,
1192
- ...options?.isHeader ? { top: 0 } : {}
1192
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
1193
1193
  };
1194
1194
  }
1195
1195
 
package/dist/core.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as CellEditType, e as DataTableClassNames, R as RowSelectionMode, c as ColumnFreezeOffset, l as SearchResultItem, m as SearchStatus, h as DataTableProps, g as DataTableLabels, f as DataTableCopyActions, P as PasteMode, k as RowsPastePayload } from './types-tkOzBZ4V.cjs';
2
- export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, I as INLINE_SEARCH_MAX_RESULTS, S as SearchCorpusRow, o as buildColumnFreezeOffsets, p as buildFlatSearchCorpus, q as buildSearchMatchKey, r as buildSearchMatchKeys, s as buildTreeSearchCorpus, t as cellValueToSearchText, u as collectAncestorKeysToExpand, v as collectSearchMatchesInRange, w as createSearchRegex, x as escapeSearchRegex, y as formatSearchResultLabel, z as getColumnFreezeEdgeAttr, A as getColumnFreezeStyle, B as mapSearchResultToVisibleItem, E as mapSearchResultsToVisibleKeys, F as nextSearchIndex, G as nextSearchStride, H as previousSearchIndex, J as resolveColumnFreezeSide, K as resolveDataTableLabels } from './types-tkOzBZ4V.cjs';
1
+ import { M as CellEditType, e as DataTableClassNames, R as RowSelectionMode, c as ColumnFreezeOffset, l as SearchResultItem, m as SearchStatus, h as DataTableProps, g as DataTableLabels, f as DataTableCopyActions, P as PasteMode, k as RowsPastePayload } from './types-Cs9MiZs1.cjs';
2
+ export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, I as INLINE_SEARCH_MAX_RESULTS, S as SearchCorpusRow, p as buildColumnFreezeOffsets, q as buildFlatSearchCorpus, r as buildSearchMatchKey, s as buildSearchMatchKeys, t as buildTreeSearchCorpus, u as cellValueToSearchText, v as collectAncestorKeysToExpand, w as collectSearchMatchesInRange, x as createSearchRegex, y as escapeSearchRegex, z as formatSearchResultLabel, A as getColumnFreezeEdgeAttr, B as getColumnFreezeStyle, E as mapSearchResultToVisibleItem, F as mapSearchResultsToVisibleKeys, G as nextSearchIndex, H as nextSearchStride, J as previousSearchIndex, K as resolveColumnFreezeSide, L as resolveDataTableLabels } from './types-Cs9MiZs1.cjs';
3
3
  import { Row, ColumnDef, Table, Updater, RowSelectionState } from '@tanstack/react-table';
4
4
  export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
5
5
  import { Virtualizer, VirtualItem } from '@tanstack/react-virtual';
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as CellEditType, e as DataTableClassNames, R as RowSelectionMode, c as ColumnFreezeOffset, l as SearchResultItem, m as SearchStatus, h as DataTableProps, g as DataTableLabels, f as DataTableCopyActions, P as PasteMode, k as RowsPastePayload } from './types-tkOzBZ4V.js';
2
- export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, I as INLINE_SEARCH_MAX_RESULTS, S as SearchCorpusRow, o as buildColumnFreezeOffsets, p as buildFlatSearchCorpus, q as buildSearchMatchKey, r as buildSearchMatchKeys, s as buildTreeSearchCorpus, t as cellValueToSearchText, u as collectAncestorKeysToExpand, v as collectSearchMatchesInRange, w as createSearchRegex, x as escapeSearchRegex, y as formatSearchResultLabel, z as getColumnFreezeEdgeAttr, A as getColumnFreezeStyle, B as mapSearchResultToVisibleItem, E as mapSearchResultsToVisibleKeys, F as nextSearchIndex, G as nextSearchStride, H as previousSearchIndex, J as resolveColumnFreezeSide, K as resolveDataTableLabels } from './types-tkOzBZ4V.js';
1
+ import { M as CellEditType, e as DataTableClassNames, R as RowSelectionMode, c as ColumnFreezeOffset, l as SearchResultItem, m as SearchStatus, h as DataTableProps, g as DataTableLabels, f as DataTableCopyActions, P as PasteMode, k as RowsPastePayload } from './types-Cs9MiZs1.js';
2
+ export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, I as INLINE_SEARCH_MAX_RESULTS, S as SearchCorpusRow, p as buildColumnFreezeOffsets, q as buildFlatSearchCorpus, r as buildSearchMatchKey, s as buildSearchMatchKeys, t as buildTreeSearchCorpus, u as cellValueToSearchText, v as collectAncestorKeysToExpand, w as collectSearchMatchesInRange, x as createSearchRegex, y as escapeSearchRegex, z as formatSearchResultLabel, A as getColumnFreezeEdgeAttr, B as getColumnFreezeStyle, E as mapSearchResultToVisibleItem, F as mapSearchResultsToVisibleKeys, G as nextSearchIndex, H as nextSearchStride, J as previousSearchIndex, K as resolveColumnFreezeSide, L as resolveDataTableLabels } from './types-Cs9MiZs1.js';
3
3
  import { Row, ColumnDef, Table, Updater, RowSelectionState } from '@tanstack/react-table';
4
4
  export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
5
5
  import { Virtualizer, VirtualItem } from '@tanstack/react-virtual';
package/dist/core.js CHANGED
@@ -1111,7 +1111,7 @@ function getColumnFreezeStyle(offset, options) {
1111
1111
  position: "sticky",
1112
1112
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
1113
1113
  zIndex: zBase + offset.stack,
1114
- ...options?.isHeader ? { top: 0 } : {}
1114
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
1115
1115
  };
1116
1116
  }
1117
1117
 
package/dist/index.cjs CHANGED
@@ -132,6 +132,7 @@ var ROW_HOVER_CLASS = "row-hoverable";
132
132
  var ROW_HOVERED_BG_CLASS = "row-hovered";
133
133
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
134
134
  var DATA_TABLE_ROW_HEIGHT = 44;
135
+ var DATA_TABLE_HEADER_ROW_HEIGHT = 40;
135
136
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
136
137
  var DATA_TABLE_COLUMN_SIZE = 150;
137
138
  var DATA_TABLE_COLUMN_MIN_SIZE = 40;
@@ -1200,7 +1201,7 @@ function getColumnFreezeStyle(offset, options) {
1200
1201
  position: "sticky",
1201
1202
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
1202
1203
  zIndex: zBase + offset.stack,
1203
- ...options?.isHeader ? { top: 0 } : {}
1204
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
1204
1205
  };
1205
1206
  }
1206
1207
 
@@ -3350,6 +3351,38 @@ function DataTableToolbar({
3350
3351
  ] });
3351
3352
  }
3352
3353
 
3354
+ // src/components/ui/table/features/column-groups/mergeHeaderGroups.ts
3355
+ function getMergedHeaderGroups(headerGroups) {
3356
+ if (headerGroups.length <= 1) {
3357
+ return headerGroups.map((group) => ({
3358
+ ...group,
3359
+ headers: group.headers.map((header) => ({
3360
+ ...header,
3361
+ mergedRowSpan: 1
3362
+ }))
3363
+ }));
3364
+ }
3365
+ const seenColumnIds = /* @__PURE__ */ new Set();
3366
+ const fullDepth = headerGroups.length;
3367
+ return headerGroups.map((group, depth) => ({
3368
+ ...group,
3369
+ headers: group.headers.filter((header) => !seenColumnIds.has(header.column.id)).map((header) => {
3370
+ seenColumnIds.add(header.column.id);
3371
+ if (header.isPlaceholder) {
3372
+ return {
3373
+ ...header,
3374
+ isPlaceholder: false,
3375
+ mergedRowSpan: fullDepth - depth
3376
+ };
3377
+ }
3378
+ return {
3379
+ ...header,
3380
+ mergedRowSpan: 1
3381
+ };
3382
+ })
3383
+ }));
3384
+ }
3385
+
3353
3386
  // src/components/ui/table/components/DataTable/DataTable.tsx
3354
3387
  var import_jsx_runtime6 = require("react/jsx-runtime");
3355
3388
  function DefaultScroll({
@@ -3435,6 +3468,7 @@ function DataTable({
3435
3468
  const PendingSlot = slots?.Pending ?? DefaultPending;
3436
3469
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
3437
3470
  const freezeOffsets = rowContextValue.columnFreeze.offsets;
3471
+ const headerGroups = getMergedHeaderGroups(table.getHeaderGroups());
3438
3472
  const contextValue = (0, import_react8.useMemo)(
3439
3473
  () => ({ ...rowContextValue, classNames }),
3440
3474
  [rowContextValue, classNames]
@@ -3504,7 +3538,7 @@ function DataTable({
3504
3538
  style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
3505
3539
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
3506
3540
  children: [
3507
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3541
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { className: cn("data-table-head", classNames?.head), children: headerGroups.map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3508
3542
  "tr",
3509
3543
  {
3510
3544
  className: cn("data-table-head-row", classNames?.headRow),
@@ -3518,7 +3552,8 @@ function DataTable({
3518
3552
  });
3519
3553
  const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
3520
3554
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
3521
- isHeader: true
3555
+ isHeader: true,
3556
+ headerTop: header.depth * DATA_TABLE_HEADER_ROW_HEIGHT
3522
3557
  });
3523
3558
  const headerStyle = {
3524
3559
  ...sizeStyle,
@@ -3527,6 +3562,8 @@ function DataTable({
3527
3562
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3528
3563
  "th",
3529
3564
  {
3565
+ colSpan: header.colSpan,
3566
+ rowSpan: header.mergedRowSpan,
3530
3567
  "data-resizing": header.column.getIsResizing() ? "" : void 0,
3531
3568
  "data-frozen": freezeOffset?.side,
3532
3569
  "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
@@ -3732,6 +3769,46 @@ function buildColumnDef(props, sort, onSort) {
3732
3769
  }
3733
3770
  };
3734
3771
  }
3772
+ function resolveGroupId(props, index) {
3773
+ if (props.id) return props.id;
3774
+ if (typeof props.header === "string" || typeof props.header === "number") {
3775
+ return `group:${props.header}:${index}`;
3776
+ }
3777
+ return `group:${index}`;
3778
+ }
3779
+ function buildColumnDefsFromTree(nodes, sort, onSort) {
3780
+ return nodes.map((node, index) => {
3781
+ if (node.type === "leaf") {
3782
+ return buildColumnDef(node.props, sort, onSort);
3783
+ }
3784
+ const childDefs = buildColumnDefsFromTree(node.columns, sort, onSort);
3785
+ const { header, align, headerClassName } = node.props;
3786
+ return {
3787
+ id: resolveGroupId(node.props, index),
3788
+ header: (
3789
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
3790
+ () => header
3791
+ ),
3792
+ columns: childDefs,
3793
+ enableResizing: false,
3794
+ meta: {
3795
+ align,
3796
+ headerClassName
3797
+ }
3798
+ };
3799
+ });
3800
+ }
3801
+ function countLeafColumns(nodes) {
3802
+ let count = 0;
3803
+ for (const node of nodes) {
3804
+ if (node.type === "leaf") {
3805
+ count += 1;
3806
+ } else {
3807
+ count += countLeafColumns(node.columns);
3808
+ }
3809
+ }
3810
+ return count;
3811
+ }
3735
3812
 
3736
3813
  // src/components/ui/table/components/Table/parseTableChildren.ts
3737
3814
  var import_react10 = require("react");
@@ -3741,6 +3818,7 @@ var import_react9 = require("react");
3741
3818
  var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
3742
3819
  var TABLE_BODY_DISPLAY_NAME = "Table.Body";
3743
3820
  var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
3821
+ var TABLE_COLUMN_GROUP_DISPLAY_NAME = "Table.ColumnGroup";
3744
3822
  var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
3745
3823
  function getComponentDisplayName(type) {
3746
3824
  if (typeof type === "function" || typeof type === "object" && type !== null) {
@@ -3757,6 +3835,9 @@ function isTableBodyElement(child) {
3757
3835
  function isTableColumnElement(child) {
3758
3836
  return (0, import_react9.isValidElement)(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
3759
3837
  }
3838
+ function isTableColumnGroupElement(child) {
3839
+ return (0, import_react9.isValidElement)(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_GROUP_DISPLAY_NAME;
3840
+ }
3760
3841
  function isTablePaginationElement(child) {
3761
3842
  return (0, import_react9.isValidElement)(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
3762
3843
  }
@@ -3783,26 +3864,38 @@ function parseTableChildren(children) {
3783
3864
  }
3784
3865
  return slots;
3785
3866
  }
3786
- function flattenColumnElements(children) {
3867
+ function walkColumnTreeNodes(children) {
3787
3868
  const result = [];
3788
3869
  for (const child of import_react10.Children.toArray(children)) {
3789
3870
  if (isTableColumnElement(child)) {
3790
- result.push(child);
3871
+ result.push({
3872
+ type: "leaf",
3873
+ props: child.props
3874
+ });
3875
+ continue;
3876
+ }
3877
+ if (isTableColumnGroupElement(child)) {
3878
+ const groupProps = child.props;
3879
+ result.push({
3880
+ type: "group",
3881
+ props: groupProps,
3882
+ columns: walkColumnTreeNodes(groupProps.children)
3883
+ });
3791
3884
  continue;
3792
3885
  }
3793
3886
  if ((0, import_react10.isValidElement)(child)) {
3794
3887
  const nested = child.props.children;
3795
3888
  if (nested != null) {
3796
- result.push(...flattenColumnElements(nested));
3889
+ result.push(...walkColumnTreeNodes(nested));
3797
3890
  }
3798
3891
  }
3799
3892
  }
3800
3893
  return result;
3801
3894
  }
3802
- function extractColumnElements(header) {
3895
+ function extractColumnTree(header) {
3803
3896
  if (!header) return [];
3804
3897
  const { children } = header.props;
3805
- return flattenColumnElements(children);
3898
+ return walkColumnTreeNodes(children);
3806
3899
  }
3807
3900
 
3808
3901
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -3818,6 +3911,13 @@ function TableColumn(props) {
3818
3911
  }
3819
3912
  TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
3820
3913
 
3914
+ // src/components/ui/table/components/Table/TableColumnGroup.tsx
3915
+ function TableColumnGroup(props) {
3916
+ void props;
3917
+ return null;
3918
+ }
3919
+ TableColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3920
+
3821
3921
  // src/components/ui/table/components/Table/tableDataPipeline.ts
3822
3922
  function sortTableData(data, sort) {
3823
3923
  if (!sort) return data;
@@ -3925,11 +4025,11 @@ function TableRoot({
3925
4025
  return null;
3926
4026
  });
3927
4027
  }, []);
3928
- const columns = (0, import_react11.useMemo)(() => {
3929
- return extractColumnElements(header).map(
3930
- (columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
3931
- );
3932
- }, [header, sort, handleSort]);
4028
+ const columnTree = (0, import_react11.useMemo)(() => extractColumnTree(header), [header]);
4029
+ const columns = (0, import_react11.useMemo)(
4030
+ () => buildColumnDefsFromTree(columnTree, sort, handleSort),
4031
+ [columnTree, sort, handleSort]
4032
+ );
3933
4033
  const paginationProps = paginationElement?.props;
3934
4034
  const pageSize = paginationProps?.pageSize ?? 10;
3935
4035
  const page = paginationProps?.page ?? 1;
@@ -3939,7 +4039,7 @@ function TableRoot({
3939
4039
  if (!paginationProps) return sortedData;
3940
4040
  return paginateTableData(sortedData, page, pageSize);
3941
4041
  }, [data, sort, paginationProps, page, pageSize]);
3942
- if (columns.length === 0) {
4042
+ if (countLeafColumns(columnTree) === 0) {
3943
4043
  console.warn("[Table] Declare at least one Table.Column inside Table.Header.");
3944
4044
  }
3945
4045
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "TableJSX", children: [
@@ -3972,6 +4072,11 @@ function createTable() {
3972
4072
  return null;
3973
4073
  }
3974
4074
  Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
4075
+ function ColumnGroup(props) {
4076
+ void props;
4077
+ return null;
4078
+ }
4079
+ ColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3975
4080
  return Object.assign(
3976
4081
  function BoundTable(props) {
3977
4082
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TableRoot, { ...props });
@@ -3979,6 +4084,7 @@ function createTable() {
3979
4084
  {
3980
4085
  Header: TableHeader,
3981
4086
  Column,
4087
+ ColumnGroup,
3982
4088
  Body: TableBody,
3983
4089
  Pagination: TablePagination
3984
4090
  }
@@ -3987,6 +4093,7 @@ function createTable() {
3987
4093
  var Table = Object.assign(TableRoot, {
3988
4094
  Header: TableHeader,
3989
4095
  Column: TableColumn,
4096
+ ColumnGroup: TableColumnGroup,
3990
4097
  Body: TableBody,
3991
4098
  Pagination: TablePagination
3992
4099
  });
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, c as ColumnFreezeOffset, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, e as DataTableClassNames, f as DataTableCopyActions, g as DataTableLabels, h as DataTableProps, i as DataTableScrollSlotProps, j as DataTableSlots, I as INLINE_SEARCH_MAX_RESULTS, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, S as SearchCorpusRow, l as SearchResultItem, m as SearchStatus, T as TableColumnProps, n as TableProps, o as buildColumnFreezeOffsets, p as buildFlatSearchCorpus, q as buildSearchMatchKey, r as buildSearchMatchKeys, s as buildTreeSearchCorpus, t as cellValueToSearchText, u as collectAncestorKeysToExpand, v as collectSearchMatchesInRange, w as createSearchRegex, x as escapeSearchRegex, y as formatSearchResultLabel, z as getColumnFreezeEdgeAttr, A as getColumnFreezeStyle, B as mapSearchResultToVisibleItem, E as mapSearchResultsToVisibleKeys, F as nextSearchIndex, G as nextSearchStride, H as previousSearchIndex, J as resolveColumnFreezeSide, K as resolveDataTableLabels } from './types-tkOzBZ4V.cjs';
1
+ export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, c as ColumnFreezeOffset, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, e as DataTableClassNames, f as DataTableCopyActions, g as DataTableLabels, h as DataTableProps, i as DataTableScrollSlotProps, j as DataTableSlots, I as INLINE_SEARCH_MAX_RESULTS, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, S as SearchCorpusRow, l as SearchResultItem, m as SearchStatus, T as TableColumnGroupProps, n as TableColumnProps, o as TableProps, p as buildColumnFreezeOffsets, q as buildFlatSearchCorpus, r as buildSearchMatchKey, s as buildSearchMatchKeys, t as buildTreeSearchCorpus, u as cellValueToSearchText, v as collectAncestorKeysToExpand, w as collectSearchMatchesInRange, x as createSearchRegex, y as escapeSearchRegex, z as formatSearchResultLabel, A as getColumnFreezeEdgeAttr, B as getColumnFreezeStyle, E as mapSearchResultToVisibleItem, F as mapSearchResultsToVisibleKeys, G as nextSearchIndex, H as nextSearchStride, J as previousSearchIndex, K as resolveColumnFreezeSide, L as resolveDataTableLabels } from './types-Cs9MiZs1.cjs';
2
2
  export { CELL_SELECTION_EDGES_CLASS, CellSelectionBounds, ColumnRowSpanMap, CopyRowEntry, CopySelectionMode, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DragState, EditingCell, RowSpanInfo, TreeRow, UseConvertTreeDataParams, UseGlideTableOptions, UseGlideTableResult, UseInlineSearchOptions, UseInlineSearchResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, buildRowsPastePayload, canExpandRow, collectCopyRowEntries, collectCopyRows, collectFillChanges, collectRowSpanColumns, flattenSubtreeRows, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getColumnSizeStyle, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, isEditablePasteTarget, measureMergedSpanRowHeights, parseCellEditValue, parseClipboardTSV, parseClipboardTSVWithDepths, resolvePasteColumnIds, resolveRowSelection, resolveRowSpanAt, rowRangeToHeightRatios, serializeCopyRowsToTSV, serializeSelectionToTSV, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable, useInlineSearch, writeSelectionToClipboard } from './core.cjs';
3
3
  export { DataTable, Table, TableCompoundComponent, createTable } from './compound.cjs';
4
4
  export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, c as ColumnFreezeOffset, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, e as DataTableClassNames, f as DataTableCopyActions, g as DataTableLabels, h as DataTableProps, i as DataTableScrollSlotProps, j as DataTableSlots, I as INLINE_SEARCH_MAX_RESULTS, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, S as SearchCorpusRow, l as SearchResultItem, m as SearchStatus, T as TableColumnProps, n as TableProps, o as buildColumnFreezeOffsets, p as buildFlatSearchCorpus, q as buildSearchMatchKey, r as buildSearchMatchKeys, s as buildTreeSearchCorpus, t as cellValueToSearchText, u as collectAncestorKeysToExpand, v as collectSearchMatchesInRange, w as createSearchRegex, x as escapeSearchRegex, y as formatSearchResultLabel, z as getColumnFreezeEdgeAttr, A as getColumnFreezeStyle, B as mapSearchResultToVisibleItem, E as mapSearchResultsToVisibleKeys, F as nextSearchIndex, G as nextSearchStride, H as previousSearchIndex, J as resolveColumnFreezeSide, K as resolveDataTableLabels } from './types-tkOzBZ4V.js';
1
+ export { C as ColumnFreezeColumnInput, a as ColumnFreezeEdgeSide, b as ColumnFreezeMeta, c as ColumnFreezeOffset, d as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, e as DataTableClassNames, f as DataTableCopyActions, g as DataTableLabels, h as DataTableProps, i as DataTableScrollSlotProps, j as DataTableSlots, I as INLINE_SEARCH_MAX_RESULTS, P as PasteMode, R as RowSelectionMode, k as RowsPastePayload, S as SearchCorpusRow, l as SearchResultItem, m as SearchStatus, T as TableColumnGroupProps, n as TableColumnProps, o as TableProps, p as buildColumnFreezeOffsets, q as buildFlatSearchCorpus, r as buildSearchMatchKey, s as buildSearchMatchKeys, t as buildTreeSearchCorpus, u as cellValueToSearchText, v as collectAncestorKeysToExpand, w as collectSearchMatchesInRange, x as createSearchRegex, y as escapeSearchRegex, z as formatSearchResultLabel, A as getColumnFreezeEdgeAttr, B as getColumnFreezeStyle, E as mapSearchResultToVisibleItem, F as mapSearchResultsToVisibleKeys, G as nextSearchIndex, H as nextSearchStride, J as previousSearchIndex, K as resolveColumnFreezeSide, L as resolveDataTableLabels } from './types-Cs9MiZs1.js';
2
2
  export { CELL_SELECTION_EDGES_CLASS, CellSelectionBounds, ColumnRowSpanMap, CopyRowEntry, CopySelectionMode, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DragState, EditingCell, RowSpanInfo, TreeRow, UseConvertTreeDataParams, UseGlideTableOptions, UseGlideTableResult, UseInlineSearchOptions, UseInlineSearchResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, buildRowsPastePayload, canExpandRow, collectCopyRowEntries, collectCopyRows, collectFillChanges, collectRowSpanColumns, flattenSubtreeRows, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getColumnSizeStyle, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, isEditablePasteTarget, measureMergedSpanRowHeights, parseCellEditValue, parseClipboardTSV, parseClipboardTSVWithDepths, resolvePasteColumnIds, resolveRowSelection, resolveRowSpanAt, rowRangeToHeightRatios, serializeCopyRowsToTSV, serializeSelectionToTSV, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable, useInlineSearch, writeSelectionToClipboard } from './core.js';
3
3
  export { DataTable, Table, TableCompoundComponent, createTable } from './compound.js';
4
4
  export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ var ROW_HOVER_CLASS = "row-hoverable";
51
51
  var ROW_HOVERED_BG_CLASS = "row-hovered";
52
52
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
53
53
  var DATA_TABLE_ROW_HEIGHT = 44;
54
+ var DATA_TABLE_HEADER_ROW_HEIGHT = 40;
54
55
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
55
56
  var DATA_TABLE_COLUMN_SIZE = 150;
56
57
  var DATA_TABLE_COLUMN_MIN_SIZE = 40;
@@ -1119,7 +1120,7 @@ function getColumnFreezeStyle(offset, options) {
1119
1120
  position: "sticky",
1120
1121
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
1121
1122
  zIndex: zBase + offset.stack,
1122
- ...options?.isHeader ? { top: 0 } : {}
1123
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
1123
1124
  };
1124
1125
  }
1125
1126
 
@@ -3276,6 +3277,38 @@ function DataTableToolbar({
3276
3277
  ] });
3277
3278
  }
3278
3279
 
3280
+ // src/components/ui/table/features/column-groups/mergeHeaderGroups.ts
3281
+ function getMergedHeaderGroups(headerGroups) {
3282
+ if (headerGroups.length <= 1) {
3283
+ return headerGroups.map((group) => ({
3284
+ ...group,
3285
+ headers: group.headers.map((header) => ({
3286
+ ...header,
3287
+ mergedRowSpan: 1
3288
+ }))
3289
+ }));
3290
+ }
3291
+ const seenColumnIds = /* @__PURE__ */ new Set();
3292
+ const fullDepth = headerGroups.length;
3293
+ return headerGroups.map((group, depth) => ({
3294
+ ...group,
3295
+ headers: group.headers.filter((header) => !seenColumnIds.has(header.column.id)).map((header) => {
3296
+ seenColumnIds.add(header.column.id);
3297
+ if (header.isPlaceholder) {
3298
+ return {
3299
+ ...header,
3300
+ isPlaceholder: false,
3301
+ mergedRowSpan: fullDepth - depth
3302
+ };
3303
+ }
3304
+ return {
3305
+ ...header,
3306
+ mergedRowSpan: 1
3307
+ };
3308
+ })
3309
+ }));
3310
+ }
3311
+
3279
3312
  // src/components/ui/table/components/DataTable/DataTable.tsx
3280
3313
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3281
3314
  function DefaultScroll({
@@ -3361,6 +3394,7 @@ function DataTable({
3361
3394
  const PendingSlot = slots?.Pending ?? DefaultPending;
3362
3395
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
3363
3396
  const freezeOffsets = rowContextValue.columnFreeze.offsets;
3397
+ const headerGroups = getMergedHeaderGroups(table.getHeaderGroups());
3364
3398
  const contextValue = useMemo4(
3365
3399
  () => ({ ...rowContextValue, classNames }),
3366
3400
  [rowContextValue, classNames]
@@ -3430,7 +3464,7 @@ function DataTable({
3430
3464
  style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
3431
3465
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
3432
3466
  children: [
3433
- /* @__PURE__ */ jsx6("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx6(
3467
+ /* @__PURE__ */ jsx6("thead", { className: cn("data-table-head", classNames?.head), children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx6(
3434
3468
  "tr",
3435
3469
  {
3436
3470
  className: cn("data-table-head-row", classNames?.headRow),
@@ -3444,7 +3478,8 @@ function DataTable({
3444
3478
  });
3445
3479
  const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
3446
3480
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
3447
- isHeader: true
3481
+ isHeader: true,
3482
+ headerTop: header.depth * DATA_TABLE_HEADER_ROW_HEIGHT
3448
3483
  });
3449
3484
  const headerStyle = {
3450
3485
  ...sizeStyle,
@@ -3453,6 +3488,8 @@ function DataTable({
3453
3488
  return /* @__PURE__ */ jsxs5(
3454
3489
  "th",
3455
3490
  {
3491
+ colSpan: header.colSpan,
3492
+ rowSpan: header.mergedRowSpan,
3456
3493
  "data-resizing": header.column.getIsResizing() ? "" : void 0,
3457
3494
  "data-frozen": freezeOffset?.side,
3458
3495
  "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
@@ -3658,6 +3695,46 @@ function buildColumnDef(props, sort, onSort) {
3658
3695
  }
3659
3696
  };
3660
3697
  }
3698
+ function resolveGroupId(props, index) {
3699
+ if (props.id) return props.id;
3700
+ if (typeof props.header === "string" || typeof props.header === "number") {
3701
+ return `group:${props.header}:${index}`;
3702
+ }
3703
+ return `group:${index}`;
3704
+ }
3705
+ function buildColumnDefsFromTree(nodes, sort, onSort) {
3706
+ return nodes.map((node, index) => {
3707
+ if (node.type === "leaf") {
3708
+ return buildColumnDef(node.props, sort, onSort);
3709
+ }
3710
+ const childDefs = buildColumnDefsFromTree(node.columns, sort, onSort);
3711
+ const { header, align, headerClassName } = node.props;
3712
+ return {
3713
+ id: resolveGroupId(node.props, index),
3714
+ header: (
3715
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
3716
+ () => header
3717
+ ),
3718
+ columns: childDefs,
3719
+ enableResizing: false,
3720
+ meta: {
3721
+ align,
3722
+ headerClassName
3723
+ }
3724
+ };
3725
+ });
3726
+ }
3727
+ function countLeafColumns(nodes) {
3728
+ let count = 0;
3729
+ for (const node of nodes) {
3730
+ if (node.type === "leaf") {
3731
+ count += 1;
3732
+ } else {
3733
+ count += countLeafColumns(node.columns);
3734
+ }
3735
+ }
3736
+ return count;
3737
+ }
3661
3738
 
3662
3739
  // src/components/ui/table/components/Table/parseTableChildren.ts
3663
3740
  import { Children, isValidElement as isValidElement2 } from "react";
@@ -3667,6 +3744,7 @@ import { isValidElement } from "react";
3667
3744
  var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
3668
3745
  var TABLE_BODY_DISPLAY_NAME = "Table.Body";
3669
3746
  var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
3747
+ var TABLE_COLUMN_GROUP_DISPLAY_NAME = "Table.ColumnGroup";
3670
3748
  var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
3671
3749
  function getComponentDisplayName(type) {
3672
3750
  if (typeof type === "function" || typeof type === "object" && type !== null) {
@@ -3683,6 +3761,9 @@ function isTableBodyElement(child) {
3683
3761
  function isTableColumnElement(child) {
3684
3762
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
3685
3763
  }
3764
+ function isTableColumnGroupElement(child) {
3765
+ return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_GROUP_DISPLAY_NAME;
3766
+ }
3686
3767
  function isTablePaginationElement(child) {
3687
3768
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
3688
3769
  }
@@ -3709,26 +3790,38 @@ function parseTableChildren(children) {
3709
3790
  }
3710
3791
  return slots;
3711
3792
  }
3712
- function flattenColumnElements(children) {
3793
+ function walkColumnTreeNodes(children) {
3713
3794
  const result = [];
3714
3795
  for (const child of Children.toArray(children)) {
3715
3796
  if (isTableColumnElement(child)) {
3716
- result.push(child);
3797
+ result.push({
3798
+ type: "leaf",
3799
+ props: child.props
3800
+ });
3801
+ continue;
3802
+ }
3803
+ if (isTableColumnGroupElement(child)) {
3804
+ const groupProps = child.props;
3805
+ result.push({
3806
+ type: "group",
3807
+ props: groupProps,
3808
+ columns: walkColumnTreeNodes(groupProps.children)
3809
+ });
3717
3810
  continue;
3718
3811
  }
3719
3812
  if (isValidElement2(child)) {
3720
3813
  const nested = child.props.children;
3721
3814
  if (nested != null) {
3722
- result.push(...flattenColumnElements(nested));
3815
+ result.push(...walkColumnTreeNodes(nested));
3723
3816
  }
3724
3817
  }
3725
3818
  }
3726
3819
  return result;
3727
3820
  }
3728
- function extractColumnElements(header) {
3821
+ function extractColumnTree(header) {
3729
3822
  if (!header) return [];
3730
3823
  const { children } = header.props;
3731
- return flattenColumnElements(children);
3824
+ return walkColumnTreeNodes(children);
3732
3825
  }
3733
3826
 
3734
3827
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -3744,6 +3837,13 @@ function TableColumn(props) {
3744
3837
  }
3745
3838
  TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
3746
3839
 
3840
+ // src/components/ui/table/components/Table/TableColumnGroup.tsx
3841
+ function TableColumnGroup(props) {
3842
+ void props;
3843
+ return null;
3844
+ }
3845
+ TableColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3846
+
3747
3847
  // src/components/ui/table/components/Table/tableDataPipeline.ts
3748
3848
  function sortTableData(data, sort) {
3749
3849
  if (!sort) return data;
@@ -3851,11 +3951,11 @@ function TableRoot({
3851
3951
  return null;
3852
3952
  });
3853
3953
  }, []);
3854
- const columns = useMemo5(() => {
3855
- return extractColumnElements(header).map(
3856
- (columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
3857
- );
3858
- }, [header, sort, handleSort]);
3954
+ const columnTree = useMemo5(() => extractColumnTree(header), [header]);
3955
+ const columns = useMemo5(
3956
+ () => buildColumnDefsFromTree(columnTree, sort, handleSort),
3957
+ [columnTree, sort, handleSort]
3958
+ );
3859
3959
  const paginationProps = paginationElement?.props;
3860
3960
  const pageSize = paginationProps?.pageSize ?? 10;
3861
3961
  const page = paginationProps?.page ?? 1;
@@ -3865,7 +3965,7 @@ function TableRoot({
3865
3965
  if (!paginationProps) return sortedData;
3866
3966
  return paginateTableData(sortedData, page, pageSize);
3867
3967
  }, [data, sort, paginationProps, page, pageSize]);
3868
- if (columns.length === 0) {
3968
+ if (countLeafColumns(columnTree) === 0) {
3869
3969
  console.warn("[Table] Declare at least one Table.Column inside Table.Header.");
3870
3970
  }
3871
3971
  return /* @__PURE__ */ jsxs8("div", { className: "TableJSX", children: [
@@ -3898,6 +3998,11 @@ function createTable() {
3898
3998
  return null;
3899
3999
  }
3900
4000
  Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
4001
+ function ColumnGroup(props) {
4002
+ void props;
4003
+ return null;
4004
+ }
4005
+ ColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3901
4006
  return Object.assign(
3902
4007
  function BoundTable(props) {
3903
4008
  return /* @__PURE__ */ jsx9(TableRoot, { ...props });
@@ -3905,6 +4010,7 @@ function createTable() {
3905
4010
  {
3906
4011
  Header: TableHeader,
3907
4012
  Column,
4013
+ ColumnGroup,
3908
4014
  Body: TableBody,
3909
4015
  Pagination: TablePagination
3910
4016
  }
@@ -3913,6 +4019,7 @@ function createTable() {
3913
4019
  var Table = Object.assign(TableRoot, {
3914
4020
  Header: TableHeader,
3915
4021
  Column: TableColumn,
4022
+ ColumnGroup: TableColumnGroup,
3916
4023
  Body: TableBody,
3917
4024
  Pagination: TablePagination
3918
4025
  });
@@ -46,6 +46,7 @@ declare function buildColumnFreezeOffsets(columns: ColumnFreezeColumnInput[]): M
46
46
  /** Sticky position styles for a frozen header or body cell. */
47
47
  declare function getColumnFreezeStyle(offset: ColumnFreezeOffset | undefined, options?: {
48
48
  isHeader?: boolean;
49
+ headerTop?: number;
49
50
  }): CSSProperties | undefined;
50
51
 
51
52
  /** Cell match as `[colIndex, rowIndex]` — same order as Glide Data Grid `Item`.
@@ -478,8 +479,18 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
478
479
  headerClassName?: string;
479
480
  render?: (value: K extends keyof T ? T[K] : unknown, row: Row<T>, index: number) => ReactNode;
480
481
  };
482
+ /** Declares a multi-row header group wrapping leaf `Table.Column`s. */
483
+ type TableColumnGroupProps = {
484
+ /** Group header label shown in the top header row */
485
+ header: ReactNode;
486
+ children: ReactNode;
487
+ /** Stable group id. Auto-generated when omitted */
488
+ id?: string;
489
+ align?: "left" | "center" | "right";
490
+ headerClassName?: string;
491
+ };
481
492
  type TableProps<T extends Record<string, unknown>> = Omit<DataTableProps<T>, "columns"> & {
482
493
  children: ReactNode;
483
494
  };
484
495
 
485
- export { getColumnFreezeStyle as A, mapSearchResultToVisibleItem as B, type ColumnFreezeColumnInput as C, DEFAULT_DATA_TABLE_LABELS as D, mapSearchResultsToVisibleKeys as E, nextSearchIndex as F, nextSearchStride as G, previousSearchIndex as H, INLINE_SEARCH_MAX_RESULTS as I, resolveColumnFreezeSide as J, resolveDataTableLabels as K, type CellEditType as L, type PasteMode as P, type RowSelectionMode as R, type SearchCorpusRow as S, type TableColumnProps as T, type ColumnFreezeEdgeSide as a, type ColumnFreezeMeta as b, type ColumnFreezeOffset as c, type ColumnFreezeSide as d, type DataTableClassNames as e, type DataTableCopyActions as f, type DataTableLabels as g, type DataTableProps as h, type DataTableScrollSlotProps as i, type DataTableSlots as j, type RowsPastePayload as k, type SearchResultItem as l, type SearchStatus as m, type TableProps as n, buildColumnFreezeOffsets as o, buildFlatSearchCorpus as p, buildSearchMatchKey as q, buildSearchMatchKeys as r, buildTreeSearchCorpus as s, cellValueToSearchText as t, collectAncestorKeysToExpand as u, collectSearchMatchesInRange as v, createSearchRegex as w, escapeSearchRegex as x, formatSearchResultLabel as y, getColumnFreezeEdgeAttr as z };
496
+ export { getColumnFreezeEdgeAttr as A, getColumnFreezeStyle as B, type ColumnFreezeColumnInput as C, DEFAULT_DATA_TABLE_LABELS as D, mapSearchResultToVisibleItem as E, mapSearchResultsToVisibleKeys as F, nextSearchIndex as G, nextSearchStride as H, INLINE_SEARCH_MAX_RESULTS as I, previousSearchIndex as J, resolveColumnFreezeSide as K, resolveDataTableLabels as L, type CellEditType as M, type PasteMode as P, type RowSelectionMode as R, type SearchCorpusRow as S, type TableColumnGroupProps as T, type ColumnFreezeEdgeSide as a, type ColumnFreezeMeta as b, type ColumnFreezeOffset as c, type ColumnFreezeSide as d, type DataTableClassNames as e, type DataTableCopyActions as f, type DataTableLabels as g, type DataTableProps as h, type DataTableScrollSlotProps as i, type DataTableSlots as j, type RowsPastePayload as k, type SearchResultItem as l, type SearchStatus as m, type TableColumnProps as n, type TableProps as o, buildColumnFreezeOffsets as p, buildFlatSearchCorpus as q, buildSearchMatchKey as r, buildSearchMatchKeys as s, buildTreeSearchCorpus as t, cellValueToSearchText as u, collectAncestorKeysToExpand as v, collectSearchMatchesInRange as w, createSearchRegex as x, escapeSearchRegex as y, formatSearchResultLabel as z };
@@ -46,6 +46,7 @@ declare function buildColumnFreezeOffsets(columns: ColumnFreezeColumnInput[]): M
46
46
  /** Sticky position styles for a frozen header or body cell. */
47
47
  declare function getColumnFreezeStyle(offset: ColumnFreezeOffset | undefined, options?: {
48
48
  isHeader?: boolean;
49
+ headerTop?: number;
49
50
  }): CSSProperties | undefined;
50
51
 
51
52
  /** Cell match as `[colIndex, rowIndex]` — same order as Glide Data Grid `Item`.
@@ -478,8 +479,18 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
478
479
  headerClassName?: string;
479
480
  render?: (value: K extends keyof T ? T[K] : unknown, row: Row<T>, index: number) => ReactNode;
480
481
  };
482
+ /** Declares a multi-row header group wrapping leaf `Table.Column`s. */
483
+ type TableColumnGroupProps = {
484
+ /** Group header label shown in the top header row */
485
+ header: ReactNode;
486
+ children: ReactNode;
487
+ /** Stable group id. Auto-generated when omitted */
488
+ id?: string;
489
+ align?: "left" | "center" | "right";
490
+ headerClassName?: string;
491
+ };
481
492
  type TableProps<T extends Record<string, unknown>> = Omit<DataTableProps<T>, "columns"> & {
482
493
  children: ReactNode;
483
494
  };
484
495
 
485
- export { getColumnFreezeStyle as A, mapSearchResultToVisibleItem as B, type ColumnFreezeColumnInput as C, DEFAULT_DATA_TABLE_LABELS as D, mapSearchResultsToVisibleKeys as E, nextSearchIndex as F, nextSearchStride as G, previousSearchIndex as H, INLINE_SEARCH_MAX_RESULTS as I, resolveColumnFreezeSide as J, resolveDataTableLabels as K, type CellEditType as L, type PasteMode as P, type RowSelectionMode as R, type SearchCorpusRow as S, type TableColumnProps as T, type ColumnFreezeEdgeSide as a, type ColumnFreezeMeta as b, type ColumnFreezeOffset as c, type ColumnFreezeSide as d, type DataTableClassNames as e, type DataTableCopyActions as f, type DataTableLabels as g, type DataTableProps as h, type DataTableScrollSlotProps as i, type DataTableSlots as j, type RowsPastePayload as k, type SearchResultItem as l, type SearchStatus as m, type TableProps as n, buildColumnFreezeOffsets as o, buildFlatSearchCorpus as p, buildSearchMatchKey as q, buildSearchMatchKeys as r, buildTreeSearchCorpus as s, cellValueToSearchText as t, collectAncestorKeysToExpand as u, collectSearchMatchesInRange as v, createSearchRegex as w, escapeSearchRegex as x, formatSearchResultLabel as y, getColumnFreezeEdgeAttr as z };
496
+ export { getColumnFreezeEdgeAttr as A, getColumnFreezeStyle as B, type ColumnFreezeColumnInput as C, DEFAULT_DATA_TABLE_LABELS as D, mapSearchResultToVisibleItem as E, mapSearchResultsToVisibleKeys as F, nextSearchIndex as G, nextSearchStride as H, INLINE_SEARCH_MAX_RESULTS as I, previousSearchIndex as J, resolveColumnFreezeSide as K, resolveDataTableLabels as L, type CellEditType as M, type PasteMode as P, type RowSelectionMode as R, type SearchCorpusRow as S, type TableColumnGroupProps as T, type ColumnFreezeEdgeSide as a, type ColumnFreezeMeta as b, type ColumnFreezeOffset as c, type ColumnFreezeSide as d, type DataTableClassNames as e, type DataTableCopyActions as f, type DataTableLabels as g, type DataTableProps as h, type DataTableScrollSlotProps as i, type DataTableSlots as j, type RowsPastePayload as k, type SearchResultItem as l, type SearchStatus as m, type TableColumnProps as n, type TableProps as o, buildColumnFreezeOffsets as p, buildFlatSearchCorpus as q, buildSearchMatchKey as r, buildSearchMatchKeys as s, buildTreeSearchCorpus as t, cellValueToSearchText as u, collectAncestorKeysToExpand as v, collectSearchMatchesInRange as w, createSearchRegex as x, escapeSearchRegex as y, formatSearchResultLabel as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-glide-table",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/zpxlffjrm/react-glide-table.git"