Skip to content

Commit 0e352f1

Browse files
committed
[FIX] html_editor,html_builder: handle gradient & o_cc & transparent image
This commit ensures that when there is a combination of background colors (o_cc, class color, static color, and gradient color) and transparent images, the background gradient is correctly applied. It also ensures that the gradient is not overridden by the transparent image. closes odoo#211634 Signed-off-by: Soukéina Bojabza (sobo) <[email protected]>
1 parent 23e4ea8 commit 0e352f1

File tree

5 files changed

+364
-22
lines changed

5 files changed

+364
-22
lines changed

addons/html_builder/static/src/core/color_style_plugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ class ColorStylePlugin extends Plugin {
88
static dependencies = ["color"];
99
resources = {
1010
builder_style_actions: this.getStyleActions(),
11-
apply_color_style: withSequence(5, (element, mode, color) => {
12-
applyNeededCss(element, mode === "backgroundColor" ? "background-color" : mode, color);
11+
apply_style: withSequence(5, (element, cssProp, color) => {
12+
applyNeededCss(element, cssProp, color);
1313
return true;
1414
}),
1515
};

addons/html_builder/static/src/utils/utils_css.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -531,13 +531,8 @@ export function applyNeededCss(
531531
computedStyle = window.getComputedStyle(el),
532532
{ force = false, allowImportant = true } = {}
533533
) {
534-
const classes = [1, 2, 3, 4, 5].map((i) => `o_cc${i}`);
535-
el.classList.remove(...classes);
536-
if (cssValue.startsWith("o_cc")) {
537-
el.style.removeProperty(cssProp);
538-
el.classList.add(cssValue);
539-
return;
540-
}
534+
// Change camelCase to kebab-case.
535+
cssProp = cssProp.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
541536
if (force) {
542537
el.style.setProperty(cssProp, cssValue, allowImportant ? "important" : "");
543538
return true;

addons/html_editor/static/src/main/font/color_plugin.js

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ import {
2424
COLOR_COMBINATION_CLASSES_REGEX,
2525
} from "@web/core/utils/colors";
2626
import { ColorSelector } from "./color_selector";
27+
import { backgroundImageCssToParts, backgroundImagePartsToCss } from "@html_editor/utils/image";
2728

2829
const RGBA_OPACITY = 0.6;
2930
const HEX_OPACITY = "99";
31+
const COLOR_COMBINATION_CLASSES = [1, 2, 3, 4, 5].map((i) => `o_cc${i}`);
32+
const COLOR_COMBINATION_SELECTOR = COLOR_COMBINATION_CLASSES.map((c) => `.${c}`).join(", ");
3033

3134
/**
3235
* @typedef { Object } ColorShared
@@ -80,7 +83,7 @@ export class ColorPlugin extends Plugin {
8083
* @param {string} color hexadecimal or bg-name/text-name class
8184
* @param {'color'|'backgroundColor'} mode 'color' or 'backgroundColor'
8285
*/
83-
apply_color_style: (element, mode, color) => {
86+
apply_style: (element, mode, color) => {
8487
element.style[mode] = color;
8588
return true;
8689
},
@@ -445,30 +448,84 @@ export class ColorPlugin extends Plugin {
445448
* @param {'color'|'backgroundColor'} mode 'color' or 'backgroundColor'
446449
*/
447450
colorElement(element, color, mode) {
451+
let parts = backgroundImageCssToParts(element.style["background-image"]);
448452
const oldClassName = element.getAttribute("class") || "";
453+
454+
if (element.matches(COLOR_COMBINATION_SELECTOR)) {
455+
removePresetGradient(element);
456+
}
457+
458+
if (color.startsWith("o_cc")) {
459+
parts = backgroundImageCssToParts(element.style["background-image"]);
460+
element.classList.remove(...COLOR_COMBINATION_CLASSES);
461+
element.classList.add(color);
462+
setBackgroundImageAndOverride(element, element.style["background-image"]);
463+
this.fixColorCombination(element);
464+
return;
465+
}
466+
467+
if (mode === "backgroundColor") {
468+
if (!color) {
469+
element.classList.remove(...COLOR_COMBINATION_CLASSES);
470+
}
471+
delete parts.gradient;
472+
const newBackgroundImage = backgroundImagePartsToCss(parts);
473+
setBackgroundImageAndOverride(element, newBackgroundImage);
474+
element.style["background-color"] = "";
475+
}
476+
449477
const newClassName = oldClassName
450478
.replace(mode === "color" ? TEXT_CLASSES_REGEX : BG_CLASSES_REGEX, "")
451479
.replace(/\btext-gradient\b/g, "") // cannot be combined with setting a background
452480
.replace(/\s+/, " ");
453-
oldClassName !== newClassName && element.setAttribute("class", newClassName);
454-
element.style["background-image"] = "";
455-
if (mode === "backgroundColor") {
456-
element.style["background"] = "";
481+
if (oldClassName !== newClassName) {
482+
element.setAttribute("class", newClassName);
457483
}
458484
if (color.startsWith("text") || color.startsWith("bg-")) {
459485
element.style[mode] = "";
460486
element.classList.add(color);
461487
} else if (isColorGradient(color)) {
462488
element.style[mode] = "";
489+
parts.gradient = color;
463490
if (mode === "color") {
464-
element.style["background"] = "";
465-
this.delegateTo("apply_color_style", element, "background-image", color);
491+
element.style["background-color"] = "";
466492
element.classList.add("text-gradient");
467-
} else {
468-
this.delegateTo("apply_color_style", element, "background-image", color);
469493
}
494+
this.delegateTo(
495+
"apply_style",
496+
element,
497+
"background-image",
498+
backgroundImagePartsToCss(parts)
499+
);
470500
} else {
471-
this.delegateTo("apply_color_style", element, mode, color);
501+
this.delegateTo("apply_style", element, mode, color);
502+
}
503+
this.fixColorCombination(element);
504+
}
505+
/**
506+
* There is a limitation with css. The defining a background image and a
507+
* background gradient is done only by setting one style (background-image).
508+
* If there is a class (in this case o_cc[1-5]) that defines a gradient, it
509+
* will be overridden by the background-image property.
510+
*
511+
* This function will set the gradient of the o_cc in the background-image
512+
* so that setting an image in the background-image property will not
513+
* override the gradient.
514+
*/
515+
fixColorCombination(element) {
516+
const parts = backgroundImageCssToParts(element.style["background-image"]);
517+
const hasBackgroundColor =
518+
element.style["background-color"] ||
519+
!!element.className.match(/\bbg-/) ||
520+
parts.gradient;
521+
522+
if (!hasBackgroundColor) {
523+
element.style["background-image"] = "";
524+
parts.gradient = backgroundImageCssToParts(
525+
// Compute the style from o_cc class.
526+
getComputedStyle(element).backgroundImage
527+
).gradient;
528+
element.style["background-image"] = backgroundImagePartsToCss(parts);
472529
}
473530
}
474531

@@ -485,3 +542,33 @@ export class ColorPlugin extends Plugin {
485542
function getColorCombinationFromClass(el) {
486543
return el.className.match?.(COLOR_COMBINATION_CLASSES_REGEX)?.[0];
487544
}
545+
546+
/**
547+
* Remove the gradient of the element only if it is the inheritance from the o_cc selector.
548+
*/
549+
function removePresetGradient(element) {
550+
const oldBackgroundImage = element.style["background-image"];
551+
const parts = backgroundImageCssToParts(oldBackgroundImage);
552+
const currentGradient = parts.gradient;
553+
element.style.removeProperty("background-image");
554+
const styleWithoutGradient = getComputedStyle(element);
555+
const presetGradient = backgroundImageCssToParts(styleWithoutGradient.backgroundImage).gradient;
556+
if (presetGradient !== currentGradient) {
557+
const withGradient = backgroundImagePartsToCss(parts);
558+
element.style["background-image"] = withGradient === "none" ? "" : withGradient;
559+
} else {
560+
delete parts.gradient;
561+
const withoutGradient = backgroundImagePartsToCss(parts);
562+
element.style["background-image"] = styleWithoutGradient === "none" ? "" : withoutGradient;
563+
}
564+
}
565+
566+
function setBackgroundImageAndOverride(el, backgroundImage) {
567+
const isNone = !backgroundImage || backgroundImage === "none";
568+
el.style.backgroundImage = isNone ? "" : backgroundImage;
569+
// If the current background image is empty but the inherited one isn't
570+
// force the background image to override the inherited one.
571+
if (isNone && getComputedStyle(el).backgroundImage !== "none") {
572+
el.style.backgroundImage = "none";
573+
}
574+
}

addons/html_editor/static/src/utils/image.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import { isColorGradient } from "@web/core/utils/colors";
66
* @param {string} CSS 'background-image' property value
77
* @returns {Object} contains the separated 'url' and 'gradient' parts
88
*/
9-
export function backgroundImageCssToParts(css) {
9+
export function backgroundImageCssToParts(css = "") {
1010
const parts = {};
11-
css = css || "";
1211
if (css.startsWith("url(")) {
1312
const urlEnd = css.indexOf(")") + 1;
1413
parts.url = css.substring(0, urlEnd).trim();

0 commit comments

Comments
 (0)