@adamosuiteservices/ui 2.14.1 → 2.15.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 (41) hide show
  1. package/dist/combobox-DGuQtXjP.js +608 -0
  2. package/dist/combobox-hTCtPMUL.cjs +40 -0
  3. package/dist/combobox.cjs +1 -1
  4. package/dist/combobox.js +1 -1
  5. package/dist/components/ui/combobox/combobox.d.ts +7 -1
  6. package/dist/components/ui/date-picker-selector/date-picker-selector.d.ts +75 -1
  7. package/dist/components/ui/file-upload/file-upload.d.ts +3 -1
  8. package/dist/components/ui/slider/slider.d.ts +1 -1
  9. package/dist/components/ui/switch/switch.d.ts +1 -1
  10. package/dist/date-picker-selector.cjs +1 -1
  11. package/dist/date-picker-selector.js +81 -78
  12. package/dist/field.cjs +2 -2
  13. package/dist/field.js +2 -2
  14. package/dist/file-upload.cjs +5 -3
  15. package/dist/file-upload.js +178 -149
  16. package/dist/slider.cjs +5 -5
  17. package/dist/slider.js +196 -177
  18. package/dist/styles.css +1 -1
  19. package/dist/switch.cjs +3 -3
  20. package/dist/switch.js +105 -85
  21. package/docs/components/ui/accordion-rounded.md +2 -6
  22. package/docs/components/ui/avatar.md +3 -1
  23. package/docs/components/ui/button.md +22 -16
  24. package/docs/components/ui/card.md +49 -31
  25. package/docs/components/ui/checkbox.md +44 -8
  26. package/docs/components/ui/combobox.md +100 -0
  27. package/docs/components/ui/date-picker-selector.md +147 -28
  28. package/docs/components/ui/file-upload.md +241 -94
  29. package/docs/components/ui/icon.md +1 -1
  30. package/docs/components/ui/input.md +4 -1
  31. package/docs/components/ui/radio-group.md +1 -1
  32. package/docs/components/ui/select.md +51 -34
  33. package/docs/components/ui/sheet.md +3 -9
  34. package/docs/components/ui/slider.md +120 -99
  35. package/docs/components/ui/switch.md +408 -408
  36. package/docs/components/ui/textarea.md +37 -22
  37. package/docs/components/ui/tooltip.md +5 -2
  38. package/docs/components/ui/typography.md +63 -39
  39. package/package.json +1 -1
  40. package/dist/combobox-B8HMlZy6.js +0 -601
  41. package/dist/combobox-Btj-hiBy.cjs +0 -40
@@ -39,6 +39,9 @@ import { Combobox } from "@adamosuiteservices/ui/combobox";
39
39
  | `labels` | `ComboboxLabels` | - | Textos personalizables (ver tabla abajo) |
40
40
  | `classNames` | `ComboboxClassNames` | - | Clases CSS por sección (ver tabla abajo) |
41
41
  | `renders` | `ComboboxRenderProps` | - | Funciones de renderizado personalizadas (ver tabla abajo) |
42
+ | `ref` | `React.Ref<HTMLButtonElement>` | - | Ref al botón trigger para posicionamiento externo |
43
+ | `aria-invalid` | `"true" \| "false" \| boolean` | - | Indica estado inválido (borde rojo) |
44
+ | `disabled` | `boolean` | `false` | Deshabilita el combobox |
42
45
 
43
46
  ### ComboboxLabels
44
47
 
