@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.
- package/dist/combobox-DGuQtXjP.js +608 -0
- package/dist/combobox-hTCtPMUL.cjs +40 -0
- package/dist/combobox.cjs +1 -1
- package/dist/combobox.js +1 -1
- package/dist/components/ui/combobox/combobox.d.ts +7 -1
- package/dist/components/ui/date-picker-selector/date-picker-selector.d.ts +75 -1
- package/dist/components/ui/file-upload/file-upload.d.ts +3 -1
- package/dist/components/ui/slider/slider.d.ts +1 -1
- package/dist/components/ui/switch/switch.d.ts +1 -1
- package/dist/date-picker-selector.cjs +1 -1
- package/dist/date-picker-selector.js +81 -78
- package/dist/field.cjs +2 -2
- package/dist/field.js +2 -2
- package/dist/file-upload.cjs +5 -3
- package/dist/file-upload.js +178 -149
- package/dist/slider.cjs +5 -5
- package/dist/slider.js +196 -177
- package/dist/styles.css +1 -1
- package/dist/switch.cjs +3 -3
- package/dist/switch.js +105 -85
- package/docs/components/ui/accordion-rounded.md +2 -6
- package/docs/components/ui/avatar.md +3 -1
- package/docs/components/ui/button.md +22 -16
- package/docs/components/ui/card.md +49 -31
- package/docs/components/ui/checkbox.md +44 -8
- package/docs/components/ui/combobox.md +100 -0
- package/docs/components/ui/date-picker-selector.md +147 -28
- package/docs/components/ui/file-upload.md +241 -94
- package/docs/components/ui/icon.md +1 -1
- package/docs/components/ui/input.md +4 -1
- package/docs/components/ui/radio-group.md +1 -1
- package/docs/components/ui/select.md +51 -34
- package/docs/components/ui/sheet.md +3 -9
- package/docs/components/ui/slider.md +120 -99
- package/docs/components/ui/switch.md +408 -408
- package/docs/components/ui/textarea.md +37 -22
- package/docs/components/ui/tooltip.md +5 -2
- package/docs/components/ui/typography.md +63 -39
- package/package.json +1 -1
- package/dist/combobox-B8HMlZy6.js +0 -601
- 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
|
|
51
|
-
|
|
|
52
|
-
| `dateRange`
|
|
53
|
-
| `onDateRangeChange
|
|
54
|
-
| `labels`
|
|
55
|
-
| `combobox`
|
|
56
|
-
| `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;
|
|
72
|
-
last7Days?: string;
|
|
73
|
-
last90Days?: string;
|
|
74
|
-
custom?: string;
|
|
75
|
-
placeholder?: string;
|
|
76
|
-
cancel?: string;
|
|
77
|
-
apply?: string;
|
|
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(
|
|
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
|
-
|
|
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
|
-
{
|
|
307
|
-
|
|
308
|
-
|
|
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/>
|