[ADD] mass_mailing, web_editor: add editor message on empty mass_mailing

When choosing the empty template for mass_mailing, it was confusing to
face a blank page with a tiny dropzone. With this we adopt the website
builder's solution, which is to include a message when the page is
empty. That message functions as a bigger dropzone.

task-2734469

closes odoo/odoo#84356

X-original-commit: fbe048c202ff7878984adf26bef0651a45851f0a
Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
This commit is contained in:
Antoine Guenet
2022-02-10 15:42:56 +00:00
parent 03ffa1d1c7
commit 533bf1ee1e
8 changed files with 184 additions and 9 deletions
+4
View File
@@ -97,6 +97,10 @@
'web/static/lib/bootstrap/scss/_variables.scss',
'mass_mailing/static/src/scss/mass_mailing.ui.scss',
],
'web_editor.assets_wysiwyg': [
'mass_mailing/static/src/js/snippets.editor.js',
'mass_mailing/static/src/js/wysiwyg.js',
],
'web.assets_common': [
'mass_mailing/static/src/js/tours/**/*',
],
@@ -72,7 +72,10 @@ var MassMailingFieldHtml = FieldHtml.extend({
self._isDirty = self.wysiwyg.isDirty();
self._doAction();
const $editorEnable = $editable.closest('.editor_enable');
$editorEnable.removeClass('editor_enable');
convertInline.toInline($editable, self.cssRules, self.wysiwyg.$iframe);
$editorEnable.addClass('editor_enable');
self.trigger_up('field_changed', {
dataPointID: self.dataPointID,
@@ -105,6 +108,18 @@ var MassMailingFieldHtml = FieldHtml.extend({
// Private
//--------------------------------------------------------------------------
/**
* Adds automatic editor messages on drag&drop zone elements.
*
* @private
*/
_addEditorMessages: function () {
const $editable = this.wysiwyg.getEditable().find('.o_editable');
this.$editorMessageElements = $editable
.not('[data-editor-message]')
.attr('data-editor-message', _t('DRAG BUILDING BLOCKS HERE'));
$editable.filter(':empty').attr('contenteditable', false);
},
/**
* @override
*/
@@ -304,7 +319,9 @@ var MassMailingFieldHtml = FieldHtml.extend({
const options = this._super.apply(this, arguments);
options.resizable = false;
options.defaultDataForLinkTools = { isNewWindow: true };
if (!this._wysiwygSnippetsActive) {
if (this._wysiwygSnippetsActive) {
options.wysiwygAlias = 'mass_mailing.wysiwyg';
} else {
delete options.snippets;
}
return options;
@@ -508,6 +525,7 @@ var MassMailingFieldHtml = FieldHtml.extend({
}
selectTheme(e);
this._addEditorMessages();
// Wait the next tick because some mutation have to be processed by
// the Odoo editor before resetting the history.
setTimeout(() => {
@@ -0,0 +1,95 @@
odoo.define('mass_mailing.snippets.editor', function (require) {
'use strict';
const snippetsEditor = require('web_editor.snippet.editor');
const MassMailingSnippetsMenu = snippetsEditor.SnippetsMenu.extend({
custom_events: _.extend({}, snippetsEditor.SnippetsMenu.prototype.custom_events, {
drop_zone_over: '_onDropZoneOver',
drop_zone_out: '_onDropZoneOut',
drop_zone_start: '_onDropZoneStart',
drop_zone_stop: '_onDropZoneStop',
}),
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* @override
*/
start: function () {
return this._super(...arguments).then(() => {
this.$editable = this.options.wysiwyg.getEditable();
});
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @override
*/
_insertDropzone: function ($hook) {
const $hookParent = $hook.parent();
const $dropzone = this._super(...arguments);
$dropzone.attr('data-editor-message', $hookParent.attr('data-editor-message'));
$dropzone.attr('data-editor-sub-message', $hookParent.attr('data-editor-sub-message'));
return $dropzone;
},
//--------------------------------------------------------------------------
// Handler
//--------------------------------------------------------------------------
/**
* @override
*/
_onDropZoneOver: function () {
this.$editable.find('.o_editable').css('background-color', '');
},
/**
* @override
*/
_onDropZoneOut: function () {
const $oEditable = this.$editable.find('.o_editable');
if ($oEditable.find('.oe_drop_zone.oe_insert:not(.oe_vertical):only-child').length) {
$oEditable[0].style.setProperty('background-color', 'transparent', 'important');
}
},
/**
* @override
*/
_onDropZoneStart: function () {
const $oEditable = this.$editable.find('.o_editable');
if ($oEditable.find('.oe_drop_zone.oe_insert:not(.oe_vertical):only-child').length) {
$oEditable[0].style.setProperty('background-color', 'transparent', 'important');
}
},
/**
* @override
*/
_onDropZoneStop: function () {
const $oEditable = this.$editable.find('.o_editable');
$oEditable.css('background-color', '');
if (!$oEditable.find('.oe_drop_zone.oe_insert:not(.oe_vertical):only-child').length) {
$oEditable.attr('contenteditable', true);
}
},
/**
* @override
*/
_onSnippetRemoved: function () {
this._super(...arguments);
const $oEditable = this.$editable.find('.o_editable');
if (!$oEditable.children().length) {
$oEditable.empty(); // remove any superfluous whitespace
$oEditable.attr('contenteditable', false);
}
},
});
return MassMailingSnippetsMenu;
});
@@ -0,0 +1,21 @@
odoo.define('mass_mailing.wysiwyg', function (require) {
'use strict';
var Wysiwyg = require('web_editor.wysiwyg');
var MassMailingSnippetsMenu = require('mass_mailing.snippets.editor');
const MassMailingWysiwyg = Wysiwyg.extend({
/**
* @override
*/
_createSnippetsMenuInstance: function (options={}) {
return new MassMailingSnippetsMenu(this, Object.assign({
wysiwyg: this,
selectorEditableArea: '.o_editable',
}, options));
},
});
return MassMailingWysiwyg;
});
@@ -273,6 +273,29 @@ body.editor_enable.o_basic_theme.o_in_iframe {
font-size: 14px;
}
.editor_enable .o_mass_mailing_iframe {
.o_editable:empty, .o_editable > .oe_drop_zone.oe_insert:not(.oe_vertical):only-child {
background-color: white;
border: 2px dashed #999999;
padding: 112px 0px;
text-align: center !important;
color: #999999;
margin: 5px;
height: auto;
&:before {
content: attr(data-editor-message);
display: block;
font-size: 20px;
line-height: 50px; // Useful for the "wizz" animation on snippet click to be more visible
}
&:after {
content: attr(data-editor-sub-message);
display: block;
}
}
}
.o_mass_mailing_iframe {
//this will display the email body without padding
@@ -284,5 +307,4 @@ body.editor_enable.o_basic_theme.o_in_iframe {
.iframe-editor-wrapper {
margin-top: -2px;
}
}
@@ -1317,6 +1317,7 @@ var SnippetsMenu = Widget.extend({
if ($oeStructure.length && !$oeStructure.children().length && this.$snippets) {
// If empty oe_structure, encourage using snippets in there by
// making them "wizz" in the panel.
this._updateRightPanelContent({content: [], tab: this.tabs.BLOCKS});
this.$snippets.odooBounce();
return;
}
@@ -2474,6 +2475,7 @@ var SnippetsMenu = Widget.extend({
dropped = true;
$(this).first().after($toInsert).addClass('invisible');
$toInsert.removeClass('oe_snippet_body');
self.trigger_up('drop_zone_over');
},
out: function () {
var prev = $toInsert.prev();
@@ -2483,6 +2485,7 @@ var SnippetsMenu = Widget.extend({
$(this).removeClass('invisible');
$toInsert.addClass('oe_snippet_body');
}
self.trigger_up('drop_zone_out');
},
});
@@ -2497,6 +2500,7 @@ var SnippetsMenu = Widget.extend({
self.draggableComponent.$scrollTarget.on('scroll.scrolling_element', function () {
self.$el.trigger('scroll');
});
self.trigger_up('drop_zone_start');
},
stop: async function (ev, ui) {
const doc = self.options.wysiwyg.odooEditor.document;
@@ -2577,6 +2581,7 @@ var SnippetsMenu = Widget.extend({
}
self.$el.find('.oe_snippet_thumbnail').removeClass('o_we_already_dragging');
}
self.trigger_up('drop_zone_stop');
},
},
});
@@ -19,14 +19,15 @@ exports.loadWysiwyg = loadWysiwyg;
* @param {object} options The wysiwyg options
*/
exports.createWysiwyg = async (parent, options, additionnalAssets = []) => {
const wysiwygAlias = options.wysiwygAlias || 'web_editor.wysiwyg';
if (!wysiwygPromise) {
wysiwygPromise = new Promise(async (resolve) => {
await loadWysiwyg(additionnalAssets);
// Wait the loading of the service and his dependencies (use string to
// avoid parsing of require function).
const stringFunction = `return new Promise(resolve => {
odoo.define('web_editor.wysiwig.loaded', require => {
` + 'require' + `('web_editor.wysiwyg');
odoo.define('${wysiwygAlias}.loaded', require => {
` + 'require' + `('${wysiwygAlias}');
resolve();
});
});`;
@@ -35,7 +36,7 @@ exports.createWysiwyg = async (parent, options, additionnalAssets = []) => {
});
}
await wysiwygPromise;
const Wysiwyg = odoo.__DEBUG__.services['web_editor.wysiwyg'];
const Wysiwyg = odoo.__DEBUG__.services[wysiwygAlias];
return new Wysiwyg(parent, options);
};
@@ -216,10 +216,7 @@ const Wysiwyg = Widget.extend({
if (options.snippets) {
$(this.odooEditor.document.body).addClass('editor_enable');
this.snippetsMenu = new snippetsEditor.SnippetsMenu(this, Object.assign({
wysiwyg: this,
selectorEditableArea: '.o_editable',
}, options));
this.snippetsMenu = this._createSnippetsMenuInstance(options);
await this._insertSnippetMenu();
this._onBeforeUnload = (event) => {
@@ -1055,6 +1052,18 @@ const Wysiwyg = Widget.extend({
// Private
//--------------------------------------------------------------------------
/**
* Returns an instance of the snippets menu.
*
* @param {Object} [options]
* @returns {widget}
*/
_createSnippetsMenuInstance: function (options={}) {
return new snippetsEditor.SnippetsMenu(this, Object.assign({
wysiwyg: this,
selectorEditableArea: '.o_editable',
}, options));
},
_configureToolbar: function (options) {
const $toolbar = this.toolbar.$el;
$toolbar.on('mousedown', e => e.preventDefault());