@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,55 +1,78 @@
1
- // @ts-nocheck
2
- // biome-ignore-all lint: utility wrappers require flexible typings
3
1
  import { zodResolver } from "@hookform/resolvers/zod";
4
2
  import type { ComponentProps, ReactNode } from "react";
5
3
  import {
4
+ Controller,
6
5
  type ControllerRenderProps,
6
+ type FieldPath,
7
7
  type FieldValues,
8
8
  FormProvider,
9
- type Path,
10
9
  type RegisterOptions,
11
10
  useForm as rhfUseForm,
12
11
  type SubmitHandler,
13
12
  type UseFormProps,
14
13
  type UseFormReturn,
15
- useController,
14
+ useFormContext,
16
15
  } from "react-hook-form";
17
- import type { z } from "zod";
16
+ import { z } from "zod";
17
+ import { Field, type FieldProps, Form as LibraryForm } from "../components";
18
18
 
19
- import { Field, type FieldProps } from "../components/field";
20
- import { Form as LibraryForm } from "../components/form";
21
-
22
- // ── useForm ───────────────────────────────────────────────────────────────────
19
+ /**
20
+ * Validates `formValues` from `Form.onFormSubmit` against a Zod schema and
21
+ * returns `{ data, errors }` ready to wire back into `Form`'s `errors` prop.
22
+ *
23
+ * - `data` is the typed, parsed value, or `null` when validation fails.
24
+ * - `errors` is the flattened `fieldErrors` object (empty on success).
25
+ */
26
+ export function parseFormValues<T extends z.ZodType>(
27
+ schema: T,
28
+ values: Record<string, unknown>,
29
+ ): { data: z.infer<T> | null; errors: Record<string, string[]> } {
30
+ const result = schema.safeParse(values);
31
+ if (result.success) {
32
+ return { data: result.data, errors: {} };
33
+ }
34
+ return {
35
+ data: null,
36
+ errors: z.flattenError(result.error).fieldErrors as Record<
37
+ string,
38
+ string[]
39
+ >,
40
+ };
41
+ }
23
42
 
