11<template >
2- <label v-if =" purpose === 'avatar'" for =" fileInput" >Choose Image File</label >
3- <input type =" file" name =" fileInput" id =" fileInput" @change =" uploadFile" ref =" fileInput" :multiple =" multiple" @drop =" uploadFile" @dragenter.prevent @dragover.prevent ><br >
4- <progress v-if =" !purpose" ref =" progressBar" style =" width : 100% " :value =" imagesProgress" max =" 100" ></progress >
2+ <div class =" input-container" @dragend.prevent.self =" hover=false;" @dragexit.prevent.self =" hover=false" @dragleave.prevent.self =" hover=false" >
3+ <div >
4+ <label v-if =" purpose !== 'editor'" for =" fileInput" class =" input-label" :class =" { 'hidden': hover }" >Choose Image File or Drag and Drop</label >
5+ <input type =" file" name =" fileInput" id =" fileInput" @change =" uploadFile" ref =" fileInput" :multiple =" multiple" @dragenter.prevent.self.stop =" hover=true" @dragover.prevent.self.stop =" hover=true" :disabled =" hover" :class =" { 'hidden': hover }" :hidden =" purpose === 'editor' ? 'hidden' : null" >
6+ <div class =" progress-container" :class =" { 'hidden': hover }" >
7+ <div class =" progress" :class =" { 'progress-editor': purpose === 'editor'}" >
8+ <span class =" meter" :style =" { width: imagesProgress + '%' }" ></span >
9+ </div >
10+ </div >
11+ <div v-if =" purpose !== 'editor'" class =" input-info" >Images should not exceed 100kB</div >
12+ </div >
13+ <div :class =" {'visible': hover, 'hidden': !hover, 'editorMode': showDropzone }" id =" dropzone" @drop.stop.prevent =" uploadFile" @dragenter.prevent.self =" hover=true" @dragover.prevent.self =" hover=true" @dragend.prevent.self =" hover=false" @dragexit.prevent.self =" hover=false" @dragleave.prevent.self =" hover=false" >
14+ Drop Image{{ purpose === 'editor' ? 's' : ''}} Here
15+ </div >
16+ </div >
17+
18+ <div v-if =" purpose === 'editor'" class =" upload-editor" >
19+ <a href =" #" data-balloon =" Upload Images" @click.prevent =" fileInput.click()" ><i class =" far fa-images" aria-hidden =" true" ></i ></a >
20+ <span v-if =" images.length > 0 && purpose === 'editor'" >
21+ (<a href =" #" @click.prevent =" openImageModal()" >
22+ <span v-if =" images.length === 1" >
23+ view <span v-html =" images.length" ></span > image
24+ </span >
25+ <span v-if =" images.length > 1" >
26+ view <span v-html =" images.length" ></span > images
27+ </span >
28+ </a >)
29+ </span >
30+ </div >
31+
32+
533</template >
634
735<script >
8- import { reactive , toRefs } from ' vue'
36+ import { reactive , toRefs , watch } from ' vue'
937import { policy , upload } from ' @/composables/services/image-upload'
1038
1139Promise .each = async (arr , fn ) => { for (const item of arr) await fn (item) }
1240
1341export default {
1442 name: ' image-uploader' ,
15- props: [' onUpload-success' , ' onUpload-error' , ' purpose' ],
43+ props: [' onUpload-success' , ' onUpload-error' , ' onHover-stop ' , ' purpose' , ' showDropzone ' ],
1644 setup (props , { emit }) { // , { emit }) {
1745 /* View Methods */
1846 const uploadFile = (e ) => {
47+ v .hover = false
1948 emit (' upload-error' , null ) // clear previous errors
20-
2149 let files = e .target .files || e .dataTransfer .files
50+ v .fileInput .files = files
2251 if (! files .length ) return
2352 let images = []
2453 for (let i = 0 ; i < files .length ; i++ ) {
2554 let file = files[i]
2655 if (! file .type .match (/ image. * / )) continue
2756 images .push (file)
2857 }
29- if (props .purpose === ' avatar' || props .purpose === ' logo' || props .purpose === ' favicon' ) { images = [images[0 ]] }
58+ if (props .purpose === ' avatar' || props .purpose === ' logo' || props .purpose === ' favicon' ) {
59+ images = [images[0 ]]
60+ const dataTransfer = new DataTransfer ()
61+ dataTransfer .items .add (images[0 ])
62+ v .fileInput .files = dataTransfer .files
63+ }
3064
3165 if (images .length > 10 ) return handleError (' Error: Exceeded 10 images.' )
3266 else if (images .length > 0 ) {
@@ -68,9 +102,9 @@ export default {
68102 v .uploadingImages = v .currentImages .length
69103 return policy (v .currentImages )
70104 // upload each image
71- .then (images => {
105+ .then (imagesToUpload => {
72106 let index = 0
73- return Promise .each (images , image => {
107+ return Promise .each (imagesToUpload , image => {
74108 v .currentImages [index].status = ' Starting'
75109 return Promise .resolve (upload (image)
76110 .progress (p => updateImagesUploading (index, p))
@@ -84,7 +118,6 @@ export default {
84118 })
85119 .success (url => {
86120 updateImagesUploading (index, 100 , url)
87- // TODO(akinsey): if ($scope.onDone) { $scope.onDone({data: url}); }
88121 if (props .purpose === ' avatar' || props .purpose === ' logo' || props .purpose === ' favicon' ) {
89122 v .model = url
90123
@@ -106,11 +139,17 @@ export default {
106139 .then (() => { if (errImages .length ) handleError (v .warningMsg ) })
107140 })
108141 .catch (() => handleError (v .warningMsg ))
142+ .finally (() => {
143+ v .currentImages = []
144+ if (props .purpose === ' editor' ) setTimeout (() => v .imagesProgress = 0 , 500 )
145+ })
109146 }
110147 }
111148
112149 const handleError = msg => {
113150 v .currentImages = []
151+ v .image = []
152+ v .imagesProgress = 0
114153 emit (' upload-error' , msg)
115154 }
116155
@@ -147,61 +186,15 @@ export default {
147186 if (v .uploadingImages <= 0 ) v .imagesUploading = false
148187 }
149188
150- // const progressHandler = e => {
151- // v.amountUploaded.innerHTML = 'Uploaded ' + e.loaded + ' bytes of ' + e.total
152- // var percent = (e.loaded / e.total) * 100
153- // v.progressBar.value = Math.round(percent)
154- // v.status.innerHTML = Math.round(percent) + '% uploaded... please wait'
155- // }
156-
157- // const completeHandler = e => {
158- // v.status.innerHTML = e.target.responseText
159- // emit('upload-success')
160- // v.progressBar.value = 0
161- // }
162-
163- // const errorHandler = e => {
164- // v.status.innerHTML = 'Upload Failed'
165- // v.status.innerHTML = e.target.responseText
166- // emit('upload-error')
167- // }
168-
169- // const abortHandler = () => {
170- // v.status.innerHTML = 'Upload Aborted'
171- // emit('upload-error')
172- // }
173-
174- // var cancelEvent = function(e) {
175- // e.stopPropagation();
176- // e.preventDefault();
177- // };
178-
179- // var removeDrag = function(e) {
180- // e.stopPropagation();
181- // e.preventDefault();
182- // };
183-
184- // var dropEvent = function(e) {
185- // removeDrag(e);
186- // uploadFile(e)
187- // };
188-
189- // $parent.on('dragenter', cancelEvent);
190- // $parent.on('dragover', cancelEvent);
191- // $dragZone.on('dragenter', cancelEvent);
192- // $dragZone.on('dragover', cancelEvent);
193- // $dragZone.on('dragend', removeDrag);
194- // $dragZone.on('dragexit', removeDrag);
195- // $dragZone.on('dragleave', removeDrag);
196-
197- // $parent.on('drop', dropEvent);
198- // $dragZone.on('drop', dropEvent);
189+ const openImageModal = () => console .log (' open image modal' )
199190
200191 const v = reactive ({
192+ hover: false ,
201193 fileInput: null ,
202- multiple: ! props .purpose ,
194+ multiple: props .purpose === ' editor ' ,
203195 progressBar: null ,
204196 amountUploaded: null ,
197+ showDropzone: props .showDropzone ,
205198 currentImages: [],
206199 images: [],
207200 imagesUploading: false ,
@@ -214,13 +207,67 @@ export default {
214207 status: null
215208 })
216209
217- return { ... toRefs (v), uploadFile }
210+ watch (() => v .hover , a => a ? null : emit (' hover-stop' ))
211+
212+ return { ... toRefs (v), uploadFile, openImageModal }
218213 }
219214}
220215 </script >
221216
222217<style lang="scss">
218+ .input-container { position : relative ; height : 4.625rem ; }
219+ .input-section label .hidden { opacity : .1 ; }
220+
223221 #fileInput {
224- height : auto ;
222+ height : 4.625rem ;
223+ & .hidden {
224+ opacity : .1 ;
225+ border : 1px solid transparent ;
226+ }
225227 }
228+ #dropzone {
229+ position : absolute ;
230+ float : left ;
231+ border : 3px dashed $color-primary ;
232+ border-radius : 3px ;
233+ box-shadow : none ;
234+ margin-bottom : 0 ;
235+ padding-top : 2rem ;
236+ padding-bottom : 1rem ;
237+ height : 4.625rem ;
238+ text-align : center ;
239+ font-weight : bold ;
240+ font-size : 1.5rem ;
241+ line-height : .5rem ;
242+ color : $base-background-color ;
243+ width : 100% ;
244+ & .visible {
245+ display : block ;
246+ z-index : 99999 ;
247+ background-color : $color-primary-transparent ;
248+ margin-top : -6.5625rem ;
249+ }
250+ & .hidden { display : none ; }
251+ & .editorMode {
252+ display : block ;
253+ height : 150vh ;
254+ bottom : 0 ;
255+ z-index : 99999 ;
256+ border : 0 ;
257+ background-color : transparent ;
258+ }
259+ }
260+
261+ .progress-container {
262+ text-align : center ;
263+ margin-bottom : 0.625rem ;
264+ margin-top : -.3125rem ;
265+ & .hidden { opacity : .1 ; }
266+ .progress {
267+ height : .25rem ;
268+ border-radius : 3px ;
269+ .meter { background : $color-primary ; height : 100% ; display : block ; border-radius : 3px ; }
270+ }
271+ }
272+
226273 </style >
0 commit comments