@boxcustodia/library 2.0.0-alpha.13 → 2.0.0-alpha.15

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 (174) hide show
  1. package/dist/index.cjs.js +1 -138
  2. package/dist/index.d.ts +1083 -717
  3. package/dist/index.es.js +7059 -56179
  4. package/dist/theme.css +1 -1
  5. package/package.json +34 -26
  6. package/src/__doc__/Changelog.mdx +6 -6
  7. package/src/__doc__/Examples.tsx +1 -1
  8. package/src/__doc__/Intro.mdx +3 -3
  9. package/src/__doc__/Tabs.mdx +112 -0
  10. package/src/__doc__/V2.mdx +1245 -0
  11. package/src/components/accordion/accordion.stories.tsx +143 -0
  12. package/src/components/accordion/accordion.tsx +135 -0
  13. package/src/components/accordion/index.ts +1 -0
  14. package/src/components/alert/alert.stories.tsx +24 -4
  15. package/src/components/alert/alert.tsx +17 -9
  16. package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
  17. package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
  18. package/src/components/alert-dialog/alert-dialog.tsx +58 -10
  19. package/src/components/auto-complete/auto-complete.stories.tsx +615 -200
  20. package/src/components/auto-complete/auto-complete.tsx +420 -68
  21. package/src/components/auto-complete/index.ts +0 -1
  22. package/src/components/avatar/avatar.stories.tsx +162 -21
  23. package/src/components/avatar/avatar.tsx +79 -20
  24. package/src/components/button/button.stories.tsx +236 -294
  25. package/src/components/button/button.test.tsx +10 -17
  26. package/src/components/button/button.tsx +53 -18
  27. package/src/components/button/components/base-button.tsx +25 -53
  28. package/src/components/button/index.ts +0 -1
  29. package/src/components/calendar/calendar.stories.tsx +1 -1
  30. package/src/components/calendar/calendar.tsx +4 -4
  31. package/src/components/card/card.stories.tsx +140 -69
  32. package/src/components/card/card.tsx +155 -54
  33. package/src/components/center/center.stories.tsx +22 -39
  34. package/src/components/checkbox/checkbox.stories.tsx +25 -5
  35. package/src/components/checkbox/checkbox.tsx +76 -15
  36. package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
  37. package/src/components/checkbox-group/checkbox-group.tsx +84 -3
  38. package/src/components/combobox/combobox.stories.tsx +33 -23
  39. package/src/components/combobox/combobox.tsx +120 -104
  40. package/src/components/date-picker/date-input.stories.tsx +14 -6
  41. package/src/components/date-picker/date-input.tsx +3 -3
  42. package/src/components/date-picker/date-picker.model.ts +13 -4
  43. package/src/components/date-picker/date-picker.stories.tsx +38 -12
  44. package/src/components/date-picker/date-picker.tsx +29 -15
  45. package/src/components/dialog/dialog.stories.tsx +18 -0
  46. package/src/components/dialog/dialog.test.tsx +1 -1
  47. package/src/components/dialog/dialog.tsx +51 -20
  48. package/src/components/divider/divider.stories.tsx +6 -0
  49. package/src/components/dropzone/dropzone.stories.tsx +70 -90
  50. package/src/components/dropzone/dropzone.tsx +383 -105
  51. package/src/components/dropzone/index.ts +0 -1
  52. package/src/components/empty/empty.stories.tsx +164 -0
  53. package/src/components/empty/empty.tsx +156 -0
  54. package/src/components/empty/index.ts +1 -0
  55. package/src/components/field/field.stories.tsx +226 -3
  56. package/src/components/field/field.tsx +77 -42
  57. package/src/components/form/form.stories.tsx +320 -197
  58. package/src/components/form/form.tsx +3 -23
  59. package/src/components/index.ts +2 -6
  60. package/src/components/input/input.stories.tsx +5 -5
  61. package/src/components/input/input.tsx +5 -5
  62. package/src/components/kbd/kbd.stories.tsx +1 -0
  63. package/src/components/label/label.stories.tsx +16 -0
  64. package/src/components/label/label.tsx +13 -2
  65. package/src/components/loader/loader.stories.tsx +7 -5
  66. package/src/components/loader/loader.tsx +8 -3
  67. package/src/components/menu/menu-primitives.tsx +207 -196
  68. package/src/components/menu/menu.stories.tsx +275 -146
  69. package/src/components/menu/menu.tsx +146 -54
  70. package/src/components/number-input/number-input.stories.tsx +27 -4
  71. package/src/components/number-input/number-input.test.tsx +2 -2
  72. package/src/components/number-input/number-input.tsx +29 -33
  73. package/src/components/otp/index.ts +1 -0
  74. package/src/components/otp/otp.stories.tsx +209 -0
  75. package/src/components/otp/otp.tsx +100 -0
  76. package/src/components/pagination/index.ts +1 -0
  77. package/src/components/pagination/pagination.model.ts +2 -0
  78. package/src/components/pagination/pagination.stories.tsx +153 -59
  79. package/src/components/pagination/pagination.test.tsx +122 -57
  80. package/src/components/pagination/pagination.tsx +575 -77
  81. package/src/components/password/password.stories.tsx +18 -3
  82. package/src/components/password/password.tsx +26 -10
  83. package/src/components/popover/popover.stories.tsx +26 -5
  84. package/src/components/popover/popover.tsx +15 -23
  85. package/src/components/progress/progress.stories.tsx +1 -0
  86. package/src/components/radio-group/index.ts +1 -0
  87. package/src/components/radio-group/radio-group.stories.tsx +251 -0
  88. package/src/components/radio-group/radio-group.tsx +212 -0
  89. package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
  90. package/src/components/select/select.stories.tsx +118 -19
  91. package/src/components/select/select.tsx +67 -62
  92. package/src/components/skeleton/skeleton.stories.tsx +1 -0
  93. package/src/components/stack/stack.stories.tsx +179 -89
  94. package/src/components/stack/stack.tsx +2 -2
  95. package/src/components/stepper/index.ts +1 -1
  96. package/src/components/stepper/stepper.stories.tsx +766 -83
  97. package/src/components/stepper/stepper.test.tsx +18 -18
  98. package/src/components/stepper/stepper.tsx +554 -0
  99. package/src/components/switch/switch.stories.tsx +15 -1
  100. package/src/components/switch/switch.tsx +17 -4
  101. package/src/components/table/index.ts +0 -2
  102. package/src/components/table/table.stories.tsx +131 -18
  103. package/src/components/table/table.test.tsx +1 -1
  104. package/src/components/table/table.tsx +183 -77
  105. package/src/components/tabs/tabs.stories.tsx +372 -155
  106. package/src/components/tabs/tabs.test.tsx +12 -12
  107. package/src/components/tabs/tabs.tsx +72 -149
  108. package/src/components/tag/index.ts +0 -1
  109. package/src/components/tag/tag.stories.tsx +147 -120
  110. package/src/components/tag/tag.tsx +47 -95
  111. package/src/components/textarea/textarea.stories.tsx +8 -22
  112. package/src/components/textarea/textarea.tsx +17 -79
  113. package/src/components/timeline/timeline.stories.tsx +322 -42
  114. package/src/components/timeline/timeline.tsx +359 -132
  115. package/src/components/toast/toast.stories.tsx +1 -0
  116. package/src/components/tooltip/tooltip.tsx +11 -9
  117. package/src/components/tree/index.ts +0 -1
  118. package/src/components/tree/tree.stories.tsx +364 -408
  119. package/src/components/tree/tree.test.tsx +163 -0
  120. package/src/components/tree/tree.tsx +212 -36
  121. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
  122. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
  123. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
  124. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
  125. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
  126. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
  127. package/src/hooks/usePagination/usePagination.tsx +36 -24
  128. package/src/styles/theme.css +1 -1
  129. package/src/utils/form.tsx +69 -37
  130. package/src/utils/index.ts +1 -1
  131. package/src/__doc__/Migration.mdx +0 -451
  132. package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
  133. package/src/components/background-image/background-image.stories.tsx +0 -21
  134. package/src/components/background-image/background-image.test.tsx +0 -29
  135. package/src/components/background-image/background-image.tsx +0 -23
  136. package/src/components/background-image/index.ts +0 -1
  137. package/src/components/button/button.variants.ts +0 -44
  138. package/src/components/button/components/loader-overlay.tsx +0 -21
  139. package/src/components/button/components/loading-icon.tsx +0 -47
  140. package/src/components/dropzone/upload-primitives.tsx +0 -310
  141. package/src/components/dropzone/use-dropzone.ts +0 -122
  142. package/src/components/empty-state/empty-state.stories.tsx +0 -56
  143. package/src/components/empty-state/empty-state.tsx +0 -39
  144. package/src/components/empty-state/index.ts +0 -1
  145. package/src/components/heading/heading.stories.tsx +0 -74
  146. package/src/components/heading/heading.tsx +0 -28
  147. package/src/components/heading/heading.variants.ts +0 -27
  148. package/src/components/heading/index.ts +0 -1
  149. package/src/components/kbd/kbd.variants.ts +0 -26
  150. package/src/components/menu/util/render-menu-item.tsx +0 -54
  151. package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
  152. package/src/components/multi-select/index.ts +0 -1
  153. package/src/components/multi-select/multi-select.stories.tsx +0 -294
  154. package/src/components/multi-select/multi-select.tsx +0 -300
  155. package/src/components/multi-select/multi-select.variants.ts +0 -22
  156. package/src/components/pagination/components/pagination-option.tsx +0 -27
  157. package/src/components/show/index.ts +0 -1
  158. package/src/components/show/show.stories.tsx +0 -197
  159. package/src/components/show/show.test.tsx +0 -41
  160. package/src/components/show/show.tsx +0 -16
  161. package/src/components/stepper/Stepper.tsx +0 -190
  162. package/src/components/stepper/context/stepper-context.tsx +0 -11
  163. package/src/components/table/table-primitives.tsx +0 -122
  164. package/src/components/table/table.model.ts +0 -20
  165. package/src/components/table-pagination/index.ts +0 -2
  166. package/src/components/table-pagination/table-pagination.model.ts +0 -2
  167. package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
  168. package/src/components/table-pagination/table-pagination.test.tsx +0 -32
  169. package/src/components/table-pagination/table-pagination.tsx +0 -108
  170. package/src/components/tabs/context/tabs-context.tsx +0 -14
  171. package/src/components/tag/tag.variants.ts +0 -31
  172. package/src/components/timeline/timeline-status.ts +0 -5
  173. package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
  174. package/src/components/tree/tree-primitives.tsx +0 -126
