0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Added first pass at feature image drag+drop uploading

refs https://github.com/TryGhost/Team/issues/884

- add `[data-user-is-dragging]` to `body` element when any drag is occurring so that we can make drop zones active
- added dropzone and drop handling to feature image component
This commit is contained in:
Kevin Ansfield 2021-07-16 15:00:48 +01:00
parent 0f9177919c
commit 876c2b6cdd
7 changed files with 138 additions and 3 deletions

View file

@ -8,6 +8,21 @@
@onComplete={{this.setUploadedImage}}
as |uploader|
>
{{#if (feature "featureImgDragDrop")}}
{{#unless @image}}
<div
class="gh-editor-feature-image-dropzone"
{{on "dragover" this.dragOver}}
{{on "dragleave" this.dragLeave}}
{{on "drop" (fn this.drop uploader.setFiles)}}
></div>
{{/unless}}
{{/if}}
{{#if this.canDrop}}
<div class="gh-editor-feature-image-drop-indicator bg-blue br-pill pe-none"></div>
{{/if}}
{{#if uploader.isUploading}}
{{!-- upload in progress --}}
{{uploader.progressBar}}
@ -63,8 +78,12 @@
{{else}}
{{!-- no-image state --}}
<div class="flex flex-row items-center {{if this.hideButton "invisible"}}">
<button type="button" class="gh-editor-feature-image-add-button" {{on "click" uploader.triggerFileDialog}}>{{svg-jar "plus"}}<span>Add feature image</span></button>
<button type="button" class="gh-editor-feature-image-unsplash" {{on "click" this.toggleUnsplashSelector}}>{{svg-jar "unsplash"}}</button>
{{#if this.canDrop}}
<div class="gh-editor-feature-image-add-button"><span>Drop to upload feature image</span></div>
{{else}}
<button type="button" class="gh-editor-feature-image-add-button" {{on "click" uploader.triggerFileDialog}}>{{svg-jar "plus"}}<span>Add feature image</span></button>
<button type="button" class="gh-editor-feature-image-unsplash" {{on "click" this.toggleUnsplashSelector}}>{{svg-jar "unsplash"}}</button>
{{/if}}
</div>
{{/if}}

View file

@ -7,9 +7,10 @@ export default class GhEditorFeatureImageComponent extends Component {
@tracked isHovered = false;
@tracked captionInputFocused = false;
@tracked showUnsplashSelector = false;
@tracked canDrop = false;
get hideButton() {
return !this.isHovered && !this.args.forceButtonDisplay;
return !this.canDrop && !this.isHovered && !this.args.forceButtonDisplay;
}
@action
@ -39,4 +40,47 @@ export default class GhEditorFeatureImageComponent extends Component {
onAltInput(event) {
this.args.updateAlt(event.target.value);
}
@action
dragOver(event) {
if (!event.dataTransfer.files) {
return;
}
// this is needed to work around inconsistencies with dropping files
// from Chrome's downloads bar
if (navigator.userAgent.indexOf('Chrome') > -1) {
let eA = event.dataTransfer.effectAllowed;
event.dataTransfer.dropEffect = (eA === 'move' || eA === 'linkMove') ? 'move' : 'copy';
}
// event.stopPropagation();
event.preventDefault();
this.canDrop = true;
}
@action
dragLeave(event) {
if (!event.dataTransfer.files) {
return;
}
event.preventDefault();
this.canDrop = false;
}
@action
drop(setFiles, event) {
if (!event.dataTransfer.files) {
return;
}
event.stopPropagation();
event.preventDefault();
this.canDrop = false;
setFiles(event.dataTransfer.files);
}
}

View file

@ -46,6 +46,8 @@ export default Route.extend(ShortcutsRoute, {
this.router.on('routeDidChange', () => {
this.notifications.displayDelayed();
});
// TODO: featureImgDragDrop - move event initialization here when removing feature flag
},
beforeModel() {
@ -143,6 +145,12 @@ export default Route.extend(ShortcutsRoute, {
}
},
willDestroy() {
// TODO: featureImgDragDrop - remove .session. when removing feature flag
document.body.removeEventListener('dragenter', this.session.bodyDragEnterHandler);
document.body.removeEventListener('dragleave', this.session.bodyDragLeaveHandler);
},
async prepareApp() {
await this.config.fetchUnauthenticated();

View file

@ -56,6 +56,7 @@ export default Service.extend({
multipleProducts: feature('multipleProducts', {developer: true}),
emailCardSegments: feature('emailCardSegments', {developer: true}),
savedIndicator: feature('savedIndicator', {developer: true}),
featureImgDragDrop: feature('featureImgDragDrop', {developer: true}),
_user: null,

View file

@ -51,6 +51,35 @@ export default class SessionService extends ESASessionService {
});
}
// TODO: featureImgDragDrop - move this to application route init when removing flag
if (this.feature.featureImgDragDrop) {
// when any drag event is occurring we add `data-user-is-dragging` to the
// body element so that we can have dropzones start listening to pointer
// events allowing us to have interactive elements "underneath" drop zones
if (this.feature.featureImgDragDrop) {
this.bodyDragEnterHandler = (event) => {
if (!event.dataTransfer) {
return;
}
event.stopPropagation();
event.preventDefault();
document.body.dataset.userIsDragging = true;
window.clearTimeout(this.dragTimer);
};
this.bodyDragLeaveHandler = (event) => {
event.preventDefault();
window.clearTimeout(this.dragTimer);
this.dragTimer = window.setTimeout(() => {
delete document.body.dataset.userIsDragging;
}, 100);
};
document.body.addEventListener('dragenter', this.bodyDragEnterHandler);
document.body.addEventListener('dragleave', this.bodyDragLeaveHandler);
}
}
this.loadServerNotifications();
this.whatsNew.fetchLatest.perform();
}

View file

@ -425,6 +425,27 @@
}
}
.gh-editor-feature-image-dropzone {
position: absolute;
left: 0;
right: 0;
height: 175px;
top: -50px;
pointer-events: none;
}
body[data-user-is-dragging] .gh-editor-feature-image-dropzone {
pointer-events: auto;
}
.gh-editor-feature-image-drop-indicator {
position: absolute;
left: 0;
right: 0;
top: 40px;
height: 4px;
}
.gh-editor-feature-image {
margin-left: -1.2rem;
padding-left: 1.2rem;

View file

@ -293,6 +293,19 @@
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Feature image drag & drop</h4>
<p class="gh-expandable-description">
Allow dropping image files on feature image to upload
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="featureImgDragDrop" />
</div>
</div>
</div>
</div>
</div>
{{/if}}