@godxjp/ui 9.2.0 → 10.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 (137) hide show
  1. package/README.md +58 -29
  2. package/dist/app/index.d.ts +1 -1
  3. package/dist/app/index.js +4 -4
  4. package/dist/{app.prop-UTc4j4nj.d.ts → app.prop-Cy6dJnU8.d.ts} +18 -40
  5. package/dist/{checkbox-ChRsR7Nk.d.ts → checkbox-em-oFM5D.d.ts} +1 -1
  6. package/dist/{chunk-LJLGABFV.js → chunk-2HXZT2WJ.js} +17 -9
  7. package/dist/{chunk-QLMXEJSY.js → chunk-3Q4A4U2P.js} +24 -1
  8. package/dist/{chunk-26CPAKUP.js → chunk-44YRPSZ7.js} +1 -2
  9. package/dist/{chunk-HB2OHB5X.js → chunk-5NCFLCM7.js} +27 -16
  10. package/dist/{chunk-FXFJF4YA.js → chunk-6CSBMMZS.js} +262 -31
  11. package/dist/{chunk-E7HBHUJY.js → chunk-6HHSU6RG.js} +8 -6
  12. package/dist/{chunk-457KVJTX.js → chunk-7Q45MBFW.js} +7 -5
  13. package/dist/{chunk-O24Z3ULJ.js → chunk-BE6GJGKJ.js} +1 -1
  14. package/dist/{chunk-FVPCVZL3.js → chunk-BG5RNXTH.js} +1 -1
  15. package/dist/{chunk-AINW5WYN.js → chunk-COD66MFF.js} +1 -2
  16. package/dist/{chunk-IOGU3ZWF.js → chunk-DNGJHWJZ.js} +3 -3
  17. package/dist/{chunk-3TS3G4U3.js → chunk-EE5DKOHX.js} +3 -1
  18. package/dist/{chunk-G6Q32VHO.js → chunk-EQZP53KI.js} +33 -8
  19. package/dist/{chunk-BHV2FUOA.js → chunk-EZHHJQWQ.js} +1 -1
  20. package/dist/{chunk-N3JPLJ3B.js → chunk-GDDCSKCB.js} +12 -5
  21. package/dist/{chunk-RLGHEV4A.js → chunk-HTG5VHU7.js} +10 -1
  22. package/dist/{chunk-R2W2FX5Q.js → chunk-I7NQ2LIL.js} +1 -9
  23. package/dist/{chunk-XQMPK4GM.js → chunk-IHRMOJXD.js} +86 -39
  24. package/dist/{chunk-TILFZBTE.js → chunk-INIIF7F7.js} +1 -4
  25. package/dist/{chunk-UIYEAUWA.js → chunk-IY347EQA.js} +2 -2
  26. package/dist/{chunk-HCM4JAC2.js → chunk-JWGLJXQU.js} +39 -11
  27. package/dist/{chunk-TO33OY4L.js → chunk-LMKUKCTN.js} +1 -1
  28. package/dist/chunk-NXVCI6YB.js +453 -0
  29. package/dist/{chunk-S6TBIL7J.js → chunk-O6DQZYNI.js} +63 -44
  30. package/dist/{chunk-O2OUNXV4.js → chunk-P5KPCT6R.js} +3 -3
  31. package/dist/{chunk-6HQMUUQW.js → chunk-PDXFQS7M.js} +14 -30
  32. package/dist/{chunk-F7PG4OEV.js → chunk-QSGW3ZWK.js} +12 -4
  33. package/dist/{chunk-25RYBC5T.js → chunk-S2IJKT3D.js} +1 -1
  34. package/dist/{chunk-OJZ6C2HM.js → chunk-SARQRCKO.js} +54 -48
  35. package/dist/chunk-T2QO2S65.js +126 -0
  36. package/dist/{chunk-26WDEDWL.js → chunk-TGNBXS7H.js} +32 -23
  37. package/dist/{chunk-6J7GRCDA.js → chunk-UNVRNJCB.js} +71 -11
  38. package/dist/{chunk-6YBYAEXD.js → chunk-VSM44AYE.js} +94 -24
  39. package/dist/{chunk-QWLXN6CT.js → chunk-VSUYVT2Q.js} +3 -2
  40. package/dist/{chunk-4R7QL3MW.js → chunk-X2VY4MOW.js} +14 -29
  41. package/dist/{chunk-ETLAI3QU.js → chunk-Z46J47FY.js} +73 -77
  42. package/dist/components/admin/index.d.ts +22 -12
  43. package/dist/components/admin/index.js +23 -24
  44. package/dist/components/data-display/badge.js +3 -3
  45. package/dist/components/data-display/card.d.ts +3 -3
  46. package/dist/components/data-display/card.js +1 -1
  47. package/dist/components/data-display/carousel.js +3 -1
  48. package/dist/components/data-display/index.js +54 -32
  49. package/dist/components/data-entry/calendar.d.ts +1 -1
  50. package/dist/components/data-entry/calendar.js +1 -1
  51. package/dist/components/data-entry/cascader.d.ts +1 -1
  52. package/dist/components/data-entry/cascader.js +5 -5
  53. package/dist/components/data-entry/checkbox.d.ts +2 -2
  54. package/dist/components/data-entry/checkbox.js +2 -2
  55. package/dist/components/data-entry/color-picker.d.ts +1 -1
  56. package/dist/components/data-entry/color-picker.js +3 -3
  57. package/dist/components/data-entry/date-picker.d.ts +2 -2
  58. package/dist/components/data-entry/date-picker.js +4 -4
  59. package/dist/components/data-entry/date-range-picker.d.ts +2 -2
  60. package/dist/components/data-entry/date-range-picker.js +4 -4
  61. package/dist/components/data-entry/index.d.ts +9 -25
  62. package/dist/components/data-entry/index.js +22 -26
  63. package/dist/components/data-entry/radio.d.ts +1 -1
  64. package/dist/components/data-entry/radio.js +2 -2
  65. package/dist/components/data-entry/select.d.ts +2 -2
  66. package/dist/components/data-entry/select.js +3 -4
  67. package/dist/components/data-entry/slider.d.ts +1 -1
  68. package/dist/components/data-entry/switch.d.ts +2 -2
  69. package/dist/components/data-entry/switch.js +1 -1
  70. package/dist/components/data-entry/time-input.js +2 -2
  71. package/dist/components/data-entry/time-picker.d.ts +3 -1
  72. package/dist/components/data-entry/time-picker.js +3 -3
  73. package/dist/components/data-entry/transfer.d.ts +2 -2
  74. package/dist/components/data-entry/transfer.js +5 -5
  75. package/dist/components/data-entry/tree-select.d.ts +1 -1
  76. package/dist/components/data-entry/tree-select.js +5 -5
  77. package/dist/components/data-entry/upload.d.ts +2 -2
  78. package/dist/components/data-entry/upload.js +5 -5
  79. package/dist/components/feedback/alert.js +5 -5
  80. package/dist/components/feedback/dialog.js +3 -3
  81. package/dist/components/feedback/index.d.ts +1 -1
  82. package/dist/components/feedback/index.js +8 -8
  83. package/dist/components/feedback/sheet.js +1 -1
  84. package/dist/components/layout/index.d.ts +6 -9
  85. package/dist/components/layout/index.js +5 -3
  86. package/dist/components/navigation/dropdown-menu.js +1 -1
  87. package/dist/components/navigation/index.d.ts +14 -16
  88. package/dist/components/navigation/index.js +7 -8
  89. package/dist/components/navigation/pagination.d.ts +11 -8
  90. package/dist/components/navigation/pagination.js +4 -5
  91. package/dist/components/navigation/steps.d.ts +3 -3
  92. package/dist/components/navigation/steps.js +3 -1
  93. package/dist/components/query/index.d.ts +1 -5
  94. package/dist/components/query/index.js +6 -8
  95. package/dist/components/ui/index.d.ts +5 -7
  96. package/dist/components/ui/index.js +26 -29
  97. package/dist/{data-entry.prop-CDkOajPj.d.ts → data-entry.prop-BR4vNA1j.d.ts} +7 -35
  98. package/dist/filter-bar-BxjSJJnQ.d.ts +7 -0
  99. package/dist/{inline-CDSVAN54.d.ts → flex-D_EXRFSW.d.ts} +2 -8
  100. package/dist/form/index.js +1 -1
  101. package/dist/i18n/index.d.ts +82 -10
  102. package/dist/i18n/index.js +2 -2
  103. package/dist/index.d.ts +6 -6
  104. package/dist/index.js +34 -37
  105. package/dist/lib/datetime/index.js +1 -1
  106. package/dist/{navigation.prop-8DgElO0c.d.ts → navigation.prop-DMcXkR-J.d.ts} +9 -11
  107. package/dist/{password-strength-kQkloEeo.d.ts → password-strength-DVRvXEOK.d.ts} +2 -2
  108. package/dist/props/components/index.d.ts +3 -3
  109. package/dist/props/index.d.ts +3 -3
  110. package/dist/props/index.js +1 -1
  111. package/dist/props/registry.d.ts +84 -39
  112. package/dist/props/registry.js +1 -1
  113. package/dist/{search-input-cezAxpgb.d.ts → search-input-DpqDMXcn.d.ts} +2 -4
  114. package/dist/{skeleton-uWAjSacg.d.ts → skeleton-cj9kh5wo.d.ts} +1 -3
  115. package/dist/styles/control.css +176 -41
  116. package/dist/styles/data-display-layout.css +41 -15
  117. package/dist/styles/feedback-layout.css +44 -12
  118. package/dist/styles/index.css +45 -1
  119. package/dist/styles/layout.css +18 -17
  120. package/dist/styles/navigation-layout.css +3 -1
  121. package/dist/styles/shell-layout.css +3 -3
  122. package/dist/styles/table-layout.css +13 -0
  123. package/dist/tokens/foundation.css +12 -1
  124. package/dist/tokens/semantic/layout.css +2 -2
  125. package/package.json +6 -7
  126. package/scripts/ui-audit.mjs +31 -2
  127. package/dist/chunk-6MCI7W5G.js +0 -201
  128. package/dist/chunk-CAEL2ZD2.js +0 -222
  129. package/dist/chunk-GKXPALFT.js +0 -32
  130. package/dist/chunk-JKHWLPM5.js +0 -101
  131. package/dist/chunk-KDBGFJJI.js +0 -220
  132. package/dist/components/data-entry/autocomplete.d.ts +0 -24
  133. package/dist/components/data-entry/autocomplete.js +0 -10
  134. package/dist/components/data-entry/combobox.d.ts +0 -22
  135. package/dist/components/data-entry/combobox.js +0 -6
  136. package/dist/filter-bar-B5TPUqEO.d.ts +0 -14
  137. /package/dist/{chunk-LDSLS6HE.js → chunk-7CFO5FFE.js} +0 -0
