[FIX] web_editor: restore selection after paste and undo
Before this commit, after pasting a valid URL on a non-collapsed selection followed by UNDO, the selection was not restored to the original range. This was due to not recording the selection in the current history step before creating the link. After pasting text composed of a valid URL between text fragments on a non-collapsed selection followed by UNDO, the selection was again not properly restored. This time, because each text fragment insertion was done via execCommand, which calls _computeHistorySelection and thus overwrites the current step's selection. This commit: - refactors code for link creation into a single function; - wraps the link creation at `_onPaste` in a 'insertHTML' command called via `_applyCommand`, which records the selection properly in the history step; - replaces `execCommand` by `_applyCommand` for text fragments insertion. task-3099012 X-original-commit: 20bb1a2090c90f87a819fdc1c3bbb332cae3720e Part-of: odoo/odoo#118204
This commit is contained in:
@@ -3244,12 +3244,16 @@ export class OdooEditor extends EventTarget {
|
||||
if (lastWordMatch) {
|
||||
const matches = getUrlsInfosInString(textSliced);
|
||||
const match = matches[matches.length - 1];
|
||||
this._createLinkWithUrlInTextNode(
|
||||
selection.anchorNode,
|
||||
match.url,
|
||||
match.index,
|
||||
match.length,
|
||||
);
|
||||
const cloneRange = selection.getRangeAt(0).cloneRange();
|
||||
const range = this.document.createRange();
|
||||
range.setStart(selection.anchorNode, match.index);
|
||||
range.setEnd(selection.anchorNode, match.index + match.length);
|
||||
const link = this._createLink(range.extractContents().textContent, match.url);
|
||||
range.insertNode(link);
|
||||
// Inserting an element into a range clears the selection in Safari
|
||||
// Hence, use the cloned range to reselect it.
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(cloneRange);
|
||||
}
|
||||
selection.collapseToEnd();
|
||||
}
|
||||
@@ -4237,33 +4241,18 @@ export class OdooEditor extends EventTarget {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Link in the node text based on the given data
|
||||
*
|
||||
* @param {Node} textNode
|
||||
* @param {String} label
|
||||
* @param {String} url
|
||||
* @param {int} index
|
||||
* @param {int} length
|
||||
*/
|
||||
_createLinkWithUrlInTextNode(textNode, url, index, length) {
|
||||
const selection = this.document.getSelection();
|
||||
const cloneRange = selection.getRangeAt(0).cloneRange();
|
||||
|
||||
_createLink(label, url) {
|
||||
const link = this.document.createElement('a');
|
||||
link.setAttribute('href', url);
|
||||
for (const [param, value] of Object.entries(this.options.defaultLinkAttributes)) {
|
||||
link.setAttribute(param, `${value}`);
|
||||
}
|
||||
const range = this.document.createRange();
|
||||
range.setStart(textNode, index);
|
||||
range.setEnd(textNode, index + length);
|
||||
link.appendChild(range.extractContents());
|
||||
range.insertNode(link);
|
||||
// Inserting an element into a range clears the selection in Safari
|
||||
// Hence, use the cloned range to reselect it.
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(cloneRange);
|
||||
link.innerText = label;
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add images inside the editable at the current selection.
|
||||
*
|
||||
@@ -4313,10 +4302,8 @@ export class OdooEditor extends EventTarget {
|
||||
this.execCommand('insert', this._prepareClipboardData(clipboardHtml));
|
||||
} else {
|
||||
const text = ev.clipboardData.getData('text/plain');
|
||||
let splitAroundUrl = [text];
|
||||
const linkAttributes = this.options.defaultLinkAttributes || {};
|
||||
const selectionIsInsideALink = !!closestElement(sel.anchorNode, 'a');
|
||||
|
||||
let splitAroundUrl = [text];
|
||||
// Avoid transforming dynamic placeholder pattern to url.
|
||||
if(!text.match(/\${.*}/gi)) {
|
||||
splitAroundUrl = text.split(URL_REGEX);
|
||||
@@ -4340,20 +4327,7 @@ export class OdooEditor extends EventTarget {
|
||||
fontawesome: 'fa-link',
|
||||
callback: () => {
|
||||
this.historyUndo();
|
||||
const link = document.createElement('A');
|
||||
link.setAttribute('href', url);
|
||||
for (const attribute in linkAttributes) {
|
||||
link.setAttribute(attribute, linkAttributes[attribute]);
|
||||
}
|
||||
link.innerText = splitAroundUrl[i];
|
||||
const sel = this.document.getSelection();
|
||||
if (!sel.isCollapsed) {
|
||||
this.deleteRange(sel);
|
||||
}
|
||||
if (sel.rangeCount) {
|
||||
sel.getRangeAt(0).insertNode(link);
|
||||
sel.collapseToEnd();
|
||||
}
|
||||
this._applyRawCommand('insert', this._createLink(splitAroundUrl[i], url));
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -4447,26 +4421,13 @@ export class OdooEditor extends EventTarget {
|
||||
...baseEmbedCommand,
|
||||
]);
|
||||
} else {
|
||||
const link = document.createElement('A');
|
||||
link.setAttribute('href', url);
|
||||
for (const attribute in linkAttributes) {
|
||||
link.setAttribute(attribute, linkAttributes[attribute]);
|
||||
}
|
||||
link.innerText = splitAroundUrl[i];
|
||||
const sel = this.document.getSelection();
|
||||
if (!sel.isCollapsed) {
|
||||
this.deleteRange(sel);
|
||||
}
|
||||
if (sel.rangeCount) {
|
||||
sel.getRangeAt(0).insertNode(link);
|
||||
sel.collapseToEnd();
|
||||
}
|
||||
this._applyCommand('insert', this._createLink(splitAroundUrl[i], url));
|
||||
}
|
||||
} else if (splitAroundUrl[i] !== '') {
|
||||
const textFragments = splitAroundUrl[i].split(/\r?\n/);
|
||||
let textIndex = 1;
|
||||
for (const textFragment of textFragments) {
|
||||
this.execCommand('insert', textFragment);
|
||||
this._applyCommand('insert', textFragment);
|
||||
if (textIndex < textFragments.length) {
|
||||
this._applyCommand('oShiftEnter');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user