[FIX] web_editor: ensure all formats use spans
The bold format is overridden to use inline style on a <span> rather than a <b>. The other formats (italic, underline, strikethrough) continued to use the default browser behavior. This harmonizes them. X-original-commit: ce59bbd1c13d7fd4cc6971b89cc95386d95fcd75 Part-of: odoo/odoo#86930
This commit is contained in:
@@ -43,6 +43,9 @@ import {
|
||||
getUrlsInfosInString,
|
||||
URL_REGEX,
|
||||
isBold,
|
||||
isItalic,
|
||||
isUnderline,
|
||||
isStrikeThrough,
|
||||
YOUTUBE_URL_GET_VIDEO_ID,
|
||||
unwrapContents,
|
||||
peek,
|
||||
@@ -1901,9 +1904,6 @@ export class OdooEditor extends EventTarget {
|
||||
}
|
||||
const paragraphDropdownButton = this.toolbar.querySelector('#paragraphDropdownButton');
|
||||
for (const commandState of [
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'justifyLeft',
|
||||
'justifyRight',
|
||||
'justifyCenter',
|
||||
@@ -1926,9 +1926,15 @@ export class OdooEditor extends EventTarget {
|
||||
const closestStartContainer = closestElement(sel.getRangeAt(0).startContainer, '*');
|
||||
const selectionStartStyle = getComputedStyle(closestStartContainer);
|
||||
|
||||
// queryCommandState('bold') does not take stylesheets into account
|
||||
const button = this.toolbar.querySelector('#bold');
|
||||
button.classList.toggle('active', isBold(closestStartContainer));
|
||||
// queryCommandState does not take stylesheets into account
|
||||
const boldButton = this.toolbar.querySelector('#bold');
|
||||
boldButton && boldButton.classList.toggle('active', isBold(closestStartContainer));
|
||||
const italicButton = this.toolbar.querySelector('#italic');
|
||||
italicButton && italicButton.classList.toggle('active', isItalic(closestStartContainer));
|
||||
const underlineButton = this.toolbar.querySelector('#underline');
|
||||
underlineButton && underlineButton.classList.toggle('active', isUnderline(closestStartContainer));
|
||||
const strikeThroughButton = this.toolbar.querySelector('#strikeThrough');
|
||||
strikeThroughButton && strikeThroughButton.classList.toggle('active', isStrikeThrough(closestStartContainer));
|
||||
|
||||
const fontSizeValue = this.toolbar.querySelector('#fontSizeCurrentValue');
|
||||
if (fontSizeValue) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
isBold,
|
||||
isItalic,
|
||||
isUnderline,
|
||||
isStrikeThrough,
|
||||
isColorGradient,
|
||||
isContentTextNode,
|
||||
isShrunkBlock,
|
||||
@@ -275,6 +276,45 @@ export function applyInlineStyle(editor, applyStyle) {
|
||||
}
|
||||
}
|
||||
}
|
||||
const styles = {
|
||||
bold: {
|
||||
is: isBold,
|
||||
name: 'fontWeight',
|
||||
value: 'bolder',
|
||||
},
|
||||
italic: {
|
||||
is: isItalic,
|
||||
name: 'fontStyle',
|
||||
value: 'italic',
|
||||
},
|
||||
underline: {
|
||||
is: isUnderline,
|
||||
name: 'textDecoration',
|
||||
value: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
is: isStrikeThrough,
|
||||
name: 'textDecoration',
|
||||
value: 'line-through',
|
||||
}
|
||||
}
|
||||
export function toggleFormat(editor, format) {
|
||||
const selection = editor.document.getSelection();
|
||||
if (!selection.rangeCount || selection.getRangeAt(0).collapsed) return;
|
||||
getDeepRange(editor.editable, { splitText: true, select: true, correctTripleClick: true });
|
||||
const style = styles[format];
|
||||
const isAlreadyFormatted = getSelectedNodes(editor.editable)
|
||||
.filter(n => n.nodeType === Node.TEXT_NODE && n.nodeValue.trim().length)
|
||||
.find(n => style.is(n.parentElement));
|
||||
applyInlineStyle(editor, el => {
|
||||
if (isAlreadyFormatted) {
|
||||
const block = closestBlock(el);
|
||||
el.style[style.name] = style.is(block) ? 'normal' : getComputedStyle(block)[style.name];
|
||||
} else {
|
||||
el.style[style.name] = style.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
function addColumn(editor, beforeOrAfter) {
|
||||
getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding td.
|
||||
const c = getInSelection(editor.document, 'td');
|
||||
@@ -368,36 +408,10 @@ export const editorCommands = {
|
||||
|
||||
// Formats
|
||||
// -------------------------------------------------------------------------
|
||||
bold: editor => {
|
||||
const nodes = getTextNodes(editor);
|
||||
if (!nodes) return;
|
||||
const isAlreadyBold = nodes.find(n => isBold(n.parentElement));
|
||||
applyInlineStyle(editor, el => {
|
||||
if (isAlreadyBold) {
|
||||
const block = closestBlock(el);
|
||||
el.style.fontWeight = isBold(block) ? 'normal' : getComputedStyle(block).fontWeight;
|
||||
} else {
|
||||
el.style.fontWeight = 'bolder';
|
||||
}
|
||||
});
|
||||
},
|
||||
italic: editor => {
|
||||
const nodes = getTextNodes(editor);
|
||||
if (!nodes) return;
|
||||
const isAlreadyItalic = nodes.find(n => isItalic(n.parentElement));
|
||||
applyInlineStyle(editor, el => {
|
||||
el.style.fontStyle = isAlreadyItalic ? 'normal' : 'italic';
|
||||
});
|
||||
},
|
||||
underline: editor => {
|
||||
const nodes = getTextNodes(editor);
|
||||
if (!nodes) return;
|
||||
const isAlreadyUnderline = nodes.find(n => isUnderline(n.parentElement));
|
||||
applyInlineStyle(editor, el => {
|
||||
el.style.textDecoration = isAlreadyUnderline ? 'none' : 'underline';
|
||||
});
|
||||
},
|
||||
strikeThrough: editor => editor.document.execCommand('strikeThrough'),
|
||||
bold: editor => toggleFormat(editor, 'bold'),
|
||||
italic: editor => toggleFormat(editor, 'italic'),
|
||||
underline: editor => toggleFormat(editor, 'underline'),
|
||||
strikeThrough: editor => toggleFormat(editor, 'strikeThrough'),
|
||||
removeFormat: editor => {
|
||||
editor.document.execCommand('removeFormat');
|
||||
for (const node of getTraversedNodes(editor.editable)) {
|
||||
|
||||
@@ -868,24 +868,31 @@ export function isBold(node) {
|
||||
return fontWeight > 500 || fontWeight > +getComputedStyle(closestBlock(node)).fontWeight;
|
||||
}
|
||||
/**
|
||||
* Return true if the given node font style equal italic
|
||||
* Return true if the given node appears italic.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isItalic(node) {
|
||||
const fontStyle = getComputedStyle(closestElement(node)).fontStyle;
|
||||
return fontStyle === 'italic';
|
||||
return getComputedStyle(closestElement(node)).fontStyle === 'italic';
|
||||
}
|
||||
/**
|
||||
* Return true if the given node text-decoration style equal underline
|
||||
* Return true if the given node appears underlined.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isUnderline(node) {
|
||||
const textDecoration = getComputedStyle(closestElement(node)).textDecorationLine;
|
||||
return textDecoration === 'underline';
|
||||
return getComputedStyle(closestElement(node)).textDecoration === 'underline';
|
||||
}
|
||||
/**
|
||||
* Return true if the given node appears struck through.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isStrikeThrough(node) {
|
||||
return getComputedStyle(closestElement(node)).textDecoration === 'line-through';
|
||||
}
|
||||
|
||||
export function isUnbreakable(node) {
|
||||
|
||||
@@ -181,15 +181,20 @@ tour.register('rte_translator', {
|
||||
mouseup.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, el);
|
||||
el.dispatchEvent(mouseup);
|
||||
},
|
||||
}, {
|
||||
content: "underline",
|
||||
trigger: '.oe-toolbar #underline',
|
||||
// This is disabled for now because it reveals a bug that is fixed in saas-15.1
|
||||
// and considered a tradeoff in 15.0. The bug concerns the invalidation of
|
||||
// translations when inserting tags with more than one character. Whereas <u>
|
||||
// didn't trigger an invalidation, <span style="text-decoration-line: underline;">
|
||||
// does.
|
||||
// }, {
|
||||
// content: "underline",
|
||||
// trigger: '.oe-toolbar #underline',
|
||||
}, {
|
||||
content: "save new change",
|
||||
trigger: 'button[data-action=save]',
|
||||
extra_trigger: '#wrap.o_dirty p span[style="text-decoration: underline;"]',
|
||||
|
||||
}, {
|
||||
// See comment above.
|
||||
// extra_trigger: '#wrap.o_dirty p span[style*="text-decoration-line: underline;"]',
|
||||
}, {
|
||||
content: "click language dropdown (4)",
|
||||
trigger: '.js_language_selector .dropdown-toggle',
|
||||
extra_trigger: 'body:not(.o_wait_reload):not(:has(.note-editor)) a[data-action="edit"]',
|
||||
|
||||
Reference in New Issue
Block a user