@@ -99,6 +102,45 @@ const frameworks = [
99
102
 
100
103
  ### Con Icono
101
104
 
105
+ ```tsx
106
+ <Combobox
107
+ icon="search"
108
+ options={frameworks}
109
+ labels={{ placeholder: "Search framework..." }}
110
+ />
111
+ ```
112
+
113
+ ### Con Ref (Para Posicionamiento Externo)
114
+
115
+ El Combobox puede recibir un ref que apunta directamente al botón trigger. Esto es útil para posicionar otros elementos relativos al Combobox usando `virtualRef` de Radix UI.
116
+
117
+ ```tsx
118
+ import { useRef } from "react";
119
+
120
+ function MyComponent() {
121
+ const comboboxRef = useRef<HTMLButtonElement>(null);
122
+
123
+ return (
124
+ <>
125
+ <Combobox ref={comboboxRef} options={options} />
126
+ {/* Usar comboboxRef para posicionar otros elementos */}
127
+ <Popover>
128
+ <PopoverAnchor virtualRef={comboboxRef} />
129
+ <PopoverContent>...</PopoverContent>
130
+ </Popover>
131
+ </>
132
+ );
133
+ }
134
+ ```
135
+
136
+ **Notas importantes sobre el ref:**
137
+
138
+ - El ref apunta directamente al botón trigger (`<Button>`), no a un wrapper
139
+ - Internamente, el Combobox mantiene su propio ref para medir el ancho del popover
140
+ - Cuando pasas un ref externo, ambos refs se sincronizan automáticamente
141
+ - Compatible con callback refs y object refs (`React.RefObject`)
142
+ - Útil para componentes como `DatePickerSelector` que usan `PopoverAnchor` con `virtualRef`
143
+
102
144
  ```tsx
103
145
  <Combobox
104
146
  searchable
@@ -246,6 +288,63 @@ const options = [
246
288
  />;
247
289
  ```
248
290
 
291
+ ### Estado Inválido
292
+
293
+ ```tsx
294
+ const [value, setValue] = useState("");
295
+ const isInvalid = !value; // Required validation
296
+
297
+ <div className="space-y-2">
298
+ <Combobox
299
+ searchable
300
+ options={frameworks}
301
+ value={value}
302
+ onValueChange={(newValue) => setValue(newValue as string)}
303
+ aria-invalid={isInvalid}
304
+ labels={{ placeholder: "Select framework..." }}
305
+ />
306
+ {isInvalid && (
307
+ <p className="text-sm text-destructive">
308
+ Please select a framework to continue
309
+ </p>
310
+ )}
311
+ </div>;
312
+ ```
313
+
314
+ **Estilos**: Cuando `aria-invalid="true"`, el trigger muestra borde rojo (heredado del componente Button).
315
+
316
+ ### Estado Deshabilitado
317
+
318
+ ```tsx
319
+ // Usando disabled
320
+ <Combobox
321
+ searchable
322
+ options={frameworks}
323
+ value="next.js"
324
+ disabled
325
+ labels={{ placeholder: "Select framework..." }}
326
+ />
327
+ ```
328
+
329
+ ### En Fieldset Disabled
330
+
331
+ ```tsx
332
+ <fieldset disabled>
333
+ <legend>Preferences (Disabled)</legend>
334
+ <Field>
335
+ <FieldLabel>Framework</FieldLabel>
336
+ <Combobox
337
+ searchable
338
+ options={frameworks}
339
+ value="next.js"
340
+ labels={{ placeholder: "Select framework..." }}
341
+ />
342
+ </Field>
343
+ </fieldset>
344
+ ```
345
+
346
+ Los comboboxes dentro de un `<fieldset disabled>` automáticamente heredan el estado disabled del navegador.
347
+
249
348
  ### Labels Personalizadas
250
349
 
251
350
  ```tsx
@@ -542,6 +641,7 @@ El prop `renders` permite personalizar completamente la UI usando el patrón ren
542
641
  ### Command Components
543
642
 
544
643
  Hereda estilos de Command component:
644
+
545
645
  - **Input wrapper**: `h-10`, `px-3`, `border-b`
546
646
  - **List**: `max-h-[300px]` con scroll
547
647
  - **Empty state**: Centrado con padding vertical
@@ -15,6 +15,53 @@ El componente `DatePickerSelector` proporciona una interfaz intuitiva para selec
15
15
  - ✅ Integración con Combobox y Calendar
16
16
  - ✅ Props personalizables para Combobox y Calendar
17
17
  - ✅ Estado controlado con callback onChange
18
+ - ✅ Posicionamiento preciso del calendario mediante `virtualRef`
19
+
20
+ ## Arquitectura
21
+
22
+ ### Gestión de Dos Popovers Independientes
23
+
24
+ El componente maneja dos Popovers simultáneamente:
25
+
26
+ 1. **Popover del Combobox** - Muestra las opciones de rango (7/30/90 días, Personalizado)
27
+ 2. **Popover del Calendario** - Se abre al seleccionar "Personalizado", permite elegir fechas específicas
28
+
29
+ ### Posicionamiento con `virtualRef`
30
+
31
+ **Problema original:** Un componente no puede ser simultáneamente `PopoverTrigger` (para su propio popover) y `PopoverAnchor` (para otro popover).
32
+
33
+ **Solución implementada:**
34
+
35
+ ```tsx
36
+ // 1. El Combobox recibe un ref que apunta a su botón trigger
37
+ <Combobox
38
+ ref={(node) => { comboboxRef.current = node; }}
39
+ // ...
40
+ />
41
+
42
+ // 2. El Popover del calendario usa virtualRef para posicionarse
43
+ <Popover open={calendarOpen}>
44
+ <PopoverAnchor virtualRef={comboboxRef} />
45
+ <PopoverContent>
46
+ <Calendar />
47
+ </PopoverContent>
48
+ </Popover>
49
+ ```
50
+
51
+ **Ventajas de este enfoque:**
52
+
53
+ - ✅ Sin elementos wrapper innecesarios en el DOM
54
+ - ✅ Referencia directa al botón trigger del Combobox
55
+ - ✅ Posicionamiento preciso del calendario
56
+ - ✅ El Combobox funciona como trigger y como ancla simultáneamente
57
+ - ✅ Type-safe con el tipo `Measurable` (cualquier elemento con `getBoundingClientRect()`)
58
+
59
+ **Detalles técnicos:**
60
+
61
+ - `comboboxRef` tiene tipo `RefObject<Measurable | null>` para compatibilidad con Radix UI
62
+ - El callback ref captura el elemento button cuando se monta
63
+ - Se usa type assertion `as React.RefObject<Measurable>` para satisfacer el tipo de Radix
64
+ - El calendario siempre se posiciona correctamente porque el ref está poblado al abrir el popover
18
65
 
19
66
  ## Importación
20
67
 
@@ -47,13 +94,15 @@ function MyComponent() {
47
94
 
48
95
  ## Props
49
96
 
50
- | Prop | Tipo | Default | Descripción |
51
- | ------------------ | ------------------------ | ----------- | -------------------------------------------------------------- |
52
- | `dateRange` | `DateRange` | - | **Requerido**. Rango de fechas actual |
53
- | `onDateRangeChange`| `(dateRange: DateRange) => void` | - | **Requerido**. Callback al cambiar el rango |
54
- | `labels` | `DatePickerSelectorLabels` | - | Labels personalizables para todos los textos de UI |
55
- | `combobox` | `ComboboxProps` | - | Props adicionales para el componente Combobox |
56
- | `calendar` | `CalendarProps` | - | Props adicionales para el componente Calendar |
97
+ | Prop | Tipo | Default | Descripción |
98
+ | ------------------- | -------------------------------- | ------- | -------------------------------------------------- |
99
+ | `dateRange` | `DateRange` | - | **Requerido**. Rango de fechas actual |
100
+ | `onDateRangeChange` | `(dateRange: DateRange) => void` | - | **Requerido**. Callback al cambiar el rango |
101
+ | `labels` | `DatePickerSelectorLabels` | - | Labels personalizables para todos los textos de UI |
102
+ | `combobox` | `ComboboxProps` | - | Props adicionales para el componente Combobox |
103
+ | `calendar` | `CalendarProps` | - | Props adicionales para el componente Calendar |
104
+ | `aria-invalid` | `"true" \| "false" \| boolean` | - | Indica estado inválido (borde rojo) |
105
+ | `disabled` | `boolean` | `false` | Deshabilita el selector de fechas |
57
106
 
58
107
  ### DateRange Type
59
108
 
@@ -68,13 +117,13 @@ type DateRange = {
68
117
 
69
118
  ```typescript
70
119
  type DatePickerSelectorLabels = {
71
- last30Days?: string; // Default: "Last 30 days"
72
- last7Days?: string; // Default: "Last 7 days"
73
- last90Days?: string; // Default: "Last 90 days"
74
- custom?: string; // Default: "Custom"
75
- placeholder?: string; // Default: "Date"
76
- cancel?: string; // Default: "Cancel"
77
- apply?: string; // Default: "Apply"
120
+ last30Days?: string; // Default: "Last 30 days"
121
+ last7Days?: string; // Default: "Last 7 days"
122
+ last90Days?: string; // Default: "Last 90 days"
123
+ custom?: string; // Default: "Custom"
124
+ placeholder?: string; // Default: "Date"
125
+ cancel?: string; // Default: "Cancel"
126
+ apply?: string; // Default: "Apply"
78
127
  };
79
128
  ```
80
129
 
@@ -159,7 +208,10 @@ function MyComponent() {
159
208
 
160
209
  const formatDateRange = () => {
161
210
  if (!dateRange.from || !dateRange.to) return "No date selected";
162
- return `${format(dateRange.from, "MMM d, yyyy")} - ${format(dateRange.to, "MMM d, yyyy")}`;
211
+ return `${format(dateRange.from, "MMM d, yyyy")} - ${format(
212
+ dateRange.to,
213
+ "MMM d, yyyy"
214
+ )}`;
163
215
  };
164
216
 
165
217
  return (
@@ -208,6 +260,61 @@ function MyComponent() {
208
260
  }
209
261
  ```
210
262
 
263
+ ### Estado Inválido
264
+
265
+ ```tsx
266
+ function MyComponent() {
267
+ const [dateRange, setDateRange] = useState<DateRange>({
268
+ from: undefined,
269
+ to: undefined,
270
+ });
271
+
272
+ const isInvalid = !dateRange.from || !dateRange.to;
273
+
274
+ return (
275
+ <div className="space-y-2">
276
+ <DatePickerSelector
277
+ dateRange={dateRange}
278
+ onDateRangeChange={setDateRange}
279
+ aria-invalid={isInvalid}
280
+ labels={{
281
+ placeholder: "Date (Required)",
282
+ }}
283
+ />
284
+ {isInvalid && (
285
+ <p className="text-sm text-destructive">
286
+ Please select a date range to continue
287
+ </p>
288
+ )}
289
+ </div>
290
+ );
291
+ }
292
+ ```
293
+
294
+ **Estilos**: Cuando `aria-invalid="true"`, el combobox muestra borde rojo (heredado del componente Button).
295
+
296
+ ### Estado Deshabilitado
297
+
298
+ ```tsx
299
+ // Usando disabled
300
+ <DatePickerSelector
301
+ dateRange={dateRange}
302
+ onDateRangeChange={setDateRange}
303
+ disabled
304
+ />
305
+ ```
306
+
307
+ ### En Fieldset Disabled
308
+
309
+ ```tsx
310
+ <fieldset disabled>
311
+ <legend>Date Range (Disabled)</legend>
312
+ <DatePickerSelector dateRange={dateRange} onDateRangeChange={setDateRange} />
313
+ </fieldset>
314
+ ```
315
+
316
+ El selector de fechas dentro de un `<fieldset disabled>` automáticamente hereda el estado disabled del navegador.
317
+
211
318
  ## Comportamiento
212
319
 
213
320
  ### Rangos Predefinidos
@@ -219,6 +326,7 @@ El componente ofrece tres rangos predefinidos:
219
326
  - **Last 90 days**: Desde hace 90 días hasta hoy
220
327
 
221
328
  Todos los rangos predefinidos:
329
+
222
330
  - Usan `date-fns` para cálculos precisos
223
331
  - Establecen las fechas a medianoche (00:00:00)
224
332
  - Se calculan dinámicamente basándose en la fecha actual
@@ -226,10 +334,19 @@ Todos los rangos predefinidos:
226
334
  ### Rango Personalizado
227
335
 
228
336
  Al seleccionar la opción "Custom":
337
+
229
338
  1. Se abre un popover con un calendario
230
- 2. El usuario selecciona un rango de fechas
339
+ 2. El usuario selecciona un rango de fechas (from y to)
231
340
  3. Los botones "Cancel" y "Apply" permiten confirmar o descartar la selección
232
- 4. El rango personalizado se mantiene independiente de los rangos predefinidos
341
+
342
+ **Comportamiento de cancelación:**
343
+
344
+ - **Botón Cancel**: Restaura la selección anterior y cierra el calendario
345
+ - **Botón Apply sin rango completo**: Si no se seleccionó un rango completo (ambas fechas), restaura la selección anterior
346
+ - **Botón Apply con rango completo**: Aplica el rango personalizado y mantiene "Custom" seleccionado
347
+ - **Click fuera del popover**: Restaura automáticamente la selección anterior si se cierra sin aplicar
348
+
349
+ Esto asegura que el usuario nunca quede con "Custom" seleccionado sin haber completado la selección de un rango válido.
233
350
 
234
351
  ### Separación de Estados
235
352
 
@@ -243,7 +360,7 @@ Al seleccionar la opción "Custom":
243
360
  **Dashboards**: Filtrar datos por períodos de tiempo
244
361
  **Analytics**: Comparar métricas en diferentes rangos
245
362
  **Búsquedas**: Filtrar resultados por rango de fechas
246
- **Logs**: Visualizar registros en períodos específicos
363
+ **Logs**: Visualizar registros en períodos específicos
247
364
 
248
365
  ## Dependencias
249
366
 
@@ -282,10 +399,7 @@ const [dateRange, setDateRange] = useState<DateRange>({
282
399
  to: undefined,
283
400
  });
284
401
 
285
- <DatePickerSelector
286
- dateRange={dateRange}
287
- onDateRangeChange={setDateRange}
288
- />
402
+ <DatePickerSelector dateRange={dateRange} onDateRangeChange={setDateRange} />;
289
403
 
290
404
  // ❌ Incorrecto - Estado no inicializado
291
405
  const [dateRange, setDateRange] = useState();
@@ -303,9 +417,11 @@ const handleChange = (range: DateRange) => {
303
417
  };
304
418
 
305
419
  // ✅ Correcto - Mostrar feedback de validación
306
- {!dateRange.from || !dateRange.to ? (
307
- <p className="text-destructive">Please select both dates</p>
308
- ) : null}
420
+ {
421
+ !dateRange.from || !dateRange.to ? (
422
+ <p className="text-destructive">Please select both dates</p>
423
+ ) : null;
424
+ }
309
425
  ```
310
426
 
311
427
  ### Formateo de Fechas
@@ -314,7 +430,7 @@ const handleChange = (range: DateRange) => {
314
430
  import { format } from "date-fns";
315
431
 
316
432
  // ✅ Correcto - Formatear con date-fns
317
- const displayDate = dateRange.from
433
+ const displayDate = dateRange.from
318
434
  ? format(dateRange.from, "PP")
319
435
  : "Not selected";
320
436
 
@@ -366,9 +482,12 @@ const normalizedRange = {
366
482
  **Problema**: Los rangos predefinidos no funcionan
367
483
  **Solución**: Asegúrate de que `onDateRangeChange` esté actualizando el estado correctamente.
368
484
 
485
+ **Problema**: El calendario se posiciona incorrectamente
486
+ **Solución**: Este problema fue resuelto en la versión actual usando `virtualRef`. Si usas una versión antigua, actualiza el componente. El calendario ahora se posiciona automáticamente respecto al Combobox usando referencias directas al botón trigger.
487
+
369
488
  ## Referencias
370
489
 
371
490
  - **Combobox**: [Ver documentación](./combobox.md)
372
491
  - **Calendar**: [Ver documentación](./calendar.md)
373
- - **date-fns**: https://date-fns.org/
374
- - **react-day-picker**: https://react-day-picker.js.org/
492
+ - **date-fns**: <https://date-fns.org/>
493
+ - **react-day-picker**: <https://react-day-picker.js.org/>