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 || '


'; this.editable.innerHTML = value; @@ -2731,8 +2733,10 @@ export class OdooEditor extends EventTarget { * @param {boolean} [show] */ _updateToolbar(show) { - if (!this.options.toolbar) return; - if (!this.options.autohideToolbar && this.toolbar.style.visibility !== 'visible') { + if (!this.toolbar) { + return; + } + if (!this.autohideToolbar && this.toolbar.style.visibility !== 'visible') { this.toolbar.style.visibility = 'visible'; } @@ -2751,7 +2755,7 @@ export class OdooEditor extends EventTarget { } } } - if (this.options.autohideToolbar && !this.toolbar.contains(sel.anchorNode)) { + if (this.autohideToolbar && !this.toolbar.contains(sel.anchorNode)) { if (!this.isMobile) { if (show !== undefined) { this.toolbar.style.visibility = show ? 'visible' : 'hidden'; @@ -2883,13 +2887,13 @@ export class OdooEditor extends EventTarget { undoButton && undoButton.classList.toggle('disabled', !this.historyCanUndo()); const redoButton = this.toolbar.querySelector('#redo'); redoButton && redoButton.classList.toggle('disabled', !this.historyCanRedo()); - if (this.options.autohideToolbar && !this.isMobile && !this.toolbar.contains(sel.anchorNode)) { + if (this.autohideToolbar && !this.isMobile && !this.toolbar.contains(sel.anchorNode)) { this._positionToolbar(); } } updateToolbarPosition() { if ( - this.options.autohideToolbar && + this.autohideToolbar && !this.isMobile && getComputedStyle(this.toolbar).visibility === 'visible' ) { diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 36e2d8b26cf..7e4de61c2e3 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -1850,12 +1850,9 @@ var SnippetsMenu = Widget.extend({ this.$el = this.window.$(this.$el); this.$el.data('snippetMenu', this); - this.folded = !!this.options.foldSnippets; - this.customizePanel = document.createElement('div'); this.customizePanel.classList.add('o_we_customize_panel', 'd-none'); - // adds toolbar if not folded - this.setFolded(this.folded); + this._addToolbar(); this._checkEditorToolbarVisibilityCallback = this._checkEditorToolbarVisibility.bind(this); $(this.options.wysiwyg.odooEditor.document.body).on('click', this._checkEditorToolbarVisibilityCallback); @@ -2152,8 +2149,6 @@ var SnippetsMenu = Widget.extend({ this.el.classList.toggle('d-none', foldState); this.el.ownerDocument.body.classList.toggle('editor_has_snippets', !foldState); this.folded = !!foldState; - // "add" toolbar to set it inside the snippet menu/in the body - this._addToolbar(); }, /** * Get the editable area. @@ -4157,7 +4152,6 @@ var SnippetsMenu = Widget.extend({ }, _addToolbar(toolbarMode = "text") { if (this.folded) { - this._addToolbarToOriginalPosition(); return; } let titleText = _t("Inline Text"); @@ -4174,35 +4168,24 @@ var SnippetsMenu = Widget.extend({ } this.options.wysiwyg.toolbar.el.classList.remove('oe-floating'); - if (!this._$toolbarContainer) { - // Create toolbar custom container. - this._$toolbarContainer = $(''); - const $title = $("" + titleText + ""); - this._$toolbarContainer.append($title); - this._$toolbarContainer.append(this.options.wysiwyg.toolbar.$el); - $(this.customizePanel).append(this._$toolbarContainer); - // Create table-options custom container. - const $customizeTableBlock = $(QWeb.render('web_editor.toolbar.table-options')); - this.options.wysiwyg.odooEditor.bindExecCommand($customizeTableBlock[0]); - $(this.customizePanel).append($customizeTableBlock); - this._$removeFormatButton = this.options.wysiwyg.toolbar.$el.find('#removeFormat'); - $title.append(this._$removeFormatButton); - this._$toolbarContainer.append(this.options.wysiwyg.toolbar.$el); - } + // Create toolbar custom container. + this._$toolbarContainer = $(''); + const $title = $("" + titleText + ""); + this._$toolbarContainer.append($title); + this._$toolbarContainer.append(this.options.wysiwyg.toolbar.$el); + $(this.customizePanel).append(this._$toolbarContainer); + + // Create table-options custom container. + const $customizeTableBlock = $(QWeb.render('web_editor.toolbar.table-options')); + this.options.wysiwyg.odooEditor.bindExecCommand($customizeTableBlock[0]); + $(this.customizePanel).append($customizeTableBlock); + this._$removeFormatButton = this.options.wysiwyg.toolbar.$el.find('#removeFormat'); + $title.append(this._$removeFormatButton); + this._$toolbarContainer.append(this.options.wysiwyg.toolbar.$el); this._checkEditorToolbarVisibility(); }, - _addToolbarToOriginalPosition: function () { - const toolbar = this.options.wysiwyg.toolbar.el; - toolbar.classList.add('oe-floating'); - if (this.options.wysiwyg.odooEditor.isMobile) { - const editorEditable = this.options.wysiwyg.odooEditor.editable; - editorEditable.before(toolbar); - } else if (this.options.autohideToolbar) { - document.body.appendChild(toolbar); - } - }, /** * Update editor UI visibility based on the current range. */ diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js index 8cfc03f0a4a..320d9738bce 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js @@ -1516,7 +1516,7 @@ const Wysiwyg = Widget.extend({ break; } }; - if (!this.options.snippets) { + if (!options.snippets) { $toolbar.find('#justify, #media-insert').remove(); } $toolbar.find('#media-insert, #media-replace, #media-description').click(openTools); @@ -1874,7 +1874,7 @@ const Wysiwyg = Widget.extend({ ].join(',')).toggleClass('d-none', !isInMedia); // The image replace button is in the image options when the sidebar // exists. - if (this.snippetsMenu && $target.is('img')) { + if (this.snippetsMenu && !this.snippetsMenu.folded && $target.is('img')) { this.toolbar.$el.find('#media-replace').toggleClass('d-none', true); } // Only show the image-transform, image-crop and media-description @@ -1931,7 +1931,7 @@ const Wysiwyg = Widget.extend({ // Always hide the unlink button on media. this.toolbar.$el.find('#unlink').toggleClass('d-none', true); // Show the floatingtoolbar on the topleft of the media. - if (this.options.autohideToolbar) { + if (this.odooEditor.autohideToolbar && !this.odooEditor.isMobile) { const imagePosition = this.lastMediaClicked.getBoundingClientRect(); this.toolbar.$el.css({ visibility: 'visible', @@ -2461,7 +2461,7 @@ const Wysiwyg = Widget.extend({ } }, _onSelectionChange() { - if (this.options.autohideToolbar) { + if (this.odooEditor.autohideToolbar) { const isVisible = this.linkPopover && this.linkPopover.el.offsetParent; if (isVisible && !this.odooEditor.document.getSelection().isCollapsed) { this.linkPopover.hide();