diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js index 0d06cd5739f..65a663dc700 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js @@ -2023,7 +2023,9 @@ export class OdooEditor extends EventTarget { if (!this.isSelectionInEditable()) { this.historyResetLatestComputedSelection(true); } - this.execCommand(buttonEl.dataset.call, buttonEl.dataset.arg1); + const arg1 = buttonEl.dataset.arg1; + const args = arg1 && arg1.split(",") || []; + this.execCommand(buttonEl.dataset.call, ...args); ev.preventDefault(); this._updateToolbar(); @@ -3113,16 +3115,22 @@ export class OdooEditor extends EventTarget { const listUIClasses = {UL: 'fa-list-ul', OL: 'fa-list-ol', CL: 'fa-tasks'}; const block = closestBlock(sel.anchorNode); let activeLabel = undefined; - for (const [style, tag, isList] of [ - ['paragraph', 'P', false], - ['pre', 'PRE', false], - ['heading1', 'H1', false], - ['heading2', 'H2', false], - ['heading3', 'H3', false], - ['heading4', 'H4', false], - ['heading5', 'H5', false], - ['heading6', 'H6', false], - ['blockquote', 'BLOCKQUOTE', false], + for (const [style, cssSelector, isList] of [ + ['paragraph', 'p:not(.small, .lead)', false], + ['pre', 'pre', false], + ['heading1', 'h1:not(.display-1, .display-2, .display-3, .display-4)', false], + ['heading2', 'h2', false], + ['heading3', 'h3', false], + ['heading4', 'h4', false], + ['heading5', 'h5', false], + ['heading6', 'h6', false], + ['display-1', 'h1.display-1', false], + ['display-2', 'h1.display-2', false], + ['display-3', 'h1.display-3', false], + ['display-4', 'h1.display-4', false], + ['blockquote', 'blockquote', false], + ['small', '.small', false], + ['light', '.lead', false], ['unordered', 'UL', true], ['ordered', 'OL', true], ['checklist', 'CL', true], @@ -3132,8 +3140,8 @@ export class OdooEditor extends EventTarget { button.classList.toggle('active', false); } else if (button) { const isActive = isList - ? block.tagName === 'LI' && getListMode(block.parentElement) === tag - : block.tagName === tag; + ? block.tagName === 'LI' && getListMode(block.parentElement) === cssSelector + : block.matches(cssSelector); button.classList.toggle('active', isActive); if (!isList && isActive) { diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js index bff7f560336..0d25e024013 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js @@ -51,6 +51,8 @@ import { resetOuids, firstLeaf, lastLeaf, + FONT_SIZE_CLASSES, + TEXT_STYLE_CLASSES, } from '../utils/utils.js'; const TEXT_CLASSES_REGEX = /\btext-[^\s]*\b/; @@ -322,7 +324,7 @@ export const editorCommands = { redo: editor => editor.historyRedo(), // Change tags - setTag(editor, tagName) { + setTag(editor, tagName, extraClass = "") { const range = getDeepRange(editor.editable, { correctTripleClick: true }); const selectedBlocks = [...new Set(getTraversedNodes(editor.editable, range).map(closestBlock))]; const deepestSelectedBlocks = selectedBlocks.filter(block => ( @@ -340,7 +342,24 @@ export const editorCommands = { if (inLI && tagName === "P") { inLI.oToggleList(0); } else { - setTagName(block, tagName); + const newEl = setTagName(block, tagName); + newEl.classList.remove( + ...FONT_SIZE_CLASSES, + ...TEXT_STYLE_CLASSES, + // We want to be able to edit the case `

` + // but in that case, we want to display "Header 2" and + // not "Header 3" as it is more important to display + // the semantic tag being used (especially for h1 ones). + // This is why those are not in `TEXT_STYLE_CLASSES`. + "h1", "h2", "h3", "h4", "h5", "h6" + ); + delete newEl.style.fontSize; + if (extraClass) { + newEl.classList.add(extraClass); + } + if (newEl.classList.length === 0) { + newEl.removeAttribute("class"); + } } } else { // eg do not change a
into a h1: insert the h1 diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js index d5c4225128f..e64b6b85cf4 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js @@ -67,6 +67,11 @@ export const PROTECTED_BLOCK_TAG = ['TR','TD','TABLE','TBODY','UL','OL','LI']; export const FONT_SIZE_CLASSES = ["display-1-fs", "display-2-fs", "display-3-fs", "display-4-fs", "h1-fs", "h2-fs", "h3-fs", "h4-fs", "h5-fs", "h6-fs", "base-fs", "small"]; +/** + * Array of all the classes used by the editor to change the text style. + */ +export const TEXT_STYLE_CLASSES = ["display-1", "display-2", "display-3", "display-4", "lead"]; + //------------------------------------------------------------------------------ // Position and sizes //------------------------------------------------------------------------------ @@ -964,7 +969,7 @@ const formatsSpecs = { .find(cls => node.classList.contains(cls)), addStyle: (node, props) => node.classList.add(props.className), removeStyle: (node) => { - node.classList.remove(...FONT_SIZE_CLASSES); + node.classList.remove(...FONT_SIZE_CLASSES, ...TEXT_STYLE_CLASSES); if (node.classList.length === 0) { node.removeAttribute("class"); } @@ -1828,10 +1833,12 @@ export function isColorGradient(value) { */ export function getFontSizeDisplayValue(sel, getCSSVariableValue, convertNumericToUnit) { const tagNameRelatedToFontSize = ["h1", "h2", "h3", "h4", "h5", "h6"]; + const styleClassesRelatedToFontSize = ["display-1", "display-2", "display-3", "display-4"]; const closestStartContainerEl = closestElement(sel.getRangeAt(0).startContainer); const closestFontSizedEl = closestStartContainerEl.closest(` [style*='font-size'], ${FONT_SIZE_CLASSES.map(className => `.${className}`)}, + ${styleClassesRelatedToFontSize.map(className => `.${className}`)}, ${tagNameRelatedToFontSize} `); let remValue; @@ -1851,13 +1858,15 @@ export function getFontSizeDisplayValue(sel, getCSSVariableValue, convertNumeric // the toolbar because it's responsive. const fontSizeClass = FONT_SIZE_CLASSES.find( className => closestFontSizedEl.classList.contains(className)); + let fsName; if (fontSizeClass) { - const fsName = fontSizeClass.substring(0, fontSizeClass.length - 3); // Without -fs - remValue = parseFloat(getCSSVariableValue(`${fsName}-font-size`)); + fsName = fontSizeClass.substring(0, fontSizeClass.length - 3); // Without -fs } else { - const tagName = closestFontSizedEl.tagName.toLowerCase(); - remValue = parseFloat(getCSSVariableValue(`${tagName}-font-size`)); + fsName = styleClassesRelatedToFontSize.find( + className => closestFontSizedEl.classList.contains(className)) + || closestFontSizedEl.tagName.toLowerCase(); } + remValue = parseFloat(getCSSVariableValue(`${fsName}-font-size`)); } // It's default font size (no font size class / style). if (remValue === undefined) { diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index 4ed62eb704e..13f0c9bbb4b 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -13,34 +13,50 @@ Normal