[FIX] mass_mailing: get styles for design tab from DOM

Before this commit, the design tab retrieved the current styles
exclusively from the `#design-element` style element. This meant that if
a template included that element to define its own styles, the defaults
were lost for the styles that it didn't define.
This reads the styles from the DOM instead so this is never an issue.

In order for the design tab to have values to read, we initialized the
`#design-tab` element with the css of the page. This was a temporary
workaround. Now that values are read via `getComputedStyle`, we don't
need it anymore.

task-2833420

closes odoo/odoo#89745

X-original-commit: e8f2f71f76f86bae84253374935fd7d14428746d
Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
This commit is contained in:
Antoine Guenet
2022-04-27 11:06:25 +02:00
parent 9323cee2f2
commit 5764c74232
2 changed files with 36 additions and 68 deletions
@@ -1,71 +1,15 @@
/** @odoo-module alias=mass_mailing.design_constants**/
export const CSS_PREFIX = '.o_mail_wrapper';
export const DEFAULT_CSS_OBJECT = {
h1: {
'font-size': '35px',
color: '#212529',
'font-family': 'Arial,Helvetica Neue,Helvetica,sans-serif',
},
h2: {
'font-size': '28px',
color: '#212529',
'font-family': 'Arial,Helvetica Neue,Helvetica,sans-serif',
},
h3: {
'font-size': '24.5px',
color: '#212529',
'font-family': 'Arial,Helvetica Neue,Helvetica,sans-serif',
},
'p, p > *, li, li > *': {
'font-size': '14px',
color: '#212529',
'font-family': 'Arial,Helvetica Neue,Helvetica,sans-serif',
},
'a:not(.btn), a.btn-link': {
'text-decoration-line': 'none',
color: '#276e72',
},
'a.btn-primary, a.btn-outline-primary, a.btn-fill-primary': {
'font-size': '12.25px',
color: '#FFFFFF',
'background-color': '#35979c',
'border-color': '#35979c',
'border-style': 'solid',
'border-width': '1px',
},
'a.btn-secondary, a.btn-outline-secondary, a.btn-fill-secondary': {
'font-size': '12.25px',
color: '#FFFFFF',
'background-color': '#685563',
'border-color': '#685563',
'border-style': 'solid',
'border-width': '1px',
},
hr: {
'border-top-color': '#212529',
'border-top-style': 'solid',
'border-top-width': '1px',
width: '100%',
}
};
export const DEFAULT_CSS = Object.keys(DEFAULT_CSS_OBJECT).map( // selectors
key => key.split(',').map( // each individual comma separated selector
selector => `${CSS_PREFIX} ${selector.trim()} {\n ${ // selector {
Object.keys(DEFAULT_CSS_OBJECT[key]).map( // css properties
prop => `${prop}: ${DEFAULT_CSS_OBJECT[key][prop]}`) // [prop: value]
.join(';\n ')}\n}` // prop: value;
).join('\n')
).join('\n'); // css text
export const BTN_SIZE_STYLES = {
'btn-sm': {
'padding': '0.25rem 0.5rem',
'padding': '3px 7.5px',
'font-size': '0.875rem',
'line-height': '1.5rem',
},
'btn-lg': {
'padding': '0.5rem 1rem',
'padding': '7px 14px',
'font-size': '1.25rem',
'line-height': '1.5rem',
},
@@ -126,7 +70,6 @@ export const initializeDesignTabCss = $editable => {
// The style element needs to be within the layout of the email in
// order to be saved along with it.
$editable.find('.o_layout').prepend(styleElement);
styleElement.textContent = splitCss(DEFAULT_CSS);
}
};
@@ -143,7 +86,7 @@ export const FONT_FAMILIES = [
].map(fontFamily => normalizeFontFamily(fontFamily));
export default {
CSS_PREFIX, DEFAULT_CSS_OBJECT, DEFAULT_CSS, BTN_SIZE_STYLES,
DEFAULT_BUTTON_SIZE, PRIORITY_STYLES, RE_CSS_TEXT_MATCH, FONT_FAMILIES,
CSS_PREFIX, BTN_SIZE_STYLES, DEFAULT_BUTTON_SIZE, PRIORITY_STYLES,
RE_CSS_TEXT_MATCH, FONT_FAMILIES,
splitCss, getFontName, normalizeFontFamily, initializeDesignTabCss,
}
@@ -185,6 +185,7 @@ options.registry.DesignTab = options.Class.extend({
const res = await this._super(...arguments);
const $editable = this.options.wysiwyg.getEditable();
this.document = $editable[0].ownerDocument;
this.$layout = $editable.find('.o_layout');
initializeDesignTabCss($editable);
this.styleElement = this.document.querySelector('#design-element');
// When editing a stylesheet, its content is not updated so it won't be
@@ -319,18 +320,42 @@ options.registry.DesignTab = options.Class.extend({
const res = await this._super(...arguments);
if (res === undefined) {
switch (methodName) {
case 'applyButtonSize':
case 'customizeCssProperty': {
if (!params.selectorText) {
return;
}
const rule = this._getRule(this._getSelectors(params.selectorText)[0]);
if (params.possibleValues && params.possibleValues[1] === FONT_FAMILIES[0]) {
// For font-family, we need to normalize it so it
// matches an option value.
return rule && normalizeFontFamily(rule.style.getPropertyValue('font-family'));
} else {
return rule && rule.style.getPropertyValue(params.cssProperty);
// Here we parse the selector in order to create a matching
// element that we inject into the DOM so we can retrieve
// its computed style. We then remove the element from the
// DOM, no harm, no foul.
const firstSelector = params.selectorText.split(',')[0].replace(CSS_PREFIX, '').trim();
const classes = firstSelector.replace(/:not\([^\)]*\)/g, '').match(/\.([\w\d-_]+)/g) || [];
const fakeElement = document.createElement(firstSelector.split(/[\.:, ]/)[0]);
for (const className of classes) {
fakeElement.classList.toggle(className.replace('.', ''), true);
}
this.$layout.find(CSS_PREFIX).prepend(fakeElement);
let res;
if (methodName === 'applyButtonSize') {
// Match a button size by its padding value.
const padding = getComputedStyle(fakeElement).padding;
const classIndex = Object.values(BTN_SIZE_STYLES).findIndex(style => style.padding === padding);
res = classIndex >= 0 ? Object.keys(BTN_SIZE_STYLES)[classIndex] : DEFAULT_BUTTON_SIZE;
} else {
fakeElement.style.display = 'none'; // Needed to get width in %.
res = getComputedStyle(fakeElement)[params.cssProperty || 'font-family'];
if (params.possibleValues && params.possibleValues[1] === FONT_FAMILIES[0]) {
// For font-family, we need to normalize it so it
// matches an option value.
res = normalizeFontFamily(res);
}
if (params.cssProperty === 'font-weight') {
res = parseInt(res) >= 600 ? 'bolder' : '';
}
}
fakeElement.remove();
return res;
}
case 'applyButtonSize':
// Match a button size by its padding value.