[IMP] web_editor, *: automatically update snippet option UI on focus
* website, website_blog, website_sale Some snippet option already needed to implement this by themself and now that they use custom widget, this is even more needed. The long-term goal is that developers should not have to override the onFocus method or the updateUI method but only the _computeWidgetState method. Part of https://github.com/odoo/odoo/pull/40282 In preparation of task-2122935 and other tasks
This commit is contained in:
@@ -319,6 +319,7 @@ var SnippetEditor = Widget.extend({
|
||||
var styles = _.values(editor.styles);
|
||||
_.sortBy(styles, '__order').forEach(style => {
|
||||
if (show) {
|
||||
style.updateUI();
|
||||
style.onFocus();
|
||||
} else {
|
||||
style.onBlur();
|
||||
|
||||
@@ -828,7 +828,7 @@ const MultiUserValueWidget = UserValueWidget.extend({
|
||||
},
|
||||
});
|
||||
|
||||
const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ // FIXME should be reloaded on focus
|
||||
const ColorpickerUserValueWidget = SelectUserValueWidget.extend({
|
||||
custom_events: {
|
||||
'color_picked': '_onColorPicked',
|
||||
'color_hover': '_onColorHovered',
|
||||
@@ -1053,16 +1053,6 @@ const SnippetOptionWidget = Widget.extend({
|
||||
this.el.appendChild(this.uiFragment);
|
||||
this.uiFragment = null;
|
||||
},
|
||||
/**
|
||||
* Called when the option is initialized (i.e. the parent edition overlay is
|
||||
* shown for the first time).
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
start: async function () {
|
||||
await this._updateUI();
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* Called when the parent edition overlay is covering the associated snippet
|
||||
* (the first time, this follows the call to the @see start method).
|
||||
@@ -1308,9 +1298,38 @@ const SnippetOptionWidget = Widget.extend({
|
||||
*/
|
||||
setTarget: async function ($target) {
|
||||
this.$target = $target;
|
||||
await this._updateUI();
|
||||
await this.updateUI();
|
||||
this.$target.trigger('snippet-option-change', [this]);
|
||||
},
|
||||
/**
|
||||
* Updates the UI. For widget update, @see _computeWidgetState.
|
||||
*
|
||||
* @param {function} [callback] - used to filter widgets to update
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateUI: async function (callback) {
|
||||
// For each widget, for each of their option method, notify to the
|
||||
// widget the current value they should hold according to the $target's
|
||||
// current state, related for that method.
|
||||
const proms = this._userValueWidgets.map(async widget => {
|
||||
if (callback && !callback(widget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const methodsNames = widget.getMethodsNames();
|
||||
const proms = methodsNames.map(async methodName => {
|
||||
const value = await this._computeWidgetState(methodName, widget.getMethodsParams(methodName));
|
||||
const normalizedValue = this._normalizeWidgetValue(value);
|
||||
widget.setValue(normalizedValue, methodName);
|
||||
});
|
||||
await Promise.all(proms);
|
||||
|
||||
// Refresh the UI of all widgets (after all the current values they
|
||||
// hold have been updated).
|
||||
return widget.updateUI();
|
||||
});
|
||||
return Promise.all(proms);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -1467,7 +1486,6 @@ const SnippetOptionWidget = Widget.extend({
|
||||
|
||||
return uiFragment;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {HTMLElement} parentEl
|
||||
@@ -1513,7 +1531,7 @@ const SnippetOptionWidget = Widget.extend({
|
||||
|
||||
return this._renderOriginalXML($xml).then(uiFragment => {
|
||||
this.$el.append(uiFragment);
|
||||
return this._updateUI();
|
||||
return this.updateUI();
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -1544,42 +1562,13 @@ const SnippetOptionWidget = Widget.extend({
|
||||
});
|
||||
|
||||
if (!previewMode) {
|
||||
await this._updateUI(w => !w.isPreviewed() || w === widget);
|
||||
await this.updateUI(w => !w.isPreviewed() || w === widget);
|
||||
} else {
|
||||
await this._updateUI(w => w !== widget && !w.isPreviewed());
|
||||
await this.updateUI(w => w !== widget && !w.isPreviewed());
|
||||
}
|
||||
|
||||
this.$target.trigger('content_changed');
|
||||
},
|
||||
/**
|
||||
* Updates the UI. For widget update, @see _computeWidgetState.
|
||||
*
|
||||
* @param {function} [callback] - used to filter widgets to update
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_updateUI: async function (callback) {
|
||||
// For each widget, for each of their option method, notify to the
|
||||
// widget the current value they should hold according to the $target's
|
||||
// current state, related for that method.
|
||||
const proms = this._userValueWidgets.map(async widget => {
|
||||
if (callback && !callback(widget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const methodsNames = widget.getMethodsNames();
|
||||
const proms = methodsNames.map(async methodName => {
|
||||
const value = await this._computeWidgetState(methodName, widget.getMethodsParams(methodName));
|
||||
const normalizedValue = this._normalizeWidgetValue(value);
|
||||
widget.setValue(normalizedValue, methodName);
|
||||
});
|
||||
await Promise.all(proms);
|
||||
|
||||
// Refresh the UI of all widgets (after all the current values they
|
||||
// hold have been updated).
|
||||
return widget.updateUI();
|
||||
});
|
||||
return Promise.all(proms);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
@@ -1748,11 +1737,6 @@ registry.sizing = SnippetOptionWidget.extend({
|
||||
* @override
|
||||
*/
|
||||
onFocus: function () {
|
||||
var resizeValues = this._getSize();
|
||||
_.each(resizeValues, (value, key) => {
|
||||
this.$handles.filter('.' + key).toggleClass('readonly', !value);
|
||||
});
|
||||
|
||||
this._onResize();
|
||||
},
|
||||
/**
|
||||
@@ -1773,6 +1757,16 @@ registry.sizing = SnippetOptionWidget.extend({
|
||||
await this._super(...arguments);
|
||||
this._onResize();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: function () {
|
||||
this._super(...arguments);
|
||||
const resizeValues = this._getSize();
|
||||
_.each(resizeValues, (value, key) => {
|
||||
this.$handles.filter('.' + key).toggleClass('readonly', !value);
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -1968,6 +1962,20 @@ registry.background = SnippetOptionWidget.extend({
|
||||
this.bindBackgroundEvents();
|
||||
this.__customImageSrc = this._getSrcFromCssValue();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
var src = this._getSrcFromCssValue();
|
||||
this.removeBgWidget.el.classList.toggle('d-none', !src);
|
||||
if (src) {
|
||||
var split = src.split('/');
|
||||
this.editBgTextEl.textContent = split[split.length - 1];
|
||||
} else {
|
||||
this.editBgTextEl.textContent = this._getDefaultTextContent();
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -2026,20 +2034,6 @@ registry.background = SnippetOptionWidget.extend({
|
||||
var srcValueWrapper = /url\(['"]*|['"]*\)|^none$/g;
|
||||
return value && value.replace(srcValueWrapper, '') || '';
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
await this._super.apply(this, arguments);
|
||||
var src = this._getSrcFromCssValue();
|
||||
this.removeBgWidget.el.classList.toggle('d-none', !src);
|
||||
if (src) {
|
||||
var split = src.split('/');
|
||||
this.editBgTextEl.textContent = split[split.length - 1];
|
||||
} else {
|
||||
this.editBgTextEl.textContent = this._getDefaultTextContent();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -2082,7 +2076,7 @@ registry.background = SnippetOptionWidget.extend({
|
||||
this.__customImageSrc = value;
|
||||
this.background(false, this.__customImageSrc, {});
|
||||
this.$target.toggleClass('oe_custom_bg', !!value);
|
||||
await this._updateUI();
|
||||
await this.updateUI();
|
||||
this.$target.trigger('snippet-option-change', [this]);
|
||||
},
|
||||
|
||||
@@ -2142,7 +2136,7 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({
|
||||
|
||||
this.$target.on('snippet-option-change', () => {
|
||||
// Hides option if the bg image is removed in favor of a bg color
|
||||
this._updateUI();
|
||||
this.updateUI();
|
||||
// this.img is used to compute dragging speed
|
||||
this.img.src = this._getSrcFromCssValue();
|
||||
});
|
||||
@@ -2160,12 +2154,6 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({
|
||||
$(window).off('.bgposition');
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
onFocus: function () {
|
||||
this._updateUI();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Options
|
||||
@@ -2211,6 +2199,21 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({
|
||||
this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* Disables background position if no background image, disables size inputs
|
||||
* in cover mode.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
this.$el.toggleClass('d-none', this.$target.css('background-image') === 'none');
|
||||
this.$el.find('we-input').toggleClass('d-none', this.$target.css('background-repeat') !== 'repeat');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -2317,17 +2320,6 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({
|
||||
// This is caused by the click event which we are currently handling bubbling up to the document.
|
||||
window.setTimeout(() => $(document).on('click.bgposition', this._onDocumentClicked.bind(this)), 0);
|
||||
},
|
||||
/**
|
||||
* Disables background position if no background image, disables size inputs
|
||||
* in cover mode.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
await this._super.apply(this, arguments);
|
||||
this.$el.toggleClass('d-none', this.$target.css('background-image') === 'none');
|
||||
this.$el.find('we-input').toggleClass('d-none', this.$target.css('background-repeat') !== 'repeat');
|
||||
},
|
||||
/**
|
||||
* Returns the src value from a css value related to a background image
|
||||
* (e.g. "url('blabla')" => "blabla" / "none" => "").
|
||||
|
||||
@@ -107,7 +107,7 @@ options.registry.background.include({
|
||||
delete target.dataset.bgVideoSrc;
|
||||
}
|
||||
this._refreshPublicWidgets();
|
||||
await this._updateUI();
|
||||
await this.updateUI();
|
||||
},
|
||||
/**
|
||||
* Returns whether the current target has a background video or not.
|
||||
@@ -350,12 +350,18 @@ options.registry.CarouselItem = options.Class.extend({
|
||||
this._super(...arguments);
|
||||
this.$carousel.off('.carousel_item_option');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Updates the slide counter.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
onFocus: function () {
|
||||
updateUI: function () {
|
||||
this._super(...arguments);
|
||||
const $items = this.$carousel.find('.carousel-item');
|
||||
const $activeSlide = $items.filter('.active');
|
||||
const updatedText = ` (${$activeSlide.index() + 1}/${$items.length})`;
|
||||
@@ -483,11 +489,23 @@ options.registry.navTabs = options.Class.extend({
|
||||
$activeLink.parent().remove();
|
||||
$activePane.remove();
|
||||
self._findLinksAndPanes();
|
||||
self._updateUI(); // TODO forced to do this because we do not return deferred for options
|
||||
self.updateUI(); // TODO forced to do this because we do not return deferred for options
|
||||
});
|
||||
$next.tab('show');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
this.$el.filter('[data-remove-tab]').toggleClass('d-none', this.$tabPanes.length <= 2);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -522,14 +540,6 @@ options.registry.navTabs = options.Class.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @override
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
await this._super.apply(this, arguments);
|
||||
this.$el.filter('[data-remove-tab]').toggleClass('d-none', this.$tabPanes.length <= 2);
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.sizing_x = options.registry.sizing.extend({
|
||||
@@ -1120,7 +1130,7 @@ options.registry.gallery = options.Class.extend({
|
||||
}
|
||||
this.mode('reset', this.getMode());
|
||||
this.trigger_up('cover_update');
|
||||
this._updateUI();
|
||||
this.updateUI();
|
||||
});
|
||||
dialog.open();
|
||||
},
|
||||
@@ -1352,6 +1362,21 @@ options.registry.gallery = options.Class.extend({
|
||||
this.mode('reset', this.getMode());
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
|
||||
this.$el.find('[data-interval]').closest('we-select')[0]
|
||||
.classList.toggle('d-none', this.activeMode !== 'slideshow');
|
||||
|
||||
this.$el.find('[data-columns]').closest('we-select')[0]
|
||||
.classList.toggle('d-none', !(this.activeMode === 'grid' || this.activeMode === 'masonry'));
|
||||
|
||||
this.el.querySelector('.o_w_image_spacing_option')
|
||||
.classList.toggle('d-none', this.activeMode === 'slideshow');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -1440,21 +1465,6 @@ options.registry.gallery = options.Class.extend({
|
||||
$container.empty().append($content);
|
||||
return $container;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
|
||||
this.$el.find('[data-interval]').closest('we-select')[0]
|
||||
.classList.toggle('d-none', this.activeMode !== 'slideshow');
|
||||
|
||||
this.$el.find('[data-columns]').closest('we-select')[0]
|
||||
.classList.toggle('d-none', !(this.activeMode === 'grid' || this.activeMode === 'masonry'));
|
||||
|
||||
this.el.querySelector('.o_w_image_spacing_option')
|
||||
.classList.toggle('d-none', this.activeMode === 'slideshow');
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.gallery_img = options.Class.extend({
|
||||
@@ -1553,10 +1563,16 @@ options.registry.topMenuColor = options.Class.extend({
|
||||
});
|
||||
return def;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
onFocus: function () {
|
||||
updateUI: function () {
|
||||
this._super(...arguments);
|
||||
this.trigger_up('action_demand', {
|
||||
actionName: 'get_page_option',
|
||||
params: ['header_overlay'],
|
||||
@@ -1779,7 +1795,7 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
if (!this.$target.hasClass('o_record_has_cover')) {
|
||||
this.$el.find('.o_record_cover_opt_size_default[data-select-class]').click();
|
||||
}
|
||||
this._updateUI();
|
||||
this.updateUI();
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -1804,33 +1820,13 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
switch (methodName) {
|
||||
case 'filterValue': {
|
||||
return parseFloat(this.$filter.css('opacity')).toFixed(1);
|
||||
}
|
||||
case 'filterColor': {
|
||||
const classes = this.filterColorClasses.split(' ');
|
||||
for (const className of classes) {
|
||||
if (this.$filter.hasClass(className)) {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
|
||||
// Only show options which are useful to the current cover
|
||||
@@ -1859,6 +1855,31 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
this.$target[0].dataset.filterValue = this.$filterValueOpts.filter('.active').data('filterValue') || 0.0;
|
||||
this.$target[0].dataset.filterColor = this.$filterColorOpts.filter('.active').data('filterColor') || '';
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
switch (methodName) {
|
||||
case 'filterValue': {
|
||||
return parseFloat(this.$filter.css('opacity')).toFixed(1);
|
||||
}
|
||||
case 'filterColor': {
|
||||
const classes = this.filterColorClasses.split(' ');
|
||||
for (const className of classes) {
|
||||
if (this.$filter.hasClass(className)) {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,6 @@ sOptions.registry.js_get_posts_selectBlog = sOptions.Class.extend({
|
||||
el.textContent = blog.name;
|
||||
menuEl.appendChild(el);
|
||||
}
|
||||
return this._updateUI();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -111,7 +111,7 @@ options.registry.CoverProperties.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
var isRegularCover = this.$target.is('.o_wblog_post_page_cover_regular');
|
||||
var $coverFull = this.$el.find('[data-select-class*="cover_full"]');
|
||||
|
||||
@@ -214,6 +214,27 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({
|
||||
}).then(reload);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super.apply(this, arguments);
|
||||
|
||||
var sizeX = parseInt(this.$target.attr('colspan') || 1);
|
||||
var sizeY = parseInt(this.$target.attr('rowspan') || 1);
|
||||
|
||||
var $size = this.$el.find('.o_wsale_soptions_menu_sizes');
|
||||
$size.find('tr:nth-child(-n + ' + sizeY + ') td:nth-child(-n + ' + sizeX + ')')
|
||||
.addClass('selected');
|
||||
|
||||
// Adapt size array preview to fit ppr
|
||||
$size.find('tr td:nth-child(n + ' + parseInt(this.ppr + 1) + ')').hide();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -244,22 +265,6 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateUI: async function () {
|
||||
await this._super.apply(this, arguments);
|
||||
|
||||
var sizeX = parseInt(this.$target.attr('colspan') || 1);
|
||||
var sizeY = parseInt(this.$target.attr('rowspan') || 1);
|
||||
|
||||
var $size = this.$el.find('.o_wsale_soptions_menu_sizes');
|
||||
$size.find('tr:nth-child(-n + ' + sizeY + ') td:nth-child(-n + ' + sizeX + ')')
|
||||
.addClass('selected');
|
||||
|
||||
// Adapt size array preview to fit ppr
|
||||
$size.find('tr td:nth-child(n + ' + parseInt(this.ppr + 1) + ')').hide();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
|
||||
Reference in New Issue
Block a user