diff --git a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js index 9af8053cbd1..6395eb479ec 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js @@ -46,11 +46,11 @@ export class MassMailingHtmlField extends HtmlField { return { ...super.wysiwygOptions, onIframeUpdated: () => this.onIframeUpdated(), - foldSnippets: device.isMobile, snippets: 'mass_mailing.email_designer_snippets', resizable: false, defaultDataForLinkTools: { isNewWindow: true }, - toolbarTemplate: device.isMobile ? 'web_editor.toolbar' : 'mass_mailing.web_editor_toolbar', + toolbarTemplate: 'mass_mailing.web_editor_toolbar', + onWysiwygBlur: () => this.wysiwyg.odooEditor.toolbarHide(), ...this.props.wysiwygOptions, }; } @@ -337,7 +337,7 @@ export class MassMailingHtmlField extends HtmlField { if (editableAreaIsEmpty) { // unfold to prevent toolbar from going over the menu - this.wysiwyg.snippetsMenu.setFolded(false); + this.wysiwyg.setSnippetsMenuFolded(false); $themeSelectorNew.appendTo(this.wysiwyg.$iframeBody); } @@ -353,9 +353,8 @@ export class MassMailingHtmlField extends HtmlField { this.wysiwyg.$iframeBody.closest('body').removeClass("o_force_mail_theme_choice"); $themeSelectorNew.remove(); - if (device.isMobile) { - this.wysiwyg.snippetsMenu.setFolded(true); - } + + this.wysiwyg.setSnippetsMenuFolded(device.isMobile || themeName === 'basic'); this._switchImages(themeParams, $snippets); @@ -424,6 +423,8 @@ export class MassMailingHtmlField extends HtmlField { selectedTheme = this._getSelectedTheme(themesParams); } + this.wysiwyg.setSnippetsMenuFolded(device.isMobile || (selectedTheme && selectedTheme.name === 'basic')); + this.wysiwyg.$iframeBody.find('.iframe-utils-zone').removeClass('d-none'); if (this.env.mailingFilterTemplates && this.wysiwyg) { this._hideIrrelevantTemplates(); diff --git a/addons/mass_mailing/static/src/js/wysiwyg.js b/addons/mass_mailing/static/src/js/wysiwyg.js index d95542583a1..5cc4d285411 100644 --- a/addons/mass_mailing/static/src/js/wysiwyg.js +++ b/addons/mass_mailing/static/src/js/wysiwyg.js @@ -3,6 +3,7 @@ import Wysiwyg from "web_editor.wysiwyg"; import MassMailingSnippetsMenu from "mass_mailing.snippets.editor"; import {closestElement} from "@web_editor/js/editor/odoo-editor/src/OdooEditor"; +import Toolbar from "web_editor.toolbar"; const MassMailingWysiwyg = Wysiwyg.extend({ //-------------------------------------------------------------------------- @@ -17,15 +18,72 @@ const MassMailingWysiwyg = Wysiwyg.extend({ e.preventDefault(); } }); + this.snippetsMenuToolbar = this.toolbar; return res; }, toggleLinkTools(options = {}) { this._super({ ...options, - // Always open the dialog for the basic theme as it has no sidebar. - forceDialog: options.forceDialog || this.$iframeBody.find('.o_basic_theme').length + // Always open the dialog when the sidebar is folded. + forceDialog: options.forceDialog || this.snippetsMenu.folded }); + if (this.snippetsMenu.folded) { + // Hide toolbar and avoid it being re-displayed after getDeepRange. + this.odooEditor.document.getSelection().collapseToEnd(); + } + }, + + /** + * Sets SnippetsMenu fold state and switches toolbar. + * Instantiates a new floating Toolbar if needed. + * + * @param {Boolean} fold + */ + setSnippetsMenuFolded: async function (fold = true) { + if (fold) { + this.snippetsMenu.setFolded(true); + if (!this.floatingToolbar) { + // Instantiate and configure new toolbar. + this.floatingToolbar = new Toolbar(this, 'web_editor.toolbar'); + this.toolbar = this.floatingToolbar; + await this.toolbar.appendTo(document.createElement('void')); + this._configureToolbar({ snippets: false }); + this._updateEditorUI(); + this.setCSSVariables(this.toolbar.el); + this.odooEditor.setupToolbar(this.toolbar.el); + if (this.odooEditor.isMobile) { + document.body.querySelector('.o_mail_body').prepend(this.toolbar.el); + } else { + document.body.append(this.toolbar.el); + } + } else { + this.toolbar = this.floatingToolbar; + } + this.toolbar.el.classList.remove('d-none'); + this.odooEditor.autohideToolbar = true; + this.odooEditor.toolbarHide(); + } else { + this.snippetsMenu.setFolded(false); + this.toolbar = this.snippetsMenuToolbar; + this.odooEditor.autohideToolbar = false; + if (this.floatingToolbar) { + this.floatingToolbar.el.classList.add('d-none'); + } + } + this.odooEditor.toolbar = this.toolbar.el; + }, + + /** + * @override + */ + openMediaDialog: function() { + this._super(...arguments); + // Opening the dialog in the outer document does not trigger the selectionChange + // (that would normally hide the toolbar) in the iframe. + if (this.snippetsMenu.folded) { + this.odooEditor.toolbarHide(); + } }, //-------------------------------------------------------------------------- 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 066439d3ba9..f91189c74f9 100644 --- a/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss +++ b/addons/mass_mailing/static/src/scss/mass_mailing.ui.scss @@ -302,10 +302,6 @@ body.editor_enable.o_basic_theme.o_in_iframe { #web_editor-top-edit .note-popover .popover { right: 0 !important; } - - #oe_snippets { - display: none; - } } .note-editable .o_layout { diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js index 1d6cc50cd42..8a7d6d04d2b 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js @@ -627,30 +627,7 @@ export class OdooEditor extends EventTarget { // ------- if (this.options.toolbar) { - this.toolbar = this.options.toolbar; - this.bindExecCommand(this.toolbar); - // Ensure anchors in the toolbar don't trigger a hash change. - const toolbarAnchors = this.toolbar.querySelectorAll('a'); - toolbarAnchors.forEach(a => a.addEventListener('click', e => e.preventDefault())); - for (const colorLabel of this.toolbar.querySelectorAll('label')) { - colorLabel.addEventListener('mousedown', ev => { - // Hack to prevent loss of focus (done by preventDefault) while still opening - // color picker dialog (which is also prevented by preventDefault on chrome, - // except when click detail is 2, which happens on a double-click but isn't - // triggered by a dblclick event) - if (ev.detail < 2) { - ev.preventDefault(); - ev.currentTarget.dispatchEvent(new MouseEvent('click', { detail: 2 })); - } - }); - colorLabel.addEventListener('input', ev => { - this.document.execCommand(ev.target.name, false, ev.target.value); - this.updateColorpickerLabels(); - }); - } - if (this.isMobile) { - this.editable.before(this.toolbar); - } + this.setupToolbar(this.options.toolbar); } // placeholder hint if (editable.textContent === '' && this.options.placeholder) { @@ -677,6 +654,31 @@ export class OdooEditor extends EventTarget { this._columnUi.remove(); } + setupToolbar(toolbar) { + this.toolbar = toolbar; + this.autohideToolbar = this.options.autohideToolbar; + this.bindExecCommand(this.toolbar); + // Ensure anchors in the toolbar don't trigger a hash change. + const toolbarAnchors = this.toolbar.querySelectorAll('a'); + toolbarAnchors.forEach(a => a.addEventListener('click', e => e.preventDefault())); + for (const colorLabel of this.toolbar.querySelectorAll('label')) { + colorLabel.addEventListener('mousedown', ev => { + // Hack to prevent loss of focus (done by preventDefault) while still opening + // color picker dialog (which is also prevented by preventDefault on chrome, + // except when click detail is 2, which happens on a double-click but isn't + // triggered by a dblclick event) + if (ev.detail < 2) { + ev.preventDefault(); + ev.currentTarget.dispatchEvent(new MouseEvent('click', { detail: 2 })); + } + }); + colorLabel.addEventListener('input', ev => { + this.document.execCommand(ev.target.name, false, ev.target.value); + this.updateColorpickerLabels(); + }); + } + } + resetContent(value) { value = value || '