@comitor/ui 1.10.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +24 -5
  2. package/dist/data-grid/compose-refs.d.ts +19 -0
  3. package/dist/data-grid/compose-refs.js +41 -0
  4. package/dist/data-grid/compose-refs.js.map +1 -0
  5. package/dist/data-grid/data-grid-cell-variants.d.ts +15 -0
  6. package/dist/data-grid/data-grid-cell-variants.js +1738 -0
  7. package/dist/data-grid/data-grid-cell-variants.js.map +1 -0
  8. package/dist/data-grid/data-grid-cell-wrapper.d.ts +9 -0
  9. package/dist/data-grid/data-grid-cell-wrapper.js +179 -0
  10. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -0
  11. package/dist/data-grid/data-grid-cell.d.ts +8 -0
  12. package/dist/data-grid/data-grid-cell.js +101 -0
  13. package/dist/data-grid/data-grid-cell.js.map +1 -0
  14. package/dist/data-grid/data-grid-column-header.d.ts +12 -0
  15. package/dist/data-grid/data-grid-column-header.js +207 -0
  16. package/dist/data-grid/data-grid-column-header.js.map +1 -0
  17. package/dist/data-grid/data-grid-context-menu.d.ts +12 -0
  18. package/dist/data-grid/data-grid-context-menu.js +161 -0
  19. package/dist/data-grid/data-grid-context-menu.js.map +1 -0
  20. package/dist/data-grid/data-grid-filter-menu.d.ts +23 -0
  21. package/dist/data-grid/data-grid-filter-menu.js +725 -0
  22. package/dist/data-grid/data-grid-filter-menu.js.map +1 -0
  23. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +31 -0
  24. package/dist/data-grid/data-grid-keyboard-shortcuts.js +229 -0
  25. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -0
  26. package/dist/data-grid/data-grid-locale.d.ts +24 -0
  27. package/dist/data-grid/data-grid-locale.js +25 -0
  28. package/dist/data-grid/data-grid-locale.js.map +1 -0
  29. package/dist/data-grid/data-grid-paste-dialog.d.ts +11 -0
  30. package/dist/data-grid/data-grid-paste-dialog.js +104 -0
  31. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -0
  32. package/dist/data-grid/data-grid-presence.d.ts +14 -0
  33. package/dist/data-grid/data-grid-presence.js +16 -0
  34. package/dist/data-grid/data-grid-presence.js.map +1 -0
  35. package/dist/data-grid/data-grid-row-height-menu.d.ts +25 -0
  36. package/dist/data-grid/data-grid-row-height-menu.js +53 -0
  37. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -0
  38. package/dist/data-grid/data-grid-row.d.ts +28 -0
  39. package/dist/data-grid/data-grid-row.js +206 -0
  40. package/dist/data-grid/data-grid-row.js.map +1 -0
  41. package/dist/data-grid/data-grid-search.d.ts +10 -0
  42. package/dist/data-grid/data-grid-search.js +184 -0
  43. package/dist/data-grid/data-grid-search.js.map +1 -0
  44. package/dist/data-grid/data-grid-select-column.d.ts +12 -0
  45. package/dist/data-grid/data-grid-select-column.js +179 -0
  46. package/dist/data-grid/data-grid-select-column.js.map +1 -0
  47. package/dist/data-grid/data-grid-skeleton.d.ts +13 -0
  48. package/dist/data-grid/data-grid-skeleton.js +52 -0
  49. package/dist/data-grid/data-grid-skeleton.js.map +1 -0
  50. package/dist/data-grid/data-grid-sort-menu.d.ts +23 -0
  51. package/dist/data-grid/data-grid-sort-menu.js +283 -0
  52. package/dist/data-grid/data-grid-sort-menu.js.map +1 -0
  53. package/dist/data-grid/data-grid-view-menu.d.ts +24 -0
  54. package/dist/data-grid/data-grid-view-menu.js +74 -0
  55. package/dist/data-grid/data-grid-view-menu.js.map +1 -0
  56. package/dist/data-grid/data-grid.d.ts +44 -0
  57. package/dist/data-grid/data-grid.js +238 -0
  58. package/dist/data-grid/data-grid.js.map +1 -0
  59. package/dist/data-grid/filters.d.ts +27 -0
  60. package/dist/data-grid/filters.js +233 -0
  61. package/dist/data-grid/filters.js.map +1 -0
  62. package/dist/data-grid/index.d.ts +25 -0
  63. package/dist/data-grid/index.js +95 -0
  64. package/dist/data-grid/index.js.map +1 -0
  65. package/dist/data-grid/messages-en.d.ts +294 -0
  66. package/dist/data-grid/messages-en.js +279 -0
  67. package/dist/data-grid/messages-en.js.map +1 -0
  68. package/dist/data-grid/messages-vi.d.ts +12 -0
  69. package/dist/data-grid/messages-vi.js +279 -0
  70. package/dist/data-grid/messages-vi.js.map +1 -0
  71. package/dist/data-grid/messages.d.ts +567 -0
  72. package/dist/data-grid/messages.js +20 -0
  73. package/dist/data-grid/messages.js.map +1 -0
  74. package/dist/data-grid/sortable.d.ts +50 -0
  75. package/dist/data-grid/sortable.js +361 -0
  76. package/dist/data-grid/sortable.js.map +1 -0
  77. package/dist/data-grid/toast.d.ts +10 -0
  78. package/dist/data-grid/toast.js +21 -0
  79. package/dist/data-grid/toast.js.map +1 -0
  80. package/dist/data-grid/types.d.ts +189 -0
  81. package/dist/data-grid/types.js +1 -0
  82. package/dist/data-grid/types.js.map +1 -0
  83. package/dist/data-grid/use-as-ref.d.ts +5 -0
  84. package/dist/data-grid/use-as-ref.js +13 -0
  85. package/dist/data-grid/use-as-ref.js.map +1 -0
  86. package/dist/data-grid/use-badge-overflow.d.ts +24 -0
  87. package/dist/data-grid/use-badge-overflow.js +123 -0
  88. package/dist/data-grid/use-badge-overflow.js.map +1 -0
  89. package/dist/data-grid/use-callback-ref.d.ts +10 -0
  90. package/dist/data-grid/use-callback-ref.js +12 -0
  91. package/dist/data-grid/use-callback-ref.js.map +1 -0
  92. package/dist/data-grid/use-data-grid-undo-redo.d.ts +45 -0
  93. package/dist/data-grid/use-data-grid-undo-redo.js +348 -0
  94. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -0
  95. package/dist/data-grid/use-data-grid.d.ts +372 -0
  96. package/dist/data-grid/use-data-grid.js +2697 -0
  97. package/dist/data-grid/use-data-grid.js.map +1 -0
  98. package/dist/data-grid/use-debounced-callback.d.ts +6 -0
  99. package/dist/data-grid/use-debounced-callback.js +19 -0
  100. package/dist/data-grid/use-debounced-callback.js.map +1 -0
  101. package/dist/data-grid/use-isomorphic-layout-effect.d.ts +5 -0
  102. package/dist/data-grid/use-isomorphic-layout-effect.js +6 -0
  103. package/dist/data-grid/use-isomorphic-layout-effect.js.map +1 -0
  104. package/dist/data-grid/use-lazy-ref.d.ts +5 -0
  105. package/dist/data-grid/use-lazy-ref.js +12 -0
  106. package/dist/data-grid/use-lazy-ref.js.map +1 -0
  107. package/dist/data-grid/utils.d.ts +81 -0
  108. package/dist/data-grid/utils.js +409 -0
  109. package/dist/data-grid/utils.js.map +1 -0
  110. package/package.json +33 -15
