Skip to content

Commit 8478118

Browse files
bso-odooAlessandroLupodivy-odooemge-odooFrancoisGe
authored andcommitted
[FIX] website, html_builder: various fixes
This commit contains multiple changes squashed together, coming from various pull requests on master-mysterious-egg. These changes includes: - No image in colorpicker and working gradient editor for preset background - Clean carousel related tests, remove repeated ones and merge/rename files - Fix logo adding/removing option - Use only "color" tabs in countdown color picker options - Add support for compact hex colors (e.g. #fff) - Fix color styling - Fix setExtraStep - Auto-optimize image upon replace media - Adapt extra product image to also rely on openMediaDialog - Use correct mimetype fields from dataset - Adapt tests - Refresh Dynamic Snippet Carousel when scrolling mode is changed Co-authored-by: Alessandro Lupo <[email protected]> Co-authored-by: Benoit Socias <[email protected]> Co-authored-by: divy-odoo <[email protected]> Co-authored-by: emge-odoo <[email protected]> Co-authored-by: FrancoisGe <[email protected]> Co-authored-by: Jinjiu Liu <[email protected]> Co-authored-by: Keval Bhatt <[email protected]> Co-authored-by: Serhii Rubanskyi - seru <[email protected]>
1 parent 12bd75e commit 8478118

36 files changed

+329
-348
lines changed

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Plugin } from "@html_editor/plugin";
2-
import { CSS_SHORTHANDS, applyNeededCss, areCssValuesEqual } from "@html_builder/utils/utils_css";
2+
import {
3+
CSS_SHORTHANDS,
4+
applyNeededCss,
5+
areCssValuesEqual,
6+
normalizeColor,
7+
} from "@html_builder/utils/utils_css";
38

