From e36cdaad0d56280aec7cd02c0c78e77dc0c4d9a1 Mon Sep 17 00:00:00 2001 From: Dan Brown <ssddanbrown@googlemail.com> Date: Wed, 26 Apr 2023 16:41:34 +0100 Subject: [PATCH] Updated attachments to work with new dropzone - Fixes existing broken attachment edit tabs. - Redesigns area to move away from old tabbed interface. - Integrates new dropzone system, for both addition and edit. --- lang/en/components.php | 1 - lang/en/entities.php | 4 +- lang/en/errors.php | 1 + resources/js/components/attachments.js | 38 ++++++--- resources/js/components/editor-toolbox.js | 4 + resources/sass/_components.scss | 11 ++- resources/sass/_layout.scss | 9 +++ .../attachments/manager-edit-form.blade.php | 33 ++++++-- .../attachments/manager-link-form.blade.php | 3 + resources/views/attachments/manager.blade.php | 80 +++++++------------ resources/views/form/dropzone.blade.php | 8 -- .../views/form/simple-dropzone.blade.php | 21 +++++ .../views/pages/parts/image-manager.blade.php | 2 +- 13 files changed, 131 insertions(+), 84 deletions(-) delete mode 100644 resources/views/form/dropzone.blade.php create mode 100644 resources/views/form/simple-dropzone.blade.php diff --git a/lang/en/components.php b/lang/en/components.php index 426a427d2..cd5dca251 100644 --- a/lang/en/components.php +++ b/lang/en/components.php @@ -25,7 +25,6 @@ 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', diff --git a/lang/en/entities.php b/lang/en/entities.php index 9b02f3111..9614f92fe 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -311,12 +311,12 @@ return [ 'attachments' => 'Attachments', 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', 'attachments_explain_instant_save' => 'Changes here are saved instantly.', - 'attachments_items' => 'Attached Items', 'attachments_upload' => 'Upload File', 'attachments_link' => 'Attach Link', + 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', 'attachments_set_link' => 'Set Link', 'attachments_delete' => 'Are you sure you want to delete this attachment?', - 'attachments_dropzone' => 'Drop files or click here to attach a file', + 'attachments_dropzone' => 'Drop files here to upload', 'attachments_no_files' => 'No files have been uploaded', 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', 'attachments_link_name' => 'Link Name', diff --git a/lang/en/errors.php b/lang/en/errors.php index eca39c946..6991f96e4 100644 --- a/lang/en/errors.php +++ b/lang/en/errors.php @@ -53,6 +53,7 @@ return [ // Attachments 'attachment_not_found' => 'Attachment not found', + 'attachment_upload_error' => 'An error occurred uploading the attachment file', // Pages 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', diff --git a/resources/js/components/attachments.js b/resources/js/components/attachments.js index 9555a59e8..e185b3c5c 100644 --- a/resources/js/components/attachments.js +++ b/resources/js/components/attachments.js @@ -1,4 +1,4 @@ -import {showLoading} from '../services/dom'; +import {onSelect, showLoading} from '../services/dom'; import {Component} from './component'; export class Attachments extends Component { @@ -8,15 +8,16 @@ export class Attachments extends Component { this.pageId = this.$opts.pageId; this.editContainer = this.$refs.editContainer; this.listContainer = this.$refs.listContainer; - this.mainTabs = this.$refs.mainTabs; - this.list = this.$refs.list; + this.linksContainer = this.$refs.linksContainer; + this.listPanel = this.$refs.listPanel; + this.attachLinkButton = this.$refs.attachLinkButton; this.setupListeners(); } setupListeners() { const reloadListBound = this.reloadList.bind(this); - this.container.addEventListener('dropzone-success', reloadListBound); + this.container.addEventListener('dropzone-upload-success', reloadListBound); this.container.addEventListener('ajax-form-success', reloadListBound); this.container.addEventListener('sortable-list-sort', event => { @@ -39,16 +40,29 @@ export class Attachments extends Component { markdown: contentTypes['text/plain'], }); }); + + this.attachLinkButton.addEventListener('click', () => { + this.showSection('links'); + }); + } + + showSection(section) { + const sectionMap = { + links: this.linksContainer, + edit: this.editContainer, + list: this.listContainer, + }; + + for (const [name, elem] of Object.entries(sectionMap)) { + elem.toggleAttribute('hidden', name !== section); + } } reloadList() { this.stopEdit(); - /** @var {Tabs} */ - const tabs = window.$components.firstOnElement(this.mainTabs, 'tabs'); - tabs.show('attachment-panel-items'); window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => { - this.list.innerHTML = resp.data; - window.$components.init(this.list); + this.listPanel.innerHTML = resp.data; + window.$components.init(this.listPanel); }); } @@ -59,8 +73,7 @@ export class Attachments extends Component { } async startEdit(id) { - this.editContainer.classList.remove('hidden'); - this.listContainer.classList.add('hidden'); + this.showSection('edit'); showLoading(this.editContainer); const resp = await window.$http.get(`/attachments/edit/${id}`); @@ -69,8 +82,7 @@ export class Attachments extends Component { } stopEdit() { - this.editContainer.classList.add('hidden'); - this.listContainer.classList.remove('hidden'); + this.showSection('list'); } } diff --git a/resources/js/components/editor-toolbox.js b/resources/js/components/editor-toolbox.js index 4d3c0ae75..1f1fce9dc 100644 --- a/resources/js/components/editor-toolbox.js +++ b/resources/js/components/editor-toolbox.js @@ -13,6 +13,10 @@ export class EditorToolbox extends Component { // Set the first tab as active on load this.setActiveTab(this.contentElements[0].dataset.tabContent); + + setTimeout(() => { + this.setActiveTab('files', true); + }, 500); } setupListeners() { diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss index d1a11a964..e28ec2836 100644 --- a/resources/sass/_components.scss +++ b/resources/sass/_components.scss @@ -228,6 +228,15 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { animation: dzAnimIn 240ms ease-in-out; } +.dropzone-landing-area { + background-color: var(--color-primary-light); + padding: $-m $-l; + width: 100%; + border: 1px dashed var(--color-primary); + color: var(--color-primary); + border-radius: 4px; +} + @keyframes dzAnimIn { 0% { opacity: 0; @@ -319,7 +328,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { .dropzone-file-item-label, .dropzone-file-item-status { align-items: center; - font-size: .9rem; + font-size: .8rem; font-weight: 700; } .dropzone-file-item-status[data-status] { diff --git a/resources/sass/_layout.scss b/resources/sass/_layout.scss index 19333faf7..541978a65 100644 --- a/resources/sass/_layout.scss +++ b/resources/sass/_layout.scss @@ -253,6 +253,15 @@ body.flexbox { position: relative; } +.fixed { + position: fixed; + z-index: 20; + &.top-right { + top: 0; + right: 0; + } +} + .hidden { display: none !important; } diff --git a/resources/views/attachments/manager-edit-form.blade.php b/resources/views/attachments/manager-edit-form.blade.php index 15837448a..9422a0752 100644 --- a/resources/views/attachments/manager-edit-form.blade.php +++ b/resources/views/attachments/manager-edit-form.blade.php @@ -17,18 +17,35 @@ </div> <div component="tabs" class="tab-container"> - <div class="nav-tabs"> - <button refs="tabs@toggleFile" type="button" class="tab-item {{ $attachment->external ? '' : 'selected' }}">{{ trans('entities.attachments_upload') }}</button> - <button refs="tabs@toggleLink" type="button" class="tab-item {{ $attachment->external ? 'selected' : '' }}">{{ trans('entities.attachments_set_link') }}</button> + <div class="nav-tabs" role="tablist"> + <button id="attachment-edit-file-tab" + type="button" + aria-controls="attachment-edit-file-panel" + aria-selected="{{ $attachment->external ? 'false' : 'true' }}" + role="tab">{{ trans('entities.attachments_upload') }}</button> + <button id="attachment-edit-link-tab" + type="button" + aria-controls="attachment-edit-link-panel" + aria-selected="{{ $attachment->external ? 'true' : 'false' }}" + role="tab">{{ trans('entities.attachments_set_link') }}</button> </div> - <div refs="tabs@contentFile" class="mb-m {{ $attachment->external ? 'hidden' : '' }}"> - @include('form.dropzone', [ + <div id="attachment-edit-file-panel" + @if($attachment->external) hidden @endif + tabindex="0" + role="tabpanel" + aria-labelledby="attachment-edit-file-tab" + class="mb-m"> + @include('form.simple-dropzone', [ 'placeholder' => trans('entities.attachments_edit_drop_upload'), 'url' => url('/attachments/upload/' . $attachment->id), 'successMessage' => trans('entities.attachments_file_updated'), ]) </div> - <div refs="tabs@contentLink" class="{{ $attachment->external ? '' : 'hidden' }}"> + <div id="attachment-edit-link-panel" + @if(!$attachment->external) hidden @endif + tabindex="0" + role="tabpanel" + aria-labelledby="attachment-edit-link-tab"> <div class="form-group"> <label for="attachment_edit_url">{{ trans('entities.attachments_link_url') }}</label> <input type="text" id="attachment_edit_url" @@ -43,6 +60,8 @@ </div> <button component="event-emit-select" - option:event-emit-select:name="edit-back" type="button" class="button outline">{{ trans('common.back') }}</button> + option:event-emit-select:name="edit-back" + type="button" + class="button outline">{{ trans('common.back') }}</button> <button refs="ajax-form@submit" type="button" class="button">{{ trans('common.save') }}</button> </div> \ No newline at end of file diff --git a/resources/views/attachments/manager-link-form.blade.php b/resources/views/attachments/manager-link-form.blade.php index b51daa40e..e9ec2d6dc 100644 --- a/resources/views/attachments/manager-link-form.blade.php +++ b/resources/views/attachments/manager-link-form.blade.php @@ -22,6 +22,9 @@ <div class="text-neg text-small">{{ $errors->first('attachment_link_url') }}</div> @endif </div> + <button component="event-emit-select" + option:event-emit-select:name="edit-back" + type="button" class="button outline">{{ trans('common.cancel') }}</button> <button refs="ajax-form@submit" type="button" class="button">{{ trans('entities.attach') }}</button> diff --git a/resources/views/attachments/manager.blade.php b/resources/views/attachments/manager.blade.php index 7d14d00e7..4eeee24bb 100644 --- a/resources/views/attachments/manager.blade.php +++ b/resources/views/attachments/manager.blade.php @@ -6,66 +6,44 @@ class="toolbox-tab-content"> <h4>{{ trans('entities.attachments') }}</h4> - <div class="px-l files"> + <div component="dropzone" + option:dropzone:url="{{ url('/attachments/upload?uploaded_to=' . $page->id) }}" + option:dropzone:success-message="{{ trans('entities.attachments_file_uploaded') }}" + option:dropzone:error-message="{{ trans('errors.attachment_upload_error') }}" + option:dropzone:upload-limit="{{ config('app.upload_limit') }}" + option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}" + option:dropzone:zone-text="{{ trans('entities.attachments_dropzone') }}" + option:dropzone:file-accept="*" + class="px-l files"> - <div refs="attachments@listContainer"> + <div refs="attachments@list-container dropzone@drop-target" class="relative"> <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p> - <div component="tabs" refs="attachments@mainTabs" class="tab-container"> - <div role="tablist"> - <button id="attachment-tab-items" - role="tab" - aria-selected="true" - aria-controls="attachment-panel-items" - type="button" - class="tab-item">{{ trans('entities.attachments_items') }}</button> - <button id="attachment-tab-upload" - role="tab" - aria-selected="false" - aria-controls="attachment-panel-upload" - type="button" - class="tab-item">{{ trans('entities.attachments_upload') }}</button> - <button id="attachment-tab-links" - role="tab" - aria-selected="false" - aria-controls="attachment-panel-links" - type="button" - class="tab-item">{{ trans('entities.attachments_link') }}</button> - </div> - <div id="attachment-panel-items" - tabindex="0" - role="tabpanel" - aria-labelledby="attachment-tab-items" - refs="attachments@list"> - @include('attachments.manager-list', ['attachments' => $page->attachments->all()]) - </div> - <div id="attachment-panel-upload" - tabindex="0" - role="tabpanel" - hidden - aria-labelledby="attachment-tab-upload"> - @include('form.dropzone', [ - 'placeholder' => trans('entities.attachments_dropzone'), - 'url' => url('/attachments/upload?uploaded_to=' . $page->id), - 'successMessage' => trans('entities.attachments_file_uploaded'), - ]) - </div> - <div id="attachment-panel-links" - tabindex="0" - role="tabpanel" - hidden - aria-labelledby="attachment-tab-links" - class="link-form-container"> - @include('attachments.manager-link-form', ['pageId' => $page->id]) - </div> + <hr class="mb-s"> + + <div class="flex-container-row"> + <button refs="dropzone@select-button" type="button" class="button outline small">{{ trans('entities.attachments_upload') }}</button> + <button refs="attachments@attach-link-button" type="button" class="button outline small">{{ trans('entities.attachments_link') }}</button> + </div> + <div> + <p class="text-muted text-small">{{ trans('entities.attachments_upload_drop') }}</p> + </div> + <div refs="dropzone@status-area" class="fixed top-right px-m py-m"></div> + + <hr> + + <div refs="attachments@list-panel"> + @include('attachments.manager-list', ['attachments' => $page->attachments->all()]) </div> </div> - <div refs="attachments@editContainer" class="hidden attachment-edit-container"> - + <div refs="attachments@links-container" hidden class="link-form-container"> + @include('attachments.manager-link-form', ['pageId' => $page->id]) </div> + <div refs="attachments@edit-container" hidden class="attachment-edit-container"></div> + </div> </div> \ No newline at end of file diff --git a/resources/views/form/dropzone.blade.php b/resources/views/form/dropzone.blade.php deleted file mode 100644 index 2eeec7505..000000000 --- a/resources/views/form/dropzone.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -{{-- -@url - URL to upload to. -@placeholder - Placeholder text -@successMessage ---}} -<div refs="" class="dropzone-container text-center"> - <button type="button" class="dz-message">{{ $placeholder }}</button> -</div> \ No newline at end of file diff --git a/resources/views/form/simple-dropzone.blade.php b/resources/views/form/simple-dropzone.blade.php new file mode 100644 index 000000000..e274949ea --- /dev/null +++ b/resources/views/form/simple-dropzone.blade.php @@ -0,0 +1,21 @@ +{{-- +@url - URL to upload to. +@placeholder - Placeholder text +@successMessage +--}} +<div component="dropzone" + option:dropzone:url="{{ $url }}" + option:dropzone:success-message="{{ $successMessage }}" + option:dropzone:error-message="{{ trans('errors.attachment_upload_error') }}" + option:dropzone:upload-limit="{{ config('app.upload_limit') }}" + option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}" + option:dropzone:zone-text="{{ trans('entities.attachments_dropzone') }}" + option:dropzone:file-accept="*" + class="relative"> + <div refs="dropzone@status-area" class="fixed top-right px-m py-m"></div> + <button type="button" + refs="dropzone@select-button dropzone@drop-target" + class="dropzone-landing-area text-center"> + {{ $placeholder }} + </button> +</div> \ No newline at end of file diff --git a/resources/views/pages/parts/image-manager.blade.php b/resources/views/pages/parts/image-manager.blade.php index cab5daa64..0594c67e8 100644 --- a/resources/views/pages/parts/image-manager.blade.php +++ b/resources/views/pages/parts/image-manager.blade.php @@ -1,7 +1,7 @@ <div components="image-manager dropzone" option:dropzone:url="{{ url('/images/gallery?' . http_build_query(['uploaded_to' => $uploaded_to ?? 0])) }}" option:dropzone:success-message="{{ trans('components.image_upload_success') }}" - option:dropzone:error-message="{{ trans('components.image_upload_failure') }}" + option:dropzone:error-message="{{ trans('errors.image_upload_error') }}" option:dropzone:upload-limit="{{ config('app.upload_limit') }}" option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}" option:dropzone:zone-text="{{ trans('components.image_dropzone_drop') }}"