1616 'h-32': !props.multiple,
1717 }"
1818 >
19- <div class =" flex flex-col items-center justify-center pt-5 pb-6" >
20-
21-
22- <svg v-if =" !selectedFiles.length" class =" w-8 h-8 mb-4 text-lightDropzoneIcon dark:text-darkDropzoneIcon" aria-hidden =" true" xmlns =" http://www.w3.org/2000/svg" fill =" none" viewBox =" 0 0 20 16" >
23- <path stroke =" currentColor" stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2" />
24- </svg >
25- <div v-else class =" flex items-center justify-center flex-wrap gap-1 w-full mt-1 mb-4" >
26- <template v-for =" file in selectedFiles " >
27- <p class =" text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon flex items-center gap-1" >
28- <IconFileSolid class =" w-5 h-5" />
29- {{ file.name }} ({{ humanifySize(file.size) }})
30- </p >
31- </template >
32-
19+ <input
20+ :id =" id"
21+ type =" file"
22+ class =" hidden"
23+ :accept =" props.extensions.join(',')"
24+ @change =" $event.target && doEmit(($event.target as HTMLInputElement).files!)"
25+ :multiple =" props.multiple || false"
26+ />
27+
28+ <div class =" flex flex-col items-center justify-center pt-5 pb-6" >
29+ <svg
30+ v-if =" !selectedFiles.length"
31+ class =" w-8 h-8 mb-4 text-lightDropzoneIcon dark:text-darkDropzoneIcon"
32+ xmlns =" http://www.w3.org/2000/svg"
33+ fill =" none"
34+ viewBox =" 0 0 20 16"
35+ >
36+ <path
37+ stroke =" currentColor"
38+ stroke-linecap =" round"
39+ stroke-linejoin =" round"
40+ stroke-width =" 2"
41+ d =" M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5
42+ 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0
43+ 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
44+ />
45+ </svg >
46+
47+ <div
48+ v-else
49+ class =" flex items-center justify-center py-1 flex-wrap gap-2 w-full gap-2 mt-1 mb-4 px-4"
50+ >
51+ <template v-for =" (file , index ) in selectedFiles " :key =" index " >
52+ <div
53+ class =" text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon bg-lightDropzoneBackgroundHover dark:bg-darkDropzoneBackgroundHover rounded-md
54+ flex items-center gap-1 px-2 py-1 group"
55+ >
56+ <IconFileSolid class =" w-4 h-4 flex-shrink-0" />
57+ <span class =" truncate max-w-[200px]" >{{ file.name }}</span >
58+ <span class =" text-xs" >({{ humanifySize(file.size) }})</span >
59+ <button
60+ type =" button"
61+ @click.prevent.stop =" removeFile(index)"
62+ class =" text-lightDropzoneIcon dark:text-darkDropzoneIcon hover:text-red-600 dark:hover:text-red-400
63+ opacity-70 hover:opacity-100 transition-all"
64+ :title =" $t('Remove file')"
65+ >
66+ <svg class =" w-4 h-4" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
67+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M6 18L18 6M6 6l12 12" />
68+ </svg >
69+ </button >
3370 </div >
34-
35- <p v-if =" !selectedFiles.length" class =" mb-2 text-sm text-lightDropzoneText dark:text-darkDropzoneText" ><span class =" font-semibold" >{{ $t('Click to upload') }}</span > {{ $t('or drag and drop') }}</p >
36- <p class =" text-xs text-lightDropzoneText dark:text-darkDropzoneText" >
37- {{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }}
38- <template v-if =" props .maxSizeBytes " >
39- (Max size: {{ humanifySize(props.maxSizeBytes) }})
40- </template >
41- </p >
71+ </template >
4272 </div >
43- <input :id =" id" type =" file" class =" hidden"
44- :accept =" props.extensions.join(', ')"
45- @change =" $event.target && doEmit(($event.target as HTMLInputElement).files!)"
46- :multiple =" props.multiple || false"
47- />
73+
74+ <p
75+ v-if =" !selectedFiles.length"
76+ class =" mb-2 text-sm text-lightDropzoneText dark:text-darkDropzoneText"
77+ >
78+ <span class =" font-semibold" >{{ $t('Click to upload') }}</span >
79+ {{ $t('or drag and drop') }}
80+ </p >
81+
82+ <p class =" text-xs text-lightDropzoneText dark:text-darkDropzoneText" >
83+ {{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }}
84+ <template v-if =" props .maxSizeBytes " >
85+ (Max size: {{ humanifySize(props.maxSizeBytes) }})
86+ </template >
87+ </p >
88+ </div >
4889 </label >
49- </form >
90+ </form >
5091</template >
5192
5293<script setup lang="ts">
@@ -73,12 +114,17 @@ const selectedFiles: Ref<{
73114 mime: string ,
74115}[]> = ref ([]);
75116
117+ const storedFiles: Ref <File []> = ref ([]);
118+
76119watch (() => props .modelValue , (files ) => {
77- selectedFiles .value = Array .from (files ).map (file => ({
78- name: file .name ,
79- size: file .size ,
80- mime: file .type ,
81- }));
120+ if (files && files .length > 0 ) {
121+ selectedFiles .value = Array .from (files ).map (file => ({
122+ name: file .name ,
123+ size: file .size ,
124+ mime: file .type ,
125+ }));
126+ storedFiles .value = Array .from (files );
127+ }
82128});
83129
84130function doEmit(filesIn : FileList ) {
@@ -110,26 +156,37 @@ function doEmit(filesIn: FileList) {
110156 });
111157 return ;
112158 }
113-
159+
114160 validFiles .push (file );
115161 });
116162
117163 if (! multiple ) {
118- validFiles .splice (1 );
164+ storedFiles .value = validFiles .slice (0 , 1 );
165+ } else {
166+ storedFiles .value = [... storedFiles .value , ... validFiles ];
119167 }
120- selectedFiles .value = validFiles .map (file => ({
168+
169+ selectedFiles .value = storedFiles .value .map (file => ({
121170 name: file .name ,
122171 size: file .size ,
123172 mime: file .type ,
124173 }));
125174
126- emit (' update:modelValue' , validFiles );
175+ emit (' update:modelValue' , storedFiles .value );
176+
127177}
128178
129179const dragging = ref (false );
130180
181+ function removeFile(index : number ) {
182+ storedFiles .value = storedFiles .value .filter ((_ , i ) => i !== index );
183+ selectedFiles .value = selectedFiles .value .filter ((_ , i ) => i !== index );
184+ emit (' update:modelValue' , storedFiles .value );
185+ }
186+
131187function clear() {
132188 selectedFiles .value = [];
189+ storedFiles .value = [];
133190 emit (' update:modelValue' , []);
134191 const form = document .getElementById (id )?.closest (' form' );
135192 form ?.reset ();
0 commit comments