@@ -24,6 +24,51 @@ const VALUE_EMPTY_DEPRECATED_MSG =
2424
2525const isValidValue = value => isFile ( value ) || ( isArray ( value ) && value . every ( v => isValidValue ( v ) ) )
2626
27+ // Drop handler function to get all files
28+ /* istanbul ignore next: not supported in JSDOM */
29+ const getAllFileEntries = async dataTransferItemList => {
30+ const fileEntries = [ ]
31+ const queue = [ ]
32+ // Unfortunately `dataTransferItemList` is not iterable i.e. no `.forEach()`
33+ for ( let i = 0 ; i < dataTransferItemList . length ; i ++ ) {
34+ queue . push ( dataTransferItemList [ i ] . webkitGetAsEntry ( ) )
35+ }
36+ while ( queue . length > 0 ) {
37+ const entry = queue . shift ( )
38+ if ( entry . isFile ) {
39+ fileEntries . push ( entry )
40+ } else if ( entry . isDirectory ) {
41+ queue . push ( ...( await readAllDirectoryEntries ( entry . createReader ( ) ) ) )
42+ }
43+ }
44+ return fileEntries
45+ }
46+
47+ // Get all the entries (files or sub-directories) in a directory
48+ // by calling `.readEntries()` until it returns empty array
49+ /* istanbul ignore next: not supported in JSDOM */
50+ const readAllDirectoryEntries = async directoryReader => {
51+ const entries = [ ]
52+ let readEntries = await readEntriesPromise ( directoryReader )
53+ while ( readEntries . length > 0 ) {
54+ entries . push ( ...readEntries )
55+ readEntries = await readEntriesPromise ( directoryReader )
56+ }
57+ return entries
58+ }
59+
60+ // Wrap `.readEntries()` in a promise to make working with it easier
61+ // `.readEntries()` will return only some of the entries in a directory
62+ // (e.g. Chrome returns at most 100 entries at a time)
63+ /* istanbul ignore next: not supported in JSDOM */
64+ const readEntriesPromise = async directoryReader => {
65+ try {
66+ return await new Promise ( ( resolve , reject ) => {
67+ directoryReader . readEntries ( resolve , reject )
68+ } )
69+ } catch { }
70+ }
71+
2772// @vue /component
2873export const BFormFile = /*#__PURE__*/ Vue . extend ( {
2974 name : NAME ,
@@ -202,40 +247,23 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
202247 // Always emit original event
203248 this . $emit ( 'change' , evt )
204249 // Check if special `items` prop is available on event (drop mode)
205- // Can be disabled by setting no-traverse
206- const items = evt . dataTransfer && evt . dataTransfer . items
250+ // Can be disabled by setting ` no-traverse`
251+ const { files , items } = evt . dataTransfer || { }
207252 /* istanbul ignore next: not supported in JSDOM */
208253 if ( items && ! this . noTraverse ) {
209- const queue = [ ]
210- for ( let i = 0 ; i < items . length ; i ++ ) {
211- const item = items [ i ] . webkitGetAsEntry ( )
212- if ( item ) {
213- queue . push ( this . traverseFileTree ( item ) )
214- }
215- }
216- Promise . all ( queue ) . then ( filesArr => {
217- this . setFiles ( arrayFrom ( filesArr ) )
254+ getAllFileEntries ( items ) . then ( files => {
255+ this . setFiles ( files )
218256 } )
219- return
257+ } else {
258+ // Normal handling
259+ this . setFiles ( evt . target . files || files )
220260 }
221- // Normal handling
222- this . setFiles ( evt . target . files || evt . dataTransfer . files )
223261 } ,
224- setFiles ( files = [ ] ) {
225- if ( ! files ) {
226- /* istanbul ignore next: this will probably not happen */
227- this . selectedFile = null
228- } else if ( this . multiple ) {
229- // Convert files to array
230- const filesArray = [ ]
231- for ( let i = 0 ; i < files . length ; i ++ ) {
232- filesArray . push ( files [ i ] )
233- }
234- // Return file(s) as array
235- this . selectedFile = filesArray
262+ setFiles ( files ) {
263+ if ( this . multiple ) {
264+ this . selectedFile = arrayFrom ( files || [ ] )
236265 } else {
237- // Return single file object
238- this . selectedFile = files [ 0 ] || null
266+ this . selectedFile = files ? files [ 0 ] || null : null
239267 }
240268 } ,
241269 onReset ( ) {
@@ -265,39 +293,12 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
265293 return
266294 }
267295 this . dragging = false
268- if ( evt . dataTransfer . files && evt . dataTransfer . files . length > 0 ) {
269- this . onFileChange ( evt )
270- }
271- } ,
272- /* istanbul ignore next: not supported in JSDOM */
273- traverseFileTree ( item , path ) /* istanbul ignore next */ {
274- // Based on https://stackoverflow.com/questions/3590058
275- return new Promise ( resolve => {
276- path = path || ''
277- if ( item . isFile ) {
278- // Get file
279- item . file ( file => {
280- file . $path = path // Inject $path to file obj
281- resolve ( file )
282- } )
283- } else if ( item . isDirectory ) {
284- // Get folder contents
285- item . createReader ( ) . readEntries ( entries => {
286- const queue = [ ]
287- for ( let i = 0 ; i < entries . length ; i ++ ) {
288- queue . push ( this . traverseFileTree ( entries [ i ] , path + item . name + '/' ) )
289- }
290- Promise . all ( queue ) . then ( filesArr => {
291- resolve ( arrayFrom ( filesArr ) )
292- } )
293- } )
294- }
295- } )
296+ this . onFileChange ( evt )
296297 }
297298 } ,
298299 render ( h ) {
299300 // Form Input
300- const input = h ( 'input' , {
301+ const $ input = h ( 'input' , {
301302 ref : 'input' ,
302303 class : [
303304 {
@@ -317,11 +318,11 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
317318 } )
318319
319320 if ( this . plain ) {
320- return input
321+ return $ input
321322 }
322323
323324 // Overlay Labels
324- const label = h (
325+ const $ label = h (
325326 'label' ,
326327 {
327328 staticClass : 'custom-file-label' ,
@@ -352,7 +353,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
352353 drop : this . onDrop
353354 }
354355 } ,
355- [ input , label ]
356+ [ $ input, $ label]
356357 )
357358 }
358359} )
0 commit comments