@@ -0,0 +1,1738 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { vi } from "date-fns/locale";
4
+ import { Check, Upload, X } from "lucide-react";
5
+ import * as React from "react";
6
+ import { Badge, badgeVariants } from "../components/ui/badge.js";
7
+ import { Button } from "../components/ui/button.js";
8
+ import { Calendar } from "../components/ui/calendar.js";
9
+ import { Checkbox } from "../components/ui/checkbox.js";
10
+ import {
11
+ Command,
12
+ CommandEmpty,
13
+ CommandGroup,
14
+ CommandInput,
15
+ CommandItem,
16
+ CommandList,
17
+ CommandSeparator
18
+ } from "../components/ui/command.js";
19
+ import { Popover, PopoverAnchor, PopoverContent } from "../components/ui/popover.js";
20
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select.js";
21
+ import { Skeleton } from "../components/ui/skeleton.js";
22
+ import { Textarea } from "../components/ui/textarea.js";
23
+ import { cn } from "../lib/cn.js";
24
+ import { DataGridCellWrapper } from "./data-grid-cell-wrapper.js";
25
+ import { useDataGridMessages } from "./data-grid-locale.js";
26
+ import { toastError, toastMessage } from "./toast.js";
27
+ import { useBadgeOverflow } from "./use-badge-overflow.js";
28
+ import { useDebouncedCallback } from "./use-debounced-callback.js";
29
+ import {
30
+ formatDateForDisplay,
31
+ formatDateToString,
32
+ formatFileSize,
33
+ generateFileId,
34
+ getCellKey,
35
+ getFileIcon,
36
+ getLineCount,
37
+ getUrlHref,
38
+ parseLocalDate
39
+ } from "./utils.js";
40
+ function ShortTextCell({
41
+ cell,
42
+ tableMeta,
43
+ rowIndex,
44
+ columnId,
45
+ rowHeight,
46
+ isEditing,
47
+ isFocused,
48
+ isSelected,
49
+ isSearchMatch,
50
+ isActiveSearchMatch,
51
+ readOnly
52
+ }) {
53
+ const initialValue = cell.getValue();
54
+ const [value, setValue] = React.useState(initialValue);
55
+ const cellRef = React.useRef(null);
56
+ const containerRef = React.useRef(null);
57
+ const prevIsEditingRef = React.useRef(false);
58
+ const prevInitialValueRef = React.useRef(initialValue);
59
+ if (initialValue !== prevInitialValueRef.current) {
60
+ prevInitialValueRef.current = initialValue;
61
+ setValue(initialValue);
62
+ if (cellRef.current && !isEditing) {
63
+ cellRef.current.textContent = initialValue;
64
+ }
65
+ }
66
+ const onBlur = React.useCallback(() => {
67
+ const currentValue = cellRef.current?.textContent ?? "";
68
+ if (!readOnly && currentValue !== initialValue) {
69
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: currentValue });
70
+ }
71
+ tableMeta?.onCellEditingStop?.();
72
+ }, [tableMeta, rowIndex, columnId, initialValue, readOnly]);
73
+ const onInput = React.useCallback((event) => {
74
+ const currentValue = event.currentTarget.textContent ?? "";
75
+ setValue(currentValue);
76
+ }, []);
77
+ const onWrapperKeyDown = React.useCallback(
78
+ (event) => {
79
+ if (isEditing) {
80
+ if (event.key === "Enter") {
81
+ event.preventDefault();
82
+ const currentValue = cellRef.current?.textContent ?? "";
83
+ if (currentValue !== initialValue) {
84
+ tableMeta?.onDataUpdate?.({
85
+ rowIndex,
86
+ columnId,
87
+ value: currentValue
88
+ });
89
+ }
90
+ tableMeta?.onCellEditingStop?.({ moveToNextRow: true });
91
+ } else if (event.key === "Tab") {
92
+ event.preventDefault();
93
+ const currentValue = cellRef.current?.textContent ?? "";
94
+ if (currentValue !== initialValue) {
95
+ tableMeta?.onDataUpdate?.({
96
+ rowIndex,
97
+ columnId,
98
+ value: currentValue
99
+ });
100
+ }
101
+ tableMeta?.onCellEditingStop?.({
102
+ direction: event.shiftKey ? "left" : "right"
103
+ });
104
+ } else if (event.key === "Escape") {
105
+ event.preventDefault();
106
+ setValue(initialValue);
107
+ cellRef.current?.blur();
108
+ }
109
+ } else if (isFocused && event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
110
+ setValue(event.key);
111
+ queueMicrotask(() => {
112
+ if (cellRef.current && cellRef.current.contentEditable === "true") {
113
+ cellRef.current.textContent = event.key;
114
+ const range = document.createRange();
115
+ const selection = window.getSelection();
116
+ range.selectNodeContents(cellRef.current);
117
+ range.collapse(false);
118
+ selection?.removeAllRanges();
119
+ selection?.addRange(range);
120
+ }
121
+ });
122
+ }
123
+ },
124
+ [isEditing, isFocused, initialValue, tableMeta, rowIndex, columnId]
125
+ );
126
+ React.useEffect(() => {
127
+ const wasEditing = prevIsEditingRef.current;
128
+ prevIsEditingRef.current = isEditing;
129
+ if (isEditing && !wasEditing && cellRef.current) {
130
+ cellRef.current.focus();
131
+ if (!cellRef.current.textContent && value) {
132
+ cellRef.current.textContent = value;
133
+ }
134
+ if (cellRef.current.textContent) {
135
+ const range = document.createRange();
136
+ const selection = window.getSelection();
137
+ range.selectNodeContents(cellRef.current);
138
+ range.collapse(false);
139
+ selection?.removeAllRanges();
140
+ selection?.addRange(range);
141
+ }
142
+ }
143
+ }, [isEditing, value]);
144
+ const displayValue = !isEditing ? value ?? "" : "";
145
+ return /* @__PURE__ */ jsx(
146
+ DataGridCellWrapper,
147
+ {
148
+ ref: containerRef,
149
+ cell,
150
+ tableMeta,
151
+ rowIndex,
152
+ columnId,
153
+ rowHeight,
154
+ isEditing,
155
+ isFocused,
156
+ isSelected,
157
+ isSearchMatch,
158
+ isActiveSearchMatch,
159
+ readOnly,
160
+ onKeyDown: onWrapperKeyDown,
161
+ children: /* @__PURE__ */ jsx(
162
+ "div",
163
+ {
164
+ role: "textbox",
165
+ "data-slot": "grid-cell-content",
166
+ contentEditable: isEditing,
167
+ tabIndex: -1,
168
+ ref: cellRef,
169
+ onBlur,
170
+ onInput,
171
+ suppressContentEditableWarning: true,
172
+ className: cn("size-full overflow-hidden outline-none", {
173
+ "whitespace-nowrap [&_*]:inline [&_*]:whitespace-nowrap [&_br]:hidden": isEditing
174
+ }),
175
+ children: displayValue
176
+ }
177
+ )
178
+ }
179
+ );
180
+ }
181
+ function LongTextCell({
182
+ cell,
183
+ tableMeta,
184
+ rowIndex,
185
+ columnId,
186
+ rowHeight,
187
+ isFocused,
188
+ isEditing,
189
+ isSelected,
190
+ isSearchMatch,
191
+ isActiveSearchMatch,
192
+ readOnly
193
+ }) {
194
+ const msg = useDataGridMessages();
195
+ const initialValue = cell.getValue();
196
+ const [value, setValue] = React.useState(initialValue ?? "");
197
+ const textareaRef = React.useRef(null);
198
+ const containerRef = React.useRef(null);
199
+ const pendingCharRef = React.useRef(null);
200
+ const sideOffset = -(containerRef.current?.clientHeight ?? 0);
201
+ const prevInitialValueRef = React.useRef(initialValue);
202
+ if (initialValue !== prevInitialValueRef.current) {
203
+ prevInitialValueRef.current = initialValue;
204
+ setValue(initialValue ?? "");
205
+ }
206
+ const debouncedSave = useDebouncedCallback((newValue) => {
207
+ if (!readOnly) {
208
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: newValue });
209
+ }
210
+ }, 300);
211
+ const onSave = React.useCallback(() => {
212
+ if (!readOnly && value !== initialValue) {
213
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value });
214
+ }
215
+ tableMeta?.onCellEditingStop?.();
216
+ }, [tableMeta, value, initialValue, rowIndex, columnId, readOnly]);
217
+ const onCancel = React.useCallback(() => {
218
+ setValue(initialValue ?? "");
219
+ if (!readOnly) {
220
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: initialValue });
221
+ }
222
+ tableMeta?.onCellEditingStop?.();
223
+ }, [tableMeta, initialValue, rowIndex, columnId, readOnly]);
224
+ const onOpenChange = React.useCallback(
225
+ (open) => {
226
+ if (open && !readOnly) {
227
+ tableMeta?.onCellEditingStart?.(rowIndex, columnId);
228
+ } else {
229
+ if (!readOnly && value !== initialValue) {
230
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value });
231
+ }
232
+ tableMeta?.onCellEditingStop?.();
233
+ }
234
+ },
235
+ [tableMeta, value, initialValue, rowIndex, columnId, readOnly]
236
+ );
237
+ const onOpenAutoFocus = React.useCallback((event) => {
238
+ event.preventDefault();
239
+ if (textareaRef.current) {
240
+ textareaRef.current.focus();
241
+ const length = textareaRef.current.value.length;
242
+ textareaRef.current.setSelectionRange(length, length);
243
+ if (pendingCharRef.current) {
244
+ const char = pendingCharRef.current;
245
+ pendingCharRef.current = null;
246
+ requestAnimationFrame(() => {
247
+ if (textareaRef.current && document.activeElement === textareaRef.current) {
248
+ document.execCommand("insertText", false, char);
249
+ textareaRef.current.scrollTop = textareaRef.current.scrollHeight;
250
+ }
251
+ });
252
+ } else {
253
+ textareaRef.current.scrollTop = textareaRef.current.scrollHeight;
254
+ }
255
+ }
256
+ }, []);
257
+ const onWrapperKeyDown = React.useCallback(
258
+ (event) => {
259
+ if (isFocused && !isEditing && !readOnly && event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
260
+ pendingCharRef.current = event.key;
261
+ }
262
+ },
263
+ [isFocused, isEditing, readOnly]
264
+ );
265
+ const onBlur = React.useCallback(() => {
266
+ if (!readOnly && value !== initialValue) {
267
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value });
268
+ }
269
+ tableMeta?.onCellEditingStop?.();
270
+ }, [tableMeta, value, initialValue, rowIndex, columnId, readOnly]);
271
+ const onChange = React.useCallback(
272
+ (event) => {
273
+ const newValue = event.target.value;
274
+ setValue(newValue);
275
+ debouncedSave(newValue);
276
+ },
277
+ [debouncedSave]
278
+ );
279
+ const onKeyDown = React.useCallback(
280
+ (event) => {
281
+ if (event.key === "Escape") {
282
+ event.preventDefault();
283
+ onCancel();
284
+ } else if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
285
+ event.preventDefault();
286
+ onSave();
287
+ } else if (event.key === "Tab") {
288
+ event.preventDefault();
289
+ if (value !== initialValue) {
290
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value });
291
+ }
292
+ tableMeta?.onCellEditingStop?.({
293
+ direction: event.shiftKey ? "left" : "right"
294
+ });
295
+ return;
296
+ }
297
+ event.stopPropagation();
298
+ },
299
+ [onSave, onCancel, value, initialValue, tableMeta, rowIndex, columnId]
300
+ );
301
+ return /* @__PURE__ */ jsxs(Popover, { open: isEditing, onOpenChange, children: [
302
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx(
303
+ DataGridCellWrapper,
304
+ {
305
+ ref: containerRef,
306
+ cell,
307
+ tableMeta,
308
+ rowIndex,
309
+ columnId,
310
+ rowHeight,
311
+ isEditing,
312
+ isFocused,
313
+ isSelected,
314
+ isSearchMatch,
315
+ isActiveSearchMatch,
316
+ readOnly,
317
+ onKeyDown: onWrapperKeyDown,
318
+ children: /* @__PURE__ */ jsx("span", { "data-slot": "grid-cell-content", children: value })
319
+ }
320
+ ) }),
321
+ /* @__PURE__ */ jsx(
322
+ PopoverContent,
323
+ {
324
+ "data-grid-cell-editor": "",
325
+ align: "start",
326
+ side: "bottom",
327
+ sideOffset,
328
+ className: "w-[400px] rounded-none p-0",
329
+ onOpenAutoFocus,
330
+ children: /* @__PURE__ */ jsx(
331
+ Textarea,
332
+ {
333
+ placeholder: msg.cell.longTextPlaceholder,
334
+ "aria-label": msg.cell.longTextPlaceholder,
335
+ className: "max-h-[300px] min-h-[150px] resize-none overflow-y-auto rounded-none border-0 shadow-none focus-visible:ring-1 focus-visible:ring-ring/30",
336
+ ref: textareaRef,
337
+ value,
338
+ onBlur,
339
+ onChange,
340
+ onKeyDown
341
+ }
342
+ )
343
+ }
344
+ )
345
+ ] });
346
+ }
347
+ function NumberCell({
348
+ cell,
349
+ tableMeta,
350
+ rowIndex,
351
+ columnId,
352
+ rowHeight,
353
+ isFocused,
354
+ isEditing,
355
+ isSelected,
356
+ isSearchMatch,
357
+ isActiveSearchMatch,
358
+ readOnly
359
+ }) {
360
+ const initialValue = cell.getValue();
361
+ const [value, setValue] = React.useState(String(initialValue ?? ""));
362
+ const inputRef = React.useRef(null);
363
+ const containerRef = React.useRef(null);
364
+ const cellOpts = cell.column.columnDef.meta?.cell;
365
+ const numberCellOpts = cellOpts?.variant === "number" ? cellOpts : null;
366
+ const min = numberCellOpts?.min;
367
+ const max = numberCellOpts?.max;
368
+ const step = numberCellOpts?.step;
369
+ const prevIsEditingRef = React.useRef(false);
370
+ const prevInitialValueRef = React.useRef(initialValue);
371
+ if (initialValue !== prevInitialValueRef.current) {
372
+ prevInitialValueRef.current = initialValue;
373
+ setValue(String(initialValue ?? ""));
374
+ }
375
+ const onBlur = React.useCallback(() => {
376
+ const numValue = value === "" ? null : Number(value);
377
+ if (!readOnly && numValue !== initialValue) {
378
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: numValue });
379
+ }
380
+ tableMeta?.onCellEditingStop?.();
381
+ }, [tableMeta, rowIndex, columnId, initialValue, value, readOnly]);
382
+ const onChange = React.useCallback((event) => {
383
+ setValue(event.target.value);
384
+ }, []);
385
+ const onWrapperKeyDown = React.useCallback(
386
+ (event) => {
387
+ if (isEditing) {
388
+ if (event.key === "Enter") {
389
+ event.preventDefault();
390
+ const numValue = value === "" ? null : Number(value);
391
+ if (numValue !== initialValue) {
392
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: numValue });
393
+ }
394
+ tableMeta?.onCellEditingStop?.({ moveToNextRow: true });
395
+ } else if (event.key === "Tab") {
396
+ event.preventDefault();
397
+ const numValue = value === "" ? null : Number(value);
398
+ if (numValue !== initialValue) {
399
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: numValue });
400
+ }
401
+ tableMeta?.onCellEditingStop?.({
402
+ direction: event.shiftKey ? "left" : "right"
403
+ });
404
+ } else if (event.key === "Escape") {
405
+ event.preventDefault();
406
+ setValue(String(initialValue ?? ""));
407
+ inputRef.current?.blur();
408
+ }
409
+ } else if (isFocused) {
410
+ if (event.key === "Backspace") {
411
+ setValue("");
412
+ } else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
413
+ setValue(event.key);
414
+ }
415
+ }
416
+ },
417
+ [isEditing, isFocused, initialValue, tableMeta, rowIndex, columnId, value]
418
+ );
419
+ React.useEffect(() => {
420
+ const wasEditing = prevIsEditingRef.current;
421
+ prevIsEditingRef.current = isEditing;
422
+ if (isEditing && !wasEditing && inputRef.current) {
423
+ inputRef.current.focus();
424
+ }
425
+ }, [isEditing]);
426
+ return /* @__PURE__ */ jsx(
427
+ DataGridCellWrapper,
428
+ {
429
+ ref: containerRef,
430
+ cell,
431
+ tableMeta,
432
+ rowIndex,
433
+ columnId,
434
+ rowHeight,
435
+ isEditing,
436
+ isFocused,
437
+ isSelected,
438
+ isSearchMatch,
439
+ isActiveSearchMatch,
440
+ readOnly,
441
+ onKeyDown: onWrapperKeyDown,
442
+ children: isEditing ? /* @__PURE__ */ jsx(
443
+ "input",
444
+ {
445
+ type: "number",
446
+ ref: inputRef,
447
+ value,
448
+ min,
449
+ max,
450
+ step,
451
+ className: "w-full border-none bg-transparent p-0 outline-none focus-visible:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
452
+ onBlur,
453
+ onChange
454
+ }
455
+ ) : /* @__PURE__ */ jsx("span", { "data-slot": "grid-cell-content", children: value })
456
+ }
457
+ );
458
+ }
459
+ function UrlCell({
460
+ cell,
461
+ tableMeta,
462
+ rowIndex,
463
+ columnId,
464
+ rowHeight,
465
+ isEditing,
466
+ isFocused,
467
+ isSelected,
468
+ isSearchMatch,
469
+ isActiveSearchMatch,
470
+ readOnly
471
+ }) {
472
+ const msg = useDataGridMessages();
473
+ const initialValue = cell.getValue();
474
+ const [value, setValue] = React.useState(initialValue ?? "");
475
+ const cellRef = React.useRef(null);
476
+ const containerRef = React.useRef(null);
477
+ const prevIsEditingRef = React.useRef(false);
478
+ const prevInitialValueRef = React.useRef(initialValue);
479
+ if (initialValue !== prevInitialValueRef.current) {
480
+ prevInitialValueRef.current = initialValue;
481
+ setValue(initialValue ?? "");
482
+ if (cellRef.current && !isEditing) {
483
+ cellRef.current.textContent = initialValue ?? "";
484
+ }
485
+ }
486
+ const onBlur = React.useCallback(() => {
487
+ const currentValue = cellRef.current?.textContent?.trim() ?? "";
488
+ if (!readOnly && currentValue !== initialValue) {
489
+ tableMeta?.onDataUpdate?.({
490
+ rowIndex,
491
+ columnId,
492
+ value: currentValue || null
493
+ });
494
+ }
495
+ tableMeta?.onCellEditingStop?.();
496
+ }, [tableMeta, rowIndex, columnId, initialValue, readOnly]);
497
+ const onInput = React.useCallback((event) => {
498
+ const currentValue = event.currentTarget.textContent ?? "";
499
+ setValue(currentValue);
500
+ }, []);
501
+ const onWrapperKeyDown = React.useCallback(
502
+ (event) => {
503
+ if (isEditing) {
504
+ if (event.key === "Enter") {
505
+ event.preventDefault();
506
+ const currentValue = cellRef.current?.textContent?.trim() ?? "";
507
+ if (!readOnly && currentValue !== initialValue) {
508
+ tableMeta?.onDataUpdate?.({
509
+ rowIndex,
510
+ columnId,
511
+ value: currentValue || null
512
+ });
513
+ }
514
+ tableMeta?.onCellEditingStop?.({ moveToNextRow: true });
515
+ } else if (event.key === "Tab") {
516
+ event.preventDefault();
517
+ const currentValue = cellRef.current?.textContent?.trim() ?? "";
518
+ if (!readOnly && currentValue !== initialValue) {
519
+ tableMeta?.onDataUpdate?.({
520
+ rowIndex,
521
+ columnId,
522
+ value: currentValue || null
523
+ });
524
+ }
525
+ tableMeta?.onCellEditingStop?.({
526
+ direction: event.shiftKey ? "left" : "right"
527
+ });
528
+ } else if (event.key === "Escape") {
529
+ event.preventDefault();
530
+ setValue(initialValue ?? "");
531
+ cellRef.current?.blur();
532
+ }
533
+ } else if (isFocused && !readOnly && event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
534
+ setValue(event.key);
535
+ queueMicrotask(() => {
536
+ if (cellRef.current && cellRef.current.contentEditable === "true") {
537
+ cellRef.current.textContent = event.key;
538
+ const range = document.createRange();
539
+ const selection = window.getSelection();
540
+ range.selectNodeContents(cellRef.current);
541
+ range.collapse(false);
542
+ selection?.removeAllRanges();
543
+ selection?.addRange(range);
544
+ }
545
+ });
546
+ }
547
+ },
548
+ [isEditing, isFocused, initialValue, tableMeta, rowIndex, columnId, readOnly]
549
+ );
550
+ const onLinkClick = React.useCallback(
551
+ (event) => {
552
+ if (isEditing) {
553
+ event.preventDefault();
554
+ return;
555
+ }
556
+ const href = getUrlHref(value);
557
+ if (!href) {
558
+ event.preventDefault();
559
+ toastError(msg.cell.invalidUrlTitle, msg.cell.invalidUrlDescription);
560
+ return;
561
+ }
562
+ event.stopPropagation();
563
+ },
564
+ [isEditing, value, msg]
565
+ );
566
+ React.useEffect(() => {
567
+ const wasEditing = prevIsEditingRef.current;
568
+ prevIsEditingRef.current = isEditing;
569
+ if (isEditing && !wasEditing && cellRef.current) {
570
+ cellRef.current.focus();
571
+ if (!cellRef.current.textContent && value) {
572
+ cellRef.current.textContent = value;
573
+ }
574
+ if (cellRef.current.textContent) {
575
+ const range = document.createRange();
576
+ const selection = window.getSelection();
577
+ range.selectNodeContents(cellRef.current);
578
+ range.collapse(false);
579
+ selection?.removeAllRanges();
580
+ selection?.addRange(range);
581
+ }
582
+ }
583
+ }, [isEditing, value]);
584
+ const displayValue = !isEditing ? value ?? "" : "";
585
+ const urlHref = displayValue ? getUrlHref(displayValue) : "";
586
+ const isDangerousUrl = displayValue && !urlHref;
587
+ return /* @__PURE__ */ jsx(
588
+ DataGridCellWrapper,
589
+ {
590
+ ref: containerRef,
591
+ cell,
592
+ tableMeta,
593
+ rowIndex,
594
+ columnId,
595
+ rowHeight,
596
+ isEditing,
597
+ isFocused,
598
+ isSelected,
599
+ isSearchMatch,
600
+ isActiveSearchMatch,
601
+ readOnly,
602
+ onKeyDown: onWrapperKeyDown,
603
+ children: !isEditing && displayValue ? /* @__PURE__ */ jsx("div", { "data-slot": "grid-cell-content", className: "size-full overflow-hidden", children: /* @__PURE__ */ jsx(
604
+ "a",
605
+ {
606
+ "data-focused": isFocused && !isDangerousUrl ? "" : void 0,
607
+ "data-invalid": isDangerousUrl ? "" : void 0,
608
+ href: urlHref,
609
+ target: "_blank",
610
+ rel: "noopener noreferrer",
611
+ className: "truncate text-link-ink underline decoration-link-ink underline-offset-2 hover:text-link-ink-hover hover:decoration-link-ink-hover data-[invalid]:cursor-not-allowed data-[focused]:text-foreground data-[invalid]:text-destructive-ink data-[focused]:decoration-foreground/50 data-[invalid]:decoration-destructive-ink data-[focused]:hover:decoration-foreground/70 data-[invalid]:hover:decoration-destructive-ink",
612
+ onClick: onLinkClick,
613
+ children: displayValue
614
+ }
615
+ ) }) : /* @__PURE__ */ jsx(
616
+ "div",
617
+ {
618
+ role: "textbox",
619
+ "data-slot": "grid-cell-content",
620
+ contentEditable: isEditing,
621
+ tabIndex: -1,
622
+ ref: cellRef,
623
+ onBlur,
624
+ onInput,
625
+ suppressContentEditableWarning: true,
626
+ className: cn("size-full overflow-hidden outline-none focus-visible:outline-none", {
627
+ "whitespace-nowrap [&_*]:inline [&_*]:whitespace-nowrap [&_br]:hidden": isEditing
628
+ }),
629
+ children: displayValue
630
+ }
631
+ )
632
+ }
633
+ );
634
+ }
635
+ function CheckboxCell({
636
+ cell,
637
+ tableMeta,
638
+ rowIndex,
639
+ columnId,
640
+ rowHeight,
641
+ isFocused,
642
+ isSelected,
643
+ isSearchMatch,
644
+ isActiveSearchMatch,
645
+ readOnly
646
+ }) {
647
+ const initialValue = cell.getValue();
648
+ const [value, setValue] = React.useState(Boolean(initialValue));
649
+ const containerRef = React.useRef(null);
650
+ const prevInitialValueRef = React.useRef(initialValue);
651
+ if (initialValue !== prevInitialValueRef.current) {
652
+ prevInitialValueRef.current = initialValue;
653
+ setValue(Boolean(initialValue));
654
+ }
655
+ const onCheckedChange = React.useCallback(
656
+ (checked) => {
657
+ if (readOnly) return;
658
+ setValue(checked);
659
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: checked });
660
+ },
661
+ [tableMeta, rowIndex, columnId, readOnly]
662
+ );
663
+ const onWrapperKeyDown = React.useCallback(
664
+ (event) => {
665
+ if (isFocused && !readOnly && (event.key === " " || event.key === "Enter")) {
666
+ event.preventDefault();
667
+ event.stopPropagation();
668
+ onCheckedChange(!value);
669
+ } else if (isFocused && event.key === "Tab") {
670
+ event.preventDefault();
671
+ tableMeta?.onCellEditingStop?.({
672
+ direction: event.shiftKey ? "left" : "right"
673
+ });
674
+ }
675
+ },
676
+ [isFocused, value, onCheckedChange, tableMeta, readOnly]
677
+ );
678
+ const onWrapperClick = React.useCallback(
679
+ (event) => {
680
+ if (isFocused && !readOnly) {
681
+ event.preventDefault();
682
+ event.stopPropagation();
683
+ onCheckedChange(!value);
684
+ }
685
+ },
686
+ [isFocused, value, onCheckedChange, readOnly]
687
+ );
688
+ const onCheckboxClick = React.useCallback((event) => {
689
+ event.stopPropagation();
690
+ }, []);
691
+ const onCheckboxMouseDown = React.useCallback((event) => {
692
+ event.stopPropagation();
693
+ }, []);
694
+ const onCheckboxDoubleClick = React.useCallback((event) => {
695
+ event.stopPropagation();
696
+ }, []);
697
+ return /* @__PURE__ */ jsx(
698
+ DataGridCellWrapper,
699
+ {
700
+ ref: containerRef,
701
+ cell,
702
+ tableMeta,
703
+ rowIndex,
704
+ columnId,
705
+ rowHeight,
706
+ isEditing: false,
707
+ isFocused,
708
+ isSelected,
709
+ isSearchMatch,
710
+ isActiveSearchMatch,
711
+ readOnly,
712
+ className: "flex size-full justify-center",
713
+ onClick: onWrapperClick,
714
+ onKeyDown: onWrapperKeyDown,
715
+ children: /* @__PURE__ */ jsx(
716
+ Checkbox,
717
+ {
718
+ checked: value,
719
+ onCheckedChange,
720
+ disabled: readOnly,
721
+ onClick: onCheckboxClick,
722
+ onMouseDown: onCheckboxMouseDown,
723
+ onDoubleClick: onCheckboxDoubleClick
724
+ }
725
+ )
726
+ }
727
+ );
728
+ }
729
+ function SelectCell({
730
+ cell,
731
+ tableMeta,
732
+ rowIndex,
733
+ columnId,
734
+ rowHeight,
735
+ isFocused,
736
+ isEditing,
737
+ isSelected,
738
+ isSearchMatch,
739
+ isActiveSearchMatch,
740
+ readOnly
741
+ }) {
742
+ const initialValue = cell.getValue() ?? void 0;
743
+ const [value, setValue] = React.useState(initialValue);
744
+ const containerRef = React.useRef(null);
745
+ const cellOpts = cell.column.columnDef.meta?.cell;
746
+ const options = React.useMemo(() => cellOpts?.variant === "select" ? cellOpts.options : [], [cellOpts]);
747
+ const optionByValue = React.useMemo(() => new Map(options.map((option) => [option.value, option])), [options]);
748
+ const prevInitialValueRef = React.useRef(initialValue);
749
+ if (initialValue !== prevInitialValueRef.current) {
750
+ prevInitialValueRef.current = initialValue;
751
+ setValue(initialValue);
752
+ }
753
+ const onValueChange = React.useCallback(
754
+ (newValue) => {
755
+ if (readOnly) return;
756
+ setValue(newValue);
757
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: newValue });
758
+ tableMeta?.onCellEditingStop?.();
759
+ },
760
+ [tableMeta, rowIndex, columnId, readOnly]
761
+ );
762
+ const onOpenChange = React.useCallback(
763
+ (open) => {
764
+ if (open && !readOnly) {
765
+ tableMeta?.onCellEditingStart?.(rowIndex, columnId);
766
+ } else {
767
+ tableMeta?.onCellEditingStop?.();
768
+ }
769
+ },
770
+ [tableMeta, rowIndex, columnId, readOnly]
771
+ );
772
+ const onWrapperKeyDown = React.useCallback(
773
+ (event) => {
774
+ if (isEditing && event.key === "Escape") {
775
+ event.preventDefault();
776
+ setValue(initialValue);
777
+ tableMeta?.onCellEditingStop?.();
778
+ } else if (isFocused && event.key === "Tab") {
779
+ event.preventDefault();
780
+ tableMeta?.onCellEditingStop?.({
781
+ direction: event.shiftKey ? "left" : "right"
782
+ });
783
+ }
784
+ },
785
+ [isEditing, isFocused, initialValue, tableMeta]
786
+ );
787
+ const displayLabel = value ? optionByValue.get(value)?.label ?? value : null;
788
+ return /* @__PURE__ */ jsx(
789
+ DataGridCellWrapper,
790
+ {
791
+ ref: containerRef,
792
+ cell,
793
+ tableMeta,
794
+ rowIndex,
795
+ columnId,
796
+ rowHeight,
797
+ isEditing,
798
+ isFocused,
799
+ isSelected,
800
+ isSearchMatch,
801
+ isActiveSearchMatch,
802
+ readOnly,
803
+ onKeyDown: onWrapperKeyDown,
804
+ children: isEditing ? /* @__PURE__ */ jsxs(Select, { value, onValueChange, open: isEditing, onOpenChange, children: [
805
+ /* @__PURE__ */ jsx(
806
+ SelectTrigger,
807
+ {
808
+ "aria-label": cell.column.columnDef.meta?.label ?? columnId,
809
+ className: "size-full items-start border-none bg-transparent p-0 shadow-none focus:ring-0 focus-visible:ring-0 [&>span]:line-clamp-none [&_svg]:hidden",
810
+ children: displayLabel ? (
811
+ // span (không dùng <Badge> vì Badge render <div>, đặt trong <button> là HTML sai)
812
+ /* @__PURE__ */ jsx("span", { className: cn(badgeVariants({ variant: "secondary" }), "whitespace-pre-wrap px-1.5 py-px"), children: /* @__PURE__ */ jsx(SelectValue, {}) })
813
+ ) : /* @__PURE__ */ jsx(SelectValue, {})
814
+ }
815
+ ),
816
+ /* @__PURE__ */ jsx(
817
+ SelectContent,
818
+ {
819
+ "data-grid-cell-editor": "",
820
+ align: "start",
821
+ alignOffset: -8,
822
+ sideOffset: -8,
823
+ className: "min-w-[calc(var(--radix-select-trigger-width)+16px)]",
824
+ children: /* @__PURE__ */ jsx(SelectGroup, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
825
+ }
826
+ )
827
+ ] }) : displayLabel ? /* @__PURE__ */ jsx(Badge, { "data-slot": "grid-cell-content", variant: "secondary", className: "whitespace-pre-wrap px-1.5 py-px", children: displayLabel }) : null
828
+ }
829
+ );
830
+ }
831
+ function MultiSelectCell({
832
+ cell,
833
+ tableMeta,
834
+ rowIndex,
835
+ columnId,
836
+ rowHeight,
837
+ isFocused,
838
+ isEditing,
839
+ isSelected,
840
+ isSearchMatch,
841
+ isActiveSearchMatch,
842
+ readOnly
843
+ }) {
844
+ const msg = useDataGridMessages();
845
+ const cellValue = React.useMemo(() => {
846
+ const value = cell.getValue();
847
+ return value ?? [];
848
+ }, [cell]);
849
+ const cellKey = getCellKey(rowIndex, columnId);
850
+ const prevCellKeyRef = React.useRef(cellKey);
851
+ const [selectedValues, setSelectedValues] = React.useState(cellValue);
852
+ const [searchValue, setSearchValue] = React.useState("");
853
+ const containerRef = React.useRef(null);
854
+ const inputRef = React.useRef(null);
855
+ const cellOpts = cell.column.columnDef.meta?.cell;
856
+ const options = React.useMemo(() => cellOpts?.variant === "multi-select" ? cellOpts.options : [], [cellOpts]);
857
+ const optionByValue = React.useMemo(() => new Map(options.map((option) => [option.value, option])), [options]);
858
+ const sideOffset = -(containerRef.current?.clientHeight ?? 0);
859
+ const prevCellValueRef = React.useRef(cellValue);
860
+ if (cellValue !== prevCellValueRef.current) {
861
+ prevCellValueRef.current = cellValue;
862
+ setSelectedValues(cellValue);
863
+ }
864
+ if (prevCellKeyRef.current !== cellKey) {
865
+ prevCellKeyRef.current = cellKey;
866
+ setSearchValue("");
867
+ }
868
+ const onValueChange = React.useCallback(
869
+ (value) => {
870
+ if (readOnly) return;
871
+ let newValues = [];
872
+ setSelectedValues((curr) => {
873
+ newValues = curr.includes(value) ? curr.filter((v) => v !== value) : [...curr, value];
874
+ return newValues;
875
+ });
876
+ queueMicrotask(() => {
877
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: newValues });
878
+ inputRef.current?.focus();
879
+ });
880
+ setSearchValue("");
881
+ },
882
+ [tableMeta, rowIndex, columnId, readOnly]
883
+ );
884
+ const removeValue = React.useCallback(
885
+ (valueToRemove, event) => {
886
+ if (readOnly) return;
887
+ event?.stopPropagation();
888
+ event?.preventDefault();
889
+ let newValues = [];
890
+ setSelectedValues((curr) => {
891
+ newValues = curr.filter((v) => v !== valueToRemove);
892
+ return newValues;
893
+ });
894
+ queueMicrotask(() => {
895
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: newValues });
896
+ inputRef.current?.focus();
897
+ });
898
+ },
899
+ [tableMeta, rowIndex, columnId, readOnly]
900
+ );
901
+ const clearAll = React.useCallback(() => {
902
+ if (readOnly) return;
903
+ setSelectedValues([]);
904
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: [] });
905
+ queueMicrotask(() => inputRef.current?.focus());
906
+ }, [tableMeta, rowIndex, columnId, readOnly]);
907
+ const onOpenChange = React.useCallback(
908
+ (open) => {
909
+ if (open && !readOnly) {
910
+ tableMeta?.onCellEditingStart?.(rowIndex, columnId);
911
+ } else {
912
+ setSearchValue("");
913
+ tableMeta?.onCellEditingStop?.();
914
+ }
915
+ },
916
+ [tableMeta, rowIndex, columnId, readOnly]
917
+ );
918
+ const onOpenAutoFocus = React.useCallback((event) => {
919
+ event.preventDefault();
920
+ inputRef.current?.focus();
921
+ }, []);
922
+ const onWrapperKeyDown = React.useCallback(
923
+ (event) => {
924
+ if (isEditing && event.key === "Escape") {
925
+ event.preventDefault();
926
+ setSelectedValues(cellValue);
927
+ setSearchValue("");
928
+ tableMeta?.onCellEditingStop?.();
929
+ } else if (isFocused && event.key === "Tab") {
930
+ event.preventDefault();
931
+ setSearchValue("");
932
+ tableMeta?.onCellEditingStop?.({
933
+ direction: event.shiftKey ? "left" : "right"
934
+ });
935
+ }
936
+ },
937
+ [isEditing, isFocused, cellValue, tableMeta]
938
+ );
939
+ const onInputKeyDown = React.useCallback(
940
+ (event) => {
941
+ if (event.key === "Backspace" && searchValue === "") {
942
+ event.preventDefault();
943
+ let newValues = null;
944
+ setSelectedValues((curr) => {
945
+ if (curr.length === 0) return curr;
946
+ newValues = curr.slice(0, -1);
947
+ return newValues;
948
+ });
949
+ queueMicrotask(() => {
950
+ if (newValues !== null) {
951
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: newValues });
952
+ }
953
+ inputRef.current?.focus();
954
+ });
955
+ }
956
+ if (event.key === "Escape") {
957
+ event.stopPropagation();
958
+ }
959
+ },
960
+ [searchValue, tableMeta, rowIndex, columnId]
961
+ );
962
+ const displayLabels = selectedValues.map((val) => optionByValue.get(val)?.label ?? val).filter(Boolean);
963
+ const selectedValuesSet = React.useMemo(() => new Set(selectedValues), [selectedValues]);
964
+ const lineCount = getLineCount(rowHeight);
965
+ const { visibleItems: visibleLabels, hiddenCount: hiddenBadgeCount } = useBadgeOverflow({
966
+ items: displayLabels,
967
+ getLabel: (label) => label,
968
+ containerRef,
969
+ lineCount
970
+ });
971
+ return /* @__PURE__ */ jsxs(
972
+ DataGridCellWrapper,
973
+ {
974
+ ref: containerRef,
975
+ cell,
976
+ tableMeta,
977
+ rowIndex,
978
+ columnId,
979
+ rowHeight,
980
+ isEditing,
981
+ isFocused,
982
+ isSelected,
983
+ isSearchMatch,
984
+ isActiveSearchMatch,
985
+ readOnly,
986
+ onKeyDown: onWrapperKeyDown,
987
+ children: [
988
+ isEditing ? /* @__PURE__ */ jsxs(Popover, { open: isEditing, onOpenChange, children: [
989
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0" }) }),
990
+ /* @__PURE__ */ jsx(
991
+ PopoverContent,
992
+ {
993
+ "data-grid-cell-editor": "",
994
+ align: "start",
995
+ sideOffset,
996
+ className: "w-[300px] rounded-none p-0",
997
+ onOpenAutoFocus,
998
+ children: /* @__PURE__ */ jsxs(Command, { className: "[&_[data-slot=command-input-wrapper]]:h-auto [&_[data-slot=command-input-wrapper]]:gap-0 [&_[data-slot=command-input-wrapper]]:border-none [&_[data-slot=command-input-wrapper]]:p-0 [&_[data-slot=command-input-wrapper]_svg]:hidden", children: [
999
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-9 flex-wrap items-center gap-1 border-b px-3 py-1.5", children: [
1000
+ selectedValues.map((value) => {
1001
+ const label = optionByValue.get(value)?.label ?? value;
1002
+ return /* @__PURE__ */ jsxs(Badge, { variant: "secondary", className: "gap-1 px-1.5 py-px", children: [
1003
+ label,
1004
+ /* @__PURE__ */ jsx(
1005
+ "button",
1006
+ {
1007
+ type: "button",
1008
+ "aria-label": `${msg.cell.clearAll}: ${label}`,
1009
+ onClick: (event) => removeValue(value, event),
1010
+ onPointerDown: (event) => {
1011
+ event.preventDefault();
1012
+ event.stopPropagation();
1013
+ },
1014
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-3" })
1015
+ }
1016
+ )
1017
+ ] }, value);
1018
+ }),
1019
+ /* @__PURE__ */ jsx(
1020
+ CommandInput,
1021
+ {
1022
+ ref: inputRef,
1023
+ value: searchValue,
1024
+ onValueChange: setSearchValue,
1025
+ onKeyDown: onInputKeyDown,
1026
+ placeholder: msg.cell.optionSearchPlaceholder,
1027
+ "aria-label": msg.cell.optionSearchPlaceholder,
1028
+ className: "h-auto flex-1 p-0"
1029
+ }
1030
+ )
1031
+ ] }),
1032
+ /* @__PURE__ */ jsxs(CommandList, { className: "max-h-full", children: [
1033
+ /* @__PURE__ */ jsx(CommandEmpty, { children: msg.cell.optionEmpty }),
1034
+ /* @__PURE__ */ jsx(CommandGroup, { className: "max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden", children: options.map((option) => {
1035
+ const isSelected2 = selectedValuesSet.has(option.value);
1036
+ return /* @__PURE__ */ jsxs(CommandItem, { value: option.label, onSelect: () => onValueChange(option.value), children: [
1037
+ /* @__PURE__ */ jsx(
1038
+ "div",
1039
+ {
1040
+ className: cn(
1041
+ "flex size-4 items-center justify-center rounded-sm border border-choice-edge",
1042
+ isSelected2 ? "border-control-on bg-control-on text-control-check" : "[&_svg]:invisible"
1043
+ ),
1044
+ children: /* @__PURE__ */ jsx(Check, { className: "size-3" })
1045
+ }
1046
+ ),
1047
+ /* @__PURE__ */ jsx("span", { children: option.label })
1048
+ ] }, option.value);
1049
+ }) }),
1050
+ selectedValues.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1051
+ /* @__PURE__ */ jsx(CommandSeparator, {}),
1052
+ /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx(CommandItem, { onSelect: clearAll, className: "justify-center text-muted-foreground", children: msg.cell.clearAll }) })
1053
+ ] })
1054
+ ] })
1055
+ ] })
1056
+ }
1057
+ )
1058
+ ] }) : null,
1059
+ displayLabels.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1 overflow-hidden", children: [
1060
+ visibleLabels.map((label, index) => /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "px-1.5 py-px", children: label }, selectedValues[index])),
1061
+ hiddenBadgeCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "px-1.5 py-px text-muted-foreground", children: [
1062
+ "+",
1063
+ hiddenBadgeCount
1064
+ ] })
1065
+ ] }) : null
1066
+ ]
1067
+ }
1068
+ );
1069
+ }
1070
+ function DateCell({
1071
+ cell,
1072
+ tableMeta,
1073
+ rowIndex,
1074
+ columnId,
1075
+ rowHeight,
1076
+ isFocused,
1077
+ isEditing,
1078
+ isSelected,
1079
+ isSearchMatch,
1080
+ isActiveSearchMatch,
1081
+ readOnly
1082
+ }) {
1083
+ const initialValue = cell.getValue();
1084
+ const [value, setValue] = React.useState(initialValue ?? "");
1085
+ const containerRef = React.useRef(null);
1086
+ const prevInitialValueRef = React.useRef(initialValue);
1087
+ if (initialValue !== prevInitialValueRef.current) {
1088
+ prevInitialValueRef.current = initialValue;
1089
+ setValue(initialValue ?? "");
1090
+ }
1091
+ const selectedDate = value ? parseLocalDate(value) ?? void 0 : void 0;
1092
+ const onDateSelect = React.useCallback(
1093
+ (date) => {
1094
+ if (!date || readOnly) return;
1095
+ const formattedDate = formatDateToString(date);
1096
+ setValue(formattedDate);
1097
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: formattedDate });
1098
+ tableMeta?.onCellEditingStop?.();
1099
+ },
1100
+ [tableMeta, rowIndex, columnId, readOnly]
1101
+ );
1102
+ const onOpenChange = React.useCallback(
1103
+ (open) => {
1104
+ if (open && !readOnly) {
1105
+ tableMeta?.onCellEditingStart?.(rowIndex, columnId);
1106
+ } else {
1107
+ tableMeta?.onCellEditingStop?.();
1108
+ }
1109
+ },
1110
+ [tableMeta, rowIndex, columnId, readOnly]
1111
+ );
1112
+ const onWrapperKeyDown = React.useCallback(
1113
+ (event) => {
1114
+ if (isEditing && event.key === "Escape") {
1115
+ event.preventDefault();
1116
+ setValue(initialValue);
1117
+ tableMeta?.onCellEditingStop?.();
1118
+ } else if (isFocused && event.key === "Tab") {
1119
+ event.preventDefault();
1120
+ tableMeta?.onCellEditingStop?.({
1121
+ direction: event.shiftKey ? "left" : "right"
1122
+ });
1123
+ }
1124
+ },
1125
+ [isEditing, isFocused, initialValue, tableMeta]
1126
+ );
1127
+ return /* @__PURE__ */ jsx(
1128
+ DataGridCellWrapper,
1129
+ {
1130
+ ref: containerRef,
1131
+ cell,
1132
+ tableMeta,
1133
+ rowIndex,
1134
+ columnId,
1135
+ rowHeight,
1136
+ isEditing,
1137
+ isFocused,
1138
+ isSelected,
1139
+ isSearchMatch,
1140
+ isActiveSearchMatch,
1141
+ readOnly,
1142
+ onKeyDown: onWrapperKeyDown,
1143
+ children: /* @__PURE__ */ jsxs(Popover, { open: isEditing, onOpenChange, children: [
1144
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("span", { "data-slot": "grid-cell-content", children: formatDateForDisplay(value) }) }),
1145
+ isEditing && /* @__PURE__ */ jsx(PopoverContent, { "data-grid-cell-editor": "", align: "start", alignOffset: -8, className: "w-auto p-0", children: /* @__PURE__ */ jsx(
1146
+ Calendar,
1147
+ {
1148
+ mode: "single",
1149
+ locale: vi,
1150
+ autoFocus: true,
1151
+ defaultMonth: selectedDate ?? /* @__PURE__ */ new Date(),
1152
+ selected: selectedDate,
1153
+ onSelect: (date) => {
1154
+ if (date) onDateSelect(date);
1155
+ }
1156
+ }
1157
+ ) })
1158
+ ] })
1159
+ }
1160
+ );
1161
+ }
1162
+ function FileCell({
1163
+ cell,
1164
+ tableMeta,
1165
+ rowIndex,
1166
+ columnId,
1167
+ rowHeight,
1168
+ isFocused,
1169
+ isEditing,
1170
+ isSelected,
1171
+ isSearchMatch,
1172
+ isActiveSearchMatch,
1173
+ readOnly
1174
+ }) {
1175
+ const msg = useDataGridMessages();
1176
+ const cellValue = React.useMemo(() => cell.getValue() ?? [], [cell]);
1177
+ const cellKey = getCellKey(rowIndex, columnId);
1178
+ const prevCellKeyRef = React.useRef(cellKey);
1179
+ const labelId = React.useId();
1180
+ const descriptionId = React.useId();
1181
+ const [files, setFiles] = React.useState(cellValue);
1182
+ const [uploadingFiles, setUploadingFiles] = React.useState(/* @__PURE__ */ new Set());
1183
+ const [deletingFiles, setDeletingFiles] = React.useState(/* @__PURE__ */ new Set());
1184
+ const [isDraggingOver, setIsDraggingOver] = React.useState(false);
1185
+ const [isDragging, setIsDragging] = React.useState(false);
1186
+ const [error, setError] = React.useState(null);
1187
+ const isUploading = uploadingFiles.size > 0;
1188
+ const isDeleting = deletingFiles.size > 0;
1189
+ const isPending = isUploading || isDeleting;
1190
+ const containerRef = React.useRef(null);
1191
+ const fileInputRef = React.useRef(null);
1192
+ const dropzoneRef = React.useRef(null);
1193
+ const cellOpts = cell.column.columnDef.meta?.cell;
1194
+ const sideOffset = -(containerRef.current?.clientHeight ?? 0);
1195
+ const fileCellOpts = cellOpts?.variant === "file" ? cellOpts : null;
1196
+ const maxFileSize = fileCellOpts?.maxFileSize ?? 10 * 1024 * 1024;
1197
+ const maxFiles = fileCellOpts?.maxFiles ?? 10;
1198
+ const accept = fileCellOpts?.accept;
1199
+ const multiple = fileCellOpts?.multiple ?? false;
1200
+ const acceptedTypes = React.useMemo(() => accept ? accept.split(",").map((t) => t.trim()) : null, [accept]);
1201
+ const prevCellValueRef = React.useRef(cellValue);
1202
+ if (cellValue !== prevCellValueRef.current) {
1203
+ prevCellValueRef.current = cellValue;
1204
+ for (const file of files) {
1205
+ if (file.url) {
1206
+ URL.revokeObjectURL(file.url);
1207
+ }
1208
+ }
1209
+ setFiles(cellValue);
1210
+ setError(null);
1211
+ }
1212
+ if (prevCellKeyRef.current !== cellKey) {
1213
+ prevCellKeyRef.current = cellKey;
1214
+ setError(null);
1215
+ }
1216
+ const validateFile = React.useCallback(
1217
+ (file) => {
1218
+ if (maxFileSize && file.size > maxFileSize) {
1219
+ return msg.file.tooLarge(formatFileSize(maxFileSize));
1220
+ }
1221
+ if (acceptedTypes) {
1222
+ const fileExtension = `.${file.name.split(".").pop()}`;
1223
+ const isAccepted = acceptedTypes.some((type) => {
1224
+ if (type.endsWith("/*")) {
1225
+ const baseType = type.slice(0, -2);
1226
+ return file.type.startsWith(`${baseType}/`);
1227
+ }
1228
+ if (type.startsWith(".")) {
1229
+ return fileExtension.toLowerCase() === type.toLowerCase();
1230
+ }
1231
+ return file.type === type;
1232
+ });
1233
+ if (!isAccepted) {
1234
+ return msg.file.wrongType;
1235
+ }
1236
+ }
1237
+ return null;
1238
+ },
1239
+ [maxFileSize, acceptedTypes, msg]
1240
+ );
1241
+ const addFiles = React.useCallback(
1242
+ async (newFiles, skipUpload = false) => {
1243
+ if (readOnly || isPending) return;
1244
+ setError(null);
1245
+ if (maxFiles && files.length + newFiles.length > maxFiles) {
1246
+ const errorMessage = msg.file.tooMany(maxFiles);
1247
+ setError(errorMessage);
1248
+ toastMessage(errorMessage);
1249
+ setTimeout(() => {
1250
+ setError(null);
1251
+ }, 2e3);
1252
+ return;
1253
+ }
1254
+ const rejectedFiles = [];
1255
+ const filesToValidate = [];
1256
+ for (const file of newFiles) {
1257
+ const validationError = validateFile(file);
1258
+ if (validationError) {
1259
+ rejectedFiles.push({ name: file.name, reason: validationError });
1260
+ continue;
1261
+ }
1262
+ filesToValidate.push(file);
1263
+ }
1264
+ if (rejectedFiles.length > 0) {
1265
+ const firstError = rejectedFiles[0];
1266
+ if (firstError) {
1267
+ setError(firstError.reason);
1268
+ const truncatedName = firstError.name.length > 20 ? `${firstError.name.slice(0, 20)}...` : firstError.name;
1269
+ if (rejectedFiles.length === 1) {
1270
+ toastMessage(firstError.reason, msg.file.rejectedOne(truncatedName));
1271
+ } else {
1272
+ toastMessage(firstError.reason, msg.file.rejectedMany(truncatedName, rejectedFiles.length - 1));
1273
+ }
1274
+ setTimeout(() => {
1275
+ setError(null);
1276
+ }, 2e3);
1277
+ }
1278
+ }
1279
+ if (filesToValidate.length > 0) {
1280
+ if (!skipUpload) {
1281
+ const tempFiles = filesToValidate.map((f) => ({
1282
+ id: generateFileId(),
1283
+ name: f.name,
1284
+ size: f.size,
1285
+ type: f.type,
1286
+ url: void 0
1287
+ }));
1288
+ const filesWithTemp = [...files, ...tempFiles];
1289
+ setFiles(filesWithTemp);
1290
+ const uploadingIds = new Set(tempFiles.map((f) => f.id));
1291
+ setUploadingFiles(uploadingIds);
1292
+ let uploadedFiles = [];
1293
+ if (tableMeta?.onFilesUpload) {
1294
+ try {
1295
+ uploadedFiles = await tableMeta.onFilesUpload({
1296
+ files: filesToValidate,
1297
+ rowIndex,
1298
+ columnId
1299
+ });
1300
+ } catch (error2) {
1301
+ toastError(error2 instanceof Error ? error2.message : msg.file.uploadFailed(filesToValidate.length));
1302
+ setFiles((prev) => prev.filter((f) => !uploadingIds.has(f.id)));
1303
+ setUploadingFiles(/* @__PURE__ */ new Set());
1304
+ return;
1305
+ }
1306
+ } else {
1307
+ uploadedFiles = filesToValidate.map((f, i) => ({
1308
+ id: tempFiles[i]?.id ?? generateFileId(),
1309
+ name: f.name,
1310
+ size: f.size,
1311
+ type: f.type,
1312
+ url: URL.createObjectURL(f)
1313
+ }));
1314
+ }
1315
+ const finalFiles = filesWithTemp.map((f) => {
1316
+ if (uploadingIds.has(f.id)) {
1317
+ return uploadedFiles.find((uf) => uf.name === f.name) ?? f;
1318
+ }
1319
+ return f;
1320
+ }).filter((f) => f.url !== void 0);
1321
+ setFiles(finalFiles);
1322
+ setUploadingFiles(/* @__PURE__ */ new Set());
1323
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: finalFiles });
1324
+ } else {
1325
+ const newFilesData = filesToValidate.map((f) => ({
1326
+ id: generateFileId(),
1327
+ name: f.name,
1328
+ size: f.size,
1329
+ type: f.type,
1330
+ url: URL.createObjectURL(f)
1331
+ }));
1332
+ const updatedFiles = [...files, ...newFilesData];
1333
+ setFiles(updatedFiles);
1334
+ tableMeta?.onDataUpdate?.({
1335
+ rowIndex,
1336
+ columnId,
1337
+ value: updatedFiles
1338
+ });
1339
+ }
1340
+ }
1341
+ },
1342
+ [files, maxFiles, validateFile, tableMeta, rowIndex, columnId, readOnly, isPending, msg]
1343
+ );
1344
+ const removeFile = React.useCallback(
1345
+ async (fileId) => {
1346
+ if (readOnly || isPending) return;
1347
+ setError(null);
1348
+ const fileToRemove = files.find((f) => f.id === fileId);
1349
+ if (!fileToRemove) return;
1350
+ setDeletingFiles((prev) => new Set(prev).add(fileId));
1351
+ if (tableMeta?.onFilesDelete) {
1352
+ try {
1353
+ await tableMeta.onFilesDelete({
1354
+ fileIds: [fileId],
1355
+ rowIndex,
1356
+ columnId
1357
+ });
1358
+ } catch (error2) {
1359
+ toastError(error2 instanceof Error ? error2.message : msg.file.deleteFailed(fileToRemove.name));
1360
+ setDeletingFiles((prev) => {
1361
+ const next = new Set(prev);
1362
+ next.delete(fileId);
1363
+ return next;
1364
+ });
1365
+ return;
1366
+ }
1367
+ }
1368
+ if (fileToRemove.url?.startsWith("blob:")) {
1369
+ URL.revokeObjectURL(fileToRemove.url);
1370
+ }
1371
+ const updatedFiles = files.filter((f) => f.id !== fileId);
1372
+ setFiles(updatedFiles);
1373
+ setDeletingFiles((prev) => {
1374
+ const next = new Set(prev);
1375
+ next.delete(fileId);
1376
+ return next;
1377
+ });
1378
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: updatedFiles });
1379
+ },
1380
+ [files, tableMeta, rowIndex, columnId, readOnly, isPending, msg]
1381
+ );
1382
+ const clearAll = React.useCallback(async () => {
1383
+ if (readOnly || isPending) return;
1384
+ setError(null);
1385
+ const fileIds = files.map((f) => f.id);
1386
+ setDeletingFiles(new Set(fileIds));
1387
+ if (tableMeta?.onFilesDelete && files.length > 0) {
1388
+ try {
1389
+ await tableMeta.onFilesDelete({
1390
+ fileIds,
1391
+ rowIndex,
1392
+ columnId
1393
+ });
1394
+ } catch (error2) {
1395
+ toastError(error2 instanceof Error ? error2.message : msg.file.deleteFailedMany);
1396
+ setDeletingFiles(/* @__PURE__ */ new Set());
1397
+ return;
1398
+ }
1399
+ }
1400
+ for (const file of files) {
1401
+ if (file.url?.startsWith("blob:")) {
1402
+ URL.revokeObjectURL(file.url);
1403
+ }
1404
+ }
1405
+ setFiles([]);
1406
+ setDeletingFiles(/* @__PURE__ */ new Set());
1407
+ tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: [] });
1408
+ }, [files, tableMeta, rowIndex, columnId, readOnly, isPending, msg]);
1409
+ const onCellDragEnter = React.useCallback((event) => {
1410
+ event.preventDefault();
1411
+ event.stopPropagation();
1412
+ if (event.dataTransfer.types.includes("Files")) {
1413
+ setIsDraggingOver(true);
1414
+ }
1415
+ }, []);
1416
+ const onCellDragLeave = React.useCallback((event) => {
1417
+ event.preventDefault();
1418
+ event.stopPropagation();
1419
+ const rect = event.currentTarget.getBoundingClientRect();
1420
+ const x = event.clientX;
1421
+ const y = event.clientY;
1422
+ if (x <= rect.left || x >= rect.right || y <= rect.top || y >= rect.bottom) {
1423
+ setIsDraggingOver(false);
1424
+ }
1425
+ }, []);
1426
+ const onCellDragOver = React.useCallback((event) => {
1427
+ event.preventDefault();
1428
+ event.stopPropagation();
1429
+ }, []);
1430
+ const onCellDrop = React.useCallback(
1431
+ (event) => {
1432
+ event.preventDefault();
1433
+ event.stopPropagation();
1434
+ setIsDraggingOver(false);
1435
+ const droppedFiles = Array.from(event.dataTransfer.files);
1436
+ if (droppedFiles.length > 0) {
1437
+ addFiles(droppedFiles, false);
1438
+ }
1439
+ },
1440
+ [addFiles]
1441
+ );
1442
+ const onDropzoneDragEnter = React.useCallback((event) => {
1443
+ event.preventDefault();
1444
+ event.stopPropagation();
1445
+ setIsDragging(true);
1446
+ }, []);
1447
+ const onDropzoneDragLeave = React.useCallback((event) => {
1448
+ event.preventDefault();
1449
+ event.stopPropagation();
1450
+ const rect = event.currentTarget.getBoundingClientRect();
1451
+ const x = event.clientX;
1452
+ const y = event.clientY;
1453
+ if (x <= rect.left || x >= rect.right || y <= rect.top || y >= rect.bottom) {
1454
+ setIsDragging(false);
1455
+ }
1456
+ }, []);
1457
+ const onDropzoneDragOver = React.useCallback((event) => {
1458
+ event.preventDefault();
1459
+ event.stopPropagation();
1460
+ }, []);
1461
+ const onDropzoneDrop = React.useCallback(
1462
+ (event) => {
1463
+ event.preventDefault();
1464
+ event.stopPropagation();
1465
+ setIsDragging(false);
1466
+ const droppedFiles = Array.from(event.dataTransfer.files);
1467
+ addFiles(droppedFiles, false);
1468
+ },
1469
+ [addFiles]
1470
+ );
1471
+ const onDropzoneClick = React.useCallback(() => {
1472
+ fileInputRef.current?.click();
1473
+ }, []);
1474
+ const onDropzoneKeyDown = React.useCallback(
1475
+ (event) => {
1476
+ if (event.key === "Enter" || event.key === " ") {
1477
+ event.preventDefault();
1478
+ onDropzoneClick();
1479
+ }
1480
+ },
1481
+ [onDropzoneClick]
1482
+ );
1483
+ const onFileInputChange = React.useCallback(
1484
+ (event) => {
1485
+ const selectedFiles = Array.from(event.target.files ?? []);
1486
+ addFiles(selectedFiles, false);
1487
+ event.target.value = "";
1488
+ },
1489
+ [addFiles]
1490
+ );
1491
+ const onOpenChange = React.useCallback(
1492
+ (open) => {
1493
+ if (open && !readOnly) {
1494
+ setError(null);
1495
+ tableMeta?.onCellEditingStart?.(rowIndex, columnId);
1496
+ } else {
1497
+ setError(null);
1498
+ tableMeta?.onCellEditingStop?.();
1499
+ }
1500
+ },
1501
+ [tableMeta, rowIndex, columnId, readOnly]
1502
+ );
1503
+ const onEscapeKeyDown = React.useCallback((event) => {
1504
+ event.stopPropagation();
1505
+ }, []);
1506
+ const onOpenAutoFocus = React.useCallback((event) => {
1507
+ event.preventDefault();
1508
+ queueMicrotask(() => {
1509
+ dropzoneRef.current?.focus();
1510
+ });
1511
+ }, []);
1512
+ const onWrapperKeyDown = React.useCallback(
1513
+ (event) => {
1514
+ if (isEditing) {
1515
+ if (event.key === "Escape") {
1516
+ event.preventDefault();
1517
+ setFiles(cellValue);
1518
+ setError(null);
1519
+ tableMeta?.onCellEditingStop?.();
1520
+ } else if (event.key === " ") {
1521
+ event.preventDefault();
1522
+ onDropzoneClick();
1523
+ } else if (event.key === "Tab") {
1524
+ event.preventDefault();
1525
+ tableMeta?.onCellEditingStop?.({
1526
+ direction: event.shiftKey ? "left" : "right"
1527
+ });
1528
+ }
1529
+ } else if (isFocused && event.key === "Enter") {
1530
+ event.preventDefault();
1531
+ tableMeta?.onCellEditingStart?.(rowIndex, columnId);
1532
+ } else if (isFocused && event.key === "Tab") {
1533
+ event.preventDefault();
1534
+ tableMeta?.onCellEditingStop?.({
1535
+ direction: event.shiftKey ? "left" : "right"
1536
+ });
1537
+ }
1538
+ },
1539
+ [isEditing, isFocused, cellValue, tableMeta, onDropzoneClick, rowIndex, columnId]
1540
+ );
1541
+ React.useEffect(() => {
1542
+ return () => {
1543
+ for (const file of files) {
1544
+ if (file.url) {
1545
+ URL.revokeObjectURL(file.url);
1546
+ }
1547
+ }
1548
+ };
1549
+ }, [files]);
1550
+ const lineCount = getLineCount(rowHeight);
1551
+ const { visibleItems: visibleFiles, hiddenCount: hiddenFileCount } = useBadgeOverflow({
1552
+ items: files,
1553
+ getLabel: (file) => file.name,
1554
+ containerRef,
1555
+ lineCount,
1556
+ cacheKeyPrefix: "file",
1557
+ iconSize: 12,
1558
+ maxWidth: 100
1559
+ });
1560
+ return /* @__PURE__ */ jsxs(
1561
+ DataGridCellWrapper,
1562
+ {
1563
+ ref: containerRef,
1564
+ cell,
1565
+ tableMeta,
1566
+ rowIndex,
1567
+ columnId,
1568
+ rowHeight,
1569
+ isEditing,
1570
+ isFocused,
1571
+ isSelected,
1572
+ isSearchMatch,
1573
+ isActiveSearchMatch,
1574
+ readOnly,
1575
+ className: cn({
1576
+ "ring-1 ring-primary/80 ring-inset": isDraggingOver
1577
+ }),
1578
+ onDragEnter: onCellDragEnter,
1579
+ onDragLeave: onCellDragLeave,
1580
+ onDragOver: onCellDragOver,
1581
+ onDrop: onCellDrop,
1582
+ onKeyDown: onWrapperKeyDown,
1583
+ children: [
1584
+ isEditing ? /* @__PURE__ */ jsxs(Popover, { open: isEditing, onOpenChange, children: [
1585
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0" }) }),
1586
+ /* @__PURE__ */ jsx(
1587
+ PopoverContent,
1588
+ {
1589
+ "data-grid-cell-editor": "",
1590
+ align: "start",
1591
+ sideOffset,
1592
+ className: "w-[400px] rounded-none p-0",
1593
+ onEscapeKeyDown,
1594
+ onOpenAutoFocus,
1595
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-3", children: [
1596
+ /* @__PURE__ */ jsx("span", { id: labelId, className: "sr-only", children: msg.file.uploadLabel }),
1597
+ /* @__PURE__ */ jsxs(
1598
+ "div",
1599
+ {
1600
+ role: "region",
1601
+ "aria-labelledby": labelId,
1602
+ "aria-describedby": descriptionId,
1603
+ "aria-invalid": !!error,
1604
+ "aria-disabled": isPending,
1605
+ "data-dragging": isDragging ? "" : void 0,
1606
+ "data-invalid": error ? "" : void 0,
1607
+ "data-disabled": isPending ? "" : void 0,
1608
+ tabIndex: isDragging || isPending ? -1 : 0,
1609
+ className: "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-md border-2 border-dashed p-6 outline-none transition-colors hover:bg-accent/30 focus-visible:border-primary-ink focus-visible:outline-none data-[disabled]:pointer-events-none data-[dragging]:border-primary-ink data-[invalid]:border-destructive-ink data-[dragging]:bg-accent/30 data-[disabled]:opacity-50",
1610
+ ref: dropzoneRef,
1611
+ onClick: onDropzoneClick,
1612
+ onDragEnter: onDropzoneDragEnter,
1613
+ onDragLeave: onDropzoneDragLeave,
1614
+ onDragOver: onDropzoneDragOver,
1615
+ onDrop: onDropzoneDrop,
1616
+ onKeyDown: onDropzoneKeyDown,
1617
+ children: [
1618
+ /* @__PURE__ */ jsx(Upload, { "aria-hidden": "true", className: "size-8 text-muted-foreground" }),
1619
+ /* @__PURE__ */ jsxs("div", { className: "text-center text-sm", children: [
1620
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: isDragging ? msg.file.dropHere : msg.file.dragHere }),
1621
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: msg.file.browse })
1622
+ ] }),
1623
+ /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-muted-foreground text-xs", children: msg.file.limits(maxFileSize ? formatFileSize(maxFileSize) : void 0, maxFiles) })
1624
+ ]
1625
+ }
1626
+ ),
1627
+ /* @__PURE__ */ jsx(
1628
+ "input",
1629
+ {
1630
+ type: "file",
1631
+ "aria-labelledby": labelId,
1632
+ "aria-describedby": descriptionId,
1633
+ multiple,
1634
+ accept,
1635
+ className: "sr-only",
1636
+ ref: fileInputRef,
1637
+ onChange: onFileInputChange
1638
+ }
1639
+ ),
1640
+ files.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
1641
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1642
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-muted-foreground text-xs", children: msg.file.count(files.length) }),
1643
+ /* @__PURE__ */ jsx(
1644
+ Button,
1645
+ {
1646
+ type: "button",
1647
+ variant: "ghost",
1648
+ className: "h-6 px-2 text-muted-foreground text-xs",
1649
+ onClick: clearAll,
1650
+ disabled: isPending,
1651
+ children: msg.file.clearAll
1652
+ }
1653
+ )
1654
+ ] }),
1655
+ /* @__PURE__ */ jsx("div", { className: "max-h-[200px] space-y-1 overflow-y-auto", children: files.map((file) => {
1656
+ const FileIcon = getFileIcon(file.type);
1657
+ const isFileUploading = uploadingFiles.has(file.id);
1658
+ const isFileDeleting = deletingFiles.has(file.id);
1659
+ const isFilePending = isFileUploading || isFileDeleting;
1660
+ return /* @__PURE__ */ jsxs(
1661
+ "div",
1662
+ {
1663
+ "data-pending": isFilePending ? "" : void 0,
1664
+ className: "flex items-center gap-2 rounded-md border bg-muted/50 px-2 py-1.5 data-[pending]:opacity-60",
1665
+ children: [
1666
+ FileIcon && /* @__PURE__ */ jsx(FileIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
1667
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden", children: [
1668
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm", children: file.name }),
1669
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: isFileUploading ? msg.file.uploading : isFileDeleting ? msg.file.deleting : formatFileSize(file.size) })
1670
+ ] }),
1671
+ /* @__PURE__ */ jsx(
1672
+ Button,
1673
+ {
1674
+ type: "button",
1675
+ "aria-label": `${msg.file.remove}: ${file.name}`,
1676
+ variant: "ghost",
1677
+ size: "icon",
1678
+ className: "size-5 rounded-sm",
1679
+ onClick: () => removeFile(file.id),
1680
+ disabled: isPending,
1681
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-3" })
1682
+ }
1683
+ )
1684
+ ]
1685
+ },
1686
+ file.id
1687
+ );
1688
+ }) })
1689
+ ] })
1690
+ ] })
1691
+ }
1692
+ )
1693
+ ] }) : null,
1694
+ isDraggingOver ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 text-primary-ink text-sm", children: [
1695
+ /* @__PURE__ */ jsx(Upload, { "aria-hidden": "true", className: "size-4" }),
1696
+ /* @__PURE__ */ jsx("span", { children: msg.file.dropHere })
1697
+ ] }) : files.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1 overflow-hidden", children: [
1698
+ visibleFiles.map((file) => {
1699
+ const isUploading2 = uploadingFiles.has(file.id);
1700
+ if (isUploading2) {
1701
+ return /* @__PURE__ */ jsx(
1702
+ Skeleton,
1703
+ {
1704
+ className: "h-5 shrink-0 px-1.5",
1705
+ style: {
1706
+ width: `${Math.min(file.name.length * 8 + 30, 100)}px`
1707
+ }
1708
+ },
1709
+ file.id
1710
+ );
1711
+ }
1712
+ const FileIcon = getFileIcon(file.type);
1713
+ return /* @__PURE__ */ jsxs(Badge, { variant: "secondary", className: "gap-1 px-1.5 py-px", children: [
1714
+ FileIcon && /* @__PURE__ */ jsx(FileIcon, { className: "size-3 shrink-0" }),
1715
+ /* @__PURE__ */ jsx("span", { className: "max-w-[100px] truncate", children: file.name })
1716
+ ] }, file.id);
1717
+ }),
1718
+ hiddenFileCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: "px-1.5 py-px text-muted-foreground", children: [
1719
+ "+",
1720
+ hiddenFileCount
1721
+ ] })
1722
+ ] }) : null
1723
+ ]
1724
+ }
1725
+ );
1726
+ }
1727
+ export {
1728
+ CheckboxCell,
1729
+ DateCell,
1730
+ FileCell,
1731
+ LongTextCell,
1732
+ MultiSelectCell,
1733
+ NumberCell,
1734
+ SelectCell,
1735
+ ShortTextCell,
1736
+ UrlCell
1737
+ };
1738
+ //# sourceMappingURL=data-grid-cell-variants.js.map