@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
|
@@ -17,6 +17,9 @@ Componente de carga de archivos con funcionalidad **drag & drop**, validación d
|
|
|
17
17
|
- ✅ Límite de cantidad de archivos en modo múltiple
|
|
18
18
|
- ✅ Etiquetas personalizables (i18n)
|
|
19
19
|
- ✅ Estado de arrastre visual (borde y fondo cambian)
|
|
20
|
+
- ✅ Estados de validación con `aria-invalid`
|
|
21
|
+
- ✅ Estados deshabilitados con `disabled`
|
|
22
|
+
- ✅ Detección automática de fieldset disabled
|
|
20
23
|
- ✅ Integración con formularios React
|
|
21
24
|
- ✅ TypeScript completo con tipos exportados
|
|
22
25
|
|
|
@@ -24,7 +27,10 @@ Componente de carga de archivos con funcionalidad **drag & drop**, validación d
|
|
|
24
27
|
|
|
25
28
|
```typescript
|
|
26
29
|
import { FileUpload } from "@adamosuiteservices/ui/file-upload";
|
|
27
|
-
import type {
|
|
30
|
+
import type {
|
|
31
|
+
FileUploadProps,
|
|
32
|
+
FileUploadLabels,
|
|
33
|
+
} from "@adamosuiteservices/ui/file-upload";
|
|
28
34
|
```
|
|
29
35
|
|
|
30
36
|
## Uso Básico
|
|
@@ -38,12 +44,7 @@ import { FileUpload } from "@adamosuiteservices/ui/file-upload";
|
|
|
38
44
|
function MyForm() {
|
|
39
45
|
const [file, setFile] = useState<File | null>(null);
|
|
40
46
|
|
|
41
|
-
return
|
|
42
|
-
<FileUpload
|
|
43
|
-
selectedFile={file}
|
|
44
|
-
onFileSelect={setFile}
|
|
45
|
-
/>
|
|
46
|
-
);
|
|
47
|
+
return <FileUpload selectedFile={file} onFileSelect={setFile} />;
|
|
47
48
|
}
|
|
48
49
|
```
|
|
49
50
|
|
|
@@ -56,17 +57,12 @@ import { FileUpload } from "@adamosuiteservices/ui/file-upload";
|
|
|
56
57
|
function MyForm() {
|
|
57
58
|
const [files, setFiles] = useState<File[]>([]);
|
|
58
59
|
|
|
59
|
-
return
|
|
60
|
-
<FileUpload
|
|
61
|
-
selectedFiles={files}
|
|
62
|
-
onFilesSelect={setFiles}
|
|
63
|
-
multiple
|
|
64
|
-
/>
|
|
65
|
-
);
|
|
60
|
+
return <FileUpload selectedFiles={files} onFilesSelect={setFiles} multiple />;
|
|
66
61
|
}
|
|
67
62
|
```
|
|
68
63
|
|
|
69
64
|
**Configuración por defecto**:
|
|
65
|
+
|
|
70
66
|
- Extensiones aceptadas: `.xls`, `.xlsx`, `.numbers`
|
|
71
67
|
- Tamaño máximo: 50 MB
|
|
72
68
|
- Máximo de archivos (modo múltiple): 10
|
|
@@ -101,7 +97,8 @@ acceptedExtensions?: string[]
|
|
|
101
97
|
Array de extensiones de archivo permitidas. Por defecto: `[".xls", ".xlsx", ".numbers"]`
|
|
102
98
|
|
|
103
99
|
**Ejemplo**:
|
|
104
|
-
|
|
100
|
+
|
|
101
|
+
````tsx
|
|
105
102
|
<FileUpload
|
|
106
103
|
selectedFile={file}
|
|
107
104
|
onFileSelect={setFile}
|
|
@@ -111,17 +108,14 @@ Array de extensiones de archivo permitidas. Por defecto: `[".xls", ".xlsx", ".nu
|
|
|
111
108
|
|
|
112
109
|
```tsx
|
|
113
110
|
maxSizeInMB?: number
|
|
114
|
-
|
|
111
|
+
````
|
|
115
112
|
|
|
116
113
|
Tamaño máximo del archivo en megabytes. Por defecto: `50`
|
|
117
114
|
|
|
118
115
|
**Ejemplo**:
|
|
116
|
+
|
|
119
117
|
```tsx
|
|
120
|
-
<FileUpload
|
|
121
|
-
selectedFile={file}
|
|
122
|
-
onFileSelect={setFile}
|
|
123
|
-
maxSizeInMB={10}
|
|
124
|
-
/>
|
|
118
|
+
<FileUpload selectedFile={file} onFileSelect={setFile} maxSizeInMB={10} />
|
|
125
119
|
```
|
|
126
120
|
|
|
127
121
|
#### labels
|
|
@@ -140,6 +134,7 @@ type FileUploadLabels = {
|
|
|
140
134
|
Etiquetas personalizables para internacionalización o textos específicos.
|
|
141
135
|
|
|
142
136
|
**Ejemplo**:
|
|
137
|
+
|
|
143
138
|
```tsx
|
|
144
139
|
<FileUpload
|
|
145
140
|
selectedFile={file}
|
|
@@ -147,20 +142,39 @@ Etiquetas personalizables para internacionalización o textos específicos.
|
|
|
147
142
|
labels={{
|
|
148
143
|
dragDrop: "Arrastra y suelta tu archivo aquí o",
|
|
149
144
|
selectFile: "Selecciona el archivo",
|
|
150
|
-
fileRequirements: "Archivos permitidos: .xls, .xlsx. Tamaño máximo 50 MB."
|
|
145
|
+
fileRequirements: "Archivos permitidos: .xls, .xlsx. Tamaño máximo 50 MB.",
|
|
151
146
|
}}
|
|
152
147
|
/>
|
|
153
148
|
```
|
|
154
149
|
|
|
150
|
+
#### aria-invalid
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
"aria-invalid"?: boolean
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Marca el componente como inválido, aplicando estilos destructivos. Útil para validación de formularios. Por defecto: `false`
|
|
157
|
+
|
|
158
|
+
**Ejemplo**:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<FileUpload
|
|
162
|
+
selectedFile={file}
|
|
163
|
+
onFileSelect={setFile}
|
|
164
|
+
aria-invalid={!!errorMessage}
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
155
168
|
#### invalid
|
|
156
169
|
|
|
157
170
|
```tsx
|
|
158
171
|
invalid?: boolean
|
|
159
172
|
```
|
|
160
173
|
|
|
161
|
-
|
|
174
|
+
Alternativa a `aria-invalid` para marcar el componente como inválido. Por defecto: `false`
|
|
162
175
|
|
|
163
176
|
**Ejemplo**:
|
|
177
|
+
|
|
164
178
|
```tsx
|
|
165
179
|
<FileUpload
|
|
166
180
|
selectedFile={file}
|
|
@@ -169,6 +183,22 @@ Marca el componente como inválido, aplicando estilos destructivos. Por defecto:
|
|
|
169
183
|
/>
|
|
170
184
|
```
|
|
171
185
|
|
|
186
|
+
#### disabled
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
disabled?: boolean
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Deshabilita el componente, impidiendo toda interacción del usuario. Por defecto: `false`
|
|
193
|
+
|
|
194
|
+
**Ejemplo**:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<FileUpload selectedFile={file} onFileSelect={setFile} disabled />
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Nota**: El componente también detecta automáticamente si está dentro de un `<fieldset disabled>` y se deshabilitará automáticamente.
|
|
201
|
+
|
|
172
202
|
#### onInvalidFile
|
|
173
203
|
|
|
174
204
|
```tsx
|
|
@@ -178,6 +208,7 @@ onInvalidFile?: (file: File, reason: "extension" | "size") => void
|
|
|
178
208
|
Callback invocado cuando un archivo no pasa la validación. Recibe el archivo y el motivo del rechazo.
|
|
179
209
|
|
|
180
210
|
**Ejemplo**:
|
|
211
|
+
|
|
181
212
|
```tsx
|
|
182
213
|
const handleInvalidFile = (file: File, reason: "extension" | "size") => {
|
|
183
214
|
if (reason === "extension") {
|
|
@@ -192,7 +223,7 @@ const handleInvalidFile = (file: File, reason: "extension" | "size") => {
|
|
|
192
223
|
onFileSelect={setFile}
|
|
193
224
|
onInvalidFile={handleInvalidFile}
|
|
194
225
|
invalid={!!error}
|
|
195
|
-
|
|
226
|
+
/>;
|
|
196
227
|
```
|
|
197
228
|
|
|
198
229
|
### Modo Múltiple
|
|
@@ -236,10 +267,12 @@ filesPosition?: "above" | "below"
|
|
|
236
267
|
```
|
|
237
268
|
|
|
238
269
|
Posición donde aparecen los archivos seleccionados en modo múltiple:
|
|
270
|
+
|
|
239
271
|
- `"above"` - Archivos arriba del área de drag & drop
|
|
240
272
|
- `"below"` - Archivos debajo del área de drag & drop (por defecto)
|
|
241
273
|
|
|
242
274
|
**Ejemplo**:
|
|
275
|
+
|
|
243
276
|
```tsx
|
|
244
277
|
<FileUpload
|
|
245
278
|
selectedFiles={files}
|
|
@@ -265,7 +298,8 @@ type FileUploadLabels = {
|
|
|
265
298
|
Etiquetas personalizables para internacionalización o textos específicos.
|
|
266
299
|
|
|
267
300
|
**Ejemplo**:
|
|
268
|
-
|
|
301
|
+
|
|
302
|
+
````tsx
|
|
269
303
|
<FileUpload
|
|
270
304
|
selectedFile={file}
|
|
271
305
|
onFileSelect={setFile}
|
|
@@ -291,11 +325,12 @@ Etiquetas personalizables para internacionalización o textos específicos.
|
|
|
291
325
|
|
|
292
326
|
```tsx
|
|
293
327
|
acceptedExtensions?: string[]
|
|
294
|
-
|
|
328
|
+
````
|
|
295
329
|
|
|
296
330
|
Array de extensiones de archivo permitidas. Por defecto: `[".xls", ".xlsx", ".numbers"]`
|
|
297
331
|
|
|
298
332
|
**Ejemplo**:
|
|
333
|
+
|
|
299
334
|
```tsx
|
|
300
335
|
<FileUpload
|
|
301
336
|
selectedFile={file}
|
|
@@ -313,12 +348,9 @@ maxSizeInMB?: number
|
|
|
313
348
|
Tamaño máximo del archivo en megabytes. Por defecto: `50`
|
|
314
349
|
|
|
315
350
|
**Ejemplo**:
|
|
351
|
+
|
|
316
352
|
```tsx
|
|
317
|
-
<FileUpload
|
|
318
|
-
selectedFile={file}
|
|
319
|
-
onFileSelect={setFile}
|
|
320
|
-
maxSizeInMB={10}
|
|
321
|
-
/>
|
|
353
|
+
<FileUpload selectedFile={file} onFileSelect={setFile} maxSizeInMB={10} />
|
|
322
354
|
```
|
|
323
355
|
|
|
324
356
|
### labels
|
|
@@ -336,6 +368,7 @@ type FileUploadLabels = {
|
|
|
336
368
|
Etiquetas personalizables para internacionalización o textos específicos.
|
|
337
369
|
|
|
338
370
|
**Ejemplo**:
|
|
371
|
+
|
|
339
372
|
```tsx
|
|
340
373
|
<FileUpload
|
|
341
374
|
selectedFile={file}
|
|
@@ -343,16 +376,127 @@ Etiquetas personalizables para internacionalización o textos específicos.
|
|
|
343
376
|
labels={{
|
|
344
377
|
dragDrop: "Arrastra y suelta tu archivo aquí o",
|
|
345
378
|
selectFile: "Selecciona el archivo",
|
|
346
|
-
fileRequirements: "Archivos permitidos: .xls, .xlsx. Tamaño máximo 50 MB."
|
|
379
|
+
fileRequirements: "Archivos permitidos: .xls, .xlsx. Tamaño máximo 50 MB.",
|
|
347
380
|
}}
|
|
348
381
|
/>
|
|
349
382
|
```
|
|
350
383
|
|
|
351
384
|
## Ejemplos
|
|
352
385
|
|
|
353
|
-
###
|
|
386
|
+
### Con Componentes Field
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
import {
|
|
390
|
+
Field,
|
|
391
|
+
FieldLabel,
|
|
392
|
+
FieldDescription,
|
|
393
|
+
FieldError,
|
|
394
|
+
FieldGroup,
|
|
395
|
+
} from "@adamosuiteservices/ui/field";
|
|
396
|
+
import { FileUpload } from "@adamosuiteservices/ui/file-upload";
|
|
397
|
+
|
|
398
|
+
function FileUploadForm() {
|
|
399
|
+
const [file, setFile] = useState<File | null>(null);
|
|
400
|
+
const [error, setError] = useState<string>("");
|
|
401
|
+
|
|
402
|
+
const handleInvalidFile = (file: File, reason: "extension" | "size") => {
|
|
403
|
+
if (reason === "extension") {
|
|
404
|
+
setError(`File "${file.name}" has an invalid extension.`);
|
|
405
|
+
} else if (reason === "size") {
|
|
406
|
+
setError(`File "${file.name}" exceeds the maximum size limit.`);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<FieldGroup className="adm:max-w-xl">
|
|
412
|
+
<Field>
|
|
413
|
+
<FieldLabel>Upload document (PDF or DOCX only, max 2MB)</FieldLabel>
|
|
414
|
+
<FileUpload
|
|
415
|
+
selectedFile={file}
|
|
416
|
+
onFileSelect={(newFile) => {
|
|
417
|
+
setFile(newFile);
|
|
418
|
+
if (newFile) setError("");
|
|
419
|
+
}}
|
|
420
|
+
onInvalidFile={handleInvalidFile}
|
|
421
|
+
aria-invalid={!!error}
|
|
422
|
+
acceptedExtensions={[".pdf", ".docx"]}
|
|
423
|
+
maxSizeInMB={2}
|
|
424
|
+
/>
|
|
425
|
+
{error ? (
|
|
426
|
+
<FieldError>{error}</FieldError>
|
|
427
|
+
) : (
|
|
428
|
+
<FieldDescription>
|
|
429
|
+
Supported formats: PDF, DOCX. Maximum 2MB.
|
|
430
|
+
</FieldDescription>
|
|
431
|
+
)}
|
|
432
|
+
</Field>
|
|
433
|
+
</FieldGroup>
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Estado Deshabilitado
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
import {
|
|
442
|
+
Field,
|
|
443
|
+
FieldLabel,
|
|
444
|
+
FieldDescription,
|
|
445
|
+
FieldGroup,
|
|
446
|
+
} from "@adamosuiteservices/ui/field";
|
|
447
|
+
|
|
448
|
+
function DisabledExample() {
|
|
449
|
+
const [file, setFile] = useState<File | null>(null);
|
|
450
|
+
|
|
451
|
+
return (
|
|
452
|
+
<FieldGroup className="adm:max-w-xl">
|
|
453
|
+
<Field>
|
|
454
|
+
<FieldLabel>Upload document (Disabled)</FieldLabel>
|
|
455
|
+
<FileUpload selectedFile={file} onFileSelect={setFile} disabled />
|
|
456
|
+
<FieldDescription>This file upload is disabled</FieldDescription>
|
|
457
|
+
</Field>
|
|
458
|
+
</FieldGroup>
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Dentro de Fieldset Disabled
|
|
354
464
|
|
|
355
465
|
```tsx
|
|
466
|
+
import {
|
|
467
|
+
FieldSet,
|
|
468
|
+
FieldLegend,
|
|
469
|
+
FieldGroup,
|
|
470
|
+
Field,
|
|
471
|
+
FieldLabel,
|
|
472
|
+
} from "@adamosuiteservices/ui/field";
|
|
473
|
+
|
|
474
|
+
function FieldsetExample() {
|
|
475
|
+
const [file, setFile] = useState<File | null>(null);
|
|
476
|
+
|
|
477
|
+
return (
|
|
478
|
+
<FieldSet disabled>
|
|
479
|
+
<FieldLegend>Document upload (Fieldset disabled)</FieldLegend>
|
|
480
|
+
<FieldGroup>
|
|
481
|
+
<Field>
|
|
482
|
+
<FieldLabel>Upload document</FieldLabel>
|
|
483
|
+
<FileUpload
|
|
484
|
+
selectedFile={file}
|
|
485
|
+
onFileSelect={setFile}
|
|
486
|
+
className="adm:max-w-xl"
|
|
487
|
+
/>
|
|
488
|
+
</Field>
|
|
489
|
+
</FieldGroup>
|
|
490
|
+
</FieldSet>
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Nota**: El componente detecta automáticamente cuando está dentro de un `<fieldset disabled>` o `<FieldSet disabled>` y se deshabilita automáticamente.
|
|
496
|
+
|
|
497
|
+
### Archivos de Excel (Por Defecto)
|
|
498
|
+
|
|
499
|
+
````tsx
|
|
356
500
|
const [file, setFile] = useState<File | null>(null);
|
|
357
501
|
|
|
358
502
|
<FileUpload
|
|
@@ -372,7 +516,7 @@ const [files, setFiles] = useState<File[]>([]);
|
|
|
372
516
|
selectFile: "Select files"
|
|
373
517
|
}}
|
|
374
518
|
/>
|
|
375
|
-
|
|
519
|
+
````
|
|
376
520
|
|
|
377
521
|
### Múltiples Imágenes
|
|
378
522
|
|
|
@@ -389,10 +533,12 @@ const [images, setImages] = useState<File[]>([]);
|
|
|
389
533
|
labels={{
|
|
390
534
|
dragDrop: "Drag and drop your images here or",
|
|
391
535
|
selectFile: "Select images",
|
|
392
|
-
fileRequirements:
|
|
393
|
-
|
|
536
|
+
fileRequirements:
|
|
537
|
+
"Supported formats: JPG, PNG, GIF, WebP. Max 5MB each. Up to 10 images.",
|
|
538
|
+
filesSelected: (count) =>
|
|
539
|
+
`${count} image${count !== 1 ? "s" : ""} selected`,
|
|
394
540
|
}}
|
|
395
|
-
|
|
541
|
+
/>;
|
|
396
542
|
```
|
|
397
543
|
|
|
398
544
|
### Archivos Arriba del Área de Drop
|
|
@@ -407,14 +553,14 @@ const [files, setFiles] = useState<File[]>([]);
|
|
|
407
553
|
filesPosition="above"
|
|
408
554
|
acceptedExtensions={[".pdf"]}
|
|
409
555
|
maxSizeInMB={10}
|
|
410
|
-
|
|
556
|
+
/>;
|
|
411
557
|
```
|
|
558
|
+
|
|
412
559
|
/>
|
|
413
|
-
```
|
|
414
560
|
|
|
415
561
|
### Imágenes
|
|
416
562
|
|
|
417
|
-
|
|
563
|
+
````tsx
|
|
418
564
|
const [image, setImage] = useState<File | null>(null);
|
|
419
565
|
|
|
420
566
|
## Estados Visuales
|
|
@@ -460,16 +606,6 @@ Cuando `invalid={true}`:
|
|
|
460
606
|
- Contador de archivos: `text-destructive`
|
|
461
607
|
- Tarjetas de archivos: borde y fondo con colores destructivos
|
|
462
608
|
|
|
463
|
-
## Validacióne={document}
|
|
464
|
-
onFileSelect={setDocument}
|
|
465
|
-
acceptedExtensions={[".pdf"]}
|
|
466
|
-
maxSizeInMB={20}
|
|
467
|
-
labels={{
|
|
468
|
-
dragDrop: "Drag and drop your PDF here or",
|
|
469
|
-
selectFile: "Select PDF",
|
|
470
|
-
fileRequirements: "Only PDF files. Maximum 20 MB."
|
|
471
|
-
}}
|
|
472
|
-
/>
|
|
473
609
|
## Validación
|
|
474
610
|
|
|
475
611
|
El componente valida automáticamente:
|
|
@@ -518,7 +654,7 @@ const handleInvalidFile = (file: File, reason: "extension" | "size") => {
|
|
|
518
654
|
</Typography>
|
|
519
655
|
</div>
|
|
520
656
|
)}
|
|
521
|
-
|
|
657
|
+
````
|
|
522
658
|
|
|
523
659
|
## Comportamiento por Modo
|
|
524
660
|
|
|
@@ -558,9 +694,9 @@ const [archive, setArchive] = useState<File | null>(null);
|
|
|
558
694
|
maxSizeInMB={500}
|
|
559
695
|
labels={{
|
|
560
696
|
dragDrop: "Drag and drop your archive here or",
|
|
561
|
-
selectFile: "Select the archive"
|
|
697
|
+
selectFile: "Select the archive",
|
|
562
698
|
}}
|
|
563
|
-
|
|
699
|
+
/>;
|
|
564
700
|
```
|
|
565
701
|
|
|
566
702
|
### Con Integración en Formulario
|
|
@@ -571,7 +707,7 @@ function UploadForm() {
|
|
|
571
707
|
|
|
572
708
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
573
709
|
e.preventDefault();
|
|
574
|
-
|
|
710
|
+
|
|
575
711
|
if (!file) return;
|
|
576
712
|
|
|
577
713
|
const formData = new FormData();
|
|
@@ -582,7 +718,7 @@ function UploadForm() {
|
|
|
582
718
|
method: "POST",
|
|
583
719
|
body: formData
|
|
584
720
|
});
|
|
585
|
-
|
|
721
|
+
|
|
586
722
|
### Botón de Remover
|
|
587
723
|
|
|
588
724
|
- Variante: `destructive-medium`
|
|
@@ -617,6 +753,7 @@ function UploadForm() {
|
|
|
617
753
|
### Sin Archivo Seleccionado
|
|
618
754
|
|
|
619
755
|
El componente muestra:
|
|
756
|
+
|
|
620
757
|
- Área de drag & drop con borde punteado
|
|
621
758
|
- Icono de documento en fondo azul claro
|
|
622
759
|
- Texto instructivo y link para seleccionar archivo
|
|
@@ -625,6 +762,7 @@ El componente muestra:
|
|
|
625
762
|
### Durante Drag Over
|
|
626
763
|
|
|
627
764
|
Cuando el usuario arrastra un archivo sobre el área:
|
|
765
|
+
|
|
628
766
|
- Borde cambia a `border-primary-500`
|
|
629
767
|
- Fondo cambia a `bg-primary-50`
|
|
630
768
|
- Transición suave con `transition-colors`
|
|
@@ -632,6 +770,7 @@ Cuando el usuario arrastra un archivo sobre el área:
|
|
|
632
770
|
### Con Archivo Seleccionado
|
|
633
771
|
|
|
634
772
|
El componente muestra:
|
|
773
|
+
|
|
635
774
|
- Tarjeta con fondo `bg-neutral-100`
|
|
636
775
|
- Icono de documento en fondo blanco
|
|
637
776
|
- Nombre del archivo (con truncate si es largo)
|
|
@@ -687,9 +826,10 @@ border-primary-500 bg-primary-50
|
|
|
687
826
|
- Label asociado correctamente con `htmlFor="file-upload"`
|
|
688
827
|
- Botón de link usa `asChild` para comportamiento semántico
|
|
689
828
|
- Typography con color `muted` para textos secundarios
|
|
829
|
+
|
|
690
830
|
## Tipos TypeScript
|
|
691
831
|
|
|
692
|
-
|
|
832
|
+
````typescript
|
|
693
833
|
export type FileUploadLabels = {
|
|
694
834
|
dragDrop?: string
|
|
695
835
|
selectFile?: string
|
|
@@ -701,22 +841,27 @@ export type FileUploadProps = ComponentProps<"div"> & Readonly<{
|
|
|
701
841
|
// Modo simple
|
|
702
842
|
selectedFile?: File | null
|
|
703
843
|
onFileSelect?: (file: File | null) => void
|
|
704
|
-
|
|
844
|
+
|
|
705
845
|
// Modo múltiple
|
|
706
846
|
selectedFiles?: File[]
|
|
707
847
|
onFilesSelect?: (files: File[]) => void
|
|
708
848
|
multiple?: boolean
|
|
709
849
|
maxFiles?: number
|
|
710
850
|
filesPosition?: "above" | "below"
|
|
711
|
-
|
|
851
|
+
|
|
712
852
|
// Validación
|
|
713
853
|
onInvalidFile?: (file: File, reason: "extension" | "size") => void
|
|
714
854
|
invalid?: boolean
|
|
715
|
-
|
|
855
|
+
"aria-invalid"?: boolean
|
|
856
|
+
|
|
857
|
+
// Estados
|
|
858
|
+
disabled?: boolean
|
|
859
|
+
|
|
716
860
|
// Común
|
|
717
861
|
acceptedExtensions?: string[]
|
|
718
862
|
maxSizeInMB?: number
|
|
719
863
|
labels?: FileUploadLabels
|
|
864
|
+
}>
|
|
720
865
|
### FormData para Upload
|
|
721
866
|
|
|
722
867
|
```tsx
|
|
@@ -750,7 +895,8 @@ const uploadFiles = async (files: File[]) => {
|
|
|
750
895
|
|
|
751
896
|
return response.json();
|
|
752
897
|
};
|
|
753
|
-
|
|
898
|
+
````
|
|
899
|
+
|
|
754
900
|
```tsx
|
|
755
901
|
// No validar el archivo antes de hacer upload
|
|
756
902
|
// Siempre verificar que file no sea null
|
|
@@ -769,18 +915,19 @@ labels={{ selectFile: "Select file" }} // Poco descriptivo
|
|
|
769
915
|
|
|
770
916
|
```typescript
|
|
771
917
|
export type FileUploadLabels = {
|
|
772
|
-
dragDrop?: string
|
|
773
|
-
selectFile?: string
|
|
774
|
-
fileRequirements?: string
|
|
918
|
+
dragDrop?: string;
|
|
919
|
+
selectFile?: string;
|
|
920
|
+
fileRequirements?: string;
|
|
775
921
|
};
|
|
776
922
|
|
|
777
|
-
export type FileUploadProps = ComponentProps<"div"> &
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
923
|
+
export type FileUploadProps = ComponentProps<"div"> &
|
|
924
|
+
Readonly<{
|
|
925
|
+
selectedFile: File | null;
|
|
926
|
+
onFileSelect: (file: File | null) => void;
|
|
927
|
+
acceptedExtensions?: string[];
|
|
928
|
+
maxSizeInMB?: number;
|
|
929
|
+
labels?: FileUploadLabels;
|
|
930
|
+
}>;
|
|
784
931
|
```
|
|
785
932
|
|
|
786
933
|
## Integración con Backend
|
|
@@ -791,13 +938,16 @@ export type FileUploadProps = ComponentProps<"div"> & Readonly<{
|
|
|
791
938
|
const uploadFile = async (file: File) => {
|
|
792
939
|
const formData = new FormData();
|
|
793
940
|
formData.append("file", file);
|
|
794
|
-
formData.append(
|
|
795
|
-
|
|
796
|
-
|
|
941
|
+
formData.append(
|
|
942
|
+
"metadata",
|
|
943
|
+
JSON.stringify({
|
|
944
|
+
uploadedAt: new Date().toISOString(),
|
|
945
|
+
})
|
|
946
|
+
);
|
|
797
947
|
|
|
798
948
|
const response = await fetch("/api/upload", {
|
|
799
949
|
method: "POST",
|
|
800
|
-
body: formData
|
|
950
|
+
body: formData,
|
|
801
951
|
});
|
|
802
952
|
|
|
803
953
|
return response.json();
|
|
@@ -806,7 +956,7 @@ const uploadFile = async (file: File) => {
|
|
|
806
956
|
|
|
807
957
|
### Con Progress Tracking
|
|
808
958
|
|
|
809
|
-
|
|
959
|
+
````tsx
|
|
810
960
|
const [progress, setProgress] = useState(0);
|
|
811
961
|
|
|
812
962
|
const uploadWithProgress = async (file: File) => {
|
|
@@ -826,6 +976,8 @@ const uploadWithProgress = async (file: File) => {
|
|
|
826
976
|
| Posición de preview | ❌ No configurable | ✅ Arriba o abajo |
|
|
827
977
|
| UX consistente | ❌ Varía por browser | ✅ Consistente |
|
|
828
978
|
| Labels customizables | ❌ No | ✅ Sí (i18n friendly) |
|
|
979
|
+
| aria-invalid | ⚠️ Manual | ✅ Integrado |
|
|
980
|
+
| Fieldset disabled | ⚠️ Nativo pero sin estilos | ✅ Detectado automáticamente |
|
|
829
981
|
|
|
830
982
|
## Notas
|
|
831
983
|
|
|
@@ -836,42 +988,37 @@ const uploadWithProgress = async (file: File) => {
|
|
|
836
988
|
- Los archivos no se almacenan automáticamente - manejar el upload con los callbacks
|
|
837
989
|
- El componente es completamente controlado - el padre maneja el estado de los archivos
|
|
838
990
|
- El input file es reutilizable: después de seleccionar, se resetea para permitir seleccionar el mismo archivo de nuevo
|
|
991
|
+
- **Detección automática de fieldset**: El componente detecta cuando está dentro de un `<fieldset disabled>` usando MutationObserver y se deshabilita automáticamente
|
|
992
|
+
- Usar con los componentes Field (`Field`, `FieldLabel`, `FieldError`, etc.) para una mejor experiencia de formulario
|
|
839
993
|
```tsx
|
|
840
994
|
<FileUpload
|
|
841
995
|
selectedFile={file}
|
|
842
996
|
onFileSelect={setFile}
|
|
843
997
|
className="adm:max-w-2xl adm:mx-auto"
|
|
844
998
|
/>
|
|
845
|
-
|
|
999
|
+
````
|
|
846
1000
|
|
|
847
1001
|
### Con Wrapper Adicional
|
|
848
1002
|
|
|
849
1003
|
```tsx
|
|
850
1004
|
<div className="adm:space-y-4">
|
|
851
1005
|
<Label>Upload your document</Label>
|
|
852
|
-
<FileUpload
|
|
853
|
-
|
|
854
|
-
onFileSelect={setFile}
|
|
855
|
-
/>
|
|
856
|
-
{file && (
|
|
857
|
-
<Typography color="success">
|
|
858
|
-
✓ File ready to upload
|
|
859
|
-
</Typography>
|
|
860
|
-
)}
|
|
1006
|
+
<FileUpload selectedFile={file} onFileSelect={setFile} />
|
|
1007
|
+
{file && <Typography color="success">✓ File ready to upload</Typography>}
|
|
861
1008
|
</div>
|
|
862
1009
|
```
|
|
863
1010
|
|
|
864
1011
|
## Comparación con Input type="file"
|
|
865
1012
|
|
|
866
|
-
| Característica
|
|
867
|
-
|
|
868
|
-
| Drag & Drop
|
|
869
|
-
| Validación visual
|
|
870
|
-
| Preview del archivo
|
|
871
|
-
| Extensiones validadas | ⚠️ Solo accept attribute | ✅ Con feedback visual
|
|
872
|
-
| Tamaño validado
|
|
873
|
-
| UX consistente
|
|
874
|
-
| Labels customizables
|
|
1013
|
+
| Característica | Input file nativo | FileUpload |
|
|
1014
|
+
| --------------------- | ------------------------ | ------------------------- |
|
|
1015
|
+
| Drag & Drop | ❌ No | ✅ Sí |
|
|
1016
|
+
| Validación visual | ❌ No | ✅ Sí |
|
|
1017
|
+
| Preview del archivo | ❌ No | ✅ Sí con nombre y tamaño |
|
|
1018
|
+
| Extensiones validadas | ⚠️ Solo accept attribute | ✅ Con feedback visual |
|
|
1019
|
+
| Tamaño validado | ❌ Solo en backend | ✅ Cliente y servidor |
|
|
1020
|
+
| UX consistente | ❌ Varía por browser | ✅ Consistente |
|
|
1021
|
+
| Labels customizables | ❌ No | ✅ Sí (i18n friendly) |
|
|
875
1022
|
|
|
876
1023
|
## Notas
|
|
877
1024
|
|
|
@@ -510,7 +510,7 @@ El componente renderiza un `<span>` con las siguientes características:
|
|
|
510
510
|
<span
|
|
511
511
|
data-slot="icon"
|
|
512
512
|
className="material-symbols-outlined adm:inline-block adm:text-2xl"
|
|
513
|
-
style={{ fontVariationSettings:
|
|
513
|
+
style={{ fontVariationSettings: "..." }}
|
|
514
514
|
>
|
|
515
515
|
{symbol}
|
|
516
516
|
</span>
|
|
@@ -135,7 +135,10 @@ import { Input } from "@adamosuiteservices/ui/input";
|
|
|
135
135
|
{
|
|
136
136
|
/* Por defecto */
|
|
137
137
|
}
|
|
138
|
-
<Input type="text" placeholder="Default input" />;
|
|
138
|
+
<Input type="text" placeholder="Default input" />;
|
|
139
|
+
{
|
|
140
|
+
/* h-10 por defecto */
|
|
141
|
+
}
|
|
139
142
|
|
|
140
143
|
{
|
|
141
144
|
/* Grande */
|
|
@@ -283,7 +283,7 @@ function App() {
|
|
|
283
283
|
## Accesibilidad
|
|
284
284
|
|
|
285
285
|
- ✅ **Role**: `role="radio"` y `role="radiogroup"`
|
|
286
|
-
- ✅ **ARIA**: `aria-checked`, `aria-
|
|
286
|
+
- ✅ **ARIA**: `aria-checked`, `aria-invalid`
|
|
287
287
|
- ✅ **Keyboard**: Arrow keys para navegar, Space para seleccionar
|
|
288
288
|
- ✅ **Label**: Asociar con `htmlFor` e `id`
|
|
289
289
|
- ✅ **Focus**: Focus ring visible en navegación por teclado
|