@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,725 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useDirection } from "@radix-ui/react-direction";
4
+ import { vi } from "date-fns/locale";
5
+ import { CalendarIcon, Check, ChevronsUpDown, GripVertical, ListFilter, Trash2 } from "lucide-react";
6
+ import * as React from "react";
7
+ import { badgeVariants } from "../components/ui/badge.js";
8
+ import { Button } from "../components/ui/button.js";
9
+ import { Calendar } from "../components/ui/calendar.js";
10
+ import {
11
+ Command,
12
+ CommandEmpty,
13
+ CommandGroup,
14
+ CommandInput,
15
+ CommandItem,
16
+ CommandList
17
+ } from "../components/ui/command.js";
18
+ import { Input } from "../components/ui/input.js";
19
+ import { Popover, PopoverContent, PopoverTrigger } from "../components/ui/popover.js";
20
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select.js";
21
+ import { cn } from "../lib/cn.js";
22
+ import { DataGridLocaleProvider, useDataGridMessages, useResolvedDataGridMessages } from "./data-grid-locale.js";
23
+ import { getDefaultOperator, getOperatorsForVariant } from "./filters.js";
24
+ import { Sortable, SortableContent, SortableItem, SortableItemHandle, SortableOverlay } from "./sortable.js";
25
+ import { useDebouncedCallback } from "./use-debounced-callback.js";
26
+ import { formatDate } from "./utils.js";
27
+ const FILTER_SHORTCUT_KEY = "f";
28
+ const REMOVE_FILTER_SHORTCUTS = /* @__PURE__ */ new Set(["backspace", "delete"]);
29
+ const FILTER_DEBOUNCE_MS = 300;
30
+ const OPERATORS_WITHOUT_VALUE = /* @__PURE__ */ new Set(["isEmpty", "isNotEmpty", "isTrue", "isFalse"]);
31
+ function DataGridFilterMenu({
32
+ table,
33
+ disabled,
34
+ className,
35
+ locale,
36
+ ...props
37
+ }) {
38
+ const msg = useResolvedDataGridMessages(locale);
39
+ const dir = useDirection();
40
+ const id = React.useId();
41
+ const labelId = React.useId();
42
+ const descriptionId = React.useId();
43
+ const [open, setOpen] = React.useState(false);
44
+ const addButtonRef = React.useRef(null);
45
+ const columnFilters = table.getState().columnFilters;
46
+ const { columnLabels, columns, columnVariants } = React.useMemo(() => {
47
+ const labels = /* @__PURE__ */ new Map();
48
+ const variants = /* @__PURE__ */ new Map();
49
+ const filteringIds = new Set(columnFilters.map((f) => f.id));
50
+ const availableColumns = [];
51
+ for (const column of table.getAllColumns()) {
52
+ if (!column.getCanFilter()) continue;
53
+ const label = column.columnDef.meta?.label ?? column.id;
54
+ const variant = column.columnDef.meta?.cell?.variant ?? "short-text";
55
+ labels.set(column.id, label);
56
+ variants.set(column.id, variant);
57
+ if (!filteringIds.has(column.id)) {
58
+ availableColumns.push({ id: column.id, label });
59
+ }
60
+ }
61
+ return {
62
+ columnLabels: labels,
63
+ columns: availableColumns,
64
+ columnVariants: variants
65
+ };
66
+ }, [columnFilters, table]);
67
+ const onFilterAdd = React.useCallback(() => {
68
+ const firstColumn = columns[0];
69
+ if (!firstColumn) return;
70
+ const variant = columnVariants.get(firstColumn.id) ?? "short-text";
71
+ const defaultOperator = getDefaultOperator(variant);
72
+ table.setColumnFilters((prevFilters) => [
73
+ ...prevFilters,
74
+ {
75
+ id: firstColumn.id,
76
+ value: {
77
+ operator: defaultOperator,
78
+ value: ""
79
+ }
80
+ }
81
+ ]);
82
+ }, [columns, columnVariants, table]);
83
+ const onFilterUpdate = React.useCallback(
84
+ (filterId, updates) => {
85
+ table.setColumnFilters((prevFilters) => {
86
+ if (!prevFilters) return prevFilters;
87
+ return prevFilters.map((filter) => filter.id === filterId ? { ...filter, ...updates } : filter);
88
+ });
89
+ },
90
+ [table]
91
+ );
92
+ const onFilterRemove = React.useCallback(
93
+ (filterId) => {
94
+ table.setColumnFilters((prevFilters) => prevFilters.filter((item) => item.id !== filterId));
95
+ },
96
+ [table]
97
+ );
98
+ const onFiltersReset = React.useCallback(() => {
99
+ table.setColumnFilters(table.initialState.columnFilters ?? []);
100
+ }, [table]);
101
+ React.useEffect(() => {
102
+ function onKeyDown(event) {
103
+ if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLElement && event.target.contentEditable === "true") {
104
+ return;
105
+ }
106
+ if (event.key.toLowerCase() === FILTER_SHORTCUT_KEY && (event.ctrlKey || event.metaKey) && event.shiftKey) {
107
+ event.preventDefault();
108
+ setOpen((prev) => !prev);
109
+ }
110
+ }
111
+ window.addEventListener("keydown", onKeyDown);
112
+ return () => window.removeEventListener("keydown", onKeyDown);
113
+ }, []);
114
+ const onTriggerKeyDown = React.useCallback(
115
+ (event) => {
116
+ if (REMOVE_FILTER_SHORTCUTS.has(event.key.toLowerCase()) && columnFilters.length > 0) {
117
+ event.preventDefault();
118
+ onFiltersReset();
119
+ }
120
+ },
121
+ [columnFilters.length, onFiltersReset]
122
+ );
123
+ return /* @__PURE__ */ jsx(DataGridLocaleProvider, { locale: msg, children: /* @__PURE__ */ jsxs(Sortable, { value: columnFilters, onValueChange: table.setColumnFilters, getItemValue: (item) => item.id, children: [
124
+ /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
125
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
126
+ Button,
127
+ {
128
+ dir,
129
+ variant: "outline",
130
+ size: "sm",
131
+ className: "font-normal",
132
+ onKeyDown: onTriggerKeyDown,
133
+ disabled,
134
+ children: [
135
+ /* @__PURE__ */ jsx(ListFilter, { "aria-hidden": "true", className: "size-3.5 text-muted-foreground" }),
136
+ msg.filter.trigger,
137
+ columnFilters.length > 0 && /* @__PURE__ */ jsx(
138
+ "span",
139
+ {
140
+ className: cn(
141
+ badgeVariants({ variant: "secondary" }),
142
+ "h-5 rounded px-1.5 font-mono text-[11px] font-medium"
143
+ ),
144
+ children: columnFilters.length
145
+ }
146
+ )
147
+ ]
148
+ }
149
+ ) }),
150
+ /* @__PURE__ */ jsxs(
151
+ PopoverContent,
152
+ {
153
+ "aria-labelledby": labelId,
154
+ "aria-describedby": descriptionId,
155
+ dir,
156
+ className: cn(
157
+ "flex w-full max-w-[--radix-popover-content-available-width] flex-col gap-3.5 p-4 sm:min-w-[480px]",
158
+ className
159
+ ),
160
+ ...props,
161
+ children: [
162
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
163
+ /* @__PURE__ */ jsx("h4", { id: labelId, className: "font-medium leading-none", children: columnFilters.length > 0 ? msg.filter.title : msg.filter.emptyTitle }),
164
+ /* @__PURE__ */ jsx(
165
+ "p",
166
+ {
167
+ id: descriptionId,
168
+ className: cn("text-muted-foreground text-sm", columnFilters.length > 0 && "sr-only"),
169
+ children: columnFilters.length > 0 ? msg.filter.description : msg.filter.emptyDescription
170
+ }
171
+ )
172
+ ] }),
173
+ columnFilters.length > 0 && /* @__PURE__ */ jsx(SortableContent, { asChild: true, children: /* @__PURE__ */ jsx("div", { role: "list", className: "flex max-h-[400px] flex-col gap-2 overflow-y-auto p-1", children: columnFilters.map((filter, index) => /* @__PURE__ */ jsx(
174
+ DataGridFilterItem,
175
+ {
176
+ filter,
177
+ index,
178
+ filterItemId: `${id}-filter-${filter.id}`,
179
+ dir,
180
+ columns,
181
+ columnLabels,
182
+ columnVariants,
183
+ table,
184
+ onFilterUpdate,
185
+ onFilterRemove
186
+ },
187
+ filter.id
188
+ )) }) }),
189
+ /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2", children: [
190
+ /* @__PURE__ */ jsx(
191
+ Button,
192
+ {
193
+ size: "sm",
194
+ className: "rounded",
195
+ ref: addButtonRef,
196
+ onClick: onFilterAdd,
197
+ disabled: columns.length === 0,
198
+ children: msg.filter.add
199
+ }
200
+ ),
201
+ columnFilters.length > 0 && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "rounded", onClick: onFiltersReset, children: msg.filter.reset })
202
+ ] })
203
+ ]
204
+ }
205
+ )
206
+ ] }),
207
+ /* @__PURE__ */ jsx(SortableOverlay, { children: /* @__PURE__ */ jsxs("div", { dir, className: "flex items-center gap-2", children: [
208
+ /* @__PURE__ */ jsx("div", { className: "h-8 min-w-[72px] rounded-sm bg-muted" }),
209
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-32 rounded-sm bg-muted" }),
210
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-32 rounded-sm bg-muted" }),
211
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-36 rounded-sm bg-muted" }),
212
+ /* @__PURE__ */ jsx("div", { className: "size-8 shrink-0 rounded-sm bg-muted" }),
213
+ /* @__PURE__ */ jsx("div", { className: "size-8 shrink-0 rounded-sm bg-muted" })
214
+ ] }) })
215
+ ] }) });
216
+ }
217
+ function DataGridFilterItem({
218
+ filter,
219
+ index,
220
+ filterItemId,
221
+ dir,
222
+ columns,
223
+ columnLabels,
224
+ columnVariants,
225
+ table,
226
+ onFilterUpdate,
227
+ onFilterRemove
228
+ }) {
229
+ const msg = useDataGridMessages();
230
+ const fieldListboxId = `${filterItemId}-field-listbox`;
231
+ const fieldTriggerId = `${filterItemId}-field-trigger`;
232
+ const operatorListboxId = `${filterItemId}-operator-listbox`;
233
+ const inputId = `${filterItemId}-input`;
234
+ const [showFieldSelector, setShowFieldSelector] = React.useState(false);
235
+ const [showOperatorSelector, setShowOperatorSelector] = React.useState(false);
236
+ const variant = columnVariants.get(filter.id) ?? "short-text";
237
+ const filterValue = filter.value;
238
+ const operator = filterValue?.operator ?? getDefaultOperator(variant);
239
+ const operators = getOperatorsForVariant(variant);
240
+ const needsValue = !OPERATORS_WITHOUT_VALUE.has(operator);
241
+ const column = table.getColumn(filter.id);
242
+ const onItemKeyDown = React.useCallback(
243
+ (event) => {
244
+ if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {
245
+ return;
246
+ }
247
+ if (showFieldSelector || showOperatorSelector) {
248
+ return;
249
+ }
250
+ if (REMOVE_FILTER_SHORTCUTS.has(event.key.toLowerCase())) {
251
+ event.preventDefault();
252
+ onFilterRemove(filter.id);
253
+ }
254
+ },
255
+ [filter.id, showFieldSelector, showOperatorSelector, onFilterRemove]
256
+ );
257
+ const onOperatorChange = React.useCallback(
258
+ (newOperator) => {
259
+ onFilterUpdate(filter.id, {
260
+ value: {
261
+ operator: newOperator,
262
+ value: filterValue?.value,
263
+ endValue: filterValue?.endValue
264
+ }
265
+ });
266
+ },
267
+ [filter.id, filterValue?.value, filterValue?.endValue, onFilterUpdate]
268
+ );
269
+ const onValueChange = React.useCallback(
270
+ (newValue) => {
271
+ onFilterUpdate(filter.id, {
272
+ value: {
273
+ operator,
274
+ value: newValue,
275
+ endValue: filterValue?.endValue
276
+ }
277
+ });
278
+ },
279
+ [filter.id, operator, filterValue?.endValue, onFilterUpdate]
280
+ );
281
+ const onEndValueChange = React.useCallback(
282
+ (newValue) => {
283
+ onFilterUpdate(filter.id, {
284
+ value: {
285
+ operator,
286
+ value: filterValue?.value,
287
+ endValue: newValue
288
+ }
289
+ });
290
+ },
291
+ [filter.id, operator, filterValue?.value, onFilterUpdate]
292
+ );
293
+ return /* @__PURE__ */ jsx(SortableItem, { value: filter.id, asChild: true, children: /* @__PURE__ */ jsxs(
294
+ "div",
295
+ {
296
+ role: "listitem",
297
+ id: filterItemId,
298
+ tabIndex: -1,
299
+ className: "flex items-center gap-2",
300
+ onKeyDown: onItemKeyDown,
301
+ children: [
302
+ /* @__PURE__ */ jsx("div", { className: "min-w-[72px] text-center", children: /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: index === 0 ? msg.filter.where : msg.filter.and }) }),
303
+ /* @__PURE__ */ jsxs(Popover, { open: showFieldSelector, onOpenChange: setShowFieldSelector, children: [
304
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
305
+ Button,
306
+ {
307
+ id: fieldTriggerId,
308
+ "aria-controls": fieldListboxId,
309
+ dir,
310
+ variant: "outline",
311
+ className: "h-8 w-32 justify-between rounded font-normal",
312
+ children: [
313
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: columnLabels.get(filter.id) }),
314
+ /* @__PURE__ */ jsx(ChevronsUpDown, { "aria-hidden": "true", className: "opacity-50" })
315
+ ]
316
+ }
317
+ ) }),
318
+ /* @__PURE__ */ jsx(PopoverContent, { id: fieldListboxId, dir, align: "start", className: "w-40 p-0", children: /* @__PURE__ */ jsxs(Command, { children: [
319
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: msg.filter.fieldPlaceholder, "aria-label": msg.filter.fieldPlaceholder }),
320
+ /* @__PURE__ */ jsxs(CommandList, { children: [
321
+ /* @__PURE__ */ jsx(CommandEmpty, { children: msg.filter.fieldEmpty }),
322
+ /* @__PURE__ */ jsx(CommandGroup, { children: columns.map((column2) => /* @__PURE__ */ jsxs(
323
+ CommandItem,
324
+ {
325
+ value: column2.id,
326
+ onSelect: (value) => {
327
+ const newVariant = columnVariants.get(value) ?? "short-text";
328
+ const newOperator = getDefaultOperator(newVariant);
329
+ table.setColumnFilters(
330
+ (prevFilters) => prevFilters.map(
331
+ (f) => f.id === filter.id ? {
332
+ id: value,
333
+ value: {
334
+ operator: newOperator,
335
+ value: ""
336
+ }
337
+ } : f
338
+ )
339
+ );
340
+ setShowFieldSelector(false);
341
+ },
342
+ children: [
343
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: column2.label }),
344
+ /* @__PURE__ */ jsx(Check, { className: cn("ms-auto", column2.id === filter.id ? "opacity-100" : "opacity-0") })
345
+ ]
346
+ },
347
+ column2.id
348
+ )) })
349
+ ] })
350
+ ] }) })
351
+ ] }),
352
+ /* @__PURE__ */ jsxs(
353
+ Select,
354
+ {
355
+ open: showOperatorSelector,
356
+ onOpenChange: setShowOperatorSelector,
357
+ value: operator,
358
+ onValueChange: onOperatorChange,
359
+ children: [
360
+ /* @__PURE__ */ jsx(SelectTrigger, { "aria-controls": operatorListboxId, "aria-label": msg.filter.title, className: "h-8 w-36 rounded", children: /* @__PURE__ */ jsx("div", { className: "truncate", children: /* @__PURE__ */ jsx(SelectValue, {}) }) }),
361
+ /* @__PURE__ */ jsx(SelectContent, { id: operatorListboxId, children: /* @__PURE__ */ jsx(SelectGroup, { children: operators.map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op.value, children: op.label }, op.value)) }) })
362
+ ]
363
+ }
364
+ ),
365
+ /* @__PURE__ */ jsx("div", { className: "min-w-36 max-w-60 flex-1", children: needsValue && column ? /* @__PURE__ */ jsx(
366
+ DataGridFilterInput,
367
+ {
368
+ variant,
369
+ operator,
370
+ column,
371
+ inputId,
372
+ dir,
373
+ value: filterValue?.value,
374
+ endValue: filterValue?.endValue,
375
+ onValueChange,
376
+ onEndValueChange
377
+ },
378
+ filter.id
379
+ ) : /* @__PURE__ */ jsx(
380
+ "div",
381
+ {
382
+ id: inputId,
383
+ role: "status",
384
+ "aria-label": msg.filter.emptyValue(columnLabels.get(filter.id) ?? filter.id),
385
+ "aria-live": "polite",
386
+ className: "h-8 w-full rounded border bg-transparent"
387
+ }
388
+ ) }),
389
+ /* @__PURE__ */ jsx(
390
+ Button,
391
+ {
392
+ "aria-controls": filterItemId,
393
+ "aria-label": msg.filter.remove,
394
+ variant: "outline",
395
+ size: "icon",
396
+ className: "size-8 rounded",
397
+ onClick: () => onFilterRemove(filter.id),
398
+ children: /* @__PURE__ */ jsx(Trash2, { "aria-hidden": "true", className: "size-3.5" })
399
+ }
400
+ ),
401
+ /* @__PURE__ */ jsx(SortableItemHandle, { asChild: true, children: /* @__PURE__ */ jsx(Button, { "aria-label": msg.filter.drag, variant: "outline", size: "icon", className: "size-8 rounded", children: /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": "true", className: "size-3.5" }) }) })
402
+ ]
403
+ }
404
+ ) });
405
+ }
406
+ function DataGridFilterInput({
407
+ variant,
408
+ operator,
409
+ dir,
410
+ placeholder,
411
+ value,
412
+ endValue,
413
+ column,
414
+ inputId,
415
+ onValueChange,
416
+ onEndValueChange
417
+ }) {
418
+ const msg = useDataGridMessages();
419
+ const inputPlaceholder = placeholder ?? msg.filter.valuePlaceholder;
420
+ const [showValueSelector, setShowValueSelector] = React.useState(false);
421
+ const [localValue, setLocalValue] = React.useState(value);
422
+ const [localEndValue, setLocalEndValue] = React.useState(endValue);
423
+ const debouncedOnChange = useDebouncedCallback((newValue) => {
424
+ onValueChange(newValue);
425
+ }, FILTER_DEBOUNCE_MS);
426
+ const debouncedOnEndValueChange = useDebouncedCallback((newValue) => {
427
+ onEndValueChange?.(newValue);
428
+ }, FILTER_DEBOUNCE_MS);
429
+ const cellVariant = column.columnDef.meta?.cell;
430
+ const selectOptions = React.useMemo(() => {
431
+ return cellVariant?.variant === "select" || cellVariant?.variant === "multi-select" ? cellVariant.options : [];
432
+ }, [cellVariant]);
433
+ const isBetween = operator === "isBetween";
434
+ if (variant === "number") {
435
+ if (isBetween) {
436
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
437
+ /* @__PURE__ */ jsx(
438
+ Input,
439
+ {
440
+ id: inputId,
441
+ type: "number",
442
+ inputMode: "numeric",
443
+ placeholder: msg.filter.rangeStart,
444
+ "aria-label": msg.filter.rangeStart,
445
+ value: localValue ?? "",
446
+ onChange: (event) => {
447
+ const val = event.target.value;
448
+ const newValue = val === "" ? void 0 : Number(val);
449
+ setLocalValue(newValue);
450
+ debouncedOnChange(newValue);
451
+ },
452
+ className: "h-8 w-full flex-1 rounded"
453
+ }
454
+ ),
455
+ /* @__PURE__ */ jsx(
456
+ Input,
457
+ {
458
+ id: `${inputId}-end`,
459
+ type: "number",
460
+ inputMode: "numeric",
461
+ placeholder: msg.filter.rangeEnd,
462
+ "aria-label": msg.filter.rangeEnd,
463
+ value: localEndValue ?? "",
464
+ onChange: (event) => {
465
+ const val = event.target.value;
466
+ const newValue = val === "" ? void 0 : Number(val);
467
+ setLocalEndValue(newValue);
468
+ debouncedOnEndValueChange(newValue);
469
+ },
470
+ className: "h-8 w-full flex-1 rounded"
471
+ }
472
+ )
473
+ ] });
474
+ }
475
+ return /* @__PURE__ */ jsx(
476
+ Input,
477
+ {
478
+ id: inputId,
479
+ type: "number",
480
+ inputMode: "numeric",
481
+ placeholder: inputPlaceholder,
482
+ "aria-label": inputPlaceholder,
483
+ value: localValue ?? "",
484
+ onChange: (event) => {
485
+ const val = event.target.value;
486
+ const newValue = val === "" ? void 0 : Number(val);
487
+ setLocalValue(newValue);
488
+ debouncedOnChange(newValue);
489
+ },
490
+ className: "h-8 w-full rounded"
491
+ }
492
+ );
493
+ }
494
+ if (variant === "date") {
495
+ const inputListboxId = `${inputId}-listbox`;
496
+ if (isBetween) {
497
+ const startDate = localValue && typeof localValue === "string" ? new Date(localValue) : void 0;
498
+ const endDate = localEndValue && typeof localEndValue === "string" ? new Date(localEndValue) : void 0;
499
+ const isSameDate = startDate && endDate && startDate.toDateString() === endDate.toDateString();
500
+ const displayValue = startDate && endDate && !isSameDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : startDate ? formatDate(startDate) : msg.filter.pickRange;
501
+ return /* @__PURE__ */ jsxs(Popover, { open: showValueSelector, onOpenChange: setShowValueSelector, children: [
502
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
503
+ Button,
504
+ {
505
+ id: inputId,
506
+ "aria-controls": inputListboxId,
507
+ dir,
508
+ variant: "outline",
509
+ className: cn(
510
+ "h-8 w-full justify-start gap-2 rounded font-normal",
511
+ !startDate && "text-muted-foreground"
512
+ ),
513
+ children: [
514
+ /* @__PURE__ */ jsx(CalendarIcon, { "aria-hidden": "true", className: "size-3.5" }),
515
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: displayValue })
516
+ ]
517
+ }
518
+ ) }),
519
+ /* @__PURE__ */ jsx(PopoverContent, { id: inputListboxId, dir, align: "start", className: "w-auto p-0", children: /* @__PURE__ */ jsx(
520
+ Calendar,
521
+ {
522
+ mode: "range",
523
+ locale: vi,
524
+ autoFocus: true,
525
+ selected: startDate ? { from: startDate, to: endDate } : void 0,
526
+ onSelect: (range) => {
527
+ const fromValue = range?.from ? range.from.toISOString() : void 0;
528
+ const toValue = range?.to ? range.to.toISOString() : void 0;
529
+ setLocalValue(fromValue);
530
+ setLocalEndValue(toValue);
531
+ onValueChange(fromValue);
532
+ onEndValueChange?.(toValue);
533
+ }
534
+ }
535
+ ) })
536
+ ] });
537
+ }
538
+ const dateValue = localValue && typeof localValue === "string" ? new Date(localValue) : void 0;
539
+ return /* @__PURE__ */ jsxs(Popover, { open: showValueSelector, onOpenChange: setShowValueSelector, children: [
540
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
541
+ Button,
542
+ {
543
+ id: inputId,
544
+ "aria-controls": inputListboxId,
545
+ dir,
546
+ variant: "outline",
547
+ className: cn("h-8 w-full justify-start gap-2 rounded font-normal", !dateValue && "text-muted-foreground"),
548
+ children: [
549
+ /* @__PURE__ */ jsx(CalendarIcon, { "aria-hidden": "true", className: "size-3.5" }),
550
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: dateValue ? formatDate(dateValue) : msg.filter.pickDate })
551
+ ]
552
+ }
553
+ ) }),
554
+ /* @__PURE__ */ jsx(PopoverContent, { id: inputListboxId, dir, align: "start", className: "w-auto p-0", children: /* @__PURE__ */ jsx(
555
+ Calendar,
556
+ {
557
+ mode: "single",
558
+ locale: vi,
559
+ autoFocus: true,
560
+ selected: dateValue,
561
+ onSelect: (date) => {
562
+ if (!date) return;
563
+ const newValue = date.toISOString();
564
+ setLocalValue(newValue);
565
+ onValueChange(newValue);
566
+ setShowValueSelector(false);
567
+ }
568
+ }
569
+ ) })
570
+ ] });
571
+ }
572
+ const isSelectVariant = variant === "select" || variant === "multi-select";
573
+ const isMultiValueOperator = operator === "isAnyOf" || operator === "isNoneOf";
574
+ if (isSelectVariant && selectOptions.length > 0) {
575
+ const inputListboxId = `${inputId}-listbox`;
576
+ if (isMultiValueOperator) {
577
+ const selectedValues = Array.isArray(value) ? value : [];
578
+ const selectedOptions = selectOptions.filter((option) => selectedValues.includes(option.value));
579
+ const selectedOptionsWithIcons = selectedOptions.filter((selectedOption2) => selectedOption2.icon);
580
+ return /* @__PURE__ */ jsxs(Popover, { open: showValueSelector, onOpenChange: setShowValueSelector, children: [
581
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
582
+ Button,
583
+ {
584
+ id: inputId,
585
+ "aria-controls": inputListboxId,
586
+ dir,
587
+ variant: "outline",
588
+ className: "h-8 w-full justify-start rounded font-normal",
589
+ children: selectedOptions.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: inputPlaceholder }) : /* @__PURE__ */ jsxs(Fragment, { children: [
590
+ selectedOptionsWithIcons.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center -space-x-2 rtl:space-x-reverse", children: selectedOptionsWithIcons.map(
591
+ (selectedOption2) => selectedOption2.icon && /* @__PURE__ */ jsx("div", { className: "rounded-full border bg-background p-0.5", children: /* @__PURE__ */ jsx(selectedOption2.icon, { className: "size-3.5" }) }, selectedOption2.value)
592
+ ) }),
593
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: selectedOptions.length > 1 ? msg.filter.selectedCount(selectedOptions.length) : selectedOptions[0]?.label })
594
+ ] })
595
+ }
596
+ ) }),
597
+ /* @__PURE__ */ jsx(PopoverContent, { id: inputListboxId, dir, align: "start", className: "w-48 p-0", children: /* @__PURE__ */ jsxs(Command, { children: [
598
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: msg.filter.optionPlaceholder, "aria-label": msg.filter.optionPlaceholder }),
599
+ /* @__PURE__ */ jsxs(CommandList, { children: [
600
+ /* @__PURE__ */ jsx(CommandEmpty, { children: msg.filter.optionEmpty }),
601
+ /* @__PURE__ */ jsx(CommandGroup, { children: selectOptions.map((option) => {
602
+ const isSelected = selectedValues.includes(option.value);
603
+ return /* @__PURE__ */ jsxs(
604
+ CommandItem,
605
+ {
606
+ value: option.value,
607
+ onSelect: () => {
608
+ const newValues = isSelected ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value];
609
+ onValueChange(newValues.length > 0 ? newValues : void 0);
610
+ },
611
+ children: [
612
+ option.icon && /* @__PURE__ */ jsx(option.icon, {}),
613
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label }),
614
+ option.count && /* @__PURE__ */ jsx("span", { className: "ms-auto font-mono text-xs", children: option.count }),
615
+ /* @__PURE__ */ jsx(Check, { className: cn("ms-auto", isSelected ? "opacity-100" : "opacity-0") })
616
+ ]
617
+ },
618
+ option.value
619
+ );
620
+ }) })
621
+ ] })
622
+ ] }) })
623
+ ] });
624
+ }
625
+ const selectedOption = selectOptions.find((opt) => opt.value === value);
626
+ return /* @__PURE__ */ jsxs(Popover, { open: showValueSelector, onOpenChange: setShowValueSelector, children: [
627
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
628
+ Button,
629
+ {
630
+ id: inputId,
631
+ "aria-controls": inputListboxId,
632
+ dir,
633
+ variant: "outline",
634
+ className: "h-8 w-full justify-start rounded font-normal",
635
+ children: selectedOption ? /* @__PURE__ */ jsxs(Fragment, { children: [
636
+ selectedOption.icon && /* @__PURE__ */ jsx(selectedOption.icon, {}),
637
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: selectedOption.label })
638
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: inputPlaceholder })
639
+ }
640
+ ) }),
641
+ /* @__PURE__ */ jsx(PopoverContent, { id: inputListboxId, dir, align: "start", className: "w-[200px] p-0", children: /* @__PURE__ */ jsxs(Command, { children: [
642
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: msg.filter.optionPlaceholder, "aria-label": msg.filter.optionPlaceholder }),
643
+ /* @__PURE__ */ jsxs(CommandList, { children: [
644
+ /* @__PURE__ */ jsx(CommandEmpty, { children: msg.filter.optionEmpty }),
645
+ /* @__PURE__ */ jsx(CommandGroup, { children: selectOptions.map((option) => /* @__PURE__ */ jsxs(
646
+ CommandItem,
647
+ {
648
+ value: option.value,
649
+ onSelect: () => {
650
+ onValueChange(option.value);
651
+ setShowValueSelector(false);
652
+ },
653
+ children: [
654
+ option.icon && /* @__PURE__ */ jsx(option.icon, {}),
655
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label }),
656
+ option.count && /* @__PURE__ */ jsx("span", { className: "ms-auto font-mono text-xs", children: option.count }),
657
+ /* @__PURE__ */ jsx(Check, { className: cn("ms-auto", value === option.value ? "opacity-100" : "opacity-0") })
658
+ ]
659
+ },
660
+ option.value
661
+ )) })
662
+ ] })
663
+ ] }) })
664
+ ] });
665
+ }
666
+ if (isBetween) {
667
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
668
+ /* @__PURE__ */ jsx(
669
+ Input,
670
+ {
671
+ id: inputId,
672
+ type: "text",
673
+ placeholder: msg.filter.rangeStart,
674
+ "aria-label": msg.filter.rangeStart,
675
+ className: "h-8 w-full flex-1 rounded",
676
+ value: localValue ?? "",
677
+ onChange: (event) => {
678
+ const val = event.target.value;
679
+ const newValue = val === "" ? void 0 : val;
680
+ setLocalValue(newValue);
681
+ debouncedOnChange(newValue);
682
+ }
683
+ }
684
+ ),
685
+ /* @__PURE__ */ jsx(
686
+ Input,
687
+ {
688
+ id: `${inputId}-end`,
689
+ type: "text",
690
+ placeholder: msg.filter.rangeEnd,
691
+ "aria-label": msg.filter.rangeEnd,
692
+ className: "h-8 w-full flex-1 rounded",
693
+ value: localEndValue ?? "",
694
+ onChange: (event) => {
695
+ const val = event.target.value;
696
+ const newValue = val === "" ? void 0 : val;
697
+ setLocalEndValue(newValue);
698
+ debouncedOnEndValueChange(newValue);
699
+ }
700
+ }
701
+ )
702
+ ] });
703
+ }
704
+ return /* @__PURE__ */ jsx(
705
+ Input,
706
+ {
707
+ id: inputId,
708
+ type: "text",
709
+ placeholder: inputPlaceholder,
710
+ "aria-label": inputPlaceholder,
711
+ className: "h-8 w-full rounded",
712
+ value: localValue ?? "",
713
+ onChange: (event) => {
714
+ const val = event.target.value;
715
+ const newValue = val === "" ? void 0 : val;
716
+ setLocalValue(newValue);
717
+ debouncedOnChange(newValue);
718
+ }
719
+ }
720
+ );
721
+ }
722
+ export {
723
+ DataGridFilterMenu
724
+ };
725
+ //# sourceMappingURL=data-grid-filter-menu.js.map