24
- export function useForm<T extends FieldValues = FieldValues>(
43
+ export function useHookForm<T extends FieldValues = FieldValues>(
25
44
  schema: z.ZodType<T>,
26
45
  options?: Partial<UseFormProps<T>>,
27
46
  ): UseFormReturn<T> {
47
+ // @ts-expect-error RHF's UseFormReturn has 3 generics; narrowing to <T> is safe here.
28
48
  return rhfUseForm<T>({
29
49
  ...options,
30
- resolver: zodResolver(schema),
50
+ // @ts-expect-error zodResolver returns Resolver<any, any, any>; incompatible with Resolver<T, any, T>.
51
+ resolver: zodResolver(schema as any),
31
52
  });
32
53
  }
33
54
 
34
- // ── Form ──────────────────────────────────────────────────────────────────────
35
-
36
- type FormProps<T extends FieldValues> = {
55
+ type HookFormProps<T extends FieldValues> = Omit<
56
+ ComponentProps<typeof LibraryForm>,
57
+ "onSubmit" | "onFormSubmit" | "children"
58
+ > & {
37
59
  form: UseFormReturn<T>;
38
- onSubmit: SubmitHandler<T>;
60
+ onFormSubmit: SubmitHandler<T>;
39
61
  children: ReactNode;
40
- } & Omit<ComponentProps<typeof LibraryForm>, "onSubmit" | "onFormSubmit">;
62
+ };
41
63
 
42
- export function Form<T extends FieldValues>({
64
+ export function HookForm<T extends FieldValues>({
43
65
  form,
44
- onSubmit,
66
+ onFormSubmit,
45
67
  children,
46
68
  ...props
47
- }: FormProps<T>) {
69
+ }: HookFormProps<T>) {
48
70
  return (
49
71
  <FormProvider {...form}>
50
72
  <LibraryForm
51
- onFormSubmit={(event) => {
52
- form.handleSubmit(onSubmit)(event);
73
+ onSubmit={(event) => {
74
+ event.preventDefault();
75
+ form.handleSubmit(onFormSubmit)();
53
76
  }}
54
77
  {...props}
55
78
  >
@@ -59,33 +82,42 @@ export function Form<T extends FieldValues>({
59
82
  );
60
83
  }
61
84
 
62
- // ── FormField ─────────────────────────────────────────────────────────────────
63
-
64
- export type FormFieldRenderProps<
85
+ export type HookFieldRenderProps<
65
86
  T extends FieldValues = FieldValues,
66
- TName extends Path<T> = Path<T>,
87
+ TName extends FieldPath<T> = FieldPath<T>,
67
88
  > = ControllerRenderProps<T, TName> & {
68
89
  invalid: boolean;
69
90
  };
70
91
 
71
- type FormFieldProps<
92
+ type HookFieldProps<
72
93
  T extends FieldValues = FieldValues,
73
- TName extends Path<T> = Path<T>,
74
- > = Omit<FieldProps, "children" | "error"> & {
94
+ TName extends FieldPath<T> = FieldPath<T>,
95
+ > = Omit<FieldProps, "children" | "error" | "render" | "name"> & {
75
96
  name: TName;
76
- rules?: RegisterOptions;
77
- render: (field: FormFieldRenderProps<T, TName>) => ReactNode;
97
+ rules?: RegisterOptions<T, TName>;
98
+ render: (field: HookFieldRenderProps<T, TName>) => ReactNode;
78
99
  };
79
100
 
80
- export function FormField<
101
+ export function HookField<
81
102
  T extends FieldValues = FieldValues,
82
- TName extends Path<T> = Path<T>,
83
- >({ name, rules, render, ...fieldProps }: FormFieldProps<T, TName>) {
84
- const { field, fieldState } = useController<T, TName>({ name, rules });
103
+ TName extends FieldPath<T> = FieldPath<T>,
104
+ >({ name, rules, render, ...fieldProps }: HookFieldProps<T, TName>) {
105
+ const { control } = useFormContext<T>();
85
106
 
86
107
  return (
87
- <Field {...fieldProps} error={fieldState.error?.message}>
88
- {render({ ...field, invalid: fieldState.invalid })}
89
- </Field>
108
+ <Controller<T, TName>
109
+ name={name}
110
+ control={control}
111
+ rules={rules}
112
+ render={({ field, fieldState }) => (
113
+ <Field
114
+ {...fieldProps}
115
+ invalid={fieldState.invalid}
116
+ error={fieldState.error?.message}
117
+ >
118
+ {render({ ...field, invalid: fieldState.invalid })}
119
+ </Field>
120
+ )}
121
+ />
90
122
  );
91
123
  }
@@ -1,3 +1,3 @@
1
+ export * from "./form";
1
2
  export * from "./functions";
2
3
  export * from "./strings";
3
- export * from "./tests";
@@ -1,451 +0,0 @@
1
- import { Meta, Source } from "@storybook/addon-docs/blocks";
2
-
3
- <Meta title="Documentation/Migration v1 → v2" />
4
-
5
- # Migración v1 → v2
6
-
7
- Esta guía te lleva paso a paso desde `@boxcustodia/library@1.x` hasta la versión estable de v2.
8
-
9
- ---
10
-
11
- ## 1. Version 2.0.0-alpha.6
12
-
13
- ### Instalación
14
-
15
- ```bash
16
- npm install @boxcustodia/library@2.0.0-alpha.6
17
- ```
18
-
19
- ### Cambios necesarios
20
-
21
- #### Provider
22
-
23
- El wrapper `Toaster` fue deprecado. Reemplazalo por `LibraryProvider`.
24
-
25
- ```tsx
26
- // Antes
27
- import { Toaster } from "@boxcustodia/library";
28
-
29
- <Toaster />;
30
- ```
31
-
32
- ```tsx
33
- // Ahora
34
- import { LibraryProvider } from "@boxcustodia/library";
35
-
36
- <LibraryProvider>
37
- <App />
38
- </LibraryProvider>;
39
- ```
40
-
41
- ---
42
-
43
- #### Avatar
44
-
45
- La prop `variant` fue renombrada a `shape`. Los valores no cambian.
46
-
47
- ```tsx
48
- // Antes
49
- <Avatar variant="circle" />
50
-
51
- // Ahora
52
- <Avatar shape="circle" />
53
- ```
54
-
55
- ---
56
-
57
- #### Checkbox
58
-
59
- La prop `onChange` fue renombrada a `onCheckedChange`. `checked` y `defaultChecked` no cambian.
60
-
61
- ```tsx
62
- // Antes
63
- <Checkbox onChange={(value) => setValue(value)} />
64
-
65
- // Ahora
66
- <Checkbox onCheckedChange={(value) => setValue(value)} />
67
- ```
68
-
69
- Las siguientes props fueron eliminadas: `size`, `containerClassName`, `containerProps`.
70
-
71
- ---
72
-
73
- #### Switch
74
-
75
- La prop `onChange` fue renombrada a `onCheckedChange`. `checked` y `defaultChecked` no cambian.
76
-
77
- ```tsx
78
- // Antes
79
- <Switch onChange={(value) => setValue(value)} />
80
-
81
- // Ahora
82
- <Switch onCheckedChange={(value) => setValue(value)} />
83
- ```
84
-
85
- Las siguientes props fueron eliminadas: `containerClassName`, `containerProps`. El control de estilos ahora es via `className`.
86
-
87
- ---
88
-
89
- #### Kbd
90
-
91
- Las siguientes props fueron eliminadas: `size`, `variant`.
92
-
93
- ---
94
-
95
- #### Skeleton
96
-
97
- Las props `width`, `height`, `isCircle` e `isLoading` fueron eliminadas. El control de tamaño y forma ahora es via `className`. Además, ya no acepta `children` — usá un ternario para controlar la visibilidad.
98
-
99
- ```tsx
100
- // Antes
101
- <Skeleton width="200px" height="16px" isLoading={isLoading}>
102
- <p>Contenido</p>
103
- </Skeleton>;
104
-
105
- // Ahora
106
- {
107
- isLoading ? <Skeleton className="h-4 w-[200px]" /> : <p>Contenido</p>;
108
- }
109
- ```
110
-
111
- ---
112
-
113
- #### Dialog
114
-
115
- `DialogContent` fue renombrado a `DialogPopup`. La prop `trigger` cambió de `ReactNode` a `ReactElement`.
116
-
117
- Las siguientes props fueron eliminadas: `triggerProps`, `contentProps`, `contentClassName`, `headerProps`, `headerClassName`, `titleProps`, `titleClassName`, `descriptionProps`, `descriptionClassName`, `footerProps`, `footerClassName`, `focus`.
118
-
119
- ---
120
-
121
- #### AlertDialog
122
-
123
- La prop `asChild` del composite fue eliminada — ya no es necesaria.
124
-
125
- ---
126
-
127
- #### Tooltip
128
-
129
- La prop `label` fue renombrada a `content`. La prop `asChild` fue eliminada.
130
-
131
- ```tsx
132
- // Antes
133
- <Tooltip label="Más información" asChild>
134
- <button>Hover</button>
135
- </Tooltip>
136
-
137
- // Ahora
138
- <Tooltip content="Más información">
139
- <button>Hover</button>
140
- </Tooltip>
141
- ```
142
-
143
- ---
144
-
145
- #### Toast
146
-
147
- La prop `type` fue renombrada a `variant` en las llamadas directas a `toast`.
148
-
149
- ```tsx
150
- // Antes
151
- toast({ type: "error", description: "Algo salió mal" });
152
-
153
- // Ahora
154
- toast({ variant: "error", description: "Algo salió mal" });
155
- ```
156
-
157
- ---
158
-
159
- La API de `toast.promise` cambió. Ahora cada estado recibe un objeto con las props del toast en lugar de un string directo.
160
-
161
- ```tsx
162
- // Antes
163
- toast.promise(mockSuccessPromise(), {
164
- loading: "Guardando...",
165
- success: (data) => data.message || "Guardado correctamente",
166
- error: (error) => error.message || "Error al guardar",
167
- });
168
- ```
169
-
170
- ```tsx
171
- // Ahora
172
- toast.promise(saveData(), {
173
- loading: { description: "Guardando..." },
174
- success: (data) => ({
175
- variant: "success",
176
- description: data.message || "Guardado correctamente",
177
- }),
178
- error: (error) => ({
179
- variant: "error",
180
- description: error.message || "Error al guardar",
181
- }),
182
- });
183
- ```
184
-
185
- ---
186
-
187
- #### Componentes nuevos
188
-
189
- Se agregaron los siguientes componentes. No requieren ningún cambio en tu código existente.
190
-
191
- - **Alert** — notificaciones y mensajes de estado inline.
192
- - **CheckboxGroup** — agrupa múltiples checkboxes con manejo de estado unificado.
193
- - **Progress** — barra de progreso determinada e indeterminada.
194
-
195
- ---
196
-
197
- ## 2. Version 2.0.0-alpha.7
198
-
199
- ### Instalación
200
-
201
- ```bash
202
- npm install @boxcustodia/library@2.0.0-alpha.7
203
- ```
204
-
205
- ### Cambios necesarios
206
-
207
- #### Field
208
-
209
- Las siguientes props fueron eliminadas: `labelClassName`, `labelProps`, `errorClassName`, `errorProps`.
210
-
211
- La prop `error` cambió de `ReactNode` a `string | { message: ReactNode; match: FieldErrorMatch }`.
212
-
213
- ```tsx
214
- // Antes
215
- <Field error={<span>Campo requerido</span>} />
216
-
217
- // Ahora
218
- <Field error="Campo requerido" />
219
- ```
220
-
221
- ---
222
-
223
- #### Input
224
-
225
- La integración con `ThemeProvider` fue eliminada. El control de estilos ahora es via `className`.
226
-
227
- ---
228
-
229
- #### Password
230
-
231
- La prop `containerClassName` fue eliminada. Usá `className` para estilizar el input directamente, y un wrapper externo para controlar el contenedor.
232
-
233
- ---
234
-
235
- ## 4. Version 2.0.0-alpha.11
236
-
237
- ### Instalación
238
-
239
- ```bash
240
- npm install @boxcustodia/library@2.0.0-alpha.11
241
- ```
242
-
243
- ### Cambios necesarios
244
-
245
- #### Form
246
-
247
- La API de `Form` cambió completamente. El componente ya no tiene acoplamiento con React Hook Form — ahora es agnóstico a cualquier estrategia de validación (HTML nativo, Zod, server actions, RHF, etc.).
248
-
249
- **Si querés migrar a la nueva API**, revisá la story **Components/Form** en Storybook para ver los distintos modos de uso.
250
-
251
- **Si no querés migrar todavía**, copiá el código desde la story **Form → WithReactHookForm** (tab "Show code") en un archivo de tu proyecto, por ejemplo `src/components/form/form.tsx`. El snippet ya exporta los helpers con los mismos nombres que usabas — `useForm`, `Form` y `FormField` — así que solo tenés que cambiar los imports:
252
-
253
- ```tsx
254
- // Antes
255
- import { useForm, Form, FormField } from "@boxcustodia/library";
256
-
257
- // Ahora
258
- import { useForm, Form, FormField } from "@/components/form/form";
259
- ```
260
-
261
- El resto de tu código no necesita ningún cambio.
262
-
263
- #### Calendar
264
-
265
- El componente `Calendar` fue completamente reescrito usando `react-day-picker` en lugar de una implementación custom. La API cambió significativamente:
266
-
267
- **Props eliminadas:**
268
-
269
- - `initialView` — el calendar ahora solo soporta modo month.
270
- - `view` / `onChangeView` — no hay control de vista (month/year/decade).
271
- - `initialDate` — usá las props nativas de `react-day-picker`.
272
- - `monthViewProps` — usá `classNames` para customizar estilos.
273
- - `goBackProps`, `goNextProps`, `changeViewProps` — los controles de navegación son internos.
274
-
275
- **Props nuevas (de react-day-picker):**
276
-
277
- - `mode` — `"single"`, `"multiple"`, `"range"`.
278
- - `captionLayout` — `"dropdown"` o `"buttons"` para seleccionar mes/año.
279
- - `classNames` — object con clases para cada parte del calendar.
280
- - `components` — customizar componentes internos (Dropdown, Chevron).
281
- - `startMonth` / `endMonth` — rango de meses navegables.
282
-
283
- ```tsx
284
- // Antes
285
- <Calendar
286
- initialView="month"
287
- disabled={(date) => date.getDay() === 0}
288
- goBackProps={{ className: "custom" }}
289
- />
290
-
291
- // Ahora
292
- <Calendar
293
- mode="single"
294
- disabled={(date) => date.getDay() === 0}
295
- captionLayout="dropdown"
296
- classNames={{ button_previous: "custom" }}
297
- />
298
- ```
299
-
300
- ---
301
-
302
- #### Popover
303
-
304
- El componente `Popover` fue completamente reescrito usando Base UI en lugar de Radix. La API cambió significativamente:
305
-
306
- **Props renombradas:**
307
-
308
- - `contentClassName` → `popupClassName`
309
- - `contentProps` → `popupProps`
310
-
311
- **Props eliminadas:**
312
-
313
- - `triggerAsChild` — el comportamiento ahora es el predeterminado. Usá `render` en lugar de `asChild`.
314
- - `triggerClassName` — pasá `className` al elemento dentro del `render`.
315
- - `offset` — se divide en dos props: `sideOffset` (brecha vertical/horizontal) y `alignOffset` (desplazamiento perpendicular).
316
-
317
- **Cambios en el tipo de `trigger`:**
318
-
319
- - Antes: `ReactNode`
320
- - Ahora: `ReactElement` (se pasa directamente, no envuelto).
321
-
322
- ```tsx
323
- // Antes
324
- <Popover trigger={<Button>Open</Button>} contentClassName="w-80">
325
- Content
326
- </Popover>
327
-
328
- // Ahora
329
- <Popover trigger={<Button>Open</Button>} popupClassName="w-80">
330
- Content
331
- </Popover>
332
- ```
333
-
334
- **Offset → sideOffset + alignOffset:**
335
-
336
- ```tsx
337
- // Antes
338
- <Popover offset={8}>Content</Popover>
339
-
340
- // Ahora
341
- <Popover sideOffset={8} alignOffset={0}>Content</Popover>
342
- ```
343
-
344
- **Primitivas con `render`:**
345
-
346
- Si usabas `PopoverTrigger` con `asChild`, reemplazá con `render`:
347
-
348
- ```tsx
349
- // Antes
350
- <PopoverTrigger asChild>
351
- <Button>Open</Button>
352
- </PopoverTrigger>
353
-
354
- // Ahora
355
- <PopoverTrigger render={<Button>Open</Button>} />
356
- ```
357
-
358
- ---
359
-
360
- #### DatePicker
361
-
362
- El componente ahora soporta múltiples modos y cambió de API internamente.
363
-
364
- **Props eliminadas:**
365
-
366
- - `hideFooter` — usá `renderFooter` para controlar completamente el pie.
367
-
368
- **Props nuevas:**
369
-
370
- - `mode` — `"single"`, `"range"`, `"multiple"` para seleccionar el tipo de picker.
371
-
372
- ```tsx
373
- // Antes
374
- <DatePicker value={date} onChange={setDate} hideFooter />
375
-
376
- // Ahora
377
- <DatePicker mode="single" value={date} onChange={setDate} renderFooter={() => null} />
378
- ```
379
-
380
- **Si necesitás un date picker editable:** Usá el nuevo componente `DateInput` que permite escribir la fecha directamente en el input además de seleccionar desde el calendar.
381
-
382
- #### Combobox
383
-
384
- El Combobox fue completamente reescrito usando Base UI en lugar de la arquitectura anterior. La API es significativamente diferente. Ahora soporta selección múltiple con chips dinámicos y una arquitectura de primitivas para casos de uso avanzados.
385
-
386
- **Props renombradas:**
387
-
388
- - `valueKey` → `getValue` (ahora una function `(item) => string`)
389
- - `labelKey` → `getLabel` (ahora una function `(item) => string`)
390
- - `renderOption` → `renderItem`
391
- - `emptyMessage` → `emptyText`
392
- - `searchPlaceholder` → `placeholder`
393
- - `triggerProps` → `inputProps` (single) o `chipsProps` (multiple)
394
- - `contentProps` → `popupProps`
395
-
396
- **Props eliminadas:**
397
-
398
- - `searchProps` — en modo multiple, el input ahora está en el popup
399
- - `commandProps` — no necesario con la nueva arquitectura
400
- - `contentClassName` — usá `popupProps` con `className`
401
-
402
- **Props nuevas:**
403
-
404
- - `multiple?: boolean` — habilita modo múltiple con chips dinámicos
405
- - `getLabel: (item: TItem) => string` — extraer label del item
406
- - `getValue: (item: TItem) => string` — extraer valor del item
407
- - `renderItem?: (item: TItem) => ReactNode` — customizar contenido de cada item
408
- - `showClear?: boolean` — mostrar botón clear (default `true`)
409
- - `listProps?: ComponentProps` — customizar lista
410
- - `itemProps?: ComponentProps` — customizar items
411
-
412
- **Cambios de comportamiento:**
413
-
414
- En single select, el input está en el trigger. En multiple select, los chips se renderizan en el trigger y el input para búsqueda va en el popup. Los chips se ajustan dinámicamente al ancho del contenedor — si no caben, muestran "+N más".
415
-
416
- ```tsx
417
- // Antes (single select)
418
- <Combobox
419
- items={fruits}
420
- valueKey="id"
421
- labelKey="name"
422
- renderOption={(item) => item.name}
423
- value={selected}
424
- onChange={(val) => setSelected(val)}
425
- />
426
-
427
- // Ahora (single select)
428
- <Combobox
429
- items={fruits}
430
- getLabel={(item) => item.name}
431
- getValue={(item) => item.id}
432
- renderItem={(item) => item.name}
433
- value={selected}
434
- onChange={(item) => setSelected(item)}
435
- />
436
-
437
- // Ahora (multiple select - NUEVO)
438
- <Combobox
439
- items={fruits}
440
- multiple
441
- getLabel={(item) => item.name}
442
- getValue={(item) => item.id}
443
- placeholder="Select fruits…"
444
- value={selectedFruits}
445
- onChange={setSelectedFruits}
446
- />
447
- ```
448
-
449
- **Para casos avanzados:** Si los props de escape-hatch no son suficientes, usá las primitivas directamente: `ComboboxRoot`, `ComboboxInput`, `ComboboxChips`, `ComboboxChipsInput`, `ComboboxPopup`, `ComboboxList`, etc.
450
-
451
- ---