diff --git a/lang/en/components.php b/lang/en/components.php index 843bd7e56..426a427d2 100644 --- a/lang/en/components.php +++ b/lang/en/components.php @@ -7,6 +7,8 @@ return [ // Image Manager 'image_select' => 'Image Select', 'image_upload' => 'Upload Image', + 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', + 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', 'image_all' => 'All', 'image_all_title' => 'View all images', 'image_book_title' => 'View images uploaded to this book', @@ -23,9 +25,9 @@ return [ 'images_deleted' => 'Images Deleted', 'image_preview' => 'Image Preview', 'image_upload_success' => 'Image uploaded successfully', + 'image_upload_failure' => 'Image failed to upload', 'image_update_success' => 'Image details successfully updated', 'image_delete_success' => 'Image successfully deleted', - 'image_upload_remove' => 'Remove', // Code Editor 'code_editor' => 'Edit Code', diff --git a/lang/en/errors.php b/lang/en/errors.php index 703d0edbe..eca39c946 100644 --- a/lang/en/errors.php +++ b/lang/en/errors.php @@ -45,7 +45,6 @@ return [ 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', 'uploaded' => 'The server does not allow uploads of this size. Please try a smaller file size.', - 'file_upload_timeout' => 'The file upload has timed out.', // Drawing & Images 'image_upload_error' => 'An error occurred uploading the image', diff --git a/resources/icons/file.svg b/resources/icons/file.svg index b6acb0988..2e87f2ca6 100644 --- a/resources/icons/file.svg +++ b/resources/icons/file.svg @@ -1,4 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/js/components/dropzone.js b/resources/js/components/dropzone.js index c6dc4df99..56c576f29 100644 --- a/resources/js/components/dropzone.js +++ b/resources/js/components/dropzone.js @@ -14,21 +14,28 @@ export class Dropzone extends Component { this.url = this.$opts.url; this.successMessage = this.$opts.successMessage; - this.removeMessage = this.$opts.removeMessage; - this.uploadLimit = Number(this.$opts.uploadLimit); // TODO - Use - this.uploadLimitMessage = this.$opts.uploadLimitMessage; // TODO - Use - this.timeoutMessage = this.$opts.timeoutMessage; // TODO - Use + this.errorMessage = this.$opts.errorMessage; + this.uploadLimitMb = Number(this.$opts.uploadLimit); + this.uploadLimitMessage = this.$opts.uploadLimitMessage; this.zoneText = this.$opts.zoneText; - // window.uploadTimeout // TODO - Use + this.fileAcceptTypes = this.$opts.fileAccept; this.setupListeners(); } setupListeners() { onSelect(this.selectButtons, this.manualSelectHandler.bind(this)); + this.setupDropTargetHandlers(); + } + setupDropTargetHandlers() { let depth = 0; + const reset = () => { + this.hideOverlay(); + depth = 0; + }; + this.dropTarget.addEventListener('dragenter', event => { event.preventDefault(); depth += 1; @@ -42,22 +49,13 @@ export class Dropzone extends Component { event.preventDefault(); }); - const reset = () => { - this.hideOverlay(); - depth = 0; - }; - - this.dropTarget.addEventListener('dragend', event => { - reset(); - }); - - this.dropTarget.addEventListener('dragleave', event => { + this.dropTarget.addEventListener('dragend', reset); + this.dropTarget.addEventListener('dragleave', () => { depth -= 1; if (depth === 0) { reset(); } }); - this.dropTarget.addEventListener('drop', event => { event.preventDefault(); reset(); @@ -70,10 +68,10 @@ export class Dropzone extends Component { } manualSelectHandler() { - const input = elem('input', {type: 'file', style: 'left: -400px; visibility: hidden; position: fixed;'}); + const input = elem('input', {type: 'file', style: 'left: -400px; visibility: hidden; position: fixed;', accept: this.fileAcceptTypes}); this.container.append(input); input.click(); - input.addEventListener('change', event => { + input.addEventListener('change', () => { for (const file of input.files) { this.createUploadFromFile(file); } @@ -101,7 +99,9 @@ export class Dropzone extends Component { * @return {Upload} */ createUploadFromFile(file) { - const {dom, status, progress, dismiss} = this.createDomForFile(file); + const { + dom, status, progress, dismiss, + } = this.createDomForFile(file); this.statusArea.append(dom); const component = this; @@ -116,6 +116,7 @@ export class Dropzone extends Component { status.setAttribute('data-status', 'error'); status.textContent = message; removeLoading(dom); + this.updateProgress(100); }, markSuccess(message) { status.setAttribute('data-status', 'success'); @@ -128,6 +129,12 @@ export class Dropzone extends Component { }, }; + // Enforce early upload filesize limit + if (file.size > (this.uploadLimitMb * 1000000)) { + upload.markError(this.uploadLimitMessage); + return upload; + } + this.startXhrForUpload(upload); return upload; @@ -139,14 +146,15 @@ export class Dropzone extends Component { startXhrForUpload(upload) { const formData = new FormData(); formData.append('file', upload.file, upload.file.name); + const component = this; const req = window.$http.createXMLHttpRequest('POST', this.url, { error() { - upload.markError('Upload failed'); // TODO - Update text + upload.markError(component.errorMessage); }, readystatechange() { if (this.readyState === XMLHttpRequest.DONE && this.status === 200) { - upload.markSuccess('Finished upload!'); + upload.markSuccess(component.successMessage); } else if (this.readyState === XMLHttpRequest.DONE && this.status >= 400) { const content = this.responseText; const data = content.startsWith('{') ? JSON.parse(content) : {message: content}; @@ -170,7 +178,7 @@ export class Dropzone extends Component { * @return {{image: Element, dom: Element, progress: Element, status: Element, dismiss: function}} */ createDomForFile(file) { - const image = elem('img', {src: ''}); + const image = elem('img', {src: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9.224 7.373a.924.924 0 0 0-.92.925l-.006 7.404c0 .509.412.925.921.925h5.557a.928.928 0 0 0 .926-.925v-5.553l-2.777-2.776Zm3.239 3.239V8.067l2.545 2.545z' style='fill:%23000;fill-opacity:.75'/%3E%3C/svg%3E"}); const status = elem('div', {class: 'dropzone-file-item-status'}, []); const progress = elem('div', {class: 'dropzone-file-item-progress'}); const imageWrap = elem('div', {class: 'dropzone-file-item-image-wrap'}, [image]); @@ -191,7 +199,7 @@ export class Dropzone extends Component { const dismiss = () => { dom.classList.add('dismiss'); - dom.addEventListener('animationend', event => { + dom.addEventListener('animationend', () => { dom.remove(); }); }; diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss index e889e1515..1c417b2ad 100644 --- a/resources/sass/_components.scss +++ b/resources/sass/_components.scss @@ -145,6 +145,7 @@ box-shadow: none; color: #FFF; padding: $-xs $-m; + cursor: pointer; } .popup-header button:not(.popup-header-close) { @@ -292,6 +293,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { .dropzone-file-item-image-wrap { width: 80px; position: relative; + background-color: var(--color-primary-light); img { object-fit: cover; width: 100%; @@ -322,7 +324,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { } .dropzone-file-item-status[data-status] { display: flex; - font-size: .8rem; + font-size: .6rem; font-weight: 500; line-height: 1.2; } @@ -407,7 +409,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { min-height: auto; padding: $-m; } - img { + .image-manager-viewer img { max-width: 100%; max-height: 180px; display: block; diff --git a/resources/views/pages/parts/image-manager.blade.php b/resources/views/pages/parts/image-manager.blade.php index 022ea1f1e..aee292f38 100644 --- a/resources/views/pages/parts/image-manager.blade.php +++ b/resources/views/pages/parts/image-manager.blade.php @@ -1,11 +1,11 @@
Here you can manage or select images that have been previously uploaded to the system.
-Upload a new image by dragging an image file into this window, - or by using the "Upload Image" button above.
+{{ trans('components.image_intro') }}
+{{ trans('components.image_intro_upload') }}