[ADD] web_editor, mass_mailing: add a "Design" tab to mass_mailing
This adds a tab to mass_mailing, that will be used to style the mailing globally. task-2789287 Part-of: odoo/odoo#86881
This commit is contained in:
@@ -1,15 +1,25 @@
|
||||
odoo.define('mass_mailing.snippets.editor', function (require) {
|
||||
'use strict';
|
||||
|
||||
const {_lt} = require('web.core');
|
||||
const snippetsEditor = require('web_editor.snippet.editor');
|
||||
|
||||
const MassMailingSnippetsMenu = snippetsEditor.SnippetsMenu.extend({
|
||||
events: _.extend({}, snippetsEditor.SnippetsMenu.prototype.events, {
|
||||
'click .o_we_customize_design_btn': '_onDesignTabClick',
|
||||
}),
|
||||
custom_events: _.extend({}, snippetsEditor.SnippetsMenu.prototype.custom_events, {
|
||||
drop_zone_over: '_onDropZoneOver',
|
||||
drop_zone_out: '_onDropZoneOut',
|
||||
drop_zone_start: '_onDropZoneStart',
|
||||
drop_zone_stop: '_onDropZoneStop',
|
||||
}),
|
||||
tabs: _.extend({}, snippetsEditor.SnippetsMenu.prototype.tabs, {
|
||||
DESIGN: 'design',
|
||||
}),
|
||||
optionsTabStructure: [
|
||||
['design-options', _lt("Design Options")],
|
||||
],
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
@@ -38,6 +48,13 @@ const MassMailingSnippetsMenu = snippetsEditor.SnippetsMenu.extend({
|
||||
$dropzone.attr('data-editor-sub-message', $hookParent.attr('data-editor-sub-message'));
|
||||
return $dropzone;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateRightPanelContent: function ({content, tab}) {
|
||||
this._super(...arguments);
|
||||
this.$('.o_we_customize_design_btn').toggleClass('active', tab === this.tabs.DESIGN);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handler
|
||||
@@ -88,6 +105,62 @@ const MassMailingSnippetsMenu = snippetsEditor.SnippetsMenu.extend({
|
||||
$oEditable.attr('contenteditable', false);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async _onDesignTabClick() {
|
||||
// Note: nothing async here but start the loading effect asap
|
||||
let releaseLoader;
|
||||
try {
|
||||
const promise = new Promise(resolve => releaseLoader = resolve);
|
||||
this._execWithLoadingEffect(() => promise, false, 0);
|
||||
// loader is added to the DOM synchronously
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
// ensure loader is rendered: first call asks for the (already done) DOM update,
|
||||
// second call happens only after rendering the first "updates"
|
||||
|
||||
if (!this.topFakeOptionEl) {
|
||||
let el;
|
||||
for (const [elementName, title] of this.optionsTabStructure) {
|
||||
const newEl = document.createElement(elementName);
|
||||
newEl.dataset.name = title;
|
||||
if (el) {
|
||||
el.appendChild(newEl);
|
||||
} else {
|
||||
this.topFakeOptionEl = newEl;
|
||||
}
|
||||
el = newEl;
|
||||
}
|
||||
this.bottomFakeOptionEl = el;
|
||||
this.el.appendChild(this.topFakeOptionEl);
|
||||
}
|
||||
|
||||
// Need all of this in that order so that:
|
||||
// - the element is visible and can be enabled and the onFocus method is
|
||||
// called each time.
|
||||
// - the element is hidden afterwards so it does not take space in the
|
||||
// DOM, same as the overlay which may make a scrollbar appear.
|
||||
this.topFakeOptionEl.classList.remove('d-none');
|
||||
const editorPromise = this._activateSnippet($(this.bottomFakeOptionEl));
|
||||
releaseLoader(); // because _activateSnippet uses the same mutex as the loader
|
||||
releaseLoader = undefined;
|
||||
const editor = await editorPromise;
|
||||
this.topFakeOptionEl.classList.add('d-none');
|
||||
editor.toggleOverlay(false);
|
||||
|
||||
this._updateRightPanelContent({
|
||||
tab: this.tabs.DESIGN,
|
||||
});
|
||||
} catch (e) {
|
||||
// Normally the loading effect is removed in case of error during the action but here
|
||||
// the actual activity is happening outside of the action, the effect must therefore
|
||||
// be cleared in case of error as well
|
||||
if (releaseLoader) {
|
||||
releaseLoader();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return MassMailingSnippetsMenu;
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='snippets_menu']" position="inside">
|
||||
<button type="button" class="o_we_select_template text-uppercase"><span>Select a template</span></button>
|
||||
<button type="button" tabindex="3" class="o_we_customize_design_btn text-uppercase" accesskey="2">
|
||||
<span>Design</span>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//t[@id='default_snippets']" position="replace">
|
||||
<t id="default_snippets">
|
||||
@@ -532,5 +534,9 @@
|
||||
data-img="/mass_mailing/static/src/img/snippets_options/align_stretch.svg"/>
|
||||
</we-button-group>
|
||||
</div>
|
||||
|
||||
<!-- DESIGN OPTIONS -->
|
||||
<div data-js="DesignTab" data-selector="design-options" data-no-check="true">
|
||||
</div>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -1545,6 +1545,20 @@ body.editor_enable.editor_has_snippets {
|
||||
&.o_we_full_row > div {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&.o_short_title we-title, .o_short_title we-title {
|
||||
width: unset !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
&.o_long_title we-title, .o_long_title we-title {
|
||||
width: fit-content !important;
|
||||
padding-right: 10px !important;
|
||||
}
|
||||
&.o_design_tab_title {
|
||||
we-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%o-we-inline {
|
||||
|
||||
Reference in New Issue
Block a user