@acronis-platform/ui-react 5.5.7 → 5.5.9

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.
@@ -1,24 +1,32 @@
1
1
  import { cn as e } from "../../../lib/utils.js";
2
+ import { TruncateText as t } from "../truncate-text/truncate-text.js";
2
3
  import "react";
3
- import { jsx as t, jsxs as n } from "react/jsx-runtime";
4
- import { ArrowDownIcon as r, ArrowUpIcon as i, ArrowsDownUpIcon as a } from "@acronis-platform/icons-react/stroke-mono";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
+ import { ArrowDownIcon as i, ArrowUpIcon as a, ArrowsDownUpIcon as o } from "@acronis-platform/icons-react/stroke-mono";
5
6
  //#region src/components/ui/data-table/data-table-column-header.tsx
6
- function o({ column: o, title: s, sortLabel: c = (e) => `Sort by ${e}`, className: l, ...u }) {
7
- if (!o.getCanSort()) return /* @__PURE__ */ t("div", {
8
- className: e(l),
9
- children: s
7
+ function s({ column: s, title: c, sortLabel: l = (e) => `Sort by ${e}`, className: u, ...d }) {
8
+ let f = s.columnDef.meta?.wrap === !0;
9
+ if (!s.getCanSort()) return f ? /* @__PURE__ */ n(t, {
10
+ className: e("min-w-0", u),
11
+ children: c
12
+ }) : /* @__PURE__ */ n("div", {
13
+ className: e(u),
14
+ children: c
10
15
  });
11
- let d = o.getIsSorted();
12
- return /* @__PURE__ */ n("button", {
16
+ let p = s.getIsSorted();
17
+ return /* @__PURE__ */ r("button", {
13
18
  type: "button",
14
- onClick: () => o.toggleSorting(),
15
- "aria-label": c(s),
16
- className: e("-ms-2 inline-flex h-8 cursor-pointer select-none items-center gap-[var(--ui-table-header-gap)] rounded-sm px-2 text-sm font-semibold focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)] [&_svg]:size-[var(--ui-table-header-sort-icon-size)] [&_svg]:shrink-0", l),
17
- ...u,
18
- children: [/* @__PURE__ */ t("span", { children: s }), d === "asc" ? /* @__PURE__ */ t(i, { className: "text-[var(--ui-table-header-sort-icon-color-active)]" }) : d === "desc" ? /* @__PURE__ */ t(r, { className: "text-[var(--ui-table-header-sort-icon-color-active)]" }) : /* @__PURE__ */ t(a, { className: "text-[var(--ui-table-header-sort-icon-color-inactive)]" })]
19
+ onClick: () => s.toggleSorting(),
20
+ "aria-label": l(c),
21
+ className: e("-ms-2 inline-flex h-8 cursor-pointer select-none items-center gap-[var(--ui-table-header-gap)] rounded-sm px-2 text-sm font-semibold focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)] [&_svg]:size-[var(--ui-table-header-sort-icon-size)] [&_svg]:shrink-0", f && "max-w-full min-w-0", u),
22
+ ...d,
23
+ children: [f ? /* @__PURE__ */ n(t, {
24
+ className: "min-w-0",
25
+ children: c
26
+ }) : /* @__PURE__ */ n("span", { children: c }), p === "asc" ? /* @__PURE__ */ n(a, { className: "text-[var(--ui-table-header-sort-icon-color-active)]" }) : p === "desc" ? /* @__PURE__ */ n(i, { className: "text-[var(--ui-table-header-sort-icon-color-active)]" }) : /* @__PURE__ */ n(o, { className: "text-[var(--ui-table-header-sort-icon-color-inactive)]" })]
19
27
  });
20
28
  }
21
29
  //#endregion
22
- export { o as DataTableColumnHeader };
30
+ export { s as DataTableColumnHeader };
23
31
 
24
32
  //# sourceMappingURL=data-table-column-header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-column-header.js","names":[],"sources":["../../../../src/components/ui/data-table/data-table-column-header.tsx"],"sourcesContent":["import * as React from 'react';\nimport type { Column } from '@tanstack/react-table';\nimport {\n ArrowDownIcon,\n ArrowsDownUpIcon,\n ArrowUpIcon,\n} from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\n\ninterface DataTableColumnHeaderProps<TData, TValue>\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n column: Column<TData, TValue>;\n title: string;\n /**\n * Builds the accessible label of the sort toggle from the column title.\n * Override to localize.\n */\n sortLabel?: (title: string) => string;\n}\n\n// Single-click sortable column header — matches the Table primitive's sortable\n// `TableHead`: one click toggles the sort (ascending → descending → unsorted) via\n// TanStack's `column.toggleSorting()`. The trailing icon shows the state with the\n// same `--ui-table-header-sort-icon-*` tokens — an up arrow (ascending) or down\n// arrow (descending) in the active blue, or the muted up/down arrows when\n// unsorted. (Column hiding lives behind the settings column's cog trigger —\n// `DataTableViewOptions` inside a `TableSettingsCell` — not a per-header menu,\n// so sorting is a single click.)\nexport function DataTableColumnHeader<TData, TValue>({\n column,\n title,\n sortLabel = (t) => `Sort by ${t}`,\n className,\n ...props\n}: DataTableColumnHeaderProps<TData, TValue>) {\n if (!column.getCanSort()) {\n return <div className={cn(className)}>{title}</div>;\n }\n\n const sorted = column.getIsSorted();\n\n return (\n <button\n type=\"button\"\n onClick={() => column.toggleSorting()}\n aria-label={sortLabel(title)}\n className={cn(\n // -ms-2 px-2 keeps the label flush at the cell padding while giving the\n // toggle a comfortable click target; the hover/press tint lives on the\n // `<th>` itself (see data-table.tsx), not this inner button, and uses\n // the kit-wide 3px focus ring (Figma stroke/width-3 + radius/radius-4).\n '-ms-2 inline-flex h-8 cursor-pointer select-none items-center gap-[var(--ui-table-header-gap)] rounded-sm px-2 text-sm font-semibold focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)] [&_svg]:size-[var(--ui-table-header-sort-icon-size)] [&_svg]:shrink-0',\n className\n )}\n {...props}\n >\n <span>{title}</span>\n {sorted === 'asc' ? (\n <ArrowUpIcon className=\"text-[var(--ui-table-header-sort-icon-color-active)]\" />\n ) : sorted === 'desc' ? (\n <ArrowDownIcon className=\"text-[var(--ui-table-header-sort-icon-color-active)]\" />\n ) : (\n <ArrowsDownUpIcon className=\"text-[var(--ui-table-header-sort-icon-color-inactive)]\" />\n )}\n </button>\n );\n}\n"],"mappings":";;;;;AA6BA,SAAgB,EAAqC,EACnD,WACA,UACA,gBAAa,MAAM,WAAW,KAC9B,cACA,GAAG,KACyC;CAC5C,IAAI,CAAC,EAAO,WAAW,GACrB,OAAO,kBAAC,OAAD;EAAK,WAAW,EAAG,CAAS;EAAI,UAAA;CAAW,CAAA;CAGpD,IAAM,IAAS,EAAO,YAAY;CAElC,OACE,kBAAC,UAAD;EACE,MAAK;EACL,eAAe,EAAO,cAAc;EACpC,cAAY,EAAU,CAAK;EAC3B,WAAW,EAKT,+SACA,CACF;EACA,GAAI;EAZN,UAAA,CAcE,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA,GAClB,MAAW,QACV,kBAAC,GAAD,EAAa,WAAU,uDAAwD,CAAA,IAC7E,MAAW,SACb,kBAAC,GAAD,EAAe,WAAU,uDAAwD,CAAA,IAEjF,kBAAC,GAAD,EAAkB,WAAU,yDAA0D,CAAA,CAElF;;AAEZ"}
1
+ {"version":3,"file":"data-table-column-header.js","names":[],"sources":["../../../../src/components/ui/data-table/data-table-column-header.tsx"],"sourcesContent":["import * as React from 'react';\nimport type { Column } from '@tanstack/react-table';\nimport {\n ArrowDownIcon,\n ArrowsDownUpIcon,\n ArrowUpIcon,\n} from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { TruncateText } from '../truncate-text';\n\ninterface DataTableColumnHeaderProps<\n TData,\n TValue,\n> extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n column: Column<TData, TValue>;\n title: string;\n /**\n * Builds the accessible label of the sort toggle from the column title.\n * Override to localize.\n */\n sortLabel?: (title: string) => string;\n}\n\n// Single-click sortable column header — matches the Table primitive's sortable\n// `TableHead`: one click toggles the sort (ascending → descending → unsorted) via\n// TanStack's `column.toggleSorting()`. The trailing icon shows the state with the\n// same `--ui-table-header-sort-icon-*` tokens — an up arrow (ascending) or down\n// arrow (descending) in the active blue, or the muted up/down arrows when\n// unsorted. (Column hiding lives behind the settings column's cog trigger —\n// `DataTableViewOptions` inside a `TableSettingsCell` — not a per-header menu,\n// so sorting is a single click.)\nexport function DataTableColumnHeader<TData, TValue>({\n column,\n title,\n sortLabel = (t) => `Sort by ${t}`,\n className,\n ...props\n}: DataTableColumnHeaderProps<TData, TValue>) {\n const truncatesTitle = column.columnDef.meta?.wrap === true;\n\n if (!column.getCanSort()) {\n return truncatesTitle ? (\n <TruncateText className={cn('min-w-0', className)}>{title}</TruncateText>\n ) : (\n <div className={cn(className)}>{title}</div>\n );\n }\n\n const sorted = column.getIsSorted();\n\n return (\n <button\n type=\"button\"\n onClick={() => column.toggleSorting()}\n aria-label={sortLabel(title)}\n className={cn(\n // -ms-2 px-2 keeps the label flush at the cell padding while giving the\n // toggle a comfortable click target; the hover/press tint lives on the\n // `<th>` itself (see data-table.tsx), not this inner button, and uses\n // the kit-wide 3px focus ring (Figma stroke/width-3 + radius/radius-4).\n '-ms-2 inline-flex h-8 cursor-pointer select-none items-center gap-[var(--ui-table-header-gap)] rounded-sm px-2 text-sm font-semibold focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)] [&_svg]:size-[var(--ui-table-header-sort-icon-size)] [&_svg]:shrink-0',\n truncatesTitle && 'max-w-full min-w-0',\n className\n )}\n {...props}\n >\n {truncatesTitle ? (\n <TruncateText className=\"min-w-0\">{title}</TruncateText>\n ) : (\n <span>{title}</span>\n )}\n {sorted === 'asc' ? (\n <ArrowUpIcon className=\"text-[var(--ui-table-header-sort-icon-color-active)]\" />\n ) : sorted === 'desc' ? (\n <ArrowDownIcon className=\"text-[var(--ui-table-header-sort-icon-color-active)]\" />\n ) : (\n <ArrowsDownUpIcon className=\"text-[var(--ui-table-header-sort-icon-color-inactive)]\" />\n )}\n </button>\n );\n}\n"],"mappings":";;;;;;AAgCA,SAAgB,EAAqC,EACnD,WACA,UACA,gBAAa,MAAM,WAAW,KAC9B,cACA,GAAG,KACyC;CAC5C,IAAM,IAAiB,EAAO,UAAU,MAAM,SAAS;CAEvD,IAAI,CAAC,EAAO,WAAW,GACrB,OAAO,IACL,kBAAC,GAAD;EAAc,WAAW,EAAG,WAAW,CAAS;EAAI,UAAA;CAAoB,CAAA,IAExE,kBAAC,OAAD;EAAK,WAAW,EAAG,CAAS;EAAI,UAAA;CAAW,CAAA;CAI/C,IAAM,IAAS,EAAO,YAAY;CAElC,OACE,kBAAC,UAAD;EACE,MAAK;EACL,eAAe,EAAO,cAAc;EACpC,cAAY,EAAU,CAAK;EAC3B,WAAW,EAKT,+SACA,KAAkB,sBAClB,CACF;EACA,GAAI;EAbN,UAAA,CAeG,IACC,kBAAC,GAAD;GAAc,WAAU;GAAW,UAAA;EAAoB,CAAA,IAEvD,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA,GAEpB,MAAW,QACV,kBAAC,GAAD,EAAa,WAAU,uDAAwD,CAAA,IAC7E,MAAW,SACb,kBAAC,GAAD,EAAe,WAAU,uDAAwD,CAAA,IAEjF,kBAAC,GAAD,EAAkB,WAAU,yDAA0D,CAAA,CAElF;;AAEZ"}
@@ -2,17 +2,18 @@ import { cn as e } from "../../../lib/utils.js";
2
2
  import { useIntersectionObserver as t } from "../../../hooks/use-intersection-observer.js";
3
3
  import { ButtonIcon as n } from "../button-icon/button-icon.js";
4
4
  import { Tooltip as r, TooltipContent as i, TooltipProvider as a, TooltipTrigger as o } from "../tooltip/tooltip.js";
5
+ import { TruncateText as ee } from "../truncate-text/truncate-text.js";
5
6
  import { getCoreRowModel as s, getExpandedRowModel as c, getFilteredRowModel as l, getPaginationRowModel as u, getSortedRowModel as d } from "../../../node_modules/.pnpm/@tanstack_table-core@8.21.3/node_modules/@tanstack/table-core/build/lib/index.js";
6
- import { flexRender as f, useReactTable as p } from "../../../node_modules/.pnpm/@tanstack_react-table@8.21.3_react-dom@19.2.6_react@19.2.6__react@19.2.6/node_modules/@tanstack/react-table/build/lib/index.js";
7
- import { DropdownMenu as m, DropdownMenuContent as ee, DropdownMenuTrigger as te } from "../dropdown-menu/dropdown-menu.js";
8
- import { Table as ne, TableActionsCell as re, TableBody as ie, TableCell as h, TableHead as ae, TableHeader as oe, TableRow as g, TableSettingsCell as se } from "../table/table.js";
7
+ import { flexRender as te, useReactTable as f } from "../../../node_modules/.pnpm/@tanstack_react-table@8.21.3_react-dom@19.2.6_react@19.2.6__react@19.2.6/node_modules/@tanstack/react-table/build/lib/index.js";
8
+ import { DropdownMenu as p, DropdownMenuContent as m, DropdownMenuTrigger as ne } from "../dropdown-menu/dropdown-menu.js";
9
+ import { Table as re, TableActionsCell as ie, TableBody as ae, TableCell as h, TableHead as oe, TableHeader as se, TableRow as g, TableSettingsCell as _ } from "../table/table.js";
9
10
  import { isBulkSelectionActive as ce } from "./data-table-selection.js";
10
11
  import { DataTableViewOptions as le } from "./data-table-view-options.js";
11
- import { Fragment as _, useEffect as ue, useMemo as de, useRef as fe, useState as v } from "react";
12
- import { jsx as y, jsxs as b } from "react/jsx-runtime";
12
+ import { Fragment as v, useEffect as ue, useMemo as de, useRef as fe, useState as y } from "react";
13
+ import { jsx as b, jsxs as x } from "react/jsx-runtime";
13
14
  import { EllipsisIcon as pe } from "@acronis-platform/icons-react/stroke-mono";
14
15
  //#region src/components/ui/data-table/data-table.tsx
15
- function x(e) {
16
+ function S(e) {
16
17
  let t = e.getIsPinned();
17
18
  if (t) return {
18
19
  position: "sticky",
@@ -22,25 +23,25 @@ function x(e) {
22
23
  boxShadow: t === "left" ? "4px 0 4px -4px var(--ui-table-global-row-border-color)" : "-4px 0 4px -4px var(--ui-table-global-row-border-color)"
23
24
  };
24
25
  }
25
- function S(e, t) {
26
+ function C(e, t) {
26
27
  if (e.id === "select") return 48;
27
28
  if (t || e.columnDef.size !== void 0) return e.getSize();
28
29
  }
29
- function C(e, t) {
30
- let n = x(e.column), r = e.subHeaders.length === 0 ? S(e.column, t) : void 0;
30
+ function w(e, t) {
31
+ let n = S(e.column), r = e.subHeaders.length === 0 ? C(e.column, t) : void 0;
31
32
  if (!(!n && r === void 0)) return {
32
33
  ...n,
33
34
  width: r
34
35
  };
35
36
  }
36
- function w(e, t) {
37
- let n = x(e.column), r = S(e.column, t);
37
+ function T(e, t) {
38
+ let n = S(e.column), r = C(e.column, t);
38
39
  if (!(!n && r === void 0)) return {
39
40
  ...n,
40
41
  width: r
41
42
  };
42
43
  }
43
- var T = 20, E = 2 ** 53 - 1;
44
+ var me = 20, E = 2 ** 53 - 1;
44
45
  function D(e, t, { shiftKey: n, min: r, max: i }) {
45
46
  let a = n ? 50 : 10;
46
47
  if (e === "ArrowLeft") return Math.min(i, Math.max(r, t - a));
@@ -52,7 +53,7 @@ function O(e, t, n) {
52
53
  let a = [...e];
53
54
  return a.splice(i, 0, a.splice(r, 1)[0]), a;
54
55
  }
55
- var me = {
56
+ var he = {
56
57
  sort: {
57
58
  label: "Sort column",
58
59
  action: "Click"
@@ -66,21 +67,21 @@ var me = {
66
67
  action: "Drag border"
67
68
  }
68
69
  };
69
- function k({ columns: x = [], data: S = [], table: k, getRowId: he, getRowCanExpand: ge, renderExpandedRow: _e, striped: ve = !1, bordered: ye = !1, highlightCurrentRow: A = !1, skeleton: j = !1, skeletonRows: be = 5, enableColumnResizing: xe = !1, onColumnSizingChange: Se, enableColumnReordering: Ce = !1, columnOrder: we, onColumnOrderChange: Te, columnVisibility: Ee, onColumnVisibilityChange: De, rowSelection: Oe, onRowSelectionChange: ke, manualSorting: M = !1, sorting: N, onSortingChange: Ae, renderRow: P, renderEmptyState: F, paginationMode: I = "page", onLoadMore: je, loadMoreRootMargin: Me, hasNextPage: L = !1, resizeColumnLabel: Ne = "Resize column", emptyLabel: Pe = "No results.", isLoadingMore: R = !1, hideActionColumn: Fe = !1, renderRowActions: z, rowActionsLabel: Ie = "Row actions", columnSettingsLabel: Le, columnSearchPlaceholder: Re, showAllColumnsLabel: ze, noColumnsFoundLabel: Be, headerHints: Ve }) {
70
- let B = {
71
- ...me,
72
- ...Ve
73
- }, [He, Ue] = v([]), We = N ?? He, [Ge, Ke] = v([]), [qe, Je] = v({}), Ye = Ee ?? qe, [Xe, Ze] = v({}), Qe = Oe ?? Xe, [$e, et] = v({}), [tt, nt] = v({}), [rt, it] = v([]), at = we ?? rt, [V, H] = v(), [ot, st] = v(), [ct, U] = v(0), W = fe([]), lt = (e) => {
74
- nt(e), Se?.(e);
70
+ function k({ columns: S = [], data: C = [], table: k, getRowId: ge, getRowCanExpand: _e, renderExpandedRow: A, striped: j = !1, bordered: ve = !1, highlightCurrentRow: M = !1, skeleton: N = !1, skeletonRows: ye = 5, enableColumnResizing: P = !1, onColumnSizingChange: be, enableColumnReordering: xe = !1, columnOrder: F, onColumnOrderChange: Se, columnVisibility: Ce, onColumnVisibilityChange: we, rowSelection: I, onRowSelectionChange: Te, manualSorting: L = !1, sorting: R, onSortingChange: Ee, renderRow: z, renderEmptyState: B, paginationMode: V = "page", onLoadMore: De, loadMoreRootMargin: Oe, hasNextPage: H = !1, resizeColumnLabel: ke = "Resize column", emptyLabel: Ae = "No results.", isLoadingMore: je = !1, hideActionColumn: Me = !1, renderRowActions: Ne, rowActionsLabel: Pe = "Row actions", columnSettingsLabel: Fe, columnSearchPlaceholder: Ie, showAllColumnsLabel: Le, noColumnsFoundLabel: Re, headerHints: ze }) {
71
+ let U = {
72
+ ...he,
73
+ ...ze
74
+ }, [Be, Ve] = y([]), He = R ?? Be, [Ue, We] = y([]), [Ge, Ke] = y({}), qe = Ce ?? Ge, [Je, Ye] = y({}), Xe = I ?? Je, [Ze, Qe] = y({}), [$e, et] = y({}), [tt, nt] = y([]), rt = F ?? tt, [W, G] = y(), [it, at] = y(), [ot, st] = y(0), ct = fe([]), lt = (e) => {
75
+ et(e), be?.(e);
75
76
  }, ut = (e) => {
76
- we === void 0 && it(e), Te?.(e);
77
+ F === void 0 && nt(e), Se?.(e);
77
78
  }, dt = (e) => {
78
- Je(e), De?.(e);
79
+ Ke(e), we?.(e);
79
80
  }, ft = (e) => {
80
- N === void 0 && Ue(e), Ae?.(e);
81
+ R === void 0 && Ve(e), Ee?.(e);
81
82
  }, pt = (e) => {
82
- Oe === void 0 && Ze(e), ke?.(e);
83
- }, G = !Fe && !k, K = de(() => G ? [{
83
+ I === void 0 && Ye(e), Te?.(e);
84
+ }, K = !Me && !k, mt = de(() => K ? [{
84
85
  id: "__actions",
85
86
  size: 48,
86
87
  enableSorting: !1,
@@ -88,71 +89,71 @@ function k({ columns: x = [], data: S = [], table: k, getRowId: he, getRowCanExp
88
89
  meta: { pin: "right" },
89
90
  header: () => null,
90
91
  cell: () => null
91
- }] : [], [G]), mt = de(() => [...x, ...K], [x, K]), [ht, gt] = v(), _t = p({
92
- data: S,
93
- columns: mt,
94
- getRowId: he,
95
- enableColumnResizing: xe,
92
+ }] : [], [K]), ht = de(() => [...S, ...mt], [S, mt]), [gt, _t] = y(), vt = f({
93
+ data: C,
94
+ columns: ht,
95
+ getRowId: ge,
96
+ enableColumnResizing: P,
96
97
  columnResizeMode: "onChange",
97
98
  getCoreRowModel: s(),
98
99
  getExpandedRowModel: c(),
99
- ...I === "page" ? { getPaginationRowModel: u() } : {},
100
- getRowCanExpand: ge,
101
- onExpandedChange: et,
102
- manualSorting: M,
100
+ ...V === "page" ? { getPaginationRowModel: u() } : {},
101
+ getRowCanExpand: _e,
102
+ onExpandedChange: Qe,
103
+ manualSorting: L,
103
104
  onSortingChange: ft,
104
- ...M ? {} : { getSortedRowModel: d() },
105
- onColumnFiltersChange: Ke,
105
+ ...L ? {} : { getSortedRowModel: d() },
106
+ onColumnFiltersChange: We,
106
107
  getFilteredRowModel: l(),
107
108
  onColumnVisibilityChange: dt,
108
109
  onRowSelectionChange: pt,
109
110
  onColumnSizingChange: lt,
110
111
  onColumnOrderChange: ut,
111
- initialState: G ? { columnPinning: { right: ["__actions"] } } : void 0,
112
+ initialState: K ? { columnPinning: { right: ["__actions"] } } : void 0,
112
113
  state: {
113
- sorting: We,
114
- columnFilters: Ge,
115
- columnVisibility: Ye,
116
- rowSelection: Qe,
117
- expanded: $e,
118
- columnSizing: tt,
119
- columnOrder: at
114
+ sorting: He,
115
+ columnFilters: Ue,
116
+ columnVisibility: qe,
117
+ rowSelection: Xe,
118
+ expanded: Ze,
119
+ columnSizing: $e,
120
+ columnOrder: rt
120
121
  }
121
- }), q = k ?? _t, J = !k && I === "infinite", Y = xe && !k, vt = Ce && !k, X = !!q.getState().columnSizingInfo.isResizingColumn;
122
+ }), q = k ?? vt, J = !k && V === "infinite", Y = P && !k, yt = xe && !k, X = !!q.getState().columnSizingInfo.isResizingColumn;
122
123
  ue(() => {
123
124
  if (X) return document.documentElement.setAttribute("data-ui-column-resizing", ""), () => {
124
125
  document.documentElement.removeAttribute("data-ui-column-resizing");
125
126
  };
126
127
  }, [X]);
127
- let yt = t({
128
- onIntersect: () => je?.(),
129
- disabled: !J || !L || R,
130
- rootMargin: Me
131
- }), bt = (e, t) => {
128
+ let bt = t({
129
+ onIntersect: () => De?.(),
130
+ disabled: !J || !H || je,
131
+ rootMargin: Oe
132
+ }), xt = (e, t) => {
132
133
  if (e.ctrlKey || e.altKey || e.metaKey) return;
133
134
  let n = D(e.key, t.column.getSize(), {
134
135
  shiftKey: e.shiftKey,
135
- min: t.column.columnDef.minSize ?? T,
136
+ min: t.column.columnDef.minSize ?? me,
136
137
  max: t.column.columnDef.maxSize ?? E
137
138
  });
138
139
  n !== void 0 && (e.preventDefault(), q.setColumnSizing((e) => ({
139
140
  ...e,
140
141
  [t.column.id]: n
141
142
  })));
142
- }, xt = (e, t) => {
143
- e.dataTransfer && (e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", t)), H(t);
144
143
  }, St = (e, t) => {
144
+ e.dataTransfer && (e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", t)), G(t);
145
+ }, Ct = (e, t) => {
145
146
  if (e.preventDefault(), !e.dataTransfer) return;
146
- let n = V ? q.getColumn(V)?.parent?.id : void 0, r = q.getColumn(t)?.parent?.id;
147
+ let n = W ? q.getColumn(W)?.parent?.id : void 0, r = q.getColumn(t)?.parent?.id;
147
148
  e.dataTransfer.dropEffect = n === r ? "move" : "none";
148
- }, Ct = (e, t) => {
149
- if (e.preventDefault(), !V) return;
150
- if (q.getColumn(V)?.parent?.id !== q.getColumn(t)?.parent?.id) {
151
- H(void 0);
149
+ }, wt = (e, t) => {
150
+ if (e.preventDefault(), !W) return;
151
+ if (q.getColumn(W)?.parent?.id !== q.getColumn(t)?.parent?.id) {
152
+ G(void 0);
152
153
  return;
153
154
  }
154
155
  let n = q.getState().columnOrder, r = n.length ? n : q.getAllLeafColumns().map((e) => e.id);
155
- q.setColumnOrder(O(r, V, t)), H(void 0);
156
+ q.setColumnOrder(O(r, W, t)), G(void 0);
156
157
  };
157
158
  ue(() => {
158
159
  k || q.getAllLeafColumns().forEach((e) => {
@@ -160,74 +161,73 @@ function k({ columns: x = [], data: S = [], table: k, getRowId: he, getRowCanExp
160
161
  });
161
162
  }, [
162
163
  q,
163
- mt,
164
+ ht,
164
165
  k
165
166
  ]);
166
- let Z = q.getRowModel().rows, Q = ce(q), wt = Z.length ? Math.min(ct, Z.length - 1) : 0, Tt = (e) => {
167
- U(e);
168
- }, Et = (e, t) => {
167
+ let Z = q.getRowModel().rows, Q = ce(q), Tt = Z.length ? Math.min(ot, Z.length - 1) : 0, Et = (e) => {
168
+ st(e);
169
+ }, Dt = (e, t) => {
169
170
  if (e.target !== e.currentTarget || e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
170
171
  let n = e.key === "ArrowDown" ? Math.min(t + 1, Z.length - 1) : Math.max(t - 1, 0);
171
- n !== t && (e.preventDefault(), U(n), W.current[n]?.focus());
172
- }, Dt = ye ? "[&_th:not(:last-child)]:border-e [&_td:not(:last-child)]:border-e [&_th]:border-[var(--ui-table-global-row-border-color)] [&_td]:border-[var(--ui-table-global-row-border-color)]" : void 0, $ = "bg-background";
173
- return /* @__PURE__ */ y("div", {
174
- className: e("rounded-md border border-[var(--ui-table-global-row-border-color)]", Dt),
175
- children: /* @__PURE__ */ b(ne, {
172
+ n !== t && (e.preventDefault(), st(n), ct.current[n]?.focus());
173
+ }, Ot = ve ? "[&_th:not(:last-child)]:border-e [&_td:not(:last-child)]:border-e [&_th]:border-[var(--ui-table-global-row-border-color)] [&_td]:border-[var(--ui-table-global-row-border-color)]" : void 0, $ = "bg-background";
174
+ return /* @__PURE__ */ b("div", {
175
+ className: e("rounded-md border border-[var(--ui-table-global-row-border-color)]", Ot),
176
+ children: /* @__PURE__ */ x(re, {
176
177
  style: Y ? { width: q.getCenterTotalSize() } : void 0,
177
- children: [/* @__PURE__ */ y(a, { children: /* @__PURE__ */ y(oe, { children: q.getHeaderGroups().map((t, n, a) => /* @__PURE__ */ y(g, {
178
+ children: [/* @__PURE__ */ b(a, { children: /* @__PURE__ */ b(se, { children: q.getHeaderGroups().map((t, n, a) => /* @__PURE__ */ b(g, {
178
179
  className: "hover:bg-transparent",
179
180
  children: t.headers.map((t) => {
180
- let s = t.column.getIsPinned(), c = t.subHeaders.length > 0, l = !c && Y && t.column.getCanResize() && t.column.id !== "select", u = !c && vt && !t.isPlaceholder && !s, d = !c && !t.isPlaceholder && t.column.getCanSort(), p = [
181
- d && B.sort,
182
- u && B.reorder,
183
- l && B.resize
181
+ let s = t.column.getIsPinned(), c = t.subHeaders.length > 0, l = !c && Y && t.column.getCanResize() && t.column.id !== "select", u = !c && yt && !t.isPlaceholder && !s, d = !c && !t.isPlaceholder && t.column.getCanSort(), f = [
182
+ d && U.sort,
183
+ u && U.reorder,
184
+ l && U.resize
184
185
  ].filter((e) => !!e);
185
- if (t.column.id === "__actions") return n < a.length - 1 ? /* @__PURE__ */ y(se, {
186
- style: C(t, Y),
186
+ if (t.column.id === "__actions") return n < a.length - 1 ? /* @__PURE__ */ b(_, {
187
+ style: w(t, Y),
187
188
  className: $
188
- }, t.id) : /* @__PURE__ */ y(se, {
189
- style: C(t, Y),
189
+ }, t.id) : /* @__PURE__ */ b(_, {
190
+ style: w(t, Y),
190
191
  className: $,
191
- children: /* @__PURE__ */ y(le, {
192
+ children: /* @__PURE__ */ b(le, {
192
193
  table: q,
193
194
  iconOnly: !0,
194
- triggerAriaLabel: Le,
195
- searchPlaceholder: Re,
196
- showAllLabel: ze,
197
- noResultsLabel: Be
195
+ triggerAriaLabel: Fe,
196
+ searchPlaceholder: Ie,
197
+ showAllLabel: Le,
198
+ noResultsLabel: Re
198
199
  })
199
200
  }, t.id);
200
- let m = /* @__PURE__ */ b(ae, {
201
+ let p = t.isPlaceholder ? null : te(t.column.columnDef.header, t.getContext()), m = /* @__PURE__ */ x(oe, {
201
202
  colSpan: t.colSpan,
202
- wrap: t.column.columnDef.meta?.wrap,
203
- style: C(t, Y),
203
+ style: w(t, Y),
204
204
  draggable: u && !X || void 0,
205
- onDragStart: u ? (e) => xt(e, t.column.id) : void 0,
206
- onDragOver: u ? (e) => St(e, t.column.id) : void 0,
207
- onDrop: u ? (e) => Ct(e, t.column.id) : void 0,
208
- onDragEnd: u ? () => H(void 0) : void 0,
209
- className: e(l && "relative", d && !X && "transition-colors hover:bg-[var(--ui-table-header-cell-color-hover)] active:bg-[var(--ui-table-header-cell-color-active)]", u && !X && "cursor-grab select-none active:cursor-grabbing", u && V === t.column.id && "opacity-50", s && $),
210
- children: [t.isPlaceholder ? null : f(t.column.columnDef.header, t.getContext()), l && /* @__PURE__ */ y("div", {
205
+ onDragStart: u ? (e) => St(e, t.column.id) : void 0,
206
+ onDragOver: u ? (e) => Ct(e, t.column.id) : void 0,
207
+ onDrop: u ? (e) => wt(e, t.column.id) : void 0,
208
+ onDragEnd: u ? () => G(void 0) : void 0,
209
+ className: e(l && "relative", d && !X && "transition-colors hover:bg-[var(--ui-table-header-cell-color-hover)] active:bg-[var(--ui-table-header-cell-color-active)]", u && !X && "cursor-grab select-none active:cursor-grabbing", u && W === t.column.id && "opacity-50", s && $),
210
+ children: [typeof p == "string" && t.column.columnDef.meta?.wrap ? /* @__PURE__ */ b(ee, { children: p }) : p, l && /* @__PURE__ */ b("div", {
211
211
  role: "separator",
212
212
  "aria-orientation": "vertical",
213
- "aria-label": Ne,
213
+ "aria-label": ke,
214
214
  "aria-valuenow": t.column.getSize(),
215
- "aria-valuemin": t.column.columnDef.minSize ?? T,
215
+ "aria-valuemin": t.column.columnDef.minSize ?? me,
216
216
  "aria-valuemax": t.column.columnDef.maxSize ?? E,
217
217
  tabIndex: 0,
218
218
  draggable: !1,
219
219
  onMouseDown: t.getResizeHandler(),
220
220
  onTouchStart: t.getResizeHandler(),
221
- onKeyDown: (e) => bt(e, t),
221
+ onKeyDown: (e) => xt(e, t),
222
222
  className: e("absolute end-0 top-0 h-full w-1 cursor-(--ui-resizable-cursor) touch-none select-none bg-[var(--ui-table-global-row-border-color)] opacity-0 transition-[opacity,background-color] hover:bg-[var(--ui-resizable-border-color-hover)] hover:opacity-100 focus-visible:bg-[var(--ui-resizable-border-color-hover)] focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-[var(--ui-focus-primary)]", t.column.getIsResizing() && "bg-[var(--ui-resizable-border-color-active)] opacity-100")
223
223
  })]
224
224
  });
225
- return p.length === 0 ? /* @__PURE__ */ y(_, { children: m }, t.id) : /* @__PURE__ */ b(r, {
226
- disabled: X || V !== void 0,
227
- children: [/* @__PURE__ */ y(o, { render: m }), /* @__PURE__ */ y(i, {
225
+ return f.length === 0 ? /* @__PURE__ */ b(v, { children: m }, t.id) : /* @__PURE__ */ x(r, {
226
+ disabled: X || W !== void 0,
227
+ children: [/* @__PURE__ */ b(o, { render: m }), /* @__PURE__ */ b(i, {
228
228
  className: "flex flex-col gap-1",
229
- children: p.map((e) => /* @__PURE__ */ b("span", { children: [
230
- /* @__PURE__ */ b("span", {
229
+ children: f.map((e) => /* @__PURE__ */ x("span", { children: [
230
+ /* @__PURE__ */ x("span", {
231
231
  className: "font-semibold",
232
232
  children: [e.label, ":"]
233
233
  }),
@@ -237,80 +237,80 @@ function k({ columns: x = [], data: S = [], table: k, getRowId: he, getRowCanExp
237
237
  })]
238
238
  }, t.id);
239
239
  })
240
- }, t.id)) }) }), /* @__PURE__ */ b(ie, { children: [
241
- j ? Array.from({ length: be }).map((e, t) => /* @__PURE__ */ y(g, {
240
+ }, t.id)) }) }), /* @__PURE__ */ x(ae, { children: [
241
+ N ? Array.from({ length: ye }).map((e, t) => /* @__PURE__ */ b(g, {
242
242
  className: "hover:bg-transparent",
243
- children: q.getVisibleLeafColumns().map((e) => /* @__PURE__ */ y(h, { children: /* @__PURE__ */ y("div", { className: "h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]" }) }, e.id))
243
+ children: q.getVisibleLeafColumns().map((e) => /* @__PURE__ */ b(h, { children: /* @__PURE__ */ b("div", { className: "h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]" }) }, e.id))
244
244
  }, `skeleton-${t}`)) : Z?.length ? Z.map((t, r) => {
245
- if (P) return /* @__PURE__ */ y(_, { children: P(t, r) }, t.id);
246
- let i = t.getIsSelected(), a = A && ot === t.id, o = i || a ? "bg-[var(--ui-table-data-row-color-active)]" : ve && r % 2 == 1 ? "bg-[var(--ui-background-surface-secondary)]" : "bg-background group-hover:bg-[var(--ui-table-data-row-color-hover)]", s = !Q && ht === t.id;
247
- return /* @__PURE__ */ b(_, { children: [/* @__PURE__ */ y(g, {
245
+ if (z) return /* @__PURE__ */ b(v, { children: z(t, r) }, t.id);
246
+ let i = t.getIsSelected(), a = M && it === t.id, o = i || a ? "bg-[var(--ui-table-data-row-color-active)]" : j && r % 2 == 1 ? "bg-[var(--ui-background-surface-secondary)]" : "bg-background group-hover:bg-[var(--ui-table-data-row-color-hover)]", ee = !Q && gt === t.id;
247
+ return /* @__PURE__ */ x(v, { children: [/* @__PURE__ */ b(g, {
248
248
  ref: (e) => {
249
- W.current[r] = e;
249
+ ct.current[r] = e;
250
250
  },
251
- tabIndex: r === wt ? 0 : -1,
252
- onFocus: () => Tt(r),
253
- onKeyDown: (e) => Et(e, r),
251
+ tabIndex: r === Tt ? 0 : -1,
252
+ onFocus: () => Et(r),
253
+ onKeyDown: (e) => Dt(e, r),
254
254
  selected: i,
255
- onClick: A ? () => st(t.id) : void 0,
256
- className: e("group", A && "cursor-pointer", ve && r % 2 == 1 && !i && !a && "bg-[var(--ui-background-surface-secondary)]", a && !i && "bg-[var(--ui-table-data-row-color-active)]", s && "hover:bg-transparent"),
255
+ onClick: M ? () => at(t.id) : void 0,
256
+ className: e("group", M && "cursor-pointer", j && r % 2 == 1 && !i && !a && "bg-[var(--ui-background-surface-secondary)]", a && !i && "bg-[var(--ui-table-data-row-color-active)]", ee && "hover:bg-transparent"),
257
257
  children: t.getVisibleCells().map((r) => {
258
258
  let i = r.column.getIsPinned();
259
- return r.column.id === "__actions" ? /* @__PURE__ */ y(re, {
260
- style: w(r, Y),
259
+ return r.column.id === "__actions" ? /* @__PURE__ */ b(ie, {
260
+ style: T(r, Y),
261
261
  className: o,
262
262
  bulkSelectionActive: Q,
263
- onMouseEnter: Q ? void 0 : () => gt(t.id),
264
- onMouseLeave: Q ? void 0 : () => gt((e) => e === t.id ? void 0 : e),
265
- children: z && /* @__PURE__ */ b(m, { children: [/* @__PURE__ */ y(te, {
266
- render: /* @__PURE__ */ y(n, { "aria-label": Ie }),
267
- children: /* @__PURE__ */ y(pe, {})
268
- }), /* @__PURE__ */ y(ee, {
263
+ onMouseEnter: Q ? void 0 : () => _t(t.id),
264
+ onMouseLeave: Q ? void 0 : () => _t((e) => e === t.id ? void 0 : e),
265
+ children: Ne && /* @__PURE__ */ x(p, { children: [/* @__PURE__ */ b(ne, {
266
+ render: /* @__PURE__ */ b(n, { "aria-label": Pe }),
267
+ children: /* @__PURE__ */ b(pe, {})
268
+ }), /* @__PURE__ */ b(m, {
269
269
  align: "end",
270
- children: z(t)
270
+ children: Ne(t)
271
271
  })] })
272
- }, r.id) : /* @__PURE__ */ y(h, {
272
+ }, r.id) : /* @__PURE__ */ b(h, {
273
273
  wrap: r.column.columnDef.meta?.wrap,
274
- style: w(r, Y),
274
+ style: T(r, Y),
275
275
  className: e(i && o),
276
- children: f(r.column.columnDef.cell, r.getContext())
276
+ children: te(r.column.columnDef.cell, r.getContext())
277
277
  }, r.id);
278
278
  })
279
- }), _e && t.getIsExpanded() && /* @__PURE__ */ y(g, {
279
+ }), A && t.getIsExpanded() && /* @__PURE__ */ b(g, {
280
280
  className: "hover:bg-transparent",
281
- children: /* @__PURE__ */ y(h, {
281
+ children: /* @__PURE__ */ b(h, {
282
282
  className: "h-auto py-3",
283
283
  colSpan: t.getVisibleCells().length,
284
- children: _e(t)
284
+ children: A(t)
285
285
  })
286
286
  })] }, t.id);
287
- }) : F ? /* @__PURE__ */ y(g, { children: /* @__PURE__ */ y(h, {
287
+ }) : B ? /* @__PURE__ */ b(g, { children: /* @__PURE__ */ b(h, {
288
288
  colSpan: q.getVisibleLeafColumns().length,
289
289
  className: "h-24 text-center",
290
- children: F({ hasFilters: q.getState().columnFilters.length > 0 })
291
- }) }) : /* @__PURE__ */ y(g, { children: /* @__PURE__ */ y(h, {
290
+ children: B({ hasFilters: q.getState().columnFilters.length > 0 })
291
+ }) }) : /* @__PURE__ */ b(g, { children: /* @__PURE__ */ b(h, {
292
292
  colSpan: q.getVisibleLeafColumns().length,
293
293
  className: "h-24 text-center text-[var(--ui-table-data-value-color-disabled)]",
294
- children: Pe
294
+ children: Ae
295
295
  }) }),
296
- J && !j && Z.length > 0 && L && /* @__PURE__ */ y(g, {
297
- ref: yt,
296
+ J && !N && Z.length > 0 && H && /* @__PURE__ */ b(g, {
297
+ ref: bt,
298
298
  "aria-hidden": !0,
299
299
  className: "hover:bg-transparent",
300
- children: /* @__PURE__ */ y(h, {
300
+ children: /* @__PURE__ */ b(h, {
301
301
  colSpan: q.getVisibleLeafColumns().length,
302
302
  className: "h-1 p-0"
303
303
  })
304
304
  }),
305
- J && !j && Z.length > 0 && R && /* @__PURE__ */ y(g, {
305
+ J && !N && Z.length > 0 && je && /* @__PURE__ */ b(g, {
306
306
  className: "hover:bg-transparent",
307
- children: /* @__PURE__ */ y(h, {
307
+ children: /* @__PURE__ */ b(h, {
308
308
  colSpan: q.getVisibleLeafColumns().length,
309
- children: /* @__PURE__ */ y("div", {
309
+ children: /* @__PURE__ */ b("div", {
310
310
  role: "status",
311
311
  "aria-live": "polite",
312
312
  className: "h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]",
313
- children: /* @__PURE__ */ y("span", {
313
+ children: /* @__PURE__ */ b("span", {
314
314
  className: "sr-only",
315
315
  children: "Loading more rows…"
316
316
  })
@@ -322,6 +322,6 @@ function k({ columns: x = [], data: S = [], table: k, getRowId: he, getRowCanExp
322
322
  });
323
323
  }
324
324
  //#endregion
325
- export { k as DataTable, w as getCellStyle, S as getColumnWidth, x as getPinnedStyle, D as getResizeKeyboardStep, O as reorderColumn };
325
+ export { k as DataTable, T as getCellStyle, C as getColumnWidth, S as getPinnedStyle, D as getResizeKeyboardStep, O as reorderColumn };
326
326
 
327
327
  //# sourceMappingURL=data-table.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-table.js","names":[],"sources":["../../../../src/components/ui/data-table/data-table.tsx"],"sourcesContent":["import {\n type CSSProperties,\n type DragEvent,\n Fragment,\n type KeyboardEvent,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n type Cell,\n type Column,\n type ColumnDef,\n type ColumnFiltersState,\n type ColumnOrderState,\n type ColumnSizingState,\n type ExpandedState,\n type Header,\n type OnChangeFn,\n type Row,\n type RowData,\n type RowSelectionState,\n type SortingState,\n type Table as TanstackTable,\n type VisibilityState,\n flexRender,\n getCoreRowModel,\n getExpandedRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n getSortedRowModel,\n useReactTable,\n} from '@tanstack/react-table';\n\nimport { EllipsisIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { useIntersectionObserver } from '@/hooks';\nimport { cn } from '@/lib/utils';\nimport { ButtonIcon } from '../button-icon';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuTrigger,\n} from '../dropdown-menu';\nimport {\n Table,\n TableActionsCell,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n TableSettingsCell,\n} from '../table';\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../tooltip';\nimport { isBulkSelectionActive } from './data-table-selection';\nimport { DataTableViewOptions } from './data-table-view-options';\n\n// Ported from `@acronis-platform/shadcn-uikit`'s `data-table`\n// (packages/ui-legacy/src/components/ui/data-table/). A TanStack-react-table v8\n// data grid composed over the ui-react Table primitives — sorting, filtering,\n// column visibility, row selection, pagination, and optional row expansion. The\n// presentational flags (`striped`, `bordered`, `skeleton`, `highlightCurrentRow`)\n// are borrowed from the Vue `AvTable`; behavioral features come from TanStack.\n// Pair with DataTableToolbar / DataTablePagination / DataTableColumnHeader, which\n// take the `table` instance returned to column cells via TanStack context.\n// The grid cells/rows/headers are themed by the Table primitives' `--ui-table-*`\n// tier; DataTable's own chrome reuses that tier too — the wrapper border matches\n// the cell borders (`--ui-table-global-row-border-color`), the empty-state uses\n// the muted table-value color, the current row the active-row color, and stripes\n// the secondary surface.\n//\n// Advanced-grid opt-ins built on native TanStack features:\n// • Column resizing -> `enableColumnResizing` + `columnResizing` state, with a\n// drag handle rendered from `header.getResizeHandler()`.\n// • Sticky columns -> `ColumnDef.meta.pin: 'left' | 'right'` drives TanStack's\n// native column-pinning API (`column.pin()` / `getStart()` / `getAfter()`),\n// surfaced as `position: sticky` cells with an opaque row-token background.\n// • Column reorder -> `enableColumnReordering` + `columnOrder` state, with\n// native HTML5 drag-and-drop on the header cells (plain `cursor-grab`/\n// `active:cursor-grabbing` utilities, since there's no dedicated Figma\n// \"Draggable\" token yet, unlike the resize handle's generated\n// `--ui-resizable-cursor`).\n\n// Extend TanStack's per-column `meta` with the flags DataTable reads. Augmenting\n// the module keeps `ColumnDef.meta.pin` type-safe at the call site.\ndeclare module '@tanstack/react-table' {\n // Type params must match TanStack's `ColumnMeta<TData, TValue>` arity/names for\n // declaration merging, even though this augmentation doesn't reference them.\n // eslint-disable-next-line unused-imports/no-unused-vars\n interface ColumnMeta<TData extends RowData, TValue> {\n /** Pin the column to a table edge (sticky while the grid scrolls horizontally). */\n pin?: 'left' | 'right';\n /**\n * Let the column's header + cells wrap onto multiple lines (drops the fixed\n * row height); mirrors the `Table` primitives' `wrap` prop on TableHead/TableCell.\n */\n wrap?: boolean;\n /** Label shown for this column in the visibility dropdown. */\n label?: string;\n /** Optional visibility-dropdown category for this column. */\n category?: string;\n }\n}\n\n// Sticky offset + stacking for a pinned column, computed from TanStack's own\n// pinning geometry rather than hand-rolled CSS math. A subtle edge shadow (facing\n// the scrollable columns) separates the pinned column from content sliding\n// underneath it — the opaque background that actually hides that content lives\n// in the caller's className (see `headerPinnedBg`/`rowBg` below), not here.\nexport function getPinnedStyle<TData>(\n column: Column<TData, unknown>\n): CSSProperties | undefined {\n const pinned = column.getIsPinned();\n if (!pinned) return undefined;\n return {\n position: 'sticky',\n left: pinned === 'left' ? column.getStart('left') : undefined,\n right: pinned === 'right' ? column.getAfter('right') : undefined,\n zIndex: 1,\n // Derived from the same border token every other table divider uses (not a\n // hardcoded color), so the separator theme-adapts automatically.\n boxShadow:\n pinned === 'left'\n ? '4px 0 4px -4px var(--ui-table-global-row-border-color)'\n : '-4px 0 4px -4px var(--ui-table-global-row-border-color)',\n };\n}\n\n// A column's rendered width tracks TanStack's size model once the consumer has\n// opted in — either explicitly (a `size` set on the `ColumnDef`) or implicitly\n// (column resizing enabled, which needs every column's width to be deterministic\n// for the drag math to work). Without either, columns stay in native `<table>`\n// auto-layout so `size`'s internal default (TanStack falls back to 150) never\n// forces every untouched column to a fixed width.\nexport function getColumnWidth<TData>(\n column: Column<TData, unknown>,\n enableColumnResizing: boolean\n): number | undefined {\n if (column.id === 'select') {\n // Matches the trailing `__actions` column's fixed 48px: symmetric gutters.\n return 48;\n }\n if (enableColumnResizing || column.columnDef.size !== undefined) {\n return column.getSize();\n }\n return undefined;\n}\n\nfunction getHeaderStyle<TData>(\n header: Header<TData, unknown>,\n enableColumnResizing: boolean\n): CSSProperties | undefined {\n const pin = getPinnedStyle(header.column);\n // Non-leaf cells (group-label spans and placeholders) have no single leaf\n // size — skip width so colSpan layout determines the cell's rendered width.\n const width =\n header.subHeaders.length === 0\n ? getColumnWidth(header.column, enableColumnResizing)\n : undefined;\n if (!pin && width === undefined) return undefined;\n return { ...pin, width };\n}\n\nexport function getCellStyle<TData>(\n cell: Cell<TData, unknown>,\n enableColumnResizing: boolean\n): CSSProperties | undefined {\n const pin = getPinnedStyle(cell.column);\n const width = getColumnWidth(cell.column, enableColumnResizing);\n if (!pin && width === undefined) return undefined;\n return { ...pin, width };\n}\n\n// Matches TanStack's own `defaultColumnSizing` fallback bounds — the same\n// range `column.getSize()` already clamps to internally when a column\n// doesn't set its own `minSize`/`maxSize`.\nconst DEFAULT_MIN_COLUMN_SIZE = 20;\nconst DEFAULT_MAX_COLUMN_SIZE = Number.MAX_SAFE_INTEGER;\n\n/**\n * Computes the next column width for an Arrow-key resize step (Shift = larger\n * step), clamped to `[min, max]` regardless of which bound `currentSize`\n * started outside of. Returns `undefined` for any other key, so the caller\n * knows not to `preventDefault()`/resize.\n */\nexport function getResizeKeyboardStep(\n key: string,\n currentSize: number,\n { shiftKey, min, max }: { shiftKey: boolean; min: number; max: number }\n): number | undefined {\n const step = shiftKey ? 50 : 10;\n if (key === 'ArrowLeft')\n return Math.min(max, Math.max(min, currentSize - step));\n if (key === 'ArrowRight')\n return Math.max(min, Math.min(max, currentSize + step));\n return undefined;\n}\n\n/**\n * Moves `from` to `to`'s position in a `columnOrder` array (the drop semantics\n * of the header drag gesture: the dragged column lands where the drop target\n * sits, pushing it aside). Returns `order` unchanged when either id is absent,\n * so a drop on a column that isn't part of the order (e.g. one added while the\n * gesture was in flight) is a no-op rather than a reshuffle.\n */\nexport function reorderColumn(\n order: string[],\n from: string,\n to: string\n): string[] {\n const fromIndex = order.indexOf(from);\n const toIndex = order.indexOf(to);\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return order;\n const next = [...order];\n next.splice(toIndex, 0, next.splice(fromIndex, 1)[0]);\n return next;\n}\n\n/** One line of the header hover tooltip: a capability and the gesture for it. */\nexport interface DataTableHeaderHint {\n /** The capability, rendered in bold (e.g. `Sort column`). */\n label: string;\n /** The gesture that triggers it (e.g. `Click`). */\n action: string;\n}\n\n/** Copy of the header hover tooltip, one entry per column capability. */\nexport interface DataTableHeaderHints {\n sort: DataTableHeaderHint;\n reorder: DataTableHeaderHint;\n resize: DataTableHeaderHint;\n}\n\nconst DEFAULT_HEADER_HINTS: DataTableHeaderHints = {\n sort: { label: 'Sort column', action: 'Click' },\n reorder: { label: 'Reorder column', action: 'Drag' },\n resize: { label: 'Resize column', action: 'Drag border' },\n};\n\n// `columns`/`data` build DataTable's own table instance; `table` renders an\n// externally-built one instead. At least one of the two forms is required —\n// omitting both would otherwise silently render an empty table — but `table`\n// may still be passed alongside `columns`/`data` (e.g. to also drive a\n// composed toolbar/pagination from the same instance DataTable renders).\ntype DataTableDataSourceProps<TData, TValue> =\n | {\n columns: ColumnDef<TData, TValue>[];\n data: TData[];\n /**\n * Also drive an externally-built TanStack `table` instance (e.g. a\n * composed toolbar/pagination) from the same state as this DataTable.\n */\n table?: TanstackTable<TData>;\n }\n | {\n columns?: ColumnDef<TData, TValue>[];\n data?: TData[];\n /**\n * Render from an externally-built TanStack `table` instance instead of\n * DataTable's own — DataTable then owns no state and renders the caller's\n * instance as-is (sorting, filtering, pagination, row models, etc. are all\n * configured on that instance). Makes `columns`/`data` unnecessary (they're\n * only used to build DataTable's own instance) and the following props\n * no-ops (configure the equivalent directly on the external instance\n * instead): `getRowId`, `columnVisibility`, `onColumnVisibilityChange`,\n * `onColumnSizingChange`, `enableColumnResizing`,\n * `enableColumnReordering`, `columnOrder`, `onColumnOrderChange`,\n * `getRowCanExpand`, `rowSelection`, `onRowSelectionChange`,\n * `manualSorting`, `sorting`, `onSortingChange`, `paginationMode`,\n * `onLoadMore`, `loadMoreRootMargin`, `hasNextPage`, `isLoadingMore`.\n * `meta.pin`-driven column pinning is also skipped — pin/unpin the\n * caller's own instance via TanStack's `column.pin()` directly.\n */\n table: TanstackTable<TData>;\n };\n\ninterface DataTableOwnProps<TData> {\n /**\n * Derives a stable row id from the row data (e.g. `row => row.id`) instead of the default\n * array index — needed whenever `data` can reorder (sort/filter) while a row's own per-row\n * state (selection, a hook keyed by row) should follow the row, not the slot it occupied.\n */\n getRowId?: (originalRow: TData, index: number, parent?: Row<TData>) => string;\n /** Enables row expansion for rows that return true. Pair with `renderExpandedRow`. */\n getRowCanExpand?: (row: Row<TData>) => boolean;\n /**\n * Renders expanded content for an expanded row. Used together with\n * `getRowCanExpand`.\n */\n renderExpandedRow?: (row: Row<TData>) => ReactNode;\n /** Alternating row backgrounds. */\n striped?: boolean;\n /** Vertical borders between columns (rows already have horizontal borders). */\n bordered?: boolean;\n /** Highlight the row the user last clicked (the \"current\" row). */\n highlightCurrentRow?: boolean;\n /** Render placeholder skeleton rows instead of data (loading state). */\n skeleton?: boolean;\n /** Number of skeleton rows to render when `skeleton` is set. */\n skeletonRows?: number;\n /**\n * Opt in to interactive column resizing. Renders a drag handle at the trailing\n * edge of each resizable header cell (TanStack's native `columnResizing`).\n */\n enableColumnResizing?: boolean;\n /** Passthrough for the `columnSizing` state so a consumer can persist widths. */\n onColumnSizingChange?: OnChangeFn<ColumnSizingState>;\n /**\n * Opt in to column reordering by dragging a header cell onto another one\n * (native HTML5 drag-and-drop driving TanStack's `columnOrder`). Pinned\n * columns are excluded — they're anchored to a table edge by definition.\n * Pointer-only, matching the design: there is no keyboard equivalent yet.\n */\n enableColumnReordering?: boolean;\n /**\n * Controlled column-order state — pass this (with `onColumnOrderChange`) to\n * persist or share the order. Uncontrolled (internal state) when omitted.\n */\n columnOrder?: ColumnOrderState;\n /** Passthrough for the `columnOrder` state; pairs with `columnOrder`. */\n onColumnOrderChange?: OnChangeFn<ColumnOrderState>;\n /**\n * Controlled column-visibility state — pass this (with\n * `onColumnVisibilityChange`) to share one visibility state with an\n * external `useReactTable` instance (e.g. a composed toolbar). Uncontrolled\n * (internal state) when omitted.\n */\n columnVisibility?: VisibilityState;\n /** Passthrough for the `columnVisibility` state; pairs with `columnVisibility`. */\n onColumnVisibilityChange?: OnChangeFn<VisibilityState>;\n /**\n * Controlled row-selection state — pass this (with `onRowSelectionChange`)\n * to share one selection state with an external `useReactTable` instance\n * (e.g. a composed toolbar/bulk-actions bar). Uncontrolled (internal state)\n * when omitted.\n */\n rowSelection?: RowSelectionState;\n /** Passthrough for the `rowSelection` state; pairs with `rowSelection`. */\n onRowSelectionChange?: OnChangeFn<RowSelectionState>;\n /**\n * Opt out of client-side sorting — pass already-sorted `data` and drive\n * sorting via `sorting`/`onSortingChange` (e.g. mapped to a server query by\n * the caller). Mapping sort state to a query and refetching stays the\n * caller's job; DataTable only skips its own comparator.\n */\n manualSorting?: boolean;\n /**\n * Controlled sorting state — pass this (with `onSortingChange`) to drive\n * sorting externally. Uncontrolled (internal state) when omitted.\n */\n sorting?: SortingState;\n /** Passthrough for the `sorting` state; pairs with `sorting`. */\n onSortingChange?: OnChangeFn<SortingState>;\n /**\n * Renders a full row, bypassing DataTable's default per-cell `flexRender`\n * path entirely (no `<TableRow>`/cell-styling/pinning of DataTable's own).\n * Use to swap in a custom, independently memoizable row component. The\n * caller owns the row's markup and equality semantics — reuse the exported\n * `getCellStyle`/`getPinnedStyle`/`getColumnWidth` helpers to match\n * DataTable's default cell styling if desired.\n *\n * Also bypasses DataTable's `renderExpandedRow` handling — a row rendered\n * via `renderRow` never gets an expanded-content row appended, even when\n * `getRowCanExpand` returns true for it. Read `row.getIsExpanded()` and\n * render the expanded content yourself if you need both.\n */\n renderRow?: (row: Row<TData>, rowIndex: number) => ReactNode;\n /**\n * Renders a custom empty state instead of the default \"No results.\" row.\n * Receives `hasFilters` (whether any column filter is currently applied) so\n * the caller can distinguish \"no data at all\" from \"no matches\" — the\n * actual copy/wording/localization stays the caller's job.\n */\n renderEmptyState?: (context: { hasFilters: boolean }) => ReactNode;\n /**\n * `'page'` (default) keeps today's client-paginated behavior. `'infinite'`\n * omits the paginated row model — `data` is assumed to be the full\n * accumulated array the caller appends to on each `onLoadMore` — and\n * renders a sentinel row that calls `onLoadMore` once it scrolls into view.\n * Does not compose with virtualization; for a large accumulated list, use\n * the `VirtualScrolling` recipe over the raw `Table` primitives instead.\n */\n paginationMode?: 'page' | 'infinite';\n /**\n * Called when the infinite-scroll sentinel intersects the viewport.\n * `paginationMode=\"infinite\"` only. The fetch-more call, cursor/offset\n * tracking, dedup, and accumulating `data` stay the caller's job. Requires\n * at least one row already rendered — an empty table with `data={[]}`\n * cannot use the sentinel to drive its very first fetch; seed the first\n * page yourself (e.g. on mount) and use `onLoadMore` for subsequent pages.\n */\n onLoadMore?: () => void;\n /**\n * Expands the sentinel's `IntersectionObserver` root margin (native CSS\n * margin syntax, e.g. `'400px'`) so `onLoadMore` fires before the sentinel\n * is literally visible — the closer the caller's fetch is to finishing by\n * the time the user actually scrolls there, the less often the trailing\n * loading row is seen. `paginationMode=\"infinite\"` only; no-op when `table`\n * is passed. How far this actually prefetches also depends on page size —\n * a large margin with small pages can trigger several `onLoadMore` calls\n * back-to-back as the user scrolls normally, which is expected.\n */\n loadMoreRootMargin?: string;\n /** Whether more rows are available to load. `paginationMode=\"infinite\"` only. */\n hasNextPage?: boolean;\n /**\n * Accessible name of a column's resize handle. Override to localize.\n * `enableColumnResizing` only.\n */\n resizeColumnLabel?: string;\n /**\n * Text of the default empty-state row. Ignored when `renderEmptyState` is\n * given. Override to localize.\n */\n emptyLabel?: string;\n /**\n * Whether a load is in flight — suppresses further `onLoadMore` calls and\n * renders a trailing loading row. `paginationMode=\"infinite\"` only.\n */\n isLoadingMore?: boolean;\n /**\n * Hide the trailing sticky action column — the column-visibility cog in the\n * header and each row's overflow-actions ellipsis. Shown by default. A\n * no-op when an external `table` is passed (build the column into that\n * instance's own `columns` instead).\n */\n hideActionColumn?: boolean;\n /**\n * Renders the content of a row's overflow-actions menu (e.g. `Edit`/\n * `Delete` items), opened from the ellipsis trigger in the trailing action\n * column. Omit to render that row without a trigger — the 48px column is\n * still reserved. Suppressed (like the trigger itself) while a bulk\n * selection is active, per `TableActionsCell`.\n */\n renderRowActions?: (row: Row<TData>) => ReactNode;\n /** Accessible name of a row's overflow-actions trigger. Override to localize. */\n rowActionsLabel?: string;\n /**\n * Accessible name of the action column's column-visibility trigger.\n * Override to localize.\n */\n columnSettingsLabel?: string;\n /** Placeholder and accessible name of the column-visibility search field. */\n columnSearchPlaceholder?: string;\n /** Label of the per-category action that reveals every column. */\n showAllColumnsLabel?: ReactNode;\n /** Empty state shown when the column search has no matches. */\n noColumnsFoundLabel?: ReactNode;\n /**\n * Copy of the tooltip shown while a header cell is hovered/focused — one\n * line per capability that column actually has (sort/reorder/resize).\n * Override (per capability) to localize; a column with no capability shows\n * no tooltip at all.\n */\n headerHints?: Partial<DataTableHeaderHints>;\n}\n\nexport type DataTableProps<TData, TValue = unknown> = DataTableOwnProps<TData> &\n DataTableDataSourceProps<TData, TValue>;\n\nexport function DataTable<TData, TValue = unknown>({\n columns = [],\n data = [],\n table: externalTable,\n getRowId,\n getRowCanExpand,\n renderExpandedRow,\n striped = false,\n bordered = false,\n highlightCurrentRow = false,\n skeleton = false,\n skeletonRows = 5,\n enableColumnResizing = false,\n onColumnSizingChange,\n enableColumnReordering = false,\n columnOrder: controlledColumnOrder,\n onColumnOrderChange,\n columnVisibility: controlledColumnVisibility,\n onColumnVisibilityChange,\n rowSelection: controlledRowSelection,\n onRowSelectionChange,\n manualSorting = false,\n sorting: controlledSorting,\n onSortingChange,\n renderRow,\n renderEmptyState,\n paginationMode = 'page',\n onLoadMore,\n loadMoreRootMargin,\n hasNextPage = false,\n resizeColumnLabel = 'Resize column',\n emptyLabel = 'No results.',\n isLoadingMore = false,\n hideActionColumn = false,\n renderRowActions,\n rowActionsLabel = 'Row actions',\n columnSettingsLabel,\n columnSearchPlaceholder,\n showAllColumnsLabel,\n noColumnsFoundLabel,\n headerHints,\n}: DataTableProps<TData, TValue>) {\n const resolvedHeaderHints: DataTableHeaderHints = {\n ...DEFAULT_HEADER_HINTS,\n ...headerHints,\n };\n const [internalSorting, setInternalSorting] = useState<SortingState>([]);\n const sorting = controlledSorting ?? internalSorting;\n const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);\n const [internalColumnVisibility, setInternalColumnVisibility] =\n useState<VisibilityState>({});\n const columnVisibility =\n controlledColumnVisibility ?? internalColumnVisibility;\n const [internalRowSelection, setInternalRowSelection] =\n useState<RowSelectionState>({});\n const rowSelection = controlledRowSelection ?? internalRowSelection;\n const [expanded, setExpanded] = useState<ExpandedState>({});\n const [columnSizing, setColumnSizing] = useState<ColumnSizingState>({});\n const [internalColumnOrder, setInternalColumnOrder] =\n useState<ColumnOrderState>([]);\n const columnOrder = controlledColumnOrder ?? internalColumnOrder;\n // The header cell the user is currently dragging, so the source cell can dim\n // while the gesture is in flight.\n const [draggedColumnId, setDraggedColumnId] = useState<string>();\n const [currentRowId, setCurrentRowId] = useState<string>();\n // Roving tabindex over the data rows: exactly one row is a Tab stop at a\n // time (the rest are `tabIndex={-1}`, still focusable programmatically),\n // and Arrow Up/Down move both the DOM focus and which row that is. Clamped\n // against the live row count below rather than reset via an effect, so a\n // filter/page change that shrinks `rows` can't leave it pointing past the\n // end.\n const [focusedRowIndex, setFocusedRowIndex] = useState(0);\n const rowRefs = useRef<Array<HTMLTableRowElement | null>>([]);\n\n const handleColumnSizingChange: OnChangeFn<ColumnSizingState> = (updater) => {\n setColumnSizing(updater);\n onColumnSizingChange?.(updater);\n };\n\n const handleColumnOrderChange: OnChangeFn<ColumnOrderState> = (updater) => {\n if (controlledColumnOrder === undefined) {\n setInternalColumnOrder(updater);\n }\n onColumnOrderChange?.(updater);\n };\n\n const handleColumnVisibilityChange: OnChangeFn<VisibilityState> = (\n updater\n ) => {\n setInternalColumnVisibility(updater);\n onColumnVisibilityChange?.(updater);\n };\n\n const handleSortingChange: OnChangeFn<SortingState> = (updater) => {\n if (controlledSorting === undefined) {\n setInternalSorting(updater);\n }\n onSortingChange?.(updater);\n };\n\n const handleRowSelectionChange: OnChangeFn<RowSelectionState> = (updater) => {\n if (controlledRowSelection === undefined) {\n setInternalRowSelection(updater);\n }\n onRowSelectionChange?.(updater);\n };\n\n // The trailing sticky action column (cog + per-row ellipsis) is a no-op\n // with an external `table` — that instance's own `columns` already decide\n // what renders, DataTable doesn't own its state in that mode (see the\n // `table` prop's tsdoc).\n const showActionColumn = !hideActionColumn && !externalTable;\n // A real `ColumnDef` (rather than chrome bolted onto the render loop) so\n // TanStack's own pinning geometry measures and offsets it like any other\n // pinned column — `header`/`cell` are never read (see the header/body\n // render loops below, which special-case this id), only `size`/`meta.pin`.\n const actionColumns = useMemo<ColumnDef<TData, TValue>[]>(\n () =>\n showActionColumn\n ? [\n {\n id: '__actions',\n size: 48,\n enableSorting: false,\n enableHiding: false,\n meta: { pin: 'right' },\n header: () => null,\n cell: () => null,\n },\n ]\n : [],\n [showActionColumn]\n );\n const tableColumns = useMemo(\n () => [...columns, ...actionColumns],\n [columns, actionColumns]\n );\n // The pinned action cell's own hover (its ellipsis/cog trigger) should be\n // the only thing that tints while it's hovered — not the row underneath it\n // (see `hover:bg-transparent` below). Tracked by id (rather than a boolean)\n // because it's set from the action cell of whichever row is currently under\n // the pointer.\n const [actionHoveredRowId, setActionHoveredRowId] = useState<string>();\n\n // Built unconditionally — hooks can't be conditionally called — but only\n // feeds the render path below when no external `table` is passed (see\n // `table` below).\n const internalTable = useReactTable({\n data,\n columns: tableColumns,\n getRowId,\n enableColumnResizing,\n columnResizeMode: 'onChange',\n getCoreRowModel: getCoreRowModel(),\n getExpandedRowModel: getExpandedRowModel(),\n ...(paginationMode === 'page'\n ? { getPaginationRowModel: getPaginationRowModel() }\n : {}),\n getRowCanExpand,\n onExpandedChange: setExpanded,\n manualSorting,\n onSortingChange: handleSortingChange,\n ...(manualSorting ? {} : { getSortedRowModel: getSortedRowModel() }),\n onColumnFiltersChange: setColumnFilters,\n getFilteredRowModel: getFilteredRowModel(),\n onColumnVisibilityChange: handleColumnVisibilityChange,\n onRowSelectionChange: handleRowSelectionChange,\n onColumnSizingChange: handleColumnSizingChange,\n onColumnOrderChange: handleColumnOrderChange,\n initialState: showActionColumn\n ? { columnPinning: { right: ['__actions'] } }\n : undefined,\n state: {\n sorting,\n columnFilters,\n columnVisibility,\n rowSelection,\n expanded,\n columnSizing,\n columnOrder,\n },\n });\n\n // The caller's instance is the single source of truth when passed — it\n // configures its own row models/state, so DataTable just renders from it.\n const table = externalTable ?? internalTable;\n const isInfiniteScroll = !externalTable && paginationMode === 'infinite';\n // `enableColumnResizing` is documented as a no-op with an external `table`\n // (the caller owns that instance's ColumnSizing state), but ColumnSizing is\n // a built-in TanStack feature present on any instance — reading the raw\n // prop below would still render live resize handles that mutate the\n // caller's own instance. Gate it the same way as `isInfiniteScroll`.\n const resizingEnabled = enableColumnResizing && !externalTable;\n // Same reasoning as `resizingEnabled`: ColumnOrdering is a built-in TanStack\n // feature present on any instance, so reading the raw prop would let the\n // header drag mutate a caller-owned instance DataTable doesn't manage.\n const reorderingEnabled = enableColumnReordering && !externalTable;\n // A resize drag necessarily moves the pointer off the (few-px-wide) resize\n // handle and onto the header cell itself. If that cell is still\n // `draggable`, the browser arms a native HTML5 reorder drag mid-resize —\n // native drag then swallows the mousemove/mouseup TanStack's resize\n // handler relies on, so the cursor flips to the reorder \"grab\" icon and\n // releasing the mouse doesn't cleanly end the resize. Reading this back\n // from TanStack's own resize state (rather than pointer position) keeps\n // every header cell non-draggable for the *entire* gesture, regardless of\n // where the pointer ends up.\n const isAnyColumnResizing = Boolean(\n table.getState().columnSizingInfo.isResizingColumn\n );\n // The resize handle only shows its `ew-resize` cursor while the pointer is\n // directly over its thin 4px hit area — once a drag starts, fast pointer\n // movement leaves that area and the browser shows whatever cursor the\n // element underneath declares (e.g. a sortable header's `cursor-pointer`).\n // This attribute + the `html[data-ui-column-resizing] *` rule in\n // styles/index.css force `ew-resize` everywhere for the duration of the\n // drag — a plain `<body>` inline cursor loses to a descendant's own\n // `cursor` declaration, so it isn't enough on its own.\n useEffect(() => {\n if (!isAnyColumnResizing) return;\n document.documentElement.setAttribute('data-ui-column-resizing', '');\n return () => {\n document.documentElement.removeAttribute('data-ui-column-resizing');\n };\n }, [isAnyColumnResizing]);\n const sentinelRef = useIntersectionObserver<HTMLTableRowElement>({\n onIntersect: () => onLoadMore?.(),\n disabled: !isInfiniteScroll || !hasNextPage || isLoadingMore,\n rootMargin: loadMoreRootMargin,\n });\n\n // Arrow-key resize on the drag handle (see `canResize` below). Ignores any\n // modifier besides Shift so it doesn't hijack browser/OS shortcuts bound to\n // Ctrl/Alt/Cmd+Arrow (e.g. back navigation) while the handle has focus.\n const handleResizeKeyDown = (\n event: KeyboardEvent<HTMLDivElement>,\n header: Header<TData, unknown>\n ) => {\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n const nextSize = getResizeKeyboardStep(event.key, header.column.getSize(), {\n shiftKey: event.shiftKey,\n min: header.column.columnDef.minSize ?? DEFAULT_MIN_COLUMN_SIZE,\n max: header.column.columnDef.maxSize ?? DEFAULT_MAX_COLUMN_SIZE,\n });\n if (nextSize === undefined) return;\n event.preventDefault();\n table.setColumnSizing((old) => ({ ...old, [header.column.id]: nextSize }));\n };\n\n // Native HTML5 drag-and-drop on the header cells. `columnOrder` starts empty\n // (TanStack reads that as \"the declared order\"), so the first drop seeds it\n // from the live leaf columns before moving anything.\n const handleColumnDragStart = (\n event: DragEvent<HTMLTableCellElement>,\n columnId: string\n ) => {\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n // Firefox requires setData() during dragstart to actually initiate a\n // native drag; the value itself is unused (drop reads draggedColumnId).\n event.dataTransfer.setData('text/plain', columnId);\n }\n setDraggedColumnId(columnId);\n };\n\n const handleColumnDragOver = (\n event: DragEvent<HTMLTableCellElement>,\n targetColumnId: string\n ) => {\n // Without this the browser rejects the drop and no `onDrop` ever fires.\n event.preventDefault();\n if (!event.dataTransfer) return;\n // Show \"no drop\" cursor when the source and target belong to different\n // header groups — cross-group reordering is not allowed (it interleaves\n // leaf columns from different groups in columnOrder, which breaks\n // TanStack's header-group rendering and causes label duplication).\n const draggedParentId = draggedColumnId\n ? table.getColumn(draggedColumnId)?.parent?.id\n : undefined;\n const targetParentId = table.getColumn(targetColumnId)?.parent?.id;\n event.dataTransfer.dropEffect =\n draggedParentId === targetParentId ? 'move' : 'none';\n };\n\n const handleColumnDrop = (\n event: DragEvent<HTMLTableCellElement>,\n targetColumnId: string\n ) => {\n event.preventDefault();\n if (!draggedColumnId) return;\n // Reject drops that would move a column outside its header group. Allowing\n // cross-group drops interleaves the two groups' leaf columns in columnOrder,\n // which causes TanStack to produce malformed header rows: the group label\n // renders at the wrong position (bug 2) and sometimes duplicates (bug 3).\n //\n // Known limitation: this guard only covers the drag gesture. The same broken\n // state is reachable via the `columnOrder` controlled prop — if the caller\n // passes a `columnOrder` where a group's leaf columns are not contiguous,\n // TanStack will produce duplicate group-label cells with no warning.\n const draggedParentId = table.getColumn(draggedColumnId)?.parent?.id;\n const targetParentId = table.getColumn(targetColumnId)?.parent?.id;\n if (draggedParentId !== targetParentId) {\n setDraggedColumnId(undefined);\n return;\n }\n const current = table.getState().columnOrder;\n const base = current.length\n ? current\n : table.getAllLeafColumns().map((column) => column.id);\n table.setColumnOrder(reorderColumn(base, draggedColumnId, targetColumnId));\n setDraggedColumnId(undefined);\n };\n\n // Read each column's `meta.pin` and drive TanStack's native pinning state.\n // Always calls `pin()` (rather than only when truthy) so a column whose\n // `meta.pin` is removed dynamically actually un-pins. Skipped for an\n // external `table` — DataTable owns no state in that mode (see the `table`\n // prop's tsdoc), so the caller's own pinning setup is left alone.\n //\n // Known limitation: pinning a leaf column that is nested inside a header\n // group breaks group contiguity. TanStack moves pinned leaves to a separate\n // bucket and reorders columns as [...left, ...center, ...right] before\n // createHeaderGroup runs, separating the pinned leaf from its siblings.\n // The result is a duplicate group-label cell in the header row — the same\n // rendering defect that the drag guard exists to prevent. Do not set\n // `meta.pin` on a leaf column that belongs to a header group.\n useEffect(() => {\n if (externalTable) return;\n table.getAllLeafColumns().forEach((column) => {\n column.pin(column.columnDef.meta?.pin ?? false);\n });\n }, [table, tableColumns, externalTable]);\n\n const rows = table.getRowModel().rows;\n // Derived from the selection state, so it's the same for every row — compute\n // it once instead of per row inside the render loop below.\n const bulkSelectionActive = isBulkSelectionActive(table);\n const activeRowIndex = rows.length\n ? Math.min(focusedRowIndex, rows.length - 1)\n : 0;\n\n // Mouse/pointer clicks still focus a `tabIndex={-1}` row (browsers allow\n // programmatic/click focus regardless of tabIndex value) — syncing the\n // roving index on focus makes that row the next Tab stop too, matching how\n // users expect the last-interacted row to keep focus.\n const handleRowFocus = (rowIndex: number) => {\n setFocusedRowIndex(rowIndex);\n };\n\n const handleRowKeyDown = (\n event: KeyboardEvent<HTMLTableRowElement>,\n rowIndex: number\n ) => {\n // Keydown bubbles, so arrow keys from an interactive control inside a cell\n // (number spinner, textarea caret, native select) would otherwise be\n // hijacked to move row focus. Only roam when the row itself is focused.\n if (event.target !== event.currentTarget) return;\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n const nextIndex =\n event.key === 'ArrowDown'\n ? Math.min(rowIndex + 1, rows.length - 1)\n : Math.max(rowIndex - 1, 0);\n if (nextIndex === rowIndex) return;\n event.preventDefault();\n setFocusedRowIndex(nextIndex);\n rowRefs.current[nextIndex]?.focus();\n };\n // Vertical borders are opt-in; a trailing border on the last cell would\n // double up with the wrapper, so suppress it.\n const borderedClass = bordered\n ? '[&_th:not(:last-child)]:border-e [&_td:not(:last-child)]:border-e [&_th]:border-[var(--ui-table-global-row-border-color)] [&_td]:border-[var(--ui-table-global-row-border-color)]'\n : undefined;\n\n // A pinned header/body cell must be opaque so the cells scrolling under it\n // (same row) aren't visible. `--ui-table-data-row-color-idle` is\n // *transparent by design* (an idle row shows the page/card surface through\n // it), so it can't be reused here — pinned idle cells need the actual\n // resolved surface color instead (`--ui-background-surface-primary`, bridged\n // to `bg-background`). Non-idle states (selected/current/striped) already use\n // real opaque tokens and are safe to mirror as-is (see `rowBg` below).\n const headerPinnedBg = 'bg-background';\n\n return (\n <div\n className={cn(\n 'rounded-md border border-[var(--ui-table-global-row-border-color)]',\n borderedClass\n )}\n >\n <Table\n style={\n resizingEnabled ? { width: table.getCenterTotalSize() } : undefined\n }\n >\n {/* One provider for the whole header row so the capability hints share\n a single open/close delay group (Provider renders no DOM, so the\n table markup is unaffected). */}\n <TooltipProvider>\n <TableHeader>\n {table.getHeaderGroups().map((headerGroup, groupIndex, headerGroups) => (\n <TableRow key={headerGroup.id} className=\"hover:bg-transparent\">\n {headerGroup.headers.map((header) => {\n const isPinned = header.column.getIsPinned();\n // Non-leaf header cells (group-label spans and TanStack's\n // structural placeholder cells) are purely presentational —\n // no sort/reorder/resize/tooltip.\n const isGroupHeader = header.subHeaders.length > 0;\n const canResize =\n !isGroupHeader &&\n resizingEnabled &&\n header.column.getCanResize() &&\n header.column.id !== 'select';\n // A pinned column is anchored to a table edge, so dragging it\n // out of that edge would contradict its own pinning.\n const canReorder =\n !isGroupHeader &&\n reorderingEnabled &&\n !header.isPlaceholder &&\n !isPinned;\n const canSort =\n !isGroupHeader &&\n !header.isPlaceholder &&\n header.column.getCanSort();\n // One tooltip line per capability the column actually has, in\n // the design's order; a column with none gets no tooltip.\n const hints = [\n canSort && resolvedHeaderHints.sort,\n canReorder && resolvedHeaderHints.reorder,\n canResize && resolvedHeaderHints.resize,\n ].filter((hint): hint is DataTableHeaderHint =>\n Boolean(hint)\n );\n if (header.column.id === '__actions') {\n if (groupIndex < headerGroups.length - 1) {\n // Group-header rows carry no cog, but the pinned cell\n // must still be present so the row scrolls horizontally\n // in sync with the leaf header row and the body.\n return (\n <TableSettingsCell\n key={header.id}\n style={getHeaderStyle(header, resizingEnabled)}\n className={headerPinnedBg}\n />\n );\n }\n return (\n <TableSettingsCell\n key={header.id}\n style={getHeaderStyle(header, resizingEnabled)}\n className={headerPinnedBg}\n >\n <DataTableViewOptions\n table={table}\n iconOnly\n triggerAriaLabel={columnSettingsLabel}\n searchPlaceholder={columnSearchPlaceholder}\n showAllLabel={showAllColumnsLabel}\n noResultsLabel={noColumnsFoundLabel}\n />\n </TableSettingsCell>\n );\n }\n const headerCell = (\n <TableHead\n colSpan={header.colSpan}\n wrap={header.column.columnDef.meta?.wrap}\n style={getHeaderStyle(header, resizingEnabled)}\n draggable={\n (canReorder && !isAnyColumnResizing) || undefined\n }\n onDragStart={\n canReorder\n ? (event) =>\n handleColumnDragStart(event, header.column.id)\n : undefined\n }\n onDragOver={\n canReorder\n ? (event) =>\n handleColumnDragOver(event, header.column.id)\n : undefined\n }\n onDrop={\n canReorder\n ? (event) => handleColumnDrop(event, header.column.id)\n : undefined\n }\n onDragEnd={\n canReorder\n ? () => setDraggedColumnId(undefined)\n : undefined\n }\n className={cn(\n canResize && 'relative',\n // Per the design, a sortable header tints the whole\n // cell on hover/press, not just the inner sort button.\n // Suppressed while any column is resizing, since the\n // pointer drags across neighboring `<th>`s and would\n // otherwise tint them via native `:hover`.\n canSort &&\n !isAnyColumnResizing &&\n 'transition-colors hover:bg-[var(--ui-table-header-cell-color-hover)] active:bg-[var(--ui-table-header-cell-color-active)]',\n canReorder &&\n !isAnyColumnResizing &&\n 'cursor-grab select-none active:cursor-grabbing',\n canReorder &&\n draggedColumnId === header.column.id &&\n 'opacity-50',\n isPinned && headerPinnedBg\n )}\n >\n {header.isPlaceholder\n ? null\n : flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n {canResize && (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={resizeColumnLabel}\n aria-valuenow={header.column.getSize()}\n aria-valuemin={\n header.column.columnDef.minSize ??\n DEFAULT_MIN_COLUMN_SIZE\n }\n aria-valuemax={\n header.column.columnDef.maxSize ??\n DEFAULT_MAX_COLUMN_SIZE\n }\n tabIndex={0}\n // Keeps a press on the handle from starting the header\n // cell's reorder drag instead of a resize when both\n // features are enabled.\n draggable={false}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onKeyDown={(event) =>\n handleResizeKeyDown(event, header)\n }\n className={cn(\n 'absolute end-0 top-0 h-full w-1 cursor-(--ui-resizable-cursor) touch-none select-none bg-[var(--ui-table-global-row-border-color)] opacity-0 transition-[opacity,background-color] hover:bg-[var(--ui-resizable-border-color-hover)] hover:opacity-100 focus-visible:bg-[var(--ui-resizable-border-color-hover)] focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-[var(--ui-focus-primary)]',\n header.column.getIsResizing() &&\n 'bg-[var(--ui-resizable-border-color-active)] opacity-100'\n )}\n />\n )}\n </TableHead>\n );\n if (hints.length === 0) {\n return <Fragment key={header.id}>{headerCell}</Fragment>;\n }\n return (\n <Tooltip\n key={header.id}\n disabled={\n isAnyColumnResizing || draggedColumnId !== undefined\n }\n >\n {/* The whole header cell is the trigger (not just its sort\n button), so the hint covers the reorder/resize gestures\n that live on the cell itself. Disabled mid-drag/resize\n so the hint doesn't pop up over a neighboring cell\n while the pointer passes through it. */}\n <TooltipTrigger render={headerCell} />\n <TooltipContent className=\"flex flex-col gap-1\">\n {hints.map((hint) => (\n <span key={hint.label}>\n <span className=\"font-semibold\">{hint.label}:</span>{' '}\n {hint.action}\n </span>\n ))}\n </TooltipContent>\n </Tooltip>\n );\n })}\n </TableRow>\n ))}\n </TableHeader>\n </TooltipProvider>\n <TableBody>\n {skeleton ? (\n Array.from({ length: skeletonRows }).map((_, rowIndex) => (\n <TableRow\n key={`skeleton-${rowIndex}`}\n className=\"hover:bg-transparent\"\n >\n {table.getVisibleLeafColumns().map((column) => (\n <TableCell key={column.id}>\n <div className=\"h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]\" />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : rows?.length ? (\n rows.map((row, rowIndex) => {\n if (renderRow) {\n return (\n <Fragment key={row.id}>{renderRow(row, rowIndex)}</Fragment>\n );\n }\n const isSelected = row.getIsSelected();\n const isCurrent = highlightCurrentRow && currentRowId === row.id;\n // Opaque background applied to pinned cells so sibling cells don't\n // show through while the grid scrolls horizontally. Mirrors the\n // row's own resolved background across selection/current/stripe —\n // the idle case falls back to the real surface color\n // (`bg-background`) since the row's own idle token is transparent\n // by design (see `headerPinnedBg` above).\n // The idle case mirrors the row's own hover tint via `group` (see\n // the row's `group` class below) so the pinned action column\n // doesn't look \"stuck\" idle while the rest of the row is\n // hovered — the row's native `hover:` can't reach it since it\n // needs its own opaque background (see `headerPinnedBg` above).\n const rowBg =\n isSelected || isCurrent\n ? 'bg-[var(--ui-table-data-row-color-active)]'\n : striped && rowIndex % 2 === 1\n ? 'bg-[var(--ui-background-surface-secondary)]'\n : 'bg-background group-hover:bg-[var(--ui-table-data-row-color-hover)]';\n // While a selection is in play the action cell renders nothing and\n // carries no tint of its own, so suppressing the row's hover tint\n // for it would leave the pointer over a row that reacts to\n // nothing. Also covers a row hovered just before the selection\n // started, whose id is still in `actionHoveredRowId`.\n const isActionCellHovered =\n !bulkSelectionActive && actionHoveredRowId === row.id;\n return (\n <Fragment key={row.id}>\n <TableRow\n ref={(node) => {\n rowRefs.current[rowIndex] = node;\n }}\n tabIndex={rowIndex === activeRowIndex ? 0 : -1}\n onFocus={() => handleRowFocus(rowIndex)}\n onKeyDown={(event) => handleRowKeyDown(event, rowIndex)}\n selected={isSelected}\n onClick={\n highlightCurrentRow\n ? () => setCurrentRowId(row.id)\n : undefined\n }\n className={cn(\n 'group',\n highlightCurrentRow && 'cursor-pointer',\n striped &&\n rowIndex % 2 === 1 &&\n !isSelected &&\n !isCurrent &&\n 'bg-[var(--ui-background-surface-secondary)]',\n isCurrent &&\n !isSelected &&\n 'bg-[var(--ui-table-data-row-color-active)]',\n // The action cell's own hover tint (its ellipsis/cog\n // trigger) should be the only thing that reacts while\n // it's hovered — not the rest of the row underneath it.\n isActionCellHovered && 'hover:bg-transparent'\n )}\n >\n {row.getVisibleCells().map((cell) => {\n const isPinned = cell.column.getIsPinned();\n if (cell.column.id === '__actions') {\n return (\n <TableActionsCell\n key={cell.id}\n style={getCellStyle(cell, resizingEnabled)}\n className={rowBg}\n bulkSelectionActive={bulkSelectionActive}\n onMouseEnter={\n bulkSelectionActive\n ? undefined\n : () => setActionHoveredRowId(row.id)\n }\n onMouseLeave={\n bulkSelectionActive\n ? undefined\n : () =>\n setActionHoveredRowId((id) =>\n id === row.id ? undefined : id\n )\n }\n >\n {renderRowActions && (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <ButtonIcon aria-label={rowActionsLabel} />\n }\n >\n <EllipsisIcon />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {renderRowActions(row)}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </TableActionsCell>\n );\n }\n return (\n <TableCell\n key={cell.id}\n wrap={cell.column.columnDef.meta?.wrap}\n style={getCellStyle(cell, resizingEnabled)}\n className={cn(isPinned && rowBg)}\n >\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )}\n </TableCell>\n );\n })}\n </TableRow>\n {renderExpandedRow && row.getIsExpanded() && (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell\n className=\"h-auto py-3\"\n colSpan={row.getVisibleCells().length}\n >\n {renderExpandedRow(row)}\n </TableCell>\n </TableRow>\n )}\n </Fragment>\n );\n })\n ) : renderEmptyState ? (\n <TableRow>\n <TableCell\n colSpan={table.getVisibleLeafColumns().length}\n className=\"h-24 text-center\"\n >\n {renderEmptyState({\n hasFilters: table.getState().columnFilters.length > 0,\n })}\n </TableCell>\n </TableRow>\n ) : (\n <TableRow>\n <TableCell\n colSpan={table.getVisibleLeafColumns().length}\n className=\"h-24 text-center text-[var(--ui-table-data-value-color-disabled)]\"\n >\n {emptyLabel}\n </TableCell>\n </TableRow>\n )}\n {isInfiniteScroll && !skeleton && rows.length > 0 && hasNextPage && (\n <TableRow\n ref={sentinelRef}\n aria-hidden\n className=\"hover:bg-transparent\"\n >\n <TableCell\n colSpan={table.getVisibleLeafColumns().length}\n className=\"h-1 p-0\"\n />\n </TableRow>\n )}\n {isInfiniteScroll &&\n !skeleton &&\n rows.length > 0 &&\n isLoadingMore && (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell colSpan={table.getVisibleLeafColumns().length}>\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]\"\n >\n <span className=\"sr-only\">Loading more rows…</span>\n </div>\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAqHA,SAAgB,EACd,GAC2B;CAC3B,IAAM,IAAS,EAAO,YAAY;CAC7B,OACL,OAAO;EACL,UAAU;EACV,MAAM,MAAW,SAAS,EAAO,SAAS,MAAM,IAAI,KAAA;EACpD,OAAO,MAAW,UAAU,EAAO,SAAS,OAAO,IAAI,KAAA;EACvD,QAAQ;EAGR,WACE,MAAW,SACP,2DACA;CACR;AACF;AAQA,SAAgB,EACd,GACA,GACoB;CACpB,IAAI,EAAO,OAAO,UAEhB,OAAO;CAET,IAAI,KAAwB,EAAO,UAAU,SAAS,KAAA,GACpD,OAAO,EAAO,QAAQ;AAG1B;AAEA,SAAS,EACP,GACA,GAC2B;CAC3B,IAAM,IAAM,EAAe,EAAO,MAAM,GAGlC,IACJ,EAAO,WAAW,WAAW,IACzB,EAAe,EAAO,QAAQ,CAAoB,IAClD,KAAA;CACF,OAAC,KAAO,MAAU,KAAA,IACtB,OAAO;EAAE,GAAG;EAAK;CAAM;AACzB;AAEA,SAAgB,EACd,GACA,GAC2B;CAC3B,IAAM,IAAM,EAAe,EAAK,MAAM,GAChC,IAAQ,EAAe,EAAK,QAAQ,CAAoB;CAC1D,OAAC,KAAO,MAAU,KAAA,IACtB,OAAO;EAAE,GAAG;EAAK;CAAM;AACzB;AAKA,IAAM,IAA0B,IAC1B;AAQN,SAAgB,EACd,GACA,GACA,EAAE,aAAU,QAAK,UACG;CACpB,IAAM,IAAO,IAAW,KAAK;CAC7B,IAAI,MAAQ,aACV,OAAO,KAAK,IAAI,GAAK,KAAK,IAAI,GAAK,IAAc,CAAI,CAAC;CACxD,IAAI,MAAQ,cACV,OAAO,KAAK,IAAI,GAAK,KAAK,IAAI,GAAK,IAAc,CAAI,CAAC;AAE1D;AASA,SAAgB,EACd,GACA,GACA,GACU;CACV,IAAM,IAAY,EAAM,QAAQ,CAAI,GAC9B,IAAU,EAAM,QAAQ,CAAE;CAChC,IAAI,MAAc,MAAM,MAAY,MAAM,MAAc,GAAS,OAAO;CACxE,IAAM,IAAO,CAAC,GAAG,CAAK;CAEtB,OADA,EAAK,OAAO,GAAS,GAAG,EAAK,OAAO,GAAW,CAAC,CAAC,CAAC,EAAE,GAC7C;AACT;AAiBA,IAAM,KAA6C;CACjD,MAAM;EAAE,OAAO;EAAe,QAAQ;CAAQ;CAC9C,SAAS;EAAE,OAAO;EAAkB,QAAQ;CAAO;CACnD,QAAQ;EAAE,OAAO;EAAiB,QAAQ;CAAc;AAC1D;AA+NA,SAAgB,EAAmC,EACjD,aAAU,CAAC,GACX,UAAO,CAAC,GACR,OAAO,GACP,cACA,qBACA,uBACA,cAAU,IACV,eAAW,IACX,yBAAsB,IACtB,cAAW,IACX,mBAAe,GACf,2BAAuB,IACvB,0BACA,6BAAyB,IACzB,aAAa,IACb,yBACA,kBAAkB,IAClB,8BACA,cAAc,IACd,0BACA,mBAAgB,IAChB,SAAS,GACT,qBACA,cACA,qBACA,oBAAiB,QACjB,gBACA,wBACA,iBAAc,IACd,wBAAoB,iBACpB,iBAAa,eACb,mBAAgB,IAChB,uBAAmB,IACnB,qBACA,sBAAkB,eAClB,yBACA,6BACA,yBACA,yBACA,mBACgC;CAChC,IAAM,IAA4C;EAChD,GAAG;EACH,GAAG;CACL,GACM,CAAC,IAAiB,MAAsB,EAAuB,CAAC,CAAC,GACjE,KAAU,KAAqB,IAC/B,CAAC,IAAe,MAAoB,EAA6B,CAAC,CAAC,GACnE,CAAC,IAA0B,MAC/B,EAA0B,CAAC,CAAC,GACxB,KACJ,MAA8B,IAC1B,CAAC,IAAsB,MAC3B,EAA4B,CAAC,CAAC,GAC1B,KAAe,MAA0B,IACzC,CAAC,IAAU,MAAe,EAAwB,CAAC,CAAC,GACpD,CAAC,IAAc,MAAmB,EAA4B,CAAC,CAAC,GAChE,CAAC,IAAqB,MAC1B,EAA2B,CAAC,CAAC,GACzB,KAAc,MAAyB,IAGvC,CAAC,GAAiB,KAAsB,EAAiB,GACzD,CAAC,IAAc,MAAmB,EAAiB,GAOnD,CAAC,IAAiB,KAAsB,EAAS,CAAC,GAClD,IAAU,GAA0C,CAAC,CAAC,GAEtD,MAA2D,MAAY;EAE3E,AADA,GAAgB,CAAO,GACvB,KAAuB,CAAO;CAChC,GAEM,MAAyD,MAAY;EAIzE,AAHI,OAA0B,KAAA,KAC5B,GAAuB,CAAO,GAEhC,KAAsB,CAAO;CAC/B,GAEM,MACJ,MACG;EAEH,AADA,GAA4B,CAAO,GACnC,KAA2B,CAAO;CACpC,GAEM,MAAiD,MAAY;EAIjE,AAHI,MAAsB,KAAA,KACxB,GAAmB,CAAO,GAE5B,KAAkB,CAAO;CAC3B,GAEM,MAA2D,MAAY;EAI3E,AAHI,OAA2B,KAAA,KAC7B,GAAwB,CAAO,GAEjC,KAAuB,CAAO;CAChC,GAMM,IAAmB,CAAC,MAAoB,CAAC,GAKzC,IAAgB,SAElB,IACI,CACE;EACE,IAAI;EACJ,MAAM;EACN,eAAe;EACf,cAAc;EACd,MAAM,EAAE,KAAK,QAAQ;EACrB,cAAc;EACd,YAAY;CACd,CACF,IACA,CAAC,GACP,CAAC,CAAgB,CACnB,GACM,KAAe,SACb,CAAC,GAAG,GAAS,GAAG,CAAa,GACnC,CAAC,GAAS,CAAa,CACzB,GAMM,CAAC,IAAoB,MAAyB,EAAiB,GAK/D,KAAgB,EAAc;EAClC;EACA,SAAS;EACT;EACA;EACA,kBAAkB;EAClB,iBAAiB,EAAgB;EACjC,qBAAqB,EAAoB;EACzC,GAAI,MAAmB,SACnB,EAAE,uBAAuB,EAAsB,EAAE,IACjD,CAAC;EACL;EACA,kBAAkB;EAClB;EACA,iBAAiB;EACjB,GAAI,IAAgB,CAAC,IAAI,EAAE,mBAAmB,EAAkB,EAAE;EAClE,uBAAuB;EACvB,qBAAqB,EAAoB;EACzC,0BAA0B;EAC1B,sBAAsB;EACtB,sBAAsB;EACtB,qBAAqB;EACrB,cAAc,IACV,EAAE,eAAe,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,IAC1C,KAAA;EACJ,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF,CAAC,GAIK,IAAQ,KAAiB,IACzB,IAAmB,CAAC,KAAiB,MAAmB,YAMxD,IAAkB,MAAwB,CAAC,GAI3C,KAAoB,MAA0B,CAAC,GAU/C,IAAsB,EAC1B,EAAM,SAAS,CAAC,CAAC,iBAAiB;CAUpC,SAAgB;EACT,OAEL,OADA,SAAS,gBAAgB,aAAa,2BAA2B,EAAE,SACtD;GACX,SAAS,gBAAgB,gBAAgB,yBAAyB;EACpE;CACF,GAAG,CAAC,CAAmB,CAAC;CACxB,IAAM,KAAc,EAA6C;EAC/D,mBAAmB,KAAa;EAChC,UAAU,CAAC,KAAoB,CAAC,KAAe;EAC/C,YAAY;CACd,CAAC,GAKK,MACJ,GACA,MACG;EACH,IAAI,EAAM,WAAW,EAAM,UAAU,EAAM,SAAS;EACpD,IAAM,IAAW,EAAsB,EAAM,KAAK,EAAO,OAAO,QAAQ,GAAG;GACzE,UAAU,EAAM;GAChB,KAAK,EAAO,OAAO,UAAU,WAAW;GACxC,KAAK,EAAO,OAAO,UAAU,WAAW;EAC1C,CAAC;EACG,MAAa,KAAA,MACjB,EAAM,eAAe,GACrB,EAAM,iBAAiB,OAAS;GAAE,GAAG;IAAM,EAAO,OAAO,KAAK;EAAS,EAAE;CAC3E,GAKM,MACJ,GACA,MACG;EAOH,AANI,EAAM,iBACR,EAAM,aAAa,gBAAgB,QAGnC,EAAM,aAAa,QAAQ,cAAc,CAAQ,IAEnD,EAAmB,CAAQ;CAC7B,GAEM,MACJ,GACA,MACG;EAGH,IADA,EAAM,eAAe,GACjB,CAAC,EAAM,cAAc;EAKzB,IAAM,IAAkB,IACpB,EAAM,UAAU,CAAe,CAAC,EAAE,QAAQ,KAC1C,KAAA,GACE,IAAiB,EAAM,UAAU,CAAc,CAAC,EAAE,QAAQ;EAChE,EAAM,aAAa,aACjB,MAAoB,IAAiB,SAAS;CAClD,GAEM,MACJ,GACA,MACG;EAEH,IADA,EAAM,eAAe,GACjB,CAAC,GAAiB;EAYtB,IAFwB,EAAM,UAAU,CAAe,CAAC,EAAE,QAAQ,OAC3C,EAAM,UAAU,CAAc,CAAC,EAAE,QAAQ,IACxB;GACtC,EAAmB,KAAA,CAAS;GAC5B;EACF;EACA,IAAM,IAAU,EAAM,SAAS,CAAC,CAAC,aAC3B,IAAO,EAAQ,SACjB,IACA,EAAM,kBAAkB,CAAC,CAAC,KAAK,MAAW,EAAO,EAAE;EAEvD,AADA,EAAM,eAAe,EAAc,GAAM,GAAiB,CAAc,CAAC,GACzE,EAAmB,KAAA,CAAS;CAC9B;CAeA,SAAgB;EACV,KACJ,EAAM,kBAAkB,CAAC,CAAC,SAAS,MAAW;GAC5C,EAAO,IAAI,EAAO,UAAU,MAAM,OAAO,EAAK;EAChD,CAAC;CACH,GAAG;EAAC;EAAO;EAAc;CAAa,CAAC;CAEvC,IAAM,IAAO,EAAM,YAAY,CAAC,CAAC,MAG3B,IAAsB,GAAsB,CAAK,GACjD,KAAiB,EAAK,SACxB,KAAK,IAAI,IAAiB,EAAK,SAAS,CAAC,IACzC,GAME,MAAkB,MAAqB;EAC3C,EAAmB,CAAQ;CAC7B,GAEM,MACJ,GACA,MACG;EAKH,IADI,EAAM,WAAW,EAAM,iBACvB,EAAM,QAAQ,eAAe,EAAM,QAAQ,WAAW;EAC1D,IAAM,IACJ,EAAM,QAAQ,cACV,KAAK,IAAI,IAAW,GAAG,EAAK,SAAS,CAAC,IACtC,KAAK,IAAI,IAAW,GAAG,CAAC;EAC1B,MAAc,MAClB,EAAM,eAAe,GACrB,EAAmB,CAAS,GAC5B,EAAQ,QAAQ,EAAU,EAAE,MAAM;CACpC,GAGM,KAAgB,KAClB,sLACA,KAAA,GASE,IAAiB;CAEvB,OACE,kBAAC,OAAD;EACE,WAAW,EACT,sEACA,EACF;EAEA,UAAA,kBAAC,IAAD;GACE,OACE,IAAkB,EAAE,OAAO,EAAM,mBAAmB,EAAE,IAAI,KAAA;GAF9D,UAAA,CAQE,kBAAC,GAAD,EAAA,UACE,kBAAC,IAAD,EAAA,UACG,EAAM,gBAAgB,CAAC,CAAC,KAAK,GAAa,GAAY,MACrD,kBAAC,GAAD;IAA+B,WAAU;IACtC,UAAA,EAAY,QAAQ,KAAK,MAAW;KACnC,IAAM,IAAW,EAAO,OAAO,YAAY,GAIrC,IAAgB,EAAO,WAAW,SAAS,GAC3C,IACJ,CAAC,KACD,KACA,EAAO,OAAO,aAAa,KAC3B,EAAO,OAAO,OAAO,UAGjB,IACJ,CAAC,KACD,MACA,CAAC,EAAO,iBACR,CAAC,GACG,IACJ,CAAC,KACD,CAAC,EAAO,iBACR,EAAO,OAAO,WAAW,GAGrB,IAAQ;MACZ,KAAW,EAAoB;MAC/B,KAAc,EAAoB;MAClC,KAAa,EAAoB;KACnC,CAAC,CAAC,QAAQ,MACR,EAAQ,CACV;KACA,IAAI,EAAO,OAAO,OAAO,aAavB,OAZI,IAAa,EAAa,SAAS,IAKnC,kBAAC,IAAD;MAEE,OAAO,EAAe,GAAQ,CAAe;MAC7C,WAAW;KACZ,GAHM,EAAO,EAGb,IAIH,kBAAC,IAAD;MAEE,OAAO,EAAe,GAAQ,CAAe;MAC7C,WAAW;MAEX,UAAA,kBAAC,IAAD;OACS;OACP,UAAA;OACA,kBAAkB;OAClB,mBAAmB;OACnB,cAAc;OACd,gBAAgB;MACjB,CAAA;KACgB,GAZZ,EAAO,EAYK;KAGvB,IAAM,IACJ,kBAAC,IAAD;MACE,SAAS,EAAO;MAChB,MAAM,EAAO,OAAO,UAAU,MAAM;MACpC,OAAO,EAAe,GAAQ,CAAe;MAC7C,WACG,KAAc,CAAC,KAAwB,KAAA;MAE1C,aACE,KACK,MACC,GAAsB,GAAO,EAAO,OAAO,EAAE,IAC/C,KAAA;MAEN,YACE,KACK,MACC,GAAqB,GAAO,EAAO,OAAO,EAAE,IAC9C,KAAA;MAEN,QACE,KACK,MAAU,GAAiB,GAAO,EAAO,OAAO,EAAE,IACnD,KAAA;MAEN,WACE,UACU,EAAmB,KAAA,CAAS,IAClC,KAAA;MAEN,WAAW,EACT,KAAa,YAMb,KACE,CAAC,KACD,6HACF,KACE,CAAC,KACD,kDACF,KACE,MAAoB,EAAO,OAAO,MAClC,cACF,KAAY,CACd;MA9CF,UAAA,CAgDG,EAAO,gBACJ,OACA,EACE,EAAO,OAAO,UAAU,QACxB,EAAO,WAAW,CACpB,GACH,KACC,kBAAC,OAAD;OACE,MAAK;OACL,oBAAiB;OACjB,cAAY;OACZ,iBAAe,EAAO,OAAO,QAAQ;OACrC,iBACE,EAAO,OAAO,UAAU,WACxB;OAEF,iBACE,EAAO,OAAO,UAAU,WACxB;OAEF,UAAU;OAIV,WAAW;OACX,aAAa,EAAO,iBAAiB;OACrC,cAAc,EAAO,iBAAiB;OACtC,YAAY,MACV,GAAoB,GAAO,CAAM;OAEnC,WAAW,EACT,wcACA,EAAO,OAAO,cAAc,KAC1B,0DACJ;MACD,CAAA,CAEM;;KAKb,OAHI,EAAM,WAAW,IACZ,kBAAC,GAAD,EAAA,UAA2B,EAAqB,GAAjC,EAAO,EAA0B,IAGvD,kBAAC,GAAD;MAEE,UACE,KAAuB,MAAoB,KAAA;MAH/C,UAAA,CAWE,kBAAC,GAAD,EAAgB,QAAQ,EAAa,CAAA,GACrC,kBAAC,GAAD;OAAgB,WAAU;OACvB,UAAA,EAAM,KAAK,MACV,kBAAC,QAAD,EAAA,UAAA;QACE,kBAAC,QAAD;SAAM,WAAU;SAAhB,UAAA,CAAiC,EAAK,OAAM,GAAO;;QAAE;QACpD,EAAK;OACF,EAAA,GAHK,EAAK,KAGV,CACP;MACa,CAAA,CACT;KAnBF,GAAA,EAAO,EAmBL;IAEb,CAAC;GACO,GAjLK,EAAY,EAiLjB,CACX,EACU,CAAA,EACE,CAAA,GACjB,kBAAC,IAAD,EAAA,UAAA;IACG,IACC,MAAM,KAAK,EAAE,QAAQ,GAAa,CAAC,CAAC,CAAC,KAAK,GAAG,MAC3C,kBAAC,GAAD;KAEE,WAAU;KAET,UAAA,EAAM,sBAAsB,CAAC,CAAC,KAAK,MAClC,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD,EAAK,WAAU,+EAAgF,CAAA,EACtF,GAFK,EAAO,EAEZ,CACZ;IACO,GARH,YAAY,GAQT,CACX,IACC,GAAM,SACR,EAAK,KAAK,GAAK,MAAa;KAC1B,IAAI,GACF,OACE,kBAAC,GAAD,EAAA,UAAwB,EAAU,GAAK,CAAQ,EAAY,GAA5C,EAAI,EAAwC;KAG/D,IAAM,IAAa,EAAI,cAAc,GAC/B,IAAY,KAAuB,OAAiB,EAAI,IAYxD,IACJ,KAAc,IACV,+CACA,MAAW,IAAW,KAAM,IAC1B,gDACA,uEAMF,IACJ,CAAC,KAAuB,OAAuB,EAAI;KACrD,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MACE,MAAM,MAAS;OACb,EAAQ,QAAQ,KAAY;MAC9B;MACA,UAAU,MAAa,KAAiB,IAAI;MAC5C,eAAe,GAAe,CAAQ;MACtC,YAAY,MAAU,GAAiB,GAAO,CAAQ;MACtD,UAAU;MACV,SACE,UACU,GAAgB,EAAI,EAAE,IAC5B,KAAA;MAEN,WAAW,EACT,SACA,KAAuB,kBACvB,MACE,IAAW,KAAM,KACjB,CAAC,KACD,CAAC,KACD,+CACF,KACE,CAAC,KACD,8CAIF,KAAuB,sBACzB;MAEC,UAAA,EAAI,gBAAgB,CAAC,CAAC,KAAK,MAAS;OACnC,IAAM,IAAW,EAAK,OAAO,YAAY;OAuCzC,OAtCI,EAAK,OAAO,OAAO,cAEnB,kBAAC,IAAD;QAEE,OAAO,EAAa,GAAM,CAAe;QACzC,WAAW;QACU;QACrB,cACE,IACI,KAAA,UACM,GAAsB,EAAI,EAAE;QAExC,cACE,IACI,KAAA,UAEE,IAAuB,MACrB,MAAO,EAAI,KAAK,KAAA,IAAY,CAC9B;QAGP,UAAA,KACC,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD;SACE,QACE,kBAAC,GAAD,EAAY,cAAY,GAAkB,CAAA;SAG5C,UAAA,kBAAC,IAAD,CAAe,CAAA;QACI,CAAA,GACrB,kBAAC,IAAD;SAAqB,OAAM;SACxB,UAAA,EAAiB,CAAG;QACF,CAAA,CACT,EAAA,CAAA;OAEA,GAhCX,EAAK,EAgCM,IAIpB,kBAAC,GAAD;QAEE,MAAM,EAAK,OAAO,UAAU,MAAM;QAClC,OAAO,EAAa,GAAM,CAAe;QACzC,WAAW,EAAG,KAAY,CAAK;QAE9B,UAAA,EACC,EAAK,OAAO,UAAU,MACtB,EAAK,WAAW,CAClB;OACS,GATJ,EAAK,EASD;MAEf,CAAC;KACO,CAAA,GACT,MAAqB,EAAI,cAAc,KACtC,kBAAC,GAAD;MAAU,WAAU;MAClB,UAAA,kBAAC,GAAD;OACE,WAAU;OACV,SAAS,EAAI,gBAAgB,CAAC,CAAC;OAE9B,UAAA,GAAkB,CAAG;MACb,CAAA;KACH,CAAA,CAEJ,EAAA,GAhGK,EAAI,EAgGT;IAEd,CAAC,IACC,IACF,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KACE,SAAS,EAAM,sBAAsB,CAAC,CAAC;KACvC,WAAU;KAET,UAAA,EAAiB,EAChB,YAAY,EAAM,SAAS,CAAC,CAAC,cAAc,SAAS,EACtD,CAAC;IACQ,CAAA,EACH,CAAA,IAEV,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KACE,SAAS,EAAM,sBAAsB,CAAC,CAAC;KACvC,WAAU;KAET,UAAA;IACQ,CAAA,EACH,CAAA;IAEX,KAAoB,CAAC,KAAY,EAAK,SAAS,KAAK,KACnD,kBAAC,GAAD;KACE,KAAK;KACL,eAAA;KACA,WAAU;KAEV,UAAA,kBAAC,GAAD;MACE,SAAS,EAAM,sBAAsB,CAAC,CAAC;MACvC,WAAU;KACX,CAAA;IACO,CAAA;IAEX,KACC,CAAC,KACD,EAAK,SAAS,KACd,KACE,kBAAC,GAAD;KAAU,WAAU;KAClB,UAAA,kBAAC,GAAD;MAAW,SAAS,EAAM,sBAAsB,CAAC,CAAC;MAChD,UAAA,kBAAC,OAAD;OACE,MAAK;OACL,aAAU;OACV,WAAU;OAEV,UAAA,kBAAC,QAAD;QAAM,WAAU;QAAU,UAAA;OAAwB,CAAA;MAC/C,CAAA;KACI,CAAA;IACH,CAAA;GAEL,EAAA,CAAA,CACN;;CACJ,CAAA;AAET"}
1
+ {"version":3,"file":"data-table.js","names":[],"sources":["../../../../src/components/ui/data-table/data-table.tsx"],"sourcesContent":["import {\n type CSSProperties,\n type DragEvent,\n Fragment,\n type KeyboardEvent,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n type Cell,\n type Column,\n type ColumnDef,\n type ColumnFiltersState,\n type ColumnOrderState,\n type ColumnSizingState,\n type ExpandedState,\n type Header,\n type OnChangeFn,\n type Row,\n type RowData,\n type RowSelectionState,\n type SortingState,\n type Table as TanstackTable,\n type VisibilityState,\n flexRender,\n getCoreRowModel,\n getExpandedRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n getSortedRowModel,\n useReactTable,\n} from '@tanstack/react-table';\n\nimport { EllipsisIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { useIntersectionObserver } from '@/hooks';\nimport { cn } from '@/lib/utils';\nimport { ButtonIcon } from '../button-icon';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuTrigger,\n} from '../dropdown-menu';\nimport {\n Table,\n TableActionsCell,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n TableSettingsCell,\n} from '../table';\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../tooltip';\nimport { TruncateText } from '../truncate-text';\nimport { isBulkSelectionActive } from './data-table-selection';\nimport { DataTableViewOptions } from './data-table-view-options';\n\n// Ported from `@acronis-platform/shadcn-uikit`'s `data-table`\n// (packages/ui-legacy/src/components/ui/data-table/). A TanStack-react-table v8\n// data grid composed over the ui-react Table primitives — sorting, filtering,\n// column visibility, row selection, pagination, and optional row expansion. The\n// presentational flags (`striped`, `bordered`, `skeleton`, `highlightCurrentRow`)\n// are borrowed from the Vue `AvTable`; behavioral features come from TanStack.\n// Pair with DataTableToolbar / DataTablePagination / DataTableColumnHeader, which\n// take the `table` instance returned to column cells via TanStack context.\n// The grid cells/rows/headers are themed by the Table primitives' `--ui-table-*`\n// tier; DataTable's own chrome reuses that tier too — the wrapper border matches\n// the cell borders (`--ui-table-global-row-border-color`), the empty-state uses\n// the muted table-value color, the current row the active-row color, and stripes\n// the secondary surface.\n//\n// Advanced-grid opt-ins built on native TanStack features:\n// • Column resizing -> `enableColumnResizing` + `columnResizing` state, with a\n// drag handle rendered from `header.getResizeHandler()`.\n// • Sticky columns -> `ColumnDef.meta.pin: 'left' | 'right'` drives TanStack's\n// native column-pinning API (`column.pin()` / `getStart()` / `getAfter()`),\n// surfaced as `position: sticky` cells with an opaque row-token background.\n// • Column reorder -> `enableColumnReordering` + `columnOrder` state, with\n// native HTML5 drag-and-drop on the header cells (plain `cursor-grab`/\n// `active:cursor-grabbing` utilities, since there's no dedicated Figma\n// \"Draggable\" token yet, unlike the resize handle's generated\n// `--ui-resizable-cursor`).\n\n// Extend TanStack's per-column `meta` with the flags DataTable reads. Augmenting\n// the module keeps `ColumnDef.meta.pin` type-safe at the call site.\ndeclare module '@tanstack/react-table' {\n // Type params must match TanStack's `ColumnMeta<TData, TValue>` arity/names for\n // declaration merging, even though this augmentation doesn't reference them.\n // eslint-disable-next-line unused-imports/no-unused-vars\n interface ColumnMeta<TData extends RowData, TValue> {\n /** Pin the column to a table edge (sticky while the grid scrolls horizontally). */\n pin?: 'left' | 'right';\n /**\n * Let the column's data cells wrap onto multiple lines (drops the fixed row\n * height). Headers remain single-line and truncate with a tooltip.\n */\n wrap?: boolean;\n /** Label shown for this column in the visibility dropdown. */\n label?: string;\n /** Optional visibility-dropdown category for this column. */\n category?: string;\n }\n}\n\n// Sticky offset + stacking for a pinned column, computed from TanStack's own\n// pinning geometry rather than hand-rolled CSS math. A subtle edge shadow (facing\n// the scrollable columns) separates the pinned column from content sliding\n// underneath it — the opaque background that actually hides that content lives\n// in the caller's className (see `headerPinnedBg`/`rowBg` below), not here.\nexport function getPinnedStyle<TData>(\n column: Column<TData, unknown>\n): CSSProperties | undefined {\n const pinned = column.getIsPinned();\n if (!pinned) return undefined;\n return {\n position: 'sticky',\n left: pinned === 'left' ? column.getStart('left') : undefined,\n right: pinned === 'right' ? column.getAfter('right') : undefined,\n zIndex: 1,\n // Derived from the same border token every other table divider uses (not a\n // hardcoded color), so the separator theme-adapts automatically.\n boxShadow:\n pinned === 'left'\n ? '4px 0 4px -4px var(--ui-table-global-row-border-color)'\n : '-4px 0 4px -4px var(--ui-table-global-row-border-color)',\n };\n}\n\n// A column's rendered width tracks TanStack's size model once the consumer has\n// opted in — either explicitly (a `size` set on the `ColumnDef`) or implicitly\n// (column resizing enabled, which needs every column's width to be deterministic\n// for the drag math to work). Without either, columns stay in native `<table>`\n// auto-layout so `size`'s internal default (TanStack falls back to 150) never\n// forces every untouched column to a fixed width.\nexport function getColumnWidth<TData>(\n column: Column<TData, unknown>,\n enableColumnResizing: boolean\n): number | undefined {\n if (column.id === 'select') {\n // Matches the trailing `__actions` column's fixed 48px: symmetric gutters.\n return 48;\n }\n if (enableColumnResizing || column.columnDef.size !== undefined) {\n return column.getSize();\n }\n return undefined;\n}\n\nfunction getHeaderStyle<TData>(\n header: Header<TData, unknown>,\n enableColumnResizing: boolean\n): CSSProperties | undefined {\n const pin = getPinnedStyle(header.column);\n // Non-leaf cells (group-label spans and placeholders) have no single leaf\n // size — skip width so colSpan layout determines the cell's rendered width.\n const width =\n header.subHeaders.length === 0\n ? getColumnWidth(header.column, enableColumnResizing)\n : undefined;\n if (!pin && width === undefined) return undefined;\n return { ...pin, width };\n}\n\nexport function getCellStyle<TData>(\n cell: Cell<TData, unknown>,\n enableColumnResizing: boolean\n): CSSProperties | undefined {\n const pin = getPinnedStyle(cell.column);\n const width = getColumnWidth(cell.column, enableColumnResizing);\n if (!pin && width === undefined) return undefined;\n return { ...pin, width };\n}\n\n// Matches TanStack's own `defaultColumnSizing` fallback bounds — the same\n// range `column.getSize()` already clamps to internally when a column\n// doesn't set its own `minSize`/`maxSize`.\nconst DEFAULT_MIN_COLUMN_SIZE = 20;\nconst DEFAULT_MAX_COLUMN_SIZE = Number.MAX_SAFE_INTEGER;\n\n/**\n * Computes the next column width for an Arrow-key resize step (Shift = larger\n * step), clamped to `[min, max]` regardless of which bound `currentSize`\n * started outside of. Returns `undefined` for any other key, so the caller\n * knows not to `preventDefault()`/resize.\n */\nexport function getResizeKeyboardStep(\n key: string,\n currentSize: number,\n { shiftKey, min, max }: { shiftKey: boolean; min: number; max: number }\n): number | undefined {\n const step = shiftKey ? 50 : 10;\n if (key === 'ArrowLeft')\n return Math.min(max, Math.max(min, currentSize - step));\n if (key === 'ArrowRight')\n return Math.max(min, Math.min(max, currentSize + step));\n return undefined;\n}\n\n/**\n * Moves `from` to `to`'s position in a `columnOrder` array (the drop semantics\n * of the header drag gesture: the dragged column lands where the drop target\n * sits, pushing it aside). Returns `order` unchanged when either id is absent,\n * so a drop on a column that isn't part of the order (e.g. one added while the\n * gesture was in flight) is a no-op rather than a reshuffle.\n */\nexport function reorderColumn(\n order: string[],\n from: string,\n to: string\n): string[] {\n const fromIndex = order.indexOf(from);\n const toIndex = order.indexOf(to);\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return order;\n const next = [...order];\n next.splice(toIndex, 0, next.splice(fromIndex, 1)[0]);\n return next;\n}\n\n/** One line of the header hover tooltip: a capability and the gesture for it. */\nexport interface DataTableHeaderHint {\n /** The capability, rendered in bold (e.g. `Sort column`). */\n label: string;\n /** The gesture that triggers it (e.g. `Click`). */\n action: string;\n}\n\n/** Copy of the header hover tooltip, one entry per column capability. */\nexport interface DataTableHeaderHints {\n sort: DataTableHeaderHint;\n reorder: DataTableHeaderHint;\n resize: DataTableHeaderHint;\n}\n\nconst DEFAULT_HEADER_HINTS: DataTableHeaderHints = {\n sort: { label: 'Sort column', action: 'Click' },\n reorder: { label: 'Reorder column', action: 'Drag' },\n resize: { label: 'Resize column', action: 'Drag border' },\n};\n\n// `columns`/`data` build DataTable's own table instance; `table` renders an\n// externally-built one instead. At least one of the two forms is required —\n// omitting both would otherwise silently render an empty table — but `table`\n// may still be passed alongside `columns`/`data` (e.g. to also drive a\n// composed toolbar/pagination from the same instance DataTable renders).\ntype DataTableDataSourceProps<TData, TValue> =\n | {\n columns: ColumnDef<TData, TValue>[];\n data: TData[];\n /**\n * Also drive an externally-built TanStack `table` instance (e.g. a\n * composed toolbar/pagination) from the same state as this DataTable.\n */\n table?: TanstackTable<TData>;\n }\n | {\n columns?: ColumnDef<TData, TValue>[];\n data?: TData[];\n /**\n * Render from an externally-built TanStack `table` instance instead of\n * DataTable's own — DataTable then owns no state and renders the caller's\n * instance as-is (sorting, filtering, pagination, row models, etc. are all\n * configured on that instance). Makes `columns`/`data` unnecessary (they're\n * only used to build DataTable's own instance) and the following props\n * no-ops (configure the equivalent directly on the external instance\n * instead): `getRowId`, `columnVisibility`, `onColumnVisibilityChange`,\n * `onColumnSizingChange`, `enableColumnResizing`,\n * `enableColumnReordering`, `columnOrder`, `onColumnOrderChange`,\n * `getRowCanExpand`, `rowSelection`, `onRowSelectionChange`,\n * `manualSorting`, `sorting`, `onSortingChange`, `paginationMode`,\n * `onLoadMore`, `loadMoreRootMargin`, `hasNextPage`, `isLoadingMore`.\n * `meta.pin`-driven column pinning is also skipped — pin/unpin the\n * caller's own instance via TanStack's `column.pin()` directly.\n */\n table: TanstackTable<TData>;\n };\n\ninterface DataTableOwnProps<TData> {\n /**\n * Derives a stable row id from the row data (e.g. `row => row.id`) instead of the default\n * array index — needed whenever `data` can reorder (sort/filter) while a row's own per-row\n * state (selection, a hook keyed by row) should follow the row, not the slot it occupied.\n */\n getRowId?: (originalRow: TData, index: number, parent?: Row<TData>) => string;\n /** Enables row expansion for rows that return true. Pair with `renderExpandedRow`. */\n getRowCanExpand?: (row: Row<TData>) => boolean;\n /**\n * Renders expanded content for an expanded row. Used together with\n * `getRowCanExpand`.\n */\n renderExpandedRow?: (row: Row<TData>) => ReactNode;\n /** Alternating row backgrounds. */\n striped?: boolean;\n /** Vertical borders between columns (rows already have horizontal borders). */\n bordered?: boolean;\n /** Highlight the row the user last clicked (the \"current\" row). */\n highlightCurrentRow?: boolean;\n /** Render placeholder skeleton rows instead of data (loading state). */\n skeleton?: boolean;\n /** Number of skeleton rows to render when `skeleton` is set. */\n skeletonRows?: number;\n /**\n * Opt in to interactive column resizing. Renders a drag handle at the trailing\n * edge of each resizable header cell (TanStack's native `columnResizing`).\n */\n enableColumnResizing?: boolean;\n /** Passthrough for the `columnSizing` state so a consumer can persist widths. */\n onColumnSizingChange?: OnChangeFn<ColumnSizingState>;\n /**\n * Opt in to column reordering by dragging a header cell onto another one\n * (native HTML5 drag-and-drop driving TanStack's `columnOrder`). Pinned\n * columns are excluded — they're anchored to a table edge by definition.\n * Pointer-only, matching the design: there is no keyboard equivalent yet.\n */\n enableColumnReordering?: boolean;\n /**\n * Controlled column-order state — pass this (with `onColumnOrderChange`) to\n * persist or share the order. Uncontrolled (internal state) when omitted.\n */\n columnOrder?: ColumnOrderState;\n /** Passthrough for the `columnOrder` state; pairs with `columnOrder`. */\n onColumnOrderChange?: OnChangeFn<ColumnOrderState>;\n /**\n * Controlled column-visibility state — pass this (with\n * `onColumnVisibilityChange`) to share one visibility state with an\n * external `useReactTable` instance (e.g. a composed toolbar). Uncontrolled\n * (internal state) when omitted.\n */\n columnVisibility?: VisibilityState;\n /** Passthrough for the `columnVisibility` state; pairs with `columnVisibility`. */\n onColumnVisibilityChange?: OnChangeFn<VisibilityState>;\n /**\n * Controlled row-selection state — pass this (with `onRowSelectionChange`)\n * to share one selection state with an external `useReactTable` instance\n * (e.g. a composed toolbar/bulk-actions bar). Uncontrolled (internal state)\n * when omitted.\n */\n rowSelection?: RowSelectionState;\n /** Passthrough for the `rowSelection` state; pairs with `rowSelection`. */\n onRowSelectionChange?: OnChangeFn<RowSelectionState>;\n /**\n * Opt out of client-side sorting — pass already-sorted `data` and drive\n * sorting via `sorting`/`onSortingChange` (e.g. mapped to a server query by\n * the caller). Mapping sort state to a query and refetching stays the\n * caller's job; DataTable only skips its own comparator.\n */\n manualSorting?: boolean;\n /**\n * Controlled sorting state — pass this (with `onSortingChange`) to drive\n * sorting externally. Uncontrolled (internal state) when omitted.\n */\n sorting?: SortingState;\n /** Passthrough for the `sorting` state; pairs with `sorting`. */\n onSortingChange?: OnChangeFn<SortingState>;\n /**\n * Renders a full row, bypassing DataTable's default per-cell `flexRender`\n * path entirely (no `<TableRow>`/cell-styling/pinning of DataTable's own).\n * Use to swap in a custom, independently memoizable row component. The\n * caller owns the row's markup and equality semantics — reuse the exported\n * `getCellStyle`/`getPinnedStyle`/`getColumnWidth` helpers to match\n * DataTable's default cell styling if desired.\n *\n * Also bypasses DataTable's `renderExpandedRow` handling — a row rendered\n * via `renderRow` never gets an expanded-content row appended, even when\n * `getRowCanExpand` returns true for it. Read `row.getIsExpanded()` and\n * render the expanded content yourself if you need both.\n */\n renderRow?: (row: Row<TData>, rowIndex: number) => ReactNode;\n /**\n * Renders a custom empty state instead of the default \"No results.\" row.\n * Receives `hasFilters` (whether any column filter is currently applied) so\n * the caller can distinguish \"no data at all\" from \"no matches\" — the\n * actual copy/wording/localization stays the caller's job.\n */\n renderEmptyState?: (context: { hasFilters: boolean }) => ReactNode;\n /**\n * `'page'` (default) keeps today's client-paginated behavior. `'infinite'`\n * omits the paginated row model — `data` is assumed to be the full\n * accumulated array the caller appends to on each `onLoadMore` — and\n * renders a sentinel row that calls `onLoadMore` once it scrolls into view.\n * Does not compose with virtualization; for a large accumulated list, use\n * the `VirtualScrolling` recipe over the raw `Table` primitives instead.\n */\n paginationMode?: 'page' | 'infinite';\n /**\n * Called when the infinite-scroll sentinel intersects the viewport.\n * `paginationMode=\"infinite\"` only. The fetch-more call, cursor/offset\n * tracking, dedup, and accumulating `data` stay the caller's job. Requires\n * at least one row already rendered — an empty table with `data={[]}`\n * cannot use the sentinel to drive its very first fetch; seed the first\n * page yourself (e.g. on mount) and use `onLoadMore` for subsequent pages.\n */\n onLoadMore?: () => void;\n /**\n * Expands the sentinel's `IntersectionObserver` root margin (native CSS\n * margin syntax, e.g. `'400px'`) so `onLoadMore` fires before the sentinel\n * is literally visible — the closer the caller's fetch is to finishing by\n * the time the user actually scrolls there, the less often the trailing\n * loading row is seen. `paginationMode=\"infinite\"` only; no-op when `table`\n * is passed. How far this actually prefetches also depends on page size —\n * a large margin with small pages can trigger several `onLoadMore` calls\n * back-to-back as the user scrolls normally, which is expected.\n */\n loadMoreRootMargin?: string;\n /** Whether more rows are available to load. `paginationMode=\"infinite\"` only. */\n hasNextPage?: boolean;\n /**\n * Accessible name of a column's resize handle. Override to localize.\n * `enableColumnResizing` only.\n */\n resizeColumnLabel?: string;\n /**\n * Text of the default empty-state row. Ignored when `renderEmptyState` is\n * given. Override to localize.\n */\n emptyLabel?: string;\n /**\n * Whether a load is in flight — suppresses further `onLoadMore` calls and\n * renders a trailing loading row. `paginationMode=\"infinite\"` only.\n */\n isLoadingMore?: boolean;\n /**\n * Hide the trailing sticky action column — the column-visibility cog in the\n * header and each row's overflow-actions ellipsis. Shown by default. A\n * no-op when an external `table` is passed (build the column into that\n * instance's own `columns` instead).\n */\n hideActionColumn?: boolean;\n /**\n * Renders the content of a row's overflow-actions menu (e.g. `Edit`/\n * `Delete` items), opened from the ellipsis trigger in the trailing action\n * column. Omit to render that row without a trigger — the 48px column is\n * still reserved. Suppressed (like the trigger itself) while a bulk\n * selection is active, per `TableActionsCell`.\n */\n renderRowActions?: (row: Row<TData>) => ReactNode;\n /** Accessible name of a row's overflow-actions trigger. Override to localize. */\n rowActionsLabel?: string;\n /**\n * Accessible name of the action column's column-visibility trigger.\n * Override to localize.\n */\n columnSettingsLabel?: string;\n /** Placeholder and accessible name of the column-visibility search field. */\n columnSearchPlaceholder?: string;\n /** Label of the per-category action that reveals every column. */\n showAllColumnsLabel?: ReactNode;\n /** Empty state shown when the column search has no matches. */\n noColumnsFoundLabel?: ReactNode;\n /**\n * Copy of the tooltip shown while a header cell is hovered/focused — one\n * line per capability that column actually has (sort/reorder/resize).\n * Override (per capability) to localize; a column with no capability shows\n * no tooltip at all.\n */\n headerHints?: Partial<DataTableHeaderHints>;\n}\n\nexport type DataTableProps<TData, TValue = unknown> = DataTableOwnProps<TData> &\n DataTableDataSourceProps<TData, TValue>;\n\nexport function DataTable<TData, TValue = unknown>({\n columns = [],\n data = [],\n table: externalTable,\n getRowId,\n getRowCanExpand,\n renderExpandedRow,\n striped = false,\n bordered = false,\n highlightCurrentRow = false,\n skeleton = false,\n skeletonRows = 5,\n enableColumnResizing = false,\n onColumnSizingChange,\n enableColumnReordering = false,\n columnOrder: controlledColumnOrder,\n onColumnOrderChange,\n columnVisibility: controlledColumnVisibility,\n onColumnVisibilityChange,\n rowSelection: controlledRowSelection,\n onRowSelectionChange,\n manualSorting = false,\n sorting: controlledSorting,\n onSortingChange,\n renderRow,\n renderEmptyState,\n paginationMode = 'page',\n onLoadMore,\n loadMoreRootMargin,\n hasNextPage = false,\n resizeColumnLabel = 'Resize column',\n emptyLabel = 'No results.',\n isLoadingMore = false,\n hideActionColumn = false,\n renderRowActions,\n rowActionsLabel = 'Row actions',\n columnSettingsLabel,\n columnSearchPlaceholder,\n showAllColumnsLabel,\n noColumnsFoundLabel,\n headerHints,\n}: DataTableProps<TData, TValue>) {\n const resolvedHeaderHints: DataTableHeaderHints = {\n ...DEFAULT_HEADER_HINTS,\n ...headerHints,\n };\n const [internalSorting, setInternalSorting] = useState<SortingState>([]);\n const sorting = controlledSorting ?? internalSorting;\n const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);\n const [internalColumnVisibility, setInternalColumnVisibility] =\n useState<VisibilityState>({});\n const columnVisibility =\n controlledColumnVisibility ?? internalColumnVisibility;\n const [internalRowSelection, setInternalRowSelection] =\n useState<RowSelectionState>({});\n const rowSelection = controlledRowSelection ?? internalRowSelection;\n const [expanded, setExpanded] = useState<ExpandedState>({});\n const [columnSizing, setColumnSizing] = useState<ColumnSizingState>({});\n const [internalColumnOrder, setInternalColumnOrder] =\n useState<ColumnOrderState>([]);\n const columnOrder = controlledColumnOrder ?? internalColumnOrder;\n // The header cell the user is currently dragging, so the source cell can dim\n // while the gesture is in flight.\n const [draggedColumnId, setDraggedColumnId] = useState<string>();\n const [currentRowId, setCurrentRowId] = useState<string>();\n // Roving tabindex over the data rows: exactly one row is a Tab stop at a\n // time (the rest are `tabIndex={-1}`, still focusable programmatically),\n // and Arrow Up/Down move both the DOM focus and which row that is. Clamped\n // against the live row count below rather than reset via an effect, so a\n // filter/page change that shrinks `rows` can't leave it pointing past the\n // end.\n const [focusedRowIndex, setFocusedRowIndex] = useState(0);\n const rowRefs = useRef<Array<HTMLTableRowElement | null>>([]);\n\n const handleColumnSizingChange: OnChangeFn<ColumnSizingState> = (updater) => {\n setColumnSizing(updater);\n onColumnSizingChange?.(updater);\n };\n\n const handleColumnOrderChange: OnChangeFn<ColumnOrderState> = (updater) => {\n if (controlledColumnOrder === undefined) {\n setInternalColumnOrder(updater);\n }\n onColumnOrderChange?.(updater);\n };\n\n const handleColumnVisibilityChange: OnChangeFn<VisibilityState> = (\n updater\n ) => {\n setInternalColumnVisibility(updater);\n onColumnVisibilityChange?.(updater);\n };\n\n const handleSortingChange: OnChangeFn<SortingState> = (updater) => {\n if (controlledSorting === undefined) {\n setInternalSorting(updater);\n }\n onSortingChange?.(updater);\n };\n\n const handleRowSelectionChange: OnChangeFn<RowSelectionState> = (updater) => {\n if (controlledRowSelection === undefined) {\n setInternalRowSelection(updater);\n }\n onRowSelectionChange?.(updater);\n };\n\n // The trailing sticky action column (cog + per-row ellipsis) is a no-op\n // with an external `table` — that instance's own `columns` already decide\n // what renders, DataTable doesn't own its state in that mode (see the\n // `table` prop's tsdoc).\n const showActionColumn = !hideActionColumn && !externalTable;\n // A real `ColumnDef` (rather than chrome bolted onto the render loop) so\n // TanStack's own pinning geometry measures and offsets it like any other\n // pinned column — `header`/`cell` are never read (see the header/body\n // render loops below, which special-case this id), only `size`/`meta.pin`.\n const actionColumns = useMemo<ColumnDef<TData, TValue>[]>(\n () =>\n showActionColumn\n ? [\n {\n id: '__actions',\n size: 48,\n enableSorting: false,\n enableHiding: false,\n meta: { pin: 'right' },\n header: () => null,\n cell: () => null,\n },\n ]\n : [],\n [showActionColumn]\n );\n const tableColumns = useMemo(\n () => [...columns, ...actionColumns],\n [columns, actionColumns]\n );\n // The pinned action cell's own hover (its ellipsis/cog trigger) should be\n // the only thing that tints while it's hovered — not the row underneath it\n // (see `hover:bg-transparent` below). Tracked by id (rather than a boolean)\n // because it's set from the action cell of whichever row is currently under\n // the pointer.\n const [actionHoveredRowId, setActionHoveredRowId] = useState<string>();\n\n // Built unconditionally — hooks can't be conditionally called — but only\n // feeds the render path below when no external `table` is passed (see\n // `table` below).\n const internalTable = useReactTable({\n data,\n columns: tableColumns,\n getRowId,\n enableColumnResizing,\n columnResizeMode: 'onChange',\n getCoreRowModel: getCoreRowModel(),\n getExpandedRowModel: getExpandedRowModel(),\n ...(paginationMode === 'page'\n ? { getPaginationRowModel: getPaginationRowModel() }\n : {}),\n getRowCanExpand,\n onExpandedChange: setExpanded,\n manualSorting,\n onSortingChange: handleSortingChange,\n ...(manualSorting ? {} : { getSortedRowModel: getSortedRowModel() }),\n onColumnFiltersChange: setColumnFilters,\n getFilteredRowModel: getFilteredRowModel(),\n onColumnVisibilityChange: handleColumnVisibilityChange,\n onRowSelectionChange: handleRowSelectionChange,\n onColumnSizingChange: handleColumnSizingChange,\n onColumnOrderChange: handleColumnOrderChange,\n initialState: showActionColumn\n ? { columnPinning: { right: ['__actions'] } }\n : undefined,\n state: {\n sorting,\n columnFilters,\n columnVisibility,\n rowSelection,\n expanded,\n columnSizing,\n columnOrder,\n },\n });\n\n // The caller's instance is the single source of truth when passed — it\n // configures its own row models/state, so DataTable just renders from it.\n const table = externalTable ?? internalTable;\n const isInfiniteScroll = !externalTable && paginationMode === 'infinite';\n // `enableColumnResizing` is documented as a no-op with an external `table`\n // (the caller owns that instance's ColumnSizing state), but ColumnSizing is\n // a built-in TanStack feature present on any instance — reading the raw\n // prop below would still render live resize handles that mutate the\n // caller's own instance. Gate it the same way as `isInfiniteScroll`.\n const resizingEnabled = enableColumnResizing && !externalTable;\n // Same reasoning as `resizingEnabled`: ColumnOrdering is a built-in TanStack\n // feature present on any instance, so reading the raw prop would let the\n // header drag mutate a caller-owned instance DataTable doesn't manage.\n const reorderingEnabled = enableColumnReordering && !externalTable;\n // A resize drag necessarily moves the pointer off the (few-px-wide) resize\n // handle and onto the header cell itself. If that cell is still\n // `draggable`, the browser arms a native HTML5 reorder drag mid-resize —\n // native drag then swallows the mousemove/mouseup TanStack's resize\n // handler relies on, so the cursor flips to the reorder \"grab\" icon and\n // releasing the mouse doesn't cleanly end the resize. Reading this back\n // from TanStack's own resize state (rather than pointer position) keeps\n // every header cell non-draggable for the *entire* gesture, regardless of\n // where the pointer ends up.\n const isAnyColumnResizing = Boolean(\n table.getState().columnSizingInfo.isResizingColumn\n );\n // The resize handle only shows its `ew-resize` cursor while the pointer is\n // directly over its thin 4px hit area — once a drag starts, fast pointer\n // movement leaves that area and the browser shows whatever cursor the\n // element underneath declares (e.g. a sortable header's `cursor-pointer`).\n // This attribute + the `html[data-ui-column-resizing] *` rule in\n // styles/index.css force `ew-resize` everywhere for the duration of the\n // drag — a plain `<body>` inline cursor loses to a descendant's own\n // `cursor` declaration, so it isn't enough on its own.\n useEffect(() => {\n if (!isAnyColumnResizing) return;\n document.documentElement.setAttribute('data-ui-column-resizing', '');\n return () => {\n document.documentElement.removeAttribute('data-ui-column-resizing');\n };\n }, [isAnyColumnResizing]);\n const sentinelRef = useIntersectionObserver<HTMLTableRowElement>({\n onIntersect: () => onLoadMore?.(),\n disabled: !isInfiniteScroll || !hasNextPage || isLoadingMore,\n rootMargin: loadMoreRootMargin,\n });\n\n // Arrow-key resize on the drag handle (see `canResize` below). Ignores any\n // modifier besides Shift so it doesn't hijack browser/OS shortcuts bound to\n // Ctrl/Alt/Cmd+Arrow (e.g. back navigation) while the handle has focus.\n const handleResizeKeyDown = (\n event: KeyboardEvent<HTMLDivElement>,\n header: Header<TData, unknown>\n ) => {\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n const nextSize = getResizeKeyboardStep(event.key, header.column.getSize(), {\n shiftKey: event.shiftKey,\n min: header.column.columnDef.minSize ?? DEFAULT_MIN_COLUMN_SIZE,\n max: header.column.columnDef.maxSize ?? DEFAULT_MAX_COLUMN_SIZE,\n });\n if (nextSize === undefined) return;\n event.preventDefault();\n table.setColumnSizing((old) => ({ ...old, [header.column.id]: nextSize }));\n };\n\n // Native HTML5 drag-and-drop on the header cells. `columnOrder` starts empty\n // (TanStack reads that as \"the declared order\"), so the first drop seeds it\n // from the live leaf columns before moving anything.\n const handleColumnDragStart = (\n event: DragEvent<HTMLTableCellElement>,\n columnId: string\n ) => {\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n // Firefox requires setData() during dragstart to actually initiate a\n // native drag; the value itself is unused (drop reads draggedColumnId).\n event.dataTransfer.setData('text/plain', columnId);\n }\n setDraggedColumnId(columnId);\n };\n\n const handleColumnDragOver = (\n event: DragEvent<HTMLTableCellElement>,\n targetColumnId: string\n ) => {\n // Without this the browser rejects the drop and no `onDrop` ever fires.\n event.preventDefault();\n if (!event.dataTransfer) return;\n // Show \"no drop\" cursor when the source and target belong to different\n // header groups — cross-group reordering is not allowed (it interleaves\n // leaf columns from different groups in columnOrder, which breaks\n // TanStack's header-group rendering and causes label duplication).\n const draggedParentId = draggedColumnId\n ? table.getColumn(draggedColumnId)?.parent?.id\n : undefined;\n const targetParentId = table.getColumn(targetColumnId)?.parent?.id;\n event.dataTransfer.dropEffect =\n draggedParentId === targetParentId ? 'move' : 'none';\n };\n\n const handleColumnDrop = (\n event: DragEvent<HTMLTableCellElement>,\n targetColumnId: string\n ) => {\n event.preventDefault();\n if (!draggedColumnId) return;\n // Reject drops that would move a column outside its header group. Allowing\n // cross-group drops interleaves the two groups' leaf columns in columnOrder,\n // which causes TanStack to produce malformed header rows: the group label\n // renders at the wrong position (bug 2) and sometimes duplicates (bug 3).\n //\n // Known limitation: this guard only covers the drag gesture. The same broken\n // state is reachable via the `columnOrder` controlled prop — if the caller\n // passes a `columnOrder` where a group's leaf columns are not contiguous,\n // TanStack will produce duplicate group-label cells with no warning.\n const draggedParentId = table.getColumn(draggedColumnId)?.parent?.id;\n const targetParentId = table.getColumn(targetColumnId)?.parent?.id;\n if (draggedParentId !== targetParentId) {\n setDraggedColumnId(undefined);\n return;\n }\n const current = table.getState().columnOrder;\n const base = current.length\n ? current\n : table.getAllLeafColumns().map((column) => column.id);\n table.setColumnOrder(reorderColumn(base, draggedColumnId, targetColumnId));\n setDraggedColumnId(undefined);\n };\n\n // Read each column's `meta.pin` and drive TanStack's native pinning state.\n // Always calls `pin()` (rather than only when truthy) so a column whose\n // `meta.pin` is removed dynamically actually un-pins. Skipped for an\n // external `table` — DataTable owns no state in that mode (see the `table`\n // prop's tsdoc), so the caller's own pinning setup is left alone.\n //\n // Known limitation: pinning a leaf column that is nested inside a header\n // group breaks group contiguity. TanStack moves pinned leaves to a separate\n // bucket and reorders columns as [...left, ...center, ...right] before\n // createHeaderGroup runs, separating the pinned leaf from its siblings.\n // The result is a duplicate group-label cell in the header row — the same\n // rendering defect that the drag guard exists to prevent. Do not set\n // `meta.pin` on a leaf column that belongs to a header group.\n useEffect(() => {\n if (externalTable) return;\n table.getAllLeafColumns().forEach((column) => {\n column.pin(column.columnDef.meta?.pin ?? false);\n });\n }, [table, tableColumns, externalTable]);\n\n const rows = table.getRowModel().rows;\n // Derived from the selection state, so it's the same for every row — compute\n // it once instead of per row inside the render loop below.\n const bulkSelectionActive = isBulkSelectionActive(table);\n const activeRowIndex = rows.length\n ? Math.min(focusedRowIndex, rows.length - 1)\n : 0;\n\n // Mouse/pointer clicks still focus a `tabIndex={-1}` row (browsers allow\n // programmatic/click focus regardless of tabIndex value) — syncing the\n // roving index on focus makes that row the next Tab stop too, matching how\n // users expect the last-interacted row to keep focus.\n const handleRowFocus = (rowIndex: number) => {\n setFocusedRowIndex(rowIndex);\n };\n\n const handleRowKeyDown = (\n event: KeyboardEvent<HTMLTableRowElement>,\n rowIndex: number\n ) => {\n // Keydown bubbles, so arrow keys from an interactive control inside a cell\n // (number spinner, textarea caret, native select) would otherwise be\n // hijacked to move row focus. Only roam when the row itself is focused.\n if (event.target !== event.currentTarget) return;\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n const nextIndex =\n event.key === 'ArrowDown'\n ? Math.min(rowIndex + 1, rows.length - 1)\n : Math.max(rowIndex - 1, 0);\n if (nextIndex === rowIndex) return;\n event.preventDefault();\n setFocusedRowIndex(nextIndex);\n rowRefs.current[nextIndex]?.focus();\n };\n // Vertical borders are opt-in; a trailing border on the last cell would\n // double up with the wrapper, so suppress it.\n const borderedClass = bordered\n ? '[&_th:not(:last-child)]:border-e [&_td:not(:last-child)]:border-e [&_th]:border-[var(--ui-table-global-row-border-color)] [&_td]:border-[var(--ui-table-global-row-border-color)]'\n : undefined;\n\n // A pinned header/body cell must be opaque so the cells scrolling under it\n // (same row) aren't visible. `--ui-table-data-row-color-idle` is\n // *transparent by design* (an idle row shows the page/card surface through\n // it), so it can't be reused here — pinned idle cells need the actual\n // resolved surface color instead (`--ui-background-surface-primary`, bridged\n // to `bg-background`). Non-idle states (selected/current/striped) already use\n // real opaque tokens and are safe to mirror as-is (see `rowBg` below).\n const headerPinnedBg = 'bg-background';\n\n return (\n <div\n className={cn(\n 'rounded-md border border-[var(--ui-table-global-row-border-color)]',\n borderedClass\n )}\n >\n <Table\n style={\n resizingEnabled ? { width: table.getCenterTotalSize() } : undefined\n }\n >\n {/* One provider for the whole header row so the capability hints share\n a single open/close delay group (Provider renders no DOM, so the\n table markup is unaffected). */}\n <TooltipProvider>\n <TableHeader>\n {table.getHeaderGroups().map((headerGroup, groupIndex, headerGroups) => (\n <TableRow key={headerGroup.id} className=\"hover:bg-transparent\">\n {headerGroup.headers.map((header) => {\n const isPinned = header.column.getIsPinned();\n // Non-leaf header cells (group-label spans and TanStack's\n // structural placeholder cells) are purely presentational —\n // no sort/reorder/resize/tooltip.\n const isGroupHeader = header.subHeaders.length > 0;\n const canResize =\n !isGroupHeader &&\n resizingEnabled &&\n header.column.getCanResize() &&\n header.column.id !== 'select';\n // A pinned column is anchored to a table edge, so dragging it\n // out of that edge would contradict its own pinning.\n const canReorder =\n !isGroupHeader &&\n reorderingEnabled &&\n !header.isPlaceholder &&\n !isPinned;\n const canSort =\n !isGroupHeader &&\n !header.isPlaceholder &&\n header.column.getCanSort();\n // One tooltip line per capability the column actually has, in\n // the design's order; a column with none gets no tooltip.\n const hints = [\n canSort && resolvedHeaderHints.sort,\n canReorder && resolvedHeaderHints.reorder,\n canResize && resolvedHeaderHints.resize,\n ].filter((hint): hint is DataTableHeaderHint =>\n Boolean(hint)\n );\n if (header.column.id === '__actions') {\n if (groupIndex < headerGroups.length - 1) {\n // Group-header rows carry no cog, but the pinned cell\n // must still be present so the row scrolls horizontally\n // in sync with the leaf header row and the body.\n return (\n <TableSettingsCell\n key={header.id}\n style={getHeaderStyle(header, resizingEnabled)}\n className={headerPinnedBg}\n />\n );\n }\n return (\n <TableSettingsCell\n key={header.id}\n style={getHeaderStyle(header, resizingEnabled)}\n className={headerPinnedBg}\n >\n <DataTableViewOptions\n table={table}\n iconOnly\n triggerAriaLabel={columnSettingsLabel}\n searchPlaceholder={columnSearchPlaceholder}\n showAllLabel={showAllColumnsLabel}\n noResultsLabel={noColumnsFoundLabel}\n />\n </TableSettingsCell>\n );\n }\n const headerContent = header.isPlaceholder\n ? null\n : flexRender(\n header.column.columnDef.header,\n header.getContext()\n );\n const headerCell = (\n <TableHead\n colSpan={header.colSpan}\n style={getHeaderStyle(header, resizingEnabled)}\n draggable={\n (canReorder && !isAnyColumnResizing) || undefined\n }\n onDragStart={\n canReorder\n ? (event) =>\n handleColumnDragStart(event, header.column.id)\n : undefined\n }\n onDragOver={\n canReorder\n ? (event) =>\n handleColumnDragOver(event, header.column.id)\n : undefined\n }\n onDrop={\n canReorder\n ? (event) => handleColumnDrop(event, header.column.id)\n : undefined\n }\n onDragEnd={\n canReorder\n ? () => setDraggedColumnId(undefined)\n : undefined\n }\n className={cn(\n canResize && 'relative',\n // Per the design, a sortable header tints the whole\n // cell on hover/press, not just the inner sort button.\n // Suppressed while any column is resizing, since the\n // pointer drags across neighboring `<th>`s and would\n // otherwise tint them via native `:hover`.\n canSort &&\n !isAnyColumnResizing &&\n 'transition-colors hover:bg-[var(--ui-table-header-cell-color-hover)] active:bg-[var(--ui-table-header-cell-color-active)]',\n canReorder &&\n !isAnyColumnResizing &&\n 'cursor-grab select-none active:cursor-grabbing',\n canReorder &&\n draggedColumnId === header.column.id &&\n 'opacity-50',\n isPinned && headerPinnedBg\n )}\n >\n {typeof headerContent === 'string' &&\n header.column.columnDef.meta?.wrap ? (\n <TruncateText>{headerContent}</TruncateText>\n ) : (\n headerContent\n )}\n {canResize && (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={resizeColumnLabel}\n aria-valuenow={header.column.getSize()}\n aria-valuemin={\n header.column.columnDef.minSize ??\n DEFAULT_MIN_COLUMN_SIZE\n }\n aria-valuemax={\n header.column.columnDef.maxSize ??\n DEFAULT_MAX_COLUMN_SIZE\n }\n tabIndex={0}\n // Keeps a press on the handle from starting the header\n // cell's reorder drag instead of a resize when both\n // features are enabled.\n draggable={false}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onKeyDown={(event) =>\n handleResizeKeyDown(event, header)\n }\n className={cn(\n 'absolute end-0 top-0 h-full w-1 cursor-(--ui-resizable-cursor) touch-none select-none bg-[var(--ui-table-global-row-border-color)] opacity-0 transition-[opacity,background-color] hover:bg-[var(--ui-resizable-border-color-hover)] hover:opacity-100 focus-visible:bg-[var(--ui-resizable-border-color-hover)] focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-[var(--ui-focus-primary)]',\n header.column.getIsResizing() &&\n 'bg-[var(--ui-resizable-border-color-active)] opacity-100'\n )}\n />\n )}\n </TableHead>\n );\n if (hints.length === 0) {\n return <Fragment key={header.id}>{headerCell}</Fragment>;\n }\n return (\n <Tooltip\n key={header.id}\n disabled={\n isAnyColumnResizing || draggedColumnId !== undefined\n }\n >\n {/* The whole header cell is the trigger (not just its sort\n button), so the hint covers the reorder/resize gestures\n that live on the cell itself. Disabled mid-drag/resize\n so the hint doesn't pop up over a neighboring cell\n while the pointer passes through it. */}\n <TooltipTrigger render={headerCell} />\n <TooltipContent className=\"flex flex-col gap-1\">\n {hints.map((hint) => (\n <span key={hint.label}>\n <span className=\"font-semibold\">{hint.label}:</span>{' '}\n {hint.action}\n </span>\n ))}\n </TooltipContent>\n </Tooltip>\n );\n })}\n </TableRow>\n ))}\n </TableHeader>\n </TooltipProvider>\n <TableBody>\n {skeleton ? (\n Array.from({ length: skeletonRows }).map((_, rowIndex) => (\n <TableRow\n key={`skeleton-${rowIndex}`}\n className=\"hover:bg-transparent\"\n >\n {table.getVisibleLeafColumns().map((column) => (\n <TableCell key={column.id}>\n <div className=\"h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]\" />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : rows?.length ? (\n rows.map((row, rowIndex) => {\n if (renderRow) {\n return (\n <Fragment key={row.id}>{renderRow(row, rowIndex)}</Fragment>\n );\n }\n const isSelected = row.getIsSelected();\n const isCurrent = highlightCurrentRow && currentRowId === row.id;\n // Opaque background applied to pinned cells so sibling cells don't\n // show through while the grid scrolls horizontally. Mirrors the\n // row's own resolved background across selection/current/stripe —\n // the idle case falls back to the real surface color\n // (`bg-background`) since the row's own idle token is transparent\n // by design (see `headerPinnedBg` above).\n // The idle case mirrors the row's own hover tint via `group` (see\n // the row's `group` class below) so the pinned action column\n // doesn't look \"stuck\" idle while the rest of the row is\n // hovered — the row's native `hover:` can't reach it since it\n // needs its own opaque background (see `headerPinnedBg` above).\n const rowBg =\n isSelected || isCurrent\n ? 'bg-[var(--ui-table-data-row-color-active)]'\n : striped && rowIndex % 2 === 1\n ? 'bg-[var(--ui-background-surface-secondary)]'\n : 'bg-background group-hover:bg-[var(--ui-table-data-row-color-hover)]';\n // While a selection is in play the action cell renders nothing and\n // carries no tint of its own, so suppressing the row's hover tint\n // for it would leave the pointer over a row that reacts to\n // nothing. Also covers a row hovered just before the selection\n // started, whose id is still in `actionHoveredRowId`.\n const isActionCellHovered =\n !bulkSelectionActive && actionHoveredRowId === row.id;\n return (\n <Fragment key={row.id}>\n <TableRow\n ref={(node) => {\n rowRefs.current[rowIndex] = node;\n }}\n tabIndex={rowIndex === activeRowIndex ? 0 : -1}\n onFocus={() => handleRowFocus(rowIndex)}\n onKeyDown={(event) => handleRowKeyDown(event, rowIndex)}\n selected={isSelected}\n onClick={\n highlightCurrentRow\n ? () => setCurrentRowId(row.id)\n : undefined\n }\n className={cn(\n 'group',\n highlightCurrentRow && 'cursor-pointer',\n striped &&\n rowIndex % 2 === 1 &&\n !isSelected &&\n !isCurrent &&\n 'bg-[var(--ui-background-surface-secondary)]',\n isCurrent &&\n !isSelected &&\n 'bg-[var(--ui-table-data-row-color-active)]',\n // The action cell's own hover tint (its ellipsis/cog\n // trigger) should be the only thing that reacts while\n // it's hovered — not the rest of the row underneath it.\n isActionCellHovered && 'hover:bg-transparent'\n )}\n >\n {row.getVisibleCells().map((cell) => {\n const isPinned = cell.column.getIsPinned();\n if (cell.column.id === '__actions') {\n return (\n <TableActionsCell\n key={cell.id}\n style={getCellStyle(cell, resizingEnabled)}\n className={rowBg}\n bulkSelectionActive={bulkSelectionActive}\n onMouseEnter={\n bulkSelectionActive\n ? undefined\n : () => setActionHoveredRowId(row.id)\n }\n onMouseLeave={\n bulkSelectionActive\n ? undefined\n : () =>\n setActionHoveredRowId((id) =>\n id === row.id ? undefined : id\n )\n }\n >\n {renderRowActions && (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <ButtonIcon aria-label={rowActionsLabel} />\n }\n >\n <EllipsisIcon />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {renderRowActions(row)}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </TableActionsCell>\n );\n }\n return (\n <TableCell\n key={cell.id}\n wrap={cell.column.columnDef.meta?.wrap}\n style={getCellStyle(cell, resizingEnabled)}\n className={cn(isPinned && rowBg)}\n >\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )}\n </TableCell>\n );\n })}\n </TableRow>\n {renderExpandedRow && row.getIsExpanded() && (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell\n className=\"h-auto py-3\"\n colSpan={row.getVisibleCells().length}\n >\n {renderExpandedRow(row)}\n </TableCell>\n </TableRow>\n )}\n </Fragment>\n );\n })\n ) : renderEmptyState ? (\n <TableRow>\n <TableCell\n colSpan={table.getVisibleLeafColumns().length}\n className=\"h-24 text-center\"\n >\n {renderEmptyState({\n hasFilters: table.getState().columnFilters.length > 0,\n })}\n </TableCell>\n </TableRow>\n ) : (\n <TableRow>\n <TableCell\n colSpan={table.getVisibleLeafColumns().length}\n className=\"h-24 text-center text-[var(--ui-table-data-value-color-disabled)]\"\n >\n {emptyLabel}\n </TableCell>\n </TableRow>\n )}\n {isInfiniteScroll && !skeleton && rows.length > 0 && hasNextPage && (\n <TableRow\n ref={sentinelRef}\n aria-hidden\n className=\"hover:bg-transparent\"\n >\n <TableCell\n colSpan={table.getVisibleLeafColumns().length}\n className=\"h-1 p-0\"\n />\n </TableRow>\n )}\n {isInfiniteScroll &&\n !skeleton &&\n rows.length > 0 &&\n isLoadingMore && (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell colSpan={table.getVisibleLeafColumns().length}>\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"h-4 w-full animate-pulse rounded bg-[var(--ui-background-surface-secondary)]\"\n >\n <span className=\"sr-only\">Loading more rows…</span>\n </div>\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -23,7 +23,7 @@ var o = i.Root, s = i.Trigger, c = n.forwardRef(({ className: n, sideOffset: a =
23
23
  className: "z-50",
24
24
  children: /* @__PURE__ */ r(i.Popup, {
25
25
  ref: f,
26
- className: e("max-w-[var(--ui-tooltip-container-width-max)] min-w-[var(--ui-tooltip-container-width-min)] rounded-[var(--ui-tooltip-container-border-radius)] bg-[var(--ui-tooltip-container-color)] px-[var(--ui-tooltip-container-padding-x)] py-[var(--ui-tooltip-container-padding-y)] text-xs font-medium leading-4 text-[var(--ui-tooltip-label-color)]", n),
26
+ className: e("max-w-[var(--ui-tooltip-container-width-max)] min-w-[var(--ui-tooltip-container-width-min)] rounded-[var(--ui-tooltip-container-border-radius)] bg-[var(--ui-tooltip-container-color)] px-[var(--ui-tooltip-container-padding-x)] py-[var(--ui-tooltip-container-padding-y)] text-xs font-medium leading-4 break-words text-[var(--ui-tooltip-label-color)]", n),
27
27
  ...d
28
28
  })
29
29
  })
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../../../../src/components/ui/tooltip/tooltip.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip';\n\nimport { cn } from '@/lib/utils';\nimport { usePortalContainer } from '@/lib/portal-container';\n\n// A contextual hint shown on hover/focus of its trigger. Wraps the Base UI\n// Tooltip primitive; the popup is themed with the `--ui-tooltip-*` token tier\n// (a dark, ~90%-opaque bubble with a light label, 12/8px padding, 4px radius,\n// 48–256px width). No arrow — matching the Figma design. Wrap the whole app (or\n// a region) in `TooltipProvider` to share open/close delays across tooltips.\n\nfunction TooltipProvider(props: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return <TooltipPrimitive.Provider delay={300} {...props} />;\n}\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Popup>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Popup> & {\n sideOffset?: number;\n side?: TooltipPrimitive.Positioner.Props['side'];\n align?: TooltipPrimitive.Positioner.Props['align'];\n /**\n * Override what the popup is positioned against — defaults to the\n * `TooltipTrigger` element. Useful when the hover/focus trigger is\n * narrower than the element the tooltip should visually align to (e.g. a\n * truncating label inside a full-width row, where the tooltip should\n * anchor to the row's edge, not the label's).\n */\n anchor?: TooltipPrimitive.Positioner.Props['anchor'];\n portalContainer?: TooltipPrimitive.Portal.Props['container'];\n keepMounted?: TooltipPrimitive.Portal.Props['keepMounted'];\n }\n>(\n (\n {\n className,\n sideOffset = 6,\n side = 'top',\n align = 'center',\n anchor,\n portalContainer,\n keepMounted,\n ...props\n },\n ref\n ) => {\n const ctxContainer = usePortalContainer();\n const resolvedContainer = portalContainer ?? ctxContainer;\n\n return (\n <TooltipPrimitive.Portal\n container={resolvedContainer}\n keepMounted={keepMounted}\n >\n <TooltipPrimitive.Positioner\n sideOffset={sideOffset}\n side={side}\n align={align}\n anchor={anchor}\n className=\"z-50\"\n >\n <TooltipPrimitive.Popup\n ref={ref}\n className={cn(\n 'max-w-[var(--ui-tooltip-container-width-max)] min-w-[var(--ui-tooltip-container-width-min)] rounded-[var(--ui-tooltip-container-border-radius)] bg-[var(--ui-tooltip-container-color)] px-[var(--ui-tooltip-container-padding-x)] py-[var(--ui-tooltip-container-padding-y)] text-xs font-medium leading-4 text-[var(--ui-tooltip-label-color)]',\n className\n )}\n {...props}\n />\n </TooltipPrimitive.Positioner>\n </TooltipPrimitive.Portal>\n );\n }\n);\nTooltipContent.displayName = 'TooltipContent';\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n"],"mappings":";;;;;;AAYA,SAAS,EAAgB,GAA+D;CACtF,OAAO,kBAAC,EAAiB,UAAlB;EAA2B,OAAO;EAAK,GAAI;CAAQ,CAAA;AAC5D;AAEA,IAAM,IAAU,EAAiB,MAE3B,IAAiB,EAAiB,SAElC,IAAiB,EAAM,YAmBzB,EACE,cACA,gBAAa,GACb,UAAO,OACP,WAAQ,UACR,WACA,oBACA,gBACA,GAAG,KAEL,MACG;CACH,IAAM,IAAe,EAAmB,GAClC,IAAoB,KAAmB;CAE7C,OACA,kBAAC,EAAiB,QAAlB;EACE,WAAW;EACE;EAEb,UAAA,kBAAC,EAAiB,YAAlB;GACc;GACN;GACC;GACC;GACR,WAAU;GAEV,UAAA,kBAAC,EAAiB,OAAlB;IACO;IACL,WAAW,EACT,mVACA,CACF;IACA,GAAI;GACL,CAAA;EAC0B,CAAA;CACN,CAAA;AAE3B,CACF;AACA,EAAe,cAAc"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../../../../src/components/ui/tooltip/tooltip.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip';\n\nimport { cn } from '@/lib/utils';\nimport { usePortalContainer } from '@/lib/portal-container';\n\n// A contextual hint shown on hover/focus of its trigger. Wraps the Base UI\n// Tooltip primitive; the popup is themed with the `--ui-tooltip-*` token tier\n// (a dark, ~90%-opaque bubble with a light label, 12/8px padding, 4px radius,\n// 48–256px width). No arrow — matching the Figma design. Wrap the whole app (or\n// a region) in `TooltipProvider` to share open/close delays across tooltips.\n\nfunction TooltipProvider(props: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return <TooltipPrimitive.Provider delay={300} {...props} />;\n}\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Popup>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Popup> & {\n sideOffset?: number;\n side?: TooltipPrimitive.Positioner.Props['side'];\n align?: TooltipPrimitive.Positioner.Props['align'];\n /**\n * Override what the popup is positioned against — defaults to the\n * `TooltipTrigger` element. Useful when the hover/focus trigger is\n * narrower than the element the tooltip should visually align to (e.g. a\n * truncating label inside a full-width row, where the tooltip should\n * anchor to the row's edge, not the label's).\n */\n anchor?: TooltipPrimitive.Positioner.Props['anchor'];\n portalContainer?: TooltipPrimitive.Portal.Props['container'];\n keepMounted?: TooltipPrimitive.Portal.Props['keepMounted'];\n }\n>(\n (\n {\n className,\n sideOffset = 6,\n side = 'top',\n align = 'center',\n anchor,\n portalContainer,\n keepMounted,\n ...props\n },\n ref\n ) => {\n const ctxContainer = usePortalContainer();\n const resolvedContainer = portalContainer ?? ctxContainer;\n\n return (\n <TooltipPrimitive.Portal\n container={resolvedContainer}\n keepMounted={keepMounted}\n >\n <TooltipPrimitive.Positioner\n sideOffset={sideOffset}\n side={side}\n align={align}\n anchor={anchor}\n className=\"z-50\"\n >\n <TooltipPrimitive.Popup\n ref={ref}\n className={cn(\n 'max-w-[var(--ui-tooltip-container-width-max)] min-w-[var(--ui-tooltip-container-width-min)] rounded-[var(--ui-tooltip-container-border-radius)] bg-[var(--ui-tooltip-container-color)] px-[var(--ui-tooltip-container-padding-x)] py-[var(--ui-tooltip-container-padding-y)] text-xs font-medium leading-4 break-words text-[var(--ui-tooltip-label-color)]',\n className\n )}\n {...props}\n />\n </TooltipPrimitive.Positioner>\n </TooltipPrimitive.Portal>\n );\n }\n);\nTooltipContent.displayName = 'TooltipContent';\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n"],"mappings":";;;;;;AAYA,SAAS,EAAgB,GAA+D;CACtF,OAAO,kBAAC,EAAiB,UAAlB;EAA2B,OAAO;EAAK,GAAI;CAAQ,CAAA;AAC5D;AAEA,IAAM,IAAU,EAAiB,MAE3B,IAAiB,EAAiB,SAElC,IAAiB,EAAM,YAmBzB,EACE,cACA,gBAAa,GACb,UAAO,OACP,WAAQ,UACR,WACA,oBACA,gBACA,GAAG,KAEL,MACG;CACH,IAAM,IAAe,EAAmB,GAClC,IAAoB,KAAmB;CAE7C,OACA,kBAAC,EAAiB,QAAlB;EACE,WAAW;EACE;EAEb,UAAA,kBAAC,EAAiB,YAAlB;GACc;GACN;GACC;GACC;GACR,WAAU;GAEV,UAAA,kBAAC,EAAiB,OAAlB;IACO;IACL,WAAW,EACT,+VACA,CACF;IACA,GAAI;GACL,CAAA;EAC0B,CAAA;CACN,CAAA;AAE3B,CACF;AACA,EAAe,cAAc"}
@@ -124,6 +124,17 @@ arrow is rendered, matching the design.
124
124
  - **Given** only `default-open`
125
125
  **Then** the tooltip manages its own open state.
126
126
 
127
+ ### Long content
128
+
129
+ - **Given** content wider than the popup's maximum width
130
+ **When** the popup opens
131
+ **Then** multi-word text wraps at spaces and the popup stays within its maximum
132
+ width.
133
+ - **Given** content with no space to wrap at (a single email, URL or UUID)
134
+ **When** the popup opens
135
+ **Then** the token breaks across lines inside the popup — it never runs past the
136
+ popup's edge.
137
+
127
138
  ### Placement
128
139
 
129
140
  - **Given** a `side` on the content
package/dist/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Acronis UI Kit for React — LLM Reference
2
2
 
3
- > `@acronis-platform/ui-react` v5.5.7
3
+ > `@acronis-platform/ui-react` v5.5.9
4
4
 
5
5
  Machine-readable component reference compiled from framework-agnostic specs.
6
6
  Each entry links to a self-contained markdown file with props, behavior,
@@ -5,8 +5,8 @@ declare module '@tanstack/react-table' {
5
5
  /** Pin the column to a table edge (sticky while the grid scrolls horizontally). */
6
6
  pin?: 'left' | 'right';
7
7
  /**
8
- * Let the column's header + cells wrap onto multiple lines (drops the fixed
9
- * row height); mirrors the `Table` primitives' `wrap` prop on TableHead/TableCell.
8
+ * Let the column's data cells wrap onto multiple lines (drops the fixed row
9
+ * height). Headers remain single-line and truncate with a tooltip.
10
10
  */
11
11
  wrap?: boolean;
12
12
  /** Label shown for this column in the visibility dropdown. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acronis-platform/ui-react",
3
- "version": "5.5.7",
3
+ "version": "5.5.9",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/acronis/uikit.git",
@@ -56,8 +56,8 @@
56
56
  "react-resizable-panels": "4.11.2",
57
57
  "recharts": "3.8.1",
58
58
  "tailwind-merge": "3.6.0",
59
- "@acronis-platform/icons-react": "2.1.0",
60
59
  "@acronis-platform/tokens-pd": "4.0.1",
60
+ "@acronis-platform/icons-react": "2.1.0",
61
61
  "@acronis-platform/design-assets": "2.0.0"
62
62
  },
63
63
  "devDependencies": {