@@ -25,34 +25,45 @@ export function connect<T extends PropTypes>(
2525) : FileUploadApi < T > {
2626 const { state, send, prop, computed, scope, context } = service
2727 const disabled = ! ! prop ( "disabled" )
28+ const readOnly = ! ! prop ( "readOnly" )
2829 const required = ! ! prop ( "required" )
2930 const allowDrop = prop ( "allowDrop" )
3031 const translations = prop ( "translations" )
3132
3233 const dragging = state . matches ( "dragging" )
3334 const focused = state . matches ( "focused" ) && ! disabled
3435
36+ const acceptedFiles = context . get ( "acceptedFiles" )
37+ const maxFiles = prop ( "maxFiles" )
38+
3539 return {
3640 dragging,
3741 focused,
38- disabled : ! ! disabled ,
42+ disabled,
43+ readOnly,
3944 transforming : context . get ( "transforming" ) ,
45+ maxFilesReached : acceptedFiles . length >= maxFiles ,
46+ remainingFiles : Math . max ( 0 , maxFiles - acceptedFiles . length ) ,
4047 openFilePicker ( ) {
41- if ( disabled ) return
48+ if ( disabled || readOnly ) return
4249 send ( { type : "OPEN" } )
4350 } ,
4451 deleteFile ( file , type = DEFAULT_ITEM_TYPE ) {
52+ if ( disabled || readOnly ) return
4553 send ( { type : "FILE.DELETE" , file, itemType : type } )
4654 } ,
47- acceptedFiles : context . get ( "acceptedFiles" ) ,
55+ acceptedFiles,
4856 rejectedFiles : context . get ( "rejectedFiles" ) ,
4957 setFiles ( files ) {
58+ if ( disabled || readOnly ) return
5059 send ( { type : "FILES.SET" , files, count : files . length } )
5160 } ,
5261 clearRejectedFiles ( ) {
62+ if ( disabled || readOnly ) return
5363 send ( { type : "REJECTED_FILES.CLEAR" } )
5464 } ,
5565 clearFiles ( ) {
66+ if ( disabled || readOnly ) return
5667 send ( { type : "FILES.CLEAR" } )
5768 } ,
5869 getFileSize ( file ) {
@@ -65,7 +76,7 @@ export function connect<T extends PropTypes>(
6576 return ( ) => win . URL . revokeObjectURL ( url )
6677 } ,
6778 setClipboardFiles ( dt ) {
68- if ( disabled ) return false
79+ if ( disabled || readOnly ) return false
6980 const items = Array . from ( dt ?. items ?? [ ] )
7081 const files = items . reduce < File [ ] > ( ( acc , item ) => {
7182 if ( item . kind !== "file" ) return acc
@@ -84,6 +95,7 @@ export function connect<T extends PropTypes>(
8495 dir : prop ( "dir" ) ,
8596 id : dom . getRootId ( scope ) ,
8697 "data-disabled" : dataAttr ( disabled ) ,
98+ "data-readonly" : dataAttr ( readOnly ) ,
8799 "data-dragging" : dataAttr ( dragging ) ,
88100 } )
89101 } ,
@@ -93,15 +105,17 @@ export function connect<T extends PropTypes>(
93105 ...parts . dropzone . attrs ,
94106 dir : prop ( "dir" ) ,
95107 id : dom . getDropzoneId ( scope ) ,
96- tabIndex : disabled || props . disableClick ? undefined : 0 ,
108+ tabIndex : disabled || readOnly || props . disableClick ? undefined : 0 ,
97109 role : props . disableClick ? "application" : "button" ,
98110 "aria-label" : translations . dropzone ,
99111 "aria-disabled" : disabled ,
112+ "aria-readonly" : readOnly ,
100113 "data-invalid" : dataAttr ( prop ( "invalid" ) ) ,
101114 "data-disabled" : dataAttr ( disabled ) ,
115+ "data-readonly" : dataAttr ( readOnly ) ,
102116 "data-dragging" : dataAttr ( dragging ) ,
103117 onKeyDown ( event ) {
104- if ( disabled ) return
118+ if ( disabled || readOnly ) return
105119 if ( event . defaultPrevented ) return
106120
107121 const target = getEventTarget < HTMLElement > ( event )
@@ -113,7 +127,7 @@ export function connect<T extends PropTypes>(
113127 send ( { type : "DROPZONE.CLICK" , src : "keydown" } )
114128 } ,
115129 onClick ( event ) {
116- if ( disabled ) return
130+ if ( disabled || readOnly ) return
117131 if ( event . defaultPrevented ) return
118132 if ( props . disableClick ) return
119133
@@ -128,7 +142,7 @@ export function connect<T extends PropTypes>(
128142 send ( { type : "DROPZONE.CLICK" } )
129143 } ,
130144 onDragOver ( event ) {
131- if ( disabled ) return
145+ if ( disabled || readOnly ) return
132146 if ( ! allowDrop ) return
133147 event . preventDefault ( )
134148 event . stopPropagation ( )
@@ -143,31 +157,31 @@ export function connect<T extends PropTypes>(
143157 send ( { type : "DROPZONE.DRAG_OVER" , count } )
144158 } ,
145159 onDragLeave ( event ) {
146- if ( disabled ) return
160+ if ( disabled || readOnly ) return
147161 if ( ! allowDrop ) return
148162 if ( contains ( event . currentTarget , event . relatedTarget ) ) return
149163 send ( { type : "DROPZONE.DRAG_LEAVE" } )
150164 } ,
151165 onDrop ( event ) {
152- if ( disabled ) return
166+ if ( disabled || readOnly ) return
153167 if ( allowDrop ) {
154168 event . preventDefault ( )
155169 event . stopPropagation ( )
156170 }
157171
158172 const hasFiles = isEventWithFiles ( event )
159- if ( disabled || ! hasFiles ) return
173+ if ( ! hasFiles ) return
160174
161175 getFileEntries ( event . dataTransfer . items , prop ( "directory" ) ) . then ( ( files ) => {
162176 send ( { type : "DROPZONE.DROP" , files : flatArray ( files ) } )
163177 } )
164178 } ,
165179 onFocus ( ) {
166- if ( disabled ) return
180+ if ( disabled || readOnly ) return
167181 send ( { type : "DROPZONE.FOCUS" } )
168182 } ,
169183 onBlur ( ) {
170- if ( disabled ) return
184+ if ( disabled || readOnly ) return
171185 send ( { type : "DROPZONE.BLUR" } )
172186 } ,
173187 } )
@@ -178,12 +192,13 @@ export function connect<T extends PropTypes>(
178192 ...parts . trigger . attrs ,
179193 dir : prop ( "dir" ) ,
180194 id : dom . getTriggerId ( scope ) ,
181- disabled,
195+ disabled : disabled || readOnly ,
182196 "data-disabled" : dataAttr ( disabled ) ,
197+ "data-readonly" : dataAttr ( readOnly ) ,
183198 "data-invalid" : dataAttr ( prop ( "invalid" ) ) ,
184199 type : "button" ,
185200 onClick ( event ) {
186- if ( disabled ) return
201+ if ( disabled || readOnly ) return
187202 // if trigger is wrapped within the dropzone, stop propagation to avoid double opening
188203 if ( contains ( dom . getDropzoneEl ( scope ) , event . currentTarget ) ) {
189204 event . stopPropagation ( )
@@ -197,7 +212,7 @@ export function connect<T extends PropTypes>(
197212 return normalize . input ( {
198213 id : dom . getHiddenInputId ( scope ) ,
199214 tabIndex : - 1 ,
200- disabled,
215+ disabled : disabled || readOnly ,
201216 type : "file" ,
202217 required : prop ( "required" ) ,
203218 capture : prop ( "capture" ) ,
@@ -211,7 +226,7 @@ export function connect<T extends PropTypes>(
211226 event . currentTarget . value = ""
212227 } ,
213228 onInput ( event ) {
214- if ( disabled ) return
229+ if ( disabled || readOnly ) return
215230 const { files } = event . currentTarget
216231 send ( { type : "FILE.SELECT" , files : files ? Array . from ( files ) : [ ] } )
217232 } ,
@@ -234,7 +249,7 @@ export function connect<T extends PropTypes>(
234249 return normalize . element ( {
235250 ...parts . item . attrs ,
236251 dir : prop ( "dir" ) ,
237- id : dom . getItemId ( scope , file . name ) ,
252+ id : dom . getItemId ( scope , dom . getFileId ( file ) ) ,
238253 "data-disabled" : dataAttr ( disabled ) ,
239254 "data-type" : type ,
240255 } )
@@ -245,7 +260,7 @@ export function connect<T extends PropTypes>(
245260 return normalize . element ( {
246261 ...parts . itemName . attrs ,
247262 dir : prop ( "dir" ) ,
248- id : dom . getItemNameId ( scope , file . name ) ,
263+ id : dom . getItemNameId ( scope , dom . getFileId ( file ) ) ,
249264 "data-disabled" : dataAttr ( disabled ) ,
250265 "data-type" : type ,
251266 } )
@@ -256,7 +271,7 @@ export function connect<T extends PropTypes>(
256271 return normalize . element ( {
257272 ...parts . itemSizeText . attrs ,
258273 dir : prop ( "dir" ) ,
259- id : dom . getItemSizeTextId ( scope , file . name ) ,
274+ id : dom . getItemSizeTextId ( scope , dom . getFileId ( file ) ) ,
260275 "data-disabled" : dataAttr ( disabled ) ,
261276 "data-type" : type ,
262277 } )
@@ -267,7 +282,7 @@ export function connect<T extends PropTypes>(
267282 return normalize . element ( {
268283 ...parts . itemPreview . attrs ,
269284 dir : prop ( "dir" ) ,
270- id : dom . getItemPreviewId ( scope , file . name ) ,
285+ id : dom . getItemPreviewId ( scope , dom . getFileId ( file ) ) ,
271286 "data-disabled" : dataAttr ( disabled ) ,
272287 "data-type" : type ,
273288 } )
@@ -293,13 +308,15 @@ export function connect<T extends PropTypes>(
293308 return normalize . button ( {
294309 ...parts . itemDeleteTrigger . attrs ,
295310 dir : prop ( "dir" ) ,
311+ id : dom . getItemDeleteTriggerId ( scope , dom . getFileId ( file ) ) ,
296312 type : "button" ,
297- disabled,
313+ disabled : disabled || readOnly ,
298314 "data-disabled" : dataAttr ( disabled ) ,
315+ "data-readonly" : dataAttr ( readOnly ) ,
299316 "data-type" : type ,
300317 "aria-label" : translations . deleteFile ?.( file ) ,
301318 onClick ( ) {
302- if ( disabled ) return
319+ if ( disabled || readOnly ) return
303320 send ( { type : "FILE.DELETE" , file, itemType : type } )
304321 } ,
305322 } )
@@ -321,12 +338,13 @@ export function connect<T extends PropTypes>(
321338 ...parts . clearTrigger . attrs ,
322339 dir : prop ( "dir" ) ,
323340 type : "button" ,
324- disabled,
325- hidden : context . get ( " acceptedFiles" ) . length === 0 ,
341+ disabled : disabled || readOnly ,
342+ hidden : acceptedFiles . length === 0 ,
326343 "data-disabled" : dataAttr ( disabled ) ,
344+ "data-readonly" : dataAttr ( readOnly ) ,
327345 onClick ( event ) {
328346 if ( event . defaultPrevented ) return
329- if ( disabled ) return
347+ if ( disabled || readOnly ) return
330348 send ( { type : "FILES.CLEAR" } )
331349 } ,
332350 } )
0 commit comments