[IMP] web_editor, website: add callback to editor clone and remove

We recently transitioned to async option methods, this allows us to have
better control on the order of execution in the edition workflow, and
among other things, keep the editor's UI in sync with the current state
of the DOM.

However, some snippet options (such as the number of columns option)
need to remove or clone portions of the DOM, and as such, trigger the
onClone or onRemove methods of the snippets contained in that portion if
applicable. Because these methods are defined on other options, we first
need to make sure all the snippetEditors for this portion are created,
which is asynchronous.

This commit adds support for an onSuccess callback to the
'clone_snippet' and 'remove_snippet' event handlers, such that options
can choose to be notified when the work has been done, allowing them to
wait for it before proceeding.

This commit also adapts the layout_column snippet option to make use of
that new feature, and remove code that was used as a workaround before,
ie: precomputing the expected number of columns after the clone/remove,
and using that number to adjust the width of the columns and update the
UI. We can now just wait for the clone/remove, and use the real number
of columns currently in the DOM.

closes odoo/odoo#45343

X-original-commit: c159d58ce9cfca4a44f159b18eb10d1138977e23
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Samuel Degueldre
2020-02-13 18:02:48 +00:00
parent 05e13f456c
commit 416500f6e3
2 changed files with 40 additions and 34 deletions
@@ -386,7 +386,7 @@ var SnippetEditor = Widget.extend({
* @private
* @param {boolean} recordUndo
*/
clone: function (recordUndo) {
clone: async function (recordUndo) {
this.trigger_up('snippet_will_be_cloned', {$target: this.$target});
var $clone = this.$target.clone(false);
@@ -396,15 +396,18 @@ var SnippetEditor = Widget.extend({
}
this.$target.after($clone);
this.trigger_up('call_for_each_child_snippet', {
$snippet: $clone,
callback: function (editor, $snippet) {
for (var i in editor.styles) {
editor.styles[i].onClone({
isCurrent: ($snippet.is($clone)),
});
}
},
await new Promise(resolve => {
this.trigger_up('call_for_each_child_snippet', {
$snippet: $clone,
callback: function (editor, $snippet) {
for (var i in editor.styles) {
editor.styles[i].onClone({
isCurrent: ($snippet.is($clone)),
});
}
resolve();
},
});
});
this.trigger_up('snippet_cloned', {$target: $clone, $origin: this.$target});
@@ -1925,11 +1928,13 @@ var SnippetsMenu = Widget.extend({
* @private
* @param {OdooEvent} ev
*/
_onCloneSnippet: function (ev) {
_onCloneSnippet: async function (ev) {
ev.stopPropagation();
this._createSnippetEditor(ev.data.$snippet).then(editor => {
editor.clone();
});
const editor = await this._createSnippetEditor(ev.data.$snippet);
await editor.clone();
if (ev.data.onSuccess) {
ev.data.onSuccess();
}
},
/**
* Called when a child editor asks to deactivate the current snippet
@@ -2103,11 +2108,13 @@ var SnippetsMenu = Widget.extend({
* @private
* @param {OdooEvent} ev
*/
_onRemoveSnippet: function (ev) {
_onRemoveSnippet: async function (ev) {
ev.stopPropagation();
this._createSnippetEditor(ev.data.$snippet).then(function (editor) {
editor.removeSnippet();
});
const editor = await this._createSnippetEditor(ev.data.$snippet);
await editor.removeSnippet();
if (ev.data.onSuccess) {
ev.data.onSuccess();
}
},
/**
* Saving will destroy all editors since they need to clean their DOM.
@@ -1027,9 +1027,9 @@ options.registry.layout_column = options.Class.extend({
*
* @see this.selectClass for parameters
*/
selectCount: function (previewMode, widgetValue, params) {
selectCount: async function (previewMode, widgetValue, params) {
const nbColumns = parseInt(widgetValue);
this._updateColumnCount(nbColumns - this.$target.children().length);
await this._updateColumnCount(nbColumns - this.$target.children().length);
},
//--------------------------------------------------------------------------
@@ -1041,7 +1041,7 @@ options.registry.layout_column = options.Class.extend({
*/
_computeWidgetState: function (methodName, params) {
if (methodName === 'selectCount') {
return '' + (this.colsLength || this.$target.children().length);
return '' + this.$target.children().length;
}
return this._super(...arguments);
},
@@ -1052,38 +1052,40 @@ options.registry.layout_column = options.Class.extend({
* @private
* @param {integer} count - positif to add, negative to remove
*/
_updateColumnCount: function (count) {
_updateColumnCount: async function (count) {
if (!count) {
return;
}
this.trigger_up('request_history_undo_record', {$target: this.$target});
var colsLength = this.$target.children().length + count;
if (count > 0) {
var $lastColumn = this.$target.children().last();
for (var i = 0; i < count; i++) {
this.trigger_up('clone_snippet', {$snippet: $lastColumn});
await new Promise(resolve => {
this.trigger_up('clone_snippet', {$snippet: $lastColumn, onSuccess: resolve});
});
}
} else {
var self = this;
_.each(this.$target.children().slice(count), function (el) {
self.trigger_up('remove_snippet', {$snippet: $(el)});
});
for (const el of this.$target.children().slice(count)) {
await new Promise(resolve => {
self.trigger_up('remove_snippet', {$snippet: $(el), onSuccess: resolve});
});
}
}
this._resizeColumns(colsLength);
this._resizeColumns();
this.trigger_up('cover_update');
},
/**
* Resizes the columns so that they are kept on one row.
*
* @private
* @param {number} [colsLength] (default to the actual number of columns)
*/
_resizeColumns: function (colsLength) {
_resizeColumns: function () {
var $columns = this.$target.children();
colsLength = colsLength || $columns.length;
const colsLength = $columns.length;
var colSize = Math.floor(12 / colsLength) || 1;
var colOffset = Math.floor((12 - colSize * colsLength) / 2);
var colClass = 'col-lg-' + colSize;
@@ -1095,9 +1097,6 @@ options.registry.layout_column = options.Class.extend({
if (colOffset) {
$columns.first().addClass('offset-lg-' + colOffset);
}
// TODO: remove in master. This is used to keep the UI in sync, but
// won't be needed once option methods are properly asynchronous.
this.colsLength = colsLength;
},
});