@crispy-seed/data-table 0.1.0 → 0.1.2

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 (51) hide show
  1. package/README.md +25 -13
  2. package/dist/index.cjs +4563 -2
  3. package/dist/index.d.ts +8 -1
  4. package/dist/index.js +4547 -1
  5. package/package.json +1 -2
  6. package/dist/DataTable-12s-8LMBtS2u.cjs +0 -1532
  7. package/dist/DataTable-12s-Bd20OuMs.js +0 -1482
  8. package/dist/DataTable-12s-Zml1x9Kr-12s-CTRzgvMT.cjs +0 -161
  9. package/dist/DataTable-12s-Zml1x9Kr-12s-Coi-3lUa.js +0 -153
  10. package/dist/Menu-12s-Db_zr1AP.cjs +0 -104
  11. package/dist/Menu-12s-Di5JADW4.js +0 -78
  12. package/dist/Select-12s-1Uh7XbkG.js +0 -219
  13. package/dist/Select-12s-D8DdKT-U.cjs +0 -250
  14. package/dist/body-cells-12s-BnOVUTKN.js +0 -453
  15. package/dist/body-cells-12s-BoOTcg-l.cjs +0 -462
  16. package/dist/column-calc-12s-C6IPSOaF.js +0 -129
  17. package/dist/column-calc-12s-CKuSpe-l.cjs +0 -149
  18. package/dist/column-layout-12s-CTu5yT_D.cjs +0 -120
  19. package/dist/column-layout-12s-s7wnxkMN.js +0 -99
  20. package/dist/column-menu-12s-BPE9N1gs.cjs +0 -427
  21. package/dist/column-menu-12s-mRBk-4oa.js +0 -407
  22. package/dist/editors-12s-BO9Mp034.js +0 -525
  23. package/dist/editors-12s-DO8TZkYV.cjs +0 -555
  24. package/dist/resize-12s-jQY-PX67.cjs +0 -58
  25. package/dist/resize-12s-nsMvrmLt.js +0 -58
  26. package/dist/states-12s-CLO_uVue.cjs +0 -125
  27. package/dist/states-12s-DEFSxJCO.js +0 -122
  28. package/dist/toolbar-12s-AVU-sbDW.js +0 -69
  29. package/dist/toolbar-12s-CaLgJrDo.cjs +0 -71
  30. package/dist/useDataTable-12s-BHiDUubw-12s-CBeCwTe7.js +0 -61
  31. package/dist/useDataTable-12s-BHiDUubw-12s-Dgvt7XDk.cjs +0 -61
  32. package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-CvD14JYo.cjs +0 -207
  33. package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-DsEse3uq.js +0 -183
  34. package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CD6goR59.js +0 -289
  35. package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CUk_CpYT.cjs +0 -292
  36. package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-BoOHq3GB.js +0 -98
  37. package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-De6YHBba.cjs +0 -98
  38. package/dist/useDataTableFilter-12s-ZzbmWMin-12s-CWyBeFna.js +0 -74
  39. package/dist/useDataTableFilter-12s-ZzbmWMin-12s-lpjzZY3_.cjs +0 -74
  40. package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-BexjP6iO.js +0 -104
  41. package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-Bt8gojr9.cjs +0 -104
  42. package/dist/useDataTableRow-12s-CPYaDa1p-12s-BmUSAnPe.js +0 -30
  43. package/dist/useDataTableRow-12s-CPYaDa1p-12s-D-DRjwAe.cjs +0 -30
  44. package/dist/useDataTableScroll-12s-BKDCr0en-12s-C7f2vHeG.js +0 -61
  45. package/dist/useDataTableScroll-12s-BKDCr0en-12s-CUvUYMtt.cjs +0 -61
  46. package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-B-65V6gH.js +0 -37
  47. package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-DhVfN2Hq.cjs +0 -37
  48. package/dist/useDataTableSelection-12s-BIDtiRQq-12s-IS__VAnU.cjs +0 -71
  49. package/dist/useDataTableSelection-12s-BIDtiRQq-12s-O6M_BhrH.js +0 -71
  50. package/dist/useDataTableSort-12s-DvJYtmeS-12s-C5VGnsq4.cjs +0 -78
  51. package/dist/useDataTableSort-12s-DvJYtmeS-12s-DABSNt6U.js +0 -78
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  function __insertCSS(code) {
2
3
  if (!code || typeof document == 'undefined') return
3
4
  let head = document.head || document.getElementsByTagName('head')[0]
@@ -7,8 +8,4568 @@ function __insertCSS(code) {
7
8
  ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
8
9
  }
9
10
 
10
- var DataTable12s = require('./DataTable-12s-8LMBtS2u.cjs');
11
+ var jsxRuntime = require('react/jsx-runtime');
12
+ var React = require('react');
13
+ var domUtils = require('@seed-design/dom-utils');
14
+ var checkbox = require('@seed-design/css/recipes/checkbox');
15
+ var checkmark = require('@seed-design/css/recipes/checkmark');
16
+ var reactCheckbox = require('@seed-design/react-checkbox');
17
+ var reactScaleFeedback = require('@seed-design/react-scale-feedback');
18
+ var checkboxGroup = require('@seed-design/css/recipes/checkbox-group');
19
+ var reactImage = require('@seed-design/react-image');
20
+ var avatar = require('@seed-design/css/recipes/avatar');
21
+ require('@seed-design/css/recipes/avatar-stack');
22
+ var badge = require('@seed-design/css/recipes/badge');
23
+ var reactSelect = require('@seed-design/react-select');
24
+ var reactField = require('@seed-design/react-field');
25
+ var select = require('@seed-design/css/recipes/select');
26
+ var selectTrigger = require('@seed-design/css/recipes/select-trigger');
27
+ var selectItem = require('@seed-design/css/recipes/select-item');
28
+ var reactSwitch = require('@seed-design/react-switch');
29
+ var _switch = require('@seed-design/css/recipes/switch');
30
+ var switchmark = require('@seed-design/css/recipes/switchmark');
31
+ var reactTextField = require('@seed-design/react-text-field');
32
+ var textInput = require('@seed-design/css/recipes/text-input');
33
+ var reactMenu = require('@seed-design/react-menu');
34
+ var menu = require('@seed-design/css/recipes/menu');
35
+ var menuItem = require('@seed-design/css/recipes/menu-item');
36
+ var segmentedControl = require('@seed-design/css/recipes/segmented-control');
37
+ var reactSegmentedControl = require('@seed-design/react-segmented-control');
11
38
 
39
+ function _interopNamespace(e) {
40
+ if (e && e.__esModule) return e;
41
+ var n = Object.create(null);
42
+ if (e) {
43
+ Object.keys(e).forEach(function (k) {
44
+ if (k !== 'default') {
45
+ var d = Object.getOwnPropertyDescriptor(e, k);
46
+ Object.defineProperty(n, k, d.get ? d : {
47
+ enumerable: true,
48
+ get: function () { return e[k]; }
49
+ });
50
+ }
51
+ });
52
+ }
53
+ n.default = e;
54
+ return n;
55
+ }
56
+
57
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
58
+
59
+ // src/IconChevronDownLine.tsx
60
+ var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
61
+ viewBox: "0 0 24 24",
62
+ fill: "none",
63
+ xmlns: "http://www.w3.org/2000/svg",
64
+ "data-seed-icon": "true",
65
+ width: size,
66
+ height: size,
67
+ ref,
68
+ ...props,
69
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
70
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
71
+ fillRule: "evenodd",
72
+ clipRule: "evenodd",
73
+ d: "M21.2612 7.27589C20.8613 6.89499 20.2283 6.9104 19.8474 7.31032L11.9996 15.55L4.15284 7.31037C3.77196 6.91043 3.13899 6.89497 2.73904 7.27584C2.3391 7.65671 2.32364 8.28969 2.70451 8.68963L11.2754 17.6896C11.4641 17.8878 11.7258 18 11.9995 18C12.2732 18 12.5349 17.8879 12.7236 17.6897L21.2956 8.68968C21.6765 8.28976 21.6611 7.65679 21.2612 7.27589Z",
74
+ fill: "currentColor"
75
+ })
76
+ })
77
+ });
78
+ var IconChevronDownLine = /*#__PURE__*/ React.forwardRef(Icon$a);
79
+ var IconChevronDownLine_default = IconChevronDownLine;
80
+
81
+ // src/IconChevronUpLine.tsx
82
+ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
83
+ viewBox: "0 0 24 24",
84
+ fill: "none",
85
+ xmlns: "http://www.w3.org/2000/svg",
86
+ "data-seed-icon": "true",
87
+ width: size,
88
+ height: size,
89
+ ref,
90
+ ...props,
91
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
92
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
93
+ fillRule: "evenodd",
94
+ clipRule: "evenodd",
95
+ d: "M21.2612 16.7241C20.8613 17.105 20.2283 17.0896 19.8474 16.6897L11.9996 8.45L4.15284 16.6896C3.77196 17.0896 3.13899 17.105 2.73904 16.7242C2.3391 16.3433 2.32364 15.7103 2.70451 15.3104L11.2754 6.31037C11.4641 6.11218 11.7258 6.00001 11.9995 6C12.2732 5.99999 12.5349 6.11215 12.7236 6.31032L21.2956 15.3103C21.6765 15.7102 21.6611 16.3432 21.2612 16.7241Z",
96
+ fill: "currentColor"
97
+ })
98
+ })
99
+ });
100
+ var IconChevronUpLine = /*#__PURE__*/ React.forwardRef(Icon$9);
101
+ var IconChevronUpLine_default = IconChevronUpLine;
102
+
103
+ // src/IconDot3HorizontalLine.tsx
104
+ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
105
+ viewBox: "0 0 24 24",
106
+ fill: "none",
107
+ xmlns: "http://www.w3.org/2000/svg",
108
+ "data-seed-icon": "true",
109
+ width: size,
110
+ height: size,
111
+ ref,
112
+ ...props,
113
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
114
+ children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
115
+ children: [
116
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
117
+ d: "M4.625 10.375C3.72754 10.375 3 11.1025 3 12C3 12.8975 3.72754 13.625 4.625 13.625C5.52246 13.625 6.25 12.8975 6.25 12C6.25 11.1025 5.52246 10.375 4.625 10.375Z",
118
+ fill: "currentColor"
119
+ }),
120
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
121
+ d: "M12.075 10.375C11.1775 10.375 10.45 11.1025 10.45 12C10.45 12.8975 11.1775 13.625 12.075 13.625C12.9724 13.625 13.7 12.8975 13.7 12C13.7 11.1025 12.9724 10.375 12.075 10.375Z",
122
+ fill: "currentColor"
123
+ }),
124
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
125
+ d: "M19.375 10.375C18.4775 10.375 17.75 11.1025 17.75 12C17.75 12.8975 18.4775 13.625 19.375 13.625C20.2725 13.625 21 12.8975 21 12C21 11.1025 20.2725 10.375 19.375 10.375Z",
126
+ fill: "currentColor"
127
+ })
128
+ ]
129
+ })
130
+ })
131
+ });
132
+ var IconDot3HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$8);
133
+ var IconDot3HorizontalLine_default = IconDot3HorizontalLine;
134
+
135
+ // src/IconMagnifyingglassLine.tsx
136
+ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
137
+ viewBox: "0 0 24 24",
138
+ fill: "none",
139
+ xmlns: "http://www.w3.org/2000/svg",
140
+ "data-seed-icon": "true",
141
+ width: size,
142
+ height: size,
143
+ ref,
144
+ ...props,
145
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
146
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
147
+ fillRule: "evenodd",
148
+ clipRule: "evenodd",
149
+ d: "M11.3111 1.2002C6.00313 1.2002 1.69995 5.49902 1.69995 10.8048V10.8178C1.69995 16.1236 6.00313 20.4224 11.3111 20.4224C13.5994 20.4224 15.711 19.6233 17.3631 18.2785L21.493 22.4083C21.8835 22.7988 22.5167 22.7988 22.9072 22.4083C23.2977 22.0177 23.2977 21.3846 22.9072 20.9941L18.777 16.864C20.1196 15.2095 20.9222 13.0996 20.9222 10.8048C20.9222 5.49902 16.619 1.2002 11.3111 1.2002ZM3.69995 10.8048C3.69995 6.60502 7.10627 3.2002 11.3111 3.2002C15.5159 3.2002 18.9222 6.60502 18.9222 10.8048C18.9222 13.0794 17.9298 15.1209 16.3465 16.5182L16.3446 16.52C15.0067 17.707 13.2419 18.4224 11.3111 18.4224C7.10627 18.4224 3.69995 15.0176 3.69995 10.8178V10.8048Z",
150
+ fill: "currentColor"
151
+ })
152
+ })
153
+ });
154
+ var IconMagnifyingglassLine = /*#__PURE__*/ React.forwardRef(Icon$7);
155
+ var IconMagnifyingglassLine_default = IconMagnifyingglassLine;
156
+
157
+ // src/IconSlider2HorizontalLine.tsx
158
+ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
159
+ viewBox: "0 0 24 24",
160
+ fill: "none",
161
+ xmlns: "http://www.w3.org/2000/svg",
162
+ "data-seed-icon": "true",
163
+ width: size,
164
+ height: size,
165
+ ref,
166
+ ...props,
167
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
168
+ children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
169
+ children: [
170
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
171
+ fillRule: "evenodd",
172
+ clipRule: "evenodd",
173
+ d: "M9.99331 7.24609H3C2.44772 7.24609 2 6.79838 2 6.24609C2 5.69381 2.44772 5.24609 3 5.24609H9.99331C10.443 3.38141 12.1222 1.99609 14.125 1.99609C16.1278 1.99609 17.807 3.38141 18.2567 5.24609H21C21.5523 5.24609 22 5.69381 22 6.24609C22 6.79838 21.5523 7.24609 21 7.24609H18.2567C17.807 9.11078 16.1278 10.4961 14.125 10.4961C12.1222 10.4961 10.443 9.11078 9.99331 7.24609ZM11.875 6.24609C11.875 5.00345 12.8824 3.99609 14.125 3.99609C15.3676 3.99609 16.375 5.00345 16.375 6.24609C16.375 7.48873 15.3676 8.49609 14.125 8.49609C12.8824 8.49609 11.875 7.48873 11.875 6.24609Z",
174
+ fill: "currentColor"
175
+ }),
176
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
177
+ fillRule: "evenodd",
178
+ clipRule: "evenodd",
179
+ d: "M6.25 13.4956C3.90279 13.4956 2 15.3984 2 17.7456C2 20.0928 3.90279 21.9956 6.25 21.9956C8.25016 21.9956 9.92761 20.6139 10.3799 18.7529L20.9996 18.7456C21.5518 18.7452 21.9993 18.2972 21.9989 17.7449C21.9985 17.1926 21.5505 16.7452 20.9982 16.7456L10.3834 16.7529C9.93635 14.8845 8.25537 13.4956 6.25 13.4956ZM4 17.7456C4 16.503 5.00736 15.4956 6.25 15.4956C7.49264 15.4956 8.5 16.503 8.5 17.7456C8.5 18.9882 7.49264 19.9956 6.25 19.9956C5.00736 19.9956 4 18.9882 4 17.7456Z",
180
+ fill: "currentColor"
181
+ })
182
+ ]
183
+ })
184
+ })
185
+ });
186
+ var IconSlider2HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$6);
187
+ var IconSlider2HorizontalLine_default = IconSlider2HorizontalLine;
188
+
189
+ // src/IconPersonCircleLine.tsx
190
+ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
191
+ viewBox: "0 0 24 24",
192
+ fill: "none",
193
+ xmlns: "http://www.w3.org/2000/svg",
194
+ "data-seed-icon": "true",
195
+ width: size,
196
+ height: size,
197
+ ref,
198
+ ...props,
199
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
200
+ children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
201
+ children: [
202
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
203
+ fillRule: "evenodd",
204
+ clipRule: "evenodd",
205
+ d: "M12 5.5C9.79172 5.5 8 7.29172 8 9.5C8 11.7083 9.79172 13.5 12 13.5C14.2083 13.5 16 11.7083 16 9.5C16 7.29172 14.2083 5.5 12 5.5ZM10 9.5C10 8.39628 10.8963 7.5 12 7.5C13.1037 7.5 14 8.39628 14 9.5C14 10.6037 13.1037 11.5 12 11.5C10.8963 11.5 10 10.6037 10 9.5Z",
206
+ fill: "currentColor"
207
+ }),
208
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
209
+ fillRule: "evenodd",
210
+ clipRule: "evenodd",
211
+ d: "M1 12C1 5.92772 5.92772 1 12 1C18.0723 1 23 5.92772 23 12C23 18.0723 18.0723 23 12 23C5.92772 23 1 18.0723 1 12ZM12 3C7.03229 3 3 7.03229 3 12C3 14.3609 3.91073 16.5105 5.39977 18.1164C5.87422 17.4277 6.5536 16.8564 7.3236 16.4211C8.60413 15.6971 10.2434 15.2793 12.0002 15.2793C13.757 15.2793 15.3962 15.6971 16.6768 16.4211C17.4467 16.8564 18.126 17.4276 18.6004 18.1162C20.0894 16.5103 21 14.3608 21 12C21 7.03229 16.9677 3 12 3ZM12 21C10.1211 21 8.3761 20.4232 6.93241 19.4372C7.17777 18.9952 7.62665 18.5473 8.30791 18.1621C9.25286 17.6279 10.5454 17.2793 12.0002 17.2793C13.455 17.2793 14.7475 17.6279 15.6925 18.1621C16.3736 18.5472 16.8225 18.9951 17.0679 19.437C15.6241 20.4231 13.879 21 12 21Z",
212
+ fill: "currentColor"
213
+ })
214
+ ]
215
+ })
216
+ })
217
+ });
218
+ var IconPersonCircleLine = /*#__PURE__*/ React.forwardRef(Icon$5);
219
+ var IconPersonCircleLine_default = IconPersonCircleLine;
220
+
221
+ // src/IconPlusLine.tsx
222
+ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
223
+ viewBox: "0 0 24 24",
224
+ fill: "none",
225
+ xmlns: "http://www.w3.org/2000/svg",
226
+ "data-seed-icon": "true",
227
+ width: size,
228
+ height: size,
229
+ ref,
230
+ ...props,
231
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
232
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
233
+ d: "M4 13H11V20C11 20.55 11.45 21 12 21C12.55 21 13 20.55 13 20V13H20C20.55 13 21 12.55 21 12C21 11.45 20.55 11 20 11H13V4C13 3.45 12.55 3 12 3C11.45 3 11 3.45 11 4V11H4C3.45 11 3 11.45 3 12C3 12.55 3.45 13 4 13Z",
234
+ fill: "currentColor"
235
+ })
236
+ })
237
+ });
238
+ var IconPlusLine = /*#__PURE__*/ React.forwardRef(Icon$4);
239
+ var IconPlusLine_default = IconPlusLine;
240
+
241
+ // src/IconMinusLine.tsx
242
+ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
243
+ viewBox: "0 0 24 24",
244
+ fill: "none",
245
+ xmlns: "http://www.w3.org/2000/svg",
246
+ "data-seed-icon": "true",
247
+ width: size,
248
+ height: size,
249
+ ref,
250
+ ...props,
251
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
252
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
253
+ d: "M4 13H20C20.55 13 21 12.55 21 12C21 11.45 20.55 11 20 11H4C3.45 11 3 11.45 3 12C3 12.55 3.45 13 4 13Z",
254
+ fill: "currentColor"
255
+ })
256
+ })
257
+ });
258
+ var IconMinusLine = /*#__PURE__*/ React.forwardRef(Icon$3);
259
+ var IconMinusLine_default = IconMinusLine;
260
+
261
+ // src/IconCheckmarkLine.tsx
262
+ var Icon$2 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
263
+ viewBox: "0 0 24 24",
264
+ fill: "none",
265
+ xmlns: "http://www.w3.org/2000/svg",
266
+ "data-seed-icon": "true",
267
+ width: size,
268
+ height: size,
269
+ ref,
270
+ ...props,
271
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
272
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
273
+ fillRule: "evenodd",
274
+ clipRule: "evenodd",
275
+ d: "M22.1186 3.71436C22.5526 4.05602 22.6273 4.68476 22.2857 5.11868L10.4749 20.1187C10.2958 20.3461 10.0269 20.4848 9.73779 20.4989C9.44865 20.5129 9.1676 20.401 8.96728 20.192L1.77809 12.692C1.39592 12.2933 1.40931 11.6603 1.80801 11.2781C2.20671 10.896 2.83973 10.9094 3.22191 11.3081L9.61511 17.9777L20.7143 3.88141C21.056 3.44749 21.6847 3.3727 22.1186 3.71436Z",
276
+ fill: "currentColor"
277
+ })
278
+ })
279
+ });
280
+ var IconCheckmarkLine = /*#__PURE__*/ React.forwardRef(Icon$2);
281
+ var IconCheckmarkLine_default = IconCheckmarkLine;
282
+
283
+ // src/IconChevronDownSmallLine.tsx
284
+ var Icon$1 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
285
+ viewBox: "0 0 24 24",
286
+ fill: "none",
287
+ xmlns: "http://www.w3.org/2000/svg",
288
+ "data-seed-icon": "true",
289
+ width: size,
290
+ height: size,
291
+ ref,
292
+ ...props,
293
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
294
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
295
+ fillRule: "evenodd",
296
+ clipRule: "evenodd",
297
+ d: "M6.29436 9.29143C6.68569 8.90171 7.31886 8.90303 7.70857 9.29436L11.9848 13.5884L16.297 9.3256C16.6897 8.93733 17.3229 8.94097 17.7112 9.33374C18.0994 9.7265 18.0958 10.3597 17.703 10.7479L12.6823 15.7112C12.2906 16.0984 11.6594 16.096 11.2707 15.7056L6.29143 10.7056C5.90171 10.3143 5.90303 9.68114 6.29436 9.29143Z",
298
+ fill: "currentColor"
299
+ })
300
+ })
301
+ });
302
+ var IconChevronDownSmallLine = /*#__PURE__*/ React.forwardRef(Icon$1);
303
+ var IconChevronDownSmallLine_default = IconChevronDownSmallLine;
304
+
305
+ // src/IconCheckmarkFatFill.tsx
306
+ var Icon = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
307
+ viewBox: "0 0 24 24",
308
+ fill: "none",
309
+ xmlns: "http://www.w3.org/2000/svg",
310
+ "data-seed-icon": "true",
311
+ width: size,
312
+ height: size,
313
+ ref,
314
+ ...props,
315
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
316
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", {
317
+ d: "M22.5517 3.16422C21.8141 2.5834 20.7452 2.71054 20.1644 3.4482L9.56331 16.9118L3.72729 10.8235C3.07759 10.1457 2.00145 10.1229 1.32367 10.7726C0.645881 11.4223 0.623111 12.4985 1.27281 13.1763L8.462 20.6763C8.80254 21.0315 9.28032 21.2218 9.77186 21.1979C10.2634 21.174 10.7205 20.9382 11.0249 20.5516L22.8357 5.55155C23.4165 4.81389 23.2894 3.74505 22.5517 3.16422Z",
318
+ fill: "currentColor"
319
+ })
320
+ })
321
+ });
322
+ var IconCheckmarkFatFill = /*#__PURE__*/ React.forwardRef(Icon);
323
+ var IconCheckmarkFatFill_default = IconCheckmarkFatFill;
324
+
325
+ var __defProp$1 = Object.defineProperty;
326
+ var __name$1 = (target, value)=>__defProp$1(target, "name", {
327
+ value,
328
+ configurable: true
329
+ });
330
+ function setRef(ref, value) {
331
+ if (typeof ref === "function") {
332
+ return ref(value);
333
+ } else if (ref !== null && ref !== void 0) {
334
+ ref.current = value;
335
+ }
336
+ }
337
+ __name$1(setRef, "setRef");
338
+ function composeRefs(...refs) {
339
+ return (node)=>{
340
+ let hasCleanup = false;
341
+ const cleanups = refs.map((ref)=>{
342
+ const cleanup = setRef(ref, node);
343
+ if (!hasCleanup && typeof cleanup == "function") {
344
+ hasCleanup = true;
345
+ }
346
+ return cleanup;
347
+ });
348
+ if (hasCleanup) {
349
+ return ()=>{
350
+ for(let i = 0; i < cleanups.length; i++){
351
+ const cleanup = cleanups[i];
352
+ if (typeof cleanup == "function") {
353
+ cleanup();
354
+ } else {
355
+ setRef(refs[i], null);
356
+ }
357
+ }
358
+ };
359
+ }
360
+ };
361
+ }
362
+ __name$1(composeRefs, "composeRefs");
363
+ function useComposedRefs(...refs) {
364
+ return React__namespace.useCallback(composeRefs(...refs), refs);
365
+ }
366
+ __name$1(useComposedRefs, "useComposedRefs");
367
+
368
+ var __defProp = Object.defineProperty;
369
+ var __name = (target, value)=>__defProp(target, "name", {
370
+ value,
371
+ configurable: true
372
+ });
373
+ // @__NO_SIDE_EFFECTS__
374
+ function createSlot(ownerName) {
375
+ const Slot2 = /*#__PURE__*/ React__namespace.forwardRef((props, forwardedRef)=>{
376
+ let { children, ...slotProps } = props;
377
+ let slottableElement = null;
378
+ let hasSlottable = false;
379
+ const newChildren = [];
380
+ if (isLazyComponent(children) && typeof use === "function") {
381
+ children = use(children._payload);
382
+ }
383
+ React__namespace.Children.forEach(children, (maybeSlottable)=>{
384
+ if (isSlottable(maybeSlottable)) {
385
+ hasSlottable = true;
386
+ const slottable = maybeSlottable;
387
+ let child = "child" in slottable.props ? slottable.props.child : slottable.props.children;
388
+ if (isLazyComponent(child) && typeof use === "function") {
389
+ child = use(child._payload);
390
+ }
391
+ slottableElement = getSlottableElementFromSlottable(slottable, child);
392
+ newChildren.push(slottableElement?.props?.children);
393
+ } else {
394
+ newChildren.push(maybeSlottable);
395
+ }
396
+ });
397
+ if (slottableElement) {
398
+ slottableElement = /*#__PURE__*/ React__namespace.cloneElement(slottableElement, void 0, newChildren);
399
+ } else if (// A `Slottable` was found but it didn't resolve to a single element (e.g.
400
+ // it wrapped multiple elements, text, or a render-prop `child` that
401
+ // wasn't an element). Don't fall back to treating the `Slottable` wrapper
402
+ // itself as the slot target — throw a descriptive error below instead.
403
+ !hasSlottable && React__namespace.Children.count(children) === 1 && /*#__PURE__*/ React__namespace.isValidElement(children)) {
404
+ slottableElement = children;
405
+ }
406
+ const slottableElementRef = slottableElement ? getElementRef(slottableElement) : void 0;
407
+ const composedRef = useComposedRefs(forwardedRef, slottableElementRef);
408
+ if (!slottableElement) {
409
+ if (children || children === 0) {
410
+ throw new Error(hasSlottable ? createSlottableError(ownerName) : createSlotError(ownerName));
411
+ }
412
+ return children;
413
+ }
414
+ const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});
415
+ if (slottableElement.type !== React__namespace.Fragment) {
416
+ mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;
417
+ }
418
+ return /*#__PURE__*/ React__namespace.cloneElement(slottableElement, mergedProps);
419
+ });
420
+ Slot2.displayName = `${ownerName}.Slot`;
421
+ return Slot2;
422
+ }
423
+ __name(createSlot, "createSlot");
424
+ var Slot = /* @__PURE__ */ createSlot("Slot");
425
+ var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
426
+ // @__NO_SIDE_EFFECTS__
427
+ function createSlottable(ownerName) {
428
+ const Slottable2 = /* @__PURE__ */ __name((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
429
+ Slottable2.displayName = `${ownerName}.Slottable`;
430
+ Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
431
+ return Slottable2;
432
+ }
433
+ __name(createSlottable, "createSlottable");
434
+ var getSlottableElementFromSlottable = /* @__PURE__ */ __name((slottable, child)=>{
435
+ if ("child" in slottable.props) {
436
+ const child2 = slottable.props.child;
437
+ if (!/*#__PURE__*/ React__namespace.isValidElement(child2)) return null;
438
+ return /*#__PURE__*/ React__namespace.cloneElement(child2, void 0, slottable.props.children(child2.props.children));
439
+ }
440
+ return /*#__PURE__*/ React__namespace.isValidElement(child) ? child : null;
441
+ }, "getSlottableElementFromSlottable");
442
+ function mergeProps(slotProps, childProps) {
443
+ const overrideProps = {
444
+ ...childProps
445
+ };
446
+ for(const propName in childProps){
447
+ const slotPropValue = slotProps[propName];
448
+ const childPropValue = childProps[propName];
449
+ const isHandler = /^on[A-Z]/.test(propName);
450
+ if (isHandler) {
451
+ if (slotPropValue && childPropValue) {
452
+ overrideProps[propName] = (...args)=>{
453
+ const result = childPropValue(...args);
454
+ slotPropValue(...args);
455
+ return result;
456
+ };
457
+ } else if (slotPropValue) {
458
+ overrideProps[propName] = slotPropValue;
459
+ }
460
+ } else if (propName === "style") {
461
+ overrideProps[propName] = {
462
+ ...slotPropValue,
463
+ ...childPropValue
464
+ };
465
+ } else if (propName === "className") {
466
+ overrideProps[propName] = [
467
+ slotPropValue,
468
+ childPropValue
469
+ ].filter(Boolean).join(" ");
470
+ }
471
+ }
472
+ return {
473
+ ...slotProps,
474
+ ...overrideProps
475
+ };
476
+ }
477
+ __name(mergeProps, "mergeProps");
478
+ function getElementRef(element) {
479
+ let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
480
+ let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
481
+ if (mayWarn) {
482
+ return element.ref;
483
+ }
484
+ getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
485
+ mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
486
+ if (mayWarn) {
487
+ return element.props.ref;
488
+ }
489
+ return element.props.ref || element.ref;
490
+ }
491
+ __name(getElementRef, "getElementRef");
492
+ function isSlottable(child) {
493
+ return /*#__PURE__*/ React__namespace.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
494
+ }
495
+ __name(isSlottable, "isSlottable");
496
+ var REACT_LAZY_TYPE = Symbol.for("react.lazy");
497
+ function isLazyComponent(element) {
498
+ return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
499
+ }
500
+ __name(isLazyComponent, "isLazyComponent");
501
+ function isPromiseLike(value) {
502
+ return typeof value === "object" && value !== null && "then" in value;
503
+ }
504
+ __name(isPromiseLike, "isPromiseLike");
505
+ var createSlotError = /* @__PURE__ */ __name((ownerName)=>{
506
+ return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
507
+ }, "createSlotError");
508
+ var createSlottableError = /* @__PURE__ */ __name((ownerName)=>{
509
+ return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
510
+ }, "createSlottableError");
511
+ var use = React__namespace[" use ".trim().toString()];
512
+
513
+ function createPrimitive(node) {
514
+ const Node = /*#__PURE__*/ React__namespace.forwardRef((props, ref)=>{
515
+ const { asChild, ...primitiveProps } = props;
516
+ const Comp = asChild ? Slot : node;
517
+ return /*#__PURE__*/ jsxRuntime.jsx(Comp, {
518
+ ...primitiveProps,
519
+ ref: ref
520
+ });
521
+ });
522
+ Node.displayName = `Primitive.${node}`;
523
+ return Node;
524
+ }
525
+ const Primitive = {
526
+ div: createPrimitive("div"),
527
+ span: createPrimitive("span"),
528
+ img: createPrimitive("img"),
529
+ button: createPrimitive("button"),
530
+ label: createPrimitive("label"),
531
+ input: createPrimitive("input"),
532
+ textarea: createPrimitive("textarea"),
533
+ a: createPrimitive("a"),
534
+ p: createPrimitive("p"),
535
+ h2: createPrimitive("h2"),
536
+ ul: createPrimitive("ul"),
537
+ li: createPrimitive("li"),
538
+ nav: createPrimitive("nav"),
539
+ svg: createPrimitive("svg"),
540
+ circle: createPrimitive("circle"),
541
+ // Native table elements -- DataTable's own rendering (crispy-seed's addition, not
542
+ // upstream seed-design) needs these for a real `<table>` grid rather than a
543
+ // `display: flex` div grid faked with ARIA roles.
544
+ table: createPrimitive("table"),
545
+ thead: createPrimitive("thead"),
546
+ tbody: createPrimitive("tbody"),
547
+ tfoot: createPrimitive("tfoot"),
548
+ tr: createPrimitive("tr"),
549
+ th: createPrimitive("th"),
550
+ td: createPrimitive("td"),
551
+ colgroup: createPrimitive("colgroup"),
552
+ col: createPrimitive("col")
553
+ };
554
+
555
+ // packages/react/use-layout-effect/src/use-layout-effect.tsx
556
+ var useLayoutEffect2$1 = globalThis?.document ? React__namespace.useLayoutEffect : ()=>{};
557
+
558
+ // src/use-controllable-state.tsx
559
+ var useInsertionEffect = React__namespace[" useInsertionEffect ".trim().toString()] || useLayoutEffect2$1;
560
+ function useControllableState({ prop, defaultProp, onChange = ()=>{}, caller }) {
561
+ const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
562
+ defaultProp,
563
+ onChange
564
+ });
565
+ const isControlled = prop !== void 0;
566
+ const value = isControlled ? prop : uncontrolledProp;
567
+ {
568
+ const isControlledRef = React__namespace.useRef(prop !== void 0);
569
+ React__namespace.useEffect(()=>{
570
+ const wasControlled = isControlledRef.current;
571
+ if (wasControlled !== isControlled) {
572
+ const from = wasControlled ? "controlled" : "uncontrolled";
573
+ const to = isControlled ? "controlled" : "uncontrolled";
574
+ console.warn(`${caller} is changing from ${from} to ${to}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`);
575
+ }
576
+ isControlledRef.current = isControlled;
577
+ }, [
578
+ isControlled,
579
+ caller
580
+ ]);
581
+ }
582
+ const setValue = React__namespace.useCallback((nextValue)=>{
583
+ if (isControlled) {
584
+ const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
585
+ if (value2 !== prop) {
586
+ onChangeRef.current?.(value2);
587
+ }
588
+ } else {
589
+ setUncontrolledProp(nextValue);
590
+ }
591
+ }, [
592
+ isControlled,
593
+ prop,
594
+ setUncontrolledProp,
595
+ onChangeRef
596
+ ]);
597
+ return [
598
+ value,
599
+ setValue
600
+ ];
601
+ }
602
+ function useUncontrolledState({ defaultProp, onChange }) {
603
+ const [value, setValue] = React__namespace.useState(defaultProp);
604
+ const prevValueRef = React__namespace.useRef(value);
605
+ const onChangeRef = React__namespace.useRef(onChange);
606
+ useInsertionEffect(()=>{
607
+ onChangeRef.current = onChange;
608
+ }, [
609
+ onChange
610
+ ]);
611
+ React__namespace.useEffect(()=>{
612
+ if (prevValueRef.current !== value) {
613
+ onChangeRef.current?.(value);
614
+ prevValueRef.current = value;
615
+ }
616
+ }, [
617
+ value,
618
+ prevValueRef
619
+ ]);
620
+ return [
621
+ value,
622
+ setValue,
623
+ onChangeRef
624
+ ];
625
+ }
626
+ function isFunction(value) {
627
+ return typeof value === "function";
628
+ }
629
+
630
+ const DATA_TABLE_UNIVERSAL_CALC_OPS = [
631
+ "count",
632
+ "countFilled"
633
+ ];
634
+ const DATA_TABLE_NUMERIC_CALC_OPS = [
635
+ "sum",
636
+ "average",
637
+ "min",
638
+ "max"
639
+ ];
640
+ const DATA_TABLE_CALC_LABEL = {
641
+ count: "Count",
642
+ countFilled: "Count filled",
643
+ sum: "Sum",
644
+ average: "Average",
645
+ min: "Min",
646
+ max: "Max"
647
+ };
648
+ /** A value counts as empty when it is null/undefined, an empty string, or an empty
649
+ * array -- crisp's own `isEmptyValue` (columncalc.tsx), reused verbatim. */ function isEmptyValueForCalc(value) {
650
+ if (value == null) return true;
651
+ if (typeof value === "string") return value.trim() === "";
652
+ if (Array.isArray(value)) return value.length === 0;
653
+ return false;
654
+ }
655
+ /**
656
+ * Pure so the arithmetic is independently (and mutation-) testable without rendering a
657
+ * table at all. `sum`/`average`/`min`/`max` skip empty values and non-numeric ones
658
+ * (coerced via `Number`, dropping `NaN`); they return `null` -- render nothing, not a
659
+ * misleading 0 -- when there is nothing numeric left to reduce over.
660
+ */ function computeColumnCalc(values, op) {
661
+ switch(op){
662
+ case "count":
663
+ return values.length;
664
+ case "countFilled":
665
+ return values.filter((value)=>!isEmptyValueForCalc(value)).length;
666
+ }
667
+ const numbers = values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>typeof value === "number" ? value : Number(value)).filter((value)=>Number.isFinite(value));
668
+ if (numbers.length === 0) return null;
669
+ switch(op){
670
+ case "sum":
671
+ return numbers.reduce((total, value)=>total + value, 0);
672
+ case "average":
673
+ return numbers.reduce((total, value)=>total + value, 0) / numbers.length;
674
+ case "min":
675
+ return Math.min(...numbers);
676
+ case "max":
677
+ return Math.max(...numbers);
678
+ default:
679
+ return null;
680
+ }
681
+ }
682
+ const EMPTY_OPS = {};
683
+ // Owns which aggregation (if any) each column's ColumnCalc footer cell currently
684
+ // shows -- the way useDataTableFilter owns one filter per column key. The actual
685
+ // REDUCE over row values needs `TRow` (a type this headless package never sees), so
686
+ // -- same split as sort/search/filter -- this hook only tracks WHICH op is active per
687
+ // column; the styled layer (DataTable.tsx's `ColumnCalcCell`) calls `computeColumnCalc`
688
+ // with the column's own resolved values.
689
+ function useDataTableColumnCalc(props) {
690
+ const { columnCalcOps: opsProp, defaultColumnCalcOps = EMPTY_OPS, onColumnCalcOpsChange } = props;
691
+ const [columnCalcOps = EMPTY_OPS, setColumnCalcOps] = useControllableState({
692
+ prop: opsProp,
693
+ defaultProp: defaultColumnCalcOps,
694
+ onChange: onColumnCalcOpsChange
695
+ });
696
+ const getColumnCalcOp = React.useCallback((columnKey)=>columnCalcOps[columnKey], [
697
+ columnCalcOps
698
+ ]);
699
+ const setColumnCalcOp = React.useCallback((columnKey, op)=>{
700
+ setColumnCalcOps((current = EMPTY_OPS)=>{
701
+ if (op === undefined) {
702
+ if (!(columnKey in current)) return current;
703
+ const next = {
704
+ ...current
705
+ };
706
+ delete next[columnKey];
707
+ return next;
708
+ }
709
+ return {
710
+ ...current,
711
+ [columnKey]: op
712
+ };
713
+ });
714
+ }, [
715
+ setColumnCalcOps
716
+ ]);
717
+ return {
718
+ columnCalcOps,
719
+ getColumnCalcOp,
720
+ setColumnCalcOp
721
+ };
722
+ }
723
+
724
+ // Select's `dom.ts` reads state off REAL rendered elements (aria-disabled, data-value)
725
+ // because its options register asynchronously through a floating-ui list and the DOM
726
+ // is the one thing always in sync with `elementsRef`. DataTable's selection has no
727
+ // such registry -- it is two plain id arrays (`rowIds`, `selectedIds`) the root already
728
+ // holds -- so there is nothing to inspect on an element. What's isolated here instead is
729
+ // the same KIND of thing Select isolates: small pure functions the state hooks would
730
+ // otherwise inline, kept out of the hooks so they can be unit-tested (and mutation-
731
+ // tested) on their own.
732
+ /**
733
+ * Tri-state selection summary of `selectedIds` against the CURRENTLY RENDERED
734
+ * `rowIds` -- never against `selectedIds` alone, so a stale id that scrolled out of a
735
+ * filtered/sorted view can't quietly count toward "all" or "some".
736
+ */ function computeSelectionState(rowIds, selectedIds) {
737
+ if (rowIds.length === 0) return "none";
738
+ const selectedIdSet = new Set(selectedIds);
739
+ const selectedRowCount = rowIds.filter((id)=>selectedIdSet.has(id)).length;
740
+ if (selectedRowCount === 0) return "none";
741
+ return selectedRowCount === rowIds.length ? "all" : "some";
742
+ }
743
+ /** Adds `id` if absent, removes it if present. Never mutates `ids`. */ function toggleMembership(ids, id) {
744
+ return ids.includes(id) ? ids.filter((existing)=>existing !== id) : [
745
+ ...ids,
746
+ id
747
+ ];
748
+ }
749
+ /**
750
+ * Reconciles a persisted/controlled column ORDER against the table's current column
751
+ * KEYS -- used by useDataTableColumns so a consumer's `columnOrder` never silently drops
752
+ * a column the table actually renders (a key added to `columns` after the order was
753
+ * first captured) nor keeps a ghost entry for one that was removed. Known keys keep
754
+ * their position from `order`; any key in `keys` missing from `order` is appended, in
755
+ * `keys`' own order; any entry in `order` no longer in `keys` is dropped.
756
+ */ function reconcileColumnOrder(order, keys) {
757
+ const keySet = new Set(keys);
758
+ const known = order.filter((key)=>keySet.has(key));
759
+ const knownSet = new Set(known);
760
+ const added = keys.filter((key)=>!knownSet.has(key));
761
+ return [
762
+ ...known,
763
+ ...added
764
+ ];
765
+ }
766
+ /**
767
+ * Moves `key` one step toward `direction` in `order` -- a no-op (same array, well,
768
+ * an equal-valued copy) at the edge it would otherwise walk off. Column-menu's
769
+ * "Move left"/"Move right" (DATATABLE-ATTIO-SPEC.md's column menu) is one call of
770
+ * this per click; drag-to-reorder is `reorderColumn` below instead, which can jump
771
+ * more than one position in a single move.
772
+ */ function moveColumnKey(order, key, direction) {
773
+ const index = order.indexOf(key);
774
+ if (index === -1) return [
775
+ ...order
776
+ ];
777
+ const targetIndex = direction === "left" ? index - 1 : index + 1;
778
+ if (targetIndex < 0 || targetIndex >= order.length) return [
779
+ ...order
780
+ ];
781
+ const next = [
782
+ ...order
783
+ ];
784
+ [next[index], next[targetIndex]] = [
785
+ next[targetIndex],
786
+ next[index]
787
+ ];
788
+ return next;
789
+ }
790
+ /**
791
+ * Drag-to-reorder: removes `dragKey` from `order` and reinserts it immediately before
792
+ * `dropKey`'s CURRENT position (i.e. its position after the removal, so dropping a
793
+ * column just after its own old slot doesn't appear to do nothing). A no-op (an
794
+ * equal-valued copy) when either key is missing or they're the same key.
795
+ */ function reorderColumnKey(order, dragKey, dropKey) {
796
+ if (dragKey === dropKey || !order.includes(dragKey) || !order.includes(dropKey)) {
797
+ return [
798
+ ...order
799
+ ];
800
+ }
801
+ const withoutDrag = order.filter((key)=>key !== dragKey);
802
+ const dropIndex = withoutDrag.indexOf(dropKey);
803
+ return [
804
+ ...withoutDrag.slice(0, dropIndex),
805
+ dragKey,
806
+ ...withoutDrag.slice(dropIndex)
807
+ ];
808
+ }
809
+ /** Clamps a drag-resized column width to a sane range -- crisp's own datatable.tsx
810
+ * resize handler clamps the same way (a column can neither collapse to unreadable
811
+ * nor balloon past a sane cap from one fast mouse move). */ function clampColumnWidth(width, min = 80, max = 640) {
812
+ return Math.min(max, Math.max(min, width));
813
+ }
814
+ /**
815
+ * Infinite scroll's own gate: fire `onLoadMore` only when the sentinel is ACTUALLY
816
+ * intersecting (not e.g. an observer's initial `false` callback, which fires once on
817
+ * `observe()` before any real intersection), there IS a next page (`hasMore`), and no
818
+ * fetch is already in flight (`loadingMore`) -- either of the latter two alone must
819
+ * suppress a trigger, which is why this is a pure `&&` rather than three separate
820
+ * call sites that could each drift. Isolated the same way `computeSelectionState`/
821
+ * `toggleMembership` are: the real IntersectionObserver callback (useDataTable
822
+ * InfiniteScroll.ts) is unit-testable only through a live browser API, but the
823
+ * DECISION it makes on every callback is plain data in, boolean out.
824
+ */ function shouldTriggerLoadMore(isIntersecting, hasMore, loadingMore) {
825
+ return hasMore && !loadingMore;
826
+ }
827
+ const EMPTY_STRINGS = [];
828
+ const EMPTY_RECORD = {};
829
+ /**
830
+ * Owns everything DATATABLE-ATTIO-SPEC.md's column menu + reorder + resize tranche
831
+ * needs -- order, visibility, a per-column label override, and a per-column width --
832
+ * the way useDataTableSelection owns the one selected-id SET rather than one hook per
833
+ * row. All four are "one value per column key" maps/lists a consumer may want to
834
+ * persist (a saved view), so they share this hook instead of splitting into four
835
+ * separate ones; drag-to-reorder and drag-to-resize (below) are the two INTERACTIONS
836
+ * built on top of the order/width state this hook already owns, not independent state
837
+ * of their own.
838
+ */ function useDataTableColumns(props) {
839
+ const { columnKeys = EMPTY_STRINGS, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys: hiddenProp, defaultHiddenColumnKeys = EMPTY_STRINGS, onHiddenColumnKeysChange, columnWidths: widthsProp, defaultColumnWidths = EMPTY_RECORD, onColumnWidthsChange, columnLabels: labelsProp, defaultColumnLabels = EMPTY_RECORD, onColumnLabelsChange } = props;
840
+ const [rawOrder, setRawOrder] = useControllableState({
841
+ prop: columnOrderProp,
842
+ defaultProp: defaultColumnOrder ?? columnKeys,
843
+ onChange: onColumnOrderChange
844
+ });
845
+ // Reconciled EVERY render against the table's current `columnKeys` (dom.ts's
846
+ // `reconcileColumnOrder`), not just once at mount -- a controlled `columnOrder` a
847
+ // consumer persisted before a new column existed must still show that new column
848
+ // (appended), and one holding a column that got removed from `columns` must not
849
+ // render a ghost header for it.
850
+ const columnOrder = React.useMemo(()=>reconcileColumnOrder(rawOrder ?? columnKeys, columnKeys), [
851
+ rawOrder,
852
+ columnKeys
853
+ ]);
854
+ const [hiddenColumnKeys = EMPTY_STRINGS, setHiddenColumnKeys] = useControllableState({
855
+ prop: hiddenProp,
856
+ defaultProp: defaultHiddenColumnKeys,
857
+ onChange: onHiddenColumnKeysChange
858
+ });
859
+ const hiddenSet = React.useMemo(()=>new Set(hiddenColumnKeys), [
860
+ hiddenColumnKeys
861
+ ]);
862
+ const [columnWidths = EMPTY_RECORD, setColumnWidths] = useControllableState({
863
+ prop: widthsProp,
864
+ defaultProp: defaultColumnWidths,
865
+ onChange: onColumnWidthsChange
866
+ });
867
+ const [columnLabels = EMPTY_RECORD, setColumnLabels] = useControllableState({
868
+ prop: labelsProp,
869
+ defaultProp: defaultColumnLabels,
870
+ onChange: onColumnLabelsChange
871
+ });
872
+ const visibleColumnOrder = React.useMemo(()=>columnOrder.filter((key)=>!hiddenSet.has(key)), [
873
+ columnOrder,
874
+ hiddenSet
875
+ ]);
876
+ const isColumnHidden = React.useCallback((key)=>hiddenSet.has(key), [
877
+ hiddenSet
878
+ ]);
879
+ const hideColumn = React.useCallback((key)=>setHiddenColumnKeys((current = EMPTY_STRINGS)=>[
880
+ ...current,
881
+ key
882
+ ]), [
883
+ setHiddenColumnKeys
884
+ ]);
885
+ const showColumn = React.useCallback((key)=>setHiddenColumnKeys((current = EMPTY_STRINGS)=>current.filter((existing)=>existing !== key)), [
886
+ setHiddenColumnKeys
887
+ ]);
888
+ const toggleColumnVisibility = React.useCallback((key)=>isColumnHidden(key) ? showColumn(key) : hideColumn(key), [
889
+ isColumnHidden,
890
+ showColumn,
891
+ hideColumn
892
+ ]);
893
+ const getColumnLabel = React.useCallback((key, fallback)=>columnLabels[key] ?? fallback, [
894
+ columnLabels
895
+ ]);
896
+ const renameColumn = React.useCallback((key, label)=>setColumnLabels((current = EMPTY_RECORD)=>({
897
+ ...current,
898
+ [key]: label
899
+ })), [
900
+ setColumnLabels
901
+ ]);
902
+ const getColumnWidth = React.useCallback((key)=>columnWidths[key], [
903
+ columnWidths
904
+ ]);
905
+ const setColumnWidth = React.useCallback((key, width)=>setColumnWidths((current = EMPTY_RECORD)=>({
906
+ ...current,
907
+ [key]: clampColumnWidth(width)
908
+ })), [
909
+ setColumnWidths
910
+ ]);
911
+ const moveColumn = React.useCallback((key, direction)=>setRawOrder((current)=>moveColumnKey(current ?? columnKeys, key, direction)), [
912
+ setRawOrder,
913
+ columnKeys
914
+ ]);
915
+ const reorderColumn = React.useCallback((dragKey, dropKey)=>setRawOrder((current)=>reorderColumnKey(current ?? columnKeys, dragKey, dropKey)), [
916
+ setRawOrder,
917
+ columnKeys
918
+ ]);
919
+ // -- drag-to-reorder ---------------------------------------------------------------
920
+ // Live state only (which column is currently being dragged) -- the styled layer
921
+ // drives it by calling `dragOverColumn` as the pointer crosses OTHER header cells
922
+ // (via `document.elementFromPoint`, since a captured pointer stops native
923
+ // `pointerenter` from firing on siblings), which reorders immediately rather than
924
+ // waiting for a drop. That is exactly what Attio's own header drag does (the column
925
+ // being passed visibly swaps live), and it means `endColumnDrag` never has to also
926
+ // commit a move -- there is nothing left to commit.
927
+ const [draggingColumnKey, setDraggingColumnKey] = React.useState(null);
928
+ const beginColumnDrag = React.useCallback((key)=>setDraggingColumnKey(key), []);
929
+ const dragOverColumn = React.useCallback((hoverKey)=>{
930
+ setDraggingColumnKey((current)=>{
931
+ if (!current || current === hoverKey) return current;
932
+ reorderColumn(current, hoverKey);
933
+ return current;
934
+ });
935
+ }, [
936
+ reorderColumn
937
+ ]);
938
+ const endColumnDrag = React.useCallback(()=>setDraggingColumnKey(null), []);
939
+ // -- drag-to-resize ------------------------------------------------------------------
940
+ // `resizeStartRef` holds the ONE thing that must survive across pointermove events
941
+ // without triggering a re-render on every pixel (the drag's origin) -- state
942
+ // (`resizingColumnKey`) is only for what the render actually needs: which column's
943
+ // handle should show its active/dragging look.
944
+ const resizeStartRef = React.useRef(null);
945
+ const [resizingColumnKey, setResizingColumnKey] = React.useState(null);
946
+ const beginColumnResize = React.useCallback((key, clientX, currentWidth)=>{
947
+ resizeStartRef.current = {
948
+ key,
949
+ startX: clientX,
950
+ startWidth: currentWidth
951
+ };
952
+ setResizingColumnKey(key);
953
+ }, []);
954
+ const updateColumnResize = React.useCallback((clientX)=>{
955
+ const start = resizeStartRef.current;
956
+ if (!start) return;
957
+ setColumnWidth(start.key, start.startWidth + (clientX - start.startX));
958
+ }, [
959
+ setColumnWidth
960
+ ]);
961
+ const endColumnResize = React.useCallback(()=>{
962
+ resizeStartRef.current = null;
963
+ setResizingColumnKey(null);
964
+ }, []);
965
+ return {
966
+ columnOrder,
967
+ visibleColumnOrder,
968
+ hiddenColumnKeys,
969
+ columnWidths,
970
+ columnLabels,
971
+ isColumnHidden,
972
+ hideColumn,
973
+ showColumn,
974
+ toggleColumnVisibility,
975
+ getColumnLabel,
976
+ renameColumn,
977
+ getColumnWidth,
978
+ setColumnWidth,
979
+ moveColumn,
980
+ reorderColumn,
981
+ draggingColumnKey,
982
+ beginColumnDrag,
983
+ dragOverColumn,
984
+ endColumnDrag,
985
+ resizingColumnKey,
986
+ beginColumnResize,
987
+ updateColumnResize,
988
+ endColumnResize,
989
+ // Exposed for the styled layer's header cell -- `data-dragging`/`data-resizing`
990
+ // read directly off state above already cover most of it; `dataAttr` re-exported
991
+ // via this hook's own module keeps every other sub-hook's habit of building its
992
+ // own data-attribute values rather than the styled layer re-deriving booleans.
993
+ isColumnDragging: (key)=>domUtils.dataAttr(draggingColumnKey === key),
994
+ isColumnResizing: (key)=>domUtils.dataAttr(resizingColumnKey === key)
995
+ };
996
+ }
997
+
998
+ function sameCell(a, id, key) {
999
+ return a !== null && a.id === id && a.key === key;
1000
+ }
1001
+ /**
1002
+ * Owns which single cell is mid-edit, which one (if any) has a commit in flight, and
1003
+ * which one last failed -- the way useDataTableColumnCalc owns which op is active per
1004
+ * column. This hook never touches `TRow` or renders an editor itself; it only tracks
1005
+ * WHICH cell is in which of those three states and orchestrates the async commit
1006
+ * (crisp's own `commitEdit`, `datatable.tsx`) -- the styled layer (react/DataTable's
1007
+ * `editors.tsx`) reads `isEditing`/`isPending`/`getEditError` to decide what to render
1008
+ * for a given `(id, key)` and calls `startEdit`/`cancelEdit`/`commitEdit` in response
1009
+ * to a double-click, Escape, Enter/blur, or an always-live widget's own change event.
1010
+ *
1011
+ * Only one cell can be mid-edit at a time (`editingCell`, not a set) -- matching
1012
+ * crisp's own single `editing` state and this table's spec (no multi-cell/range
1013
+ * editing is in scope here).
1014
+ */ function useDataTableEdit(props) {
1015
+ const { onEdit } = props;
1016
+ const [editingCell, setEditingCell] = React.useState(null);
1017
+ const [pendingCell, setPendingCell] = React.useState(null);
1018
+ const [editError, setEditError] = React.useState(null);
1019
+ const isEditing = React.useCallback((id, key)=>sameCell(editingCell, id, key), [
1020
+ editingCell
1021
+ ]);
1022
+ const isPending = React.useCallback((id, key)=>sameCell(pendingCell, id, key), [
1023
+ pendingCell
1024
+ ]);
1025
+ const getEditError = React.useCallback((id, key)=>editError && editError.id === id && editError.key === key ? editError.message : null, [
1026
+ editError
1027
+ ]);
1028
+ const startEdit = React.useCallback((id, key)=>{
1029
+ // Opening a cell's editor also clears any stale error left over from a PREVIOUS
1030
+ // failed commit on it (crisp/#385 comment) -- otherwise a rejection from three
1031
+ // edits ago would still read under a freshly-reopened editor.
1032
+ setEditError(null);
1033
+ setEditingCell({
1034
+ id,
1035
+ key
1036
+ });
1037
+ }, []);
1038
+ const cancelEdit = React.useCallback(()=>setEditingCell(null), []);
1039
+ // Async-aware (crisp/#385): a sync `onEdit` (returns undefined) commits exactly as
1040
+ // before -- close the editor immediately. A Promise-returning `onEdit` keeps the
1041
+ // editor open with a pending state while it settles, then either closes it
1042
+ // (resolve) or closes it AND reverts to the last-known-good value while showing the
1043
+ // rejection's message beside the cell (reject). The revert needs no extra code:
1044
+ // neither this hook nor the styled layer ever mutates the caller's own row data, so
1045
+ // once `editingCell` clears, the cell simply shows that data again.
1046
+ const commitEdit = React.useCallback((id, key, value)=>{
1047
+ setEditError(null);
1048
+ const result = onEdit?.(id, key, value);
1049
+ if (result && typeof result.then === "function") {
1050
+ setPendingCell({
1051
+ id,
1052
+ key
1053
+ });
1054
+ result.then(()=>{
1055
+ setPendingCell(null);
1056
+ setEditingCell(null);
1057
+ }, (err)=>{
1058
+ setPendingCell(null);
1059
+ setEditingCell(null);
1060
+ setEditError({
1061
+ id,
1062
+ key,
1063
+ message: err instanceof Error ? err.message : typeof err === "string" ? err : "Could not save"
1064
+ });
1065
+ });
1066
+ return;
1067
+ }
1068
+ setEditingCell(null);
1069
+ }, [
1070
+ onEdit
1071
+ ]);
1072
+ return {
1073
+ editingCell,
1074
+ isEditing,
1075
+ isPending,
1076
+ getEditError,
1077
+ startEdit,
1078
+ cancelEdit,
1079
+ commitEdit
1080
+ };
1081
+ }
1082
+
1083
+ const EMPTY_FILTERS = {};
1084
+ // Owns one filter per column key -- the way useDataTableSelection owns one selected-id
1085
+ // SET rather than one boolean per row. Applying a column's filter to `data` needs
1086
+ // `TRow` (a type this headless package never sees), so -- same split as search/sort --
1087
+ // this hook only tracks which mode/values are checked per column; the styled layer
1088
+ // (DataTable.tsx's `getVisibleRows`) reads it to actually narrow rows.
1089
+ function useDataTableFilter(props) {
1090
+ const { filters: filtersProp, defaultFilters = EMPTY_FILTERS, onFiltersChange } = props;
1091
+ const [filters = EMPTY_FILTERS, setFilters] = useControllableState({
1092
+ prop: filtersProp,
1093
+ defaultProp: defaultFilters,
1094
+ onChange: onFiltersChange
1095
+ });
1096
+ const setFilterMode = React.useCallback((columnKey, mode)=>{
1097
+ setFilters((current = EMPTY_FILTERS)=>({
1098
+ ...current,
1099
+ [columnKey]: {
1100
+ mode,
1101
+ values: current[columnKey]?.values ?? []
1102
+ }
1103
+ }));
1104
+ }, [
1105
+ setFilters
1106
+ ]);
1107
+ const toggleFilterValue = React.useCallback((columnKey, value)=>{
1108
+ setFilters((current = EMPTY_FILTERS)=>({
1109
+ ...current,
1110
+ [columnKey]: {
1111
+ mode: current[columnKey]?.mode ?? "is",
1112
+ values: toggleMembership(current[columnKey]?.values ?? [], value)
1113
+ }
1114
+ }));
1115
+ }, [
1116
+ setFilters
1117
+ ]);
1118
+ const clearFilter = React.useCallback((columnKey)=>{
1119
+ setFilters((current = EMPTY_FILTERS)=>{
1120
+ if (!(columnKey in current)) return current;
1121
+ const next = {
1122
+ ...current
1123
+ };
1124
+ delete next[columnKey];
1125
+ return next;
1126
+ });
1127
+ }, [
1128
+ setFilters
1129
+ ]);
1130
+ const isFilterActive = React.useCallback((columnKey)=>(filters[columnKey]?.values.length ?? 0) > 0, [
1131
+ filters
1132
+ ]);
1133
+ return {
1134
+ filters,
1135
+ setFilterMode,
1136
+ toggleFilterValue,
1137
+ clearFilter,
1138
+ isFilterActive
1139
+ };
1140
+ }
1141
+
1142
+ // Triggers before the sentinel is actually on-screen -- Attio's own list prefetches
1143
+ // a page ahead of the visible edge so the next batch of rows is already there by the
1144
+ // time a fast scroll reaches them, rather than the user briefly out-running the data.
1145
+ const ROOT_MARGIN = "160px";
1146
+ /**
1147
+ * Owns the "is the user close enough to the bottom to load more" concern -- the way
1148
+ * useDataTableScroll owns the horizontal scroll-shadow edge state. An
1149
+ * IntersectionObserver watching a single sentinel row the styled layer renders as the
1150
+ * last child of `<tbody>` while `hasMore` is true (DataTable.tsx's own
1151
+ * `InfiniteScrollRow`), observed against `scrollRootRef`'s element (useDataTable.ts
1152
+ * wires this to useDataTableScroll's own scroll container) so a BOUNDED table
1153
+ * triggers off its own internal scroll, not the browsing viewport.
1154
+ */ function useDataTableInfiniteScroll(props) {
1155
+ const { onLoadMore, hasMore = true, loadingMore: loadingMoreProp, scrollRootRef } = props;
1156
+ const [internalLoadingMore, setInternalLoadingMore] = React.useState(false);
1157
+ const loadingMore = loadingMoreProp ?? internalLoadingMore;
1158
+ // Mirrors the latest props into a ref the observer callback reads -- the callback
1159
+ // is registered once per sentinel element (see the effect below) and must not
1160
+ // recreate the whole IntersectionObserver on every render just because a fresh
1161
+ // `onLoadMore` closure came in, the same "ref mirrors the latest value" shape a
1162
+ // debounced/throttled handler needs.
1163
+ const latest = React.useRef({
1164
+ onLoadMore,
1165
+ hasMore,
1166
+ loadingMore
1167
+ });
1168
+ latest.current = {
1169
+ onLoadMore,
1170
+ hasMore,
1171
+ loadingMore
1172
+ };
1173
+ const triggerLoadMore = React.useCallback(()=>{
1174
+ const { onLoadMore: fn, hasMore: has, loadingMore: busy } = latest.current;
1175
+ if (!shouldTriggerLoadMore(true, has, busy) || !fn) return;
1176
+ const result = fn();
1177
+ // Only a THENABLE result puts this hook in charge of `loadingMore` -- a
1178
+ // synchronous `onLoadMore` (already-fetched data, a local page) commits
1179
+ // immediately and never becomes "busy" at all, matching `loadingMoreProp`'s own
1180
+ // controlled shape: nothing to suppress a very next scroll-triggered call with.
1181
+ if (result && typeof result.then === "function") {
1182
+ if (loadingMoreProp === undefined) setInternalLoadingMore(true);
1183
+ result.finally(()=>{
1184
+ if (loadingMoreProp === undefined) setInternalLoadingMore(false);
1185
+ });
1186
+ }
1187
+ }, [
1188
+ loadingMoreProp
1189
+ ]);
1190
+ // A state slot (not a plain ref) so attaching the sentinel re-runs the effect below
1191
+ // -- a callback ref's own node can't be an effect dependency otherwise.
1192
+ const [sentinelNode, setSentinelNode] = React.useState(null);
1193
+ const sentinelRef = React.useCallback((node)=>setSentinelNode(node), []);
1194
+ React.useEffect(()=>{
1195
+ if (!sentinelNode || !onLoadMore || typeof IntersectionObserver === "undefined") return;
1196
+ // Effects run after every ref callback of the SAME commit (including
1197
+ // `scrollRootRef`'s own, set by useDataTableScroll's `scrollAreaRef`) regardless
1198
+ // of sibling ref attach order, so `scrollRootRef.current` is already populated
1199
+ // here even on the very first render that mounts both elements at once. `null`
1200
+ // (no scroll container yet, or none supplied) falls back to the browsing
1201
+ // viewport, IntersectionObserver's own default.
1202
+ const root = scrollRootRef?.current ?? null;
1203
+ const observer = new IntersectionObserver((entries)=>{
1204
+ if (entries.some((entry)=>entry.isIntersecting)) triggerLoadMore();
1205
+ }, {
1206
+ root,
1207
+ rootMargin: ROOT_MARGIN
1208
+ });
1209
+ observer.observe(sentinelNode);
1210
+ return ()=>observer.disconnect();
1211
+ }, [
1212
+ sentinelNode,
1213
+ onLoadMore,
1214
+ scrollRootRef,
1215
+ triggerLoadMore
1216
+ ]);
1217
+ return {
1218
+ /** Attach to the sentinel row's DOM node (a ref callback, `DataTable.tsx`'s own
1219
+ * `InfiniteScrollRow`). A no-op (no observer ever created) when `onLoadMore` is
1220
+ * omitted -- the styled layer still shouldn't render the sentinel at all in that
1221
+ * case (nothing to load more OF), this is just the inert fallback. */ sentinelRef,
1222
+ /** Whether to keep rendering the sentinel row at all. */ hasMore,
1223
+ /** A fetch is in flight -- shows the loading-more spinner row and gates further
1224
+ * triggers, whichever side (controlled or internal) reports it. */ loadingMore,
1225
+ /** Fires `onLoadMore` directly, subject to the same `hasMore`/`loadingMore` gate
1226
+ * the observer itself uses -- exposed for a consumer that wants a manual retry
1227
+ * affordance (e.g. after a failed load) rather than only the observer's own
1228
+ * scroll trigger. */ triggerLoadMore
1229
+ };
1230
+ }
1231
+
1232
+ /**
1233
+ * Tracks a horizontally-scrolling element's edge state for the sticky-column scroll
1234
+ * shadow (DATATABLE-ATTIO-SPEC.md's `stickyFirst`/`stickyLast`) -- crisp's own
1235
+ * `.crisp-table-scroll[data-scrolled-x]`/`[data-scrolled-x-end]`. Owns the DOM
1236
+ * measurement the way `useSelectFloating` owns floating-ui's own DOM measurement: a
1237
+ * real ref to a real scrollable element, re-measured on every scroll (and whenever the
1238
+ * styled layer calls `remeasure`, e.g. after a column resize/reorder changes the
1239
+ * scrollable width without the element itself ever firing a `scroll` event).
1240
+ */ function useDataTableScroll() {
1241
+ const elementRef = React.useRef(null);
1242
+ const [scrolledStart, setScrolledStart] = React.useState(false);
1243
+ const [scrolledEnd, setScrolledEnd] = React.useState(false);
1244
+ const remeasure = React.useCallback(()=>{
1245
+ const element = elementRef.current;
1246
+ if (!element) return;
1247
+ setScrolledStart(element.scrollLeft > 0);
1248
+ // A 1px slop: sub-pixel layout can leave `scrollLeft + clientWidth` a fraction
1249
+ // short of `scrollWidth` even when fully scrolled, which would otherwise leave
1250
+ // the trailing shadow stuck on forever.
1251
+ setScrolledEnd(element.scrollLeft + element.clientWidth < element.scrollWidth - 1);
1252
+ }, []);
1253
+ const scrollAreaRef = React.useCallback((node)=>{
1254
+ elementRef.current = node;
1255
+ if (node) remeasure();
1256
+ }, [
1257
+ remeasure
1258
+ ]);
1259
+ const onScroll = React.useCallback(()=>remeasure(), [
1260
+ remeasure
1261
+ ]);
1262
+ return {
1263
+ scrollAreaRef,
1264
+ scrolledStart,
1265
+ scrolledEnd,
1266
+ onScroll,
1267
+ remeasure,
1268
+ // The raw ref object itself (not just the callback ref above) -- useDataTable.ts
1269
+ // hands this to useDataTableInfiniteScroll as the IntersectionObserver `root` for
1270
+ // its sentinel row, so a scrolled/bounded table (DATATABLE-ATTIO-SPEC.md's
1271
+ // full-body-height feature) auto-loads against ITS OWN scroll boundary rather
1272
+ // than the browsing viewport. A stable object identity (a plain ref, not state):
1273
+ // useDataTableInfiniteScroll reads `.current` inside an effect, which always runs
1274
+ // after every ref callback of the same commit (including this one), so the
1275
+ // element is never read before it's attached regardless of sibling ref order.
1276
+ elementRef
1277
+ };
1278
+ }
1279
+
1280
+ // Owns the toolbar's single free-text query -- the way useDataTableSort owns the single
1281
+ // active sort column. It has no idea which columns are searchable or how a row's value
1282
+ // is read off them (that needs `TRow`, a type this headless package never sees), so
1283
+ // narrowing `data` against the query is the styled layer's job (DataTable.tsx's
1284
+ // `getVisibleRows`) -- the same split sort already draws: this hook tracks WHICH state
1285
+ // is active, the styled layer decides what that state does to the rows.
1286
+ function useDataTableSearch(props) {
1287
+ const { query: queryProp, defaultQuery = "", onQueryChange } = props;
1288
+ const [query = "", setQuery] = useControllableState({
1289
+ prop: queryProp,
1290
+ defaultProp: defaultQuery,
1291
+ onChange: onQueryChange
1292
+ });
1293
+ const clearQuery = React.useCallback(()=>setQuery(""), [
1294
+ setQuery
1295
+ ]);
1296
+ return {
1297
+ query,
1298
+ setQuery,
1299
+ clearQuery
1300
+ };
1301
+ }
1302
+
1303
+ // Owns the selected-id set and its two shapes of toggle (one row, select-all) --
1304
+ // the way useSelectOptions owns the selected VALUE. `selectionState` and `toggleAll`
1305
+ // are always computed against `rowIds` (the currently rendered order), never against
1306
+ // `selectedIds` alone -- see dom.ts's `computeSelectionState` doc comment.
1307
+ function useDataTableSelection(props) {
1308
+ const { rowIds = [], selectable = false, selectedIds: selectedIdsProp, defaultSelectedIds = [], onSelectionChange } = props;
1309
+ const [selectedIds = [], setSelectedIds] = useControllableState({
1310
+ prop: selectedIdsProp,
1311
+ defaultProp: defaultSelectedIds,
1312
+ onChange: onSelectionChange
1313
+ });
1314
+ const selectedIdSet = React.useMemo(()=>new Set(selectedIds), [
1315
+ selectedIds
1316
+ ]);
1317
+ const isSelected = React.useCallback((id)=>selectedIdSet.has(id), [
1318
+ selectedIdSet
1319
+ ]);
1320
+ const toggleRow = React.useCallback((id)=>{
1321
+ setSelectedIds((current = [])=>toggleMembership(current, id));
1322
+ }, [
1323
+ setSelectedIds
1324
+ ]);
1325
+ const selectionState = React.useMemo(()=>computeSelectionState(rowIds, selectedIds), [
1326
+ rowIds,
1327
+ selectedIds
1328
+ ]);
1329
+ const toggleAll = React.useCallback(()=>{
1330
+ setSelectedIds(selectionState === "all" ? [] : rowIds);
1331
+ }, [
1332
+ selectionState,
1333
+ rowIds,
1334
+ setSelectedIds
1335
+ ]);
1336
+ const getRowProps = React.useCallback((rowId)=>{
1337
+ const isRowSelected = isSelected(rowId);
1338
+ return domUtils.elementProps({
1339
+ role: "row",
1340
+ "aria-selected": domUtils.ariaAttr(isRowSelected),
1341
+ "data-selected": domUtils.dataAttr(isRowSelected)
1342
+ });
1343
+ }, [
1344
+ isSelected
1345
+ ]);
1346
+ return {
1347
+ selectable,
1348
+ rowIds,
1349
+ selectedIds,
1350
+ selectionState,
1351
+ isSelected,
1352
+ toggleRow,
1353
+ toggleAll,
1354
+ getRowProps
1355
+ };
1356
+ }
1357
+
1358
+ // Owns the single active sort column/direction and the click/keyboard toggle that
1359
+ // cycles it -- the way useSelectHighlight owns the single active index. A header
1360
+ // cell's full prop set (aria-sort, data-sortable/data-sorted, the click and
1361
+ // Enter/Space handlers) is built here too, beside the state it reads, rather than
1362
+ // re-derived in the styled layer or the root primitive.
1363
+ function useDataTableSort(props) {
1364
+ const { sort: sortProp, defaultSort, onSortChange } = props;
1365
+ const [sort, setSort] = useControllableState({
1366
+ prop: sortProp,
1367
+ defaultProp: defaultSort,
1368
+ onChange: (nextSort)=>{
1369
+ // `toggleSort` never sets `sort` back to `undefined`, but the underlying state is
1370
+ // typed as possibly-undefined so an uncontrolled table can start with no sort at all.
1371
+ if (nextSort) onSortChange?.(nextSort);
1372
+ }
1373
+ });
1374
+ const toggleSort = React.useCallback((key)=>{
1375
+ setSort((current)=>{
1376
+ if (!current || current.key !== key) return {
1377
+ key,
1378
+ dir: "asc"
1379
+ };
1380
+ return {
1381
+ key,
1382
+ dir: current.dir === "asc" ? "desc" : "asc"
1383
+ };
1384
+ });
1385
+ }, [
1386
+ setSort
1387
+ ]);
1388
+ const getHeaderCellProps = React.useCallback((columnKey, sortable)=>{
1389
+ const isSorted = Boolean(columnKey) && sort?.key === columnKey;
1390
+ const canSort = Boolean(sortable && columnKey);
1391
+ return domUtils.elementProps({
1392
+ role: "columnheader",
1393
+ "aria-sort": canSort ? isSorted ? sort?.dir === "asc" ? "ascending" : "descending" : "none" : undefined,
1394
+ tabIndex: canSort ? 0 : undefined,
1395
+ "data-sortable": domUtils.dataAttr(canSort),
1396
+ "data-sorted": domUtils.dataAttr(isSorted),
1397
+ "data-sort-direction": isSorted ? sort?.dir : undefined,
1398
+ onClick (event) {
1399
+ if (event.defaultPrevented || !canSort || !columnKey) return;
1400
+ toggleSort(columnKey);
1401
+ },
1402
+ onKeyDown (event) {
1403
+ if (event.defaultPrevented || !canSort || !columnKey) return;
1404
+ if (event.key === "Enter" || event.key === " ") {
1405
+ event.preventDefault();
1406
+ toggleSort(columnKey);
1407
+ }
1408
+ }
1409
+ });
1410
+ }, [
1411
+ sort,
1412
+ toggleSort
1413
+ ]);
1414
+ return {
1415
+ sort,
1416
+ toggleSort,
1417
+ getHeaderCellProps
1418
+ };
1419
+ }
1420
+
1421
+ // Composes the eight independent concerns a table root owns -- which column is
1422
+ // sorted, which rows are selected, the toolbar's free-text query, each column's own
1423
+ // filter, column order/visibility/width/label, each column's ColumnCalc footer op, the
1424
+ // horizontal scroll-shadow edge state, and which single cell is mid-edit/pending/
1425
+ // erroring -- the way useSelect composes useSelectHighlight + useSelectOptions. No
1426
+ // sub-hook knows about any other; a future concern (row grouping) is a ninth sibling
1427
+ // call here, not a branch inside an existing one.
1428
+ function useDataTable(props) {
1429
+ const sort = useDataTableSort(props);
1430
+ const selection = useDataTableSelection(props);
1431
+ const search = useDataTableSearch(props);
1432
+ const filter = useDataTableFilter(props);
1433
+ const columns = useDataTableColumns(props);
1434
+ const columnCalc = useDataTableColumnCalc(props);
1435
+ const scroll = useDataTableScroll();
1436
+ const edit = useDataTableEdit(props);
1437
+ // Wired to `scroll`'s own scroll-container ref (not a fresh one of its own) so a
1438
+ // BOUNDED table's auto-load triggers off its own internal scroll, not the
1439
+ // browsing viewport -- see useDataTableInfiniteScroll.ts's own doc comment.
1440
+ const infiniteScroll = useDataTableInfiniteScroll({
1441
+ ...props,
1442
+ scrollRootRef: scroll.elementRef
1443
+ });
1444
+ return {
1445
+ ...sort,
1446
+ ...selection,
1447
+ ...search,
1448
+ ...filter,
1449
+ ...columns,
1450
+ ...columnCalc,
1451
+ ...scroll,
1452
+ ...edit,
1453
+ ...infiniteScroll,
1454
+ rootProps: domUtils.elementProps({
1455
+ role: "table"
1456
+ })
1457
+ };
1458
+ }
1459
+
1460
+ // Per-row read of the root's selection state -- the way useSelectItem reads
1461
+ // `getItemProps` off the root for one option. Kept as its own hook (rather than
1462
+ // inlined into the styled `DataTableRow`) so `DataTableCell`/`DataTableSelectionCell`
1463
+ // can read the SAME row's `isSelected`/`toggleRow` through `useDataTableRowContext`
1464
+ // without either subscribing to the whole table's context or being handed `rowId`
1465
+ // again as a prop.
1466
+ function useDataTableRow(props) {
1467
+ const { rowId } = props;
1468
+ const { isSelected, toggleRow, getRowProps } = useDataTableContext();
1469
+ return {
1470
+ rowId,
1471
+ isSelected: isSelected(rowId),
1472
+ toggleRow: ()=>toggleRow(rowId),
1473
+ rootProps: getRowProps(rowId)
1474
+ };
1475
+ }
1476
+
1477
+ const DataTableContext = /*#__PURE__*/ React.createContext(null);
1478
+ const DataTableProvider = DataTableContext.Provider;
1479
+ function useDataTableContext({ strict = true } = {}) {
1480
+ const context = React.useContext(DataTableContext);
1481
+ if (!context && strict) {
1482
+ throw new Error("useDataTableContext must be used within a DataTable");
1483
+ }
1484
+ return context;
1485
+ }
1486
+ const DataTableRowContext = /*#__PURE__*/ React.createContext(null);
1487
+ const DataTableRowProvider = DataTableRowContext.Provider;
1488
+ function useDataTableRowContext({ strict = true } = {}) {
1489
+ const context = React.useContext(DataTableRowContext);
1490
+ if (!context && strict) {
1491
+ throw new Error("useDataTableRowContext must be used within a DataTableRow");
1492
+ }
1493
+ return context;
1494
+ }
1495
+ const DataTableRoot = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1496
+ const { sort, defaultSort, onSortChange, rowIds, selectable, selectedIds, defaultSelectedIds, onSelectionChange, query, defaultQuery, onQueryChange, filters, defaultFilters, onFiltersChange, columnKeys, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, onEdit, onLoadMore, hasMore, loadingMore, ...otherProps } = props;
1497
+ const api = useDataTable({
1498
+ sort,
1499
+ defaultSort,
1500
+ onSortChange,
1501
+ rowIds,
1502
+ selectable,
1503
+ selectedIds,
1504
+ defaultSelectedIds,
1505
+ onSelectionChange,
1506
+ query,
1507
+ defaultQuery,
1508
+ onQueryChange,
1509
+ filters,
1510
+ defaultFilters,
1511
+ onFiltersChange,
1512
+ columnKeys,
1513
+ columnOrder,
1514
+ defaultColumnOrder,
1515
+ onColumnOrderChange,
1516
+ hiddenColumnKeys,
1517
+ defaultHiddenColumnKeys,
1518
+ onHiddenColumnKeysChange,
1519
+ columnWidths,
1520
+ defaultColumnWidths,
1521
+ onColumnWidthsChange,
1522
+ columnLabels,
1523
+ defaultColumnLabels,
1524
+ onColumnLabelsChange,
1525
+ columnCalcOps,
1526
+ defaultColumnCalcOps,
1527
+ onColumnCalcOpsChange,
1528
+ onEdit,
1529
+ onLoadMore,
1530
+ hasMore,
1531
+ loadingMore
1532
+ });
1533
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableProvider, {
1534
+ value: api,
1535
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
1536
+ ref: ref,
1537
+ ...otherProps
1538
+ })
1539
+ });
1540
+ });
1541
+ DataTableRoot.displayName = "DataTableRoot";
1542
+ /**
1543
+ * The real `<table>` -- everything a native table gets for free (columns that line up
1544
+ * by construction across `<thead>`/`<tbody>`/`<tfoot>`, `<th scope="col">` semantics,
1545
+ * sticky `th`/`td` that actually work under horizontal scroll) that a `display: flex`
1546
+ * div grid faked with ARIA and broke on. `rootProps` (just `role: "table"`, which a
1547
+ * real `<table>` already implies) comes off context rather than being computed here --
1548
+ * `useDataTable()` runs once, in `DataTableRoot` above, not per render of this sibling.
1549
+ */ const DataTableTable$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1550
+ const { rootProps } = useDataTableContext();
1551
+ const mergedProps = domUtils.mergeProps(rootProps, props);
1552
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.table, {
1553
+ ref: ref,
1554
+ ...mergedProps
1555
+ });
1556
+ });
1557
+ DataTableTable$1.displayName = "DataTableTable";
1558
+ /**
1559
+ * `<thead>`, wrapping its own children in a single `<tr>` -- the styled layer still
1560
+ * renders every header cell as a flat child of `DataTableHeader` (one header row is
1561
+ * the only shape this table supports), so that `<tr>` is supplied HERE rather than
1562
+ * asking every call site to add it.
1563
+ */ const DataTableHeader$1 = /*#__PURE__*/ React.forwardRef(({ children, ...otherProps }, ref)=>{
1564
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.thead, {
1565
+ ref: ref,
1566
+ ...otherProps,
1567
+ children: /*#__PURE__*/ jsxRuntime.jsx("tr", {
1568
+ children: children
1569
+ })
1570
+ });
1571
+ });
1572
+ DataTableHeader$1.displayName = "DataTableHeader";
1573
+ const DataTableHeaderCell$1 = /*#__PURE__*/ React.forwardRef(({ columnKey, sortable, ...otherProps }, ref)=>{
1574
+ const { getHeaderCellProps } = useDataTableContext();
1575
+ const mergedProps = domUtils.mergeProps(getHeaderCellProps(columnKey, sortable), otherProps);
1576
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.th, {
1577
+ scope: "col",
1578
+ ref: ref,
1579
+ ...mergedProps
1580
+ });
1581
+ });
1582
+ DataTableHeaderCell$1.displayName = "DataTableHeaderCell";
1583
+ const DataTableBody$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1584
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.tbody, {
1585
+ ref: ref,
1586
+ ...props
1587
+ });
1588
+ });
1589
+ DataTableBody$1.displayName = "DataTableBody";
1590
+ const DataTableRow$1 = /*#__PURE__*/ React.forwardRef(({ rowId, ...otherProps }, ref)=>{
1591
+ const row = useDataTableRow({
1592
+ rowId
1593
+ });
1594
+ const mergedProps = domUtils.mergeProps(row.rootProps, otherProps);
1595
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableRowProvider, {
1596
+ value: row,
1597
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.tr, {
1598
+ ref: ref,
1599
+ ...mergedProps
1600
+ })
1601
+ });
1602
+ });
1603
+ DataTableRow$1.displayName = "DataTableRow";
1604
+ const DataTableCell$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1605
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.td, {
1606
+ ref: ref,
1607
+ ...props
1608
+ });
1609
+ });
1610
+ DataTableCell$1.displayName = "DataTableCell";
1611
+
1612
+ function r(e) {
1613
+ var t, f, n = "";
1614
+ if ("string" == typeof e || "number" == typeof e) n += e;
1615
+ else if ("object" == typeof e) if (Array.isArray(e)) {
1616
+ var o = e.length;
1617
+ for(t = 0; t < o; t++)e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
1618
+ } else for(f in e)e[f] && (n && (n += " "), n += f);
1619
+ return n;
1620
+ }
1621
+ function clsx() {
1622
+ for(var e, t, f = 0, n = "", o = arguments.length; f < o; f++)(e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
1623
+ return n;
1624
+ }
1625
+
1626
+ function splitMultipleVariantsProps(props, recipesMap) {
1627
+ const multipleVariantsProps = {};
1628
+ const extractedKeys = new Set();
1629
+ for(const recipeKey in recipesMap){
1630
+ const recipe = recipesMap[recipeKey];
1631
+ const [variantProps] = recipe.splitVariantProps(props);
1632
+ multipleVariantsProps[recipeKey] = variantProps;
1633
+ for(const variantPropKey in variantProps){
1634
+ extractedKeys.add(variantPropKey);
1635
+ }
1636
+ }
1637
+ const remainingProps = {};
1638
+ for(const propKey in props){
1639
+ if (extractedKeys.has(propKey)) continue;
1640
+ remainingProps[propKey] = props[propKey];
1641
+ }
1642
+ return [
1643
+ multipleVariantsProps,
1644
+ remainingProps
1645
+ ];
1646
+ }
1647
+
1648
+ /** Restores defaults overwritten by explicitly passed undefined values. */ function restoreDefaultProps(props, defaultProps) {
1649
+ if (!defaultProps) return;
1650
+ for (const key of Object.keys(defaultProps)){
1651
+ if (props[key] === undefined) {
1652
+ props[key] = defaultProps[key];
1653
+ }
1654
+ }
1655
+ }
1656
+
1657
+ function createSlotRecipeContext(recipe) {
1658
+ const ClassNamesContext = /*#__PURE__*/ React.createContext(null);
1659
+ const PropsContext = /*#__PURE__*/ React.createContext(null);
1660
+ const ClassNamesProvider = ({ children, value })=>{
1661
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesContext.Provider, {
1662
+ value: value,
1663
+ children: children
1664
+ });
1665
+ };
1666
+ const PropsProvider = ({ children, value })=>{
1667
+ return /*#__PURE__*/ jsxRuntime.jsx(PropsContext.Provider, {
1668
+ value: value,
1669
+ children: children
1670
+ });
1671
+ };
1672
+ function useClassNames() {
1673
+ const context = React.useContext(ClassNamesContext);
1674
+ if (context === null) {
1675
+ throw new Error("useClassNames must be used within a ClassNamesProvider. Did you forget to wrap your component in a ClassNamesProvider?");
1676
+ }
1677
+ return context;
1678
+ }
1679
+ function useProps() {
1680
+ return React.useContext(PropsContext);
1681
+ }
1682
+ const withRootProvider = (Component, options)=>{
1683
+ const { defaultProps } = options ?? {};
1684
+ const StyledComponent = (innerProps)=>{
1685
+ const props = {
1686
+ ...defaultProps ?? {},
1687
+ ...useProps(),
1688
+ ...innerProps
1689
+ };
1690
+ restoreDefaultProps(props, defaultProps);
1691
+ const [variantProps, otherProps] = recipe.splitVariantProps(props);
1692
+ const classNames = recipe(variantProps); // TODO: should we memoize this?
1693
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider, {
1694
+ value: classNames,
1695
+ children: /*#__PURE__*/ jsxRuntime.jsx(Component, {
1696
+ ...otherProps
1697
+ })
1698
+ });
1699
+ };
1700
+ // @ts-ignore
1701
+ StyledComponent.displayName = Component.displayName || Component.name;
1702
+ return StyledComponent;
1703
+ };
1704
+ const withProvider = (Component, slot, options)=>{
1705
+ const { defaultProps } = options ?? {};
1706
+ const StyledComponent = /*#__PURE__*/ React.forwardRef((innerProps, ref)=>{
1707
+ const props = {
1708
+ ...defaultProps ?? {},
1709
+ ...useProps(),
1710
+ ...innerProps
1711
+ };
1712
+ restoreDefaultProps(props, defaultProps);
1713
+ const [variantProps, otherProps] = recipe.splitVariantProps(props);
1714
+ const classNames = recipe(variantProps); // TODO: should we memoize this?
1715
+ const className = classNames[slot];
1716
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider, {
1717
+ value: classNames,
1718
+ children: /*#__PURE__*/ jsxRuntime.jsx(Component, {
1719
+ ref: ref,
1720
+ ...otherProps,
1721
+ className: clsx(className, props.className)
1722
+ })
1723
+ });
1724
+ });
1725
+ // @ts-ignore
1726
+ StyledComponent.displayName = Component.displayName || Component.name;
1727
+ return StyledComponent;
1728
+ };
1729
+ const withContext = (Component, slot)=>{
1730
+ const StyledComponent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1731
+ const classNames = useClassNames();
1732
+ const className = classNames?.[slot];
1733
+ return /*#__PURE__*/ jsxRuntime.jsx(Component, {
1734
+ ref: ref,
1735
+ ...props,
1736
+ className: clsx(className, props.className)
1737
+ });
1738
+ });
1739
+ // @ts-ignore
1740
+ StyledComponent.displayName = Component.displayName || Component.name;
1741
+ return StyledComponent;
1742
+ };
1743
+ return {
1744
+ ClassNamesProvider,
1745
+ PropsProvider,
1746
+ useClassNames,
1747
+ useProps,
1748
+ withRootProvider,
1749
+ withProvider,
1750
+ withContext
1751
+ };
1752
+ }
1753
+
1754
+ function createWithStateProps(useContexts) {
1755
+ return function withStateProps(Component) {
1756
+ const Node = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1757
+ const stateProps = {};
1758
+ for (const contextConfig of useContexts){
1759
+ if (typeof contextConfig === "function") {
1760
+ Object.assign(stateProps, contextConfig({
1761
+ strict: true
1762
+ })?.stateProps);
1763
+ } else {
1764
+ const { useContext, strict = false } = contextConfig;
1765
+ Object.assign(stateProps, useContext({
1766
+ strict
1767
+ })?.stateProps);
1768
+ }
1769
+ }
1770
+ // @ts-ignore
1771
+ return /*#__PURE__*/ jsxRuntime.jsx(Component, {
1772
+ ref: ref,
1773
+ ...stateProps,
1774
+ ...props
1775
+ });
1776
+ });
1777
+ Node.displayName = Component.displayName || Component.name;
1778
+ return Node;
1779
+ };
1780
+ }
1781
+
1782
+ function withScaleFeedback(Component) {
1783
+ const Pressable = /*#__PURE__*/ React__namespace.forwardRef((props, ref)=>{
1784
+ const { scaleFeedbackRef, scaleFeedbackClassName } = reactScaleFeedback.useScaleFeedback();
1785
+ const composedRef = useComposedRefs(scaleFeedbackRef, ref);
1786
+ return /*#__PURE__*/ jsxRuntime.jsx(Component, {
1787
+ ...props,
1788
+ ref: composedRef,
1789
+ className: clsx(scaleFeedbackClassName, props.className)
1790
+ });
1791
+ });
1792
+ Pressable.displayName = Component.displayName;
1793
+ return Pressable;
1794
+ }
1795
+
1796
+ const InternalIcon = /*#__PURE__*/ React.forwardRef(({ svg, ...otherProps }, ref)=>{
1797
+ return /*#__PURE__*/ jsxRuntime.jsx(Slot, {
1798
+ ref: ref,
1799
+ "aria-hidden": true,
1800
+ ...otherProps,
1801
+ children: svg
1802
+ });
1803
+ });
1804
+
1805
+ function createRecipeContext(recipe) {
1806
+ const PropsContext = /*#__PURE__*/ React.createContext(null);
1807
+ const PropsProvider = ({ children, value })=>{
1808
+ return /*#__PURE__*/ jsxRuntime.jsx(PropsContext.Provider, {
1809
+ value: value,
1810
+ children: children
1811
+ });
1812
+ };
1813
+ function useProps() {
1814
+ return React.useContext(PropsContext);
1815
+ }
1816
+ const withContext = (Component, options)=>{
1817
+ const { defaultProps } = options ?? {};
1818
+ const StyledComponent = /*#__PURE__*/ React.forwardRef((innerProps, ref)=>{
1819
+ const props = {
1820
+ ...defaultProps ?? {},
1821
+ ...useProps(),
1822
+ ...innerProps
1823
+ };
1824
+ restoreDefaultProps(props, defaultProps);
1825
+ const [variantProps, otherProps] = recipe.splitVariantProps(props);
1826
+ const className = recipe(variantProps); // TODO: should we memoize this?
1827
+ return /*#__PURE__*/ jsxRuntime.jsx(Component, {
1828
+ ref: ref,
1829
+ ...otherProps,
1830
+ className: clsx(className, props.className)
1831
+ });
1832
+ });
1833
+ StyledComponent.displayName = Component.displayName || Component.name;
1834
+ return StyledComponent;
1835
+ };
1836
+ function withPropsProvider() {
1837
+ return PropsProvider;
1838
+ }
1839
+ return {
1840
+ PropsProvider,
1841
+ useProps,
1842
+ withContext,
1843
+ withPropsProvider
1844
+ };
1845
+ }
1846
+
1847
+ const { withContext: withGroupContext } = createRecipeContext(checkboxGroup.checkboxGroup);
1848
+ const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$6 } = createSlotRecipeContext(checkbox.checkbox);
1849
+ const { withProvider: withCheckmarkProvider, useClassNames: useCheckmarkClassNames, PropsProvider: CheckmarkPropsProvider } = createSlotRecipeContext(checkmark.checkmark);
1850
+ const withStateProps$4 = createWithStateProps([
1851
+ reactCheckbox.useCheckboxContext
1852
+ ]);
1853
+ withGroupContext(Primitive.div);
1854
+ const CheckboxRoot = Object.assign(/*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
1855
+ const [{ checkbox: checkboxVariantProps, checkmark: checkmarkVariantProps }, otherProps] = splitMultipleVariantsProps(props, {
1856
+ checkbox: checkbox.checkbox,
1857
+ checkmark: checkmark.checkmark
1858
+ });
1859
+ const classNames = checkbox.checkbox(checkboxVariantProps);
1860
+ return /*#__PURE__*/ jsxRuntime.jsx(CheckmarkPropsProvider, {
1861
+ value: checkmarkVariantProps,
1862
+ children: /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$3, {
1863
+ value: classNames,
1864
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactCheckbox.Checkbox.Root, {
1865
+ ref: ref,
1866
+ className: clsx(classNames.root, className),
1867
+ ...otherProps
1868
+ })
1869
+ })
1870
+ });
1871
+ }), {
1872
+ Primitive: reactCheckbox.Checkbox.Root
1873
+ });
1874
+ const CheckboxControl = withScaleFeedback(withCheckmarkProvider(reactCheckbox.Checkbox.Control, "root"));
1875
+ const CheckboxIndicator = /*#__PURE__*/ React.forwardRef(({ unchecked: uncheckedSvg, checked: checkedSvg, indeterminate: indeterminateSvg, ...otherProps }, ref)=>{
1876
+ const { stateProps, checked, indeterminate } = reactCheckbox.useCheckboxContext();
1877
+ const classNames = useCheckmarkClassNames();
1878
+ const mergedProps = domUtils.mergeProps(stateProps, {
1879
+ className: classNames.icon
1880
+ }, otherProps);
1881
+ if (indeterminate && !indeterminateSvg) {
1882
+ console.warn("CheckboxIndicator: the `indeterminate` prop must be provided when the checkbox is in an indeterminate state.");
1883
+ }
1884
+ if (indeterminate) return /*#__PURE__*/ jsxRuntime.jsx(InternalIcon, {
1885
+ ref: ref,
1886
+ svg: indeterminateSvg,
1887
+ ...mergedProps
1888
+ });
1889
+ if (checked) return /*#__PURE__*/ jsxRuntime.jsx(InternalIcon, {
1890
+ ref: ref,
1891
+ svg: checkedSvg,
1892
+ ...mergedProps
1893
+ });
1894
+ if (uncheckedSvg) return /*#__PURE__*/ jsxRuntime.jsx(InternalIcon, {
1895
+ ref: ref,
1896
+ svg: uncheckedSvg,
1897
+ ...mergedProps
1898
+ });
1899
+ return null;
1900
+ });
1901
+ CheckboxIndicator.displayName = "CheckboxIndicator";
1902
+ const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
1903
+ const CheckboxHiddenInput = reactCheckbox.Checkbox.HiddenInput;
1904
+
1905
+ const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar.avatar);
1906
+ const AvatarRoot = withProvider$2(reactImage.Image.Root, "root");
1907
+ const AvatarImage = withContext$5(reactImage.Image.Content, "image");
1908
+ const AvatarFallback = withContext$5(reactImage.Image.Fallback, "fallback");
1909
+ withContext$5(Primitive.div, "badge");
1910
+
1911
+ const Badge = /*#__PURE__*/ React.forwardRef(({ className, children, ...props }, ref)=>{
1912
+ const [variantProps, restProps] = badge.badge.splitVariantProps(props);
1913
+ const { root, label } = badge.badge(variantProps);
1914
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
1915
+ className: clsx(root, className),
1916
+ ...restProps,
1917
+ ref: ref,
1918
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
1919
+ className: label,
1920
+ children: children
1921
+ })
1922
+ });
1923
+ });
1924
+ Badge.displayName = "Badge";
12
1925
 
