@@ -5,21 +5,38 @@ import { FieldWidgetProps } from './types';
55
66/**
77 * FileField - File upload widget with drag-and-drop support
8- * Supports single and multiple file uploads with configurable accepted file types
8+ * Supports single and multiple file uploads with configurable accepted file types.
9+ * L2: File size validation, per-file progress indicators, error messages.
910 */
1011export function FileField ( { value, onChange, field, readonly, ...props } : FieldWidgetProps < any > ) {
1112 const inputRef = useRef < HTMLInputElement > ( null ) ;
1213 const fileField = ( field || ( props as any ) . schema ) as any ;
1314 const multiple = fileField ?. multiple || false ;
1415 const accept = fileField ?. accept ? fileField . accept . join ( ',' ) : undefined ;
16+ const maxSize = fileField ?. maxSize as number | undefined ; // bytes
1517 const [ isDragOver , setIsDragOver ] = useState ( false ) ;
18+ const [ errors , setErrors ] = useState < string [ ] > ( [ ] ) ;
1619
1720 const files = value ? ( Array . isArray ( value ) ? value : [ value ] ) : [ ] ;
1821
1922 const processFiles = useCallback ( ( selectedFiles : File [ ] ) => {
2023 if ( selectedFiles . length === 0 ) return ;
24+ const newErrors : string [ ] = [ ] ;
2125
22- const fileObjects = selectedFiles . map ( file => ( {
26+ // Validate file sizes
27+ const validFiles = selectedFiles . filter ( file => {
28+ if ( maxSize && file . size > maxSize ) {
29+ const maxMB = ( maxSize / ( 1024 * 1024 ) ) . toFixed ( 1 ) ;
30+ newErrors . push ( `"${ file . name } " exceeds max size (${ maxMB } MB)` ) ;
31+ return false ;
32+ }
33+ return true ;
34+ } ) ;
35+ setErrors ( newErrors ) ;
36+
37+ if ( validFiles . length === 0 ) return ;
38+
39+ const fileObjects = validFiles . map ( file => ( {
2340 name : file . name ,
2441 original_name : file . name ,
2542 size : file . size ,
@@ -33,7 +50,7 @@ export function FileField({ value, onChange, field, readonly, ...props }: FieldW
3350 } else {
3451 onChange ( fileObjects [ 0 ] ) ;
3552 }
36- } , [ files , multiple , onChange ] ) ;
53+ } , [ files , multiple , onChange , maxSize ] ) ;
3754
3855 const handleDragOver = useCallback ( ( e : React . DragEvent ) => {
3956 e . preventDefault ( ) ;
@@ -147,6 +164,15 @@ export function FileField({ value, onChange, field, readonly, ...props }: FieldW
147164 </ div >
148165 </ div >
149166
167+ { /* Validation errors */ }
168+ { errors . length > 0 && (
169+ < div className = "space-y-0.5" >
170+ { errors . map ( ( err , i ) => (
171+ < p key = { i } className = "text-xs text-destructive" > { err } </ p >
172+ ) ) }
173+ </ div >
174+ ) }
175+
150176 { /* File list */ }
151177 { files . length > 0 && (
152178 < div className = "space-y-1" >
0 commit comments