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

Koenig - Create image cards when pasting image files

refs https://github.com/TryGhost/Ghost/issues/9623
- detect pastes that contain image files and insert a card for each, setting `payload.files` to the `File` instance grabbed from the clipboard
- update the `addComponent` hook to ensure the `payload.files` array is preserved, browsers do not allow `File` instances to be copied
- update `{{koenig-card-image}}` to look for a `payload.files` value to auto-upload it via it's internal `{{gh-uploader}}`
- NOTE: browser support for accessing image files on paste varies:
    - Safari: will paste all images
    - Chrome: will only paste the first image
    - Firefox: will not paste any images
This commit is contained in:
Kevin Ansfield 2018-05-29 15:23:50 +01:00
parent b0d778a5dc
commit 032a8def9e
3 changed files with 54 additions and 4 deletions

View file

@ -5,17 +5,18 @@ import {
IMAGE_EXTENSIONS,
IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {computed} from '@ember/object';
import {computed, set} from '@ember/object';
import {htmlSafe} from '@ember/string';
import {isEmpty} from '@ember/utils';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {set} from '@ember/object';
export default Component.extend({
ui: service(),
layout,
// attrs
files: null,
payload: null,
isSelected: false,
isEditing: false,
@ -92,6 +93,21 @@ export default Component.extend({
}
},
didReceiveAttrs() {
this._super(...arguments);
// `payload.files` can be set if we have an externaly set image that
// should be uploaded. Typical example would be from a paste or drag/drop
if (!isEmpty(this.payload.files)) {
run.schedule('afterRender', this, function () {
this.set('files', this.payload.files);
// we don't want to persist any file data in the document
delete this.payload.files;
});
}
},
willDestroyElement() {
this._super(...arguments);
this._detachHandlers();

View file

@ -243,7 +243,11 @@ export default Component.extend({
let componentName = CARD_COMPONENT_MAP[cardName];
// the payload must be copied to avoid sharing the reference
payload = copy(payload, true);
// `payload.files` is special because it's set by paste/drag-n-drop
// events and can't be copied for security reasons
let {files} = payload;
let payloadCopy = copy(payload, true);
payloadCopy.files = files;
// all of the properties that will be passed through to the
// component cards via the template
@ -251,7 +255,7 @@ export default Component.extend({
cardName,
componentName,
koenigOptions,
payload,
payload: payloadCopy,
env,
options,
editor,
@ -646,6 +650,35 @@ export default Component.extend({
return;
}
// if we have image files pasted, create an image card for each and set
// the payload.files property which will cause the image to be auto-uploaded
// NOTE: browser support varies as of May 2018:
// - Safari: will paste all images
// - Chrome: will only paste the first image
// - Firefox: will not paste any images
let images = Array.from(event.clipboardData.files).filter(file => file.type.indexOf('image') > -1);
if (images.length > 0) {
event.preventDefault();
event.stopImmediatePropagation();
editor.run((postEditor) => {
let {builder} = postEditor;
if (editor.activeSection.isBlank) {
postEditor.removeSection(editor.activeSection);
}
images.forEach((image) => {
let payload = {
files: [image]
};
let imageCard = builder.createCardSection('image', payload);
postEditor.insertSection(imageCard);
});
});
return;
}
let range = editor.range;
let {html, text} = getContentFromPasteEvent(event);

View file

@ -12,6 +12,7 @@
hasEditMode=false
}}
{{#gh-uploader
files=files
accept=imageMimeTypes
extensions=imageExtensions
onStart=(action "setPreviewSrc")