|
1 | 1 | <script> |
| 2 | + // @ts-nocheck |
2 | 3 | import { fromEvent } from "file-selector"; |
3 | 4 | import { |
4 | 5 | allFilesAccepted, |
5 | 6 | composeEventHandlers, |
6 | 7 | fileAccepted, |
7 | 8 | fileMatchSize, |
| 9 | + getBase64, |
8 | 10 | isEvtWithFiles, |
9 | 11 | isIeOrEdge, |
10 | 12 | isPropagationStopped, |
|
32 | 34 | /** @type {any} */ |
33 | 35 | export let inputElement = undefined; |
34 | 36 | export let required = false; |
35 | | - export let dropText = "Drag 'n' drop some files here, or click to select files"; |
| 37 | + export let dropText = "Drag and drop some files here, or click to select files"; |
| 38 | +
|
| 39 | + let innerDropText = dropText; |
| 40 | + let isLoading = false; |
| 41 | + let isError = false; |
| 42 | + let isSuccess = false; |
| 43 | + let reason = ''; |
| 44 | +
|
| 45 | + $: { |
| 46 | + if (isLoading) { |
| 47 | + innerDropText = "Uploading..."; |
| 48 | + disabled = true; |
| 49 | + } else if (isSuccess) { |
| 50 | + innerDropText = "Upload succeeded"; |
| 51 | + disabled = true; |
| 52 | + } else if (isError) { |
| 53 | + innerDropText = reason; |
| 54 | + disabled = true; |
| 55 | + } else { |
| 56 | + innerDropText = dropText; |
| 57 | + disabled = false; |
| 58 | + } |
| 59 | + } |
| 60 | +
|
| 61 | + const duration = 3000; |
36 | 62 | const dispatch = createEventDispatcher(); |
37 | 63 | |
38 | 64 | //state |
|
110 | 136 | |
111 | 137 | /** @param {any} event */ |
112 | 138 | function onDragEnterCb(event) { |
113 | | - console.log('inside drag enter: ', event); |
114 | 139 | event.preventDefault(); |
115 | 140 | stopPropagation(event); |
116 | 141 |
|
|
134 | 159 | |
135 | 160 | /** @param {any} event */ |
136 | 161 | function onDragOverCb(event) { |
137 | | - console.log('inside drag over: ', event); |
138 | 162 | event.preventDefault(); |
139 | 163 | stopPropagation(event); |
140 | 164 |
|
|
155 | 179 | |
156 | 180 | /** @param {any} event */ |
157 | 181 | function onDragLeaveCb(event) { |
158 | | - console.log('inside drag leave: ', event); |
159 | 182 | event.preventDefault(); |
160 | 183 | stopPropagation(event); |
161 | 184 |
|
|
203 | 226 | /** @type {any[]} */ |
204 | 227 | const fileRejections = []; |
205 | 228 |
|
| 229 | + /** @type {Promise<any>[]} */ |
| 230 | + const promises = []; |
| 231 | + isLoading = true; |
206 | 232 | files.forEach(file => { |
207 | | - const [accepted, acceptError] = fileAccepted(file, accept); |
208 | | - const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize); |
209 | | - if (accepted && sizeMatch) { |
210 | | - acceptedFiles.push(file); |
211 | | - } else { |
212 | | - const errors = [acceptError, sizeError].filter(e => e); |
213 | | - fileRejections.push({ file, errors }); |
214 | | - } |
| 233 | + promises.push(new Promise((resolve, reject) => { |
| 234 | + const [accepted, acceptError] = fileAccepted(file, accept); |
| 235 | + const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize); |
| 236 | + getBase64(file).then(res => { |
| 237 | + if (accepted && sizeMatch) { |
| 238 | + acceptedFiles.push({ |
| 239 | + file_name: file.name, |
| 240 | + file_path: file.path, |
| 241 | + url: res |
| 242 | + }); |
| 243 | + } else { |
| 244 | + const errors = [acceptError, sizeError].filter(Boolean); |
| 245 | + fileRejections.push({ file, errors }); |
| 246 | + } |
| 247 | + resolve('done'); |
| 248 | + }); |
| 249 | + })); |
215 | 250 | }); |
216 | 251 |
|
217 | | - if (!multiple && acceptedFiles.length > 1) { |
218 | | - // Reject everything and empty accepted files |
219 | | - acceptedFiles.forEach(file => { |
220 | | - fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); |
221 | | - }); |
222 | | - acceptedFiles.splice(0); |
223 | | - } |
| 252 | + Promise.all(promises).then(() => { |
| 253 | + isLoading = false; |
224 | 254 |
|
225 | | - // Files dropped keep input in sync |
226 | | - if (event.dataTransfer) { |
227 | | - inputElement.files = event.dataTransfer.files; |
228 | | - } |
| 255 | + if (!multiple && acceptedFiles.length > 1) { |
| 256 | + // Reject everything and empty accepted files |
| 257 | + acceptedFiles.forEach(file => { |
| 258 | + fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }); |
| 259 | + }); |
| 260 | + acceptedFiles.splice(0); |
| 261 | + } |
229 | 262 |
|
230 | | - state.acceptedFiles = acceptedFiles; |
231 | | - state.fileRejections = fileRejections; |
| 263 | + // Files dropped keep input in sync |
| 264 | + if (event.dataTransfer) { |
| 265 | + inputElement.files = event.dataTransfer.files; |
| 266 | + } |
232 | 267 |
|
233 | | - dispatch("drop", { |
234 | | - acceptedFiles, |
235 | | - fileRejections, |
236 | | - event |
237 | | - }); |
| 268 | + state.acceptedFiles = acceptedFiles; |
| 269 | + state.fileRejections = fileRejections; |
238 | 270 |
|
239 | | - if (fileRejections.length > 0) { |
240 | | - dispatch("droprejected", { |
| 271 | + dispatch("drop", { |
| 272 | + acceptedFiles, |
241 | 273 | fileRejections, |
242 | 274 | event |
243 | 275 | }); |
244 | | - } |
245 | 276 |
|
246 | | - if (acceptedFiles.length > 0) { |
247 | | - dispatch("dropaccepted", { |
248 | | - acceptedFiles, |
249 | | - event |
250 | | - }); |
251 | | - } |
| 277 | + if (fileRejections.length > 0) { |
| 278 | + dispatch("droprejected", { |
| 279 | + fileRejections, |
| 280 | + event |
| 281 | + }); |
| 282 | +
|
| 283 | + isError = true; |
| 284 | + reason = fileRejections[0]?.errors[0]?.message || "Upload failed"; |
| 285 | + setTimeout(() => { |
| 286 | + reason = ''; |
| 287 | + isError = false; |
| 288 | + }, duration); |
| 289 | + } |
| 290 | +
|
| 291 | + if (acceptedFiles.length > 0) { |
| 292 | + dispatch("dropaccepted", { |
| 293 | + acceptedFiles, |
| 294 | + event |
| 295 | + }); |
| 296 | + } |
| 297 | +
|
| 298 | + if (acceptedFiles.length > 0 && fileRejections.length === 0) { |
| 299 | + isSuccess = true; |
| 300 | + setTimeout(() => { |
| 301 | + isSuccess = false; |
| 302 | + }, duration); |
| 303 | + } |
| 304 | + }); |
252 | 305 | }); |
253 | 306 | } |
254 | 307 | resetState(); |
|
324 | 377 | bind:this={rootRef} |
325 | 378 | tabindex="0" |
326 | 379 | role="button" |
327 | | - class="{disableDefaultStyles ? '' : 'dropzone'} {containerClasses}" |
328 | | - style={containerStyles} |
| 380 | + class="{disableDefaultStyles ? '' : 'file-dropzone'} {containerClasses}" |
| 381 | + style={`${containerStyles}`} |
329 | 382 | on:keydown={composeKeyboardHandler(onKeyDownCb)} |
330 | 383 | on:focus={composeKeyboardHandler(onFocusCb)} |
331 | 384 | on:blur={composeKeyboardHandler(onBlurCb)} |
|
349 | 402 | style="display: none;" |
350 | 403 | /> |
351 | 404 | <slot> |
352 | | - <p class="drop-text">{dropText}</p> |
| 405 | + <p class={`file-drop-text ${isError ? 'text-danger' : isSuccess ? 'text-success' : ''}`}>{innerDropText}</p> |
353 | 406 | </slot> |
354 | 407 | </div> |
355 | 408 | |
356 | 409 | <style> |
357 | | - .dropzone { |
| 410 | + .file-dropzone { |
358 | 411 | flex: 1; |
359 | 412 | display: flex; |
360 | 413 | flex-direction: column; |
|
368 | 421 | color: #bdbdbd; |
369 | 422 | outline: none; |
370 | 423 | transition: border 0.24s ease-in-out; |
| 424 | + margin-top: 3px; |
| 425 | + width: 100%; |
| 426 | + height: 10rem; |
371 | 427 | } |
372 | 428 |
|
373 | | - .drop-text { |
| 429 | + .file-drop-text { |
374 | 430 | margin-top: auto; |
375 | 431 | margin-bottom: auto; |
376 | 432 | } |
|
0 commit comments