mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 20:34:02 +03:00
Koenig - Convert pasted <img> and <hr> elements to cards
refs https://github.com/TryGhost/Ghost/issues/9623 - use our custom paste event to intercept and modify pasted HTML before passing to mobiledoc - wrap the html in a `<div>` so that all elements get parsed by mobiledoc-kit - when pasting results in the last pasted section being a card, insert a blank paragraph - fixes immediate breaking of the undo stack
This commit is contained in:
parent
0fa0068f6a
commit
fb226ac956
@ -523,6 +523,8 @@ export default Component.extend({
|
|||||||
|
|
||||||
cursorDidChange(editor) {
|
cursorDidChange(editor) {
|
||||||
let {head, tail, direction, isCollapsed, head: {section}} = editor.range;
|
let {head, tail, direction, isCollapsed, head: {section}} = editor.range;
|
||||||
|
let isPasting = this._isPasting;
|
||||||
|
this._isPasting = false;
|
||||||
|
|
||||||
// sometimes we perform a programatic edit that causes a cursor change
|
// sometimes we perform a programatic edit that causes a cursor change
|
||||||
// but we actually want to skip the default behaviour because we've
|
// but we actually want to skip the default behaviour because we've
|
||||||
@ -543,6 +545,29 @@ export default Component.extend({
|
|||||||
|
|
||||||
// select the card if the cursor is on the before/after ‌ char
|
// select the card if the cursor is on the before/after ‌ char
|
||||||
if (section && isCollapsed && section.type === 'card-section') {
|
if (section && isCollapsed && section.type === 'card-section') {
|
||||||
|
if (isPasting) {
|
||||||
|
// when pasting, if the last section added is a card we don't want to
|
||||||
|
// select it (it breaks the undo stack) but rather create an empty
|
||||||
|
// paragraph underneath
|
||||||
|
// TODO: why does the undo stack break?
|
||||||
|
let collection = section.parent.sections;
|
||||||
|
let nextSection = section.next;
|
||||||
|
editor.run((postEditor) => {
|
||||||
|
let newSection = postEditor.builder.createMarkupSection('p');
|
||||||
|
|
||||||
|
if (nextSection) {
|
||||||
|
postEditor.insertSectionBefore(collection, newSection, nextSection);
|
||||||
|
} else {
|
||||||
|
postEditor.insertSectionAtEnd(newSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
postEditor.setRange(newSection.tailPosition());
|
||||||
|
this.set('selectedRange', newSection.tailPosition());
|
||||||
|
this._skipCursorChange = true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (head.offset === 0 || head.offset === 1) {
|
if (head.offset === 0 || head.offset === 1) {
|
||||||
// select card after render to ensure that our componentCards
|
// select card after render to ensure that our componentCards
|
||||||
// attr is populated
|
// attr is populated
|
||||||
@ -620,6 +645,11 @@ export default Component.extend({
|
|||||||
let editor = this.editor;
|
let editor = this.editor;
|
||||||
let range = editor.range;
|
let range = editor.range;
|
||||||
|
|
||||||
|
// when pasting, if the last section added is a card we don't want to
|
||||||
|
// select it (it breaks the undo stack) but rather create an empty
|
||||||
|
// paragraph underneath
|
||||||
|
this._isPasting = true;
|
||||||
|
|
||||||
// if a URL is pasted and we have a selection, make that selection a link
|
// if a URL is pasted and we have a selection, make that selection a link
|
||||||
if (range && !range.isCollapsed && range.headSection === range.tailSection && range.headSection.isMarkerable) {
|
if (range && !range.isCollapsed && range.headSection === range.tailSection && range.headSection.isMarkerable) {
|
||||||
let {text} = getContentFromPasteEvent(event);
|
let {text} = getContentFromPasteEvent(event);
|
||||||
@ -689,7 +719,14 @@ export default Component.extend({
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
if (type === 'text/html') {
|
if (type === 'text/html') {
|
||||||
return normalizedHtml;
|
// HACK: mobiledoc-kit won't parse top-level <img> or
|
||||||
|
// other "unknown" elements so we wrap everything here
|
||||||
|
// so that we don't get blank posts and elements are
|
||||||
|
// passed through to our parser plugins correctly
|
||||||
|
// TODO: fix parsing in mobiledoc, related issues:
|
||||||
|
// https://github.com/bustle/mobiledoc-kit/issues/619
|
||||||
|
// https://github.com/bustle/mobiledoc-kit/issues/494
|
||||||
|
return `<div>${normalizedHtml}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,30 @@ export function removeLeadingNewline(node) {
|
|||||||
node.nodeValue = node.nodeValue.replace(/^\n/, '');
|
node.nodeValue = node.nodeValue.replace(/^\n/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function imgToCard(node, builder, {addSection, nodeFinished}) {
|
||||||
|
if (node.nodeType !== 1 || node.tagName !== 'IMG') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = {src: node.src};
|
||||||
|
let cardSection = builder.createCardSection('image', payload);
|
||||||
|
addSection(cardSection);
|
||||||
|
nodeFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hrToCard(node, builder, {addSection, nodeFinished}) {
|
||||||
|
if (node.nodeType !== 1 || node.tagName !== 'HR') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cardSection = builder.createCardSection('hr');
|
||||||
|
addSection(cardSection);
|
||||||
|
nodeFinished();
|
||||||
|
}
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
brToSoftBreakAtom,
|
brToSoftBreakAtom,
|
||||||
removeLeadingNewline,
|
removeLeadingNewline,
|
||||||
|
imgToCard,
|
||||||
|
hrToCard
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user