@adamosuiteservices/ui 2.18.0 → 2.18.2

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.
@@ -4,6 +4,8 @@
4
4
 
5
5
  File upload component with **drag & drop** functionality, extension and size validation, preview of selected files, and visual drag area. Supports both single file and multiple file upload. Ideal for forms that require file uploads with immediate user feedback.
6
6
 
7
+ > **Looking for server integration?** Check out [FileUploadV2](file-upload-v2.md) which includes automatic ID generation and metadata support for server-side file tracking.
8
+
7
9
  ## Features
8
10
 
9
11
  - ✅ Drag & drop with visual feedback
@@ -437,50 +439,13 @@ Position where selected files appear in multiple mode:
437
439
  />
438
440
  ```
439
441
 
440
- #### labels
441
-
442
- ```tsx
443
- labels?: FileUploadLabels
444
-
445
- type FileUploadLabels = {
446
- dragDrop?: string
447
- selectFile?: string
448
- fileRequirements?: string
449
- filesSelected?: (count: number) => string
450
- }
451
- ```
452
-
453
- Customizable labels for internationalization or specific texts.
454
-
455
- **Example**:
456
-
457
- ````tsx
458
- <FileUpload
459
- selectedFile={file}
460
- onFileSelect={setFile}
461
- labels={{
462
- dragDrop: "Drag and drop your file here or",
463
- selectFile: "Select the file",
464
- fileRequirements: "Allowed files: .xls, .xlsx. Maximum size 50 MB."
465
- }}
466
- />
467
-
468
- // Multiple mode with custom counter
469
- <FileUpload
470
- selectedFiles={files}
471
- onFilesSelect={setFiles}
472
- multiple
473
- labels={{
474
- filesSelected: (count) => `${count} document${count !== 1 ? "s" : ""} selected`
475
- }}
476
- />
477
- ```Comunes
442
+ ## Examples
478
443
 
479
- ### acceptedExtensions
444
+ ### With Field components
480
445
 
481
446
  ```tsx
482
447
  acceptedExtensions?: string[]
483
- ````
448
+ ```
484
449
 
485
450
  Array of allowed file extensions. Default: `[".xls", ".xlsx", ".numbers"]`
486
451
 
@@ -508,87 +473,44 @@ Maximum file size in megabytes. Default: `50`
508
473
  <FileUpload selectedFile={file} onFileSelect={setFile} maxSizeInMB={10} />
509
474
  ```
510
475
 
511
- ### labels
512
-
513
- ```tsx
514
- labels?: FileUploadLabels
476
+ ## Examples
515
477
 