49
export function withoutTransition(editingElement, callback) {
510
if (editingElement.classList.contains("o_we_force_no_transition")) {
@@ -263,17 +268,27 @@ const attributeAction = {
263268
};
264269

265270
const dataAttributeAction = {
266-
getValue: ({ editingElement, params: { mainParam: attributeName } = {} }) =>
267-
editingElement.dataset[attributeName],
271+
// if it's a color action, we have to normalize the value
272+
getValue: ({ editingElement, params: { mainParam: attributeName } = {} }) => {
273+
if (!/(^color|Color)($|(?=[A-Z]))/.test(attributeName)) {
274+
return editingElement.dataset[attributeName];
275+
}
276+
const color = normalizeColor(editingElement.dataset[attributeName]);
277+
return color;
278+
},
268279
isApplied: ({ editingElement, params: { mainParam: attributeName } = {}, value }) => {
269280
if (value) {
281+
const match = value.match(/^var\(--(.*)\)$/);
282+
value = match ? match[1] : value;
270283
return editingElement.dataset[attributeName] === value;
271284
} else {
272285
return !(attributeName in editingElement.dataset);
273286
}
274287
},
275288
apply: ({ editingElement, params: { mainParam: attributeName } = {}, value }) => {
276289
if (value) {
290+
const match = value.match(/^var\(--(.*)\)$/);
291+
value = match ? match[1] : value;
277292
editingElement.dataset[attributeName] = value;
278293
} else {
279294
delete editingElement.dataset[attributeName];

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
rgbaToHex,
2424
COLOR_COMBINATION_CLASSES_REGEX,
2525
} from "@web/core/utils/colors";
26+
import { backgroundImageCssToParts } from "@html_editor/utils/image";
2627
import { ColorSelector } from "./color_selector";
2728

2829
const RGBA_OPACITY = 0.6;
@@ -136,7 +137,8 @@ export class ColorPlugin extends Plugin {
136137
getElementColors(el) {
137138
const elStyle = getComputedStyle(el);
138139
const backgroundImage = elStyle.backgroundImage;
139-
const hasGradient = isColorGradient(backgroundImage);
140+
const gradient = backgroundImageCssToParts(backgroundImage).gradient;
141+
const hasGradient = isColorGradient(gradient);
140142
const hasTextGradientClass = el.classList.contains("text-gradient");
141143

142144
let backgroundColor = elStyle.backgroundColor;
@@ -155,9 +157,9 @@ export class ColorPlugin extends Plugin {
155157
}
156158

157159
return {
158-
color: hasGradient && hasTextGradientClass ? backgroundImage : rgbaToHex(elStyle.color),
160+
color: hasGradient && hasTextGradientClass ? gradient : rgbaToHex(elStyle.color),
159161
backgroundColor:
160-
hasGradient && !hasTextGradientClass ? backgroundImage : rgbaToHex(backgroundColor),
162+
hasGradient && !hasTextGradientClass ? gradient : rgbaToHex(backgroundColor),
161163
};
162164
}
163165

addons/html_editor/static/src/main/media/image_crop_plugin.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ export class ImageCropPlugin extends Plugin {
4242
onSave: async (newDataset) => {
4343
// todo: should use the mutex if there is one?
4444
const updateImageAttributes =
45-
await this.dependencies.imagePostProcess.processImage(
46-
selectedImg,
47-
newDataset
48-
);
45+
await this.dependencies.imagePostProcess.processImage({
46+
img: selectedImg,
47+
newDataset,
48+
});
4949
updateImageAttributes();
5050
this.dependencies.history.addStep();
5151
},

addons/html_editor/static/src/main/media/image_post_process_plugin.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,21 @@ export class ImagePostProcessPlugin extends Plugin {
2424
*
2525
* @param {HTMLImageElement} img the image to which modifications are applied
2626
* @param {Object} newDataset an object containing the modifications to apply
27+
* @param {Function} [onImageInfoLoaded] can be used to fill
28+
* newDataset after having access to image info, return true to cancel call
2729
* @returns {Function} callback that sets dataURL of the image with the
2830
* applied modifications to `img` element
2931
*/
30-
async processImage(img, newDataset = {}) {
32+
async processImage({ img, newDataset = {}, onImageInfoLoaded }) {
3133
const processContext = {};
3234
if (!newDataset.originalSrc || !newDataset.mimetypeBeforeConversion) {
3335
Object.assign(newDataset, await loadImageInfo(img));
3436
}
37+
if (onImageInfoLoaded) {
38+
if (await onImageInfoLoaded(newDataset)) {
39+
return () => {};
40+
}
41+
}
3542
for (const cb of this.getResource("process_image_warmup_handlers")) {
3643
const addedContext = await cb(img, newDataset);
3744
if (addedContext) {

addons/html_editor/static/src/main/media/media_dialog/image_selector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export class ImageSelector extends FileSelector {
9494

9595
this.fileMimetypes = IMAGE_MIMETYPES.join(",");
9696
this.isImageField =
97-
!!this.props.media?.closest("[data-oe-type=image]") || !!this.env.addFieldImage;
97+
!!this.props.media?.closest("[data-oe-type=image]") || !!this.props.addFieldImage;
9898
}
9999

100100
get canLoadMore() {

addons/html_editor/static/src/main/media/media_dialog/media_dialog.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export class MediaDialog extends Component {
133133
this.addTab(TABS.IMAGES, {
134134
useMediaLibrary: this.props.useMediaLibrary,
135135
multiSelect: this.props.multiImages,
136+
addFieldImage: this.props.addFieldImage,
136137
});
137138
}
138139
if (!noIcons) {

addons/html_editor/static/src/main/media/media_plugin.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,12 @@ export class MediaPlugin extends Plugin {
126126
}
127127
}
128128

129-
onSaveMediaDialog(element, { node }) {
129+
async onSaveMediaDialog(element, { node }) {
130130
if (!element) {
131131
// @todo @phoenix to remove
132132
throw new Error("Element is required: onSaveMediaDialog");
133133
// return;
134134
}
135-
136135
if (node) {
137136
const changedIcon = isIconElement(node) && isIconElement(element);
138137
if (changedIcon) {
@@ -151,11 +150,25 @@ export class MediaPlugin extends Plugin {
151150
// Collapse selection after the inserted/replaced element.
152151
const [anchorNode, anchorOffset] = rightPos(element);
153152
this.dependencies.selection.setSelection({ anchorNode, anchorOffset });
154-
this.delegateTo("afer_save_media_dialog_handlers", element);
153+
this.delegateTo("after_save_media_dialog_handlers", element);
155154
this.dependencies.history.addStep();
156155
}
157156

158157
openMediaDialog(params = {}, editableEl = null) {
158+
const oldSave =
159+
params.save || ((element) => this.onSaveMediaDialog(element, { node: params.node }));
160+
params.save = async (...args) => {
161+
const selection = args[0];
162+
const elements = selection
163+
? selection[Symbol.iterator]
164+
? selection
165+
: [selection]
166+
: [];
167+
for (const onMediaDialogSaved of this.getResource("on_media_dialog_saved_handlers")) {
168+
await onMediaDialogSaved(elements, { node: params.node });
169+
}
170+
return oldSave(...args);
171+
};
159172
const { resModel, resId, field, type } = this.getRecordInfo(editableEl);
160173
const mediaDialogClosedPromise = this.dependencies.dialog.addDialog(MediaDialog, {
161174
resModel,
@@ -165,9 +178,6 @@ export class MediaPlugin extends Plugin {
165178
((resModel === "ir.ui.view" && field === "arch") || type === "html")
166179
), // @todo @phoenix: should be removed and moved to config.mediaModalParams
167180
media: params.node,
168-
save: (element) => {
169-
this.onSaveMediaDialog(element, { node: params.node });
170-
},
171181
onAttachmentChange: this.config.onAttachmentChange || (() => {}),
172182
noVideos: !this.config.allowMediaDialogVideo,
173183
noImages: !this.config.allowImage,

addons/html_editor/static/src/others/embedded_components/plugins/caption_plugin/caption_plugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class CaptionPlugin extends Plugin {
3737
clean_for_save_handlers: this.cleanForSave.bind(this),
3838
mount_component_handlers: this.setupNewCaption.bind(this),
3939
delete_image_handlers: this.handleDeleteImage.bind(this),
40-
afer_save_media_dialog_handlers: this.onImageReplaced.bind(this),
40+
after_save_media_dialog_handlers: this.onImageReplaced.bind(this),
4141
hints: [{ selector: "FIGCAPTION", text: _t("Write a caption...") }],
4242
unsplittable_node_predicates: [
4343
(node) => ["FIGURE", "FIGCAPTION"].includes(node.nodeName), // avoid merge

addons/web/static/src/core/utils/colors.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,16 @@ export function convertCSSColorToRgba(cssColor) {
217217
}
218218

219219
// Otherwise, check if cssColor is an hexadecimal code color
220+
// first check if it's in its compact form (e.g. #FFF)
221+
if (/^#([0-9a-f]{3})$/i.test(cssColor)) {
222+
return {
223+
red: parseInt(cssColor[1] + cssColor[1], 16),
224+
green: parseInt(cssColor[2] + cssColor[2], 16),
225+
blue: parseInt(cssColor[3] + cssColor[3], 16),
226+
opacity: 100,
227+
};
228+
}
229+
220230
if (/^#([0-9A-F]{6}|[0-9A-F]{8})$/i.test(cssColor)) {
221231
return {
222232
red: parseInt(cssColor.substr(1, 2), 16),

addons/website/static/src/builder/plugins/background_option/background_image_option_plugin.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class BackgroundImageOptionPlugin extends Plugin {
7777
editingElement.querySelector(".o_we_bg_filter")?.remove();
7878
this.applyReplaceBackgroundImage.bind(this)({
7979
editingElement: editingElement,
80-
loadResult: "",
80+
loadResult: undefined,
8181
params: { forceClean: true },
8282
});
8383
this.dispatchTo("on_bg_image_hide_handlers", editingElement);
@@ -134,31 +134,38 @@ export class BackgroundImageOptionPlugin extends Plugin {
134134
newEditingEl.classList.toggle("o_modified_image_to_save", isModifiedImage);
135135
}
136136
}
137-
loadReplaceBackgroundImage() {
137+
loadReplaceBackgroundImage({ editingElement }) {
138138
return new Promise((resolve) => {
139139
const onClose = this.dependencies.media.openMediaDialog({
140140
onlyImages: true,
141-
save: (imageEl) => {
142-
resolve(imageEl.getAttribute("src"));
141+
node: editingElement,
142+
save: async (imageEl) => {
143+
resolve(imageEl);
143144
},
144145
});
145146
onClose.then(resolve);
146147
});
147148
}
148149
applyReplaceBackgroundImage({
149150
editingElement,
150-
loadResult: imageSrc,
151+
loadResult: imageEl,
151152
params: { forceClean = false },
152153
}) {
153-
if (!forceClean && !imageSrc) {
154+
if (!forceClean && !imageEl) {
154155
// Do nothing: no images has been selected on the media dialog
155156
return;
156157
}
157-
this.setImageBackground(editingElement, imageSrc);
158+
const src = imageEl?.src || "";
159+
this.setImageBackground(editingElement, src);
158160
for (const attr of removeOnImageChangeAttrs) {
159161
delete editingElement.dataset[attr];
160162
}
161-
// TODO: call _autoOptimizeImage of the ImageHandlersOption
163+
if (imageEl) {
164+
if (src.startsWith("data:")) {
165+
editingElement.classList.add("o_modified_image_to_save");
166+
}
167+
Object.assign(editingElement.dataset, imageEl.dataset);
168+
}
162169
}
163170
/**
164171
*

0 commit comments

Comments
 (0)