react-glide-table 1.6.0 → 2.0.1

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.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;
@@ -70,20 +71,6 @@ function getCellEditDraftValue(value) {
70
71
  if (value === null || value === void 0) return "";
71
72
  return String(value);
72
73
  }
73
- function applyCellEdit(data, rows, rowIndex, colIndex, raw) {
74
- const cell = rows[rowIndex]?.getVisibleCells()[colIndex];
75
- if (!cell) return null;
76
- const columnDef = cell.column.columnDef;
77
- if (!isColumnEditable(columnDef)) return null;
78
- const accessorKey = getColumnAccessorKey(columnDef);
79
- if (!accessorKey) return null;
80
- const parsed = parseCellEditValue(raw, getColumnEditType(columnDef));
81
- if (!parsed.ok) return null;
82
- const newData = data.map((row) => ({ ...row }));
83
- if (!newData[rowIndex]) return null;
84
- newData[rowIndex][accessorKey] = parsed.value;
85
- return newData;
86
- }
87
74
 
88
75
  // src/components/ui/table/features/cell-selection/cellSelection.ts
89
76
  var INITIAL_DRAG_STATE = {
@@ -468,10 +455,40 @@ function getColumnFreezeStyle(offset, options) {
468
455
  return {
469
456
  position: "sticky",
470
457
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
471
- zIndex: zBase + offset.stack,
472
- ...options?.isHeader ? { top: 0 } : {}
458
+ zIndex: zBase + offset.stack
459
+ };
460
+ }
461
+ function resolveHeaderFreezeOffset(column, freezeOffsets) {
462
+ const direct = freezeOffsets.get(column.id);
463
+ if (direct) return direct;
464
+ const leaves = typeof column.getLeafColumns === "function" ? column.getLeafColumns() : column.columns && column.columns.length > 0 ? flattenHeaderLeaves(column) : [];
465
+ if (leaves.length === 0) return void 0;
466
+ const leafOffsets = [];
467
+ for (const leaf of leaves) {
468
+ const offset2 = freezeOffsets.get(leaf.id);
469
+ if (!offset2) return void 0;
470
+ leafOffsets.push(offset2);
471
+ }
472
+ const side = leafOffsets[0]?.side;
473
+ if (!side || leafOffsets.some((offset2) => offset2.side !== side)) {
474
+ return void 0;
475
+ }
476
+ const offset = Math.min(...leafOffsets.map((item) => item.offset));
477
+ const leftmost = leafOffsets[0];
478
+ const rightmost = leafOffsets[leafOffsets.length - 1];
479
+ return {
480
+ side,
481
+ offset,
482
+ edgeLeft: leftmost.edgeLeft,
483
+ edgeRight: rightmost.edgeRight,
484
+ isEdge: leftmost.edgeLeft || rightmost.edgeRight,
485
+ stack: Math.max(...leafOffsets.map((item) => item.stack))
473
486
  };
474
487
  }
488
+ function flattenHeaderLeaves(column) {
489
+ if (!column.columns || column.columns.length === 0) return [column];
490
+ return column.columns.flatMap((child) => flattenHeaderLeaves(child));
491
+ }
475
492
 
476
493
  // src/components/ui/table/features/column-resize/columnResize.ts
477
494
  function getColumnSizeStyle(size, options) {
@@ -1693,6 +1710,38 @@ function DataTableToolbar({
1693
1710
  ] });
1694
1711
  }
1695
1712
 
1713
+ // src/components/ui/table/features/column-groups/mergeHeaderGroups.ts
1714
+ function getMergedHeaderGroups(headerGroups) {
1715
+ if (headerGroups.length <= 1) {
1716
+ return headerGroups.map((group) => ({
1717
+ ...group,
1718
+ headers: group.headers.map((header) => ({
1719
+ ...header,
1720
+ mergedRowSpan: 1
1721
+ }))
1722
+ }));
1723
+ }
1724
+ const seenColumnIds = /* @__PURE__ */ new Set();
1725
+ const fullDepth = headerGroups.length;
1726
+ return headerGroups.map((group, depth) => ({
1727
+ ...group,
1728
+ headers: group.headers.filter((header) => !seenColumnIds.has(header.column.id)).map((header) => {
1729
+ seenColumnIds.add(header.column.id);
1730
+ if (header.isPlaceholder) {
1731
+ return {
1732
+ ...header,
1733
+ isPlaceholder: false,
1734
+ mergedRowSpan: fullDepth - depth
1735
+ };
1736
+ }
1737
+ return {
1738
+ ...header,
1739
+ mergedRowSpan: 1
1740
+ };
1741
+ })
1742
+ }));
1743
+ }
1744
+
1696
1745
  // src/core/useGlideTable.ts