516
- type FileUploadLabels = {
517
- dragDrop?: string
518
- selectFile?: string
519
- fileRequirements?: string
478
+ const handleInvalidFile = (file: File, reason: "extension" | "size") => {
479
+ if (reason === "extension") {
480
+ setError(`File "${file.name}" has an invalid extension.`);
481
+ } else if (reason === "size") {
482
+ setError(`File "${file.name}" exceeds the maximum size limit.`);
520
483
  }
521
- ```
522
-
523
- Customizable labels for internationalization or specific texts.
524
-
525
- **Example**:
484
+ };
526
485
 
527
- ```tsx
486
+ return (
487
+ <FieldGroup className="adm:max-w-xl">
488
+ <Field>
489
+ <FieldLabel>Upload document (PDF or DOCX only, max 2MB)</FieldLabel>
528
490
  <FileUpload
529
- selectedFile={file}
530
- onFileSelect={setFile}
531
- labels={{
532
- dragDrop: "Drag and drop your file here or",
533
- selectFile: "Select the file",
534
- fileRequirements: "Allowed files: .xls, .xlsx. Maximum size 50 MB.",
535
- }}
491
+ selectedFile={file}
492
+ onFileSelect={(newFile) => {
493
+ setFile(newFile);
494
+ if (newFile) setError("");
495
+ }}
496
+ onInvalidFile={handleInvalidFile}
497
+ aria-invalid={!!error}
498
+ acceptedExtensions={[".pdf", ".docx"]}
499
+ maxSizeInMB={2}
536
500
  />
537
- ```
538
-
539
- ## Examples
540
-
541
- ### With Field components
542
-
543
- ```tsx
544
- import {
545
- Field,
546
- FieldLabel,
547
- FieldDescription,
548
- FieldError,
549
- FieldGroup,
550
- } from "@adamosuiteservices/ui/field";
551
- import { FileUpload } from "@adamosuiteservices/ui/file-upload";
552
-
553
- function FileUploadForm() {
554
- const [file, setFile] = useState<File | null>(null);
555
- const [error, setError] = useState<string>("");
556
-
557
- const handleInvalidFile = (file: File, reason: "extension" | "size") => {
558
- if (reason === "extension") {
559
- setError(`File "${file.name}" has an invalid extension.`);
560
- } else if (reason === "size") {
561
- setError(`File "${file.name}" exceeds the maximum size limit.`);
562
- }
563
- };
564
-
565
- return (
566
- <FieldGroup className="adm:max-w-xl">
567
- <Field>
568
- <FieldLabel>Upload document (PDF or DOCX only, max 2MB)</FieldLabel>
569
- <FileUpload
570
- selectedFile={file}
571
- onFileSelect={(newFile) => {
572
- setFile(newFile);
573
- if (newFile) setError("");
574
- }}
575
- onInvalidFile={handleInvalidFile}
576
- aria-invalid={!!error}
577
- acceptedExtensions={[".pdf", ".docx"]}
578
- maxSizeInMB={2}
579
- />
580
- {error ? (
581
- <FieldError>{error}</FieldError>
582
- ) : (
583
- <FieldDescription>
584
- Supported formats: PDF, DOCX. Maximum 2MB.
585
- </FieldDescription>
586
- )}
587
- </Field>
588
- </FieldGroup>
589
- );
501
+ {error ? (
502
+ <FieldError>{error}</FieldError>
503
+ ) : (
504
+ <FieldDescription>
505
+ Supported formats: PDF, DOCX. Maximum 2MB.
506
+ </FieldDescription>
507
+ )}
508
+ </Field>
509
+ </FieldGroup>
510
+ );
590
511
  }
591
- ```
512
+
513
+ ````
592
514
 
593
515
  ### Estado Deshabilitado
594
516
 
@@ -633,7 +555,7 @@ function DisabledWithFileExample() {
633
555
  </FieldGroup>
634
556
  );
635
557
  }
636
- ```
558
+ ````
637
559
 
638
560
  ### Inside fieldset disabled
639
561
 
@@ -992,269 +914,97 @@ const handleInvalidFile = (file: File, reason: "extension" | "size") => {
992
914
  - Dragging a new file replaces the previous one
993
915
  - Using the input also replaces the file
994
916
 
995
- ### Normal state vs dragging
917
+ ### Multiple mode (`selectedFiles` + `onFilesSelect`)
996
918
 
997
- ```tsx
998
- // Normal
999
- border-input bg-background
919
+ - Allows multiple files up to `maxFiles` limit
920
+ - Files can be displayed above or below the drop area (`filesPosition`)
921
+ - Each file has its own remove button
922
+ - A "Clear all" button appears when there are 2+ files
1000
923
 
1001
- // Dragging
1002
- border-primary
1003
- ```
924
+ ### Visual styling details
1004
925
 
1005
- ### Selected file card
926
+ **Drag & drop area:**
927
+
928
+ - Padding: `p-6` (24px)
929
+ - Border radius: `rounded-2xl` (16px)
930
+ - Border: `border-2 border-dashed`
931
+ - Normal state: `border-input bg-background`
932
+ - Dragging: `border-primary`
933
+ - Transitions: `transition-colors`
934
+
935
+ **Selected file card:**
1006
936
 
1007
937
  - Padding: `p-6` (24px)
1008
938
  - Border radius: `rounded-2xl` (16px)
1009
939
  - Background: `bg-muted`
1010
940
  - Border: `border-input`
1011
941
  - Horizontal gap: `gap-4` (16px)
1012
- - Icon on `bg-primary-50` background with `rounded-xl` and `p-2.5`
1013
-
1014
- ```tsx
1015
- const [archive, setArchive] = useState<File | null>(null);
1016
-
1017
- <FileUpload
1018
- selectedFile={archive}
1019
- onFileSelect={setArchive}
1020
- acceptedExtensions={[".zip", ".rar", ".7z"]}
1021
- maxSizeInMB={500}
1022
- labels={{
1023
- dragDrop: "Drag and drop your archive here or",
1024
- selectFile: "Select the archive",
1025
- }}
1026
- />;
1027
- ```
1028
-
1029
- ### With form integration
1030
-
1031
- ```tsx
1032
- function UploadForm() {
1033
- const [file, setFile] = useState<File | null>(null);
1034
-
1035
- const handleSubmit = async (e: React.FormEvent) => {
1036
- e.preventDefault();
1037
-
1038
- if (!file) return;
1039
-
1040
- const formData = new FormData();
1041
- formData.append("file", file);
942
+ - Icon: `text-primary` on `bg-primary/15` background
1042
943
 
1043
- try {
1044
- const response = await fetch("/api/upload", {
1045
- method: "POST",
1046
- body: formData
1047
- });
1048
-
1049
- ### Remove button
944
+ **Remove button:**
1050
945
 
1051
946
  - Variant: `destructive-medium`
1052
947
  - Icon: `delete` from Material Symbols
1053
948
  - Icon color: `text-destructive`
1054
949
 
1055
- ### Clear all button (multiple mode)
950
+ **Clear all button (multiple mode):**
1056
951
 
1057
952
  - Variant: `ghost`
1058
953
  - Size: `sm`
1059
954
  - Appears only when there are 2+ files
1060
- console.error("Upload failed:", error);
1061
- }
1062
- };
1063
-
1064
- return (
1065
- <form onSubmit={handleSubmit} className="space-y-4">
1066
- <FileUpload
1067
- selectedFile={file}
1068
- onFileSelect={setFile}
1069
- />
1070
- <Button type="submit" disabled={!file}>
1071
- Upload File
1072
- </Button>
1073
- </form>
1074
- );
1075
- }
1076
- ```
1077
-
1078
- ## Visual states
1079
-
1080
- ### Without selected file
1081
-
1082
- The component shows:
1083
-
1084
- - Drag & drop area with dashed border
1085
- - Document icon on light blue background
1086
- - Instructional text and link to select file
1087
- - File requirements (extensions and size)
1088
-
1089
- ### During drag over
1090
-
1091
- When the user drags a file over the area:
1092
-
1093
- - Border changes to `border-primary-500`
1094
- - Background changes to `bg-primary-50`
1095
- - Smooth transition with `transition-colors`
1096
-
1097
- ### With selected file
1098
-
1099
- The component shows:
1100
-
1101
- - Card with `bg-neutral-100` background
1102
- - Document icon on white background
1103
- - File name (with truncate if long)
1104
- - File size in MB
1105
- - Destructive button to remove the file
1106
-
1107
- ## Validation
1108
-
1109
- The component automatically validates:
1110
-
1111
- 1. **Extension**: Only accepts files with extensions in `acceptedExtensions`
1112
- 2. **Size**: Rejects files larger than `maxSizeInMB`
1113
-
1114
- If a file does not meet these validations, it is not selected and `onFileSelect` is not invoked.
1115
-
1116
- ## Base styles
1117
-
1118
- ### Drag & drop area
1119
-
1120
- - Padding: `p-6` (24px)
1121
- - Border radius: `rounded-2xl` (16px)
1122
- - Border: `border-2 border-dashed`
1123
- - Gap between elements: `gap-6` (24px)
1124
- - Transitions: `transition-colors`
1125
-
1126
- ### Normal state vs dragging
1127
-
1128
- ```tsx
1129
- // Normal
1130
- border-neutral-300 bg-neutral-50
1131
-
1132
- // Dragging
1133
- border-primary-500 bg-primary-50
1134
- ```
1135
-
1136
- ### Selected file card
1137
-
1138
- - Padding: `p-6` (24px)
1139
- - Border radius: `rounded-2xl` (16px)
1140
- - Background: `bg-neutral-100`
1141
- - Horizontal gap: `gap-4` (16px)
1142
- - Icon on white background with `rounded-xl` and `p-2.5`
1143
-
1144
- ### Remove button
1145
-
1146
- - Variant: `destructive-medium`
1147
- - Icon: `delete` from Material Symbols
1148
- - Icon color: `text-destructive-500`
1149
955
 
1150
956
  ## Accessibility
1151
957
 
1152
958
  - Hidden file input with `className="adm:hidden"`
1153
- - Label properly associated with `htmlFor="file-upload"`
959
+ - Label properly associated via auto-generated ID or custom `input.id`
1154
960
  - Link button uses `asChild` for semantic behavior
1155
961
  - Typography with `muted` color for secondary text
962
+ - Supports `aria-invalid` for form validation feedback
963
+ - Supports `aria-disabled` and `disabled` for accessibility
964
+ - Automatically detects and respects `<fieldset disabled>`
1156
965
 
1157
966
  ## TypeScript types
1158
967
 
1159
- ````typescript
1160
- export type FileUploadLabels = {
1161
- dragDrop?: string
1162
- selectFile?: string
1163
- fileRequirements?: string
1164
- filesSelected?: (count: number) => string
1165
- };
1166
-
1167
- export type FileUploadProps = ComponentProps<"div"> & Readonly<{
1168
- // Simple mode
1169
- selectedFile?: File | null
1170
- onFileSelect?: (file: File | null) => void
1171
-
1172
- // Multiple mode
1173
- selectedFiles?: File[]
1174
- onFilesSelect?: (files: File[]) => void
1175
- multiple?: boolean
1176
- maxFiles?: number
1177
- filesPosition?: "above" | "below"
1178
-
1179
- // Validation
1180
- onInvalidFile?: (file: File, reason: "extension" | "size") => void
1181
- invalid?: boolean
1182
- "aria-invalid"?: boolean
1183
-
1184
- // States
1185
- disabled?: boolean
1186
-
1187
- // Common
1188
- acceptedExtensions?: string[]
1189
- maxSizeInMB?: number
1190
- labels?: FileUploadLabels
1191
- input?: ComponentProps<"input">
1192
- }>
1193
- ### FormData for upload
1194
-
1195
- ```tsx
1196
- // Un archivo
1197
- const uploadFile = async (file: File) => {
1198
- const formData = new FormData();
1199
- formData.append("file", file);
1200
- formData.append("metadata", JSON.stringify({
1201
- uploadedAt: new Date().toISOString()
1202
- }));
1203
-
1204
- const response = await fetch("/api/upload", {
1205
- method: "POST",
1206
- body: formData
1207
- });
1208
-
1209
- return response.json();
1210
- };
1211
-
1212
- // Múltiples archivos
1213
- const uploadFiles = async (files: File[]) => {
1214
- const formData = new FormData();
1215
- files.forEach((file, index) => {
1216
- formData.append(`file${index}`, file);
1217
- });
1218
-
1219
- const response = await fetch("/api/upload-multiple", {
1220
- method: "POST",
1221
- body: formData
1222
- });
1223
-
1224
- return response.json();
1225
- };
1226
- ````
1227
-
1228
- ```tsx
1229
- // No validar el archivo antes de hacer upload
1230
- // Siempre verificar que file no sea null
1231
-
1232
- // Extensiones demasiado permisivas
1233
- acceptedExtensions={[".*"]} // Inseguro
1234
-
1235
- // Tamaño excesivo sin justificación
1236
- maxSizeInMB={10000} // 10GB es excesivo
1237
-
1238
- // Labels genéricas cuando el contexto es específico
1239
- labels={{ selectFile: "Select file" }} // Poco descriptivo
1240
- ```
1241
-
1242
- ## TypeScript types (duplicate)
1243
-
1244
968
  ```typescript
1245
969
  export type FileUploadLabels = {
1246
970
  dragDrop?: string;
1247
971
  selectFile?: string;
1248
972
  fileRequirements?: string;
973
+ filesSelected?: (count: number) => string;
1249
974
  };
1250
975
 
1251
976
  export type FileUploadProps = ComponentProps<"div"> &
1252
977
  Readonly<{
1253
- selectedFile: File | null;
1254
- onFileSelect: (file: File | null) => void;
978
+ // Simple mode
979
+ selectedFile?: File | null;
980
+ onFileSelect?: (file: File | null) => void;
981
+
982
+ // Multiple mode
983
+ selectedFiles?: File[];
984
+ onFilesSelect?: (files: File[]) => void;
985
+ multiple?: boolean;
986
+ maxFiles?: number;
987
+ filesPosition?: "above" | "below";
988
+
989
+ // Callbacks
990
+ onFileAdd?: (file: File) => void;
991
+ onFileRemove?: (file: File) => void;
992
+ onFilesAdd?: (files: File[]) => void;
993
+ onFilesRemove?: (files: File[]) => void;
994
+
995
+ // Validation
996
+ onInvalidFile?: (file: File, reason: "extension" | "size") => void;
997
+ invalid?: boolean;
998
+ "aria-invalid"?: boolean;
999
+
1000
+ // States
1001
+ disabled?: boolean;
1002
+
1003
+ // Common
1255
1004
  acceptedExtensions?: string[];
1256
1005
  maxSizeInMB?: number;
1257
1006
  labels?: FileUploadLabels;
1007
+ input?: ComponentProps<"input">;
1258
1008
  }>;
1259
1009
  ```
1260
1010
 
@@ -1263,6 +1013,7 @@ export type FileUploadProps = ComponentProps<"div"> &
1263
1013
  ### FormData for upload
1264
1014
 
1265
1015
  ```tsx
1016
+ // Single file
1266
1017
  const uploadFile = async (file: File) => {
1267
1018
  const formData = new FormData();
1268
1019
  formData.append("file", file);
@@ -1280,6 +1031,21 @@ const uploadFile = async (file: File) => {
1280
1031
 
1281
1032
  return response.json();
1282
1033
  };
1034
+
1035
+ // Multiple files
1036
+ const uploadFiles = async (files: File[]) => {
1037
+ const formData = new FormData();
1038
+ files.forEach((file, index) => {
1039
+ formData.append(`file${index}`, file);
1040
+ });
1041
+
1042
+ const response = await fetch("/api/upload-multiple", {
1043
+ method: "POST",
1044
+ body: formData,
1045
+ });
1046
+
1047
+ return response.json();
1048
+ };
1283
1049
  ```
1284
1050
 
1285
1051
  ### With progress tracking
@@ -1310,73 +1076,69 @@ const uploadWithProgress = async (file: File) => {
1310
1076
  };
1311
1077
  ```
1312
1078
 
1313
- ## Comparison with Input type="file"
1314
-
1315
- | Característica | Input file nativo | FileUpload |
1316
- | --------------------- | ------------------------ | ------------------------- |
1317
- | Drag & Drop | ❌ No | ✅ Sí |
1318
- | Modo múltiple | ⚠️ Básico | ✅ Completo con UI |
1319
- | Validación visual | ❌ No | ✅ Sí |
1320
- | Preview de archivos | ❌ No | ✅ Sí con nombre y tamaño |
1321
- | Extensiones validadas | ⚠️ Solo accept attribute | ✅ Con feedback visual |
1322
- | Tamaño validado | ❌ Solo en backend | ✅ Cliente y servidor |
1323
- | Límite de archivos | ❌ No | ✅ Sí (maxFiles) |
1324
- | Remover archivos | ❌ No | ✅ Individual y Clear all |
1325
- | Posición de preview | ❌ No configurable | ✅ Arriba o abajo |
1326
- | UX consistente | ❌ Varía por browser | ✅ Consistente |
1327
- | Labels customizables | ❌ No | ✅ Sí (i18n friendly) |
1328
- | aria-invalid | ⚠️ Manual | ✅ Integrado |
1329
- | Fieldset disabled | ⚠️ Native but no styles | ✅ Automatically detected |
1330
- | Disabled files | ❌ Not supported | ✅ Full visual feedback |
1079
+ ## Best practices
1331
1080
 
1332
- ## Notes
1333
-
1334
- - The component uses native browser `File` API
1335
- - Validation is client-side only - always validate on the server as well
1336
- - In simple mode, drag & drop accepts one file at a time
1337
- - In multiple mode, multiple files can be dragged simultaneously
1338
- - Files are not stored automatically - handle upload with callbacks
1339
- - The component is fully controlled - the parent manages file state
1340
- - The file input is reusable: after selecting, it resets to allow selecting the same file again
1341
- - **Automatic ID generation**: If no `id` is provided in the `input` prop, one is automatically generated with format `file-upload-{random}` to ensure accessibility
1342
- - **Automatic fieldset detection**: The component detects when it is inside a `<fieldset disabled>` using MutationObserver and disables itself automatically
1343
- - **Selected files and disabled**: When the component is disabled, selected files are visually shown as disabled and cannot be removed
1344
- - Use with Field components (`Field`, `FieldLabel`, `FieldError`, etc.) for a better form experience
1081
+ ### ✅ Do
1345
1082
 
1346
1083
  ```tsx
1347
- <FileUpload
1348
- selectedFile={file}
1349
- onFileSelect={setFile}
1350
- className="adm:max-w-2xl adm:mx-auto"
1351
- />
1084
+ // Validate on server-side as well
1085
+ // Always check that file is not null before upload
1086
+
1087
+ // Use specific extensions for security
1088
+ acceptedExtensions={[".pdf", ".docx", ".xlsx"]}
1089
+
1090
+ // Set reasonable size limits
1091
+ maxSizeInMB={10}
1092
+
1093
+ // Provide descriptive labels for context
1094
+ labels={{
1095
+ dragDrop: "Drag and drop your invoice here or",
1096
+ selectFile: "Select invoice"
1097
+ }}
1352
1098
  ```
1353
1099
 
1354
- ### With additional wrapper
1100
+ ### Don't
1355
1101
 
1356
1102
  ```tsx
1357
- <div className="adm:space-y-4">
1358
- <Label>Upload your document</Label>
1359
- <FileUpload selectedFile={file} onFileSelect={setFile} />
1360
- {file && <Typography color="success">✓ File ready to upload</Typography>}
1361
- </div>
1103
+ // Don't skip server-side validation
1104
+ // Always verify file is not null
1105
+
1106
+ // Don't use overly permissive extensions (security risk)
1107
+ acceptedExtensions={[".*"]}
1108
+
1109
+ // Don't set excessive size limits without justification
1110
+ maxSizeInMB={10000} // 10GB is excessive
1111
+
1112
+ // Don't use generic labels when context is specific
1113
+ labels={{ selectFile: "Select file" }} // Not descriptive enough
1362
1114
  ```
1363
1115
 
1364
- ## Comparison with Input type="file"
1116
+ ## Comparison with FileUploadV2
1117
+
1118
+ | Feature | FileUpload | FileUploadV2 |
1119
+ | ------------------- | ------------------------------------ | -------------------------------------------------- |
1120
+ | File type | `File` (plain browser File object) | `FileWithMetadata` (with required ID) |
1121
+ | Status tracking | ❌ No | ✅ Yes (idle, uploading, success, error, deleting) |
1122
+ | Unique IDs | ❌ No | ✅ Yes (auto-generated) |
1123
+ | Custom ID generator | ❌ No | ✅ Yes (generateId prop) |
1124
+ | Metadata support | ❌ No | ✅ Yes (metadata property) |
1125
+ | Server integration | ⚠️ Manual only | ✅ Built-in with IDs and status |
1126
+ | Use case | Simple forms without status tracking | Server integration with file tracking |
1127
+ | Complexity | ⚡ Simple | 🔧 Advanced |
1365
1128
 
1366
- | Feature | Native file input | FileUpload |
1367
- | -------------------- | ------------------------ | ------------------------- |
1368
- | Drag & Drop | ❌ No | ✅ Yes |
1369
- | Visual validation | ❌ No | ✅ Yes |
1370
- | File preview | ❌ No | ✅ Yes with name and size |
1371
- | Validated extensions | ⚠️ Only accept attribute | ✅ With visual feedback |
1372
- | Validated size | ❌ Only on backend | ✅ Client and server |
1373
- | Consistent UX | ❌ Varies by browser | ✅ Consistent |
1374
- | Customizable labels | ❌ No | ✅ Yes (i18n friendly) |
1129
+ > **Need status tracking and metadata?** Use [FileUploadV2](file-upload-v2.md) for server-side file tracking with built-in unique identifiers and status management.
1375
1130
 
1376
1131
  ## Notes
1377
1132
 
1378
- - The component uses native browser `File` API
1379
- - Validation is client-side only - always validate on the server as well
1380
- - Drag & drop only works with one file at a time
1381
- - Files are not stored automatically - handle upload with `onFileSelect`
1133
+ - The component uses native browser `File` API (plain File objects without modifications)
1134
+ - Validation is client-side only - **always validate on the server as well**
1135
+ - In simple mode, drag & drop accepts one file at a time
1136
+ - In multiple mode, multiple files can be dragged simultaneously
1137
+ - Files are not stored automatically - handle upload with callbacks
1382
1138
  - The component is fully controlled - the parent manages file state
1139
+ - The file input is reusable: after selecting, it resets to allow selecting the same file again
1140
+ - **No status tracking**: FileUpload works with plain File objects - use [FileUploadV2](file-upload-v2.md) if you need status tracking
1141
+ - **Automatic ID generation**: If no `id` is provided in the `input` prop, one is automatically generated with format `file-upload-{random}` to ensure accessibility
1142
+ - **Automatic fieldset detection**: The component detects when it is inside a `<fieldset disabled>` using MutationObserver and disables itself automatically
1143
+ - **Selected files and disabled**: When the component is disabled, selected files are visually shown as disabled and cannot be removed
1144
+ - Use with Field components (`Field`, `FieldLabel`, `FieldError`, etc.) for a better form experience
package/llm.txt CHANGED
@@ -32,7 +32,7 @@ Before creating ANY component, verify it doesn't exist here. For implementation
32
32
  - **Label** [`docs/components/ui/label.md`] - `@adamosuiteservices/ui/label`
33
33
  - **Input Group** [`docs/components/ui/input-group.md`] - `@adamosuiteservices/ui/input-group`
34
34
  - **File Upload** [`docs/components/ui/file-upload.md`] - `@adamosuiteservices/ui/file-upload` ⚠️ Simple file upload
35
- - **File Upload V2** [`docs/components/ui/file-upload-v2.md`] - `@adamosuiteservices/ui/file-upload` ⚠️ With IDs for server integration
35
+ - **File Upload V2** [`docs/components/ui/file-upload-v2.md`] - `@adamosuiteservices/ui/file-upload-v2` ⚠️ With unique IDs and metadata for server integration
36
36
 
37
37
  ### Overlay Components (8)
38
38
  - **Dialog** [`docs/components/ui/dialog.md`] - `@adamosuiteservices/ui/dialog` (modal)
@@ -90,9 +90,9 @@ Before creating ANY component, verify it doesn't exist here. For implementation
90
90
  1. ❌ Creating components that already exist (check list above first!)
91
91
  2. ❌ Using barrel imports: `import { X } from "@adamosuiteservices/ui"`
92
92
  3. ❌ Using `adm:` prefix in user code
93
- 5. ❌ Using FileUploadV2 for simple forms (use FileUpload instead)
94
- 6. ❌ Using FileUpload when you need server-side file tracking (use FileUploadV2)
95
- 4. ❌ Not consulting component documentation before implementation
93
+ 4. ❌ Using FileUploadV2 when you don't need unique IDs or metadata (use FileUpload instead)
94
+ 5. ❌ Using FileUpload without status when you need server-side tracking (use FileUploadV2 with IDs)
95
+ 6. ❌ Not consulting component documentation before implementation
96
96
 
97
97
  ## 📚 Documentation Structure
98
98