@@ -0,0 +1,453 @@
1
+ import { Command, CommandList, CommandItem, CommandGroup } from './chunk-HTEL5DQI.js';
2
+ import { Input } from './chunk-VOHTRR5X.js';
3
+ import { Button } from './chunk-M4PZNAMV.js';
4
+ import { Popover, PopoverTrigger, PopoverContent } from './chunk-DY5C44UP.js';
5
+ import { controlTriggerClass } from './chunk-IBK5D2Q6.js';
6
+ import { useTranslation } from './chunk-HTG5VHU7.js';
7
+ import { cn } from './chunk-U7N2A7A3.js';
8
+ import * as React from 'react';
9
+ import * as SelectPrimitive from '@radix-ui/react-select';
10
+ import { ChevronDown, ChevronUp, ChevronsUpDown, Check, Loader2 } from 'lucide-react';
11
+ import { jsxs, jsx } from 'react/jsx-runtime';
12
+
13
+ var DEBOUNCE_MS = 250;
14
+ function SearchSelect({
15
+ value = "",
16
+ onValueChange,
17
+ options: staticOptions,
18
+ loadOptions,
19
+ renderOption,
20
+ selectedLabel,
21
+ placeholder,
22
+ searchPlaceholder,
23
+ emptyMessage,
24
+ loadingMessage,
25
+ clearLabel,
26
+ clearable = true,
27
+ disabled = false,
28
+ name,
29
+ id,
30
+ className,
31
+ "data-testid": dataTestId
32
+ }) {
33
+ const { t } = useTranslation();
34
+ const reactId = React.useId();
35
+ const listId = `${reactId}-listbox`;
36
+ const optionDomId = (optionValue) => `${reactId}-opt-${optionValue}`;
37
+ const [open, setOpen] = React.useState(false);
38
+ const [query, setQuery] = React.useState("");
39
+ const [debouncedQuery, setDebouncedQuery] = React.useState("");
40
+ const [loaded, setLoaded] = React.useState([]);
41
+ const [page, setPage] = React.useState(1);
42
+ const [hasMore, setHasMore] = React.useState(false);
43
+ const [loading, setLoading] = React.useState(false);
44
+ const [activeIndex, setActiveIndex] = React.useState(0);
45
+ const [picked, setPicked] = React.useState(null);
46
+ const reqId = React.useRef(0);
47
+ const resolvedLoad = React.useMemo(
48
+ () => loadOptions ?? (async ({ query: search }) => {
49
+ const needle = search.trim().toLowerCase();
50
+ const list = staticOptions ?? [];
51
+ return {
52
+ options: needle ? list.filter(
53
+ (option) => option.label.toLowerCase().includes(needle) || option.value.toLowerCase().includes(needle)
54
+ ) : list,
55
+ hasMore: false
56
+ };
57
+ }),
58
+ [loadOptions, staticOptions]
59
+ );
60
+ React.useEffect(() => {
61
+ const handle = window.setTimeout(() => setDebouncedQuery(query.trim()), DEBOUNCE_MS);
62
+ return () => window.clearTimeout(handle);
63
+ }, [query]);
64
+ const fetchPage = React.useCallback(
65
+ async (nextPage, search, append) => {
66
+ const ticket = ++reqId.current;
67
+ setLoading(true);
68
+ try {
69
+ const result = await resolvedLoad({ query: search, page: nextPage });
70
+ if (ticket !== reqId.current) return;
71
+ setLoaded((prev) => append ? [...prev, ...result.options] : result.options);
72
+ setHasMore(Boolean(result.hasMore));
73
+ setPage(nextPage);
74
+ } finally {
75
+ if (ticket === reqId.current) setLoading(false);
76
+ }
77
+ },
78
+ [resolvedLoad]
79
+ );
80
+ React.useEffect(() => {
81
+ if (!open) return;
82
+ setActiveIndex(0);
83
+ void fetchPage(1, debouncedQuery, false);
84
+ }, [open, debouncedQuery, fetchPage]);
85
+ const grouped = React.useMemo(() => {
86
+ const order = [];
87
+ const buckets = /* @__PURE__ */ new Map();
88
+ for (const option of loaded) {
89
+ const key = option.group ?? "";
90
+ if (!buckets.has(key)) {
91
+ buckets.set(key, []);
92
+ order.push(key);
93
+ }
94
+ buckets.get(key).push(option);
95
+ }
96
+ let flatIndex = 0;
97
+ return order.map((key) => ({
98
+ heading: key || void 0,
99
+ items: (buckets.get(key) ?? []).map((option) => ({ option, index: flatIndex++ }))
100
+ }));
101
+ }, [loaded]);
102
+ const flatOrdered = React.useMemo(
103
+ () => grouped.flatMap((group) => group.items.map((entry) => entry.option)),
104
+ [grouped]
105
+ );
106
+ const resolvedPlaceholder = placeholder ?? t("dataEntry.searchSelect.placeholder");
107
+ const currentLabel = value ? picked?.label ?? selectedLabel ?? resolvedPlaceholder : resolvedPlaceholder;
108
+ const select = (option) => {
109
+ if (option.disabled) return;
110
+ setPicked(option);
111
+ onValueChange?.(option.value, option);
112
+ setOpen(false);
113
+ };
114
+ const clear = () => {
115
+ setPicked(null);
116
+ onValueChange?.("", void 0);
117
+ setOpen(false);
118
+ };
119
+ const onScroll = (event) => {
120
+ const el = event.currentTarget;
121
+ if (el.scrollHeight - el.scrollTop - el.clientHeight < 48 && hasMore && !loading) {
122
+ void fetchPage(page + 1, debouncedQuery, true);
123
+ }
124
+ };
125
+ const onKeyDown = (event) => {
126
+ if (event.key === "ArrowDown") {
127
+ event.preventDefault();
128
+ setActiveIndex((i) => Math.min(i + 1, Math.max(flatOrdered.length - 1, 0)));
129
+ } else if (event.key === "ArrowUp") {
130
+ event.preventDefault();
131
+ setActiveIndex((i) => Math.max(i - 1, 0));
132
+ } else if (event.key === "Enter" && flatOrdered[activeIndex]) {
133
+ event.preventDefault();
134
+ select(flatOrdered[activeIndex]);
135
+ } else if (event.key === "Escape") {
136
+ event.preventDefault();
137
+ setOpen(false);
138
+ }
139
+ };
140
+ const optionTestId = (optionValue) => dataTestId ? `${dataTestId}-option-${optionValue}` : void 0;
141
+ const activeOption = flatOrdered[activeIndex];
142
+ const activeOptionId = activeOption ? optionDomId(activeOption.value) : void 0;
143
+ return /* @__PURE__ */ jsxs(
144
+ Popover,
145
+ {
146
+ open,
147
+ onOpenChange: (next) => {
148
+ setOpen(next);
149
+ if (!next) setQuery("");
150
+ },
151
+ children: [
152
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
153
+ Button,
154
+ {
155
+ id,
156
+ type: "button",
157
+ variant: "outline",
158
+ role: "combobox",
159
+ "aria-expanded": open,
160
+ disabled,
161
+ "data-testid": dataTestId,
162
+ className: cn("w-full justify-between font-normal", className),
163
+ children: [
164
+ /* @__PURE__ */ jsx("span", { className: cn("truncate text-start", !value && "text-muted-foreground"), children: currentLabel }),
165
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ms-2 size-4 shrink-0 opacity-50", "aria-hidden": "true" })
166
+ ]
167
+ }
168
+ ) }),
169
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value }) : null,
170
+ /* @__PURE__ */ jsx(
171
+ PopoverContent,
172
+ {
173
+ align: "start",
174
+ sideOffset: 4,
175
+ collisionPadding: 12,
176
+ className: "flex max-h-[min(24rem,var(--radix-popover-content-available-height))] w-max max-w-[min(32rem,calc(100vw-1.5rem))] min-w-[var(--radix-popover-trigger-width)] flex-col p-0",
177
+ children: /* @__PURE__ */ jsxs(Command, { shouldFilter: false, className: "flex min-h-0 flex-col", children: [
178
+ /* @__PURE__ */ jsx("div", { className: "border-border shrink-0 border-b p-2", children: /* @__PURE__ */ jsx(
179
+ Input,
180
+ {
181
+ autoFocus: true,
182
+ "aria-controls": listId,
183
+ "aria-autocomplete": "list",
184
+ "aria-activedescendant": activeOptionId,
185
+ value: query,
186
+ onChange: (event) => setQuery(event.target.value),
187
+ onKeyDown,
188
+ placeholder: searchPlaceholder ?? t("dataEntry.searchSelect.search")
189
+ }
190
+ ) }),
191
+ /* @__PURE__ */ jsxs(
192
+ CommandList,
193
+ {
194
+ id: listId,
195
+ role: "listbox",
196
+ className: "min-h-0 flex-1 overflow-y-auto p-1",
197
+ onScroll,
198
+ children: [
199
+ clearable && value ? /* @__PURE__ */ jsx(CommandItem, { value: "", "data-testid": optionTestId("none"), onSelect: clear, children: /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: clearLabel ?? t("dataEntry.searchSelect.clear") }) }) : null,
200
+ grouped.map((group) => {
201
+ const rows = group.items.map(({ option, index }) => /* @__PURE__ */ jsxs(
202
+ CommandItem,
203
+ {
204
+ id: optionDomId(option.value),
205
+ role: "option",
206
+ value: option.value,
207
+ "data-testid": optionTestId(option.value),
208
+ "aria-selected": activeIndex === index,
209
+ disabled: option.disabled,
210
+ className: activeIndex === index ? "bg-accent text-accent-foreground" : void 0,
211
+ onMouseEnter: () => setActiveIndex(index),
212
+ onSelect: () => select(option),
213
+ children: [
214
+ renderOption ? /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: renderOption(option) }) : /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
215
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm", children: option.label }),
216
+ option.sublabel ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-xs", children: option.sublabel }) : null
217
+ ] }),
218
+ value === option.value ? /* @__PURE__ */ jsx(Check, { className: "text-primary size-4 shrink-0", "aria-hidden": "true" }) : null
219
+ ]
220
+ },
221
+ option.value
222
+ ));
223
+ return group.heading ? /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: rows }, group.heading) : /* @__PURE__ */ jsx(React.Fragment, { children: rows }, "__ungrouped");
224
+ }),
225
+ loading ? /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground flex items-center gap-2 px-2 py-3 text-sm", children: [
226
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }),
227
+ loadingMessage ?? t("dataEntry.searchSelect.loading")
228
+ ] }) : null,
229
+ !loading && loaded.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-muted-foreground px-2 py-6 text-center text-sm", children: emptyMessage ?? t("dataEntry.searchSelect.empty") }) : null
230
+ ]
231
+ }
232
+ )
233
+ ] })
234
+ }
235
+ )
236
+ ]
237
+ }
238
+ );
239
+ }
240
+ function isDataSelect(props) {
241
+ return "options" in props || "loadOptions" in props;
242
+ }
243
+ function Select(props) {
244
+ if (isDataSelect(props)) {
245
+ return /* @__PURE__ */ jsx(DataSelect, { ...props });
246
+ }
247
+ return /* @__PURE__ */ jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
248
+ }
249
+ function SelectGroup(props) {
250
+ return /* @__PURE__ */ jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
251
+ }
252
+ function SelectValue(props) {
253
+ return /* @__PURE__ */ jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
254
+ }
255
+ var SelectTrigger = React.forwardRef(({ className, children, size = "md", ...props }, ref) => /* @__PURE__ */ jsxs(
256
+ SelectPrimitive.Trigger,
257
+ {
258
+ ref,
259
+ "data-slot": "select-trigger",
260
+ "data-size": size,
261
+ className: cn(
262
+ controlTriggerClass,
263
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground w-fit gap-2 whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
264
+ className
265
+ ),
266
+ ...props,
267
+ children: [
268
+ children,
269
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" }) })
270
+ ]
271
+ }
272
+ ));
273
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
274
+ var SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
275
+ SelectPrimitive.ScrollUpButton,
276
+ {
277
+ ref,
278
+ "data-slot": "select-scroll-up-button",
279
+ className: cn("flex cursor-default items-center justify-center py-1", className),
280
+ ...props,
281
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "size-4", "aria-hidden": "true" })
282
+ }
283
+ ));
284
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
285
+ var SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
286
+ SelectPrimitive.ScrollDownButton,
287
+ {
288
+ ref,
289
+ "data-slot": "select-scroll-down-button",
290
+ className: cn("flex cursor-default items-center justify-center py-1", className),
291
+ ...props,
292
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4", "aria-hidden": "true" })
293
+ }
294
+ ));
295
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
296
+ var SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
297
+ SelectPrimitive.Content,
298
+ {
299
+ ref,
300
+ "data-slot": "select-content",
301
+ className: cn(
302
+ "bg-popover text-popover-foreground data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border shadow-md",
303
+ position === "popper" && "translate-y-1",
304
+ className
305
+ ),
306
+ position,
307
+ ...props,
308
+ children: [
309
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
310
+ /* @__PURE__ */ jsx(
311
+ SelectPrimitive.Viewport,
312
+ {
313
+ "data-slot": "select-viewport",
314
+ className: cn(
315
+ "p-1",
316
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
317
+ ),
318
+ children
319
+ }
320
+ ),
321
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
322
+ ]
323
+ }
324
+ ) }));
325
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
326
+ var SelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
327
+ SelectPrimitive.Label,
328
+ {
329
+ ref,
330
+ "data-slot": "select-label",
331
+ className: cn("px-2 py-1.5 text-sm font-medium", className),
332
+ ...props
333
+ }
334
+ ));
335
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
336
+ var SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
337
+ SelectPrimitive.Item,
338
+ {
339
+ ref,
340
+ "data-slot": "select-item",
341
+ className: cn(
342
+ "focus:bg-accent focus:text-accent-foreground data-[state=checked]:bg-accent data-[state=checked]:text-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm px-2.5 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[state=checked]:font-medium [&_svg]:pointer-events-none [&_svg]:shrink-0",
343
+ className
344
+ ),
345
+ ...props,
346
+ children: /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
347
+ }
348
+ ));
349
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
350
+ var SelectSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
351
+ SelectPrimitive.Separator,
352
+ {
353
+ ref,
354
+ "data-slot": "select-separator",
355
+ className: cn("bg-border -mx-1 my-1 h-px", className),
356
+ ...props
357
+ }
358
+ ));
359
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
360
+ function groupDataOptions(options) {
361
+ const order = [];
362
+ const buckets = /* @__PURE__ */ new Map();
363
+ for (const option of options) {
364
+ const key = option.group ?? "";
365
+ if (!buckets.has(key)) {
366
+ buckets.set(key, []);
367
+ order.push(key);
368
+ }
369
+ buckets.get(key).push(option);
370
+ }
371
+ return order.map((key) => ({ heading: key || void 0, items: buckets.get(key) ?? [] }));
372
+ }
373
+ function DataSelect({
374
+ options = [],
375
+ loadOptions,
376
+ showSearch,
377
+ value = "",
378
+ onValueChange,
379
+ renderOption,
380
+ selectedLabel,
381
+ placeholder,
382
+ searchPlaceholder,
383
+ emptyMessage,
384
+ loadingMessage,
385
+ clearLabel,
386
+ clearable,
387
+ disabled,
388
+ name,
389
+ id,
390
+ className,
391
+ "data-testid": dataTestId
392
+ }) {
393
+ const searchable = showSearch ?? Boolean(loadOptions);
394
+ if (searchable) {
395
+ return /* @__PURE__ */ jsx(
396
+ SearchSelect,
397
+ {
398
+ value,
399
+ onValueChange,
400
+ options,
401
+ loadOptions,
402
+ renderOption,
403
+ selectedLabel,
404
+ placeholder,
405
+ searchPlaceholder,
406
+ emptyMessage,
407
+ loadingMessage,
408
+ clearLabel,
409
+ clearable,
410
+ disabled,
411
+ name,
412
+ id,
413
+ className,
414
+ "data-testid": dataTestId
415
+ }
416
+ );
417
+ }
418
+ const optionTestId = (optionValue) => dataTestId ? `${dataTestId}-option-${optionValue}` : void 0;
419
+ const renderItem = (option) => /* @__PURE__ */ jsx(
420
+ SelectItem,
421
+ {
422
+ value: option.value,
423
+ disabled: option.disabled,
424
+ "data-testid": optionTestId(option.value),
425
+ children: renderOption ? renderOption(option) : option.label
426
+ },
427
+ option.value
428
+ );
429
+ return /* @__PURE__ */ jsxs(
430
+ SelectPrimitive.Root,
431
+ {
432
+ "data-slot": "select",
433
+ value: value || void 0,
434
+ onValueChange: (next) => onValueChange?.(
435
+ next,
436
+ options.find((option) => option.value === next)
437
+ ),
438
+ disabled,
439
+ name,
440
+ children: [
441
+ /* @__PURE__ */ jsx(SelectTrigger, { id, "data-testid": dataTestId, className, children: /* @__PURE__ */ jsx(SelectValue, { placeholder }) }),
442
+ /* @__PURE__ */ jsx(SelectContent, { children: groupDataOptions(options).map(
443
+ (group) => group.heading ? /* @__PURE__ */ jsxs(SelectGroup, { children: [
444
+ /* @__PURE__ */ jsx(SelectLabel, { children: group.heading }),
445
+ group.items.map(renderItem)
446
+ ] }, group.heading) : /* @__PURE__ */ jsx(React.Fragment, { children: group.items.map(renderItem) }, "__ungrouped")
447
+ ) })
448
+ ]
449
+ }
450
+ );
451
+ }
452
+
453
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue };
@@ -1,15 +1,16 @@
1
+ import { Checkbox } from './chunk-BE6GJGKJ.js';
1
2
  import { Button } from './chunk-M4PZNAMV.js';