1697
1746
  import {
1698
1747
  getCoreRowModel,
@@ -1711,6 +1760,39 @@ import {
1711
1760
 
1712
1761
  // src/components/ui/table/features/cell-edit/useCellEdit.ts
1713
1762
  import { useCallback, useEffect as useEffect3, useRef as useRef3, useState } from "react";
1763
+
1764
+ // src/components/ui/table/features/cell-render/commitCellValue.ts
1765
+ function commitCellValue({
1766
+ data,
1767
+ rows,
1768
+ rowId,
1769
+ columnId,
1770
+ value,
1771
+ onCellChange,
1772
+ onDataChange
1773
+ }) {
1774
+ if (!onCellChange && !onDataChange) return true;
1775
+ if (onCellChange) {
1776
+ onCellChange(rowId, columnId, value);
1777
+ return true;
1778
+ }
1779
+ const row = rows.find((item) => item.id === rowId);
1780
+ if (!row) return false;
1781
+ const cell = row.getAllCells().find((item) => item.column.id === columnId) ?? row.getVisibleCells().find((item) => item.column.id === columnId);
1782
+ if (!cell) return false;
1783
+ const accessorKey = getColumnAccessorKey(cell.column.columnDef);
1784
+ if (!accessorKey) return false;
1785
+ const dataIndex = row.index;
1786
+ if (dataIndex < 0 || dataIndex >= data.length) return false;
1787
+ const next = data.map((item) => ({ ...item }));
1788
+ const target = next[dataIndex];
1789
+ if (!target) return false;
1790
+ target[accessorKey] = value;
1791
+ onDataChange?.(next);
1792
+ return true;
1793
+ }
1794
+
1795
+ // src/components/ui/table/features/cell-edit/useCellEdit.ts
1714
1796
  function useCellEdit({
1715
1797
  data,
1716
1798
  rows,
@@ -1745,21 +1827,23 @@ function useCellEdit({
1745
1827
  cancelEdit();
1746
1828
  return true;
1747
1829
  }
1748
- const value = raw ?? draftValueRef.current;
1749
1830
  if (!isColumnEditable(cell.column.columnDef)) {
1750
1831
  cancelEdit();
1751
1832
  return true;
1752
1833
  }
1834
+ const value = raw ?? draftValueRef.current;
1753
1835
  const parsed = parseCellEditValue(value, getColumnEditType(cell.column.columnDef));
1754
1836
  if (!parsed.ok) return false;
1755
- if (onCellChange) {
1756
- onCellChange(row.id, cell.column.id, parsed.value);
1757
- cancelEdit();
1758
- return true;
1759
- }
1760
- const next = applyCellEdit(data, rows, current.rowIndex, current.colIndex, value);
1761
- if (!next) return false;
1762
- onDataChange?.(next);
1837
+ const committed = commitCellValue({
1838
+ data,
1839
+ rows,
1840
+ rowId: row.id,
1841
+ columnId: cell.column.id,
1842
+ value: parsed.value,
1843
+ onCellChange,
1844
+ onDataChange
1845
+ });
1846
+ if (!committed) return false;
1763
1847
  cancelEdit();
1764
1848
  return true;
1765
1849
  },
@@ -1788,13 +1872,250 @@ function useCellEdit({
1788
1872
  };
1789
1873
  }
1790
1874
 
1875
+ // src/components/ui/table/features/cell-render/builtins.tsx
1876
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1877
+ function asString(value) {
1878
+ if (value == null) return "";
1879
+ return String(value);
1880
+ }
1881
+ function asStringList(value) {
1882
+ if (Array.isArray(value)) {
1883
+ return value.map((item) => asString(item)).filter(Boolean);
1884
+ }
1885
+ if (value == null || value === "") return [];
1886
+ return [asString(value)];
1887
+ }
1888
+ function asDrilldownItems(value) {
1889
+ if (!Array.isArray(value)) return [];
1890
+ return value.flatMap((item) => {
1891
+ if (item == null) return [];
1892
+ if (typeof item === "string") return [{ text: item }];
1893
+ if (typeof item === "object") {
1894
+ const record = item;
1895
+ const text = asString(record.text ?? record.label ?? "");
1896
+ if (!text) return [];
1897
+ const img = record.img ?? record.image;
1898
+ return [{ text, ...typeof img === "string" ? { img } : {} }];
1899
+ }
1900
+ return [{ text: asString(item) }];
1901
+ });
1902
+ }
1903
+ function escapeHtml(text) {
1904
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1905
+ }
1906
+ function simpleMarkdownToHtml(source) {
1907
+ const escaped = escapeHtml(source);
1908
+ return escaped.replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>").replace(/\n/g, "<br />");
1909
+ }
1910
+ function TextCell({ value }) {
1911
+ return asString(value);
1912
+ }
1913
+ function NumberCell({ value }) {
1914
+ if (value == null || value === "") return null;
1915
+ return asString(value);
1916
+ }
1917
+ function BooleanCell({ value, update, cellProps }) {
1918
+ const checked = Boolean(value);
1919
+ const readonly = Boolean(cellProps?.readonly);
1920
+ return /* @__PURE__ */ jsx6(
1921
+ "input",
1922
+ {
1923
+ type: "checkbox",
1924
+ className: "data-table-cell-boolean",
1925
+ checked,
1926
+ disabled: readonly,
1927
+ "aria-checked": checked,
1928
+ onChange: (event) => {
1929
+ if (readonly) return;
1930
+ update(event.target.checked);
1931
+ },
1932
+ onClick: (event) => {
1933
+ event.stopPropagation();
1934
+ },
1935
+ onMouseDown: (event) => {
1936
+ event.stopPropagation();
1937
+ }
1938
+ }
1939
+ );
1940
+ }
1941
+ function sanitizeUriHref(raw) {
1942
+ const href = raw.trim();
1943
+ if (!href) return null;
1944
+ if (href.startsWith("/") || href.startsWith("#") || href.startsWith("?") || href.startsWith("./") || href.startsWith("../")) {
1945
+ return href;
1946
+ }
1947
+ try {
1948
+ const parsed = new URL(href);
1949
+ const protocol = parsed.protocol.toLowerCase();
1950
+ if (protocol === "http:" || protocol === "https:" || protocol === "mailto:") {
1951
+ return href;
1952
+ }
1953
+ return null;
1954
+ } catch {
1955
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return null;
1956
+ return href;
1957
+ }
1958
+ }
1959
+ function UriCell({ value }) {
1960
+ const raw = asString(value);
1961
+ if (!raw) return null;
1962
+ const href = sanitizeUriHref(raw);
1963
+ if (!href) {
1964
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-uri", children: raw });
1965
+ }
1966
+ return /* @__PURE__ */ jsx6(
1967
+ "a",
1968
+ {
1969
+ className: "data-table-cell-uri",
1970
+ href,
1971
+ target: "_blank",
1972
+ rel: "noopener noreferrer",
1973
+ onClick: (event) => event.stopPropagation(),
1974
+ onMouseDown: (event) => event.stopPropagation(),
1975
+ children: raw
1976
+ }
1977
+ );
1978
+ }
1979
+ function ImageCell({ value }) {
1980
+ const urls = asStringList(value);
1981
+ if (urls.length === 0) return null;
1982
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-image", children: urls.map((url, index) => /* @__PURE__ */ jsx6(
1983
+ "img",
1984
+ {
1985
+ src: url,
1986
+ alt: "",
1987
+ className: "data-table-cell-image-item"
1988
+ },
1989
+ `${index}:${url}`
1990
+ )) });
1991
+ }
1992
+ function BubbleCell({ value }) {
1993
+ const items = asStringList(value);
1994
+ if (items.length === 0) return null;
1995
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-bubble", children: items.map((item, index) => /* @__PURE__ */ jsx6("span", { className: "data-table-cell-bubble-item", children: item }, `${index}:${item}`)) });
1996
+ }
1997
+ function MarkdownCell({ value }) {
1998
+ const source = asString(value);
1999
+ if (!source) return null;
2000
+ return /* @__PURE__ */ jsx6(
2001
+ "span",
2002
+ {
2003
+ className: "data-table-cell-markdown",
2004
+ dangerouslySetInnerHTML: { __html: simpleMarkdownToHtml(source) }
2005
+ }
2006
+ );
2007
+ }
2008
+ function DrilldownCell({ value }) {
2009
+ const items = asDrilldownItems(value);
2010
+ if (items.length === 0) return null;
2011
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-drilldown", children: items.map((item, index) => /* @__PURE__ */ jsxs5(
2012
+ "span",
2013
+ {
2014
+ className: "data-table-cell-drilldown-item",
2015
+ children: [
2016
+ item.img ? /* @__PURE__ */ jsx6(
2017
+ "img",
2018
+ {
2019
+ src: item.img,
2020
+ alt: "",
2021
+ className: "data-table-cell-drilldown-image"
2022
+ }
2023
+ ) : null,
2024
+ /* @__PURE__ */ jsx6("span", { className: "data-table-cell-drilldown-text", children: item.text })
2025
+ ]
2026
+ },
2027
+ `${index}:${item.text}:${item.img ?? ""}`
2028
+ )) });
2029
+ }
2030
+ function LoadingCell() {
2031
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-loading", "aria-busy": "true" });
2032
+ }
2033
+ function ProtectedCell() {
2034
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-protected", "aria-label": "protected", children: "****" });
2035
+ }
2036
+ function RowIdCell({ value }) {
2037
+ return /* @__PURE__ */ jsx6("span", { className: "data-table-cell-row-id", children: asString(value) });
2038
+ }
2039
+ var BUILTIN_RENDER_MAP = {
2040
+ text: TextCell,
2041
+ number: NumberCell,
2042
+ boolean: BooleanCell,
2043
+ uri: UriCell,
2044
+ image: ImageCell,
2045
+ bubble: BubbleCell,
2046
+ markdown: MarkdownCell,
2047
+ drilldown: DrilldownCell,
2048
+ loading: LoadingCell,
2049
+ protected: ProtectedCell,
2050
+ "row-id": RowIdCell
2051
+ };
2052
+ var BUILTIN_CELL_RENDERERS = Object.keys(BUILTIN_RENDER_MAP).map((kind) => ({
2053
+ kind,
2054
+ render: BUILTIN_RENDER_MAP[kind]
2055
+ }));
2056
+
2057
+ // src/components/ui/table/features/cell-render/registry.ts
2058
+ function createCellRendererRegistry(customRenderers = []) {
2059
+ const registry = /* @__PURE__ */ new Map();
2060
+ for (const renderer of BUILTIN_CELL_RENDERERS) {
2061
+ registry.set(renderer.kind, renderer);
2062
+ }
2063
+ for (const renderer of customRenderers) {
2064
+ registry.set(renderer.kind, renderer);
2065
+ }
2066
+ return registry;
2067
+ }
2068
+ function resolveCellRenderer(registry, kind, ctx) {
2069
+ if (!kind) return void 0;
2070
+ const renderer = registry.get(kind);
2071
+ if (!renderer) return void 0;
2072
+ if (renderer.isMatch && !renderer.isMatch(ctx)) {
2073
+ return void 0;
2074
+ }
2075
+ return renderer;
2076
+ }
2077
+ function formatDefaultCellValue(value) {
2078
+ if (value == null) return null;
2079
+ if (typeof value === "string") return value;
2080
+ if (typeof value === "number" || typeof value === "boolean") {
2081
+ return String(value);
2082
+ }
2083
+ if (typeof value === "bigint") return value.toString();
2084
+ return String(value);
2085
+ }
2086
+
1791
2087
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
1792
2088
  import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
