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

Added drag/drop and paste support for video files

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

- generalized `insertImageCards()` to `insertCardsFromFiles()` and added support for video cards
- added `canInsertCardsFromFiles()` function so the editor can check before starting an editor run loop and generating an undo point
This commit is contained in:
Kevin Ansfield 2021-11-29 13:38:31 +00:00
parent 7e4a277163
commit 09cae883a8
2 changed files with 50 additions and 36 deletions

View file

@ -21,6 +21,7 @@ import {TrackedObject} from 'tracked-built-ins';
import {action} from '@ember/object'; import {action} from '@ember/object';
import {assign} from '@ember/polyfills'; import {assign} from '@ember/polyfills';
import {camelize, capitalize} from '@ember/string'; import {camelize, capitalize} from '@ember/string';
import {canInsertCardsFromFiles, insertCardsFromFiles} from '../utils/insert-cards-from-files';
import {captureMessage} from '@sentry/browser'; import {captureMessage} from '@sentry/browser';
import {createParserPlugins} from '@tryghost/kg-parser-plugins'; import {createParserPlugins} from '@tryghost/kg-parser-plugins';
import {getContentFromPasteEvent} from 'mobiledoc-kit/utils/parse-utils'; import {getContentFromPasteEvent} from 'mobiledoc-kit/utils/parse-utils';
@ -29,7 +30,6 @@ import {getOwner} from '@ember/application';
import {getParent} from '../lib/dnd/utils'; import {getParent} from '../lib/dnd/utils';
import {utils as ghostHelperUtils} from '@tryghost/helpers'; import {utils as ghostHelperUtils} from '@tryghost/helpers';
import {guidFor} from '@ember/object/internals'; import {guidFor} from '@ember/object/internals';
import {insertImageCards} from '../utils/insert-cards-from-files';
import {run} from '@ember/runloop'; import {run} from '@ember/runloop';
import {inject as service} from '@ember/service'; import {inject as service} from '@ember/service';
import {svgJar} from 'ghost-admin/helpers/svg-jar'; import {svgJar} from 'ghost-admin/helpers/svg-jar';
@ -956,19 +956,11 @@ export default Component.extend({
return; return;
} }
// if we have image files pasted, create an image card for each and set // if we have files pasted, create a card for each and set the
// the payload.files property which will cause the image to be auto-uploaded // payload.files property which will cause the file to be auto-uploaded
// NOTE: browser support varies as of May 2018: if (canInsertCardsFromFiles(event.clipboardData.files)) {
// - 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) => { editor.run((postEditor) => {
insertImageCards(images, postEditor); insertCardsFromFiles(event.clipboardData.files, postEditor);
}); });
return; return;
} }
@ -1139,14 +1131,11 @@ export default Component.extend({
event.preventDefault(); event.preventDefault();
if (event.dataTransfer.files) { if (canInsertCardsFromFiles(event.dataTransfer.files)) {
let images = Array.from(event.dataTransfer.files).filter(file => file.type.indexOf('image') > -1); this.editor.run((postEditor) => {
if (images.length > 0) { insertCardsFromFiles(event.dataTransfer.files, postEditor);
this.editor.run((postEditor) => { });
insertImageCards(images, postEditor); this._scrollCursorIntoView({jumpToCard: true});
});
this._scrollCursorIntoView({jumpToCard: true});
}
} }
}, },

View file

@ -1,12 +1,22 @@
// helper function to insert image cards at or after the current active section export function canInsertCardsFromFiles(files) {
return filterAllowedFiles(files).length > 0;
}
// helper function to insert cards at or after the current active section
// used when pasting or dropping image files // used when pasting or dropping image files
export function insertImageCards(files, postEditor) { export function insertCardsFromFiles(_files, postEditor) {
let {builder, editor} = postEditor; const files = filterAllowedFiles(_files);
let collection = editor.post.sections;
if (!files.length) {
return;
}
const {builder, editor} = postEditor;
const collection = editor.post.sections;
let section = editor.activeSection; let section = editor.activeSection;
// when dropping an image on the editor before it's had focus there will be // when dropping an file on the editor before it's had focus there will be
// no active section so we insert the image at the end of the document // no active section so we insert the card at the end of the document
if (!section) { if (!section) {
section = editor.post.sections.tail; section = editor.post.sections.tail;
@ -14,7 +24,7 @@ export function insertImageCards(files, postEditor) {
// we use `insertSectionBefore` and don't want the image to be added // we use `insertSectionBefore` and don't want the image to be added
// before the last card // before the last card
if (!section.isMarkerable) { if (!section.isMarkerable) {
let blank = builder.createMarkupSection(); const blank = builder.createMarkupSection();
postEditor.insertSectionAtEnd(blank); postEditor.insertSectionAtEnd(blank);
postEditor.setRange(blank.toRange()); postEditor.setRange(blank.toRange());
section = postEditor._range.head.section; section = postEditor._range.head.section;
@ -29,8 +39,8 @@ export function insertImageCards(files, postEditor) {
// list items cannot contain card sections so insert a blank paragraph after // list items cannot contain card sections so insert a blank paragraph after
// the whole list ready to be replaced by the image cards // the whole list ready to be replaced by the image cards
if (section.isListItem) { if (section.isListItem) {
let list = section.parent; const list = section.parent;
let blank = builder.createMarkupSection(); const blank = builder.createMarkupSection();
if (list.next) { if (list.next) {
postEditor.insertSectionBefore(collection, blank, list.next); postEditor.insertSectionBefore(collection, blank, list.next);
} else { } else {
@ -40,15 +50,16 @@ export function insertImageCards(files, postEditor) {
section = postEditor._range.head.section; section = postEditor._range.head.section;
} }
// insert an image card for each image, keep track of the last card to be // insert a card for each file, keep track of the last card to be
// inserted so that the cursor can be placed on it at the end // inserted so that the cursor can be placed on it at the end
let lastImageSection; let lastCardSection;
files.forEach((file) => { files.forEach((file) => {
let payload = { const cardName = getCardNameFromFile(file);
const payload = {
files: [file] files: [file]
}; };
lastImageSection = builder.createCardSection('image', payload); lastCardSection = builder.createCardSection(cardName, payload);
postEditor.insertSectionBefore(collection, lastImageSection, section); postEditor.insertSectionBefore(collection, lastCardSection, section);
}); });
// remove the current section if it's blank - avoids unexpected blank // remove the current section if it's blank - avoids unexpected blank
@ -58,5 +69,19 @@ export function insertImageCards(files, postEditor) {
} }
// place cursor on the last inserted image // place cursor on the last inserted image
postEditor.setRange(lastImageSection.tailPosition()); postEditor.setRange(lastCardSection.tailPosition());
}
function filterAllowedFiles(files) {
return Array.from(files).filter(file => file.type.match(/^(image|video)/));
}
function getCardNameFromFile(file) {
if (file.type.startsWith('image')) {
return 'image';
}
if (file.type.startsWith('video')) {
return 'video';
}
} }