2
- import { Inline, densityClass } from './chunk-TILFZBTE.js';
3
+ import { Flex, densityClass } from './chunk-INIIF7F7.js';
3
4
  import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from './chunk-4MMIMZMK.js';
4
5
  import { tableCellPaddingClass, tableRowHeightClass, controlIconSmClass } from './chunk-IBK5D2Q6.js';
5
- import { useTranslation } from './chunk-RLGHEV4A.js';
6
+ import { useTranslation } from './chunk-HTG5VHU7.js';
6
7
  import { cn } from './chunk-U7N2A7A3.js';
7
8
  import { jsx, jsxs } from 'react/jsx-runtime';
8
9
  import * as React from 'react';
9
10
  import { Layers, Layers2, ArrowUp, ArrowDown, ChevronsUpDown, MoreHorizontal } from 'lucide-react';
10
11
 
11
12
  function EmptyState({ icon: Icon, title, description, action, className }) {
12
- return /* @__PURE__ */ jsxs("div", { "data-slot": "empty-state", className: cn("ui-empty-state", className), children: [
13
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "empty-state", role: "status", className: cn("ui-empty-state", className), children: [
13
14
  Icon && /* @__PURE__ */ jsx("div", { className: "ui-empty-state-icon", children: /* @__PURE__ */ jsx(Icon, { className: "text-muted-foreground size-6", "aria-hidden": "true" }) }),
14
15
  /* @__PURE__ */ jsx("h3", { className: "ui-empty-state-title", children: title }),
15
16
  description && /* @__PURE__ */ jsx("p", { className: "ui-empty-state-description", children: description }),
@@ -139,14 +140,10 @@ DataTable.SelectAll = function DataTableSelectAll() {
139
140
  const { t } = useTranslation();
140
141
  if (!selectable) return null;
141
142
  return /* @__PURE__ */ jsx(
142
- "input",
143
+ Checkbox,
143
144
  {
144
- type: "checkbox",
145
- checked: allSelected,
146
- ref: (el) => {
147
- if (el) el.indeterminate = someSelected;
148
- },
149
- onChange: toggleSelectAll,
145
+ checked: allSelected ? true : someSelected ? "indeterminate" : false,
146
+ onCheckedChange: toggleSelectAll,
150
147
  "aria-label": t("dataTable.selectAll")
151
148
  }
152
149
  );
@@ -190,7 +187,7 @@ DataTable.DensityToggle = function DataTableDensityToggle() {
190
187
  setDensity(next);
191
188
  },
192
189
  "aria-label": t("dataTable.densitySwitch", { density: nextLabel }),
193
- children: /* @__PURE__ */ jsxs(Inline, { gap: "xs", children: [
190
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "row", wrap: true, align: "center", gap: "xs", children: [
194
191
  /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": "true" }),
195
192
  density === "compact" ? t("dataTable.densityCompact") : t("dataTable.densityComfortable")
196
193
  ] })
@@ -227,62 +224,84 @@ DataTable.Content = function DataTableContent() {
227
224
  onSortChange(void 0);
228
225
  }
229
226
  };
230
- return /* @__PURE__ */ jsx("div", { className: "ui-data-table-scroll", children: /* @__PURE__ */ jsx("div", { className: "ui-data-table-surface min-w-[640px] sm:min-w-0", children: /* @__PURE__ */ jsxs(Table, { children: [
227
+ return /* @__PURE__ */ jsx("div", { className: "ui-data-table-scroll", "aria-busy": loading, children: /* @__PURE__ */ jsx("div", { className: "ui-data-table-surface min-w-[640px] sm:min-w-0", children: /* @__PURE__ */ jsxs(Table, { children: [
231
228
  /* @__PURE__ */ jsx(TableHeader, { className: "bg-secondary sticky top-0 z-10", children: /* @__PURE__ */ jsxs(TableRow, { children: [
232
229
  selectable && /* @__PURE__ */ jsx(TableHead, { className: "w-10", children: /* @__PURE__ */ jsx(DataTable.SelectAll, {}) }),
233
- columns.map((col) => /* @__PURE__ */ jsx(
234
- TableHead,
235
- {
236
- "data-empty": !col.header || void 0,
237
- className: cn(
238
- col.width,
239
- col.align === "right" && "text-right",
240
- col.align === "center" && "text-center",
241
- col.hiddenOnMobile && "hidden md:table-cell",
242
- col.sortable && onSortChange && "cursor-pointer select-none"
243
- ),
244
- onClick: () => {
245
- onHeaderClick(col);
246
- },
247
- children: /* @__PURE__ */ jsxs("span", { className: "ui-data-table-sort-label", children: [
248
- col.header,
249
- col.sortable && onSortChange && (sort?.key === col.key ? sort.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(
250
- ChevronsUpDown,
230
+ columns.map((col) => {
231
+ const isSortable = !!col.sortable && !!onSortChange;
232
+ const isActiveSort = isSortable && sort?.key === col.key;
233
+ const sortIndicator = isSortable ? isActiveSort ? sort?.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-muted-foreground size-3", "aria-hidden": "true" }) : null;
234
+ const label = /* @__PURE__ */ jsxs("span", { className: "ui-data-table-sort-label", children: [
235
+ col.header,
236
+ sortIndicator
237
+ ] });
238
+ return /* @__PURE__ */ jsx(
239
+ TableHead,
240
+ {
241
+ "data-empty": !col.header || void 0,
242
+ "aria-sort": isSortable ? isActiveSort ? sort?.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
243
+ className: cn(
244
+ col.width,
245
+ col.align === "right" && "text-right",
246
+ col.align === "center" && "text-center",
247
+ col.hiddenOnMobile && "hidden md:table-cell",
248
+ isSortable && "select-none"
249
+ ),
250
+ children: isSortable ? /* @__PURE__ */ jsx(
251
+ "button",
251
252
  {
252
- className: "text-muted-foreground size-3",
253
- "aria-hidden": "true"
253
+ type: "button",
254
+ className: "ui-data-table-sort-button focus-visible:ring-ring rounded-sm focus-visible:ring-2",
255
+ onClick: () => {
256
+ onHeaderClick(col);
257
+ },
258
+ children: label
254
259
  }
255
- ))
256
- ] })
257
- },
258
- col.key
259
- ))
260
+ ) : label
261
+ },
262
+ col.key
263
+ );
264
+ })
260
265
  ] }) }),
261
- /* @__PURE__ */ jsx(TableBody, { children: loading ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, className: "h-32 text-center", children: /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: t("dataTable.loading") }) }) }) : data.length === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, className: "ui-data-table-empty", children: empty ?? /* @__PURE__ */ jsx(EmptyState, { title: t("dataTable.empty") }) }) }) : data.map((row) => {
266
+ /* @__PURE__ */ jsx(TableBody, { children: loading ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, className: "h-32 text-center", "aria-live": "polite", children: /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: t("dataTable.loading") }) }) }) : data.length === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
267
+ TableCell,
268
+ {
269
+ colSpan: emptyColSpan,
270
+ className: "ui-data-table-empty",
271
+ "aria-live": "polite",
272
+ children: empty ?? /* @__PURE__ */ jsx(EmptyState, { title: t("dataTable.empty") })
273
+ }
274
+ ) }) : data.map((row) => {
262
275
  const id = getRowId(row);
263
276
  const isSelected = selected.has(id);
277
+ const isInteractiveTarget = (target) => !!target.closest("button, a, input, select, textarea, [role=menuitem]");
264
278
  return /* @__PURE__ */ jsxs(
265
279
  TableRow,
266
280
  {
267
281
  "data-state": isSelected ? "selected" : void 0,
282
+ tabIndex: onRowClick ? 0 : void 0,
268
283
  onClick: (e) => {
269
284
  const target = e.target;
270
- if (target.closest("button, a, input, select, textarea, [role=menuitem]"))
271
- return;
285
+ if (isInteractiveTarget(target)) return;
272
286
  onRowClick?.(row);
273
287
  },
288
+ onKeyDown: onRowClick ? (e) => {
289
+ if (e.key !== "Enter" && e.key !== " ") return;
290
+ if (e.target !== e.currentTarget) return;
291
+ e.preventDefault();
292
+ onRowClick?.(row);
293
+ } : void 0,
274
294
  className: cn(
275
295
  rowPadding,
276
- onRowClick && "hover:bg-muted/50 cursor-pointer",
296
+ onRowClick && "hover:bg-muted/50 focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset",
277
297
  isSelected && "bg-muted/30"
278
298
  ),
279
299
  children: [
280
300
  selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx(
281
- "input",
301
+ Checkbox,
282
302
  {
283
- type: "checkbox",
284
303
  checked: isSelected,
285
- onChange: () => {
304
+ onCheckedChange: () => {
286
305
  toggleSelect(id);
287
306
  },
288
307
  "aria-label": t("dataTable.selectRow", { id }),
@@ -6,14 +6,14 @@ import { jsx, jsxs } from 'react/jsx-runtime';
6
6
  var cardVariants = cva("group/card border", {
7
7
  variants: {
8
8
  size: {
9
- default: "",
9
+ md: "",
10
10
  compact: ""
11
11
  }
12
12
  },
13
- defaultVariants: { size: "default" }
13
+ defaultVariants: { size: "md" }
14
14
  });
15
15
  var Card = React.forwardRef(
16
- ({ className, size = "default", accent, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
16
+ ({ className, size = "md", accent, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
17
17
  "div",
18
18
  {
19
19
  ref,