diff --git a/addons/html_builder/static/src/bs/bs.buttons.scss b/addons/html_builder/static/src/bs/bs.buttons.scss index 89f37d2b0de73..9d285967cfb75 100644 --- a/addons/html_builder/static/src/bs/bs.buttons.scss +++ b/addons/html_builder/static/src/bs/bs.buttons.scss @@ -1,4 +1,4 @@ -.o-website-builder_sidebar .btn { +.o-snippets-menu .btn { --btn-font-weight: normal; --btn-font-size: #{$o-we-font-size-sm}; --btn-box-shadow: 0 0; diff --git a/addons/html_builder/static/src/builder.js b/addons/html_builder/static/src/builder.js index c3e07fc785184..3f92621c0fb6d 100644 --- a/addons/html_builder/static/src/builder.js +++ b/addons/html_builder/static/src/builder.js @@ -6,6 +6,7 @@ import { onWillDestroy, onWillStart, onWillUpdateProps, + status, useRef, useState, useSubEnv, @@ -30,11 +31,11 @@ export class Builder extends Component { static template = "html_builder.Builder"; static components = { BlockTab, CustomizeTab, InvisibleElementsPanel }; static props = { - closeEditor: { type: Function }, + closeEditor: { type: Function, optional: true }, reloadEditor: { type: Function, optional: true }, onEditorLoad: { type: Function, optional: true }, installSnippetModule: { type: Function, optional: true }, - snippetsName: { type: String }, + snippetModel: { type: Object }, toggleMobile: { type: Function }, overlayRef: { type: Function }, iframeLoaded: { type: Object }, @@ -42,6 +43,10 @@ export class Builder extends Component { Plugins: { type: Array, optional: true }, config: { type: Object, optional: true }, getThemeTab: { type: Function, optional: true }, + editableSelector: { type: String }, + toggleFullscreen: { type: Function, optional: true }, + toggleCodeView: { type: Function, optional: true }, + getExternalScrollableAncestor: { type: Function, optional: true }, }; static defaultProps = { onEditorLoad: () => {}, @@ -66,6 +71,7 @@ export class Builder extends Component { this.dialog = useService("dialog"); this.ui = useService("ui"); this.notification = useService("notification"); + this.snippetModel = useState(this.props.snippetModel); const editorBus = new EventBus(); @@ -79,6 +85,9 @@ export class Builder extends Component { if (!isPreviewing) { this.state.canUndo = this.editor.shared.history.canUndo(); this.state.canRedo = this.editor.shared.history.canRedo(); + if (this.props.config.onChange) { + this.props.config.onChange(); + } this.updateInvisibleEls(); editorBus.trigger("UPDATE_EDITING_ELEMENT"); editorBus.trigger("DOM_UPDATED"); @@ -91,10 +100,10 @@ export class Builder extends Component { }); }, closeEditor: async () => { - await this.props.closeEditor(); + await this.props.closeEditor?.(); }, installSnippetModule: async (snippet) => - this.props.installSnippetModule(snippet, this.save.bind(this)), + this.props.installSnippetModule?.(snippet, this.save.bind(this)), resources: { trigger_dom_updated: () => { editorBus.trigger("DOM_UPDATED"); @@ -128,6 +137,7 @@ export class Builder extends Component { cleanForSaveHandlers, wrapWithSaveSnippetHandlers ), + snippetModel: this.snippetModel, getShared: () => this.editor.shared, updateInvisibleElementsPanel: () => this.updateInvisibleEls(), allowCustomStyle: true, @@ -138,15 +148,18 @@ export class Builder extends Component { ); this.props.onEditorLoad(this.editor); - this.snippetModel = useState(useService("html_builder.snippets")); - onWillStart(async () => { await this.snippetModel.load(); // Ensure that the iframe is loaded and the editor is created before // instantiating the sub components that potentially need the // editor. const iframeEl = await this.props.iframeLoaded; - this.editableEl = iframeEl.contentDocument.body.querySelector("#wrapwrap"); + if (status(this) === "destroyed") { + return; + } + this.editableEl = iframeEl.contentDocument.body.querySelector( + this.props.editableSelector + ); setEditableWindow(iframeEl.contentWindow); setEditableDocument(iframeEl.contentDocument); @@ -172,7 +185,7 @@ export class Builder extends Component { // }); onWillDestroy(() => { this.editor.destroy(); - this.editableEl.removeEventListener("dragstart", this.onDragStart); + this.editableEl?.removeEventListener("dragstart", this.onDragStart); // actionService.setActionMode("current"); }); @@ -221,6 +234,9 @@ export class Builder extends Component { } async save() { + if (!this.props.closeEditor) { + return; + } this.editor.shared.operation.next(this._save.bind(this), { withLoadingEffect: false }); } @@ -266,6 +282,9 @@ export class Builder extends Component { } onBeforeUnload(event) { + if (!this.props.closeEditor) { + return; + } if (!this.isSaving && this.state.canUndo) { event.preventDefault(); event.returnValue = "Unsaved changes"; @@ -273,6 +292,9 @@ export class Builder extends Component { } async onBeforeLeave() { + if (!this.props.closeEditor) { + return true; + } if (this.state.canUndo && !this.editor.shared.savePlugin.isAlreadySaved()) { let continueProcess = true; await new Promise((resolve) => { diff --git a/addons/html_builder/static/src/builder.xml b/addons/html_builder/static/src/builder.xml index ca03c0a83418d..995573544b4bf 100644 --- a/addons/html_builder/static/src/builder.xml +++ b/addons/html_builder/static/src/builder.xml @@ -9,9 +9,13 @@ - - + + + + +
@@ -29,12 +33,12 @@ Edit
- + diff --git a/addons/html_builder/static/src/core/border_radius_style_plugin.js b/addons/html_builder/static/src/core/border_radius_style_plugin.js index df6a13ddbca62..ac07d84bcf771 100644 --- a/addons/html_builder/static/src/core/border_radius_style_plugin.js +++ b/addons/html_builder/static/src/core/border_radius_style_plugin.js @@ -37,4 +37,4 @@ class BorderRadiusStylePlugin extends Plugin { }); } } -registry.category("website-plugins").add(BorderRadiusStylePlugin.id, BorderRadiusStylePlugin); +registry.category("builder-plugins").add(BorderRadiusStylePlugin.id, BorderRadiusStylePlugin); diff --git a/addons/html_builder/static/src/core/builder_options_plugin.js b/addons/html_builder/static/src/core/builder_options_plugin.js index 1ca43d3a0ed57..c9a79a9c5c297 100644 --- a/addons/html_builder/static/src/core/builder_options_plugin.js +++ b/addons/html_builder/static/src/core/builder_options_plugin.js @@ -352,7 +352,7 @@ export class BuilderOptionsPlugin extends Plugin { } patchBuilderOptions({ target_name, target_element, method, value }) { - if (!target_name || !target_element || !method || !value) { + if (!target_name || !target_element || !method || (!value && method !== "remove")) { throw new Error( `Missing patch_builder_options required parameters: target_name, target_element, method, value` ); @@ -367,6 +367,9 @@ export class BuilderOptionsPlugin extends Plugin { case "replace": builderOption[target_element] = value; break; + case "remove": + delete builderOption[target_element]; + break; case "add": if (!builderOption[target_element]) { throw new Error( diff --git a/addons/html_builder/static/src/core/building_blocks/builder_colorpicker.js b/addons/html_builder/static/src/core/building_blocks/builder_colorpicker.js index 5bdaf15cdc221..a55d18cc9e321 100644 --- a/addons/html_builder/static/src/core/building_blocks/builder_colorpicker.js +++ b/addons/html_builder/static/src/core/building_blocks/builder_colorpicker.js @@ -108,6 +108,7 @@ export class BuilderColorPicker extends Component { getUsedCustomColors: { type: Function, optional: true }, selectedTab: { type: String, optional: true }, defaultColor: { type: String, optional: true }, + colorPrefix: { type: String, optional: true }, }; static defaultProps = { getUsedCustomColors: () => [], @@ -133,7 +134,7 @@ export class BuilderColorPicker extends Component { applyColorPreview: onPreview, applyColorResetPreview: onPreviewRevert, getUsedCustomColors: this.props.getUsedCustomColors, - colorPrefix: "color-prefix-", + colorPrefix: this.props.colorPrefix || "color-prefix-", showRgbaField: true, noTransparency: this.props.noTransparency, enabledTabs: this.props.enabledTabs, diff --git a/addons/html_builder/static/src/core/color_style_plugin.js b/addons/html_builder/static/src/core/color_style_plugin.js index 1bee68e1060df..7def3a63a51e9 100644 --- a/addons/html_builder/static/src/core/color_style_plugin.js +++ b/addons/html_builder/static/src/core/color_style_plugin.js @@ -33,4 +33,4 @@ class ColorStylePlugin extends Plugin { } } -registry.category("website-plugins").add(ColorStylePlugin.id, ColorStylePlugin); +registry.category("builder-plugins").add(ColorStylePlugin.id, ColorStylePlugin); diff --git a/addons/html_builder/static/src/core/customize_tab_plugin.js b/addons/html_builder/static/src/core/customize_tab_plugin.js index 64c6e4ed49072..b9cc9dbfa46a2 100644 --- a/addons/html_builder/static/src/core/customize_tab_plugin.js +++ b/addons/html_builder/static/src/core/customize_tab_plugin.js @@ -39,4 +39,4 @@ export class CustomizeTabPlugin extends Plugin { } } -registry.category("website-plugins").add(CustomizeTabPlugin.id, CustomizeTabPlugin); +registry.category("builder-plugins").add(CustomizeTabPlugin.id, CustomizeTabPlugin); diff --git a/addons/html_builder/static/src/core/disable_snippets_plugin.js b/addons/html_builder/static/src/core/disable_snippets_plugin.js index c3c0793cbbfa4..3f6a70686f3b0 100644 --- a/addons/html_builder/static/src/core/disable_snippets_plugin.js +++ b/addons/html_builder/static/src/core/disable_snippets_plugin.js @@ -14,7 +14,7 @@ export class DisableSnippetsPlugin extends Plugin { }; setup() { - this.snippetModel = this.services["html_builder.snippets"]; + this.snippetModel = this.config.snippetModel; this._disableSnippets = this.disableUndroppableSnippets.bind(this); // TODO only for website ? @@ -65,6 +65,9 @@ export class DisableSnippetsPlugin extends Plugin { } }; const canDrop = (snippet) => { + if (!snippet) { + return false; + } const snippetEl = snippet.content; return !!dropAreasBySelector.find( ({ selector, exclude, dropAreaEls }) => diff --git a/addons/html_builder/static/src/core/drag_and_drop_plugin.js b/addons/html_builder/static/src/core/drag_and_drop_plugin.js index 3fe0f3d37058b..d0059138bff40 100644 --- a/addons/html_builder/static/src/core/drag_and_drop_plugin.js +++ b/addons/html_builder/static/src/core/drag_and_drop_plugin.js @@ -107,7 +107,9 @@ export class DragAndDropPlugin extends Plugin { this.document.defaultView !== window ? this.document.defaultView : false; const scrollingElement = () => - this.dependencies.dropzone.getDropRootElement() || getScrollingElement(this.document); + this.dependencies.dropzone.getDropRootElement() || + this.config.getExternalScrollableAncestor?.() || + getScrollingElement(this.document); const dragAndDropOptions = { ref: { el: element }, diff --git a/addons/html_builder/static/src/core/drop_zone_plugin.js b/addons/html_builder/static/src/core/drop_zone_plugin.js index 8b15b6f96164e..65822a26390b6 100644 --- a/addons/html_builder/static/src/core/drop_zone_plugin.js +++ b/addons/html_builder/static/src/core/drop_zone_plugin.js @@ -15,7 +15,7 @@ export class DropZonePlugin extends Plugin { ]; setup() { - this.snippetModel = this.services["html_builder.snippets"]; + this.snippetModel = this.config.snippetModel; this.dropzoneSelectors = this.getResource("dropzone_selector"); this.iframe = this.document.defaultView.frameElement; } diff --git a/addons/html_builder/static/src/core/setup_editor_plugin.js b/addons/html_builder/static/src/core/setup_editor_plugin.js index 2bcb0dcfba808..08390d7f9fdbf 100644 --- a/addons/html_builder/static/src/core/setup_editor_plugin.js +++ b/addons/html_builder/static/src/core/setup_editor_plugin.js @@ -8,6 +8,7 @@ export class SetupEditorPlugin extends Plugin { resources = { clean_for_save_handlers: this.cleanForSave.bind(this), normalize_handlers: withSequence(0, this.setContenteditable.bind(this)), + o_editable_selectors: "[data-oe-model]", }; setup() { @@ -20,7 +21,9 @@ export class SetupEditorPlugin extends Plugin { return; } // Add the `o_editable` class on the editable elements - let editableEls = this.getEditableElements("[data-oe-model]") + let editableEls = this.getEditableElements( + this.getResource("o_editable_selectors").join(", ") + ) .filter((el) => !el.matches("link, script")) .filter((el) => !el.hasAttribute("data-oe-readonly")) .filter( diff --git a/addons/html_builder/static/src/core/version_control_plugin.js b/addons/html_builder/static/src/core/version_control_plugin.js index 1d84d07244496..91bb7603f5770 100644 --- a/addons/html_builder/static/src/core/version_control_plugin.js +++ b/addons/html_builder/static/src/core/version_control_plugin.js @@ -14,7 +14,7 @@ export class VersionControlPlugin extends Plugin { return this.accessPerOutdatedEl.get(el); } const snippetKey = el.dataset.snippet; - const snippet = this.services["html_builder.snippets"].getOriginalSnippet(snippetKey); + const snippet = this.config.snippetModel.getOriginalSnippet(snippetKey); let isUpToDate = true; if (snippet) { const { @@ -34,7 +34,7 @@ export class VersionControlPlugin extends Plugin { } replaceWithNewVersion(el) { const snippetKey = el.dataset.snippet; - const snippet = this.services["html_builder.snippets"].getOriginalSnippet(snippetKey); + const snippet = this.config.snippetModel.getOriginalSnippet(snippetKey); const cloneEl = snippet.content.cloneNode(true); el.replaceWith(cloneEl); this.dependencies["builderOptions"].updateContainers(cloneEl); diff --git a/addons/html_builder/static/src/plugins/alert_option_plugin.js b/addons/html_builder/static/src/plugins/alert_option_plugin.js index 2def6b8313c27..bff067d42a547 100644 --- a/addons/html_builder/static/src/plugins/alert_option_plugin.js +++ b/addons/html_builder/static/src/plugins/alert_option_plugin.js @@ -15,6 +15,7 @@ class AlertOptionPlugin extends Plugin { withSequence(before(WIDTH), { template: "html_builder.AlertOption", selector: ".s_alert", + name: "alertTypeOption", }), ], so_content_addition_selector: [".s_alert"], @@ -48,4 +49,4 @@ export class AlertIconAction extends BuilderAction { return iconEl.classList.contains(className); } } -registry.category("website-plugins").add(AlertOptionPlugin.id, AlertOptionPlugin); +registry.category("builder-plugins").add(AlertOptionPlugin.id, AlertOptionPlugin); diff --git a/addons/website/static/src/builder/plugins/background_option/background_hook.js b/addons/html_builder/static/src/plugins/background_option/background_hook.js similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_hook.js rename to addons/html_builder/static/src/plugins/background_option/background_hook.js diff --git a/addons/website/static/src/builder/plugins/background_option/background_image.xml b/addons/html_builder/static/src/plugins/background_option/background_image.xml similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_image.xml rename to addons/html_builder/static/src/plugins/background_option/background_image.xml diff --git a/addons/website/static/src/builder/plugins/background_option/background_image_option.js b/addons/html_builder/static/src/plugins/background_option/background_image_option.js similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_image_option.js rename to addons/html_builder/static/src/plugins/background_option/background_image_option.js diff --git a/addons/website/static/src/builder/plugins/background_option/background_image_option_plugin.js b/addons/html_builder/static/src/plugins/background_option/background_image_option_plugin.js similarity index 99% rename from addons/website/static/src/builder/plugins/background_option/background_image_option_plugin.js rename to addons/html_builder/static/src/plugins/background_option/background_image_option_plugin.js index 6c4f5c6ce468a..0ecda09168148 100644 --- a/addons/website/static/src/builder/plugins/background_option/background_image_option_plugin.js +++ b/addons/html_builder/static/src/plugins/background_option/background_image_option_plugin.js @@ -220,5 +220,5 @@ export class DynamicColorAction extends BuilderAction { } registry - .category("website-plugins") + .category("builder-plugins") .add(BackgroundImageOptionPlugin.id, BackgroundImageOptionPlugin); diff --git a/addons/website/static/src/builder/plugins/background_option/background_option.js b/addons/html_builder/static/src/plugins/background_option/background_option.js similarity index 95% rename from addons/website/static/src/builder/plugins/background_option/background_option.js rename to addons/html_builder/static/src/plugins/background_option/background_option.js index 5ade33733b316..44c37939e7c2b 100644 --- a/addons/website/static/src/builder/plugins/background_option/background_option.js +++ b/addons/html_builder/static/src/plugins/background_option/background_option.js @@ -7,7 +7,7 @@ import { ImageFilterOption } from "@html_builder/plugins/image/image_filter_opti import { ImageFormatOption } from "@html_builder/plugins/image/image_format_option"; export class BackgroundOption extends BaseOptionComponent { - static template = "website.BackgroundOption"; + static template = "html_builder.BackgroundOption"; static components = { BackgroundImageOption, BackgroundPositionOption, diff --git a/addons/website/static/src/builder/plugins/background_option/background_option.xml b/addons/html_builder/static/src/plugins/background_option/background_option.xml similarity index 97% rename from addons/website/static/src/builder/plugins/background_option/background_option.xml rename to addons/html_builder/static/src/plugins/background_option/background_option.xml index 9900372f31e53..2cc280fb2f1b4 100644 --- a/addons/website/static/src/builder/plugins/background_option/background_option.xml +++ b/addons/html_builder/static/src/plugins/background_option/background_option.xml @@ -1,7 +1,7 @@ - + diff --git a/addons/website/static/src/builder/plugins/background_option/background_option_plugin.js b/addons/html_builder/static/src/plugins/background_option/background_option_plugin.js similarity index 84% rename from addons/website/static/src/builder/plugins/background_option/background_option_plugin.js rename to addons/html_builder/static/src/plugins/background_option/background_option_plugin.js index a12d0244eeb6e..c0338ae592aff 100644 --- a/addons/website/static/src/builder/plugins/background_option/background_option_plugin.js +++ b/addons/html_builder/static/src/plugins/background_option/background_option_plugin.js @@ -1,4 +1,4 @@ -import { applyFunDependOnSelectorAndExclude } from "@website/builder/plugins/utils"; +import { applyFunDependOnSelectorAndExclude } from "@html_builder/plugins/utils"; import { Plugin } from "@html_editor/plugin"; import { registry } from "@web/core/registry"; @@ -22,4 +22,4 @@ class BackgroundOptionPlugin extends Plugin { editingEl.classList.add("o_colored_level"); } } -registry.category("website-plugins").add(BackgroundOptionPlugin.id, BackgroundOptionPlugin); +registry.category("builder-plugins").add(BackgroundOptionPlugin.id, BackgroundOptionPlugin); diff --git a/addons/website/static/src/builder/plugins/background_option/background_position_option.js b/addons/html_builder/static/src/plugins/background_option/background_position_option.js similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_position_option.js rename to addons/html_builder/static/src/plugins/background_option/background_position_option.js diff --git a/addons/website/static/src/builder/plugins/background_option/background_position_option.xml b/addons/html_builder/static/src/plugins/background_option/background_position_option.xml similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_position_option.xml rename to addons/html_builder/static/src/plugins/background_option/background_position_option.xml diff --git a/addons/website/static/src/builder/plugins/background_option/background_position_option_plugin.js b/addons/html_builder/static/src/plugins/background_option/background_position_option_plugin.js similarity index 99% rename from addons/website/static/src/builder/plugins/background_option/background_position_option_plugin.js rename to addons/html_builder/static/src/plugins/background_option/background_position_option_plugin.js index 33bc776fa0372..1a8e9e100a641 100644 --- a/addons/website/static/src/builder/plugins/background_option/background_position_option_plugin.js +++ b/addons/html_builder/static/src/plugins/background_option/background_position_option_plugin.js @@ -121,5 +121,5 @@ export class BackgroundPositionOverlayAction extends BuilderAction { } registry - .category("website-plugins") + .category("builder-plugins") .add(BackgroundPositionOptionPlugin.id, BackgroundPositionOptionPlugin); diff --git a/addons/website/static/src/builder/plugins/background_option/background_position_overlay.js b/addons/html_builder/static/src/plugins/background_option/background_position_overlay.js similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_position_overlay.js rename to addons/html_builder/static/src/plugins/background_option/background_position_overlay.js diff --git a/addons/website/static/src/builder/plugins/background_option/background_position_overlay.scss b/addons/html_builder/static/src/plugins/background_option/background_position_overlay.scss similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_position_overlay.scss rename to addons/html_builder/static/src/plugins/background_option/background_position_overlay.scss diff --git a/addons/website/static/src/builder/plugins/background_option/background_position_overlay.xml b/addons/html_builder/static/src/plugins/background_option/background_position_overlay.xml similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_position_overlay.xml rename to addons/html_builder/static/src/plugins/background_option/background_position_overlay.xml diff --git a/addons/website/static/src/builder/plugins/background_option/background_shape_option.js b/addons/html_builder/static/src/plugins/background_option/background_shape_option.js similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_shape_option.js rename to addons/html_builder/static/src/plugins/background_option/background_shape_option.js diff --git a/addons/website/static/src/builder/plugins/background_option/background_shape_option.xml b/addons/html_builder/static/src/plugins/background_option/background_shape_option.xml similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_shape_option.xml rename to addons/html_builder/static/src/plugins/background_option/background_shape_option.xml diff --git a/addons/website/static/src/builder/plugins/background_option/background_shape_option_plugin.js b/addons/html_builder/static/src/plugins/background_option/background_shape_option_plugin.js similarity index 99% rename from addons/website/static/src/builder/plugins/background_option/background_shape_option_plugin.js rename to addons/html_builder/static/src/plugins/background_option/background_shape_option_plugin.js index 975f43c91d2b9..997660677f4be 100644 --- a/addons/website/static/src/builder/plugins/background_option/background_shape_option_plugin.js +++ b/addons/html_builder/static/src/plugins/background_option/background_shape_option_plugin.js @@ -458,5 +458,5 @@ class BackgroundShapeColorAction extends BaseAnimationAction { } registry - .category("website-plugins") + .category("builder-plugins") .add(BackgroundShapeOptionPlugin.id, BackgroundShapeOptionPlugin); diff --git a/addons/website/static/src/builder/plugins/background_option/background_shapes_definition.js b/addons/html_builder/static/src/plugins/background_option/background_shapes_definition.js similarity index 100% rename from addons/website/static/src/builder/plugins/background_option/background_shapes_definition.js rename to addons/html_builder/static/src/plugins/background_option/background_shapes_definition.js diff --git a/addons/html_builder/static/src/plugins/block_alignment_option_plugin.js b/addons/html_builder/static/src/plugins/block_alignment_option_plugin.js index e08144a0aaf0e..5207a4926073c 100644 --- a/addons/html_builder/static/src/plugins/block_alignment_option_plugin.js +++ b/addons/html_builder/static/src/plugins/block_alignment_option_plugin.js @@ -15,4 +15,4 @@ class BlockAlignmentOptionPlugin extends Plugin { }; } -registry.category("website-plugins").add(BlockAlignmentOptionPlugin.id, BlockAlignmentOptionPlugin); +registry.category("builder-plugins").add(BlockAlignmentOptionPlugin.id, BlockAlignmentOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/border_configurator_option.js b/addons/html_builder/static/src/plugins/border_configurator_option.js index 220cc7f63bf1a..1cc94380542c6 100644 --- a/addons/html_builder/static/src/plugins/border_configurator_option.js +++ b/addons/html_builder/static/src/plugins/border_configurator_option.js @@ -1,4 +1,4 @@ -import { BaseOptionComponent, useDomState } from "@html_builder/core/utils"; +import { BaseOptionComponent, useDomState, useGetItemValue } from "@html_builder/core/utils"; export class BorderConfigurator extends BaseOptionComponent { static template = "html_builder.BorderConfiguratorOption"; @@ -17,10 +17,16 @@ export class BorderConfigurator extends BaseOptionComponent { setup() { super.setup(); + this.getItemValue = useGetItemValue(); this.state = useDomState((editingElement) => ({ hasBorder: this.hasBorder(editingElement), })); } + + get showBorderRadiusOption() { + return this.props.withRoundCorner && this.state.hasBorder; + } + getStyleActionParam(param) { return `border-${this.props.direction ? this.props.direction + "-" : ""}${param}`; } diff --git a/addons/html_builder/static/src/plugins/border_configurator_option.xml b/addons/html_builder/static/src/plugins/border_configurator_option.xml index bbefe709f3424..11c18edd3fa14 100644 --- a/addons/html_builder/static/src/plugins/border_configurator_option.xml +++ b/addons/html_builder/static/src/plugins/border_configurator_option.xml @@ -17,7 +17,7 @@ - + diff --git a/addons/html_builder/static/src/plugins/font/font_plugin.js b/addons/html_builder/static/src/plugins/font/font_plugin.js index 1b1ab58b6207f..249037319a400 100644 --- a/addons/html_builder/static/src/plugins/font/font_plugin.js +++ b/addons/html_builder/static/src/plugins/font/font_plugin.js @@ -9,7 +9,7 @@ import { BuilderFontSizeSelector } from "./font_size_selector"; export class BuilderFontPlugin extends Plugin { static id = "builderFont"; static shared = ["getFontsCache", "getFontsData"]; - static dependencies = ["savePlugin", "toolbar"]; + static dependencies = ["toolbar"]; resources = { // Lists CSS variables that will be reset when a font is deleted if // they refer to that font. @@ -139,4 +139,4 @@ export class BuilderFontPlugin extends Plugin { }; } } -registry.category("website-plugins").add(BuilderFontPlugin.id, BuilderFontPlugin); +registry.category("builder-plugins").add(BuilderFontPlugin.id, BuilderFontPlugin); diff --git a/addons/html_builder/static/src/plugins/image/image_filter_option_plugin.js b/addons/html_builder/static/src/plugins/image/image_filter_option_plugin.js index d9c97dce5a3b8..e463c623674e8 100644 --- a/addons/html_builder/static/src/plugins/image/image_filter_option_plugin.js +++ b/addons/html_builder/static/src/plugins/image/image_filter_option_plugin.js @@ -66,4 +66,4 @@ export class SetCustomFilterAction extends BuilderAction { } } -registry.category("website-plugins").add(ImageFilterOptionPlugin.id, ImageFilterOptionPlugin); +registry.category("builder-plugins").add(ImageFilterOptionPlugin.id, ImageFilterOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/image/image_format_option_plugin.js b/addons/html_builder/static/src/plugins/image/image_format_option_plugin.js index 64561ef4ab3a2..efb0a31837e09 100644 --- a/addons/html_builder/static/src/plugins/image/image_format_option_plugin.js +++ b/addons/html_builder/static/src/plugins/image/image_format_option_plugin.js @@ -104,4 +104,4 @@ export class SetImageQualityAction extends BuilderAction { } } -registry.category("website-plugins").add(ImageFormatOptionPlugin.id, ImageFormatOptionPlugin); +registry.category("builder-plugins").add(ImageFormatOptionPlugin.id, ImageFormatOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/image/image_shape_option.js b/addons/html_builder/static/src/plugins/image/image_shape_option.js index 3fd829b4570be..de498258e85bd 100644 --- a/addons/html_builder/static/src/plugins/image/image_shape_option.js +++ b/addons/html_builder/static/src/plugins/image/image_shape_option.js @@ -5,7 +5,9 @@ import { ShapeSelector } from "@html_builder/plugins/shape/shape_selector"; export class ImageShapeOption extends BaseOptionComponent { static template = "html_builder.ImageShapeOption"; - static props = {}; + static props = { + withoutAnimatedShapes: { type: Boolean, optional: true }, + }; setup() { super.setup(); this.customizeTabPlugin = this.env.editor.shared.customizeTab; @@ -48,6 +50,7 @@ export class ImageShapeOption extends BaseOptionComponent { buttonWrapperClassName: "o-hb-img-shape-btn", selectorTitle: _t("Shapes"), shapeGroups: this.imageShapeOption.getImageShapeGroups(), + withoutAnimatedShapes: this.props.withoutAnimatedShapes, } ); } diff --git a/addons/html_builder/static/src/plugins/image/image_shape_option_plugin.js b/addons/html_builder/static/src/plugins/image/image_shape_option_plugin.js index 9d05710055126..d51222096f4ab 100644 --- a/addons/html_builder/static/src/plugins/image/image_shape_option_plugin.js +++ b/addons/html_builder/static/src/plugins/image/image_shape_option_plugin.js @@ -468,4 +468,4 @@ export class ToggleImageShapeRatioAction extends BuilderAction { } } -registry.category("website-plugins").add(ImageShapeOptionPlugin.id, ImageShapeOptionPlugin); +registry.category("builder-plugins").add(ImageShapeOptionPlugin.id, ImageShapeOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/image/image_tool_option_plugin.js b/addons/html_builder/static/src/plugins/image/image_tool_option_plugin.js index f47c510607075..16d91718131a8 100644 --- a/addons/html_builder/static/src/plugins/image/image_tool_option_plugin.js +++ b/addons/html_builder/static/src/plugins/image/image_tool_option_plugin.js @@ -44,11 +44,13 @@ class ImageToolOptionPlugin extends Plugin { OptionComponent: ImageToolOption, selector: "img", exclude: "[data-oe-type='image'] > img", + name: "imageToolOption", }), withSequence(ALIGNMENT_STYLE_PADDING, { template: "html_builder.ImageAndFaOption", selector: "span.fa, i.fa, img", exclude: "[data-oe-type='image'] > img, [data-oe-xpath]", + name: "imageAndFaOption", }), ], builder_actions: { @@ -298,7 +300,7 @@ export class AltAction extends BuilderAction { } } -registry.category("website-plugins").add(ImageToolOptionPlugin.id, ImageToolOptionPlugin); +registry.category("builder-plugins").add(ImageToolOptionPlugin.id, ImageToolOptionPlugin); /** * @param {String} mimetype diff --git a/addons/html_builder/static/src/plugins/layout_column_option_plugin.js b/addons/html_builder/static/src/plugins/layout_column_option_plugin.js index c6cbb0c392c07..e5cfc5348a2b2 100644 --- a/addons/html_builder/static/src/plugins/layout_column_option_plugin.js +++ b/addons/html_builder/static/src/plugins/layout_column_option_plugin.js @@ -122,4 +122,4 @@ export class ChangeColumnCountAction extends BuilderAction { } } -registry.category("website-plugins").add(LayoutColumnOptionPlugin.id, LayoutColumnOptionPlugin); +registry.category("builder-plugins").add(LayoutColumnOptionPlugin.id, LayoutColumnOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/rating_option_plugin.js b/addons/html_builder/static/src/plugins/rating_option_plugin.js index 9067ba2a50022..1b0d109b791ec 100644 --- a/addons/html_builder/static/src/plugins/rating_option_plugin.js +++ b/addons/html_builder/static/src/plugins/rating_option_plugin.js @@ -109,7 +109,7 @@ export class TotalIconsNumberAction extends BuilderAction { } } -registry.category("website-plugins").add(RatingOptionPlugin.id, RatingOptionPlugin); +registry.category("builder-plugins").add(RatingOptionPlugin.id, RatingOptionPlugin); function createIcons({ editingElement, nbActiveIcons, nbTotalIcons }) { const activeIconEl = editingElement.querySelector(".s_rating_active_icons"); diff --git a/addons/html_builder/static/src/plugins/shadow_option_plugin.js b/addons/html_builder/static/src/plugins/shadow_option_plugin.js index 4c6c03ac16081..e7ddbffff816f 100644 --- a/addons/html_builder/static/src/plugins/shadow_option_plugin.js +++ b/addons/html_builder/static/src/plugins/shadow_option_plugin.js @@ -62,7 +62,7 @@ export function shadowToString(shadow) { }`; } -registry.category("website-plugins").add(ShadowOptionPlugin.id, ShadowOptionPlugin); +registry.category("builder-plugins").add(ShadowOptionPlugin.id, ShadowOptionPlugin); export class SetShadowModeAction extends BuilderAction { static id = "setShadowMode"; diff --git a/addons/html_builder/static/src/plugins/shape/shape_selector.js b/addons/html_builder/static/src/plugins/shape/shape_selector.js index d4ec9731d0014..c0746f27c1ce5 100644 --- a/addons/html_builder/static/src/plugins/shape/shape_selector.js +++ b/addons/html_builder/static/src/plugins/shape/shape_selector.js @@ -13,6 +13,7 @@ export class ShapeSelector extends BaseOptionComponent { buttonWrapperClassName: { type: String, optional: true }, imgThroughDiv: { type: Boolean, optional: true }, getShapeUrl: { type: Function, optional: true }, + withoutAnimatedShapes: { type: Boolean, optional: true }, }; setup() { diff --git a/addons/html_builder/static/src/plugins/shape/shape_selector.xml b/addons/html_builder/static/src/plugins/shape/shape_selector.xml index 0b739a37f47c4..f0536785bcf36 100644 --- a/addons/html_builder/static/src/plugins/shape/shape_selector.xml +++ b/addons/html_builder/static/src/plugins/shape/shape_selector.xml @@ -27,7 +27,7 @@
- +
diff --git a/addons/html_builder/static/src/plugins/text_alignment_option_plugin.js b/addons/html_builder/static/src/plugins/text_alignment_option_plugin.js index d17bdb1f76e10..191e1ba83cfdb 100644 --- a/addons/html_builder/static/src/plugins/text_alignment_option_plugin.js +++ b/addons/html_builder/static/src/plugins/text_alignment_option_plugin.js @@ -15,4 +15,4 @@ class TextAlignmentOptionPlugin extends Plugin { }; } -registry.category("website-plugins").add(TextAlignmentOptionPlugin.id, TextAlignmentOptionPlugin); +registry.category("builder-plugins").add(TextAlignmentOptionPlugin.id, TextAlignmentOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/utils.js b/addons/html_builder/static/src/plugins/utils.js new file mode 100644 index 0000000000000..1d9305a5bce7d --- /dev/null +++ b/addons/html_builder/static/src/plugins/utils.js @@ -0,0 +1,24 @@ +export function applyFunDependOnSelectorAndExclude(fn, rootEl, selectorParams) { + const editingEls = getEditingEls(rootEl, selectorParams); + if (!editingEls.length) { + return false; + } + return Promise.all(editingEls.map((el) => fn(el))); +} + +export function getEditingEls(rootEl, { selector, exclude, applyTo }) { + const closestSelector = rootEl.closest(selector); + let editingEls = closestSelector ? [closestSelector] : [...rootEl.querySelectorAll(selector)]; + if (exclude) { + editingEls = editingEls.filter((selectorEl) => !selectorEl.matches(exclude)); + } + if (!applyTo) { + return editingEls; + } + const targetEls = []; + for (const editingEl of editingEls) { + const applyToEls = applyTo ? editingEl.querySelectorAll(applyTo) : [editingEl]; + targetEls.push(...applyToEls); + } + return targetEls; +} diff --git a/addons/html_builder/static/src/plugins/vertical_alignment_option_plugin.js b/addons/html_builder/static/src/plugins/vertical_alignment_option_plugin.js index 9e48b1fd3d7df..64ce91caf4765 100644 --- a/addons/html_builder/static/src/plugins/vertical_alignment_option_plugin.js +++ b/addons/html_builder/static/src/plugins/vertical_alignment_option_plugin.js @@ -5,14 +5,13 @@ import { VerticalAlignmentOption } from "@html_builder/plugins/vertical_alignmen import { withSequence } from "@html_editor/utils/resource"; import { VERTICAL_ALIGNMENT } from "@html_builder/utils/option_sequence"; -class VerticalAlignmentOptionPlugin extends Plugin { +export class VerticalAlignmentOptionPlugin extends Plugin { static id = "verticalAlignmentOption"; resources = { builder_options: [ withSequence(VERTICAL_ALIGNMENT, { OptionComponent: VerticalAlignmentOption, - selector: - ".s_text_image, .s_image_text, .s_three_columns, .s_showcase, .s_numbers, .s_faq_collapse, .s_references, .s_accordion_image, .s_shape_image", + selector: this.selector, applyTo: ".row", props: { level: 1, @@ -23,6 +22,9 @@ class VerticalAlignmentOptionPlugin extends Plugin { SetVerticalAlignmentAction, }, }; + get selector() { + return ".s_text_image, .s_image_text, .s_three_columns, .s_showcase, .s_numbers, .s_faq_collapse, .s_references, .s_accordion_image, .s_shape_image"; + } } export class SetVerticalAlignmentAction extends ClassAction { @@ -39,5 +41,5 @@ export class SetVerticalAlignmentAction extends ClassAction { } registry - .category("website-plugins") + .category("builder-plugins") .add(VerticalAlignmentOptionPlugin.id, VerticalAlignmentOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/vertical_justify_option_plugin.js b/addons/html_builder/static/src/plugins/vertical_justify_option_plugin.js index 09893d633daa8..fc42c02091fcd 100644 --- a/addons/html_builder/static/src/plugins/vertical_justify_option_plugin.js +++ b/addons/html_builder/static/src/plugins/vertical_justify_option_plugin.js @@ -17,5 +17,5 @@ class VerticalJustifyOptionPlugin extends Plugin { } registry - .category("website-plugins") + .category("builder-plugins") .add(VerticalJustifyOptionPlugin.id, VerticalJustifyOptionPlugin); diff --git a/addons/html_builder/static/src/plugins/width_option_plugin.js b/addons/html_builder/static/src/plugins/width_option_plugin.js index a728038197a30..91b2ee058a168 100644 --- a/addons/html_builder/static/src/plugins/width_option_plugin.js +++ b/addons/html_builder/static/src/plugins/width_option_plugin.js @@ -10,8 +10,9 @@ class WidthOptionPlugin extends Plugin { withSequence(WIDTH, { template: "html_builder.WidthOption", selector: ".s_alert, .s_blockquote, .s_text_highlight", + name: "widthOption", }), ], }; } -registry.category("website-plugins").add(WidthOptionPlugin.id, WidthOptionPlugin); +registry.category("builder-plugins").add(WidthOptionPlugin.id, WidthOptionPlugin); diff --git a/addons/html_builder/static/src/sidebar/block_tab.js b/addons/html_builder/static/src/sidebar/block_tab.js index ba8532bc861f1..849021b94d057 100644 --- a/addons/html_builder/static/src/sidebar/block_tab.js +++ b/addons/html_builder/static/src/sidebar/block_tab.js @@ -13,13 +13,16 @@ import { CustomInnerSnippet } from "./custom_inner_snippet"; export class BlockTab extends Component { static template = "html_builder.BlockTab"; static components = { Snippet, CustomInnerSnippet }; - static props = {}; + static props = { + snippetModel: { type: Object }, + getExternalScollableAncestor: { type: Function, optional: true }, + }; setup() { this.dialog = useService("dialog"); this.orm = useService("orm"); this.popover = useService("popover"); - this.snippetModel = useState(useService("html_builder.snippets")); + this.snippetModel = useState(this.props.snippetModel); this.blockTabRef = useRef("block-tab"); // Needed to avoid race condition in tours. this.state = useState({ ongoingInsertion: false }); @@ -198,6 +201,7 @@ export class BlockTab extends Component { this.document.defaultView !== window ? this.document.defaultView : false; const scrollingElement = () => + this.props.getExternalScrollableAncestor?.() || this.shared.dropzone.getDropRootElement() || this.editable.querySelector(".o_notebook") || getScrollingElement(this.document) || diff --git a/addons/html_builder/static/src/sidebar/block_tab.scss b/addons/html_builder/static/src/sidebar/block_tab.scss index f903fb6081cba..25f1d9f8a66b8 100644 --- a/addons/html_builder/static/src/sidebar/block_tab.scss +++ b/addons/html_builder/static/src/sidebar/block_tab.scss @@ -39,6 +39,12 @@ $o-we-zindex: $o-we-overlay-zindex + 1 !default; } } +body.modal-open:has(.o_block_tab) { + .o_snippet { + z-index: var(--modal-zindex) + 1; + } +} + .o_block_tab { background-color: $o-we-sidebar-blocks-content-bg; padding-left: map-get($spacers, 2) ; @@ -173,3 +179,9 @@ $o-we-zindex: $o-we-overlay-zindex + 1 !default; } } } + +.modal-open:has(.o_block_tab) { + .o_snippet { + z-index: 1056; + } +} diff --git a/addons/html_builder/static/src/sidebar/block_tab.xml b/addons/html_builder/static/src/sidebar/block_tab.xml index 2cd20f359361c..85371a28691db 100644 --- a/addons/html_builder/static/src/sidebar/block_tab.xml +++ b/addons/html_builder/static/src/sidebar/block_tab.xml @@ -7,7 +7,7 @@
-
+
Categories
@@ -33,7 +33,8 @@
-
+
Inner Content
@@ -44,6 +45,19 @@
+ +
+
+
+ + + +
+
diff --git a/addons/html_builder/static/src/sidebar/customize_tab.js b/addons/html_builder/static/src/sidebar/customize_tab.js index 5d8273d0751c0..104c93d76d98a 100644 --- a/addons/html_builder/static/src/sidebar/customize_tab.js +++ b/addons/html_builder/static/src/sidebar/customize_tab.js @@ -27,10 +27,10 @@ export class CustomizeTab extends Component { } getCurrentOptionsContainers() { - const currentOptionsContainers = this.props.currentOptionsContainers; - if (!currentOptionsContainers.length) { - return this.env.editor.shared["builderOptions"].getPageContainers(); - } + const currentOptionsContainers = + this.props.currentOptionsContainers || + this.env.editor.shared["builderOptions"].getPageContainers(); + this.state.hasContent = currentOptionsContainers.length > 0; return currentOptionsContainers; } } diff --git a/addons/html_builder/static/src/snippets/snippet_service.js b/addons/html_builder/static/src/snippets/snippet_service.js index a902714fde999..1f710c9e0bfc5 100644 --- a/addons/html_builder/static/src/snippets/snippet_service.js +++ b/addons/html_builder/static/src/snippets/snippet_service.js @@ -5,17 +5,31 @@ import { Reactive } from "@web/core/utils/reactive"; import { escape } from "@web/core/utils/strings"; import { AddSnippetDialog } from "./add_snippet_dialog"; import { registry } from "@web/core/registry"; -import { user } from "@web/core/user"; import { markup } from "@odoo/owl"; +export class SnippetModelFactory { + constructor(services) { + this.services = services; + } + + makeSnippetModel(snippetsName, { context = {} } = {}) { + return new SnippetModel(this.services, { + snippetsName, + context, + }); + } +} + export class SnippetModel extends Reactive { constructor(services, { snippetsName, context }) { super(); this.orm = services.orm; this.dialog = services.dialog; this.snippetsName = snippetsName; + this.uiService = services.ui; this.context = context; this.loadProm = null; + this.beforeReload = null; this.snippetsByCategory = { snippet_groups: [], @@ -57,6 +71,10 @@ export class SnippetModel extends Reactive { return this.snippetsByCategory.snippet_custom_content; } + get snippetContentCategories() { + return Object.entries(this.contentCategories); + } + isCustomInnerContent(customSnippetName) { return !!this.snippetsByCategory.snippet_content.find( (snippet) => snippet.name === customSnippetName @@ -147,7 +165,15 @@ export class SnippetModel extends Reactive { computeSnippetTemplates(snippetsDocument) { const snippetsBody = snippetsDocument.body; - this.snippetsByCategory = {}; + const categories = new Set([ + "snippet_groups", + "snippet_custom", + "snippet_structure", + "snippet_content", + "snippet_custom_content", + ]); + this.snippetsByCategory = Object.fromEntries([...categories].map((c) => [c, []])); + this.contentCategories = {}; for (const snippetCategory of snippetsBody.querySelectorAll("snippets")) { const snippets = []; for (const snippetEl of snippetCategory.children) { @@ -188,9 +214,19 @@ export class SnippetModel extends Reactive { snippet.groupName = "custom"; snippet.isCustom = true; break; + case "snippet_content": + case "snippet_custom_content": + break; + default: + snippet.contentCategory = snippetCategory.id; + this.contentCategories[snippetCategory.id] = + snippetCategory.getAttribute("string"); } snippets.push(snippet); } + if (!categories.has(snippetCategory.id)) { + this.contentCategories[snippetCategory.id] = snippetCategory.getAttribute("string"); + } this.snippetsByCategory[snippetCategory.id] = snippets; } @@ -393,18 +429,11 @@ export class SnippetModel extends Reactive { } registry.category("services").add("html_builder.snippets", { - dependencies: ["orm", "dialog", "website"], + dependencies: ["orm", "dialog"], - start(env, { orm, dialog, website }) { - const services = { orm, dialog, website }; - const context = { - lang: website.currentWebsite?.metadata.lang, - user_lang: user.context.lang, - }; + start(env, { orm, dialog }) { + const services = { orm, dialog }; - return new SnippetModel(services, { - snippetsName: "website.snippets", - context, - }); + return new SnippetModelFactory(services); }, }); diff --git a/addons/html_builder/static/src/snippets/snippet_viewer.js b/addons/html_builder/static/src/snippets/snippet_viewer.js index 911f6729b4f1f..a7ac20338e4a4 100644 --- a/addons/html_builder/static/src/snippets/snippet_viewer.js +++ b/addons/html_builder/static/src/snippets/snippet_viewer.js @@ -26,9 +26,11 @@ export class SnippetViewer extends Component { this.dialog = useService("dialog"); this.content = useRef("content"); - this.websiteService = useService("website"); - this.innerWebsiteEditService = - this.websiteService.websiteRootInstance?.bindService("website_edit"); + if (this.props.snippetModel.snippetsName.includes("website")) { + this.websiteService = useService("website"); + this.innerWebsiteEditService = + this.websiteService.websiteRootInstance?.bindService("website_edit"); + } this.previousSearch = ""; const updatePreview = () => { diff --git a/addons/html_builder/static/tests/helpers.js b/addons/html_builder/static/tests/helpers.js index 66b5a3b8bab86..3b294875341fd 100644 --- a/addons/html_builder/static/tests/helpers.js +++ b/addons/html_builder/static/tests/helpers.js @@ -20,6 +20,7 @@ import { import { isBrowserFirefox } from "@web/core/browser/feature_detection"; import { registry } from "@web/core/registry"; import { uniqueId } from "@web/core/utils/functions"; +import { useService } from "@web/core/utils/hooks"; export function patchWithCleanupImg() { const defaultImg = @@ -96,6 +97,7 @@ class BuilderContainer extends Component { useSubEnv({ builderRef: useRef("container"), }); + this.snippetModel = useService("html_builder.snippets").makeSnippetModel(""); } onLoad() { @@ -105,7 +107,7 @@ class BuilderContainer extends Component { getBuilderProps() { return { closeEditor: () => {}, - snippetsName: "", + snippetModel: this.snippetModel, toggleMobile: () => { this.state.isMobile = !this.state.isMobile; }, diff --git a/addons/html_editor/static/src/core/overlay_plugin.js b/addons/html_editor/static/src/core/overlay_plugin.js index 017a6bcb354be..1f23d4e050e70 100644 --- a/addons/html_editor/static/src/core/overlay_plugin.js +++ b/addons/html_editor/static/src/core/overlay_plugin.js @@ -2,7 +2,7 @@ import { markRaw, EventBus } from "@odoo/owl"; import { Plugin } from "../plugin"; import { EditorOverlay } from "./overlay"; import { throttleForAnimation } from "@web/core/utils/timing"; -import { findUpTo } from "@html_editor/utils/dom_traversal"; +import { closestScrollableY } from "@web/core/utils/scrolling"; /** * @typedef { Object } OverlayShared @@ -56,14 +56,7 @@ export class OverlayPlugin extends Plugin { } getScrollContainer() { - const isScrollable = (element) => - element.scrollHeight > element.clientHeight && - ["auto", "scroll"].includes(getComputedStyle(element).overflowY); - - return ( - findUpTo(this.iframe || this.editable, null, isScrollable) || - this.topDocument.documentElement - ); + return closestScrollableY(this.iframe || this.editable) || this.topDocument.documentElement; } } diff --git a/addons/html_editor/static/src/editor.js b/addons/html_editor/static/src/editor.js index 9680134fdb229..0a27e85165593 100644 --- a/addons/html_editor/static/src/editor.js +++ b/addons/html_editor/static/src/editor.js @@ -25,7 +25,6 @@ import { fixInvalidHTML, initElementForEdition } from "./utils/sanitize"; * @property { boolean } [allowInlineAtRoot] * @property { string } [baseContainer] * @property { PluginConstructor[] } [Plugins] - * @property { boolean } [disableFloatingToolbar] * @property { string[] } [classList] * @property { Object } [localOverlayContainers] * @property { Object } [embeddedComponentInfo] @@ -231,7 +230,9 @@ export class Editor { getElContent() { const el = this.editable.cloneNode(true); + this.document.body.append(el); this.resources["clean_for_save_handlers"].forEach((cb) => cb({ root: el })); + el.remove(); return el; } diff --git a/addons/html_editor/static/src/fields/html_field.js b/addons/html_editor/static/src/fields/html_field.js index 1081327448f90..12ae622263932 100644 --- a/addons/html_editor/static/src/fields/html_field.js +++ b/addons/html_editor/static/src/fields/html_field.js @@ -189,7 +189,7 @@ export class HtmlField extends Component { async commitChanges({ urgent } = {}) { if (urgent) { - this._commitChanges({ urgent }); + return this._commitChanges({ urgent }); } else { return this.mutex.exec(() => this._commitChanges({ urgent })); } @@ -212,8 +212,7 @@ export class HtmlField extends Component { await this.commitChanges(); this.state.showCodeView = !this.state.showCodeView; if (!this.state.showCodeView && this.editor) { - this.editor.editable.innerHTML = this.value; - this.editor.shared.history.addStep(); + this.state.key++; } } @@ -228,7 +227,7 @@ export class HtmlField extends Component { ...(this.props.embeddedComponents ? EMBEDDED_COMPONENT_PLUGINS : []), ], classList: this.classList, - onChange: this.onChange.bind(this), + onChange: () => this.onChange(), collaboration: this.props.isCollaborative && { busService: this.busService, ormService: this.ormService, @@ -241,7 +240,7 @@ export class HtmlField extends Component { peerId: this.generateId(), }, dropImageAsAttachment: true, // @todo @phoenix always true ? - dynamicPlaceholder: this.dynamicPlaceholder, + dynamicPlaceholder: this.props.dynamicPlaceholder, dynamicPlaceholderResModel: this.props.record.data[this.props.dynamicPlaceholderModelReferenceField || "model"], direction: localization.direction || "ltr", @@ -258,8 +257,8 @@ export class HtmlField extends Component { } if (this.props.embeddedComponents) { - // TODO @engagement: fill this array with default/base components config.resources.embedded_components = [...MAIN_EMBEDDINGS]; + config.embeddedComponentInfo = { app: this.__owl__.app, env: this.env }; } const { sanitize_tags, sanitize } = this.props.record.fields[this.props.name]; diff --git a/addons/html_editor/static/src/main/toolbar/toolbar_plugin.js b/addons/html_editor/static/src/main/toolbar/toolbar_plugin.js index 8c13e6d800505..14b0caa1ec454 100644 --- a/addons/html_editor/static/src/main/toolbar/toolbar_plugin.js +++ b/addons/html_editor/static/src/main/toolbar/toolbar_plugin.js @@ -306,11 +306,9 @@ export class ToolbarPlugin extends Plugin { updateToolbar(selectionData = this.dependencies.selection.getSelectionData()) { this.updateNamespace(); - if (!this.config.disableFloatingToolbar) { - this.updateToolbarVisibility(selectionData); - if (!this.overlay.isOpen) { - return; - } + this.updateToolbarVisibility(selectionData); + if (!this.overlay.isOpen) { + return; } this.updateButtonsStates(selectionData.editableSelection); } diff --git a/addons/html_editor/static/src/wysiwyg.js b/addons/html_editor/static/src/wysiwyg.js index 73db0669beb59..5ab88209b3c3c 100644 --- a/addons/html_editor/static/src/wysiwyg.js +++ b/addons/html_editor/static/src/wysiwyg.js @@ -1,4 +1,4 @@ -import { Component, onMounted, onWillDestroy, useRef, useState, useSubEnv } from "@odoo/owl"; +import { Component, onMounted, onWillDestroy, useRef, useSubEnv } from "@odoo/owl"; import { Editor } from "./editor"; import { Toolbar } from "./main/toolbar/toolbar"; import { useChildRef, useSpellCheck } from "@web/core/utils/hooks"; @@ -30,7 +30,6 @@ export class Wysiwyg extends Component { class: { type: String, optional: true }, contentClass: { type: String, optional: true }, // on editable element style: { type: String, optional: true }, - toolbar: { type: Boolean, optional: true }, iframe: { type: Boolean, optional: true }, copyCss: { type: Boolean, optional: true }, onLoad: { type: Function, optional: true }, @@ -44,9 +43,6 @@ export class Wysiwyg extends Component { }; setup() { - this.state = useState({ - showToolbar: false, - }); this.overlayRef = useChildRef(); useSubEnv({ localOverlayContainerKey: uniqueId("wysiwyg"), @@ -61,9 +57,6 @@ export class Wysiwyg extends Component { }); onMounted(() => { - // now that component is mounted, editor is attached to el, and - // plugins are started, so we can allow the toolbar to be displayed - this.state.showToolbar = true; /** @type { any } **/ const el = contentRef.el; @@ -107,17 +100,10 @@ export class Wysiwyg extends Component { getEditorConfig() { return { ...this.props.config, - // TODO ABD TODO @phoenix: check if there is too much info in the wysiwyg env. - // i.e.: env has X because of parent component, - // embedded component descendant sometimes uses X from env which is set conditionally: - // -> it will override the one one from the parent => OK. - // -> it will not => the embedded component still has X in env because of its ancestors => Issue. - embeddedComponentInfo: { app: this.__owl__.app, env: this.env }, localOverlayContainers: { key: this.env.localOverlayContainerKey, ref: this.overlayRef, }, - disableFloatingToolbar: this.props.toolbar, }; } } diff --git a/addons/html_editor/static/src/wysiwyg.xml b/addons/html_editor/static/src/wysiwyg.xml index 9a346e30da18e..fa31266c2d678 100644 --- a/addons/html_editor/static/src/wysiwyg.xml +++ b/addons/html_editor/static/src/wysiwyg.xml @@ -1,9 +1,6 @@
- - -
diff --git a/addons/mail/static/src/views/web/fields/html_mail_field/html_mail_field.js b/addons/mail/static/src/views/web/fields/html_mail_field/html_mail_field.js index 8fbb88a8b9fd3..f635f8f72461d 100644 --- a/addons/mail/static/src/views/web/fields/html_mail_field/html_mail_field.js +++ b/addons/mail/static/src/views/web/fields/html_mail_field/html_mail_field.js @@ -3,29 +3,51 @@ import { registry } from "@web/core/registry"; import { getCSSRules, toInline } from "./convert_inline"; import { ColumnPlugin } from "@html_editor/main/column_plugin"; -const cssRulesByElement = new WeakMap(); +const cssRulesByDocument = new WeakMap(); export class HtmlMailField extends HtmlField { + setup() { + super.setup(); + this.alwaysComputeInlineEditorContent = true; + } + /** - * @param {WeakMap} cssRulesByElement - * @param {Editor} editor - * @param {HTMLElement} el + * @param {HTMLElement} el element to be processed + * @param {Document} styleDocument source document for the style */ - static async getInlinedEditorContent(cssRulesByElement, editor, el) { - if (!cssRulesByElement.has(editor.editable)) { - cssRulesByElement.set(editor.editable, getCSSRules(editor.document)); + static async getInlineHTML(el, styleDocument) { + if (!cssRulesByDocument.has(styleDocument)) { + cssRulesByDocument.set(styleDocument, getCSSRules(styleDocument)); } - const cssRules = cssRulesByElement.get(editor.editable); - // Insert the cloned element inside an DOM so we can get its computed style. - editor.editable.after(el); - el.classList.remove("odoo-editor-editable"); + const cssRules = cssRulesByDocument.get(styleDocument); await toInline(el, cssRules); - el.remove(); + return el; } async getEditorContent() { - const el = await super.getEditorContent(); - await HtmlMailField.getInlinedEditorContent(cssRulesByElement, this.editor, el); + if (this.alwaysComputeInlineEditorContent) { + return this.getInlineEditorContent(); + } + return super.getEditorContent(); + } + + /** + * Temporarily insert the cloned element inside the DOM so we can get its computed style. + * @param {HTMLElement} el + */ + insertForInlineProcessing(el) { + const editable = this.editor.editable; + editable.after(el); + } + + async getInlineEditorContent() { + let el = await super.getEditorContent(); + el.classList.remove("odoo-editor-editable"); + const editable = this.editor.editable; + const editableDocument = editable.ownerDocument; + this.insertForInlineProcessing(el); + el = await HtmlMailField.getInlineHTML(el, editableDocument); + el.remove(); return el; } diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss b/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss index a1f62d3886fe0..c69481b155b7a 100644 --- a/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss +++ b/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss @@ -28,6 +28,7 @@ body { flex-direction: column; } +// DEPRECATED (DEAD CODE) .o_mail_theme_selector { > a { height: $o-we-toolbar-height; @@ -68,6 +69,7 @@ body { } } +// TODO EGGMAIL: everything related to theme_selector should be removed .o_mail_theme_selector_new { display: block; font-size: 1rem; @@ -221,6 +223,7 @@ body { } } +// DEPRECATED (DEAD CODE) body.o_force_mail_theme_choice { #oe_snippets { width: 100%; diff --git a/addons/mass_mailing/static/src/xml/mass_mailing.xml b/addons/mass_mailing/static/src/xml/mass_mailing.xml index 70e94b4cdb80a..3b953224e33aa 100644 --- a/addons/mass_mailing/static/src/xml/mass_mailing.xml +++ b/addons/mass_mailing/static/src/xml/mass_mailing.xml @@ -1,5 +1,6 @@ +