1793
2089
 
1794
2090
  // src/components/ui/table/features/cell-selection/copyData.ts
2091
+ function formatPrimitive(value) {
2092
+ if (value === null || value === void 0) return "";
2093
+ if (typeof value === "string") return value;
2094
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
2095
+ return String(value);
2096
+ }
2097
+ return "";
2098
+ }
2099
+ function formatObjectValue(value) {
2100
+ const text = value.text ?? value.label ?? value.name ?? value.title;
2101
+ if (text != null && text !== "") {
2102
+ return formatCellValue(text);
2103
+ }
2104
+ try {
2105
+ return JSON.stringify(value);
2106
+ } catch {
2107
+ return "";
2108
+ }
2109
+ }
1795
2110
  function formatCellValue(value) {
1796
2111
  if (value === null || value === void 0) return "";
1797
- return String(value);
2112
+ if (Array.isArray(value)) {
2113
+ return value.map((item) => formatCellValue(item)).filter((item) => item.length > 0).join(", ");
2114
+ }
2115
+ if (typeof value === "object") {
2116
+ return formatObjectValue(value);
2117
+ }
2118
+ return formatPrimitive(value);
1798
2119
  }
1799
2120
  function getNestedValue(row, path) {
1800
2121
  if (!path.includes(".")) return row[path];
@@ -2683,6 +3004,7 @@ function useGlideTable(options) {
2683
3004
  onDataChange,
2684
3005
  onCellChange,
2685
3006
  onBatchChange,
3007
+ cellRenderers,
2686
3008
  preserveRowSelection = false,
2687
3009
  toggleField,
2688
3010
  childField,
@@ -2908,6 +3230,22 @@ function useGlideTable(options) {
2908
3230
  commitEdit,
2909
3231
  cancelEdit
2910
3232
  } = useCellEdit({ data: tableData, rows, onDataChange, onCellChange });
3233
+ const cellRendererRegistry = useMemo3(
3234
+ () => createCellRendererRegistry(cellRenderers),
3235
+ [cellRenderers]
3236
+ );
3237
+ const commitRenderedCellValue = useCallback4(
3238
+ (rowId, columnId, value) => commitCellValue({
3239
+ data: tableData,
3240
+ rows,
3241
+ rowId,
3242
+ columnId,
3243
+ value,
3244
+ onCellChange,
3245
+ onDataChange
3246
+ }),
3247
+ [onCellChange, onDataChange, rows, tableData]
3248
+ );
2911
3249
  const handleCellMouseDownWithCommit = useCallback4(
2912
3250
  (rowIndex, colIndex, options2) => {
2913
3251
  const isSameEditingCell = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === colIndex;
@@ -3144,6 +3482,10 @@ function useGlideTable(options) {
3144
3482
  onCommitEdit: commitEdit,
3145
3483
  onCancelEdit: cancelEdit
3146
3484
  },
3485
+ cellRender: {
3486
+ registry: cellRendererRegistry,
3487
+ commitValue: commitRenderedCellValue
3488
+ },
3147
3489
  expand: {
3148
3490
  enableExpand,
3149
3491
  toggleField,
@@ -3190,6 +3532,8 @@ function useGlideTable(options) {
3190
3532
  startEdit,
3191
3533
  commitEdit,
3192
3534
  cancelEdit,
3535
+ cellRendererRegistry,
3536
+ commitRenderedCellValue,
3193
3537
  enableExpand,
3194
3538
  toggleField,
3195
3539
  expandedRows,
@@ -3255,20 +3599,20 @@ function useGlideTable(options) {
3255
3599
  }
3256
3600
 
3257
3601
  // src/components/ui/table/components/DataTable/DataTable.tsx
3258
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3602
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3259
3603
  function DefaultScroll({
3260
3604
  scrollRef,
3261
3605
  children,
3262
3606
  className
3263
3607
  }) {
3264
- return /* @__PURE__ */ jsx6("div", { ref: scrollRef, className: cn("data-table-scroll", className), children });
3608
+ return /* @__PURE__ */ jsx7("div", { ref: scrollRef, className: cn("data-table-scroll", className), children });
3265
3609
  }
3266
3610
  function DefaultPending({
3267
3611
  loadingText,
3268
3612
  className,
3269
3613
  classNames
3270
3614
  }) {
3271
- return /* @__PURE__ */ jsx6(
3615
+ return /* @__PURE__ */ jsx7(
3272
3616
  "div",
3273
3617
  {
3274
3618
  className: cn(
@@ -3278,7 +3622,7 @@ function DefaultPending({
3278
3622
  classNames?.pending,
3279
3623
  className
3280
3624
  ),
3281
- children: /* @__PURE__ */ jsx6("span", { className: cn("data-table-loading-text", classNames?.loadingText), children: loadingText })
3625
+ children: /* @__PURE__ */ jsx7("span", { className: cn("data-table-loading-text", classNames?.loadingText), children: loadingText })
3282
3626
  }
3283
3627
  );
3284
3628
  }
@@ -3287,7 +3631,7 @@ function DefaultEmpty({
3287
3631
  columnCount,
3288
3632
  classNames
3289
3633
  }) {
3290
- return /* @__PURE__ */ jsx6("tr", { children: /* @__PURE__ */ jsx6(
3634
+ return /* @__PURE__ */ jsx7("tr", { children: /* @__PURE__ */ jsx7(
3291
3635
  "td",
3292
3636
  {
3293
3637
  colSpan: columnCount,
@@ -3339,12 +3683,13 @@ function DataTable({
3339
3683
  const PendingSlot = slots?.Pending ?? DefaultPending;
3340
3684
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
3341
3685
  const freezeOffsets = rowContextValue.columnFreeze.offsets;
3686
+ const headerGroups = getMergedHeaderGroups(table.getHeaderGroups());
3342
3687
  const contextValue = useMemo4(
3343
3688
  () => ({ ...rowContextValue, classNames }),
3344
3689
  [rowContextValue, classNames]
3345
3690
  );
3346
3691
  if (isPending) {
3347
- return /* @__PURE__ */ jsx6(
3692
+ return /* @__PURE__ */ jsx7(
3348
3693
  PendingSlot,
3349
3694
  {
3350
3695
  loadingText,
@@ -3353,7 +3698,7 @@ function DataTable({
3353
3698
  }
3354
3699
  );
3355
3700
  }
3356
- return /* @__PURE__ */ jsxs5(
3701
+ return /* @__PURE__ */ jsxs6(
3357
3702
  "div",
3358
3703
  {
3359
3704
  ref: rootRef,
@@ -3367,7 +3712,7 @@ function DataTable({
3367
3712
  className
3368
3713
  ),
3369
3714
  children: [
3370
- /* @__PURE__ */ jsx6(
3715
+ /* @__PURE__ */ jsx7(
3371
3716
  ToolbarSlot,
3372
3717
  {
3373
3718
  filteredCount: filteredCount ?? tableData.length,
@@ -3379,7 +3724,7 @@ function DataTable({
3379
3724
  classNames
3380
3725
  }
3381
3726
  ),
3382
- enableInlineSearch ? /* @__PURE__ */ jsx6(
3727
+ enableInlineSearch ? /* @__PURE__ */ jsx7(
3383
3728
  DataTableSearch,
3384
3729
  {
3385
3730
  showSearch: inlineSearch.showSearch,
@@ -3401,14 +3746,14 @@ function DataTable({
3401
3746
  onPrevious: inlineSearch.goToPrevious
3402
3747
  }
3403
3748
  ) : null,
3404
- /* @__PURE__ */ jsx6(ScrollSlot, { scrollRef, className: classNames?.scroll, children: /* @__PURE__ */ jsxs5(
3749
+ /* @__PURE__ */ jsx7(ScrollSlot, { scrollRef, className: classNames?.scroll, children: /* @__PURE__ */ jsxs6(
3405
3750
  "table",
3406
3751
  {
3407
3752
  className: cn("data-table", classNames?.table),
3408
3753
  style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
3409
3754
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
3410
3755
  children: [
3411
- /* @__PURE__ */ jsx6("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx6(
3756
+ /* @__PURE__ */ jsx7("thead", { className: cn("data-table-head", classNames?.head), children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx7(
3412
3757
  "tr",
3413
3758
  {
3414
3759
  className: cn("data-table-head-row", classNames?.headRow),
@@ -3420,17 +3765,20 @@ function DataTable({
3420
3765
  force: enableColumnResize,
3421
3766
  lockMax: enableColumnResize
3422
3767
  });
3423
- const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
3768
+ const freezeOffset = enableColumnFreeze ? resolveHeaderFreezeOffset(header.column, freezeOffsets) : void 0;
3424
3769
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
3425
- isHeader: true
3770
+ isHeader: true,
3771
+ headerTop: header.depth * DATA_TABLE_HEADER_ROW_HEIGHT
3426
3772
  });
3427
3773
  const headerStyle = {
3428
3774
  ...sizeStyle,
3429
3775
  ...freezeStyle
3430
3776
  };
3431
- return /* @__PURE__ */ jsxs5(
3777
+ return /* @__PURE__ */ jsxs6(
3432
3778
  "th",
3433
3779
  {
3780
+ colSpan: header.colSpan,
3781
+ rowSpan: header.mergedRowSpan,
3434
3782
  "data-resizing": header.column.getIsResizing() ? "" : void 0,
3435
3783
  "data-frozen": freezeOffset?.side,
3436
3784
  "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
@@ -3443,8 +3791,11 @@ function DataTable({
3443
3791
  headerClassName
3444
3792
  ),
3445
3793
  children: [
3446
- header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext()),
3447
- canResize ? /* @__PURE__ */ jsx6(
3794
+ header.isPlaceholder ? null : flexRender2(
3795
+ header.column.columnDef.header,
3796
+ header.getContext()
3797
+ ),
3798
+ canResize ? /* @__PURE__ */ jsx7(
3448
3799
  "div",
3449
3800
  {
3450
3801
  role: "separator",
@@ -3470,20 +3821,20 @@ function DataTable({
3470
3821
  },
3471
3822
  headerGroup.id
3472
3823
  )) }),
3473
- /* @__PURE__ */ jsx6(DataTableContextProvider, { value: contextValue, children: /* @__PURE__ */ jsx6(
3824
+ /* @__PURE__ */ jsx7(DataTableContextProvider, { value: contextValue, children: /* @__PURE__ */ jsx7(
3474
3825
  "tbody",
3475
3826
  {
3476
3827
  onMouseLeave: clearHover,
3477
3828
  className: cn("data-table-body", classNames?.body),
3478
- children: rows.length === 0 ? /* @__PURE__ */ jsx6(
3829
+ children: rows.length === 0 ? /* @__PURE__ */ jsx7(
3479
3830
  EmptySlot,
3480
3831
  {
3481
3832
  emptyText,
3482
3833
  columnCount,
3483
3834
  classNames
3484
3835
  }
3485
- ) : shouldVirtualize ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
3486
- paddingTop > 0 && /* @__PURE__ */ jsx6(
3836
+ ) : shouldVirtualize ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3837
+ paddingTop > 0 && /* @__PURE__ */ jsx7(
3487
3838
  "tr",
3488
3839
  {
3489
3840
  "aria-hidden": true,
@@ -3491,7 +3842,7 @@ function DataTable({
3491
3842
  "data-table-virtual-spacer",
3492
3843
  classNames?.virtualSpacer
3493
3844
  ),
3494
- children: /* @__PURE__ */ jsx6(
3845
+ children: /* @__PURE__ */ jsx7(
3495
3846
  "td",
3496
3847
  {
3497
3848
  colSpan: columnCount,
@@ -3507,7 +3858,7 @@ function DataTable({
3507
3858
  virtualRows.map((virtualRow) => {
3508
3859
  const row = rows[virtualRow.index];
3509
3860
  if (!row) return null;
3510
- return /* @__PURE__ */ jsx6(
3861
+ return /* @__PURE__ */ jsx7(
3511
3862
  RowSlot,
3512
3863
  {
3513
3864
  row,
@@ -3518,7 +3869,7 @@ function DataTable({
3518
3869
  row.id
3519
3870
  );
3520
3871
  }),
3521
- paddingBottom > 0 && /* @__PURE__ */ jsx6(
3872
+ paddingBottom > 0 && /* @__PURE__ */ jsx7(
3522
3873
  "tr",
3523
3874
  {
3524
3875
  "aria-hidden": true,
@@ -3526,7 +3877,7 @@ function DataTable({
3526
3877
  "data-table-virtual-spacer",
3527
3878
  classNames?.virtualSpacer
3528
3879
  ),
3529
- children: /* @__PURE__ */ jsx6(
3880
+ children: /* @__PURE__ */ jsx7(
3530
3881
  "td",
3531
3882
  {
3532
3883
  colSpan: columnCount,
@@ -3539,7 +3890,7 @@ function DataTable({
3539
3890
  )
3540
3891
  }
3541
3892
  )
3542
- ] }) : rows.map((row) => /* @__PURE__ */ jsx6(
3893
+ ] }) : rows.map((row) => /* @__PURE__ */ jsx7(
3543
3894
  RowSlot,
3544
3895
  {
3545
3896
  row,
@@ -3558,10 +3909,44 @@ function DataTable({
3558
3909
  }
3559
3910
 
3560
3911
  // src/components/ui/table/components/Table/Table.tsx
3561
- import { useCallback as useCallback5, useMemo as useMemo5, useState as useState5 } from "react";
3912
+ import { useCallback as useCallback6, useMemo as useMemo5, useState as useState5 } from "react";
3913
+
3914
+ // src/components/ui/table/features/cell-render/ResolvedTableCell.tsx
3915
+ import { useCallback as useCallback5 } from "react";
3916
+ function ResolvedTableCell({
3917
+ info
3918
+ }) {
3919
+ const { cellRender } = useDataTableRowContext();
3920
+ const { row, column, getValue } = info;
3921
+ const meta = column.columnDef.meta;
3922
+ const value = getValue();
3923
+ const columnId = column.id;
3924
+ const update = useCallback5(
3925
+ (next) => {
3926
+ cellRender.commitValue(row.id, columnId, next);
3927
+ },
3928
+ [cellRender, columnId, row.id]
3929
+ );
3930
+ const ctx = {
3931
+ value,
3932
+ row,
3933
+ index: row.index,
3934
+ columnId,
3935
+ cellProps: meta?.cellProps,
3936
+ update
3937
+ };
3938
+ if (meta?.cellRender) {
3939
+ return meta.cellRender(ctx);
3940
+ }
3941
+ const renderer = resolveCellRenderer(cellRender.registry, meta?.kind, ctx);
3942
+ if (renderer) {
3943
+ return renderer.render(ctx);
3944
+ }
3945
+ return formatDefaultCellValue(value);
3946
+ }
3562
3947
 
3563
3948
  // src/components/ui/table/components/Table/buildColumnDef.tsx
3564
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3949
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
3565
3950
  function SortableHeader({
3566
3951
  label,
3567
3952
  field,
@@ -3570,15 +3955,18 @@ function SortableHeader({
3570
3955
  }) {
3571
3956
  const isActive = sort?.field === field;
3572
3957
  const Icon = isActive ? sort.direction === "asc" ? ArrowUp : ArrowDown : ArrowUpDown;
3573
- return /* @__PURE__ */ jsxs6(
3958
+ return /* @__PURE__ */ jsxs7(
3574
3959
  "button",
3575
3960
  {
3576
3961
  type: "button",
3577
- className: cn("SortableHeaderJSX", isActive ? "is-active" : "is-inactive"),
3962
+ className: cn(
3963
+ "SortableHeaderJSX",
3964
+ isActive ? "is-active" : "is-inactive"
3965
+ ),
3578
3966
  onClick: () => onSort(field),
3579
3967
  children: [
3580
- /* @__PURE__ */ jsx7("span", { children: label }),
3581
- /* @__PURE__ */ jsx7(Icon, { className: "sortable-header-icon" })
3968
+ /* @__PURE__ */ jsx8("span", { children: label }),
3969
+ /* @__PURE__ */ jsx8(Icon, { className: "sortable-header-icon" })
3582
3970
  ]
3583
3971
  }
3584
3972
  );
@@ -3600,6 +3988,8 @@ function buildColumnDef(props, sort, onSort) {
3600
3988
  editable,
3601
3989
  editType,
3602
3990
  editInputProps,
3991
+ kind,
3992
+ cellProps,
3603
3993
  className,
3604
3994
  headerClassName,
3605
3995
  render
@@ -3611,18 +4001,20 @@ function buildColumnDef(props, sort, onSort) {
3611
4001
  ...minWidth != null ? { minSize: minWidth } : {},
3612
4002
  ...maxWidth != null ? { maxSize: maxWidth } : {},
3613
4003
  ...resizable === false ? { enableResizing: false } : {},
3614
- header: sortable ? () => /* @__PURE__ */ jsx7(SortableHeader, { label: children, field, sort, onSort }) : (
4004
+ header: sortable ? () => /* @__PURE__ */ jsx8(
4005
+ SortableHeader,
4006
+ {
4007
+ label: children,
4008
+ field,
4009
+ sort,
4010
+ onSort
4011
+ }
4012
+ ) : (
3615
4013
  // eslint-disable-next-line @typescript-eslint/promise-function-async
3616
4014
  () => children
3617
4015
  ),
3618
- ...render ? {
3619
- // eslint-disable-next-line @typescript-eslint/promise-function-async
3620
- cell: ({ row, getValue }) => render(
3621
- getValue(),
3622
- row,
3623
- row.index
3624
- )
3625
- } : {},
4016
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
4017
+ cell: (info) => /* @__PURE__ */ jsx8(ResolvedTableCell, { info }),
3626
4018
  meta: {
3627
4019
  align,
3628
4020
  rowSpan,
@@ -3630,12 +4022,53 @@ function buildColumnDef(props, sort, onSort) {
3630
4022
  editable,
3631
4023
  editType,
3632
4024
  editInputProps,
4025
+ kind,
4026
+ cellProps,
4027
+ cellRender: render,
3633
4028
  frozen,
3634
4029
  className,
3635
4030
  headerClassName
3636
4031
  }
3637
4032
  };
3638
4033
  }
4034
+ function resolveGroupId(props, index) {
4035
+ if (props.id) return props.id;
4036
+ if (typeof props.header === "string" || typeof props.header === "number") {
4037
+ return `group:${props.header}:${index}`;
4038
+ }
4039
+ return `group:${index}`;
4040
+ }
4041
+ function buildColumnDefsFromTree(nodes, sort, onSort) {
4042
+ return nodes.map((node, index) => {
4043
+ if (node.type === "leaf") {
4044
+ return buildColumnDef(node.props, sort, onSort);
4045
+ }
4046
+ const childDefs = buildColumnDefsFromTree(node.columns, sort, onSort);
4047
+ const { header, align, headerClassName } = node.props;
4048
+ return {
4049
+ id: resolveGroupId(node.props, index),
4050
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
4051
+ header: () => header,
4052
+ columns: childDefs,
4053
+ enableResizing: false,
4054
+ meta: {
4055
+ align,
4056
+ headerClassName
4057
+ }
4058
+ };
4059
+ });
4060
+ }
4061
+ function countLeafColumns(nodes) {
4062
+ let count = 0;
4063
+ for (const node of nodes) {
4064
+ if (node.type === "leaf") {
4065
+ count += 1;
4066
+ } else {
4067
+ count += countLeafColumns(node.columns);
4068
+ }
4069
+ }
4070
+ return count;
4071
+ }
3639
4072
 
3640
4073
  // src/components/ui/table/components/Table/parseTableChildren.ts
3641
4074
  import { Children, isValidElement as isValidElement2 } from "react";
@@ -3645,6 +4078,7 @@ import { isValidElement } from "react";
3645
4078
  var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
3646
4079
  var TABLE_BODY_DISPLAY_NAME = "Table.Body";
3647
4080
  var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
4081
+ var TABLE_COLUMN_GROUP_DISPLAY_NAME = "Table.ColumnGroup";
3648
4082
  var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
3649
4083
  function getComponentDisplayName(type) {
3650
4084
  if (typeof type === "function" || typeof type === "object" && type !== null) {
@@ -3661,6 +4095,9 @@ function isTableBodyElement(child) {
3661
4095
  function isTableColumnElement(child) {
3662
4096
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
3663
4097
  }
4098
+ function isTableColumnGroupElement(child) {
4099
+ return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_GROUP_DISPLAY_NAME;
4100
+ }
3664
4101
  function isTablePaginationElement(child) {
3665
4102
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
3666
4103
  }
@@ -3687,26 +4124,38 @@ function parseTableChildren(children) {
3687
4124
  }
3688
4125
  return slots;
3689
4126
  }
3690
- function flattenColumnElements(children) {
4127
+ function walkColumnTreeNodes(children) {
3691
4128
  const result = [];
3692
4129
  for (const child of Children.toArray(children)) {
3693
4130
  if (isTableColumnElement(child)) {
3694
- result.push(child);
4131
+ result.push({
4132
+ type: "leaf",
4133
+ props: child.props
4134
+ });
4135
+ continue;
4136
+ }
4137
+ if (isTableColumnGroupElement(child)) {
4138
+ const groupProps = child.props;
4139
+ result.push({
4140
+ type: "group",
4141
+ props: groupProps,
4142
+ columns: walkColumnTreeNodes(groupProps.children)
4143
+ });
3695
4144
  continue;
3696
4145
  }
3697
4146
  if (isValidElement2(child)) {
3698
4147
  const nested = child.props.children;
3699
4148
  if (nested != null) {
3700
- result.push(...flattenColumnElements(nested));
4149
+ result.push(...walkColumnTreeNodes(nested));
3701
4150
  }
3702
4151
  }
3703
4152
  }
3704
4153
  return result;
3705
4154
  }
3706
- function extractColumnElements(header) {
4155
+ function extractColumnTree(header) {
3707
4156
  if (!header) return [];
3708
4157
  const { children } = header.props;
3709
- return flattenColumnElements(children);
4158
+ return walkColumnTreeNodes(children);
3710
4159
  }
3711
4160
 
3712
4161
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -3722,6 +4171,13 @@ function TableColumn(props) {
3722
4171
  }
3723
4172
  TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
3724
4173
 
4174
+ // src/components/ui/table/components/Table/TableColumnGroup.tsx
4175
+ function TableColumnGroup(props) {
4176
+ void props;
4177
+ return null;
4178
+ }
4179
+ TableColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
4180
+
3725
4181
  // src/components/ui/table/components/Table/tableDataPipeline.ts
3726
4182
  function sortTableData(data, sort) {
3727
4183
  if (!sort) return data;
@@ -3759,7 +4215,7 @@ function TableHeader(props) {
3759
4215
  TableHeader.displayName = TABLE_HEADER_DISPLAY_NAME;
3760
4216
 
3761
4217
  // src/components/ui/table/components/Table/TablePagination.tsx
3762
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4218
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3763
4219
  function TablePagination({
3764
4220
  page,
3765
4221
  pageSize = 10,
@@ -3771,8 +4227,8 @@ function TablePagination({
3771
4227
  const safePage = Math.min(Math.max(1, page), totalPages);
3772
4228
  const canGoPrev = safePage > 1;
3773
4229
  const canGoNext = safePage < totalPages;
3774
- return /* @__PURE__ */ jsxs7("div", { className: cn("TablePaginationJSX", className), children: [
3775
- /* @__PURE__ */ jsx8(
4230
+ return /* @__PURE__ */ jsxs8("div", { className: cn("TablePaginationJSX", className), children: [
4231
+ /* @__PURE__ */ jsx9(
3776
4232
  "button",
3777
4233
  {
3778
4234
  type: "button",
@@ -3780,15 +4236,15 @@ function TablePagination({
3780
4236
  disabled: !canGoPrev,
3781
4237
  onClick: () => onChange(safePage - 1),
3782
4238
  "aria-label": "Previous page",
3783
- children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "pagination-button-icon" })
4239
+ children: /* @__PURE__ */ jsx9(ChevronLeft, { className: "pagination-button-icon" })
3784
4240
  }
3785
4241
  ),
3786
- /* @__PURE__ */ jsxs7("span", { className: "pagination-label", children: [
4242
+ /* @__PURE__ */ jsxs8("span", { className: "pagination-label", children: [
3787
4243
  safePage,
3788
4244
  " / ",
3789
4245
  totalPages
3790
4246
  ] }),
3791
- /* @__PURE__ */ jsx8(
4247
+ /* @__PURE__ */ jsx9(
3792
4248
  "button",
3793
4249
  {
3794
4250
  type: "button",
@@ -3796,7 +4252,7 @@ function TablePagination({
3796
4252
  disabled: !canGoNext,
3797
4253
  onClick: () => onChange(safePage + 1),
3798
4254
  "aria-label": "Next page",
3799
- children: /* @__PURE__ */ jsx8(ChevronRight, { className: "pagination-button-icon" })
4255
+ children: /* @__PURE__ */ jsx9(ChevronRight, { className: "pagination-button-icon" })
3800
4256
  }
3801
4257
  )
3802
4258
  ] });
@@ -3804,7 +4260,7 @@ function TablePagination({
3804
4260
  TablePagination.displayName = TABLE_PAGINATION_DISPLAY_NAME;
3805
4261
 
3806
4262
  // src/components/ui/table/components/Table/Table.tsx
3807
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4263
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
3808
4264
  function TableRoot({
3809
4265
  data,
3810
4266
  children,
@@ -3818,7 +4274,7 @@ function TableRoot({
3818
4274
  [children]
3819
4275
  );
3820
4276
  const [sort, setSort] = useState5(null);
3821
- const handleSort = useCallback5((field) => {
4277
+ const handleSort = useCallback6((field) => {
3822
4278
  setSort((previous) => {
3823
4279
  if (previous?.field !== field) {
3824
4280
  return { field, direction: "asc" };
@@ -3829,11 +4285,11 @@ function TableRoot({
3829
4285
  return null;
3830
4286
  });
3831
4287
  }, []);
3832
- const columns = useMemo5(() => {
3833
- return extractColumnElements(header).map(
3834
- (columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
3835
- );
3836
- }, [header, sort, handleSort]);
4288
+ const columnTree = useMemo5(() => extractColumnTree(header), [header]);
4289
+ const columns = useMemo5(
4290
+ () => buildColumnDefsFromTree(columnTree, sort, handleSort),
4291
+ [columnTree, sort, handleSort]
4292
+ );
3837
4293
  const paginationProps = paginationElement?.props;
3838
4294
  const pageSize = paginationProps?.pageSize ?? 10;
3839
4295
  const page = paginationProps?.page ?? 1;
@@ -3843,11 +4299,11 @@ function TableRoot({
3843
4299
  if (!paginationProps) return sortedData;
3844
4300
  return paginateTableData(sortedData, page, pageSize);
3845
4301
  }, [data, sort, paginationProps, page, pageSize]);
3846
- if (columns.length === 0) {
4302
+ if (countLeafColumns(columnTree) === 0) {
3847
4303
  console.warn("[Table] Declare at least one Table.Column inside Table.Header.");
3848
4304
  }
3849
- return /* @__PURE__ */ jsxs8("div", { className: "TableJSX", children: [
3850
- /* @__PURE__ */ jsx9(
4305
+ return /* @__PURE__ */ jsxs9("div", { className: "TableJSX", children: [
4306
+ /* @__PURE__ */ jsx10(
3851
4307
  DataTable,
3852
4308
  {
3853
4309
  ...dataTableProps,
@@ -3858,7 +4314,7 @@ function TableRoot({
3858
4314
  className: cn(className, paginationProps && "DataTableJSX--with-pagination")
3859
4315
  }
3860
4316
  ),
3861
- paginationProps && /* @__PURE__ */ jsx9(
4317
+ paginationProps && /* @__PURE__ */ jsx10(
3862
4318
  TablePagination,
3863
4319
  {
3864
4320
  page,
@@ -3876,13 +4332,19 @@ function createTable() {
3876
4332
  return null;
3877
4333
  }
3878
4334
  Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
4335
+ function ColumnGroup(props) {
4336
+ void props;
4337
+ return null;
4338
+ }
4339
+ ColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3879
4340
  return Object.assign(
3880
4341
  function BoundTable(props) {
3881
- return /* @__PURE__ */ jsx9(TableRoot, { ...props });
4342
+ return /* @__PURE__ */ jsx10(TableRoot, { ...props });
3882
4343
  },
3883
4344
  {
3884
4345
  Header: TableHeader,
3885
4346
  Column,
4347
+ ColumnGroup,
3886
4348
  Body: TableBody,
3887
4349
  Pagination: TablePagination
3888
4350
  }
@@ -3891,6 +4353,7 @@ function createTable() {
3891
4353
  var Table = Object.assign(TableRoot, {
3892
4354
  Header: TableHeader,
3893
4355
  Column: TableColumn,
4356
+ ColumnGroup: TableColumnGroup,
3894
4357
  Body: TableBody,
3895
4358
  Pagination: TablePagination
3896
4359
  });