[FIX] web_editor: blinky colorpicker when applyColor on removed fonts
Reproduction: 1. in Note, type ` abcd ` in an empty note 2. color the background of all the text, bold `abcd` 3. select from the end till the letter `b`, try to change the color, traceback pops up Fix: The main reason is we remove element in `splitElement`/ `splitAroundUntil`. In this special case, there are two selected nodes which returns the same font element. The first font element will be removed and replaced first. Because the first font element doesn’t exist anymore, when recovering selection, the second element considered as collapsed and the recovered selection is a caret. Thus the color picker is gone after applying the color (preview) The fix is then, we always make sure that the font we have is still connected to the page (exist in the page), otherwise it’s problematic to apply color to a non-existing element and recover cursor from it. If there is font element(s) that is not connected, we simply get the fonts again, because new font element is created for the second node, it won’t split (remove) the first node’s font element again Task-3476494 closes odoo/odoo#150500 X-original-commit: 06a8b930176093e2c5a853b78431316af90f3550 Signed-off-by: Nicolas Bayet (nby) <nby@odoo.com> Signed-off-by: Jinjiu Liu (jili) <jili@odoo.com> Co-authored-by: Goaman <nby@odoo.com>
This commit is contained in:
@@ -135,9 +135,9 @@ function hasColor(element, mode) {
|
||||
}
|
||||
}
|
||||
return (
|
||||
(style[mode] && style[mode] !== 'inherit' && style[mode] !== parent.style[mode]) ||
|
||||
(style[mode] && style[mode] !== 'inherit' && (!parent || style[mode] !== parent.style[mode])) ||
|
||||
(classRegex.test(element.className) &&
|
||||
getComputedStyle(element)[mode] !== getComputedStyle(parent)[mode])
|
||||
(!parent || getComputedStyle(element)[mode] !== getComputedStyle(parent)[mode]))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -598,59 +598,69 @@ export const editorCommands = {
|
||||
.map(n => closestElement(n, "*[t-field],*[t-out],*[t-esc]"))
|
||||
.filter(Boolean));
|
||||
|
||||
const fonts = selectedNodes.flatMap(node => {
|
||||
let font = closestElement(node, 'font') || closestElement(node, 'span');
|
||||
const children = font && descendants(font);
|
||||
if (font && (font.nodeName === 'FONT' || (font.nodeName === 'SPAN' && font.style[mode]))) {
|
||||
// Partially selected <font>: split it.
|
||||
const selectedChildren = children.filter(child => selectedNodes.includes(child));
|
||||
if (selectedChildren.length) {
|
||||
font = splitAroundUntil(selectedChildren, font);
|
||||
function getFonts(selectedNodes) {
|
||||
return selectedNodes.flatMap(node => {
|
||||
let font = closestElement(node, 'font') || closestElement(node, 'span');
|
||||
const children = font && descendants(font);
|
||||
if (font && (font.nodeName === 'FONT' || (font.nodeName === 'SPAN' && font.style[mode]))) {
|
||||
// Partially selected <font>: split it.
|
||||
const selectedChildren = children.filter(child => selectedNodes.includes(child));
|
||||
if (selectedChildren.length) {
|
||||
font = splitAroundUntil(selectedChildren, font);
|
||||
} else {
|
||||
font = [];
|
||||
}
|
||||
} else if ((node.nodeType === Node.TEXT_NODE && !isWhitespace(node))
|
||||
|| (node.nodeName === 'BR' && isEmptyBlock(node.parentNode))
|
||||
|| (node.nodeType === Node.ELEMENT_NODE &&
|
||||
['inline', 'inline-block'].includes(getComputedStyle(node).display) &&
|
||||
!isWhitespace(node.textContent) &&
|
||||
!node.classList.contains('btn') &&
|
||||
!node.querySelector('font'))) {
|
||||
// Node is a visible text or inline node without font nor a button:
|
||||
// wrap it in a <font>.
|
||||
const previous = node.previousSibling;
|
||||
const classRegex = mode === 'color' ? BG_CLASSES_REGEX : TEXT_CLASSES_REGEX;
|
||||
if (
|
||||
previous &&
|
||||
previous.nodeName === 'FONT' &&
|
||||
!previous.style[mode === 'color' ? 'backgroundColor' : 'color'] &&
|
||||
!classRegex.test(previous.className) &&
|
||||
selectedNodes.includes(previous.firstChild) &&
|
||||
selectedNodes.includes(previous.lastChild)
|
||||
) {
|
||||
// Directly follows a fully selected <font> that isn't
|
||||
// colored in the other mode: append to that.
|
||||
font = previous;
|
||||
} else {
|
||||
// No <font> found: insert a new one.
|
||||
font = document.createElement('font');
|
||||
node.after(font);
|
||||
}
|
||||
if (node.textContent) {
|
||||
font.appendChild(node);
|
||||
} else {
|
||||
fillEmpty(font);
|
||||
}
|
||||
} else {
|
||||
font = [];
|
||||
font = []; // Ignore non-text or invisible text nodes.
|
||||
}
|
||||
} else if ((node.nodeType === Node.TEXT_NODE && !isWhitespace(node))
|
||||
|| (node.nodeName === 'BR' && isEmptyBlock(node.parentNode))
|
||||
|| (node.nodeType === Node.ELEMENT_NODE &&
|
||||
['inline', 'inline-block'].includes(getComputedStyle(node).display) &&
|
||||
!isWhitespace(node.textContent) &&
|
||||
!node.classList.contains('btn') &&
|
||||
!node.querySelector('font'))) {
|
||||
// Node is a visible text or inline node without font nor a button:
|
||||
// wrap it in a <font>.
|
||||
const previous = node.previousSibling;
|
||||
const classRegex = mode === 'color' ? BG_CLASSES_REGEX : TEXT_CLASSES_REGEX;
|
||||
if (
|
||||
previous &&
|
||||
previous.nodeName === 'FONT' &&
|
||||
!previous.style[mode === 'color' ? 'backgroundColor' : 'color'] &&
|
||||
!classRegex.test(previous.className) &&
|
||||
selectedNodes.includes(previous.firstChild) &&
|
||||
selectedNodes.includes(previous.lastChild)
|
||||
) {
|
||||
// Directly follows a fully selected <font> that isn't
|
||||
// colored in the other mode: append to that.
|
||||
font = previous;
|
||||
} else {
|
||||
// No <font> found: insert a new one.
|
||||
font = document.createElement('font');
|
||||
node.after(font);
|
||||
}
|
||||
if (node.textContent) {
|
||||
font.appendChild(node);
|
||||
} else {
|
||||
fillEmpty(font);
|
||||
}
|
||||
} else {
|
||||
font = []; // Ignore non-text or invisible text nodes.
|
||||
}
|
||||
return font;
|
||||
});
|
||||
return font;
|
||||
});
|
||||
}
|
||||
|
||||
for (const fieldNode of selectedFieldNodes) {
|
||||
colorElement(fieldNode, color, mode);
|
||||
}
|
||||
|
||||
let fonts = getFonts(selectedNodes);
|
||||
// Dirty fix as the previous call could have unconnected elements
|
||||
// because of the `splitAroundUntil`. Another call should provide he
|
||||
// correct list of fonts.
|
||||
if (!fonts.every((font) => font.isConnected)) {
|
||||
fonts = getFonts(selectedNodes);
|
||||
}
|
||||
|
||||
// Color the selected <font>s and remove uncolored fonts.
|
||||
const fontsSet = new Set(fonts);
|
||||
for (const font of fontsSet) {
|
||||
|
||||
Reference in New Issue
Block a user