@stackframe/dashboard-ui-components 2.8.84 → 2.8.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/dist/components/analytics-chart/analytics-chart-pie.d.ts +67 -0
  2. package/dist/components/analytics-chart/analytics-chart-pie.d.ts.map +1 -0
  3. package/dist/components/analytics-chart/analytics-chart-pie.js +253 -0
  4. package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -0
  5. package/dist/components/analytics-chart/analytics-chart.d.ts +554 -0
  6. package/dist/components/analytics-chart/analytics-chart.d.ts.map +1 -0
  7. package/dist/components/analytics-chart/analytics-chart.js +1021 -0
  8. package/dist/components/analytics-chart/analytics-chart.js.map +1 -0
  9. package/dist/components/analytics-chart/default-analytics-chart-tooltip.d.ts +66 -0
  10. package/dist/components/analytics-chart/default-analytics-chart-tooltip.d.ts.map +1 -0
  11. package/dist/components/analytics-chart/default-analytics-chart-tooltip.js +179 -0
  12. package/dist/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -0
  13. package/dist/components/analytics-chart/format.d.ts +13 -0
  14. package/dist/components/analytics-chart/format.d.ts.map +1 -0
  15. package/dist/components/analytics-chart/format.js +138 -0
  16. package/dist/components/analytics-chart/format.js.map +1 -0
  17. package/dist/components/analytics-chart/index.d.ts +8 -0
  18. package/dist/components/analytics-chart/index.js +184 -0
  19. package/dist/components/analytics-chart/palette.d.ts +15 -0
  20. package/dist/components/analytics-chart/palette.d.ts.map +1 -0
  21. package/dist/components/analytics-chart/palette.js +60 -0
  22. package/dist/components/analytics-chart/palette.js.map +1 -0
  23. package/dist/components/analytics-chart/render-data-series.d.ts +28 -0
  24. package/dist/components/analytics-chart/render-data-series.d.ts.map +1 -0
  25. package/dist/components/analytics-chart/render-data-series.js +109 -0
  26. package/dist/components/analytics-chart/render-data-series.js.map +1 -0
  27. package/dist/components/analytics-chart/state.d.ts +54 -0
  28. package/dist/components/analytics-chart/state.d.ts.map +1 -0
  29. package/dist/components/analytics-chart/state.js +142 -0
  30. package/dist/components/analytics-chart/state.js.map +1 -0
  31. package/dist/components/analytics-chart/strings.d.ts +33 -0
  32. package/dist/components/analytics-chart/strings.d.ts.map +1 -0
  33. package/dist/components/analytics-chart/strings.js +37 -0
  34. package/dist/components/analytics-chart/strings.js.map +1 -0
  35. package/dist/components/analytics-chart/types.d.ts +157 -0
  36. package/dist/components/analytics-chart/types.d.ts.map +1 -0
  37. package/dist/components/analytics-chart/types.js +21 -0
  38. package/dist/components/analytics-chart/types.js.map +1 -0
  39. package/dist/components/badge.d.ts +16 -0
  40. package/dist/components/badge.d.ts.map +1 -1
  41. package/dist/components/badge.js +16 -0
  42. package/dist/components/badge.js.map +1 -1
  43. package/dist/components/button.d.ts +15 -1
  44. package/dist/components/button.d.ts.map +1 -1
  45. package/dist/components/button.js +14 -0
  46. package/dist/components/button.js.map +1 -1
  47. package/dist/components/card.d.ts +28 -0
  48. package/dist/components/card.d.ts.map +1 -1
  49. package/dist/components/card.js +28 -0
  50. package/dist/components/card.js.map +1 -1
  51. package/dist/components/chart-card.d.ts +29 -0
  52. package/dist/components/chart-card.d.ts.map +1 -1
  53. package/dist/components/chart-card.js +29 -0
  54. package/dist/components/chart-card.js.map +1 -1
  55. package/dist/components/chart-legend.d.ts +1 -2
  56. package/dist/components/chart-legend.d.ts.map +1 -1
  57. package/dist/components/chart-legend.js +0 -4
  58. package/dist/components/chart-legend.js.map +1 -1
  59. package/dist/components/data-grid/data-grid-sizing.d.ts +11 -0
  60. package/dist/components/data-grid/data-grid-sizing.d.ts.map +1 -0
  61. package/dist/components/data-grid/data-grid-sizing.js +34 -0
  62. package/dist/components/data-grid/data-grid-sizing.js.map +1 -0
  63. package/dist/components/data-grid/data-grid-toolbar.d.ts +31 -0
  64. package/dist/components/data-grid/data-grid-toolbar.d.ts.map +1 -0
  65. package/dist/components/data-grid/data-grid-toolbar.js +226 -0
  66. package/dist/components/data-grid/data-grid-toolbar.js.map +1 -0
  67. package/dist/components/data-grid/data-grid.d.ts +233 -0
  68. package/dist/components/data-grid/data-grid.d.ts.map +1 -0
  69. package/dist/components/data-grid/data-grid.js +871 -0
  70. package/dist/components/data-grid/data-grid.js.map +1 -0
  71. package/dist/components/data-grid/index.d.ts +7 -0
  72. package/dist/components/data-grid/index.js +176 -0
  73. package/dist/components/data-grid/state.d.ts +91 -0
  74. package/dist/components/data-grid/state.d.ts.map +1 -0
  75. package/dist/components/data-grid/state.js +329 -0
  76. package/dist/components/data-grid/state.js.map +1 -0
  77. package/dist/components/data-grid/strings.d.ts +8 -0
  78. package/dist/components/data-grid/strings.d.ts.map +1 -0
  79. package/dist/components/data-grid/strings.js +42 -0
  80. package/dist/components/data-grid/strings.js.map +1 -0
  81. package/dist/components/data-grid/types.d.ts +242 -0
  82. package/dist/components/data-grid/types.d.ts.map +1 -0
  83. package/dist/components/data-grid/types.js +0 -0
  84. package/dist/components/data-grid/use-data-source.d.ts +79 -0
  85. package/dist/components/data-grid/use-data-source.d.ts.map +1 -0
  86. package/dist/components/data-grid/use-data-source.js +236 -0
  87. package/dist/components/data-grid/use-data-source.js.map +1 -0
  88. package/dist/components/empty-state.d.ts +16 -0
  89. package/dist/components/empty-state.d.ts.map +1 -1
  90. package/dist/components/empty-state.js +16 -0
  91. package/dist/components/empty-state.js.map +1 -1
  92. package/dist/components/metric-card.d.ts +24 -0
  93. package/dist/components/metric-card.d.ts.map +1 -1
  94. package/dist/components/metric-card.js +24 -0
  95. package/dist/components/metric-card.js.map +1 -1
  96. package/dist/components/progress-bar.d.ts +10 -0
  97. package/dist/components/progress-bar.d.ts.map +1 -1
  98. package/dist/components/progress-bar.js +10 -0
  99. package/dist/components/progress-bar.js.map +1 -1
  100. package/dist/components/separator.d.ts +9 -0
  101. package/dist/components/separator.d.ts.map +1 -1
  102. package/dist/components/separator.js +9 -0
  103. package/dist/components/separator.js.map +1 -1
  104. package/dist/components/skeleton.d.ts +12 -0
  105. package/dist/components/skeleton.d.ts.map +1 -1
  106. package/dist/components/skeleton.js +12 -0
  107. package/dist/components/skeleton.js.map +1 -1
  108. package/dist/components/table.d.ts +25 -0
  109. package/dist/components/table.d.ts.map +1 -1
  110. package/dist/components/table.js +25 -0
  111. package/dist/components/table.js.map +1 -1
  112. package/dist/dashboard-ui-components.global.js +8607 -2902
  113. package/dist/dashboard-ui-components.global.js.map +4 -4
  114. package/dist/esm/components/analytics-chart/analytics-chart-pie.d.ts +67 -0
  115. package/dist/esm/components/analytics-chart/analytics-chart-pie.d.ts.map +1 -0
  116. package/dist/esm/components/analytics-chart/analytics-chart-pie.js +251 -0
  117. package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -0
  118. package/dist/esm/components/analytics-chart/analytics-chart.d.ts +554 -0
  119. package/dist/esm/components/analytics-chart/analytics-chart.d.ts.map +1 -0
  120. package/dist/esm/components/analytics-chart/analytics-chart.js +1019 -0
  121. package/dist/esm/components/analytics-chart/analytics-chart.js.map +1 -0
  122. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.d.ts +66 -0
  123. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.d.ts.map +1 -0
  124. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.js +176 -0
  125. package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -0
  126. package/dist/esm/components/analytics-chart/format.d.ts +13 -0
  127. package/dist/esm/components/analytics-chart/format.d.ts.map +1 -0
  128. package/dist/esm/components/analytics-chart/format.js +133 -0
  129. package/dist/esm/components/analytics-chart/format.js.map +1 -0
  130. package/dist/esm/components/analytics-chart/index.d.ts +8 -0
  131. package/dist/esm/components/analytics-chart/index.js +9 -0
  132. package/dist/esm/components/analytics-chart/palette.d.ts +15 -0
  133. package/dist/esm/components/analytics-chart/palette.d.ts.map +1 -0
  134. package/dist/esm/components/analytics-chart/palette.js +55 -0
  135. package/dist/esm/components/analytics-chart/palette.js.map +1 -0
  136. package/dist/esm/components/analytics-chart/render-data-series.d.ts +28 -0
  137. package/dist/esm/components/analytics-chart/render-data-series.d.ts.map +1 -0
  138. package/dist/esm/components/analytics-chart/render-data-series.js +107 -0
  139. package/dist/esm/components/analytics-chart/render-data-series.js.map +1 -0
  140. package/dist/esm/components/analytics-chart/state.d.ts +54 -0
  141. package/dist/esm/components/analytics-chart/state.d.ts.map +1 -0
  142. package/dist/esm/components/analytics-chart/state.js +126 -0
  143. package/dist/esm/components/analytics-chart/state.js.map +1 -0
  144. package/dist/esm/components/analytics-chart/strings.d.ts +33 -0
  145. package/dist/esm/components/analytics-chart/strings.d.ts.map +1 -0
  146. package/dist/esm/components/analytics-chart/strings.js +34 -0
  147. package/dist/esm/components/analytics-chart/strings.js.map +1 -0
  148. package/dist/esm/components/analytics-chart/types.d.ts +157 -0
  149. package/dist/esm/components/analytics-chart/types.d.ts.map +1 -0
  150. package/dist/esm/components/analytics-chart/types.js +18 -0
  151. package/dist/esm/components/analytics-chart/types.js.map +1 -0
  152. package/dist/esm/components/badge.d.ts +16 -0
  153. package/dist/esm/components/badge.d.ts.map +1 -1
  154. package/dist/esm/components/badge.js +16 -0
  155. package/dist/esm/components/badge.js.map +1 -1
  156. package/dist/esm/components/button.d.ts +14 -0
  157. package/dist/esm/components/button.d.ts.map +1 -1
  158. package/dist/esm/components/button.js +14 -0
  159. package/dist/esm/components/button.js.map +1 -1
  160. package/dist/esm/components/card.d.ts +28 -0
  161. package/dist/esm/components/card.d.ts.map +1 -1
  162. package/dist/esm/components/card.js +28 -0
  163. package/dist/esm/components/card.js.map +1 -1
  164. package/dist/esm/components/chart-card.d.ts +29 -0
  165. package/dist/esm/components/chart-card.d.ts.map +1 -1
  166. package/dist/esm/components/chart-card.js +29 -0
  167. package/dist/esm/components/chart-card.js.map +1 -1
  168. package/dist/esm/components/chart-legend.d.ts +1 -2
  169. package/dist/esm/components/chart-legend.d.ts.map +1 -1
  170. package/dist/esm/components/chart-legend.js +1 -3
  171. package/dist/esm/components/chart-legend.js.map +1 -1
  172. package/dist/esm/components/data-grid/data-grid-sizing.d.ts +11 -0
  173. package/dist/esm/components/data-grid/data-grid-sizing.d.ts.map +1 -0
  174. package/dist/esm/components/data-grid/data-grid-sizing.js +29 -0
  175. package/dist/esm/components/data-grid/data-grid-sizing.js.map +1 -0
  176. package/dist/esm/components/data-grid/data-grid-toolbar.d.ts +31 -0
  177. package/dist/esm/components/data-grid/data-grid-toolbar.d.ts.map +1 -0
  178. package/dist/esm/components/data-grid/data-grid-toolbar.js +223 -0
  179. package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -0
  180. package/dist/esm/components/data-grid/data-grid.d.ts +233 -0
  181. package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -0
  182. package/dist/esm/components/data-grid/data-grid.js +868 -0
  183. package/dist/esm/components/data-grid/data-grid.js.map +1 -0
  184. package/dist/esm/components/data-grid/index.d.ts +7 -0
  185. package/dist/esm/components/data-grid/index.js +7 -0
  186. package/dist/esm/components/data-grid/state.d.ts +91 -0
  187. package/dist/esm/components/data-grid/state.d.ts.map +1 -0
  188. package/dist/esm/components/data-grid/state.js +305 -0
  189. package/dist/esm/components/data-grid/state.js.map +1 -0
  190. package/dist/esm/components/data-grid/strings.d.ts +8 -0
  191. package/dist/esm/components/data-grid/strings.d.ts.map +1 -0
  192. package/dist/esm/components/data-grid/strings.js +39 -0
  193. package/dist/esm/components/data-grid/strings.js.map +1 -0
  194. package/dist/esm/components/data-grid/types.d.ts +242 -0
  195. package/dist/esm/components/data-grid/types.d.ts.map +1 -0
  196. package/dist/esm/components/data-grid/types.js +1 -0
  197. package/dist/esm/components/data-grid/use-data-source.d.ts +79 -0
  198. package/dist/esm/components/data-grid/use-data-source.d.ts.map +1 -0
  199. package/dist/esm/components/data-grid/use-data-source.js +234 -0
  200. package/dist/esm/components/data-grid/use-data-source.js.map +1 -0
  201. package/dist/esm/components/empty-state.d.ts +16 -0
  202. package/dist/esm/components/empty-state.d.ts.map +1 -1
  203. package/dist/esm/components/empty-state.js +16 -0
  204. package/dist/esm/components/empty-state.js.map +1 -1
  205. package/dist/esm/components/metric-card.d.ts +24 -0
  206. package/dist/esm/components/metric-card.d.ts.map +1 -1
  207. package/dist/esm/components/metric-card.js +24 -0
  208. package/dist/esm/components/metric-card.js.map +1 -1
  209. package/dist/esm/components/progress-bar.d.ts +10 -0
  210. package/dist/esm/components/progress-bar.d.ts.map +1 -1
  211. package/dist/esm/components/progress-bar.js +10 -0
  212. package/dist/esm/components/progress-bar.js.map +1 -1
  213. package/dist/esm/components/separator.d.ts +9 -0
  214. package/dist/esm/components/separator.d.ts.map +1 -1
  215. package/dist/esm/components/separator.js +9 -0
  216. package/dist/esm/components/separator.js.map +1 -1
  217. package/dist/esm/components/skeleton.d.ts +12 -0
  218. package/dist/esm/components/skeleton.d.ts.map +1 -1
  219. package/dist/esm/components/skeleton.js +12 -0
  220. package/dist/esm/components/skeleton.js.map +1 -1
  221. package/dist/esm/components/table.d.ts +25 -0
  222. package/dist/esm/components/table.d.ts.map +1 -1
  223. package/dist/esm/components/table.js +25 -0
  224. package/dist/esm/components/table.js.map +1 -1
  225. package/dist/esm/index.d.ts +4 -2
  226. package/dist/esm/index.js +6 -2
  227. package/dist/index.d.ts +15 -2
  228. package/dist/index.js +16 -7
  229. package/package.json +4 -3