1926
+ // Attio cell-type renderers, built from seed's own components -- not exported from
1927
+ // the package's public surface (kept alongside DataTable.tsx as an implementation
1928
+ // detail, the way Select keeps `InternalIcon` under `components/private`). Each
1929
+ // renderer takes the raw column value and the table's resolved recipe classNames,
1930
+ // and returns `null` for an empty value: Attio's own empty cell is genuinely blank,
1931
+ // no placeholder character, for any type (crisp's datatable.tsx, #412).
1932
+ // seed's `CheckboxIndicator` renders exactly what its `checked` prop is -- it does not
1933
+ // supply a checkmark on its own. `checked={null}` (this file's own pre-fix state)
1934
+ // therefore rendered an empty box even once the value WAS true; every read-only/
1935
+ // editable checkbox cell in this component needs a real icon here, the same one the
1936
+ // row-selection checkbox already uses (`DataTable.tsx`'s `SELECTION_ICON_SIZE`, 12px).
1937
+ const CHECKBOX_CELL_CHECKED_ICON = /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkLine_default, {
1938
+ size: 12
1939
+ });
1940
+ // The 6 tones Badge ships (`@seed-design/css/recipes/badge`) -- not Attio's own
1941
+ // wider palette (crisp's tag/status cells hash across a measured 7- or 12-colour
1942
+ // set it defines itself), because this component builds cell chrome ONLY from
1943
+ // seed's already-shipped primitives rather than adding a parallel palette. Same
1944
+ // stable-hash approach as crisp's own `toneFor` (multiply-and-add over char codes),
1945
+ // just over seed's tone list instead of crisp's.
1946
+ const BADGE_TONES = [
1947
+ "neutral",
1948
+ "brand",
1949
+ "informative",
1950
+ "positive",
1951
+ "warning",
1952
+ "critical"
1953
+ ];
1954
+ function toneFor(value) {
1955
+ let hash = 0;
1956
+ for(let index = 0; index < value.length; index++){
1957
+ hash = hash * 31 + value.charCodeAt(index) >>> 0;
1958
+ }
1959
+ return BADGE_TONES[hash % BADGE_TONES.length];
1960
+ }
1961
+ function initialsFor(name) {
1962
+ const parts = name.trim().split(/\s+/);
1963
+ return ((parts[0]?.[0] ?? "") + (parts[1]?.[0] ?? "")).toUpperCase() || "?";
1964
+ }
1965
+ function formatNumberCell(value, locale) {
1966
+ return typeof value === "number" ? value.toLocaleString(locale) : null;
1967
+ }
1968
+ function formatCurrencyCell(value, symbol, locale) {
1969
+ return typeof value === "number" ? `${symbol}${value.toLocaleString(locale)}` : null;
1970
+ }
1971
+ function toDate(value) {
1972
+ const date = value instanceof Date ? value : typeof value === "string" ? new Date(value) : null;
1973
+ return date && !Number.isNaN(date.getTime()) ? date : null;
1974
+ }
1975
+ /** `<input type="date">`'s own value shape (`yyyy-mm-dd`) -- editors.tsx's plain
1976
+ * `cellType: "date"` inline editor reads/writes exactly this string, native to the
1977
+ * browser's own date input, so no separate parse/format step is needed on commit. */ function toDateInputValue(value) {
1978
+ const date = toDate(value);
1979
+ if (!date) return "";
1980
+ const pad = (n)=>String(n).padStart(2, "0");
1981
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
1982
+ }
1983
+ /** `<input type="datetime-local">`'s own value shape (`yyyy-mm-ddThh:mm`) -- same
1984
+ * reasoning as `toDateInputValue` above, one input type over. */ function toDateTimeInputValue(value) {
1985
+ const date = toDate(value);
1986
+ if (!date) return "";
1987
+ const pad = (n)=>String(n).padStart(2, "0");
1988
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
1989
+ }
1990
+ function formatDateCell(value, locale) {
1991
+ const date = toDate(value);
1992
+ if (!date) return null;
1993
+ return new Intl.DateTimeFormat(locale, {
1994
+ month: "short",
1995
+ day: "numeric",
1996
+ year: "numeric"
1997
+ }).format(date);
1998
+ }
1999
+ function formatDateTimeCell(value, locale) {
2000
+ const date = toDate(value);
2001
+ if (!date) return null;
2002
+ return new Intl.DateTimeFormat(locale, {
2003
+ month: "short",
2004
+ day: "numeric",
2005
+ year: "numeric",
2006
+ hour: "numeric",
2007
+ minute: "2-digit"
2008
+ }).format(date);
2009
+ }
2010
+ function EntityCell({ value, className, nameClassName }) {
2011
+ const entity = typeof value === "object" && value !== null ? value : {
2012
+ name: String(value ?? "")
2013
+ };
2014
+ if (!entity.name) return null;
2015
+ return /*#__PURE__*/ jsxRuntime.jsxs("span", {
2016
+ className: className,
2017
+ children: [
2018
+ /*#__PURE__*/ jsxRuntime.jsxs(AvatarRoot, {
2019
+ size: "20",
2020
+ children: [
2021
+ entity.avatar && /*#__PURE__*/ jsxRuntime.jsx(AvatarImage, {
2022
+ src: entity.avatar,
2023
+ alt: ""
2024
+ }),
2025
+ /*#__PURE__*/ jsxRuntime.jsx(AvatarFallback, {
2026
+ children: initialsFor(entity.name)
2027
+ })
2028
+ ]
2029
+ }),
2030
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
2031
+ className: nameClassName,
2032
+ children: entity.name
2033
+ })
2034
+ ]
2035
+ });
2036
+ }
2037
+ function TagsCell({ value, className }) {
2038
+ const tags = Array.isArray(value) ? value : [];
2039
+ if (!tags.length) return null;
2040
+ return /*#__PURE__*/ jsxRuntime.jsx("span", {
2041
+ className: className,
2042
+ children: tags.map((tag)=>/*#__PURE__*/ jsxRuntime.jsx(Badge, {
2043
+ tone: toneFor(tag),
2044
+ variant: "weak",
2045
+ size: "medium",
2046
+ children: tag
2047
+ }, tag))
2048
+ });
2049
+ }
2050
+ function StatusCell({ value }) {
2051
+ const status = value == null ? "" : String(value);
2052
+ if (!status) return null;
2053
+ return /*#__PURE__*/ jsxRuntime.jsx(Badge, {
2054
+ tone: toneFor(status),
2055
+ variant: "weak",
2056
+ size: "medium",
2057
+ children: status
2058
+ });
2059
+ }
2060
+ function LinkCell({ column, value, className }) {
2061
+ const text = value == null ? "" : String(value);
2062
+ if (!text) return null;
2063
+ const isUrl = column.type === "url";
2064
+ const href = isUrl ? /^https?:\/\//.test(text) ? text : `https://${text}` : `mailto:${text}`;
2065
+ return /*#__PURE__*/ jsxRuntime.jsx("a", {
2066
+ className: className,
2067
+ href: href,
2068
+ target: isUrl ? "_blank" : undefined,
2069
+ rel: isUrl ? "noreferrer" : undefined,
2070
+ // A link cell points somewhere other than "select this row" -- its own
2071
+ // navigation must not also fire a row click handler.
2072
+ onClick: (event)=>event.stopPropagation(),
2073
+ children: text
2074
+ });
2075
+ }
2076
+ // Attio's own measured 5-point star path (crisp's `RATING_STAR_D`, traced from the
2077
+ // live widget's SVG) -- reused verbatim rather than a generic star glyph, so a
2078
+ // rating cell's silhouette matches Attio exactly instead of approximating it. Exported
2079
+ // so editors.tsx's interactive `RatingEditor` (an `editable` rating column, clicking a
2080
+ // star commits) draws the identical silhouette rather than a second, slightly
2081
+ // different one.
2082
+ const RATING_STAR_D = "M7.32203 2.81085C7.65793 1.97438 8.84207 1.97438 9.17797 2.81085L10.0851 5.06967C10.2281 5.42587 10.5624 5.66876 10.9454 5.69473L13.3739 5.8594C14.2733 5.92038 14.6392 7.04656 13.9475 7.62451L12.0795 9.18521C11.7849 9.43132 11.6572 9.82433 11.7509 10.1966L12.3447 12.5572C12.5647 13.4313 11.6067 14.1273 10.8433 13.6481L8.78171 12.3538C8.45661 12.1497 8.04339 12.1497 7.71829 12.3538L5.65675 13.6481C4.89333 14.1273 3.93534 13.4313 4.15526 12.5572L4.74911 10.1966C4.84276 9.82433 4.71507 9.43132 4.4205 9.18521L2.55254 7.62451C1.86081 7.04656 2.22673 5.92038 3.12606 5.8594L5.55463 5.69473C5.9376 5.66876 6.27191 5.42587 6.41495 5.06967L7.32203 2.81085Z";
2083
+ // Filled = the same measured Attio brand blue the selected-row tint uses
2084
+ // (`data-table.ts`'s `ATTIO_BRAND_BLUE`); empty = crisp's own measured
2085
+ // `rgba(0,0,0,0.1)`. Literal, not a recipe token -- see that file's comment on why.
2086
+ const RATING_FILLED = "#0298d9";
2087
+ const RATING_EMPTY = "rgba(0, 0, 0, 0.1)";
2088
+ function RatingCell({ value, maxRating = 5 }) {
2089
+ const rating = typeof value === "number" ? value : Number(value);
2090
+ if (!rating || Number.isNaN(rating)) return null;
2091
+ return /*#__PURE__*/ jsxRuntime.jsx("span", {
2092
+ style: {
2093
+ display: "inline-flex",
2094
+ alignItems: "center",
2095
+ gap: 2
2096
+ },
2097
+ children: Array.from({
2098
+ length: maxRating
2099
+ }, (_, index)=>// biome-ignore lint/suspicious/noArrayIndexKey: a fixed-length star row has no other identity
2100
+ /*#__PURE__*/ jsxRuntime.jsx("svg", {
2101
+ width: 16,
2102
+ height: 16,
2103
+ viewBox: "0 0 16 16",
2104
+ fill: "none",
2105
+ role: "presentation",
2106
+ "aria-hidden": "true",
2107
+ children: /*#__PURE__*/ jsxRuntime.jsx("path", {
2108
+ d: RATING_STAR_D,
2109
+ fill: index < rating ? RATING_FILLED : RATING_EMPTY
2110
+ })
2111
+ }, index))
2112
+ });
2113
+ }
2114
+ /** `type: "checkbox"`'s READ-ONLY display -- a disabled seed `Checkbox` reflecting
2115
+ * the raw boolean, so a non-`editable` checkbox column still reads as a checkbox
2116
+ * (crisp's own measured shape, distinct from `switch`/`rating`'s toggle/star), not a
2117
+ * bare "true"/"false" string. The `editable` case (a live, clickable checkbox) is
2118
+ * `editors.tsx`'s `CheckboxEditor` -- this renderer never handles a click. */ function CheckboxCell({ value }) {
2119
+ const checked = value === true || value === "true";
2120
+ return /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
2121
+ checked: checked,
2122
+ disabled: true,
2123
+ tabIndex: -1,
2124
+ children: [
2125
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
2126
+ children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
2127
+ checked: CHECKBOX_CELL_CHECKED_ICON
2128
+ })
2129
+ }),
2130
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
2131
+ "aria-label": "Value"
2132
+ })
2133
+ ]
2134
+ });
2135
+ }
2136
+
2137
+ /** The subset of `useDataTableContext()` every "resolve which columns to actually
2138
+ * render, in what order, under what label/width" helper below needs -- narrowed so
2139
+ * each helper's own signature documents exactly what it reads, rather than the whole
2140
+ * table API. */ function useEffectiveColumns(columns) {
2141
+ const { visibleColumnOrder, getColumnLabel, getColumnWidth } = useDataTableContext();
2142
+ return React__namespace.useMemo(()=>{
2143
+ const byKey = new Map(columns.map((column)=>[
2144
+ column.key,
2145
+ column
2146
+ ]));
2147
+ return visibleColumnOrder.map((key)=>byKey.get(key)).filter((column)=>Boolean(column)).map((column)=>({
2148
+ column,
2149
+ label: getColumnLabel(column.key, column.header),
2150
+ width: getColumnWidth(column.key) ?? column.width
2151
+ }));
2152
+ }, [
2153
+ columns,
2154
+ visibleColumnOrder,
2155
+ getColumnLabel,
2156
+ getColumnWidth
2157
+ ]);
2158
+ }
2159
+ /** Sticky positioning for the leading/trailing visible cell of a row (header, body, or
2160
+ * footer) -- `stickyFirst` pins the SELECTION cell when the table has one, plus the
2161
+ * first data column; `stickyLast` always pins the last VISIBLE data column. Only the
2162
+ * OUTERMOST sticky cell on each side gets `data-sticky-edge(-end)` -- that's the one
2163
+ * whose shadow should reveal on scroll, not every sticky cell.
2164
+ *
2165
+ * When both a selection column and a sticky first data column are present, the edge
2166
+ * is the data column (e.g. Company), not the 40px selection cell: the data column is
2167
+ * ALSO `position: sticky`, sitting immediately to its right, so a shadow cast from the
2168
+ * selection cell's trailing edge would render directly underneath that opaque sticky
2169
+ * cell and never actually be visible. Putting `data-sticky-edge` on the trailing-most
2170
+ * sticky-left cell is what makes the shadow render somewhere unobstructed -- past the
2171
+ * whole pinned group, not between two members of it. */ function stickyProps(options) {
2172
+ const { selectable, stickyFirst, stickyLast, isSelectionCell, isFirst, isLast } = options;
2173
+ if (stickyFirst && isSelectionCell) {
2174
+ // Still sticky either way. The edge marker only belongs here when there is no
2175
+ // sticky first DATA column to hand it to instead (i.e. no columns at all) --
2176
+ // otherwise that data column is the true trailing edge of the pinned group.
2177
+ return selectable ? {
2178
+ "data-sticky": "",
2179
+ style: {
2180
+ insetInlineStart: 0
2181
+ }
2182
+ } : {
2183
+ "data-sticky": "",
2184
+ "data-sticky-edge": "",
2185
+ style: {
2186
+ insetInlineStart: 0
2187
+ }
2188
+ };
2189
+ }
2190
+ if (stickyFirst && isFirst && !selectable) {
2191
+ return {
2192
+ "data-sticky": "",
2193
+ "data-sticky-edge": "",
2194
+ style: {
2195
+ insetInlineStart: 0
2196
+ }
2197
+ };
2198
+ }
2199
+ if (stickyFirst && isFirst && selectable) {
2200
+ // The trailing edge of the pinned group -- past the 40px selection cell, so this
2201
+ // is where the scroll shadow actually renders unobstructed.
2202
+ return {
2203
+ "data-sticky": "",
2204
+ "data-sticky-edge": "",
2205
+ style: {
2206
+ insetInlineStart: 40
2207
+ }
2208
+ };
2209
+ }
2210
+ if (stickyLast && isLast) {
2211
+ return {
2212
+ "data-sticky": "",
2213
+ "data-sticky-edge-end": "",
2214
+ style: {
2215
+ insetInlineEnd: 0
2216
+ }
2217
+ };
2218
+ }
2219
+ return {};
2220
+ }
2221
+
2222
+ // A select-only combobox spans three specs — the trigger, the floating listbox
2223
+ // container, and the option rows — each with its own independent states and its own
2224
+ // variant map. `SelectRoot` seeds defaults for all three, but only the container
2225
+ // resolves there, since its slots are spread across the portal subtree with no
2226
+ // single element to own them. The trigger and each option row resolve their own
2227
+ // recipe, so a per-instance variant can override the root's default.
2228
+ const { ClassNamesProvider: TriggerClassNamesProvider, PropsProvider: TriggerPropsProvider, useProps: useTriggerProps, withContext: withTriggerContext, useClassNames: useTriggerClassNames } = createSlotRecipeContext(selectTrigger.selectTrigger);
2229
+ const { ClassNamesProvider: ContentClassNamesProvider, withContext: withContentContext, useClassNames: useContentClassNames } = createSlotRecipeContext(select.select);
2230
+ const { ClassNamesProvider: ItemClassNamesProvider$1, PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem.selectItem);
2231
+ const withStateProps$3 = createWithStateProps([
2232
+ reactSelect.useSelectContext
2233
+ ]);
2234
+ const withItemStateProps$1 = createWithStateProps([
2235
+ reactSelect.useSelectItemContext
2236
+ ]);
2237
+ // NOTE: `gutter` and `overflowPadding` are specified in Rootage (`select.yaml`,
2238
+ // both `$dimension.x2`) and generated into `@seed-design/css`, but nothing reads
2239
+ // them: floating-ui's `offset`/`shift` take numbers, not CSS custom properties,
2240
+ // so the headless layer falls back to its own `8`. That default stays
2241
+ // design-system-agnostic on purpose — this layer is the one that owns the
2242
+ // Rootage binding, so seed the spec values from here when they need to win,
2243
+ // the way `HelpBubbleRoot` passes `gutter` through `defaultProps`.
2244
+ const SelectRoot = (props)=>{
2245
+ const [{ select: contentVariantProps, selectTrigger: triggerVariantProps, selectItem: itemVariantProps }, otherProps] = splitMultipleVariantsProps(props, {
2246
+ select: select.select,
2247
+ selectTrigger: selectTrigger.selectTrigger,
2248
+ selectItem: selectItem.selectItem
2249
+ });
2250
+ const contentClassNames = select.select(contentVariantProps);
2251
+ return /*#__PURE__*/ jsxRuntime.jsx(ContentClassNamesProvider, {
2252
+ value: contentClassNames,
2253
+ children: /*#__PURE__*/ jsxRuntime.jsx(TriggerPropsProvider, {
2254
+ value: triggerVariantProps,
2255
+ children: /*#__PURE__*/ jsxRuntime.jsx(ItemPropsProvider$1, {
2256
+ value: itemVariantProps,
2257
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Root, {
2258
+ ...otherProps
2259
+ })
2260
+ })
2261
+ })
2262
+ });
2263
+ };
2264
+ const SelectTrigger = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...props }, ref)=>{
2265
+ const [variantProps, otherProps] = selectTrigger.selectTrigger.splitVariantProps(props);
2266
+ const rootProps = useTriggerProps();
2267
+ const classNames = selectTrigger.selectTrigger({
2268
+ ...rootProps,
2269
+ ...variantProps
2270
+ });
2271
+ const fieldContext = reactField.useFieldContext({
2272
+ strict: false
2273
+ });
2274
+ // Pull only the field's labelledby/describedby: `useSelect` owns the
2275
+ // combobox's aria-invalid/required and native disabled, so we don't merge
2276
+ // those from the field (avoids duplicate ownership and aria-disabled on top
2277
+ // of the native disabled button).
2278
+ const mergedProps = domUtils.mergeProps(fieldContext ? {
2279
+ "aria-labelledby": fieldContext.inputAriaAttributes["aria-labelledby"],
2280
+ "aria-describedby": fieldContext.inputAriaAttributes["aria-describedby"]
2281
+ } : {}, otherProps);
2282
+ if (process.env.NODE_ENV !== "production" && !fieldContext && !otherProps["aria-label"] && !otherProps["aria-labelledby"]) {
2283
+ console.warn("SelectTrigger: Please provide `aria-label` or `aria-labelledby` for accessibility, or put `SelectTrigger` inside a `Field` where a `FieldLabel` is provided.");
2284
+ }
2285
+ return /*#__PURE__*/ jsxRuntime.jsx(TriggerClassNamesProvider, {
2286
+ value: classNames,
2287
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Trigger, {
2288
+ ref: ref,
2289
+ className: clsx(classNames.root, className),
2290
+ ...mergedProps
2291
+ })
2292
+ });
2293
+ });
2294
+ SelectTrigger.displayName = "SelectTrigger";
2295
+ const SelectValue = withTriggerContext(withStateProps$3(reactSelect.Select.Value), "value");
2296
+ const SelectPlaceholder = withTriggerContext(withStateProps$3(reactSelect.Select.Placeholder), "placeholder");
2297
+ const SelectPrefixIcon = /*#__PURE__*/ React__namespace.forwardRef(({ fallback, className, ...otherProps }, ref)=>{
2298
+ const { selectedItem, stateProps } = reactSelect.useSelectContext();
2299
+ const classNames = useTriggerClassNames();
2300
+ const svg = selectedItem?.icon ?? fallback;
2301
+ if (!svg) return null;
2302
+ return /*#__PURE__*/ jsxRuntime.jsx(InternalIcon, {
2303
+ ref: ref,
2304
+ svg: svg,
2305
+ className: clsx(classNames.prefixIcon, className),
2306
+ ...stateProps,
2307
+ ...otherProps
2308
+ });
2309
+ });
2310
+ SelectPrefixIcon.displayName = "SelectPrefixIcon";
2311
+ const SelectSuffixIcon = withTriggerContext(withStateProps$3(InternalIcon), "suffixIcon");
2312
+ const SelectPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...props }, ref)=>{
2313
+ const classNames = useContentClassNames();
2314
+ return /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Positioner, {
2315
+ ref: ref,
2316
+ className: clsx(classNames.positioner, className),
2317
+ ...props
2318
+ });
2319
+ });
2320
+ SelectPositioner.displayName = "SelectPositioner";
2321
+ const SelectContent = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...otherProps }, ref)=>{
2322
+ const classNames = useContentClassNames();
2323
+ const fieldContext = reactField.useFieldContext({
2324
+ strict: false
2325
+ });
2326
+ // Label the listbox popup with the same field label (APG combobox pattern).
2327
+ const mergedProps = domUtils.mergeProps(fieldContext ? {
2328
+ "aria-labelledby": fieldContext.inputAriaAttributes["aria-labelledby"]
2329
+ } : {}, otherProps);
2330
+ return /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Content, {
2331
+ ref: ref,
2332
+ className: clsx(classNames.content, className),
2333
+ ...mergedProps
2334
+ });
2335
+ });
2336
+ SelectContent.displayName = "SelectContent";
2337
+ const SelectScrollArea = withContentContext(Primitive.div, "scrollArea");
2338
+ withContentContext(reactSelect.Select.Group, "group");
2339
+ withContentContext(reactSelect.Select.GroupLabel, "groupLabel");
2340
+ const SelectItem = /*#__PURE__*/ React__namespace.forwardRef(({ prefixIcon, className, ...props }, ref)=>{
2341
+ const [variantProps, otherProps] = selectItem.selectItem.splitVariantProps(props);
2342
+ const rootProps = useItemProps$1();
2343
+ const classNames = selectItem.selectItem({
2344
+ ...rootProps,
2345
+ ...variantProps
2346
+ });
2347
+ return /*#__PURE__*/ jsxRuntime.jsx(ItemClassNamesProvider$1, {
2348
+ value: classNames,
2349
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Item, {
2350
+ ref: ref,
2351
+ icon: prefixIcon,
2352
+ ...otherProps,
2353
+ className: clsx(classNames.root, className)
2354
+ })
2355
+ });
2356
+ });
2357
+ SelectItem.displayName = "SelectItem";
2358
+ const SelectItemPrefixIcon = /*#__PURE__*/ React__namespace.forwardRef(({ svg: svgOverride, className, ...otherProps }, ref)=>{
2359
+ const { icon, stateProps } = reactSelect.useSelectItemContext();
2360
+ const classNames = useItemClassNames();
2361
+ const svg = svgOverride ?? icon;
2362
+ if (!svg) return null;
2363
+ return /*#__PURE__*/ jsxRuntime.jsx(InternalIcon, {
2364
+ ref: ref,
2365
+ svg: svg,
2366
+ className: clsx(classNames.prefixIcon, className),
2367
+ ...stateProps,
2368
+ ...otherProps
2369
+ });
2370
+ });
2371
+ SelectItemPrefixIcon.displayName = "SelectItemPrefixIcon";
2372
+ const SelectItemBody = withItemContext$1(withItemStateProps$1(Primitive.div), "body");
2373
+ const SelectItemLabel = /*#__PURE__*/ React__namespace.forwardRef(({ children, className, ...otherProps }, ref)=>{
2374
+ const { label, stateProps } = reactSelect.useSelectItemContext();
2375
+ const classNames = useItemClassNames();
2376
+ // stateProps stays because the label's disabled style targets its own
2377
+ // `data-disabled`.
2378
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
2379
+ ref: ref,
2380
+ className: clsx(classNames.label, className),
2381
+ ...stateProps,
2382
+ ...otherProps,
2383
+ children: children ?? label
2384
+ });
2385
+ });
2386
+ SelectItemLabel.displayName = "SelectItemLabel";
2387
+ withItemContext$1(withItemStateProps$1(Primitive.span), "description");
2388
+ const SelectItemIndicator = /*#__PURE__*/ React__namespace.forwardRef(({ selected: selectedSvg, unselected: unselectedSvg, className, ...otherProps }, ref)=>{
2389
+ const { isSelected, stateProps } = reactSelect.useSelectItemContext();
2390
+ const classNames = useItemClassNames();
2391
+ const svg = isSelected ? selectedSvg : unselectedSvg;
2392
+ if (!svg) return null;
2393
+ return /*#__PURE__*/ jsxRuntime.jsx(InternalIcon, {
2394
+ ref: ref,
2395
+ svg: svg,
2396
+ className: clsx(classNames.indicator, className),
2397
+ ...stateProps,
2398
+ ...otherProps
2399
+ });
2400
+ });
2401
+ SelectItemIndicator.displayName = "SelectItemIndicator";
2402
+ const SelectHiddenSelect = /*#__PURE__*/ React__namespace.forwardRef((props, ref)=>{
2403
+ const fieldContext = reactField.useFieldContext({
2404
+ strict: false
2405
+ });
2406
+ // The field label's `htmlFor` targets the field input id. Carrying that id on
2407
+ // the hidden native select — a labelable element whose label activation only
2408
+ // focuses, never opens anything — makes label clicks reach the trigger through
2409
+ // the hidden select's focus redirect. Associating the label with the trigger
2410
+ // button instead would forward label clicks as activation and open the listbox.
2411
+ return /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.HiddenSelect, {
2412
+ ref: ref,
2413
+ id: fieldContext?.inputProps.id,
2414
+ ...props
2415
+ });
2416
+ });
2417
+ SelectHiddenSelect.displayName = "SelectHiddenSelect";
2418
+
2419
+ const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(_switch.switchStyle);
2420
+ const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark.switchmark);
2421
+ const withStateProps$2 = createWithStateProps([
2422
+ reactSwitch.useSwitchContext
2423
+ ]);
2424
+ const SwitchRoot = /*#__PURE__*/ React__namespace.default.forwardRef(({ className, ...props }, ref)=>{
2425
+ const [{ switch: switchVariantProps, switchmark: switchmarkVariantProps }, otherProps] = splitMultipleVariantsProps(props, {
2426
+ switchmark: switchmark.switchmark,
2427
+ switch: _switch.switchStyle
2428
+ });
2429
+ const classNames = _switch.switchStyle(switchVariantProps);
2430
+ return /*#__PURE__*/ jsxRuntime.jsx(ControlPropsProvider, {
2431
+ value: switchmarkVariantProps,
2432
+ children: /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$2, {
2433
+ value: classNames,
2434
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactSwitch.Switch.Root, {
2435
+ ref: ref,
2436
+ className: clsx(classNames.root, className),
2437
+ ...otherProps
2438
+ })
2439
+ })
2440
+ });
2441
+ });
2442
+ SwitchRoot.displayName = "SwitchRoot";
2443
+ const SwitchControl = withScaleFeedback(withControlProvider(reactSwitch.Switch.Control, "root"));
2444
+ const SwitchThumb = withControlContext(reactSwitch.Switch.Thumb, "thumb");
2445
+ withContext$4(withStateProps$2(Primitive.span), "label");
2446
+ const SwitchHiddenInput = reactSwitch.Switch.HiddenInput;
2447
+
2448
+ // src/use-layout-effect.tsx
2449
+ var useLayoutEffect2 = globalThis?.document ? React__namespace.useLayoutEffect : ()=>{};
2450
+
2451
+ const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput.textInput);
2452
+ const withFieldStateProps = createWithStateProps([
2453
+ {
2454
+ useContext: reactField.useFieldContext,
2455
+ strict: false
2456
+ }
2457
+ ]);
2458
+ const withStateProps$1 = createWithStateProps([
2459
+ reactTextField.useTextFieldContext,
2460
+ {
2461
+ useContext: reactField.useFieldContext,
2462
+ strict: false
2463
+ }
2464
+ ]);
2465
+ const TextFieldRoot = withProvider$1(withFieldStateProps(reactTextField.TextField.Root), "root");
2466
+ const TextFieldPrefixIcon = withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
2467
+ withContext$3(withStateProps$1(Primitive.span), "prefixText");
2468
+ withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
2469
+ withContext$3(withStateProps$1(Primitive.span), "suffixText");
2470
+ const TextFieldInput = /*#__PURE__*/ React.forwardRef(({ className, ...otherProps }, ref)=>{
2471
+ const classNames = useClassNames$2();
2472
+ const textFieldContext = reactTextField.useTextFieldContext();
2473
+ const fieldContext = reactField.useFieldContext({
2474
+ strict: false
2475
+ });
2476
+ const mergedProps = domUtils.mergeProps(fieldContext ? fieldContext.stateProps : {}, fieldContext ? fieldContext.inputAriaAttributes : {}, textFieldContext.inputProps, fieldContext ? fieldContext.inputProps : {}, otherProps);
2477
+ if (// if not in field, then must have aria-label or aria-labelledby
2478
+ !fieldContext && !otherProps["aria-label"] && !otherProps["aria-labelledby"]) {
2479
+ console.warn("TextFieldInput: Please provide `aria-label` or `aria-labelledby` for accessibility, or put `TextFieldInput` inside a `Field` where a `FieldLabel` is provided.");
2480
+ }
2481
+ return /*#__PURE__*/ jsxRuntime.jsx(reactTextField.TextField.Input, {
2482
+ ref: ref,
2483
+ ...mergedProps,
2484
+ className: clsx(classNames.value, className)
2485
+ });
2486
+ });
2487
+ TextFieldInput.displayName = "TextFieldInput";
2488
+ const TextFieldTextarea = /*#__PURE__*/ React.forwardRef(({ className, autoresize = true, ...otherProps }, ref)=>{
2489
+ const classNames = useClassNames$2();
2490
+ const textFieldContext = reactTextField.useTextFieldContext();
2491
+ const fieldContext = reactField.useFieldContext({
2492
+ strict: false
2493
+ });
2494
+ const mergedProps = domUtils.mergeProps(fieldContext ? fieldContext.stateProps : {}, fieldContext ? fieldContext.inputAriaAttributes : {}, textFieldContext.inputProps, fieldContext ? fieldContext.inputProps : {}, otherProps);
2495
+ if (// if not in field, then must have aria-label or aria-labelledby
2496
+ !fieldContext && !otherProps["aria-label"] && !otherProps["aria-labelledby"]) {
2497
+ console.warn("TextFieldTextarea: Please provide `aria-label` or `aria-labelledby` for accessibility, or put `TextFieldTextarea` inside a `Field` where a `FieldLabel` is provided.");
2498
+ }
2499
+ // referenced from React Spectrum
2500
+ const inputRef = React.useRef(null);
2501
+ // biome-ignore lint/correctness/useExhaustiveDependencies: intended
2502
+ const onHeightChange = React.useCallback(()=>{
2503
+ if (!inputRef.current) return;
2504
+ if (otherProps.style?.height) return;
2505
+ if (!autoresize) return;
2506
+ // Quiet textareas always grow based on their text content.
2507
+ // Standard textareas also grow by default, unless an explicit height is set.
2508
+ const input = inputRef.current;
2509
+ const prevAlignment = input.style.alignSelf;
2510
+ const prevOverflow = input.style.overflow;
2511
+ // Firefox scroll position is lost when overflow: 'hidden' is applied so we skip applying it.
2512
+ // The measure/applied height is also incorrect/reset if we turn on and off
2513
+ // overflow: hidden in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1787062
2514
+ const isFirefox = "MozAppearance" in input.style;
2515
+ if (!isFirefox) {
2516
+ input.style.overflow = "hidden";
2517
+ }
2518
+ input.style.alignSelf = "start";
2519
+ input.style.height = "auto";
2520
+ // offsetHeight - clientHeight accounts for the border/padding.
2521
+ input.style.height = `${input.scrollHeight + (input.offsetHeight - input.clientHeight)}px`;
2522
+ input.style.overflow = prevOverflow;
2523
+ input.style.alignSelf = prevAlignment;
2524
+ }, [
2525
+ inputRef,
2526
+ otherProps.style?.height,
2527
+ autoresize
2528
+ ]);
2529
+ useLayoutEffect2(()=>{
2530
+ if (inputRef.current) {
2531
+ onHeightChange();
2532
+ }
2533
+ }, [
2534
+ onHeightChange,
2535
+ textFieldContext.value,
2536
+ inputRef
2537
+ ]);
2538
+ return /*#__PURE__*/ jsxRuntime.jsx(reactTextField.TextField.Textarea, {
2539
+ ref: composeRefs(inputRef, ref),
2540
+ ...mergedProps,
2541
+ className: clsx(classNames.value, className)
2542
+ });
2543
+ });
2544
+ TextFieldTextarea.displayName = "TextFieldTextarea";
2545
+
2546
+ /** The raw value a live editor reads, distinct from `DataTableColumn.value` (the
2547
+ * accessor sort/search/filter/export share): `editValue` returns whatever TYPE the
2548
+ * editor itself expects -- a real `boolean` for `editor: "switch"`/`type:
2549
+ * "checkbox"`, a `number` for `type: "rating"`, the option's own string for `select`/
2550
+ * `reference`/`actor` -- where `value`/`render` may format that same field for
2551
+ * display. Falls back to the table's own accessor when the column supplies none. */ function resolveEditValue(column, row, fallback) {
2552
+ return column.editValue ? column.editValue(row) : fallback;
2553
+ }
2554
+ function EditPendingIndicator() {
2555
+ return /*#__PURE__*/ jsxRuntime.jsxs("svg", {
2556
+ className: "seed-data-table-edit-pending",
2557
+ width: 14,
2558
+ height: 14,
2559
+ viewBox: "0 0 16 16",
2560
+ fill: "none",
2561
+ role: "presentation",
2562
+ "aria-hidden": "true",
2563
+ children: [
2564
+ /*#__PURE__*/ jsxRuntime.jsx("circle", {
2565
+ cx: 8,
2566
+ cy: 8,
2567
+ r: 6.5,
2568
+ stroke: "currentColor",
2569
+ strokeOpacity: 0.25,
2570
+ strokeWidth: 2
2571
+ }),
2572
+ /*#__PURE__*/ jsxRuntime.jsx("path", {
2573
+ d: "M14.5 8a6.5 6.5 0 0 0-6.5-6.5",
2574
+ stroke: "currentColor",
2575
+ strokeWidth: 2,
2576
+ strokeLinecap: "round"
2577
+ })
2578
+ ]
2579
+ });
2580
+ }
2581
+ function EditError({ message }) {
2582
+ return /*#__PURE__*/ jsxRuntime.jsx("span", {
2583
+ className: "seed-data-table-edit-error",
2584
+ children: message
2585
+ });
2586
+ }
2587
+ ////////////////////////////////////////////////////////////////////////////////////
2588
+ // -- plain editable: text/number (seed TextField) and date/datetime (a native input,
2589
+ // styled the same way, per `cellType`) -- both double-click gated, Enter/blur
2590
+ // commits, Escape cancels. -------------------------------------------------------
2591
+ function TextEditor({ column, value, pending, onCommit, onCancel }) {
2592
+ const [draft, setDraft] = React__namespace.useState(()=>value == null ? "" : String(value));
2593
+ return(// `size="medium"` (seed's smallest fixed `TextField` size, 40px min-height,
2594
+ // `text-input.ts`'s own token) -- the default `size="large"` overflowed a 36px
2595
+ // table row (same class of fix as `toolbar.tsx`'s `SearchBox`, which made the
2596
+ // same call for its own 32px-ish search box).
2597
+ /*#__PURE__*/ jsxRuntime.jsx(TextFieldRoot, {
2598
+ size: "medium",
2599
+ style: {
2600
+ width: "100%"
2601
+ },
2602
+ onClick: (event)=>event.stopPropagation(),
2603
+ onKeyDown: (event)=>event.stopPropagation(),
2604
+ children: /*#__PURE__*/ jsxRuntime.jsx(TextFieldInput, {
2605
+ autoFocus: true,
2606
+ type: column.cellType === "number" ? "number" : "text",
2607
+ "aria-label": `Edit ${column.key}`,
2608
+ value: draft,
2609
+ disabled: pending,
2610
+ onChange: (event)=>setDraft(event.target.value),
2611
+ onBlur: ()=>onCommit(draft),
2612
+ onKeyDown: (event)=>{
2613
+ if (event.key === "Enter") onCommit(draft);
2614
+ else if (event.key === "Escape") onCancel();
2615
+ }
2616
+ })
2617
+ }));
2618
+ }
2619
+ function DateEditor({ column, value, pending, onCommit, onCancel }) {
2620
+ const isDateTime = column.cellType === "datetime";
2621
+ const [draft, setDraft] = React__namespace.useState(()=>isDateTime ? toDateTimeInputValue(value) : toDateInputValue(value));
2622
+ return(// See `TextEditor`'s own comment above -- same `size="medium"` fix.
2623
+ /*#__PURE__*/ jsxRuntime.jsx(TextFieldRoot, {
2624
+ size: "medium",
2625
+ style: {
2626
+ width: "100%"
2627
+ },
2628
+ onClick: (event)=>event.stopPropagation(),
2629
+ onKeyDown: (event)=>event.stopPropagation(),
2630
+ children: /*#__PURE__*/ jsxRuntime.jsx(TextFieldInput, {
2631
+ autoFocus: true,
2632
+ type: isDateTime ? "datetime-local" : "date",
2633
+ "aria-label": `Edit ${column.key}`,
2634
+ value: draft,
2635
+ disabled: pending,
2636
+ onChange: (event)=>setDraft(event.target.value),
2637
+ onBlur: ()=>onCommit(draft),
2638
+ onKeyDown: (event)=>{
2639
+ if (event.key === "Enter") onCommit(draft);
2640
+ else if (event.key === "Escape") onCancel();
2641
+ }
2642
+ })
2643
+ }));
2644
+ }
2645
+ ////////////////////////////////////////////////////////////////////////////////////
2646
+ // -- editor: "select" -- double-click opens seed's own Select, auto-opened (seed's
2647
+ // `SelectRoot` takes `defaultOpen` directly -- unlike crisp's closed-API `Select`,
2648
+ // which needs a synthetic click on its own trigger to start open). -------------
2649
+ function SelectEditor({ column, value, pending, onCommit, onCancel }) {
2650
+ const options = column.options ?? [];
2651
+ const current = value == null ? "" : String(value);
2652
+ return /*#__PURE__*/ jsxRuntime.jsxs(SelectRoot, {
2653
+ // Controlled open, per docs/examples/react/select/controlled-open.tsx --
2654
+ // this editor is only ever mounted while the cell IS editing, so it always
2655
+ // wants to start (and stay) open; `onOpenChange(false)` is what tells us
2656
+ // the popover wants to close for ANY reason (Escape, an outside click, OR
2657
+ // the internal auto-close a single-select commit already triggers), so
2658
+ // `onCancel` alone now covers the Escape gap the old raw `document`
2659
+ // listener existed for, without a second, independent listener.
2660
+ open: true,
2661
+ onOpenChange: (next)=>{
2662
+ if (!next) onCancel();
2663
+ },
2664
+ disabled: pending,
2665
+ value: current ? [
2666
+ current
2667
+ ] : [],
2668
+ onValueChange: (next)=>onCommit(next[0] ?? ""),
2669
+ children: [
2670
+ /*#__PURE__*/ jsxRuntime.jsxs(SelectTrigger, {
2671
+ "aria-label": `Edit ${column.key}`,
2672
+ children: [
2673
+ /*#__PURE__*/ jsxRuntime.jsx(SelectValue, {}),
2674
+ /*#__PURE__*/ jsxRuntime.jsx(SelectPlaceholder, {
2675
+ children: "Select…"
2676
+ }),
2677
+ /*#__PURE__*/ jsxRuntime.jsx(SelectSuffixIcon, {
2678
+ svg: /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownSmallLine_default, {})
2679
+ })
2680
+ ]
2681
+ }),
2682
+ /*#__PURE__*/ jsxRuntime.jsx(SelectPositioner, {
2683
+ children: /*#__PURE__*/ jsxRuntime.jsx(SelectContent, {
2684
+ children: /*#__PURE__*/ jsxRuntime.jsx(SelectScrollArea, {
2685
+ children: options.map((option)=>/*#__PURE__*/ jsxRuntime.jsxs(SelectItem, {
2686
+ value: option.value,
2687
+ label: option.label,
2688
+ children: [
2689
+ /*#__PURE__*/ jsxRuntime.jsx(SelectItemBody, {
2690
+ children: /*#__PURE__*/ jsxRuntime.jsx(SelectItemLabel, {})
2691
+ }),
2692
+ /*#__PURE__*/ jsxRuntime.jsx(SelectItemIndicator, {
2693
+ selected: /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkFatFill_default, {})
2694
+ })
2695
+ ]
2696
+ }, option.value))
2697
+ })
2698
+ })
2699
+ })
2700
+ ]
2701
+ });
2702
+ }
2703
+ ////////////////////////////////////////////////////////////////////////////////////
2704
+ // -- editor: "reference" / "actor" -- same picker, search-enabled, each option
2705
+ // carrying a small avatar (a photo for a real one, a generic person glyph for
2706
+ // none) -- `actor` is `reference` over people specifically (crisp's own #449/
2707
+ // #432 `ReferenceCell` shape), so the two share one component; only the search
2708
+ // placeholder differs. Seed's `Select` has no built-in search combobox (unlike
2709
+ // crisp's own `Select`), so this filters `refOptions` itself against a small
2710
+ // `TextField` rendered as the content's own first row. --------------------------
2711
+ function ReferenceEditor({ column, value, pending, onCommit, onCancel }) {
2712
+ const [query, setQuery] = React__namespace.useState("");
2713
+ const options = column.refOptions ?? [];
2714
+ const filtered = query.trim() ? options.filter((option)=>option.label.toLowerCase().includes(query.trim().toLowerCase())) : options;
2715
+ const current = value == null ? "" : String(value);
2716
+ const isActor = column.editor === "actor";
2717
+ return /*#__PURE__*/ jsxRuntime.jsxs(SelectRoot, {
2718
+ // See `SelectEditor`'s own comment above -- same controlled-open wiring.
2719
+ open: true,
2720
+ onOpenChange: (next)=>{
2721
+ if (!next) onCancel();
2722
+ },
2723
+ disabled: pending,
2724
+ value: current ? [
2725
+ current
2726
+ ] : [],
2727
+ onValueChange: (next)=>onCommit(next[0] ?? ""),
2728
+ children: [
2729
+ /*#__PURE__*/ jsxRuntime.jsxs(SelectTrigger, {
2730
+ "aria-label": `Edit ${column.key}`,
2731
+ children: [
2732
+ /*#__PURE__*/ jsxRuntime.jsx(SelectValue, {}),
2733
+ /*#__PURE__*/ jsxRuntime.jsx(SelectPlaceholder, {
2734
+ children: isActor ? "Assign…" : "Select…"
2735
+ }),
2736
+ /*#__PURE__*/ jsxRuntime.jsx(SelectSuffixIcon, {
2737
+ svg: /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownSmallLine_default, {})
2738
+ })
2739
+ ]
2740
+ }),
2741
+ /*#__PURE__*/ jsxRuntime.jsx(SelectPositioner, {
2742
+ children: /*#__PURE__*/ jsxRuntime.jsxs(SelectContent, {
2743
+ children: [
2744
+ /*#__PURE__*/ jsxRuntime.jsx(TextFieldRoot, {
2745
+ size: "medium",
2746
+ style: {
2747
+ margin: 4
2748
+ },
2749
+ onKeyDown: (event)=>{
2750
+ // The listbox's own typeahead/arrow handling lives on THIS field's
2751
+ // sibling items, not on the field itself -- stop everything except
2752
+ // Escape from also driving the listbox's own keyboard nav. Escape
2753
+ // itself needs no special-casing to "reach" anything any more: it's
2754
+ // SelectContent's own `DismissibleLayer` (Select.tsx) that closes the
2755
+ // listbox, and that dismissal is a document-level listener outside
2756
+ // this React subtree entirely -- a synthetic `stopPropagation` here
2757
+ // was never able to block it either way, so the controlled `open`
2758
+ // above (via `onOpenChange`) is what actually ends this cell's edit.
2759
+ if (event.key !== "Escape") event.stopPropagation();
2760
+ },
2761
+ children: /*#__PURE__*/ jsxRuntime.jsx(TextFieldInput, {
2762
+ autoFocus: true,
2763
+ "aria-label": isActor ? "Search people" : `Search ${column.key}`,
2764
+ placeholder: isActor ? "Search people…" : "Search…",
2765
+ value: query,
2766
+ onChange: (event)=>setQuery(event.target.value)
2767
+ })
2768
+ }),
2769
+ /*#__PURE__*/ jsxRuntime.jsxs(SelectScrollArea, {
2770
+ children: [
2771
+ filtered.length === 0 && /*#__PURE__*/ jsxRuntime.jsx("div", {
2772
+ style: {
2773
+ padding: "6px 12px",
2774
+ opacity: 0.63,
2775
+ fontSize: 13
2776
+ },
2777
+ children: isActor ? "No users" : "No results"
2778
+ }),
2779
+ filtered.map((option)=>/*#__PURE__*/ jsxRuntime.jsxs(SelectItem, {
2780
+ value: option.value,
2781
+ label: option.label,
2782
+ children: [
2783
+ /*#__PURE__*/ jsxRuntime.jsxs(SelectItemBody, {
2784
+ children: [
2785
+ /*#__PURE__*/ jsxRuntime.jsx(ReferenceAvatar, {
2786
+ avatar: option.avatar
2787
+ }),
2788
+ /*#__PURE__*/ jsxRuntime.jsx(SelectItemLabel, {})
2789
+ ]
2790
+ }),
2791
+ /*#__PURE__*/ jsxRuntime.jsx(SelectItemIndicator, {
2792
+ selected: /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkFatFill_default, {})
2793
+ })
2794
+ ]
2795
+ }, option.value))
2796
+ ]
2797
+ })
2798
+ ]
2799
+ })
2800
+ })
2801
+ ]
2802
+ });
2803
+ }
2804
+ function ReferenceAvatar({ avatar }) {
2805
+ return /*#__PURE__*/ jsxRuntime.jsxs(AvatarRoot, {
2806
+ size: "20",
2807
+ children: [
2808
+ avatar && /*#__PURE__*/ jsxRuntime.jsx(AvatarImage, {
2809
+ src: avatar,
2810
+ alt: ""
2811
+ }),
2812
+ /*#__PURE__*/ jsxRuntime.jsx(AvatarFallback, {
2813
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconPersonCircleLine_default, {})
2814
+ })
2815
+ ]
2816
+ });
2817
+ }
2818
+ ////////////////////////////////////////////////////////////////////////////////////
2819
+ // -- always-live editors: no double-click gate, no separate rest/edit state --
2820
+ // interacting with the widget IS the commit gesture (crisp's own #453/#760/#761).
2821
+ function SwitchEditor({ column, value, pending, onCommit }) {
2822
+ const checked = value === true || value === "true";
2823
+ return /*#__PURE__*/ jsxRuntime.jsxs("span", {
2824
+ className: "seed-data-table-edit-picker",
2825
+ children: [
2826
+ /*#__PURE__*/ jsxRuntime.jsxs(SwitchRoot, {
2827
+ size: "16",
2828
+ checked: checked,
2829
+ disabled: pending,
2830
+ onCheckedChange: (next)=>onCommit(String(next)),
2831
+ children: [
2832
+ /*#__PURE__*/ jsxRuntime.jsx(SwitchControl, {
2833
+ children: /*#__PURE__*/ jsxRuntime.jsx(SwitchThumb, {})
2834
+ }),
2835
+ /*#__PURE__*/ jsxRuntime.jsx(SwitchHiddenInput, {
2836
+ "aria-label": `Edit ${column.key}`
2837
+ })
2838
+ ]
2839
+ }),
2840
+ pending && /*#__PURE__*/ jsxRuntime.jsx(EditPendingIndicator, {})
2841
+ ]
2842
+ });
2843
+ }
2844
+ function CheckboxEditor({ column, value, pending, onCommit }) {
2845
+ const checked = value === true || value === "true";
2846
+ return /*#__PURE__*/ jsxRuntime.jsxs("span", {
2847
+ className: "seed-data-table-edit-picker",
2848
+ children: [
2849
+ /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
2850
+ checked: checked,
2851
+ disabled: pending,
2852
+ onCheckedChange: (next)=>onCommit(String(next)),
2853
+ children: [
2854
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
2855
+ children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
2856
+ checked: CHECKBOX_CELL_CHECKED_ICON
2857
+ })
2858
+ }),
2859
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
2860
+ "aria-label": `Edit ${column.key}`
2861
+ })
2862
+ ]
2863
+ }),
2864
+ pending && /*#__PURE__*/ jsxRuntime.jsx(EditPendingIndicator, {})
2865
+ ]
2866
+ });
2867
+ }
2868
+ function RatingEditor({ column, value, pending, onCommit }) {
2869
+ const maxRating = column.maxRating ?? 5;
2870
+ const rating = typeof value === "number" ? value : Number(value) || 0;
2871
+ return /*#__PURE__*/ jsxRuntime.jsxs("span", {
2872
+ className: "seed-data-table-edit-picker",
2873
+ children: [
2874
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
2875
+ role: "group",
2876
+ "aria-label": `Edit ${column.key}`,
2877
+ style: {
2878
+ display: "inline-flex",
2879
+ alignItems: "center",
2880
+ gap: 2
2881
+ },
2882
+ children: Array.from({
2883
+ length: maxRating
2884
+ }, (_, index)=>{
2885
+ const starValue = index + 1;
2886
+ return /*#__PURE__*/ jsxRuntime.jsx("button", {
2887
+ type: "button",
2888
+ disabled: pending,
2889
+ "aria-label": `Set ${starValue} out of ${maxRating}`,
2890
+ "aria-pressed": starValue <= rating,
2891
+ onClick: ()=>onCommit(String(starValue)),
2892
+ style: {
2893
+ border: "none",
2894
+ background: "none",
2895
+ padding: 0,
2896
+ cursor: "pointer",
2897
+ lineHeight: 0
2898
+ },
2899
+ children: /*#__PURE__*/ jsxRuntime.jsx("svg", {
2900
+ width: 16,
2901
+ height: 16,
2902
+ viewBox: "0 0 16 16",
2903
+ fill: "none",
2904
+ "aria-hidden": "true",
2905
+ children: /*#__PURE__*/ jsxRuntime.jsx("path", {
2906
+ d: RATING_STAR_D,
2907
+ fill: starValue <= rating ? RATING_FILLED : RATING_EMPTY
2908
+ })
2909
+ })
2910
+ }, index);
2911
+ })
2912
+ }),
2913
+ pending && /*#__PURE__*/ jsxRuntime.jsx(EditPendingIndicator, {})
2914
+ ]
2915
+ });
2916
+ }
2917
+
2918
+ // Row-narrowing for the toolbar's search box and each column's filter picker -- split
2919
+ // out of DataTable.tsx the way cells.tsx splits out cell-type rendering: both are a
2920
+ // self-contained, independently testable concern DataTable.tsx just calls, rather than
2921
+ // inline `.filter()`/`.sort()` calls piling up in the same component that also owns
2922
+ // markup.
2923
+ /**
2924
+ * A column's `value(row)` if it supplies one, else the raw field at `column.key` --
2925
+ * the single accessor sort/search/filter/export all read through
2926
+ * (DATATABLE-ATTIO-SPEC.md's Column API), so a consumer only teaches the table how to
2927
+ * read a value ONCE rather than once per feature.
2928
+ */ function getColumnValue(column, row) {
2929
+ return column.value ? column.value(row) : row[column.key];
2930
+ }
2931
+ /**
2932
+ * Case-insensitive substring match against a row's stringified value -- crisp's own
2933
+ * `applyFilters` "contains" semantics (`tabletoolbar.tsx`), reused here for the
2934
+ * toolbar's single free-text query rather than a per-column condition. `null`/
2935
+ * `undefined` never match a non-empty query -- there is no text to search.
2936
+ */ function matchesSearch(value, query) {
2937
+ if (!query) return true;
2938
+ if (value == null) return false;
2939
+ return String(value).toLowerCase().includes(query.toLowerCase());
2940
+ }
2941
+ /**
2942
+ * A column's own filter predicate -- "is"/"is not" against a SET of checked values.
2943
+ * One checked value reads as "is X", several as crisp's "is any of X, Y": the exact
2944
+ * same predicate, just more values in the set (crisp's own `applyFilters`,
2945
+ * `tabletoolbar.tsx`: `isanyof` is membership-in-`values`, `is`/`isnot` is equality --
2946
+ * both compare the stringified value, case-insensitively, never a substring). An empty
2947
+ * `values` (the picker is open, nothing checked yet) matches every row -- crisp's own
2948
+ * `applyFilters` drops an operator with nothing to compare against from its "active"
2949
+ * set the same way, rather than treating "nothing checked" as "match nothing".
2950
+ */ function matchesFilter(value, filter) {
2951
+ if (filter.values.length === 0) return true;
2952
+ const text = value == null ? "" : String(value).toLowerCase();
2953
+ const isIncluded = filter.values.some((candidate)=>candidate.toLowerCase() === text);
2954
+ return filter.mode === "isnot" ? !isIncluded : isIncluded;
2955
+ }
2956
+ /**
2957
+ * The toolbar's search query (an OR across `searchable` columns, matching GRC's own
2958
+ * `useFilteredRows`) THEN every column's own filter (an AND across active filters,
2959
+ * matching crisp's `applyFilters`) -- in that order, since a filter chip narrows what
2960
+ * the search box already found, not the other way round. A column with no active
2961
+ * filter (absent from `filters`, or present with an empty `values`) is skipped
2962
+ * entirely rather than treated as "filter out everything".
2963
+ */ function getVisibleRows(data, columns, query, filters) {
2964
+ const searchableColumns = columns.filter((column)=>column.searchable);
2965
+ const activeFilters = Object.entries(filters).filter(([, filter])=>filter.values.length > 0);
2966
+ if (!query && activeFilters.length === 0) return data;
2967
+ return data.filter((row)=>{
2968
+ if (query) {
2969
+ const matchesQuery = searchableColumns.some((column)=>matchesSearch(getColumnValue(column, row), query));
2970
+ if (!matchesQuery) return false;
2971
+ }
2972
+ return activeFilters.every(([columnKey, filter])=>{
2973
+ const column = columns.find((candidate)=>candidate.key === columnKey);
2974
+ if (!column) return true;
2975
+ return matchesFilter(getColumnValue(column, row), filter);
2976
+ });
2977
+ });
2978
+ }
2979
+
2980
+ /** `editable` + one of these shapes needs no double-click gate and no separate rest
2981
+ * display -- interacting with the widget itself IS the commit gesture (crisp's own
2982
+ * #453/#760/#761: `switch`/`rating`/`checkbox` never had a meaningfully different
2983
+ * "rest" state to show). Every other `editable` column opens on double-click. */ function isAlwaysLiveEditable(column) {
2984
+ if (!column.editable) return false;
2985
+ return column.editor === "switch" || column.type === "rating" || column.type === "checkbox";
2986
+ }
2987
+ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
2988
+ const effective = useEffectiveColumns(columns);
2989
+ const { startEdit } = useDataTableContext();
2990
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
2991
+ children: effective.map(({ column }, index)=>{
2992
+ const isFirst = index === 0;
2993
+ const isLast = index === effective.length - 1;
2994
+ const sticky = stickyProps({
2995
+ selectable,
2996
+ stickyFirst,
2997
+ stickyLast,
2998
+ isSelectionCell: false,
2999
+ isFirst,
3000
+ isLast
3001
+ });
3002
+ // A gated `editable` column (everything except the always-live widgets)
3003
+ // opens on double-click -- Attio's own measured gesture (crisp's #411),
3004
+ // matched here rather than a single click, which stays "select this cell"
3005
+ // in crisp's own spreadsheet model and would otherwise be indistinguishable
3006
+ // from this table's plain row click.
3007
+ const gated = column.editable && !isAlwaysLiveEditable(column);
3008
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
3009
+ "data-numeric": domUtils.dataAttr(column.type === "number" || column.type === "currency"),
3010
+ "data-editable": domUtils.dataAttr(!!column.editable),
3011
+ "data-sticky": sticky["data-sticky"],
3012
+ "data-sticky-edge": sticky["data-sticky-edge"],
3013
+ "data-sticky-edge-end": sticky["data-sticky-edge-end"],
3014
+ style: sticky.style,
3015
+ onDoubleClick: gated ? ()=>startEdit(row.id, column.key) : undefined,
3016
+ children: /*#__PURE__*/ jsxRuntime.jsx(CellContent, {
3017
+ column: column,
3018
+ row: row,
3019
+ rowId: row.id,
3020
+ classNames: classNames,
3021
+ locale: locale
3022
+ })
3023
+ }, column.key);
3024
+ })
3025
+ });
3026
+ }
3027
+ /**
3028
+ * Dispatches to whichever of cells.tsx's static renderers or editors.tsx's live
3029
+ * editors applies to THIS column/row right now -- always-live first (no rest state to
3030
+ * fall back to), then a gated editor while the cell is mid-edit, else the plain
3031
+ * display (a custom `render` wins there, same as before `editable` existed; it never
3032
+ * overrides an actual editing/always-live state, matching crisp's own split between
3033
+ * `renderTyped`'s rest-state branch and the editing branches above it).
3034
+ */ function CellContent({ column, row, rowId, classNames, locale }) {
3035
+ const { isEditing, isPending, getEditError, commitEdit, cancelEdit } = useDataTableContext();
3036
+ const rawValue = getColumnValue(column, row);
3037
+ const pending = isPending(rowId, column.key);
3038
+ const error = column.editable ? getEditError(rowId, column.key) : null;
3039
+ const commit = (value)=>commitEdit(rowId, column.key, value);
3040
+ const cancel = ()=>cancelEdit();
3041
+ let node;
3042
+ if (column.editable && column.editor === "switch") {
3043
+ node = /*#__PURE__*/ jsxRuntime.jsx(SwitchEditor, {
3044
+ column: column,
3045
+ value: resolveEditValue(column, row, rawValue),
3046
+ pending: pending,
3047
+ onCommit: commit
3048
+ });
3049
+ } else if (column.editable && column.type === "rating") {
3050
+ node = /*#__PURE__*/ jsxRuntime.jsx(RatingEditor, {
3051
+ column: column,
3052
+ value: resolveEditValue(column, row, rawValue),
3053
+ pending: pending,
3054
+ onCommit: commit
3055
+ });
3056
+ } else if (column.editable && column.type === "checkbox") {
3057
+ node = /*#__PURE__*/ jsxRuntime.jsx(CheckboxEditor, {
3058
+ column: column,
3059
+ value: resolveEditValue(column, row, rawValue),
3060
+ pending: pending,
3061
+ onCommit: commit
3062
+ });
3063
+ } else if (column.editable && isEditing(rowId, column.key)) {
3064
+ if (column.editor === "select") {
3065
+ node = /*#__PURE__*/ jsxRuntime.jsx(SelectEditor, {
3066
+ column: column,
3067
+ value: resolveEditValue(column, row, rawValue),
3068
+ pending: pending,
3069
+ onCommit: commit,
3070
+ onCancel: cancel
3071
+ });
3072
+ } else if (column.editor === "reference" || column.editor === "actor") {
3073
+ node = /*#__PURE__*/ jsxRuntime.jsx(ReferenceEditor, {
3074
+ column: column,
3075
+ value: resolveEditValue(column, row, rawValue),
3076
+ pending: pending,
3077
+ onCommit: commit,
3078
+ onCancel: cancel
3079
+ });
3080
+ } else if (column.cellType === "date" || column.cellType === "datetime") {
3081
+ node = /*#__PURE__*/ jsxRuntime.jsx(DateEditor, {
3082
+ column: column,
3083
+ value: rawValue,
3084
+ pending: pending,
3085
+ onCommit: commit,
3086
+ onCancel: cancel
3087
+ });
3088
+ } else {
3089
+ node = /*#__PURE__*/ jsxRuntime.jsx(TextEditor, {
3090
+ column: column,
3091
+ value: rawValue,
3092
+ pending: pending,
3093
+ onCommit: commit,
3094
+ onCancel: cancel
3095
+ });
3096
+ }
3097
+ } else {
3098
+ node = /*#__PURE__*/ jsxRuntime.jsx(DisplayCellContent, {
3099
+ column: column,
3100
+ row: row,
3101
+ value: rawValue,
3102
+ classNames: classNames,
3103
+ locale: locale
3104
+ });
3105
+ }
3106
+ return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
3107
+ children: [
3108
+ node,
3109
+ error && /*#__PURE__*/ jsxRuntime.jsx(EditError, {
3110
+ message: error
3111
+ })
3112
+ ]
3113
+ });
3114
+ }
3115
+ // The plain (non-editing) Attio cell-type renderer -- unchanged from before this
3116
+ // table grew inline editing, just renamed (it used to be the whole of `CellContent`)
3117
+ // since that name now belongs to the dispatcher above.
3118
+ function DisplayCellContent({ column, row, value, classNames, locale }) {
3119
+ if (column.render) return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3120
+ children: column.render(row)
3121
+ });
3122
+ switch(column.type){
3123
+ case "entity":
3124
+ return /*#__PURE__*/ jsxRuntime.jsx(EntityCell, {
3125
+ value: value,
3126
+ className: classNames.entity,
3127
+ nameClassName: classNames.entityName
3128
+ });
3129
+ case "url":
3130
+ case "email":
3131
+ return /*#__PURE__*/ jsxRuntime.jsx(LinkCell, {
3132
+ column: column,
3133
+ value: value,
3134
+ className: classNames.link
3135
+ });
3136
+ case "tags":
3137
+ return /*#__PURE__*/ jsxRuntime.jsx(TagsCell, {
3138
+ value: value,
3139
+ className: classNames.tagList
3140
+ });
3141
+ case "status":
3142
+ return /*#__PURE__*/ jsxRuntime.jsx(StatusCell, {
3143
+ value: value
3144
+ });
3145
+ case "number":
3146
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3147
+ children: formatNumberCell(value, locale)
3148
+ });
3149
+ case "currency":
3150
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3151
+ children: formatCurrencyCell(value, column.symbol ?? "$", locale)
3152
+ });
3153
+ case "date":
3154
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3155
+ children: formatDateCell(value, locale)
3156
+ });
3157
+ case "datetime":
3158
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3159
+ children: formatDateTimeCell(value, locale)
3160
+ });
3161
+ case "rating":
3162
+ return /*#__PURE__*/ jsxRuntime.jsx(RatingCell, {
3163
+ value: value,
3164
+ maxRating: column.maxRating
3165
+ });
3166
+ case "checkbox":
3167
+ return /*#__PURE__*/ jsxRuntime.jsx(CheckboxCell, {
3168
+ value: value
3169
+ });
3170
+ default:
3171
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3172
+ children: value == null ? null : String(value)
3173
+ });
3174
+ }
3175
+ }
3176
+
3177
+ const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(menu.menu);
3178
+ const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem.menuItem);
3179
+ const withStateProps = createWithStateProps([
3180
+ reactMenu.useMenuContext
3181
+ ]);
3182
+ const withItemStateProps = createWithStateProps([
3183
+ reactMenu.useMenuItemContext
3184
+ ]);
3185
+ const MenuRoot = (props)=>{
3186
+ const [variantProps, otherProps] = menu.menu.splitVariantProps(props);
3187
+ const classNames = menu.menu(variantProps);
3188
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$1, {
3189
+ value: classNames,
3190
+ children: /*#__PURE__*/ jsxRuntime.jsx(ItemPropsProvider, {
3191
+ value: {
3192
+ size: variantProps.size
3193
+ },
3194
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Root, {
3195
+ ...otherProps
3196
+ })
3197
+ })
3198
+ });
3199
+ };
3200
+ reactMenu.Menu.Anchor;
3201
+ const MenuTrigger = reactMenu.Menu.Trigger;
3202
+ const MenuPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...props }, ref)=>{
3203
+ const classNames = useClassNames$1();
3204
+ return /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Positioner, {
3205
+ ref: ref,
3206
+ className: clsx(classNames.positioner, className),
3207
+ ...props
3208
+ });
3209
+ });
3210
+ MenuPositioner.displayName = "MenuPositioner";
3211
+ const MenuContent = withContext$2(reactMenu.Menu.Content, "content");
3212
+ const MenuScrollArea = withContext$2(withStateProps(Primitive.div), "scrollArea");
3213
+ withContext$2(reactMenu.Menu.Group, "group");
3214
+ withContext$2(reactMenu.Menu.GroupLabel, "groupLabel");
3215
+ const MenuItem = /*#__PURE__*/ React__namespace.forwardRef(({ className: propClassName, ...props }, ref)=>{
3216
+ const [variantProps, otherProps] = menuItem.menuItem.splitVariantProps(props);
3217
+ const parentProps = useItemProps();
3218
+ const classNames = menuItem.menuItem({
3219
+ ...parentProps,
3220
+ ...variantProps
3221
+ });
3222
+ return /*#__PURE__*/ jsxRuntime.jsx(ItemClassNamesProvider, {
3223
+ value: classNames,
3224
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Item, {
3225
+ ref: ref,
3226
+ className: clsx(classNames.root, propClassName),
3227
+ ...otherProps
3228
+ })
3229
+ });
3230
+ });
3231
+ MenuItem.displayName = "MenuItem";
3232
+ withItemContext(withItemStateProps(Primitive.div), "body");
3233
+ const MenuItemLabel = withItemContext(withItemStateProps(Primitive.span), "label");
3234
+ withItemContext(withItemStateProps(Primitive.span), "description");
3235
+
3236
+ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast }) {
3237
+ const effective = useEffectiveColumns(columns);
3238
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3239
+ children: effective.map(({ column }, index)=>{
3240
+ const isFirst = index === 0;
3241
+ const isLast = index === effective.length - 1;
3242
+ const sticky = stickyProps({
3243
+ selectable,
3244
+ stickyFirst,
3245
+ stickyLast,
3246
+ isSelectionCell: false,
3247
+ isFirst,
3248
+ isLast
3249
+ });
3250
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableFooterCell, {
3251
+ "data-sticky": sticky["data-sticky"],
3252
+ "data-sticky-edge": sticky["data-sticky-edge"],
3253
+ "data-sticky-edge-end": sticky["data-sticky-edge-end"],
3254
+ style: sticky.style,
3255
+ children: column.summary && /*#__PURE__*/ jsxRuntime.jsx(ColumnCalcCell, {
3256
+ column: column,
3257
+ rows: rows,
3258
+ numeric: column.type === "number" || column.type === "currency",
3259
+ align: column.type === "number" || column.type === "currency" ? "right" : "left"
3260
+ })
3261
+ }, column.key);
3262
+ })
3263
+ });
3264
+ }
3265
+ function formatCalcValue(value) {
3266
+ return value.toLocaleString(undefined, {
3267
+ maximumFractionDigits: 2
3268
+ });
3269
+ }
3270
+ /**
3271
+ * One ColumnCalc footer cell -- a click-anywhere-in-the-cell trigger (crisp's own
3272
+ * measured 246x36 hit target, `columncalc.recipe.ts`) that reads "Add calculation"
3273
+ * until an op is picked, then shows the computed value with its operation name beside
3274
+ * it. The math itself (`computeColumnCalc`) is headless and independently tested;
3275
+ * this component only resolves this column's own values and renders.
3276
+ */ function ColumnCalcCell({ column, rows, numeric, align }) {
3277
+ const { getColumnCalcOp, setColumnCalcOp } = useDataTableContext();
3278
+ const op = getColumnCalcOp(column.key) ?? column.summaryDefaultOp;
3279
+ const values = React__namespace.useMemo(()=>rows.map((row)=>getColumnValue(column, row)), [
3280
+ rows,
3281
+ column
3282
+ ]);
3283
+ const result = React__namespace.useMemo(()=>op ? computeColumnCalc(values, op) : null, [
3284
+ values,
3285
+ op
3286
+ ]);
3287
+ const ops = [
3288
+ ...DATA_TABLE_UNIVERSAL_CALC_OPS,
3289
+ ...numeric ? DATA_TABLE_NUMERIC_CALC_OPS : []
3290
+ ];
3291
+ return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
3292
+ children: [
3293
+ /*#__PURE__*/ jsxRuntime.jsx(MenuTrigger, {
3294
+ asChild: true,
3295
+ children: /*#__PURE__*/ jsxRuntime.jsx("button", {
3296
+ type: "button",
3297
+ className: "seed-data-table-column-calc-trigger",
3298
+ "data-align": align === "right" ? "right" : undefined,
3299
+ children: op && result !== null ? /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
3300
+ children: [
3301
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
3302
+ className: "seed-data-table-column-calc-value",
3303
+ children: formatCalcValue(result)
3304
+ }),
3305
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
3306
+ className: "seed-data-table-column-calc-op",
3307
+ children: DATA_TABLE_CALC_LABEL[op]
3308
+ }),
3309
+ /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
3310
+ size: 14
3311
+ })
3312
+ ]
3313
+ }) : /*#__PURE__*/ jsxRuntime.jsx("span", {
3314
+ className: "seed-data-table-column-calc-placeholder",
3315
+ children: "Add calculation"
3316
+ })
3317
+ })
3318
+ }),
3319
+ /*#__PURE__*/ jsxRuntime.jsx(MenuPositioner, {
3320
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuContent, {
3321
+ style: {
3322
+ minWidth: 160
3323
+ },
3324
+ children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
3325
+ children: [
3326
+ ops.map((candidate)=>/*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3327
+ onClick: ()=>setColumnCalcOp(column.key, candidate),
3328
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3329
+ children: DATA_TABLE_CALC_LABEL[candidate]
3330
+ })
3331
+ }, candidate)),
3332
+ op && /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3333
+ onClick: ()=>setColumnCalcOp(column.key, undefined),
3334
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3335
+ children: "Clear"
3336
+ })
3337
+ })
3338
+ ]
3339
+ })
3340
+ })
3341
+ })
3342
+ ]
3343
+ });
3344
+ }
3345
+
3346
+ const { withProvider, withContext: withContext$1 } = createSlotRecipeContext(segmentedControl.segmentedControl);
3347
+ const SegmentedControlRoot = withProvider(reactSegmentedControl.SegmentedControl.Root, "root");
3348
+ const SegmentedControlIndicator = withContext$1(Primitive.div, "indicator");
3349
+ const SegmentedControlItem = withContext$1(reactSegmentedControl.SegmentedControl.Item, "item");
3350
+ const SegmentedControlItemHiddenInput = reactSegmentedControl.SegmentedControl.ItemHiddenInput;
3351
+
3352
+ /**
3353
+ * The column-edge resize grip -- geometry ported from crisp's `resizablepanel`
3354
+ * recipe's "column" variant (`resizablepanel.recipe.ts`): a 7px hit target straddling
3355
+ * the 1px grid line (not a 1px target of its own -- far too thin to reliably grab),
3356
+ * its 3px inner bar invisible at rest and brand-solid on hover/drag. Uses native
3357
+ * Pointer Capture (`setPointerCapture`) rather than a `window`-level listener pair:
3358
+ * once captured, this SAME element keeps receiving `pointermove`/`pointerup` even
3359
+ * after the cursor leaves it, so the drag math can live entirely in this one
3360
+ * handler set with no effect/cleanup to get wrong.
3361
+ */ function ColumnResizeHandle({ columnKey, width }) {
3362
+ const { beginColumnResize, updateColumnResize, endColumnResize, isColumnResizing } = useDataTableContext();
3363
+ return /*#__PURE__*/ jsxRuntime.jsx("span", {
3364
+ // Mouse/pointer-drag only for now (no keyboard equivalent yet) -- `aria-hidden`
3365
+ // rather than `role="separator"` + `aria-label`, which would otherwise fold
3366
+ // "Resize column" into the OWNING header cell's own accessible name (a
3367
+ // columnheader's name is the concatenation of its own text plus every
3368
+ // descendant's), turning "Name" into "Name Resize column" for anyone using an
3369
+ // accessible-name query -- including this file's own tests.
3370
+ "aria-hidden": "true",
3371
+ className: "seed-data-table-resize-handle",
3372
+ "data-resizing": isColumnResizing(columnKey),
3373
+ draggable: false,
3374
+ onPointerDown: (event)=>{
3375
+ event.stopPropagation();
3376
+ event.preventDefault();
3377
+ event.currentTarget.setPointerCapture(event.pointerId);
3378
+ const startWidth = width ?? event.currentTarget.closest('[role="columnheader"]')?.getBoundingClientRect().width ?? 150;
3379
+ beginColumnResize(columnKey, event.clientX, startWidth);
3380
+ },
3381
+ onPointerMove: (event)=>{
3382
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
3383
+ updateColumnResize(event.clientX);
3384
+ }
3385
+ },
3386
+ onPointerUp: (event)=>{
3387
+ event.currentTarget.releasePointerCapture(event.pointerId);
3388
+ endColumnResize();
3389
+ },
3390
+ children: /*#__PURE__*/ jsxRuntime.jsx("span", {
3391
+ className: "seed-data-table-resize-handle-inner"
3392
+ })
3393
+ });
3394
+ }
3395
+
3396
+ // Same fix as cells.tsx's `CheckboxCell`/editors.tsx's `CheckboxEditor` -- seed's
3397
+ // `CheckboxIndicator` needs a real icon for its `checked` prop (it renders exactly
3398
+ // what's passed there, nothing automatically); `checked={null}` here used to render an
3399
+ // empty box even once a value was actually checked. 12px, matching the row-selection
3400
+ // checkbox's own already-correct icon size (`DataTable.tsx`'s `SELECTION_ICON_SIZE`).
3401
+ const FILTER_CHECKED_ICON = /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkLine_default, {
3402
+ size: 12
3403
+ });
3404
+ // Fix (bug report): karrot icons default to a 24px intrinsic `width`/`height`
3405
+ // SVG ATTRIBUTE (not CSS) -- inside a fixed 16px `headerCellIcon` box (a flex
3406
+ // item), flexbox only auto-shrinks the MAIN axis (width) to fit, leaving the
3407
+ // CROSS axis (height) at its full 24px intrinsic size, so the glyph rendered
3408
+ // visibly oversized/non-square and spilled out of its slot (measured live:
3409
+ // 16×24, not 16×16). Passing `size` sets both attributes directly, sidestepping
3410
+ // the mismatch entirely rather than fighting it with more CSS. Matches crisp's
3411
+ // own measured `.crisp-datatable-chevron` size (14px).
3412
+ const SORT_ICON_SIZE = 14;
3413
+ const DEFAULT_SORT_ICON = {
3414
+ idle: /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
3415
+ size: SORT_ICON_SIZE
3416
+ }),
3417
+ asc: /*#__PURE__*/ jsxRuntime.jsx(IconChevronUpLine_default, {
3418
+ size: SORT_ICON_SIZE
3419
+ }),
3420
+ desc: /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
3421
+ size: SORT_ICON_SIZE
3422
+ })
3423
+ };
3424
+ /**
3425
+ * Rendered for EVERY sortable column, sorted or not -- unlike a naive "only show the
3426
+ * active column's arrow", Attio reveals a neutral chevron on ANY sortable header on
3427
+ * hover (`data-chevron`'s own CSS in `data-table.ts` owns the opacity: 0 at rest, 0.6
3428
+ * on header hover, 1 + full foreground once sorted). Conditionally rendering only the
3429
+ * sorted state, the way this component used to, has nothing for that hover state to
3430
+ * reveal.
3431
+ */ function SortIndicator({ columnKey, icon }) {
3432
+ const { sort } = useDataTableContext();
3433
+ const isSorted = sort?.key === columnKey;
3434
+ const resolved = isSorted ? sort?.dir === "asc" ? icon?.asc ?? DEFAULT_SORT_ICON.asc : icon?.desc ?? DEFAULT_SORT_ICON.desc : icon?.idle ?? DEFAULT_SORT_ICON.idle;
3435
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellIcon, {
3436
+ "aria-hidden": true,
3437
+ "data-chevron": "",
3438
+ draggable: false,
3439
+ children: resolved
3440
+ });
3441
+ }
3442
+ /** Attio's own condition word: "Is"/"Is not" alone with zero or one value checked,
3443
+ * "Is any of"/"Is not any of" once a second value joins the set -- the underlying
3444
+ * predicate never changes (`useDataTableFilter.ts`'s own comment: membership in a
3445
+ * set of one IS "is X"), only the word describing it does. */ function filterConditionLabel(mode, hasMultipleValues) {
3446
+ if (mode === "isnot") return hasMultipleValues ? "Is not any of" : "Is not";
3447
+ return hasMultipleValues ? "Is any of" : "Is";
3448
+ }
3449
+ /**
3450
+ * A column's "is / is not / is any of" checkbox filter picker (DATATABLE-ATTIO-
3451
+ * SPEC.md's Feature contract), Attio's own chrome: a two-way condition control above a
3452
+ * checkbox value list, both built from seed's own documented components -- `Menu` for
3453
+ * the popover shell (`MenuRoot`/`Trigger`/`Positioner`/`Content`, unchanged), seed's
3454
+ * `SegmentedControl` (docs/content/react/components/segmented-control.mdx) for the
3455
+ * is/is-not toggle in place of the two hand-styled buttons (hardcoded hex, no seed
3456
+ * token) this used to be, and plain `<label>` + `CheckboxRoot` rows for the value
3457
+ * list -- NOT `MenuItem` there either: `MenuItem`'s own `getItemProps` (`useMenu.ts`)
3458
+ * closes the menu unconditionally after every click, which is correct for a normal
3459
+ * command menu but would close this ENTIRE popover after a single condition pick or a
3460
+ * single checked value -- exactly wrong for a control meant to stay open while the
3461
+ * condition changes and several boxes get checked. `SegmentedControl` has no such
3462
+ * auto-close (it's a plain controlled radio group, not a menu), which is exactly why
3463
+ * it -- not `MenuItem` -- is the documented component that fits here.
3464
+ */ function FilterMenu({ column }) {
3465
+ const { filters, toggleFilterValue, setFilterMode } = useDataTableContext();
3466
+ const values = column.filterValues ?? [];
3467
+ const current = filters[column.key];
3468
+ const mode = current?.mode ?? "is";
3469
+ const checkedValues = current?.values ?? [];
3470
+ const hasMultipleValues = checkedValues.length > 1;
3471
+ const columnLabel = typeof column.header === "string" ? column.header : column.key;
3472
+ // A filter trigger lives INSIDE a (possibly sortable) header cell, whose own
3473
+ // onClick/onKeyDown toggle sort -- stop the event here so opening the filter
3474
+ // picker never also reorders the rows out from under it.
3475
+ const stopPropagation = (event)=>event.stopPropagation();
3476
+ return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
3477
+ children: [
3478
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellIcon, {
3479
+ asChild: true,
3480
+ onClick: stopPropagation,
3481
+ onKeyDown: stopPropagation,
3482
+ draggable: false,
3483
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuTrigger, {
3484
+ "aria-label": `Filter ${columnLabel}`,
3485
+ "data-active": domUtils.dataAttr(checkedValues.length > 0),
3486
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconSlider2HorizontalLine_default, {
3487
+ size: 16
3488
+ })
3489
+ })
3490
+ }),
3491
+ /*#__PURE__*/ jsxRuntime.jsx(MenuPositioner, {
3492
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuContent, {
3493
+ style: {
3494
+ minWidth: 180
3495
+ },
3496
+ children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
3497
+ children: [
3498
+ /*#__PURE__*/ jsxRuntime.jsx("div", {
3499
+ style: {
3500
+ padding: "0 8px"
3501
+ },
3502
+ children: /*#__PURE__*/ jsxRuntime.jsxs(SegmentedControlRoot, {
3503
+ "aria-label": `${columnLabel} filter condition`,
3504
+ value: mode,
3505
+ onValueChange: (next)=>setFilterMode(column.key, next),
3506
+ style: {
3507
+ width: "100%"
3508
+ },
3509
+ children: [
3510
+ /*#__PURE__*/ jsxRuntime.jsxs(SegmentedControlItem, {
3511
+ value: "is",
3512
+ children: [
3513
+ /*#__PURE__*/ jsxRuntime.jsx(SegmentedControlItemHiddenInput, {}),
3514
+ filterConditionLabel("is", hasMultipleValues)
3515
+ ]
3516
+ }),
3517
+ /*#__PURE__*/ jsxRuntime.jsxs(SegmentedControlItem, {
3518
+ value: "isnot",
3519
+ children: [
3520
+ /*#__PURE__*/ jsxRuntime.jsx(SegmentedControlItemHiddenInput, {}),
3521
+ filterConditionLabel("isnot", hasMultipleValues)
3522
+ ]
3523
+ }),
3524
+ /*#__PURE__*/ jsxRuntime.jsx(SegmentedControlIndicator, {})
3525
+ ]
3526
+ })
3527
+ }),
3528
+ values.map((value)=>// `CheckboxRoot` is ALREADY a `<label>` (`Checkbox.tsx`'s own
3529
+ // `Primitive.label`) wrapping the hidden input + `CheckboxLabel` text --
3530
+ // wrapping it in a SECOND `<label>` here used to nest one label inside
3531
+ // another, which is invalid HTML and made a click on the row's own text
3532
+ // reach the checkbox's native activation twice (once via each label),
3533
+ // toggling it on then immediately back off. `CheckboxRoot` takes the
3534
+ // row's own layout via `style` directly instead.
3535
+ /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
3536
+ checked: checkedValues.includes(value),
3537
+ onCheckedChange: ()=>toggleFilterValue(column.key, value),
3538
+ style: {
3539
+ display: "flex",
3540
+ alignItems: "center",
3541
+ gap: 8,
3542
+ padding: "4px 8px",
3543
+ cursor: "pointer"
3544
+ },
3545
+ children: [
3546
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
3547
+ children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
3548
+ checked: FILTER_CHECKED_ICON
3549
+ })
3550
+ }),
3551
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxLabel, {
3552
+ children: value
3553
+ }),
3554
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
3555
+ "aria-label": value
3556
+ })
3557
+ ]
3558
+ }, value))
3559
+ ]
3560
+ })
3561
+ })
3562
+ })
3563
+ ]
3564
+ });
3565
+ }
3566
+ /** Every header cell, in the effective (ordered, visible) order -- a standalone
3567
+ * component (not inlined in `DataTableInner`'s render) so it alone re-renders when
3568
+ * `visibleColumnOrder`/labels/widths change, without also re-deriving `sortedData`'s
3569
+ * render above it. */ function ColumnHeaderCells({ columns, columnMenu, reorderable, resizable, sortIcon, selectable, stickyFirst, stickyLast }) {
3570
+ const { draggingColumnKey, beginColumnDrag, dragOverColumn, endColumnDrag } = useDataTableContext();
3571
+ const effective = useEffectiveColumns(columns);
3572
+ const [renamingKey, setRenamingKey] = React__namespace.useState(null);
3573
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3574
+ children: effective.map(({ column, label, width }, index)=>{
3575
+ const isFirst = index === 0;
3576
+ const isLast = index === effective.length - 1;
3577
+ const sticky = stickyProps({
3578
+ selectable,
3579
+ stickyFirst,
3580
+ stickyLast,
3581
+ isSelectionCell: false,
3582
+ isFirst,
3583
+ isLast
3584
+ });
3585
+ return /*#__PURE__*/ jsxRuntime.jsxs(DataTableHeaderCell, {
3586
+ columnKey: column.key,
3587
+ sortable: column.sortable,
3588
+ "data-column-key": column.key,
3589
+ draggable: reorderable && renamingKey !== column.key,
3590
+ onDragStart: (event)=>{
3591
+ event.dataTransfer.effectAllowed = "move";
3592
+ beginColumnDrag(column.key);
3593
+ },
3594
+ onDragOver: (event)=>{
3595
+ event.preventDefault();
3596
+ if (draggingColumnKey && draggingColumnKey !== column.key) dragOverColumn(column.key);
3597
+ },
3598
+ onDrop: (event)=>event.preventDefault(),
3599
+ onDragEnd: endColumnDrag,
3600
+ "data-dragging": domUtils.dataAttr(draggingColumnKey === column.key),
3601
+ style: sticky.style,
3602
+ "data-sticky": sticky["data-sticky"],
3603
+ "data-sticky-edge": sticky["data-sticky-edge"],
3604
+ "data-sticky-edge-end": sticky["data-sticky-edge-end"],
3605
+ children: [
3606
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellInner, {
3607
+ children: renamingKey === column.key ? /*#__PURE__*/ jsxRuntime.jsx(ColumnRenameInput, {
3608
+ column: column,
3609
+ initialLabel: typeof label === "string" ? label : column.key,
3610
+ onDone: ()=>setRenamingKey(null)
3611
+ }) : /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
3612
+ children: [
3613
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellLabel, {
3614
+ children: label
3615
+ }),
3616
+ column.filterValues && column.filterValues.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(FilterMenu, {
3617
+ column: column
3618
+ }),
3619
+ column.sortable && /*#__PURE__*/ jsxRuntime.jsx(SortIndicator, {
3620
+ columnKey: column.key,
3621
+ icon: sortIcon
3622
+ }),
3623
+ columnMenu && /*#__PURE__*/ jsxRuntime.jsx(ColumnMenu, {
3624
+ column: column,
3625
+ onRename: ()=>setRenamingKey(column.key)
3626
+ })
3627
+ ]
3628
+ })
3629
+ }),
3630
+ resizable && column.resizable !== false && /*#__PURE__*/ jsxRuntime.jsx(ColumnResizeHandle, {
3631
+ columnKey: column.key,
3632
+ width: width
3633
+ })
3634
+ ]
3635
+ }, column.key);
3636
+ })
3637
+ });
3638
+ }
3639
+ function ColumnRenameInput({ column, initialLabel, onDone }) {
3640
+ const { renameColumn } = useDataTableContext();
3641
+ const [value, setValue] = React__namespace.useState(initialLabel);
3642
+ // Opening this input is ITSELF the result of clicking a `MenuItem` -- the menu's own
3643
+ // close (which happens on that same click) restores focus to its trigger a beat
3644
+ // later, and that focus move fires as a `blur` on this input the instant it mounts
3645
+ // with `autoFocus`. Committing on THAT blur would silently re-close the editor
3646
+ // before anyone saw it (found writing this file's own rename test, which failed
3647
+ // with the header reverting to its plain label with no visible input in between).
3648
+ // `ready` flips true one frame after mount, so only a blur after that -- an actual
3649
+ // click/tab away -- commits.
3650
+ const readyRef = React__namespace.useRef(false);
3651
+ const inputRef = React__namespace.useRef(null);
3652
+ React__namespace.useEffect(()=>{
3653
+ // The menu's close restores focus to its trigger one frame after this input
3654
+ // mounts, stealing focus from `autoFocus`. Re-grab it in the SAME frame (after
3655
+ // the restore) and select the text, so the user can type/replace immediately.
3656
+ const frame = requestAnimationFrame(()=>{
3657
+ readyRef.current = true;
3658
+ inputRef.current?.focus();
3659
+ inputRef.current?.select();
3660
+ });
3661
+ return ()=>cancelAnimationFrame(frame);
3662
+ }, []);
3663
+ const commit = ()=>{
3664
+ if (value.trim()) renameColumn(column.key, value.trim());
3665
+ onDone();
3666
+ };
3667
+ return(// A bare, minimal input -- NOT seed's `TextField` (its labeled root measures
3668
+ // ~52px tall, per DATATABLE-ATTIO-SPEC.md's own note; this has to fit a 40px
3669
+ // header cell with no label/description chrome of its own). Seed has no lighter
3670
+ // input primitive at this level, so this follows crisp's own measured
3671
+ // `.crisp-datatable-th-rename` (`datatable.recipe.ts`) instead: full-width,
3672
+ // header-row height, a small inset, no border (an inset brand-solid box-shadow
3673
+ // stands in for a focus ring), the header's own font weight/size inherited
3674
+ // rather than a new size introduced for one input.
3675
+ /*#__PURE__*/ jsxRuntime.jsx("input", {
3676
+ ref: inputRef,
3677
+ // biome-ignore lint/a11y/noAutofocus: opening this input IS the user's own click a moment earlier (crisp's `.crisp-datatable-th-rename` does the same), not an unannounced page-load autofocus.
3678
+ autoFocus: true,
3679
+ "aria-label": `Rename ${initialLabel}`,
3680
+ value: value,
3681
+ onClick: (event)=>event.stopPropagation(),
3682
+ onChange: (event)=>setValue(event.target.value),
3683
+ onBlur: ()=>{
3684
+ if (readyRef.current) commit();
3685
+ },
3686
+ onKeyDown: (event)=>{
3687
+ event.stopPropagation();
3688
+ if (event.key === "Enter") commit();
3689
+ if (event.key === "Escape") onDone();
3690
+ },
3691
+ // Bare inline input: no border/ring/background/radius -- it reads as the
3692
+ // header label made editable in place, not a boxed field.
3693
+ style: {
3694
+ width: "100%",
3695
+ minWidth: 0,
3696
+ height: "100%",
3697
+ margin: 0,
3698
+ padding: 0,
3699
+ border: "none",
3700
+ borderRadius: 0,
3701
+ background: "transparent",
3702
+ boxShadow: "none",
3703
+ font: "inherit",
3704
+ fontWeight: "var(--seed-font-weight-medium)",
3705
+ color: "var(--seed-color-fg-neutral)",
3706
+ outline: "none"
3707
+ }
3708
+ }));
3709
+ }
3710
+ /** The per-column header menu (rename / hide / move left / move right) -- seed
3711
+ * `Menu`, opened from the same `headerCellIcon` slot the filter trigger and sort
3712
+ * chevron already share. Hover/focus-reveal (`data-column-menu`'s own CSS in
3713
+ * `data-table.ts`) -- crisp's own header shows the trigger only on hover, not
3714
+ * permanently, so this one carries its own marker attribute distinct from that
3715
+ * slot's OTHER occupants (the sort chevron, the filter trigger), which stay visible
3716
+ * under their own separate rules regardless of hover. */ function ColumnMenu({ column, onRename }) {
3717
+ const { moveColumn, hideColumn } = useDataTableContext();
3718
+ const columnLabel = typeof column.header === "string" ? column.header : column.key;
3719
+ const stopPropagation = (event)=>event.stopPropagation();
3720
+ return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
3721
+ children: [
3722
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellIcon, {
3723
+ asChild: true,
3724
+ "data-column-menu": "",
3725
+ onClick: stopPropagation,
3726
+ onKeyDown: stopPropagation,
3727
+ draggable: false,
3728
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuTrigger, {
3729
+ "aria-label": `${columnLabel} column menu`,
3730
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconDot3HorizontalLine_default, {
3731
+ size: 16
3732
+ })
3733
+ })
3734
+ }),
3735
+ /*#__PURE__*/ jsxRuntime.jsx(MenuPositioner, {
3736
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuContent, {
3737
+ style: {
3738
+ minWidth: 160
3739
+ },
3740
+ children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
3741
+ children: [
3742
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3743
+ onClick: onRename,
3744
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3745
+ children: "Rename"
3746
+ })
3747
+ }),
3748
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3749
+ onClick: ()=>moveColumn(column.key, "left"),
3750
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3751
+ children: "Move left"
3752
+ })
3753
+ }),
3754
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3755
+ onClick: ()=>moveColumn(column.key, "right"),
3756
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3757
+ children: "Move right"
3758
+ })
3759
+ }),
3760
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3761
+ onClick: ()=>hideColumn(column.key),
3762
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3763
+ children: "Hide column"
3764
+ })
3765
+ })
3766
+ ]
3767
+ })
3768
+ })
3769
+ })
3770
+ ]
3771
+ });
3772
+ }
3773
+
3774
+ __insertCSS(".seed-data-table__root{box-sizing:border-box;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{flex:1 1 0%;max-width:280px}.seed-data-table__count{flex-shrink:0;margin-inline-start:auto;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
3775
+
3776
+ const createClassName = (className, variants, compoundVariants = [])=>{
3777
+ const variantKeys = Object.keys(variants);
3778
+ const variantValues = variantKeys.map((key)=>variants[key]);
3779
+ const variantClassName = variantKeys.map((key, index)=>`${className}--${key}_${variantValues[index]}`).join(" ");
3780
+ const compountVariantClassName = compoundVariants.filter((compoundVariant)=>Object.keys(compoundVariant).every((key)=>compoundVariant[key] === variants[key])).map((compoundVariant)=>`${className}--${Object.keys(compoundVariant).map((key)=>{
3781
+ return `${key}_${compoundVariant[key]}`;
3782
+ }).join("-")}`).join(" ");
3783
+ return [
3784
+ className,
3785
+ variantClassName,
3786
+ compountVariantClassName
3787
+ ].filter(Boolean).join(" ");
3788
+ };
3789
+ function mergeVariants(a, b) {
3790
+ const result = {
3791
+ ...a
3792
+ };
3793
+ for(const k in b){
3794
+ if (b[k] != null) {
3795
+ result[k] = b[k];
3796
+ }
3797
+ }
3798
+ return result;
3799
+ }
3800
+ function splitVariantProps(props, variantMap) {
3801
+ const variantProps = {};
3802
+ const otherProps = {};
3803
+ for(const key in props){
3804
+ if (variantMap[key] != null) {
3805
+ variantProps[key] = props[key];
3806
+ } else {
3807
+ otherProps[key] = props[key];
3808
+ }
3809
+ }
3810
+ return [
3811
+ variantProps,
3812
+ otherProps
3813
+ ];
3814
+ }
3815
+
3816
+ const dataTableSlotNames = [
3817
+ [
3818
+ "root",
3819
+ "seed-data-table__root"
3820
+ ],
3821
+ [
3822
+ "table",
3823
+ "seed-data-table__table"
3824
+ ],
3825
+ [
3826
+ "header",
3827
+ "seed-data-table__header"
3828
+ ],
3829
+ [
3830
+ "headerCell",
3831
+ "seed-data-table__headerCell"
3832
+ ],
3833
+ [
3834
+ "headerCellInner",
3835
+ "seed-data-table__headerCellInner"
3836
+ ],
3837
+ [
3838
+ "headerCellLabel",
3839
+ "seed-data-table__headerCellLabel"
3840
+ ],
3841
+ [
3842
+ "headerCellIcon",
3843
+ "seed-data-table__headerCellIcon"
3844
+ ],
3845
+ [
3846
+ "row",
3847
+ "seed-data-table__row"
3848
+ ],
3849
+ [
3850
+ "cell",
3851
+ "seed-data-table__cell"
3852
+ ],
3853
+ [
3854
+ "entity",
3855
+ "seed-data-table__entity"
3856
+ ],
3857
+ [
3858
+ "entityName",
3859
+ "seed-data-table__entityName"
3860
+ ],
3861
+ [
3862
+ "tagList",
3863
+ "seed-data-table__tagList"
3864
+ ],
3865
+ [
3866
+ "link",
3867
+ "seed-data-table__link"
3868
+ ],
3869
+ [
3870
+ "toolbar",
3871
+ "seed-data-table__toolbar"
3872
+ ],
3873
+ [
3874
+ "search",
3875
+ "seed-data-table__search"
3876
+ ],
3877
+ [
3878
+ "count",
3879
+ "seed-data-table__count"
3880
+ ],
3881
+ [
3882
+ "scrollArea",
3883
+ "seed-data-table__scrollArea"
3884
+ ],
3885
+ [
3886
+ "footer",
3887
+ "seed-data-table__footer"
3888
+ ],
3889
+ [
3890
+ "footerCell",
3891
+ "seed-data-table__footerCell"
3892
+ ],
3893
+ [
3894
+ "emptyState",
3895
+ "seed-data-table__emptyState"
3896
+ ],
3897
+ [
3898
+ "loadingState",
3899
+ "seed-data-table__loadingState"
3900
+ ],
3901
+ [
3902
+ "addRow",
3903
+ "seed-data-table__addRow"
3904
+ ],
3905
+ [
3906
+ "addRowCell",
3907
+ "seed-data-table__addRowCell"
3908
+ ],
3909
+ [
3910
+ "selectionCell",
3911
+ "seed-data-table__selectionCell"
3912
+ ]
3913
+ ];
3914
+ const defaultVariant = {
3915
+ "surface": "card",
3916
+ "interactive": false
3917
+ };
3918
+ const compoundVariants = [];
3919
+ const dataTableVariantMap = {
3920
+ "surface": [
3921
+ "card",
3922
+ "bleed"
3923
+ ],
3924
+ "interactive": [
3925
+ true,
3926
+ false
3927
+ ]
3928
+ };
3929
+ function dataTable(props) {
3930
+ return Object.fromEntries(dataTableSlotNames.map(([slot, className])=>{
3931
+ return [
3932
+ slot,
3933
+ createClassName(className, mergeVariants(defaultVariant, props), compoundVariants)
3934
+ ];
3935
+ }));
3936
+ }
3937
+ Object.assign(dataTable, {
3938
+ splitVariantProps: (props)=>splitVariantProps(props, dataTableVariantMap)
3939
+ }); // @recipe(seed): data-table
3940
+
3941
+ // The one `createSlotRecipeContext(dataTable)` call every sibling file in this
3942
+ // directory shares -- `ClassNamesProvider` and `withContext` must be the SAME
3943
+ // binding everywhere (a slot component built from a second, independent call would
3944
+ // read a DIFFERENT React context and never see the classNames `DataTableInner`
3945
+ // provides), so this is the one place that calls `createSlotRecipeContext`, and every
3946
+ // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3947
+ // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3948
+ // than re-deriving its own.
3949
+ const { ClassNamesProvider, withContext, useClassNames } = createSlotRecipeContext(dataTable);
3950
+
3951
+ /**
3952
+ * A small inline spinner, not seed's own `ProgressCircle` -- that component's
3953
+ * smallest size (24px) is already bigger than crisp's own measured 16px loading
3954
+ * icon, the same gap `editors.tsx`'s `EditPendingIndicator` already works around for
3955
+ * its own (smaller still, 14px) in-cell pending spinner. Same shape, one size up,
3956
+ * reusing the SAME global `rotate` keyframe (this file's own qvism recipe slot
3957
+ * nests `.seed-data-table-loading-spinner` under it) rather than a third near-
3958
+ * identical spinner.
3959
+ */ function LoadingSpinner() {
3960
+ return /*#__PURE__*/ jsxRuntime.jsxs("svg", {
3961
+ className: "seed-data-table-loading-spinner",
3962
+ width: 16,
3963
+ height: 16,
3964
+ viewBox: "0 0 16 16",
3965
+ fill: "none",
3966
+ role: "presentation",
3967
+ "aria-hidden": "true",
3968
+ children: [
3969
+ /*#__PURE__*/ jsxRuntime.jsx("circle", {
3970
+ cx: 8,
3971
+ cy: 8,
3972
+ r: 6.5,
3973
+ stroke: "currentColor",
3974
+ strokeOpacity: 0.25,
3975
+ strokeWidth: 2
3976
+ }),
3977
+ /*#__PURE__*/ jsxRuntime.jsx("path", {
3978
+ d: "M14.5 8a6.5 6.5 0 0 0-6.5-6.5",
3979
+ stroke: "currentColor",
3980
+ strokeWidth: 2,
3981
+ strokeLinecap: "round"
3982
+ })
3983
+ ]
3984
+ });
3985
+ }
3986
+ /** The full-body empty/error row's own content -- one `<tr>`/`<td colSpan>` wrapping
3987
+ * this, rendered by `DataTable.tsx` itself (it already owns `totalCols`). Shared by
3988
+ * `empty` and `error` alike, matching crisp's own single `.crisp-datatable-empty`
3989
+ * class doing double duty for both. */ function EmptyStateContent({ children, classNames }) {
3990
+ return /*#__PURE__*/ jsxRuntime.jsx("div", {
3991
+ className: classNames.emptyState,
3992
+ children: children
3993
+ });
3994
+ }
3995
+ /** The full-body loading row's own content (spinner + `loadingLabel`). */ function LoadingStateContent({ label, classNames }) {
3996
+ return /*#__PURE__*/ jsxRuntime.jsxs("div", {
3997
+ className: classNames.loadingState,
3998
+ children: [
3999
+ /*#__PURE__*/ jsxRuntime.jsx(LoadingSpinner, {}),
4000
+ label
4001
+ ]
4002
+ });
4003
+ }
4004
+ /**
4005
+ * Infinite scroll's own sentinel + "loading more" row -- the LAST child of
4006
+ * `<tbody>` while `hasMore` (the headless `useDataTableInfiniteScroll`'s own
4007
+ * return, read off context the same way `SelectAllCheckbox` reads `selectionState`).
4008
+ * Renders nothing once `hasMore` is false -- there is no manual pagination
4009
+ * fallback, matching DATATABLE-ATTIO-SPEC.md's auto-load-only contract. While a
4010
+ * fetch is in flight (`loadingMore`) the SAME row shows the compact spinner
4011
+ * (`loadingState`'s own `[data-inline]` modifier, a normal 36px row); at rest it's
4012
+ * an empty sentinel row the IntersectionObserver watches.
4013
+ */ function InfiniteScrollRow({ colSpan, classNames, loadingLabel }) {
4014
+ const { sentinelRef, hasMore, loadingMore } = useDataTableContext();
4015
+ if (!hasMore) return null;
4016
+ return /*#__PURE__*/ jsxRuntime.jsx("tr", {
4017
+ ref: sentinelRef,
4018
+ "aria-hidden": loadingMore ? undefined : "true",
4019
+ children: /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
4020
+ colSpan: colSpan,
4021
+ children: loadingMore && /*#__PURE__*/ jsxRuntime.jsxs("div", {
4022
+ className: classNames.loadingState,
4023
+ "data-inline": "",
4024
+ children: [
4025
+ /*#__PURE__*/ jsxRuntime.jsx(LoadingSpinner, {}),
4026
+ loadingLabel
4027
+ ]
4028
+ })
4029
+ })
4030
+ });
4031
+ }
4032
+ /** The trailing "+ New" add-row footer -- crisp's own `.crisp-datatable-addrow`
4033
+ * inside a zero-padding `.crisp-datatable-addcell`, its own `<tfoot>` (a table can
4034
+ * have more than one; the ColumnCalc summary footer, when both are present, is a
4035
+ * separate one, matching crisp's own two independent `<TableFooter>` rows). */ function AddRowFooter({ onAddRow, addRowLabel, colSpan, classNames }) {
4036
+ return /*#__PURE__*/ jsxRuntime.jsx("tfoot", {
4037
+ children: /*#__PURE__*/ jsxRuntime.jsx("tr", {
4038
+ children: /*#__PURE__*/ jsxRuntime.jsx("td", {
4039
+ className: classNames.addRowCell,
4040
+ colSpan: colSpan,
4041
+ children: /*#__PURE__*/ jsxRuntime.jsxs("button", {
4042
+ type: "button",
4043
+ className: classNames.addRow,
4044
+ onClick: onAddRow,
4045
+ children: [
4046
+ /*#__PURE__*/ jsxRuntime.jsx(IconPlusLine_default, {
4047
+ size: 15
4048
+ }),
4049
+ addRowLabel
4050
+ ]
4051
+ })
4052
+ })
4053
+ })
4054
+ });
4055
+ }
4056
+
4057
+ const DataTableToolbar = withContext(Primitive.div, "toolbar");
4058
+ const DataTableCount = withContext(Primitive.span, "count");
4059
+ /**
4060
+ * Fix (recurring bug): the search box is seed's own DOCUMENTED `TextField`
4061
+ * composition -- `TextFieldRoot` (`variant="outline" size="medium"`) +
4062
+ * `TextFieldPrefixIcon` + `TextFieldInput`, exactly `docs/examples/react/
4063
+ * text-field-input/prefix.tsx` -- not that same trio fought with a custom
4064
+ * `classNames.search` overriding its height/padding/icon position with `!important`.
4065
+ * That override (this file's own previous state) is what kept mis-placing the icon:
4066
+ * every fix landed on ANOTHER property the override was still fighting (the icon's own
4067
+ * intrinsic SVG size, its inherited margin, the root's own min-height beating a plain
4068
+ * `height` override) because the override was never the right layer to fix it at.
4069
+ * `size="medium"`'s built-in prefix-icon spacing (a flex `gap` + edge margins, not
4070
+ * absolute positioning) is what `text-input.ts`'s own variant CSS already gets right,
4071
+ * so it needs no help from this component at all -- `search` (recipe.ts) is now just
4072
+ * this box's own layout constraint (`flex`, `max-width`) within the toolbar row, not
4073
+ * its internal chrome.
4074
+ */ function SearchBox({ placeholder, label }) {
4075
+ const { query, setQuery } = useDataTableContext();
4076
+ const classNames = dataTable();
4077
+ return /*#__PURE__*/ jsxRuntime.jsxs(TextFieldRoot, {
4078
+ variant: "outline",
4079
+ size: "medium",
4080
+ className: classNames.search,
4081
+ children: [
4082
+ /*#__PURE__*/ jsxRuntime.jsx(TextFieldPrefixIcon, {
4083
+ svg: /*#__PURE__*/ jsxRuntime.jsx(IconMagnifyingglassLine_default, {})
4084
+ }),
4085
+ /*#__PURE__*/ jsxRuntime.jsx(TextFieldInput, {
4086
+ type: "text",
4087
+ value: query,
4088
+ onChange: (event)=>setQuery(event.target.value),
4089
+ placeholder: placeholder,
4090
+ "aria-label": label
4091
+ })
4092
+ ]
4093
+ });
4094
+ }
4095
+ /** crisp's own count format (`datatable.tsx`'s `resolvedCountLabel`): unfiltered reads
4096
+ * "N rows" ("N row" for exactly one), filtered/searched reads "N of M" with no
4097
+ * trailing word at all -- reproduced literally rather than reinvented, since this is
4098
+ * the one piece of copy DATATABLE-ATTIO-SPEC.md calls out by example. */ function defaultCountLabel(shown, total) {
4099
+ if (shown === total) return `${total} ${total === 1 ? "row" : "rows"}`;
4100
+ return `${shown} of ${total}`;
4101
+ }
4102
+ function RowCount({ shown, total, countLabel }) {
4103
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableCount, {
4104
+ children: (countLabel ?? defaultCountLabel)(shown, total)
4105
+ });
4106
+ }
4107
+
4108
+ function DataTableInner(props, ref) {
4109
+ const { columns, data, surface, className, locale = "en-US", sort, defaultSort, onSortChange, sortIcon, selectable, selectedIds, defaultSelectedIds, onSelectionChange, selectAllLabel = "Select all rows", getRowSelectionLabel = ()=>"Select row", selectionCheckedIcon, selectionIndeterminateIcon, query, defaultQuery, onQueryChange, searchPlaceholder = "Search…", searchLabel = "Search table", filters, defaultFilters, onFiltersChange, showCount = true, countLabel, columnMenu = false, reorderable = true, resizable = true, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, onColumnsChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, stickyFirst = false, stickyLast = false, onEdit, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", loading = false, loadingLabel = "Loading...", error, empty = "No results.", onLoadMore, hasMore, loadingMore, scrollProps, ...restProps } = props;
4110
+ const classNames = dataTable({
4111
+ surface,
4112
+ interactive
4113
+ });
4114
+ // The batteries-included DataTable owns the sort state (controllable) so it
4115
+ // can actually reorder `data` -- the headless primitive only tracks which
4116
+ // column/direction is active. A consumer that pre-sorts server-side can pass
4117
+ // a controlled `sort`; this still sorts the rows it's given, which for
4118
+ // already-sorted data is a no-op.
4119
+ const [internalSort, setInternalSort] = React__namespace.useState(defaultSort);
4120
+ const resolvedSort = sort !== undefined ? sort : internalSort;
4121
+ const handleSortChange = React__namespace.useCallback((next)=>{
4122
+ if (sort === undefined) setInternalSort(next);
4123
+ onSortChange?.(next);
4124
+ }, [
4125
+ sort,
4126
+ onSortChange
4127
+ ]);
4128
+ // Same shape as sort just above -- the headless primitive only tracks the ACTIVE
4129
+ // query/filter state (it never sees `TRow`), this component owns the controllable
4130
+ // state that actually narrows `data` (`getVisibleRows`, ./filtering.ts).
4131
+ const [internalQuery, setInternalQuery] = React__namespace.useState(defaultQuery ?? "");
4132
+ const resolvedQuery = query !== undefined ? query : internalQuery;
4133
+ const handleQueryChange = React__namespace.useCallback((next)=>{
4134
+ if (query === undefined) setInternalQuery(next);
4135
+ onQueryChange?.(next);
4136
+ }, [
4137
+ query,
4138
+ onQueryChange
4139
+ ]);
4140
+ const [internalFilters, setInternalFilters] = React__namespace.useState(defaultFilters ?? {});
4141
+ const resolvedFilters = filters !== undefined ? filters : internalFilters;
4142
+ const handleFiltersChange = React__namespace.useCallback((next)=>{
4143
+ if (filters === undefined) setInternalFilters(next);
4144
+ onFiltersChange?.(next);
4145
+ }, [
4146
+ filters,
4147
+ onFiltersChange
4148
+ ]);
4149
+ const visibleRows = React__namespace.useMemo(()=>getVisibleRows(data, columns, resolvedQuery, resolvedFilters), [
4150
+ data,
4151
+ columns,
4152
+ resolvedQuery,
4153
+ resolvedFilters
4154
+ ]);
4155
+ const sortedData = React__namespace.useMemo(()=>{
4156
+ if (!resolvedSort) return visibleRows;
4157
+ const column = columns.find((candidate)=>candidate.key === resolvedSort.key);
4158
+ const factor = resolvedSort.dir === "asc" ? 1 : -1;
4159
+ return [
4160
+ ...visibleRows
4161
+ ].sort((a, b)=>{
4162
+ const av = column ? getColumnValue(column, a) : a[resolvedSort.key];
4163
+ const bv = column ? getColumnValue(column, b) : b[resolvedSort.key];
4164
+ if (av == null && bv == null) return 0;
4165
+ if (av == null) return -factor;
4166
+ if (bv == null) return factor;
4167
+ if (typeof av === "number" && typeof bv === "number") return (av - bv) * factor;
4168
+ return String(av).localeCompare(String(bv)) * factor;
4169
+ });
4170
+ }, [
4171
+ visibleRows,
4172
+ resolvedSort,
4173
+ columns
4174
+ ]);
4175
+ const rowIds = React__namespace.useMemo(()=>sortedData.map((row)=>row.id), [
4176
+ sortedData
4177
+ ]);
4178
+ const columnKeys = React__namespace.useMemo(()=>columns.map((column)=>column.key), [
4179
+ columns
4180
+ ]);
4181
+ const searchable = React__namespace.useMemo(()=>columns.some((column)=>column.searchable), [
4182
+ columns
4183
+ ]);
4184
+ const showToolbar = searchable || showCount;
4185
+ const showFooter = columns.some((column)=>column.summary);
4186
+ // crisp's own `totalCols` (`datatable.tsx`) -- every full-width spanning row (the
4187
+ // loading/error/empty body, infinite scroll's sentinel, the add-row footer) reads
4188
+ // this ONE value rather than each re-deriving it, so they can never drift apart.
4189
+ const totalCols = columns.length + (selectable ? 1 : 0);
4190
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider, {
4191
+ value: classNames,
4192
+ children: /*#__PURE__*/ jsxRuntime.jsxs(DataTableRoot, {
4193
+ ref: ref,
4194
+ className: clsx(classNames.root, className),
4195
+ rowIds: rowIds,
4196
+ selectable: selectable,
4197
+ selectedIds: selectedIds,
4198
+ defaultSelectedIds: defaultSelectedIds,
4199
+ onSelectionChange: onSelectionChange,
4200
+ sort: resolvedSort,
4201
+ onSortChange: handleSortChange,
4202
+ query: resolvedQuery,
4203
+ onQueryChange: handleQueryChange,
4204
+ filters: resolvedFilters,
4205
+ onFiltersChange: handleFiltersChange,
4206
+ columnKeys: columnKeys,
4207
+ columnOrder: columnOrder,
4208
+ defaultColumnOrder: defaultColumnOrder,
4209
+ onColumnOrderChange: onColumnOrderChange,
4210
+ hiddenColumnKeys: hiddenColumnKeys,
4211
+ defaultHiddenColumnKeys: defaultHiddenColumnKeys,
4212
+ onHiddenColumnKeysChange: onHiddenColumnKeysChange,
4213
+ columnWidths: columnWidths,
4214
+ defaultColumnWidths: defaultColumnWidths,
4215
+ onColumnWidthsChange: onColumnWidthsChange,
4216
+ columnLabels: columnLabels,
4217
+ defaultColumnLabels: defaultColumnLabels,
4218
+ onColumnLabelsChange: onColumnLabelsChange,
4219
+ columnCalcOps: columnCalcOps,
4220
+ defaultColumnCalcOps: defaultColumnCalcOps,
4221
+ onColumnCalcOpsChange: onColumnCalcOpsChange,
4222
+ onEdit: onEdit,
4223
+ onLoadMore: onLoadMore,
4224
+ hasMore: hasMore,
4225
+ loadingMore: loadingMore,
4226
+ ...restProps,
4227
+ children: [
4228
+ onColumnsChange && /*#__PURE__*/ jsxRuntime.jsx(ColumnsChangeEffect, {
4229
+ columns: columns,
4230
+ onColumnsChange: onColumnsChange
4231
+ }),
4232
+ showToolbar && /*#__PURE__*/ jsxRuntime.jsxs(DataTableToolbar, {
4233
+ children: [
4234
+ searchable && /*#__PURE__*/ jsxRuntime.jsx(SearchBox, {
4235
+ placeholder: searchPlaceholder,
4236
+ label: searchLabel
4237
+ }),
4238
+ showCount && /*#__PURE__*/ jsxRuntime.jsx(RowCount, {
4239
+ shown: sortedData.length,
4240
+ total: data.length,
4241
+ countLabel: countLabel
4242
+ })
4243
+ ]
4244
+ }),
4245
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableScrollBody, {
4246
+ scrollProps: scrollProps,
4247
+ children: /*#__PURE__*/ jsxRuntime.jsxs(DataTableTable, {
4248
+ children: [
4249
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableColGroup, {
4250
+ columns: columns,
4251
+ selectable: !!selectable
4252
+ }),
4253
+ /*#__PURE__*/ jsxRuntime.jsxs(DataTableHeader, {
4254
+ children: [
4255
+ selectable && /*#__PURE__*/ jsxRuntime.jsx(DataTableSelectionHeaderCell, {
4256
+ ...stickyProps({
4257
+ selectable,
4258
+ stickyFirst,
4259
+ stickyLast,
4260
+ isSelectionCell: true,
4261
+ isFirst: false,
4262
+ isLast: false
4263
+ }),
4264
+ children: /*#__PURE__*/ jsxRuntime.jsx(SelectAllCheckbox, {
4265
+ label: selectAllLabel,
4266
+ checkedIcon: selectionCheckedIcon ?? DEFAULT_CHECKED_ICON,
4267
+ indeterminateIcon: selectionIndeterminateIcon ?? DEFAULT_INDETERMINATE_ICON
4268
+ })
4269
+ }),
4270
+ /*#__PURE__*/ jsxRuntime.jsx(ColumnHeaderCells, {
4271
+ columns: columns,
4272
+ columnMenu: columnMenu,
4273
+ reorderable: reorderable,
4274
+ resizable: resizable,
4275
+ sortIcon: sortIcon,
4276
+ selectable: !!selectable,
4277
+ stickyFirst: stickyFirst,
4278
+ stickyLast: stickyLast
4279
+ })
4280
+ ]
4281
+ }),
4282
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableBody, {
4283
+ children: loading ? /*#__PURE__*/ jsxRuntime.jsx("tr", {
4284
+ children: /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
4285
+ colSpan: totalCols,
4286
+ children: /*#__PURE__*/ jsxRuntime.jsx(LoadingStateContent, {
4287
+ label: loadingLabel,
4288
+ classNames: classNames
4289
+ })
4290
+ })
4291
+ }) : error ? /*#__PURE__*/ jsxRuntime.jsx("tr", {
4292
+ children: /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
4293
+ colSpan: totalCols,
4294
+ children: /*#__PURE__*/ jsxRuntime.jsx(EmptyStateContent, {
4295
+ classNames: classNames,
4296
+ children: error
4297
+ })
4298
+ })
4299
+ }) : sortedData.length === 0 ? /*#__PURE__*/ jsxRuntime.jsx("tr", {
4300
+ children: /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
4301
+ colSpan: totalCols,
4302
+ children: /*#__PURE__*/ jsxRuntime.jsx(EmptyStateContent, {
4303
+ classNames: classNames,
4304
+ children: empty
4305
+ })
4306
+ })
4307
+ }) : /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
4308
+ children: [
4309
+ sortedData.map((row)=>/*#__PURE__*/ jsxRuntime.jsxs(DataTableRow, {
4310
+ rowId: row.id,
4311
+ "data-clickable": onRowClick ? "" : undefined,
4312
+ onClick: onRowClick ? ()=>onRowClick(row) : undefined,
4313
+ children: [
4314
+ selectable && /*#__PURE__*/ jsxRuntime.jsx(DataTableSelectionCell, {
4315
+ ...stickyProps({
4316
+ selectable,
4317
+ stickyFirst,
4318
+ stickyLast,
4319
+ isSelectionCell: true,
4320
+ isFirst: false,
4321
+ isLast: false
4322
+ }),
4323
+ children: /*#__PURE__*/ jsxRuntime.jsx(RowCheckbox, {
4324
+ label: getRowSelectionLabel(row),
4325
+ checkedIcon: selectionCheckedIcon ?? DEFAULT_CHECKED_ICON
4326
+ })
4327
+ }),
4328
+ /*#__PURE__*/ jsxRuntime.jsx(ColumnBodyCells, {
4329
+ columns: columns,
4330
+ row: row,
4331
+ classNames: classNames,
4332
+ locale: locale,
4333
+ selectable: !!selectable,
4334
+ stickyFirst: stickyFirst,
4335
+ stickyLast: stickyLast
4336
+ })
4337
+ ]
4338
+ }, row.id)),
4339
+ onLoadMore && /*#__PURE__*/ jsxRuntime.jsx(InfiniteScrollRow, {
4340
+ colSpan: totalCols,
4341
+ classNames: classNames,
4342
+ loadingLabel: loadingLabel
4343
+ })
4344
+ ]
4345
+ })
4346
+ }),
4347
+ showFooter && /*#__PURE__*/ jsxRuntime.jsxs(DataTableFooter, {
4348
+ children: [
4349
+ selectable && // The 40px-wide selection SPACER cell is now a plain `DataTableFooterCell`
4350
+ // (not the `selectionCell` slot) -- the `<colgroup>`'s own 40px `<col>`
4351
+ // is what makes every row's selection column line up now, not a matching
4352
+ // per-slot width formula, so there is no more reason for this cell to
4353
+ // borrow a different slot than its footer siblings.
4354
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableFooterCell, {
4355
+ ...stickyProps({
4356
+ selectable,
4357
+ stickyFirst,
4358
+ stickyLast,
4359
+ isSelectionCell: true,
4360
+ isFirst: false,
4361
+ isLast: false
4362
+ })
4363
+ }),
4364
+ /*#__PURE__*/ jsxRuntime.jsx(ColumnCalcCells, {
4365
+ columns: columns,
4366
+ rows: sortedData,
4367
+ selectable: !!selectable,
4368
+ stickyFirst: stickyFirst,
4369
+ stickyLast: stickyLast
4370
+ })
4371
+ ]
4372
+ }),
4373
+ onAddRow && /*#__PURE__*/ jsxRuntime.jsx(AddRowFooter, {
4374
+ onAddRow: onAddRow,
4375
+ addRowLabel: addRowLabel,
4376
+ colSpan: totalCols,
4377
+ classNames: classNames
4378
+ })
4379
+ ]
4380
+ })
4381
+ })
4382
+ ]
4383
+ })
4384
+ });
4385
+ }
4386
+ DataTableInner.displayName = "DataTable";
4387
+ /**
4388
+ * @see https://seed-design.io/react/components/data-table
4389
+ */ const DataTable = /*#__PURE__*/ React.forwardRef(DataTableInner);
4390
+ ////////////////////////////////////////////////////////////////////////////////////
4391
+ /** Fires `onColumnsChange` with the effective (ordered, visible, relabelled) column
4392
+ * list whenever it actually changes -- a thin convenience over the four controllable
4393
+ * pairs (order/hidden/width/label), for a consumer that would rather persist "the
4394
+ * current column configuration" as one blob. A standalone component (not inlined in
4395
+ * `DataTableInner`) purely so it can call `useDataTableContext()` -- that context is
4396
+ * provided by the `DataTablePrimitiveRoot` this renders as a SIBLING of, one level
4397
+ * up from where `DataTableInner` itself sits. */ function ColumnsChangeEffect({ columns, onColumnsChange }) {
4398
+ const effective = useEffectiveColumns(columns);
4399
+ // A NEW array every time `effective` itself is a new array (its own `useMemo`
4400
+ // recomputes only when order/hidden/label/width actually change) -- `header`
4401
+ // resolves to the menu's rename override (falling back to the column's own static
4402
+ // `header`) and `width` resolves to the live resize/consumer-set width (falling
4403
+ // back to the column's own initial one), so this is genuinely "what's showing now",
4404
+ // not the original static `columns` prop echoed back unchanged.
4405
+ const resolvedColumns = React__namespace.useMemo(()=>effective.map(({ column, label, width })=>({
4406
+ ...column,
4407
+ header: label,
4408
+ width
4409
+ })), [
4410
+ effective
4411
+ ]);
4412
+ React__namespace.useEffect(()=>{
4413
+ onColumnsChange(resolvedColumns);
4414
+ }, [
4415
+ resolvedColumns,
4416
+ onColumnsChange
4417
+ ]);
4418
+ return null;
4419
+ }
4420
+ ////////////////////////////////////////////////////////////////////////////////////
4421
+ // The real `<table>` -- see react-headless's own `DataTableTable` comment for why this
4422
+ // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
4423
+ // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
4424
+ // child without the HTML parser foster-parenting it back out.
4425
+ const DataTableTable = withContext(DataTableTable$1, "table");
4426
+ /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
4427
+ * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
4428
+ * reads its own width off this shared `<colgroup>` by construction (native table
4429
+ * column-sizing), which is what makes them align -- no per-cell width style to drift
4430
+ * out of sync between the three. A column with no explicit width gets a bare `<col>`
4431
+ * (no `style`): `table-layout: fixed`'s own algorithm gives every such column an equal
4432
+ * share of whatever space is left, matching the old flex grid's `flex: 1 1 0%`
4433
+ * default with no extra code needed here. */ function DataTableColGroup({ columns, selectable }) {
4434
+ const effective = useEffectiveColumns(columns);
4435
+ return /*#__PURE__*/ jsxRuntime.jsxs("colgroup", {
4436
+ children: [
4437
+ selectable && /*#__PURE__*/ jsxRuntime.jsx("col", {
4438
+ style: {
4439
+ width: 40
4440
+ }
4441
+ }),
4442
+ effective.map(({ column, width })=>/*#__PURE__*/ jsxRuntime.jsx("col", {
4443
+ style: width === undefined ? undefined : {
4444
+ width
4445
+ }
4446
+ }, column.key))
4447
+ ]
4448
+ });
4449
+ }
4450
+ const DataTableHeader = withContext(DataTableHeader$1, "header");
4451
+ const DataTableHeaderCell = withContext(DataTableHeaderCell$1, "headerCell");
4452
+ // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
4453
+ // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
4454
+ // for why this lives one level below the cell itself rather than on it.
4455
+ const DataTableHeaderCellInner = withContext(Primitive.div, "headerCellInner");
4456
+ /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
4457
+ * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
4458
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext(Primitive.span, "headerCellLabel");
4459
+ /**
4460
+ * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
4461
+ * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
4462
+ * so the checkbox column still inherits every shared table-wide rule (sticky header,
4463
+ * sticky footer, height, borders) for free instead of a second, independent copy of
4464
+ * them under a slot of its own. `useClassNames()` reads the SAME classNames object
4465
+ * `ClassNamesProvider` (below, in `DataTableInner`) already put in context; this isn't
4466
+ * a second recipe call.
4467
+ */ function withSelectionCell(Component, baseSlot) {
4468
+ const StyledComponent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
4469
+ const classNames = useClassNames();
4470
+ return /*#__PURE__*/ jsxRuntime.jsx(Component, {
4471
+ ref: ref,
4472
+ ...props,
4473
+ className: clsx(classNames[baseSlot], classNames.selectionCell, props["className"])
4474
+ });
4475
+ });
4476
+ StyledComponent.displayName = `SelectionCell(${baseSlot})`;
4477
+ return StyledComponent;
4478
+ }
4479
+ const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
4480
+ const DataTableHeaderCellIcon = withContext(Primitive.span, "headerCellIcon");
4481
+ // body has no CSS slot of its own in the rootage spec -- it is a plain structural
4482
+ // `<tbody>`, so it is not wrapped in `withContext`.
4483
+ const DataTableBody = DataTableBody$1;
4484
+ const DataTableRow = withContext(DataTableRow$1, "row");
4485
+ const DataTableCell = withContext(DataTableCell$1, "cell");
4486
+ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
4487
+ // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
4488
+ // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
4489
+ // column width than the card is wide) scroll horizontally at all, which sticky columns
4490
+ // and their scroll shadow (this slot's own `data-scrolled-start`/`-end`, set from
4491
+ // `useDataTableScroll`) need something concrete to measure and listen to. `overflowY`
4492
+ // stays out of this slot's own base css (nothing here has a bounded height of its own
4493
+ // to clip), so it never introduces a second vertical scrollbar next to whatever
4494
+ // ambient container the whole DataTable already sits in.
4495
+ const DataTableScrollBodyBase = withContext(Primitive.div, "scrollArea");
4496
+ function DataTableScrollBody({ children, scrollProps }) {
4497
+ const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
4498
+ const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
4499
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableScrollBodyBase, {
4500
+ ...otherScrollProps,
4501
+ ref: scrollAreaRef,
4502
+ onScroll: onScroll,
4503
+ className: scrollClassName,
4504
+ style: scrollStyle,
4505
+ "data-scrolled-start": domUtils.dataAttr(scrolledStart),
4506
+ "data-scrolled-end": domUtils.dataAttr(scrolledEnd),
4507
+ children: children
4508
+ });
4509
+ }
4510
+ const DataTableFooterBase = withContext(Primitive.tfoot, "footer");
4511
+ function DataTableFooter({ children, ...props }) {
4512
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableFooterBase, {
4513
+ ...props,
4514
+ children: /*#__PURE__*/ jsxRuntime.jsx("tr", {
4515
+ children: children
4516
+ })
4517
+ });
4518
+ }
4519
+ const DataTableFooterCell = withContext(Primitive.td, "footerCell");
4520
+ ////////////////////////////////////////////////////////////////////////////////////
4521
+ // Fix (bug report): karrot icons default to a 24px intrinsic SVG attribute size
4522
+ // -- see column-menu.tsx's own `SORT_ICON_SIZE` comment for the full mechanism.
4523
+ // The selection checkbox's own indicator is the smallest glyph in the grid
4524
+ // (crisp's own measured checkmark), so it gets the smallest explicit size.
4525
+ const SELECTION_ICON_SIZE = 12;
4526
+ const DEFAULT_CHECKED_ICON = /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkLine_default, {
4527
+ size: SELECTION_ICON_SIZE
4528
+ });
4529
+ const DEFAULT_INDETERMINATE_ICON = /*#__PURE__*/ jsxRuntime.jsx(IconMinusLine_default, {
4530
+ size: SELECTION_ICON_SIZE
4531
+ });
4532
+ function SelectAllCheckbox({ label, checkedIcon, indeterminateIcon }) {
4533
+ const { selectionState, toggleAll } = useDataTableContext();
4534
+ return /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
4535
+ checked: selectionState === "all",
4536
+ indeterminate: selectionState === "some",
4537
+ onCheckedChange: toggleAll,
4538
+ children: [
4539
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
4540
+ children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
4541
+ checked: checkedIcon ?? null,
4542
+ indeterminate: indeterminateIcon
4543
+ })
4544
+ }),
4545
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
4546
+ "aria-label": label
4547
+ })
4548
+ ]
4549
+ });
4550
+ }
4551
+ /**
4552
+ * Reads its OWN row's selected state off `useDataTableRowContext` (set by the
4553
+ * `DataTableRow` it renders inside) rather than being handed `rowId` again as a prop
4554
+ * and looking it up in the table-wide context -- the same shape Select's
4555
+ * `SelectItemLabel` reads `label` off `useSelectItemContext` instead of a prop
4556
+ * re-threaded down from `SelectItem`.
4557
+ */ function RowCheckbox({ label, checkedIcon }) {
4558
+ const { isSelected, toggleRow } = useDataTableRowContext();
4559
+ return /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
4560
+ checked: isSelected,
4561
+ onCheckedChange: toggleRow,
4562
+ children: [
4563
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
4564
+ children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
4565
+ checked: checkedIcon ?? null
4566
+ })
4567
+ }),
4568
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
4569
+ "aria-label": label
4570
+ })
4571
+ ]
4572
+ });
4573
+ }
13
4574
 
14
- exports.DataTable = DataTable12s.DataTable;
4575
+ exports.DataTable = DataTable;