[IMP] web_editor: rebuild the text style dropdown

This commit improves the text style dropdown by adding new styles with
responsive font sizes. This will allow to solve the biggest responsive
font-size problem we have right now: users add a cover snippet with
a big font-size (it is currently hardcoded as 62px font size in
snippets). On mobile, it stays 62 and it's too big. Using displayN
classes of bootstrap will allow to solve that problem. Previous commit
also changed the way custom font-sizes work, so after this commit, and
after snippets are adapted every text will now be responsive.

task-1958098

Part-of: odoo/odoo#129791
This commit is contained in:
Guillaume (gdi)
2023-10-19 19:08:11 +00:00
committed by qsm-odoo
parent 194f73a9bb
commit c91f5fdc47
4 changed files with 94 additions and 42 deletions
@@ -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) {
@@ -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 `<h2 class="h3">`
// 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 <div> into a h1: insert the h1
@@ -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) {
+38 -22
View File
@@ -13,34 +13,50 @@
<span title="Text style">Normal</span>
</button>
<ul class="dropdown-menu">
<li id="display-1-dropdown-item">
<a class="dropdown-item" href="#" id="display-1" data-call="setTag" data-arg1="h1,display-1">Header 1 Display 1</a>
</li>
<li id="display-2-dropdown-item">
<a class="dropdown-item" href="#" id="display-2" data-call="setTag" data-arg1="h1,display-2">Header 1 Display 2</a>
</li>
<li id="display-3-dropdown-item">
<a class="dropdown-item" href="#" id="display-3" data-call="setTag" data-arg1="h1,display-3">Header 1 Display 3</a>
</li>
<li id="display-4-dropdown-item">
<a class="dropdown-item" href="#" id="display-4" data-call="setTag" data-arg1="h1,display-4">Header 1 Display 4</a>
</li>
<li t-if="props.showHeading1" id="heading1-dropdown-item">
<a class="dropdown-item" href="#" id="heading1" data-call="setTag" data-arg1="h1">Header 1</a>
</li>
<li t-if="props.showHeading2" id="heading2-dropdown-item">
<a class="dropdown-item" href="#" id="heading2" data-call="setTag" data-arg1="h2">Header 2</a>
</li>
<li t-if="props.showHeading3" id="heading3-dropdown-item">
<a class="dropdown-item" href="#" id="heading3" data-call="setTag" data-arg1="h3">Header 3</a>
</li>
<li t-if="props.showHeading4" id="heading4-dropdown-item">
<a class="dropdown-item" href="#" id="heading4" data-call="setTag" data-arg1="h4">Header 4</a>
</li>
<li t-if="props.showHeading5" id="heading5-dropdown-item">
<a class="dropdown-item" href="#" id="heading5" data-call="setTag" data-arg1="h5">Header 5</a>
</li>
<li t-if="props.showHeading6" id="heading6-dropdown-item">
<a class="dropdown-item" href="#" id="heading6" data-call="setTag" data-arg1="h6">Header 6</a>
</li>
<li id="paragraph-dropdown-item">
<a class="dropdown-item" href="#" id="paragraph" data-call="setTag" data-arg1="p">Normal</a>
</li>
<li id="light-dropdown-item">
<a class="dropdown-item" href="#" id="light" data-call="setTag" data-arg1="p,lead">Light</a>
</li>
<li id="small-dropdown-item">
<a class="dropdown-item" href="#" id="small" data-call="setTag" data-arg1="p,small">Small</a>
</li>
<li id="pre-dropdown-item">
<a class="dropdown-item" href="#" id="pre" data-call="setTag" data-arg1="pre"><pre>Code</pre></a>
</li>
<li t-if="props.showHeading1" id="heading1-dropdown-item">
<a class="dropdown-item" href="#" id="heading1" data-call="setTag" data-arg1="h1"><h1>Header 1</h1></a>
</li>
<li t-if="props.showHeading2" id="heading2-dropdown-item">
<a class="dropdown-item" href="#" id="heading2" data-call="setTag" data-arg1="h2"><h2>Header 2</h2></a>
</li>
<li t-if="props.showHeading3" id="heading3-dropdown-item">
<a class="dropdown-item" href="#" id="heading3" data-call="setTag" data-arg1="h3"><h3>Header 3</h3></a>
</li>
<li t-if="props.showHeading4" id="heading4-dropdown-item">
<a class="dropdown-item" href="#" id="heading4" data-call="setTag" data-arg1="h4"><h4>Header 4</h4></a>
</li>
<li t-if="props.showHeading5" id="heading5-dropdown-item">
<a class="dropdown-item" href="#" id="heading5" data-call="setTag" data-arg1="h5"><h5>Header 5</h5></a>
</li>
<li t-if="props.showHeading6" id="heading6-dropdown-item">
<a class="dropdown-item" href="#" id="heading6" data-call="setTag" data-arg1="h6"><h6>Header 6</h6></a>
<a class="dropdown-item" href="#" id="pre" data-call="setTag" data-arg1="pre">Code</a>
</li>
<li id="blockquote-dropdown-item">
<a class="dropdown-item" href="#" id="blockquote" data-call="setTag" data-arg1="blockquote">
<blockquote>Quote</blockquote>
</a>
<a class="dropdown-item" href="#" id="blockquote" data-call="setTag" data-arg1="blockquote">Quote</a>
</li>
</ul>
</div>