mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
✨ Koenig - Auto-convert URLs pasted on blank lines to embed cards
refs https://github.com/TryGhost/Ghost/issues/9724 - if a URL is pasted in a blank paragraph, insert an embed card configured to switch immediately to a link if the embed fails - if <kbd>Shift</kbd> is pressed when pasting insert a linked URL with no auto-embed - added `payload.linkOnError` handling to the embed card - if that option is set, immediately convert the embed card section to a linked URL on embed failure - skip adding an undo step when auto-converting to a URL so that no undo loop is created. Without this an undo would convert from link to embed card which would automatically look up the url then convert back to a link meaning it's impossible to undo back past the URL paste - ensure that the cursor position doesn't jump if the user has continued writing whilst the oembed lookup is in progress
This commit is contained in:
parent
b46d60b820
commit
54cc17707f
2 changed files with 52 additions and 6 deletions
|
@ -104,7 +104,9 @@ export default Component.extend({
|
||||||
this.set('hasError', false);
|
this.set('hasError', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
insertAsLink() {
|
insertAsLink(options = {linkOnError: false}) {
|
||||||
|
let {range} = this.editor;
|
||||||
|
|
||||||
this.editor.run((postEditor) => {
|
this.editor.run((postEditor) => {
|
||||||
let {builder} = postEditor;
|
let {builder} = postEditor;
|
||||||
let cardSection = this.env.postModel;
|
let cardSection = this.env.postModel;
|
||||||
|
@ -113,6 +115,20 @@ export default Component.extend({
|
||||||
|
|
||||||
postEditor.replaceSection(cardSection, p);
|
postEditor.replaceSection(cardSection, p);
|
||||||
postEditor.insertTextWithMarkup(p.toRange().head, this.payload.url, [link]);
|
postEditor.insertTextWithMarkup(p.toRange().head, this.payload.url, [link]);
|
||||||
|
|
||||||
|
// if a user is typing further on in the doc (possible if embed
|
||||||
|
// was created automatically via paste of URL) then return the
|
||||||
|
// cursor so the card->link change doesn't cause a cursor jump
|
||||||
|
if (range.headSection !== cardSection) {
|
||||||
|
postEditor.setRange(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid adding an extra undo step when automatically creating
|
||||||
|
// link after an error so that an Undo after pasting a URL
|
||||||
|
// doesn't get stuck in a loop going through link->embed->link
|
||||||
|
if (options.linkOnError) {
|
||||||
|
postEditor.cancelSnapshot();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -135,12 +151,17 @@ export default Component.extend({
|
||||||
throw 'No HTML returned';
|
throw 'No HTML returned';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set(this.payload.linkOnError, undefined);
|
||||||
set(this.payload, 'html', response.html);
|
set(this.payload, 'html', response.html);
|
||||||
set(this.payload, 'type', response.type);
|
set(this.payload, 'type', response.type);
|
||||||
this.saveCard(this.payload, false);
|
this.saveCard(this.payload, false);
|
||||||
|
|
||||||
run.schedule('afterRender', this, this._populateIframe);
|
run.schedule('afterRender', this, this._populateIframe);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (this.payload.linkOnError) {
|
||||||
|
this.send('insertAsLink', {linkOnError: true});
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.set('hasError', true);
|
this.set('hasError', true);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -176,7 +176,6 @@ function insertImageCards(files, postEditor) {
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
layout,
|
layout,
|
||||||
|
|
||||||
tagName: 'article',
|
tagName: 'article',
|
||||||
classNames: ['koenig-editor', 'w-100', 'flex-grow', 'relative', 'center', 'mb0', 'mt0'],
|
classNames: ['koenig-editor', 'w-100', 'flex-grow', 'relative', 'center', 'mb0', 'mt0'],
|
||||||
|
|
||||||
|
@ -784,14 +783,40 @@ export default Component.extend({
|
||||||
let range = editor.range;
|
let range = editor.range;
|
||||||
let {html, text} = getContentFromPasteEvent(event);
|
let {html, text} = getContentFromPasteEvent(event);
|
||||||
|
|
||||||
// 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 (text && validator.isURL(text)) {
|
if (text && validator.isURL(text)) {
|
||||||
|
// if we have a text selection, make that selection a link
|
||||||
|
if (range && !range.isCollapsed && range.headSection === range.tailSection && range.headSection.isMarkerable) {
|
||||||
let linkMarkup = editor.builder.createMarkup('a', {href: text});
|
let linkMarkup = editor.builder.createMarkup('a', {href: text});
|
||||||
editor.run((postEditor) => {
|
editor.run((postEditor) => {
|
||||||
postEditor.addMarkupToRange(range, linkMarkup);
|
postEditor.addMarkupToRange(range, linkMarkup);
|
||||||
});
|
});
|
||||||
editor.selectRange(range.tail);
|
editor.selectRange(range.tail);
|
||||||
|
|
||||||
|
// prevent mobiledoc's default paste event handler firing
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's no selection and cursor is on an empty paragraph,
|
||||||
|
// insert the url as an embed card, unless SHIFT is pressed. Setting
|
||||||
|
// the `linkOnError` option results in an immediate switch to a
|
||||||
|
// plain link if the embed fails for any reason (eg, unknown provider)
|
||||||
|
if (range && range.isCollapsed && range.headSection.isBlank && !range.headSection.isListItem) {
|
||||||
|
if (!this._modifierKeys.shift) {
|
||||||
|
editor.run((postEditor) => {
|
||||||
|
let payload = {url: text, linkOnError: true};
|
||||||
|
let card = postEditor.builder.createCardSection('embed', payload);
|
||||||
|
postEditor.replaceSection(range.headSection, card);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// ensure the pasted URL is still auto-linked when Shift is pressed
|
||||||
|
editor.run((postEditor) => {
|
||||||
|
let linkMarkup = editor.builder.createMarkup('a', {href: text});
|
||||||
|
postEditor.insertTextWithMarkup(range.head, text, [linkMarkup]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// prevent mobiledoc's default paste event handler firing
|
// prevent mobiledoc's default paste event handler firing
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
@ -802,7 +827,7 @@ export default Component.extend({
|
||||||
// if plain text is pasted we run it through our markdown parser so that
|
// if plain text is pasted we run it through our markdown parser so that
|
||||||
// we get better output than mobiledoc's default text parsing and we can
|
// we get better output than mobiledoc's default text parsing and we can
|
||||||
// provide an easier MD->Mobiledoc conversion route
|
// provide an easier MD->Mobiledoc conversion route
|
||||||
// NOTE: will not work in IE/Edge which only ever expose `html`
|
// NOTE: will not work in Edge which only ever exposes `html`
|
||||||
if (text && !html && !this._modifierKeys.shift) {
|
if (text && !html && !this._modifierKeys.shift) {
|
||||||
// prevent mobiledoc's default paste event handler firing
|
// prevent mobiledoc's default paste event handler firing
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
Loading…
Add table
Reference in a new issue