@@ -0,0 +1,871 @@
1
+ "use client";
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_chunk = require('../../chunk-BE-pF4vm.js');
5
+ let _phosphor_icons_react = require("@phosphor-icons/react");
6
+ let _stackframe_stack_ui = require("@stackframe/stack-ui");
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ react = require_chunk.__toESM(react);
10
+ let __state_js = require("./state.js");
11
+ let __strings_js = require("./strings.js");
12
+ let _tanstack_react_virtual = require("@tanstack/react-virtual");
13
+ let ___skeleton_js = require("../skeleton.js");
14
+ let __data_grid_toolbar_js = require("./data-grid-toolbar.js");
15
+ let __data_grid_sizing_js = require("./data-grid-sizing.js");
16
+
17
+ //#region src/components/data-grid/data-grid.tsx
18
+ function ResizeHandle({ onResize, onResizeEnd }) {
19
+ const startXRef = (0, react.useRef)(0);
20
+ const rafRef = (0, react.useRef)(0);
21
+ const latestDeltaRef = (0, react.useRef)(0);
22
+ const callbacksRef = (0, react.useRef)({
23
+ onResize,
24
+ onResizeEnd
25
+ });
26
+ callbacksRef.current = {
27
+ onResize,
28
+ onResizeEnd
29
+ };
30
+ const onPointerDown = (0, react.useCallback)((e) => {
31
+ e.preventDefault();
32
+ e.stopPropagation();
33
+ startXRef.current = e.clientX;
34
+ latestDeltaRef.current = 0;
35
+ const el = e.currentTarget;
36
+ el.setPointerCapture(e.pointerId);
37
+ let finished = false;
38
+ const onMove = (ev) => {
39
+ latestDeltaRef.current = ev.clientX - startXRef.current;
40
+ if (rafRef.current !== 0) return;
41
+ rafRef.current = requestAnimationFrame(() => {
42
+ rafRef.current = 0;
43
+ callbacksRef.current.onResize(latestDeltaRef.current);
44
+ });
45
+ };
46
+ const finish = () => {
47
+ if (finished) return;
48
+ finished = true;
49
+ if (rafRef.current !== 0) {
50
+ cancelAnimationFrame(rafRef.current);
51
+ rafRef.current = 0;
52
+ callbacksRef.current.onResize(latestDeltaRef.current);
53
+ }
54
+ el.removeEventListener("pointermove", onMove);
55
+ el.removeEventListener("pointerup", finish);
56
+ el.removeEventListener("pointercancel", finish);
57
+ el.removeEventListener("lostpointercapture", finish);
58
+ if (el.hasPointerCapture(e.pointerId)) el.releasePointerCapture(e.pointerId);
59
+ callbacksRef.current.onResizeEnd();
60
+ };
61
+ el.addEventListener("pointermove", onMove);
62
+ el.addEventListener("pointerup", finish);
63
+ el.addEventListener("pointercancel", finish);
64
+ el.addEventListener("lostpointercapture", finish);
65
+ }, []);
66
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
67
+ className: (0, _stackframe_stack_ui.cn)("absolute right-0 top-0 bottom-0 z-10 w-[5px] cursor-col-resize touch-none", "group-hover/header:bg-foreground/[0.06] hover:!bg-blue-500/30", "transition-colors duration-100"),
68
+ onClick: (e) => {
69
+ e.preventDefault();
70
+ e.stopPropagation();
71
+ },
72
+ onPointerDown
73
+ });
74
+ }
75
+ function HeaderCell({ col, isSorted, sortIndex, resizable, onSort, onResize, onResizeEnd }) {
76
+ const ctx = {
77
+ columnId: col.id,
78
+ columnDef: col,
79
+ isSorted,
80
+ sortIndex
81
+ };
82
+ const label = typeof col.header === "function" ? col.header(ctx) : col.header;
83
+ const sortable = col.sortable !== false;
84
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
85
+ className: (0, _stackframe_stack_ui.cn)("group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent", "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0", sortable && "cursor-pointer"),
86
+ style: (0, __data_grid_sizing_js.getColumnSizingStyle)(col),
87
+ "data-col-id": col.id,
88
+ onClick: (e) => sortable && onSort(col.id, e.metaKey || e.ctrlKey),
89
+ role: "columnheader",
90
+ "aria-sort": isSorted === "asc" ? "ascending" : isSorted === "desc" ? "descending" : "none",
91
+ children: [
92
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
93
+ className: (0, _stackframe_stack_ui.cn)("flex-1 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground", col.align === "center" && "text-center", col.align === "right" && "text-right"),
94
+ children: label
95
+ }),
96
+ isSorted && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
97
+ className: "flex items-center gap-0.5 text-foreground/60",
98
+ children: [isSorted === "asc" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.ArrowUp, {
99
+ className: "h-3 w-3",
100
+ weight: "bold"
101
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.ArrowDown, {
102
+ className: "h-3 w-3",
103
+ weight: "bold"
104
+ }), sortIndex != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
105
+ className: "text-[10px] font-medium tabular-nums",
106
+ children: sortIndex
107
+ })]
108
+ }),
109
+ !isSorted && sortable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
110
+ className: "hidden group-hover/header:flex items-center text-foreground/20",
111
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.CaretUp, {
112
+ className: "h-2.5 w-2.5 -mb-[1px]",
113
+ weight: "bold"
114
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.CaretDown, {
115
+ className: "h-2.5 w-2.5 -mt-[1px]",
116
+ weight: "bold"
117
+ })]
118
+ }),
119
+ resizable && col.resizable !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResizeHandle, {
120
+ onResize: (delta) => onResize(col.id, delta),
121
+ onResizeEnd
122
+ })
123
+ ]
124
+ });
125
+ }
126
+ function DataCell({ col, row, rowId, rowIndex, isSelected, dateDisplay }) {
127
+ const value = (0, __state_js.resolveColumnValue)(col, row);
128
+ const ctx = {
129
+ row,
130
+ rowId,
131
+ rowIndex,
132
+ value,
133
+ columnId: col.id,
134
+ isSelected,
135
+ dateDisplay
136
+ };
137
+ const isDateCol = col.type === "date" || col.type === "dateTime";
138
+ let content;
139
+ if (col.renderCell) content = col.renderCell(ctx);
140
+ else if (isDateCol) content = renderDateCell(value, dateDisplay, col);
141
+ else content = formatCellValue(value);
142
+ const hasCellClick = col.onCellClick || col.onCellDoubleClick;
143
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
144
+ className: (0, _stackframe_stack_ui.cn)("flex items-center px-3 truncate bg-transparent", "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0", "text-sm text-foreground", col.align === "center" && "justify-center", col.align === "right" && "justify-end", hasCellClick && "cursor-pointer"),
145
+ style: (0, __data_grid_sizing_js.getColumnSizingStyle)(col),
146
+ "data-col-id": col.id,
147
+ role: "gridcell",
148
+ onClick: col.onCellClick ? (e) => {
149
+ e.stopPropagation();
150
+ col.onCellClick(ctx, e);
151
+ } : void 0,
152
+ onDoubleClick: col.onCellDoubleClick ? (e) => {
153
+ e.stopPropagation();
154
+ col.onCellDoubleClick(ctx, e);
155
+ } : void 0,
156
+ children: content
157
+ });
158
+ }
159
+ function formatCellValue(value) {
160
+ if (value == null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
161
+ className: "text-muted-foreground/40",
162
+ children: "-"
163
+ });
164
+ if (typeof value === "boolean") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
165
+ className: (0, _stackframe_stack_ui.cn)("inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium", value ? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400" : "bg-foreground/[0.04] text-muted-foreground"),
166
+ children: value ? "Yes" : "No"
167
+ });
168
+ if (value instanceof Date) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
169
+ className: "tabular-nums text-muted-foreground",
170
+ children: value.toLocaleDateString()
171
+ });
172
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
173
+ className: "truncate",
174
+ children: String(value)
175
+ });
176
+ }
177
+ /** Built-in date cell — mirrors what `formatGridDate` returns but wraps
178
+ * the display in a `<span>` with a `title` tooltip showing the absolute
179
+ * datetime. Only used when the column has `type: "date" | "dateTime"`
180
+ * and no custom `renderCell`. */
181
+ function renderDateCell(value, dateDisplay, col) {
182
+ const { display, tooltip } = (0, __state_js.formatGridDate)(value, dateDisplay, {
183
+ parseValue: col.parseValue,
184
+ dateFormat: col.dateFormat
185
+ });
186
+ if (display == null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
187
+ className: "text-muted-foreground/40",
188
+ children: "-"
189
+ });
190
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
191
+ className: "tabular-nums text-muted-foreground truncate cursor-help",
192
+ title: tooltip ?? void 0,
193
+ children: display
194
+ });
195
+ }
196
+ function SkeletonRow({ columns, height, showCheckbox }) {
197
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
198
+ className: "flex",
199
+ style: { height },
200
+ role: "row",
201
+ children: [showCheckbox && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
202
+ className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
203
+ style: { width: 44 },
204
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___skeleton_js.DesignSkeleton, { className: "h-4 w-4 rounded" })
205
+ }), columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
206
+ className: "flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0",
207
+ style: (0, __data_grid_sizing_js.getColumnSizingStyle)(col),
208
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___skeleton_js.DesignSkeleton, {
209
+ className: "h-3.5 rounded-md",
210
+ style: { width: `${40 + Math.random() * 40}%` }
211
+ })
212
+ }, col.id))]
213
+ });
214
+ }
215
+ function SelectionCheckbox({ checked, indeterminate, onChange, ariaLabel }) {
216
+ const Icon = indeterminate ? _phosphor_icons_react.MinusSquare : checked ? _phosphor_icons_react.CheckSquare : _phosphor_icons_react.Square;
217
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
218
+ className: (0, _stackframe_stack_ui.cn)("flex items-center justify-center w-full h-full", "hover:bg-foreground/[0.04] transition-colors duration-75", checked || indeterminate ? "text-blue-600 dark:text-blue-400" : "text-muted-foreground/40 hover:text-muted-foreground/60"),
219
+ onClick: (e) => {
220
+ e.stopPropagation();
221
+ onChange(e);
222
+ },
223
+ "aria-label": ariaLabel,
224
+ role: "checkbox",
225
+ "aria-checked": indeterminate ? "mixed" : checked,
226
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
227
+ className: "h-4 w-4",
228
+ weight: checked || indeterminate ? "fill" : "regular"
229
+ })
230
+ });
231
+ }
232
+ function InfiniteScrollSentinel({ onIntersect, isLoading, strings }) {
233
+ const ref = (0, react.useRef)(null);
234
+ (0, react.useEffect)(() => {
235
+ const el = ref.current;
236
+ if (!el) return;
237
+ const observer = new IntersectionObserver((entries) => {
238
+ if (entries[0]?.isIntersecting) onIntersect();
239
+ }, { rootMargin: "200px" });
240
+ observer.observe(el);
241
+ return () => observer.disconnect();
242
+ }, [onIntersect]);
243
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
244
+ ref,
245
+ className: "flex items-center justify-center py-4",
246
+ children: isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
247
+ className: "flex items-center gap-2 text-xs text-muted-foreground",
248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-3 w-3 rounded-full border-2 border-current border-t-transparent animate-spin" }), strings.loadingMore]
249
+ })
250
+ });
251
+ }
252
+ function DefaultFooter({ ctx, pagination, onChange }) {
253
+ const { state, totalRowCount, visibleRowCount, selectedRowCount, strings } = ctx;
254
+ const totalPages = totalRowCount != null ? Math.max(1, Math.ceil(totalRowCount / state.pagination.pageSize)) : void 0;
255
+ const setPage = (pageIndex) => onChange((s) => ({
256
+ ...s,
257
+ pagination: {
258
+ ...s.pagination,
259
+ pageIndex
260
+ }
261
+ }));
262
+ const setPageSize = (pageSize) => onChange((s) => ({
263
+ ...s,
264
+ pagination: {
265
+ ...s.pagination,
266
+ pageSize,
267
+ pageIndex: 0
268
+ }
269
+ }));
270
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
271
+ className: "flex items-center justify-between px-4 py-2.5 border-t border-foreground/[0.06] text-xs text-muted-foreground",
272
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
273
+ className: "flex items-center gap-3",
274
+ children: [selectedRowCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
275
+ className: "font-medium text-foreground",
276
+ children: strings.rowsSelected(selectedRowCount)
277
+ }), totalRowCount != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
278
+ visibleRowCount,
279
+ " of ",
280
+ totalRowCount,
281
+ " rows"
282
+ ] })]
283
+ }), pagination !== "infinite" && totalPages != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
284
+ className: "flex items-center gap-3",
285
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
286
+ className: "flex items-center gap-1.5",
287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: strings.rowsPerPage }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
288
+ className: (0, _stackframe_stack_ui.cn)("h-7 rounded-lg border border-black/[0.08] dark:border-white/[0.08] bg-background px-1.5", "text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]", "cursor-pointer"),
289
+ value: state.pagination.pageSize,
290
+ onChange: (e) => setPageSize(Number(e.target.value)),
291
+ children: [
292
+ 10,
293
+ 25,
294
+ 50,
295
+ 100
296
+ ].map((size) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
297
+ value: size,
298
+ children: size
299
+ }, size))
300
+ })]
301
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
302
+ className: "flex items-center gap-1",
303
+ children: [
304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
305
+ className: (0, _stackframe_stack_ui.cn)("h-7 w-7 flex items-center justify-center rounded-lg", "hover:bg-foreground/[0.04] disabled:opacity-30 disabled:cursor-not-allowed", "transition-colors duration-75"),
306
+ onClick: () => setPage(state.pagination.pageIndex - 1),
307
+ disabled: state.pagination.pageIndex === 0,
308
+ "aria-label": "Previous page",
309
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.CaretUp, {
310
+ className: "h-3.5 w-3.5 -rotate-90",
311
+ weight: "bold"
312
+ })
313
+ }),
314
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
315
+ className: "px-2 tabular-nums font-medium",
316
+ children: strings.pageOf(state.pagination.pageIndex + 1, totalPages)
317
+ }),
318
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
319
+ className: (0, _stackframe_stack_ui.cn)("h-7 w-7 flex items-center justify-center rounded-lg", "hover:bg-foreground/[0.04] disabled:opacity-30 disabled:cursor-not-allowed", "transition-colors duration-75"),
320
+ onClick: () => setPage(state.pagination.pageIndex + 1),
321
+ disabled: state.pagination.pageIndex >= totalPages - 1,
322
+ "aria-label": "Next page",
323
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.CaretDown, {
324
+ className: "h-3.5 w-3.5 -rotate-90",
325
+ weight: "bold"
326
+ })
327
+ })
328
+ ]
329
+ })]
330
+ })]
331
+ });
332
+ }
333
+ /**
334
+ * Interactive table with sorting, quick search, pagination, selection,
335
+ * and virtualization. Handles 10k+ rows smoothly. Pair with
336
+ * `useDataSource` for client-side data; use an async `dataSource`
337
+ * generator for server or infinite-scroll modes.
338
+ *
339
+ * ## Mental model (read this first — everything else depends on it)
340
+ *
341
+ * DataGrid is a **display** component. It does NOT sort, search, or
342
+ * paginate your data directly — you own that, but `useDataSource` does
343
+ * it for you. The `rows` prop is always the already-processed slice to
344
+ * show. The grid tracks user intent in `state` (sort model, quick
345
+ * search text, page index). You feed that state into `useDataSource`,
346
+ * and its output goes back in as `rows`.
347
+ *
348
+ * `useDataSource` IS the processor. Given your full dataset and the
349
+ * grid's state, it returns the searched + sorted + paginated rows
350
+ * ready to pass to DataGrid. This is the ONLY correct pattern for
351
+ * client-side data — do NOT pass a raw array to `rows`.
352
+ *
353
+ * ## Search (client vs async)
354
+ *
355
+ * - **Client mode** (`useDataSource` with `data`): a case-insensitive
356
+ * substring match across every column is applied automatically.
357
+ * Override the matcher with `matchRow` for fuzzy / weighted search,
358
+ * or disable by passing `matchRow: () => true`.
359
+ * - **Async mode** (`useDataSource` with `dataSource`): `state.quickSearch`
360
+ * is forwarded to the generator as `params.quickSearch`. Same
361
+ * mechanism as `params.sorting` — a change triggers a refetch, and
362
+ * the generator is the "matching logic" (typically a WHERE / ILIKE
363
+ * clause in the backend query). The grid does NO client-side
364
+ * filtering in async mode.
365
+ *
366
+ * ## The canonical pattern
367
+ *
368
+ * ```tsx
369
+ * // 1. Columns — define OUTSIDE the component or inside a useMemo. Must be stable.
370
+ * const columns = React.useMemo(() => [
371
+ * { id: "name", header: "Name", accessor: "name", width: 180, type: "string" },
372
+ * { id: "email", header: "Email", accessor: "email", width: 240, type: "string" },
373
+ * { id: "role", header: "Role", accessor: "role", width: 120, type: "singleSelect",
374
+ * valueOptions: [{ value: "admin", label: "Admin" }, { value: "member", label: "Member" }] },
375
+ * { id: "signUps", header: "Sign-ups", accessor: "signUps", width: 120, type: "number", align: "right",
376
+ * renderCell: ({ value }) => <span className="tabular-nums">{Number(value).toLocaleString()}</span> },
377
+ * ], []);
378
+ *
379
+ * // 2. Grid state — one hook, initialized from the columns. NEVER build the state object by hand.
380
+ * const [gridState, setGridState] = React.useState(() => createDefaultDataGridState(columns));
381
+ *
382
+ * // 3. Data source — wires your raw array through the grid state. ALWAYS call this
383
+ * // hook unconditionally at the top level (no if/return before it).
384
+ * const gridData = useDataSource({
385
+ * data: users, // your raw array (can be [] while loading)
386
+ * columns,
387
+ * getRowId: (row) => row.id,
388
+ * sorting: gridState.sorting,
389
+ * quickSearch: gridState.quickSearch,
390
+ * pagination: gridState.pagination,
391
+ * paginationMode: "client", // "client" | "server" | "infinite"
392
+ * });
393
+ *
394
+ * // 4. Render — `rows` comes from gridData.rows, NOT from your raw array.
395
+ * <DataGrid
396
+ * columns={columns}
397
+ * rows={gridData.rows}
398
+ * getRowId={(row) => row.id}
399
+ * totalRowCount={gridData.totalRowCount}
400
+ * isLoading={gridData.isLoading}
401
+ * state={gridState}
402
+ * onChange={setGridState}
403
+ * selectionMode="none" // "none" | "single" | "multiple"
404
+ * maxHeight={480}
405
+ * />
406
+ * ```
407
+ *
408
+ * ## Iron rules (violating any of these breaks the grid)
409
+ *
410
+ * 1. The prop is `rows`, NOT `data`. There is no `data` prop on DataGrid.
411
+ * `data` belongs on `useDataSource`.
412
+ * 2. `rows` is ALWAYS `gridData.rows`. Never pass your raw array to
413
+ * `rows` — the grid won't search, sort, or paginate it.
414
+ * 3. Columns must be stable across renders. Define them outside the
415
+ * component or wrap in `React.useMemo`. A fresh columns array every
416
+ * render will reset sorting state.
417
+ * 4. Initialize grid state with `createDefaultDataGridState(columns)`.
418
+ * Do NOT spell out the state object manually — you will miss fields
419
+ * and crash.
420
+ * 5. `onChange` takes a `SetStateAction` (the setter you got from
421
+ * `useState`). Pass `setGridState` directly. Do NOT wrap it unless
422
+ * you know exactly what you're doing.
423
+ * 6. Call `useDataSource` ONCE per grid, at the top level, before any
424
+ * early return. It contains hooks.
425
+ * 7. `renderCell` is a PURE function of its context. NEVER call React
426
+ * hooks inside it (no `useState`, `useMemo`, `useEffect`, nothing).
427
+ * If you need derived data per row, compute it BEFORE the render —
428
+ * e.g. build a `Map<rowId, sparklineData>` in a `useMemo` and look
429
+ * it up in `renderCell`.
430
+ * 8. `toolbar` accepts `false` (hide it) or a render function
431
+ * `(ctx) => ReactNode`. Anything else — `true`, `undefined`, a state
432
+ * variable — will either show the default toolbar or crash. If you
433
+ * just want the default toolbar, omit the prop entirely.
434
+ * 9. The toolbar's search input writes to `state.quickSearch`. That
435
+ * value is consumed by `useDataSource` — client mode filters
436
+ * client-side, async mode forwards to the generator. Do NOT wire
437
+ * a separate "controlled" search prop, everything flows through
438
+ * grid state.
439
+ *
440
+ * ## renderCell — what you can and cannot do inside it
441
+ *
442
+ * ```tsx
443
+ * // OK — pure rendering from ctx:
444
+ * renderCell: ({ value }) => <span className="tabular-nums">{Number(value).toLocaleString()}</span>
445
+ * renderCell: ({ row }) => <Badge variant={row.active ? "default" : "outline"}>{row.status}</Badge>
446
+ *
447
+ * // OK — looking up pre-computed data by row id:
448
+ * // BEFORE the return, in the component body:
449
+ * const sparklinesById = React.useMemo(() => {
450
+ * const m = new Map();
451
+ * for (const u of users) {
452
+ * m.set(u.id, u.recentActivity.map((n, i) => ({ ts: i, values: { primary: n } })));
453
+ * }
454
+ * return m;
455
+ * }, [users]);
456
+ * // Then inside the column def:
457
+ * renderCell: ({ rowId }) => <MiniSparkline data={sparklinesById.get(rowId) ?? []} />
458
+ *
459
+ * // NOT OK — hooks inside renderCell:
460
+ * renderCell: ({ row }) => {
461
+ * const [hovered, setHovered] = React.useState(false); // ← crashes the grid
462
+ * const data = React.useMemo(() => ..., []); // ← crashes the grid
463
+ * return ...;
464
+ * }
465
+ *
466
+ * // NOT OK — embedding AnalyticsChart (or any other controlled, stateful chart) per row:
467
+ * // AnalyticsChart owns its own state, tooltips, zoom, and virtualized data
468
+ * // pipeline. Instantiating one per row is expensive and fights the grid's
469
+ * // virtualizer. Don't do it.
470
+ * ```
471
+ *
472
+ * ## Sparklines and mini-charts in cells — use raw Recharts
473
+ *
474
+ * If you want a tiny chart (sparkline, micro bar chart, trend line) inside
475
+ * a cell, drop down to raw `Recharts.*` components — they are lightweight
476
+ * and stateless, so they render cleanly per row without owning any state.
477
+ * Read pre-computed points off the row (or off a `Map<rowId, points>` you
478
+ * built in a `useMemo` above) and pass them directly to the Recharts
479
+ * primitive. Do NOT wrap them in `DesignChartContainer` or
480
+ * `DesignChartCard` inside a cell — those add chrome meant for full-size
481
+ * charts.
482
+ *
483
+ * ```tsx
484
+ * // OK — raw Recharts sparkline per row:
485
+ * renderCell: ({ rowId }) => {
486
+ * const points = sparklinesById.get(rowId) ?? [];
487
+ * return (
488
+ * <Recharts.ResponsiveContainer width="100%" height={28}>
489
+ * <Recharts.LineChart data={points} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
490
+ * <Recharts.Line type="monotone" dataKey="v" stroke="currentColor" strokeWidth={1.5} dot={false} isAnimationActive={false} />
491
+ * </Recharts.LineChart>
492
+ * </Recharts.ResponsiveContainer>
493
+ * );
494
+ * }
495
+ * ```
496
+ *
497
+ * Keep in-cell Recharts configs minimal: no axes, no tooltips, no animation
498
+ * (`isAnimationActive={false}`), tight margins, fixed height. The goal is a
499
+ * visual summary, not an interactive chart.
500
+ *
501
+ * ## State shape (from `createDefaultDataGridState`)
502
+ *
503
+ * ```ts
504
+ * {
505
+ * sorting: [], // { columnId, direction: "asc" | "desc" }[]
506
+ * quickSearch: "", // search input text
507
+ * dateDisplay: "relative", // "relative" | "absolute"
508
+ * columnVisibility: {}, columnWidths: {...},
509
+ * columnPinning: { left: [], right: [] }, columnOrder: [...],
510
+ * pagination: { pageIndex: 0, pageSize: 50 },
511
+ * selection: { selectedIds: new Set(), anchorId: null },
512
+ * }
513
+ * ```
514
+ *
515
+ * Everything is updated through `setGridState` — the toolbar, header,
516
+ * and footer all call it for you. You do not need to wire any of this
517
+ * manually.
518
+ *
519
+ * ## Height and scrolling
520
+ *
521
+ * DataGrid is NOT a card. It has no border, rounded corners, or shadow of
522
+ * its own. Wrap it in whatever chrome you want — a `DesignCard`, a section,
523
+ * or just raw layout. The grid itself fills its parent's height via
524
+ * `h-full`.
525
+ *
526
+ * How the grid gets its height (pick ONE):
527
+ * 1. Bounded parent — put the grid inside a flex/grid container with a
528
+ * definite height (e.g. `flex-1 min-h-0` inside a page-filling flex
529
+ * column). The grid stretches to that height and scrolls its body.
530
+ * 2. `maxHeight` prop — pass a number (pixels) or CSS string
531
+ * (`"480px"`, `"60vh"`, `"100%"`). The grid caps at that size and
532
+ * scrolls its body.
533
+ * 3. Unbounded — omit `maxHeight` and let the parent grow freely. The
534
+ * grid renders at its full content height and the page scrolls. Fine
535
+ * for small lists; bad UX for thousands of rows.
536
+ *
537
+ * The toolbar, header, and footer are always `shrink-0`; only the body
538
+ * scrolls. You do NOT need to subtract toolbar/footer heights from
539
+ * `maxHeight` — the grid's internal flex layout handles that.
540
+ *
541
+ * ## When to use what
542
+ *
543
+ * - Simple static list, < 20 rows, no interaction → use a plain table component instead.
544
+ * - Interactive table, sortable + searchable, any size → `DataGrid` +
545
+ * `useDataSource` with `paginationMode: "client"`.
546
+ * - Infinite scroll over a huge dataset you fetch in pages → `dataSource` async
547
+ * generator + `paginationMode: "infinite"`. Only reach for this if you actually
548
+ * need pagination over a remote source. For anything that fits in memory,
549
+ * `"client"` is simpler and faster.
550
+ *
551
+ * ## Features you get for free
552
+ *
553
+ * Quick search, sortable columns (shift-click for multi-sort), column
554
+ * visibility toggle, column resize, CSV export, virtualized rendering
555
+ * for 10k+ rows, keyboard navigation, and a relative/absolute date
556
+ * toggle for `date` / `dateTime` columns.
557
+ */
558
+ function DataGrid(props) {
559
+ const { columns: allColumns, rows, getRowId, totalRowCount, isLoading = false, isRefetching = false, hasMore = false, isLoadingMore = false, onLoadMore, state, onChange, paginationMode = "paginated", selectionMode = "none", resizable = true, rowHeight = 44, headerHeight = 44, overscan = 5, maxHeight, toolbar, toolbarExtra, emptyState, loadingState, footer, footerExtra, exportFilename = "export", strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange } = props;
560
+ const strings = (0, react.useMemo)(() => (0, __strings_js.resolveDataGridStrings)(stringsOverride), [stringsOverride]);
561
+ const visibleColumns = (0, react.useMemo)(() => (state.columnOrder.length > 0 ? state.columnOrder.map((id) => allColumns.find((c) => c.id === id)).filter(Boolean) : allColumns).filter((col) => (0, __state_js.isColumnVisible)(col.id, state.columnVisibility)), [
562
+ allColumns,
563
+ state.columnOrder,
564
+ state.columnVisibility
565
+ ]);
566
+ const rowIds = (0, react.useMemo)(() => rows.map(getRowId), [rows, getRowId]);
567
+ const visibleColumnMetrics = (0, react.useMemo)(() => {
568
+ const widths = /* @__PURE__ */ new Map();
569
+ let totalWidth = selectionMode !== "none" ? 44 : 0;
570
+ for (const col of visibleColumns) {
571
+ const width = (0, __state_js.resolveColumnWidth)(col, state.columnWidths[col.id]);
572
+ widths.set(col.id, width);
573
+ totalWidth += width;
574
+ }
575
+ return {
576
+ widths,
577
+ totalWidth
578
+ };
579
+ }, [
580
+ selectionMode,
581
+ state.columnWidths,
582
+ visibleColumns
583
+ ]);
584
+ const gridSizingStyle = (0, react.useMemo)(() => (0, __data_grid_sizing_js.createGridSizingStyle)(visibleColumnMetrics.widths, visibleColumnMetrics.totalWidth), [visibleColumnMetrics]);
585
+ const resizeRef = (0, react.useRef)(null);
586
+ const gridRef = (0, react.useRef)(null);
587
+ const handleSort = (0, react.useCallback)((columnId, multi) => {
588
+ onChange((s) => {
589
+ const next = (0, __state_js.toggleSort)(s.sorting, columnId, multi);
590
+ onSortChange?.(next);
591
+ return {
592
+ ...s,
593
+ sorting: next
594
+ };
595
+ });
596
+ }, [onChange, onSortChange]);
597
+ const handleResize = (0, react.useCallback)((columnId, delta) => {
598
+ const col = allColumns.find((c) => c.id === columnId);
599
+ if (!col) return;
600
+ if (!resizeRef.current || resizeRef.current.columnId !== columnId) {
601
+ const baseWidth = visibleColumnMetrics.widths.get(columnId) ?? (0, __state_js.resolveColumnWidth)(col, state.columnWidths[columnId]);
602
+ resizeRef.current = {
603
+ columnId,
604
+ baseWidth,
605
+ baseTotalWidth: visibleColumnMetrics.totalWidth,
606
+ latestWidth: baseWidth
607
+ };
608
+ }
609
+ const newWidth = (0, __data_grid_sizing_js.clampColumnWidth)(col, resizeRef.current.baseWidth + delta);
610
+ resizeRef.current.latestWidth = newWidth;
611
+ if (gridRef.current) (0, __data_grid_sizing_js.applyDraggedColumnWidth)(gridRef.current, columnId, newWidth, resizeRef.current.baseTotalWidth + (newWidth - resizeRef.current.baseWidth));
612
+ }, [
613
+ allColumns,
614
+ state.columnWidths,
615
+ visibleColumnMetrics
616
+ ]);
617
+ (0, react.useLayoutEffect)(() => {
618
+ const r = resizeRef.current;
619
+ if (r && gridRef.current) (0, __data_grid_sizing_js.applyDraggedColumnWidth)(gridRef.current, r.columnId, r.latestWidth, r.baseTotalWidth + (r.latestWidth - r.baseWidth));
620
+ }, [gridSizingStyle]);
621
+ const handleResizeEnd = (0, react.useCallback)(() => {
622
+ const r = resizeRef.current;
623
+ resizeRef.current = null;
624
+ if (!r || r.latestWidth === r.baseWidth) return;
625
+ onChange((s) => ({
626
+ ...s,
627
+ columnWidths: {
628
+ ...s.columnWidths,
629
+ [r.columnId]: r.latestWidth
630
+ }
631
+ }));
632
+ }, [onChange]);
633
+ const handleRowClick = (0, react.useCallback)((row, rowId, event) => {
634
+ if (selectionMode !== "none") onChange((s) => {
635
+ const next = (0, __state_js.toggleRowSelection)(s.selection, rowId, selectionMode, event.shiftKey, event.metaKey || event.ctrlKey, rowIds);
636
+ if (onSelectionChange) {
637
+ const selectedRows = rows.filter((r) => next.selectedIds.has(getRowId(r)));
638
+ setTimeout(() => onSelectionChange(next.selectedIds, selectedRows), 0);
639
+ }
640
+ return {
641
+ ...s,
642
+ selection: next
643
+ };
644
+ });
645
+ onRowClick?.(row, rowId, event);
646
+ }, [
647
+ selectionMode,
648
+ onChange,
649
+ onRowClick,
650
+ onSelectionChange,
651
+ rowIds,
652
+ rows,
653
+ getRowId
654
+ ]);
655
+ const handleRowSelectionCheckboxClick = (0, react.useCallback)((row, rowId, event) => {
656
+ handleRowClick(row, rowId, event);
657
+ }, [handleRowClick]);
658
+ const handleSelectAll = (0, react.useCallback)(() => {
659
+ onChange((s) => {
660
+ const allSelected = rowIds.every((id) => s.selection.selectedIds.has(id));
661
+ const next = allSelected ? (0, __state_js.clearSelection)() : (0, __state_js.selectAll)(rowIds);
662
+ if (onSelectionChange) {
663
+ const selectedRows = allSelected ? [] : rows;
664
+ setTimeout(() => onSelectionChange(next.selectedIds, [...selectedRows]), 0);
665
+ }
666
+ return {
667
+ ...s,
668
+ selection: next
669
+ };
670
+ });
671
+ }, [
672
+ onChange,
673
+ rowIds,
674
+ rows,
675
+ onSelectionChange
676
+ ]);
677
+ const handleExportCsv = (0, react.useCallback)(() => {
678
+ (0, __state_js.exportToCsv)(rows, visibleColumns, exportFilename);
679
+ }, [
680
+ rows,
681
+ visibleColumns,
682
+ exportFilename
683
+ ]);
684
+ const scrollContainerRef = (0, react.useRef)(null);
685
+ const headerScrollRef = (0, react.useRef)(null);
686
+ const rowVirtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
687
+ count: rows.length,
688
+ getScrollElement: () => scrollContainerRef.current,
689
+ estimateSize: () => rowHeight,
690
+ overscan
691
+ });
692
+ const handleBodyScroll = (0, react.useCallback)(() => {
693
+ const body = scrollContainerRef.current;
694
+ const header = headerScrollRef.current;
695
+ if (body && header) header.scrollLeft = body.scrollLeft;
696
+ }, []);
697
+ const toolbarCtx = (0, react.useMemo)(() => ({
698
+ state,
699
+ onChange,
700
+ columns: allColumns,
701
+ visibleColumns,
702
+ totalRowCount,
703
+ selectedRowCount: state.selection.selectedIds.size,
704
+ strings,
705
+ exportCsv: handleExportCsv
706
+ }), [
707
+ state,
708
+ onChange,
709
+ allColumns,
710
+ visibleColumns,
711
+ totalRowCount,
712
+ strings,
713
+ handleExportCsv
714
+ ]);
715
+ const footerCtx = (0, react.useMemo)(() => ({
716
+ state,
717
+ totalRowCount,
718
+ visibleRowCount: rows.length,
719
+ selectedRowCount: state.selection.selectedIds.size,
720
+ paginationMode,
721
+ strings
722
+ }), [
723
+ state,
724
+ totalRowCount,
725
+ rows.length,
726
+ paginationMode,
727
+ strings
728
+ ]);
729
+ const allSelected = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));
730
+ const someSelected = !allSelected && rowIds.some((id) => state.selection.selectedIds.has(id));
731
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
732
+ ref: gridRef,
733
+ className: (0, _stackframe_stack_ui.cn)("flex flex-col h-full min-h-0 bg-transparent", className),
734
+ style: maxHeight != null ? {
735
+ ...gridSizingStyle,
736
+ maxHeight
737
+ } : gridSizingStyle,
738
+ role: "grid",
739
+ "aria-rowcount": totalRowCount ?? rows.length,
740
+ "aria-colcount": visibleColumns.length,
741
+ children: [
742
+ toolbar !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
743
+ className: "relative shrink-0 bg-transparent",
744
+ children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__data_grid_toolbar_js.DataGridToolbar, {
745
+ ctx: toolbarCtx,
746
+ extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
747
+ })
748
+ }),
749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
750
+ className: "relative flex min-h-0 flex-1 flex-col",
751
+ children: [
752
+ isRefetching && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
753
+ className: "absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden",
754
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" })
755
+ }),
756
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
757
+ ref: headerScrollRef,
758
+ className: "overflow-hidden shrink-0 border-b border-foreground/[0.06]",
759
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
760
+ className: "flex",
761
+ style: {
762
+ height: headerHeight,
763
+ minWidth: visibleColumnMetrics.totalWidth
764
+ },
765
+ role: "row",
766
+ children: [selectionMode !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
767
+ className: "flex items-center justify-center border-r border-foreground/[0.04]",
768
+ style: { width: 44 },
769
+ children: selectionMode === "multiple" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectionCheckbox, {
770
+ checked: allSelected,
771
+ indeterminate: someSelected,
772
+ onChange: handleSelectAll,
773
+ ariaLabel: "Select all rows"
774
+ })
775
+ }), visibleColumns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeaderCell, {
776
+ col,
777
+ isSorted: (0, __state_js.getSortDirection)(state.sorting, col.id),
778
+ sortIndex: (0, __state_js.getSortIndex)(state.sorting, col.id),
779
+ resizable,
780
+ onSort: handleSort,
781
+ onResize: handleResize,
782
+ onResizeEnd: handleResizeEnd
783
+ }, col.id))]
784
+ })
785
+ }),
786
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
787
+ ref: scrollContainerRef,
788
+ className: (0, _stackframe_stack_ui.cn)("min-h-0 overflow-auto flex-1 bg-transparent", "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full", "[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]"),
789
+ onScroll: handleBodyScroll,
790
+ children: [
791
+ isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
792
+ style: { minWidth: visibleColumnMetrics.totalWidth },
793
+ children: loadingState ?? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkeletonRow, {
794
+ columns: visibleColumns,
795
+ height: rowHeight,
796
+ showCheckbox: selectionMode !== "none"
797
+ }, i))
798
+ }),
799
+ !isLoading && rows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
800
+ className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
801
+ style: { minWidth: visibleColumnMetrics.totalWidth },
802
+ children: emptyState ?? strings.noData
803
+ }),
804
+ !isLoading && rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
805
+ style: {
806
+ height: rowVirtualizer.getTotalSize(),
807
+ width: "100%",
808
+ minWidth: visibleColumnMetrics.totalWidth,
809
+ position: "relative"
810
+ },
811
+ children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
812
+ const row = rows[virtualRow.index];
813
+ const rowId = getRowId(row);
814
+ const isSelected = state.selection.selectedIds.has(rowId);
815
+ const isOddRow = virtualRow.index % 2 === 1;
816
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
817
+ className: (0, _stackframe_stack_ui.cn)("absolute left-0 w-full flex", "border-b border-black/[0.03] dark:border-white/[0.03]", "transition-colors duration-75", isSelected ? "bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]" : isOddRow ? "bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]" : "hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]", selectionMode !== "none" && "cursor-pointer"),
818
+ style: {
819
+ height: rowHeight,
820
+ transform: `translateY(${virtualRow.start}px)`
821
+ },
822
+ onClick: (e) => handleRowClick(row, rowId, e),
823
+ onDoubleClick: (e) => onRowDoubleClick?.(row, rowId, e),
824
+ role: "row",
825
+ "aria-rowindex": virtualRow.index + 2,
826
+ "aria-selected": isSelected,
827
+ "data-row-id": rowId,
828
+ "data-state": isSelected ? "selected" : void 0,
829
+ children: [selectionMode !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
830
+ className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
831
+ style: { width: 44 },
832
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectionCheckbox, {
833
+ checked: isSelected,
834
+ onChange: (event) => handleRowSelectionCheckboxClick(row, rowId, event),
835
+ ariaLabel: `Select row ${rowId}`
836
+ })
837
+ }), visibleColumns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataCell, {
838
+ col,
839
+ row,
840
+ rowId,
841
+ rowIndex: virtualRow.index,
842
+ isSelected,
843
+ dateDisplay: state.dateDisplay
844
+ }, col.id))]
845
+ }, rowId);
846
+ })
847
+ }),
848
+ paginationMode === "infinite" && hasMore && !isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfiniteScrollSentinel, {
849
+ onIntersect: onLoadMore ?? (() => {}),
850
+ isLoading: isLoadingMore,
851
+ strings
852
+ })
853
+ ]
854
+ })
855
+ ]
856
+ }),
857
+ footer !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
858
+ className: "relative z-10 shrink-0 bg-transparent",
859
+ children: [footer ? footer(footerCtx) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultFooter, {
860
+ ctx: footerCtx,
861
+ pagination: paginationMode,
862
+ onChange
863
+ }), footerExtra && (typeof footerExtra === "function" ? footerExtra(footerCtx) : footerExtra)]
864
+ })
865
+ ]
866
+ });
867
+ }
868
+
869
+ //#endregion
870
+ exports.DataGrid = DataGrid;
871
+ //# sourceMappingURL=data-grid.js.map