@@ -1,82 +1,434 @@
1
- import { ComponentProps } from "react";
2
- import { ClassName } from "@/models";
3
- import {
4
- AutoCompleteEmpty,
5
- AutoCompleteInput,
6
- AutoCompleteItem,
7
- AutoCompleteList,
8
- AutoCompleteRoot,
9
- AutoCompleteShortcut,
10
- } from "../../components";
11
-
12
- interface AutoCompleteItem {
13
- value: string;
14
- label: string;
15
- shortcut?: string;
1
+ "use client";
2
+
3
+ import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete";
4
+ import { ChevronsUpDownIcon, XIcon } from "lucide-react";
5
+ import type React from "react";
6
+ import { cn } from "../../lib";
7
+ import { inputBaseClasses } from "../input";
8
+ import { ScrollArea } from "../scroll-area";
9
+
10
+ export const AutocompleteRoot = AutocompletePrimitive.Root;
11
+
12
+ export function AutocompleteInput({
13
+ className,
14
+ adornment,
15
+ startAddon,
16
+ triggerProps,
17
+ clearProps,
18
+ ...props
19
+ }: AutocompletePrimitive.Input.Props & {
20
+ adornment?: "trigger" | "clear";
21
+ startAddon?: React.ReactNode;
22
+ ref?: React.Ref<HTMLInputElement>;
23
+ triggerProps?: AutocompletePrimitive.Trigger.Props;
24
+ clearProps?: AutocompletePrimitive.Clear.Props;
25
+ }): React.ReactElement {
26
+ return (
27
+ <AutocompletePrimitive.InputGroup
28
+ className="relative not-has-[>*.w-full]:w-fit w-full text-foreground has-disabled:opacity-64"
29
+ data-slot="autocomplete-input-group"
30
+ >
31
+ {startAddon && (
32
+ <div
33
+ aria-hidden="true"
34
+ className="pointer-events-none absolute inset-y-0 start-px z-10 flex items-center ps-[calc(--spacing(3)-1px)] opacity-80 [&_svg:not([class*='size-'])]:size-4 [&_svg]:-mx-0.5"
35
+ data-slot="autocomplete-start-addon"
36
+ >
37
+ {startAddon}
38
+ </div>
39
+ )}
40
+ <AutocompletePrimitive.Input
41
+ className={cn(
42
+ inputBaseClasses,
43
+ "focus-visible:border-ring",
44
+ "aria-invalid:border-error focus-visible:aria-invalid:ring-error/20",
45
+ "disabled:cursor-not-allowed disabled:opacity-50",
46
+ startAddon && "ps-9",
47
+ adornment && "pe-9",
48
+ className,
49
+ )}
50
+ data-slot="autocomplete-input"
51
+ render={<input autoComplete="off" data-slot="input" />}
52
+ {...props}
53
+ />
54
+ {adornment === "clear" ? (
55
+ <AutocompleteClear {...clearProps} />
56
+ ) : adornment === "trigger" ? (
57
+ <AutocompleteTrigger {...triggerProps} />
58
+ ) : null}
59
+ </AutocompletePrimitive.InputGroup>
60
+ );
61
+ }
62
+
63
+ export function AutocompletePopup({
64
+ className,
65
+ children,
66
+ side = "bottom",
67
+ sideOffset = 4,
68
+ alignOffset,
69
+ align = "start",
70
+ anchor,
71
+ portalProps,
72
+ ...props
73
+ }: AutocompletePrimitive.Popup.Props & {
74
+ align?: AutocompletePrimitive.Positioner.Props["align"];
75
+ sideOffset?: AutocompletePrimitive.Positioner.Props["sideOffset"];
76
+ alignOffset?: AutocompletePrimitive.Positioner.Props["alignOffset"];
77
+ side?: AutocompletePrimitive.Positioner.Props["side"];
78
+ anchor?: AutocompletePrimitive.Positioner.Props["anchor"];
79
+ portalProps?: AutocompletePrimitive.Portal.Props;
80
+ }): React.ReactElement {
81
+ return (
82
+ <AutocompletePrimitive.Portal {...portalProps}>
83
+ <AutocompletePrimitive.Positioner
84
+ align={align}
85
+ alignOffset={alignOffset}
86
+ anchor={anchor}
87
+ className="z-50 select-none"
88
+ data-slot="autocomplete-positioner"
89
+ side={side}
90
+ sideOffset={sideOffset}
91
+ >
92
+ <span
93
+ className={cn(
94
+ "relative flex max-h-full min-w-(--anchor-width) max-w-(--available-width) origin-(--transform-origin) rounded-lg border bg-popover shadow-lg/5 transition-[scale,opacity]",
95
+ className,
96
+ )}
97
+ >
98
+ <AutocompletePrimitive.Popup
99
+ className="flex max-h-[min(var(--available-height),23rem)] flex-1 flex-col text-foreground"
100
+ data-slot="autocomplete-popup"
101
+ {...props}
102
+ >
103
+ {children}
104
+ </AutocompletePrimitive.Popup>
105
+ </span>
106
+ </AutocompletePrimitive.Positioner>
107
+ </AutocompletePrimitive.Portal>
108
+ );
109
+ }
110
+
111
+ export function AutocompleteList({
112
+ className,
113
+ ...props
114
+ }: AutocompletePrimitive.List.Props): React.ReactElement {
115
+ return (
116
+ <ScrollArea scrollbarGutter scrollFade>
117
+ <AutocompletePrimitive.List
118
+ className={cn(
119
+ "not-empty:scroll-py-1 not-empty:p-1 in-data-has-overflow-y:pe-3",
120
+ className,
121
+ )}
122
+ data-slot="autocomplete-list"
123
+ {...props}
124
+ />
125
+ </ScrollArea>
126
+ );
127
+ }
128
+
129
+ export function AutocompleteItem({
130
+ className,
131
+ children,
132
+ ...props
133
+ }: AutocompletePrimitive.Item.Props): React.ReactElement {
134
+ return (
135
+ <AutocompletePrimitive.Item
136
+ className={cn(
137
+ "flex min-h-8 cursor-default select-none items-center rounded-sm px-2 py-1 text-base outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm",
138
+ className,
139
+ )}
140
+ data-slot="autocomplete-item"
141
+ {...props}
142
+ >
143
+ {children}
144
+ </AutocompletePrimitive.Item>
145
+ );
146
+ }
147
+
148
+ export function AutocompleteSeparator({
149
+ className,
150
+ ...props
151
+ }: AutocompletePrimitive.Separator.Props): React.ReactElement {
152
+ return (
153
+ <AutocompletePrimitive.Separator
154
+ className={cn("mx-2 my-1 h-px bg-border last:hidden", className)}
155
+ data-slot="autocomplete-separator"
156
+ {...props}
157
+ />
158
+ );
159
+ }
160
+
161
+ export function AutocompleteGroup({
162
+ className,
163
+ ...props
164
+ }: AutocompletePrimitive.Group.Props): React.ReactElement {
165
+ return (
166
+ <AutocompletePrimitive.Group
167
+ className={cn("[[role=group]+&]:mt-1.5", className)}
168
+ data-slot="autocomplete-group"
169
+ {...props}
170
+ />
171
+ );
172
+ }
173
+
174
+ export function AutocompleteGroupLabel({
175
+ className,
176
+ ...props
177
+ }: AutocompletePrimitive.GroupLabel.Props): React.ReactElement {
178
+ return (
179
+ <AutocompletePrimitive.GroupLabel
180
+ className={cn(
181
+ "px-2 py-1.5 font-medium text-muted-foreground text-xs",
182
+ className,
183
+ )}
184
+ data-slot="autocomplete-group-label"
185
+ {...props}
186
+ />
187
+ );
16
188
  }
17
189
 
18
- interface ExtendedProps {
19
- containerClassName: ClassName;
20
- inputClassName: ClassName;
21
- listClassName: ClassName;
22
- itemClassName: ClassName;
23
- emptyClassName: ClassName;
24
- containerProps: ComponentProps<typeof AutoCompleteRoot>;
25
- inputProps: ComponentProps<typeof AutoCompleteInput>;
26
- listProps: ComponentProps<typeof AutoCompleteList>;
27
- itemProps: ComponentProps<typeof AutoCompleteItem>;
28
- emptyProps: ComponentProps<typeof AutoCompleteEmpty>;
190
+ export function AutocompleteEmpty({
191
+ className,
192
+ ...props
193
+ }: AutocompletePrimitive.Empty.Props): React.ReactElement {
194
+ return (
195
+ <AutocompletePrimitive.Empty
196
+ className={cn(
197
+ "p-2 text-center text-base text-muted-foreground sm:text-sm empty:hidden",
198
+ className,
199
+ )}
200
+ data-slot="autocomplete-empty"
201
+ {...props}
202
+ />
203
+ );
204
+ }
205
+
206
+ export function AutocompleteTrigger({
207
+ className,
208
+ children,
209
+ ...props
210
+ }: AutocompletePrimitive.Trigger.Props): React.ReactElement {
211
+ return (
212
+ <AutocompletePrimitive.Trigger
213
+ className={cn(
214
+ "absolute end-0.5 top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-colors pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 sm:size-7 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
215
+ className,
216
+ )}
217
+ data-slot="autocomplete-trigger"
218
+ {...props}
219
+ >
220
+ {children ?? (
221
+ <AutocompletePrimitive.Icon data-slot="autocomplete-icon">
222
+ <ChevronsUpDownIcon />
223
+ </AutocompletePrimitive.Icon>
224
+ )}
225
+ </AutocompletePrimitive.Trigger>
226
+ );
227
+ }
228
+
229
+ export function AutocompleteClear({
230
+ className,
231
+ children,
232
+ ...props
233
+ }: AutocompletePrimitive.Clear.Props): React.ReactElement {
234
+ return (
235
+ <AutocompletePrimitive.Clear
236
+ className={cn(
237
+ "absolute end-0.5 top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-[color,background-color,box-shadow,opacity] pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 sm:size-7 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
238
+ className,
239
+ )}
240
+ data-slot="autocomplete-clear"
241
+ {...props}
242
+ >
243
+ {children ?? <XIcon />}
244
+ </AutocompletePrimitive.Clear>
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Groups items into a horizontal row within the list.
250
+ * Use to create multi-column grid layouts (color swatches, icon grids, emoji pickers).
251
+ * Each `AutocompleteItem` inside the row remains fully keyboard-navigable.
252
+ *
253
+ * ```tsx
254
+ * <AutocompleteList>
255
+ * {(row) => (
256
+ * <AutocompleteRow key={row.id}>
257
+ * {row.items.map((item) => (
258
+ * <AutocompleteItem key={item.value} value={item}>
259
+ * {item.label}
260
+ * </AutocompleteItem>
261
+ * ))}
262
+ * </AutocompleteRow>
263
+ * )}
264
+ * </AutocompleteList>
265
+ * ```
266
+ */
267
+ export function AutocompleteRow({
268
+ className,
269
+ ...props
270
+ }: AutocompletePrimitive.Row.Props): React.ReactElement {
271
+ return (
272
+ <AutocompletePrimitive.Row
273
+ className={className}
274
+ data-slot="autocomplete-row"
275
+ {...props}
276
+ />
277
+ );
278
+ }
279
+
280
+ /**
281
+ * Renders the selected value's label outside the input element.
282
+ * Useful when building custom trigger-style layouts where the selected item
283
+ * must be displayed independently — e.g., inside a badge, a button, or a custom
284
+ * trigger that replaces the input entirely.
285
+ * Renders nothing when no item is selected.
286
+ */
287
+ export function AutocompleteValue({
288
+ ...props
289
+ }: AutocompletePrimitive.Value.Props): React.ReactElement {
290
+ return (
291
+ <AutocompletePrimitive.Value data-slot="autocomplete-value" {...props} />
292
+ );
293
+ }
294
+
295
+ export function AutocompleteStatus({
296
+ className,
297
+ ...props
298
+ }: AutocompletePrimitive.Status.Props): React.ReactElement {
299
+ return (
300
+ <AutocompletePrimitive.Status
301
+ className={cn(
302
+ "px-3 py-2 font-medium text-muted-foreground text-xs empty:m-0 empty:p-0",
303
+ className,
304
+ )}
305
+ data-slot="autocomplete-status"
306
+ {...props}
307
+ />
308
+ );
309
+ }
310
+
311
+ export function AutocompleteCollection({
312
+ ...props
313
+ }: AutocompletePrimitive.Collection.Props): React.ReactElement {
314
+ return (
315
+ <AutocompletePrimitive.Collection
316
+ data-slot="autocomplete-collection"
317
+ {...props}
318
+ />
319
+ );
29
320
  }
30
321
 
31
- interface Props extends ExtendedProps {
32
- items: AutoCompleteItem[];
322
+ export const useAutocompleteFilter: typeof AutocompletePrimitive.useFilter =
323
+ AutocompletePrimitive.useFilter;
324
+
325
+ // ─── Composite ───────────────────────────────────────────────────────────────
326
+ export interface AutocompleteOption {
327
+ value: string;
328
+ label: string;
329
+ }
330
+
331
+ interface AutocompleteProps<
332
+ TOption extends AutocompleteOption = AutocompleteOption,
333
+ > {
334
+ items: TOption[];
335
+ value?: TOption | null;
336
+ defaultValue?: TOption | null;
337
+ onValueChange?: (value: TOption | null) => void;
338
+ disabled?: boolean;
339
+ readOnly?: boolean;
340
+ isLoading?: boolean;
341
+ filter?: AutocompletePrimitive.Root.Props<TOption>["filter"];
342
+ autoHighlight?: boolean | "always";
33
343
  placeholder?: string;
34
344
  emptyMessage?: string;
35
- onSelect?: (value: AutoCompleteItem["value"], item: AutoCompleteItem) => any;
345
+ loadingMessage?: string;
346
+ renderOption?: (option: TOption) => React.ReactNode;
347
+ adornment?: "trigger" | "clear";
348
+ startAddon?: React.ReactNode;
349
+ /** Styles the input field. */
350
+ className?: string;
351
+ /** Styles applied to each internal slot. */
352
+ classNames?: {
353
+ /** Dropdown popup container. */
354
+ popup?: string;
355
+ /** Scrollable list inside the popup. */
356
+ list?: string;
357
+ /** Each option item. */
358
+ item?: string;
359
+ /** Empty state message. */
360
+ empty?: string;
361
+ /** Status row (e.g. loading message). */
362
+ status?: string;
363
+ };
36
364
  }
37
365
 
38
- export const AutoComplete = ({
366
+ export function Autocomplete<
367
+ TOption extends AutocompleteOption = AutocompleteOption,
368
+ >({
39
369
  items,
370
+ value,
371
+ defaultValue,
372
+ onValueChange,
373
+ disabled,
374
+ readOnly,
375
+ isLoading = false,
376
+ filter,
377
+ autoHighlight,
40
378
  placeholder = "Buscar...",
41
- emptyMessage = "No se encontraron resultados",
42
- onSelect,
43
- containerClassName,
44
- containerProps,
45
- inputClassName,
46
- inputProps,
47
- listClassName,
48
- listProps,
49
- itemClassName,
50
- itemProps,
51
- emptyClassName,
52
- emptyProps,
53
- }: Props) => {
379
+ emptyMessage = "Sin resultados",
380
+ loadingMessage = "Cargando...",
381
+ renderOption,
382
+ adornment,
383
+ startAddon,
384
+ className,
385
+ classNames,
386
+ }: AutocompleteProps<TOption>): React.ReactElement {
54
387
  return (
55
- <AutoCompleteRoot className={containerClassName} {...containerProps}>
56
- <AutoCompleteInput
388
+ <AutocompleteRoot
389
+ items={items}
390
+ value={value?.label}
391
+ defaultValue={defaultValue?.label}
392
+ onValueChange={(opt) =>
393
+ onValueChange?.((opt as unknown as TOption | undefined) ?? null)
394
+ }
395
+ disabled={disabled}
396
+ readOnly={readOnly}
397
+ filter={filter}
398
+ autoHighlight={autoHighlight}
399
+ itemToStringValue={(opt) => (opt as TOption).label}
400
+ >
401
+ <AutocompleteInput
402
+ adornment={adornment}
403
+ startAddon={startAddon}
57
404
  placeholder={placeholder}
58
- className={inputClassName}
59
- {...inputProps}
405
+ className={className}
60
406
  />
61
- <AutoCompleteList className={listClassName} {...listProps}>
62
- {(items as AutoCompleteItem[]).map((item) => (
63
- <AutoCompleteItem
64
- className={itemClassName}
65
- key={item.value}
66
- onSelect={() => onSelect?.(item.value, item)}
67
- {...itemProps}
68
- >
69
- {item.label}
70
- {item.shortcut && (
71
- <AutoCompleteShortcut>{item.shortcut}</AutoCompleteShortcut>
72
- )}
73
- </AutoCompleteItem>
74
- ))}
75
- </AutoCompleteList>
76
-
77
- <AutoCompleteEmpty className={emptyClassName} {...emptyProps}>
78
- {emptyMessage}
79
- </AutoCompleteEmpty>
80
- </AutoCompleteRoot>
407
+ <AutocompletePopup className={classNames?.popup}>
408
+ <AutocompleteStatus className={classNames?.status}>
409
+ {isLoading ? loadingMessage : undefined}
410
+ </AutocompleteStatus>
411
+ <AutocompleteList className={classNames?.list}>
412
+ {(option) => (
413
+ <AutocompleteItem
414
+ key={(option as TOption).value}
415
+ value={option}
416
+ className={classNames?.item}
417
+ >
418
+ {renderOption
419
+ ? renderOption(option as TOption)
420
+ : (option as TOption).label}
421
+ </AutocompleteItem>
422
+ )}
423
+ </AutocompleteList>
424
+ {!isLoading && (
425
+ <AutocompleteEmpty className={classNames?.empty}>
426
+ {emptyMessage}
427
+ </AutocompleteEmpty>
428
+ )}
429
+ </AutocompletePopup>
430
+ </AutocompleteRoot>
81
431
  );
82
- };
432
+ }
433
+
434
+ export { AutocompletePrimitive };
@@ -1,2 +1 @@
1
1
  export * from "./auto-complete";
2
- export * from "./auto-complete-primitives";