[IMP] web_editor: add new way to toggle left panel widget visibility
Now, instead of extending updateUI, developers can extend _computeVisibility and _computeWidgetVisibility to indicate if the whole option or a specific widget needs to be visible or not. Without any JS, developers can also add a 'data-dependencies' attribute on widgets whose value is a comma-separated list of widget names. Widget names are specificied through 'data-name'. Other notable changes: 1) *All* the options UI of a same section are updated on a change (not only the widgets of the same option). 2) Back as it was before: the UI is only updated in non-preview mode (prevent flickering of background removal during color preview because of point 1). Part of https://github.com/odoo/odoo/pull/41600 closes odoo/odoo#41600 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
@@ -25,6 +25,8 @@ var SnippetEditor = Widget.extend({
|
||||
xmlDependencies: ['/web_editor/static/src/xml/snippets.xml'],
|
||||
custom_events: {
|
||||
'option_update': '_onOptionUpdate',
|
||||
'user_value_widget_request': '_onUserValueWidgetRequest',
|
||||
'snippet_option_update': '_onSnippetOptionUpdate',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -676,6 +678,44 @@ var SnippetEditor = Widget.extend({
|
||||
this.trigger_up('request_history_undo_record', {$target: this.$target});
|
||||
this.removeSnippet();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent}
|
||||
*/
|
||||
_onSnippetOptionUpdate: async function (ev) {
|
||||
if (ev.data.previewMode) {
|
||||
ev.data.onSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
const proms1 = Object.keys(this.styles).map(key => {
|
||||
return this.styles[key].updateUI({
|
||||
forced: ev.data.widget,
|
||||
noVisibility: true,
|
||||
});
|
||||
});
|
||||
await Promise.all(proms1);
|
||||
|
||||
const proms2 = Object.keys(this.styles).map(key => {
|
||||
return this.styles[key].updateUIVisibility();
|
||||
});
|
||||
await Promise.all(proms2);
|
||||
|
||||
ev.data.onSuccess();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onUserValueWidgetRequest: function (ev) {
|
||||
for (const key of Object.keys(this.styles)) {
|
||||
const widget = this.styles[key].findWidget(ev.data.name);
|
||||
if (widget) {
|
||||
ev.data.onSuccess(widget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,6 +148,22 @@ const UserValueWidget = Widget.extend({
|
||||
close: function () {
|
||||
this._userValueWidgets.forEach(widget => widget.close());
|
||||
},
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {UserValueWidget|null}
|
||||
*/
|
||||
findWidget: function (name) {
|
||||
for (const widget of this._userValueWidgets) {
|
||||
if (widget.getName() === name) {
|
||||
return widget;
|
||||
}
|
||||
const depWidget = widget.findWidget(name);
|
||||
if (depWidget) {
|
||||
return depWidget;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* Returns the value that the widget would hold if it was active, by default
|
||||
* the internal value it holds.
|
||||
@@ -169,6 +185,12 @@ const UserValueWidget = Widget.extend({
|
||||
const possibleValues = this._methodsParams.optionsPossibleValues[methodName];
|
||||
return possibleValues && possibleValues[0] || '';
|
||||
},
|
||||
/**
|
||||
* @returns {string[]}
|
||||
*/
|
||||
getDependencies: function () {
|
||||
return this._dependencies;
|
||||
},
|
||||
/**
|
||||
* Returns the names of the option methods associated to the widget. Those
|
||||
* are loaded with @see loadMethodsData.
|
||||
@@ -194,6 +216,12 @@ const UserValueWidget = Widget.extend({
|
||||
}
|
||||
return params;
|
||||
},
|
||||
/**
|
||||
* @returns {string} empty string if no name is used by the widget
|
||||
*/
|
||||
getName: function () {
|
||||
return this._methodsParams.name || '';
|
||||
},
|
||||
/**
|
||||
* Returns the user value that the widget currently holds. The value is a
|
||||
* string, this is the value that will be received in the option methods
|
||||
@@ -254,11 +282,14 @@ const UserValueWidget = Widget.extend({
|
||||
this._methodsNames = [];
|
||||
this._methodsParams = _.extend({}, extraParams);
|
||||
this._methodsParams.optionsPossibleValues = {};
|
||||
this._dependencies = [];
|
||||
|
||||
for (const key in this.el.dataset) {
|
||||
const dataValue = this.el.dataset[key].trim();
|
||||
|
||||
if (validMethodNames.includes(key)) {
|
||||
if (key === 'dependencies') {
|
||||
this._dependencies.push(...dataValue.split(/\s*,\s*/g));
|
||||
} else if (validMethodNames.includes(key)) {
|
||||
this._methodsNames.push(key);
|
||||
this._methodsParams.optionsPossibleValues[key] = dataValue.split(/\s*\|\s*/g);
|
||||
} else {
|
||||
@@ -313,6 +344,12 @@ const UserValueWidget = Widget.extend({
|
||||
setValue: function (value, methodName) {
|
||||
this._value = value;
|
||||
},
|
||||
/**
|
||||
* @param {boolean} show
|
||||
*/
|
||||
toggleVisibility: function (show) {
|
||||
this.el.classList.toggle('d-none', !show);
|
||||
},
|
||||
/**
|
||||
* Updates the UI to match the user value the widget currently holds, only
|
||||
* if the UI can currently be updated.
|
||||
@@ -1427,6 +1464,22 @@ const SnippetOptionWidget = Widget.extend({
|
||||
closeWidgets: function () {
|
||||
this._userValueWidgets.forEach(widget => widget.close());
|
||||
},
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {UserValueWidget|null}
|
||||
*/
|
||||
findWidget: function (name) {
|
||||
for (const widget of this._userValueWidgets) {
|
||||
if (widget.getName() === name) {
|
||||
return widget;
|
||||
}
|
||||
const depWidget = widget.findWidget(name);
|
||||
if (depWidget) {
|
||||
return depWidget;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* Sometimes, options may need to notify other options, even in parent
|
||||
* editors. This can be done thanks to the 'option_update' event, which
|
||||
@@ -1453,16 +1506,18 @@ const SnippetOptionWidget = Widget.extend({
|
||||
setTarget: async function ($target) {
|
||||
this.$target = $target;
|
||||
await this.updateUI();
|
||||
this.$target.trigger('snippet-option-change', [this]);
|
||||
},
|
||||
/**
|
||||
* Updates the UI. For widget update, @see _computeWidgetState.
|
||||
*
|
||||
* @param {UserValueWidget} [forced=null]
|
||||
* Only non-previewed widgets are updated, except for the one given here
|
||||
* @param {boolean} [noVisibility=false]
|
||||
* If true, only update widget values and their UI, not their visibility
|
||||
* -> @see updateUIVisibility for toggling visibility only
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateUI: async function (forced) {
|
||||
updateUI: async function ({forced, noVisibility} = {}) {
|
||||
// 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.
|
||||
@@ -1494,6 +1549,49 @@ const SnippetOptionWidget = Widget.extend({
|
||||
// hold have been updated).
|
||||
return widget.updateUI(widget === forced);
|
||||
});
|
||||
|
||||
if (!noVisibility) {
|
||||
await this.updateUIVisibility();
|
||||
}
|
||||
|
||||
return Promise.all(proms);
|
||||
},
|
||||
/**
|
||||
* Updates the UI visibility - @see _computeVisibility. For widget update,
|
||||
* @see _computeWidgetVisibility.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateUIVisibility: async function () {
|
||||
const proms = this._userValueWidgets.map(async widget => {
|
||||
const params = widget.getMethodsParams();
|
||||
const show = await this._computeWidgetVisibility(widget.getName(), params);
|
||||
if (!show) {
|
||||
widget.toggleVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const dependencies = widget.getDependencies();
|
||||
const dependenciesOK = !dependencies.length || dependencies.some(depName => {
|
||||
const toBeActive = (depName[0] !== '!');
|
||||
if (!toBeActive) {
|
||||
depName = depName.substr(1);
|
||||
}
|
||||
|
||||
let widget = null;
|
||||
this.trigger_up('user_value_widget_request', {
|
||||
name: depName,
|
||||
onSuccess: _widget => widget = _widget,
|
||||
});
|
||||
return widget && (widget.isActive() === toBeActive);
|
||||
});
|
||||
|
||||
widget.toggleVisibility(dependenciesOK);
|
||||
});
|
||||
|
||||
const showUI = await this._computeVisibility();
|
||||
this.el.classList.toggle('d-none', !showUI);
|
||||
|
||||
return Promise.all(proms);
|
||||
},
|
||||
|
||||
@@ -1501,6 +1599,13 @@ const SnippetOptionWidget = Widget.extend({
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {Promise<boolean>|boolean}
|
||||
*/
|
||||
_computeVisibility: async function () {
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* Returns the string value that should be hold by the widget which is
|
||||
* related to the given method name.
|
||||
@@ -1565,6 +1670,15 @@ const SnippetOptionWidget = Widget.extend({
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {string} widgetName
|
||||
* @param {Object} params
|
||||
* @returns {Promise<boolean>|boolean}
|
||||
*/
|
||||
_computeWidgetVisibility: async function (widgetName, params) {
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {HTMLElement} el
|
||||
@@ -1598,25 +1712,25 @@ const SnippetOptionWidget = Widget.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is not preview mode, the user selected the option for good
|
||||
// (so record the action)
|
||||
if (!previewMode) {
|
||||
this.trigger_up('request_history_undo_record', {$target: this.$target});
|
||||
}
|
||||
|
||||
// Call widget option methods and update $target
|
||||
await this._select(previewMode, widget);
|
||||
|
||||
// Trigger the related events notifying snippet changes
|
||||
let evName = 'snippet-option-reset';
|
||||
if (previewMode === false) {
|
||||
evName = 'snippet-option-change';
|
||||
} else if (previewMode === true) {
|
||||
evName = 'snippet-option-preview';
|
||||
}
|
||||
this.$target.trigger(evName, [this]);
|
||||
this.$target.trigger('content_changed');
|
||||
|
||||
// Update the UI of the correct widgets
|
||||
if (!previewMode) {
|
||||
await this.updateUI(widget);
|
||||
} else {
|
||||
await this.updateUI();
|
||||
}
|
||||
await new Promise(resolve => {
|
||||
// Will update the UI of the correct widgets for all options
|
||||
// related to the same $target/editor if necessary
|
||||
this.trigger_up('snippet_option_update', {
|
||||
widget: widget,
|
||||
previewMode: previewMode,
|
||||
onSuccess: () => resolve(),
|
||||
});
|
||||
});
|
||||
}});
|
||||
},
|
||||
/**
|
||||
@@ -1754,12 +1868,6 @@ const SnippetOptionWidget = Widget.extend({
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_select: async function (previewMode, widget) {
|
||||
// If it is not preview mode, the user selected the option for good
|
||||
// (so record the action)
|
||||
if (!previewMode) {
|
||||
this.trigger_up('request_history_undo_record', {$target: this.$target});
|
||||
}
|
||||
|
||||
// Call each option method sequentially
|
||||
for (const methodName of widget.getMethodsNames()) {
|
||||
const widgetValue = widget.getValue(methodName);
|
||||
@@ -2099,7 +2207,7 @@ registry.background = SnippetOptionWidget.extend({
|
||||
if (previewMode === 'reset') {
|
||||
// No background has been selected and we want to reset back to the
|
||||
// original custom image
|
||||
await this._setCustomBackground(this.__customImageSrc);
|
||||
await this._setCustomBackground(this.__customImageSrc, previewMode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2120,16 +2228,21 @@ registry.background = SnippetOptionWidget.extend({
|
||||
var options = this._getMediaDialogOptions();
|
||||
var media = this._getEditableMedia();
|
||||
|
||||
var _editor = new weWidgets.MediaDialog(this, options, media).open();
|
||||
_editor.on('save', this, data => {
|
||||
this._onSaveMediaDialog(data).then(() => {
|
||||
this.$target.trigger('content_changed');
|
||||
return new Promise(resolve => {
|
||||
const _editor = new weWidgets.MediaDialog(this, options, media).open();
|
||||
let _saving = false;
|
||||
_editor.on('save', this, data => {
|
||||
_saving = true;
|
||||
this._onSaveMediaDialog(data).then(() => resolve());
|
||||
});
|
||||
_editor.on('closed', this, () => {
|
||||
if (media.classList.contains('o_we_fake_image')) {
|
||||
media.parentNode.removeChild(media);
|
||||
}
|
||||
if (!_saving) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
_editor.on('closed', this, () => {
|
||||
if (media.classList.contains('o_we_fake_image')) {
|
||||
media.parentNode.removeChild(media);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2163,7 +2276,6 @@ registry.background = SnippetOptionWidget.extend({
|
||||
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];
|
||||
@@ -2171,6 +2283,14 @@ registry.background = SnippetOptionWidget.extend({
|
||||
this.editBgTextEl.textContent = this._getDefaultTextContent();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUIVisibility: async function () {
|
||||
await this._super(...arguments);
|
||||
var src = this._getSrcFromCssValue();
|
||||
this.removeBgWidget.el.classList.toggle('d-none', !src);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -2267,12 +2387,18 @@ registry.background = SnippetOptionWidget.extend({
|
||||
* @param {string} value
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_setCustomBackground: async function (value) {
|
||||
_setCustomBackground: async function (value, previewMode) {
|
||||
this.__customImageSrc = value;
|
||||
this.background(false, this.__customImageSrc, {});
|
||||
this.$target.toggleClass('oe_custom_bg', !!value);
|
||||
await this.updateUI();
|
||||
this.$target.trigger('snippet-option-change', [this]);
|
||||
await new Promise(resolve => {
|
||||
// Will update the UI of the correct widgets for all options
|
||||
// related to the same $target/editor
|
||||
this.trigger_up('snippet_option_update', {
|
||||
previewMode: previewMode,
|
||||
onSuccess: () => resolve(),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -2326,15 +2452,6 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this._initOverlay();
|
||||
this.img = document.createElement('img');
|
||||
this.img.src = this._getSrcFromCssValue();
|
||||
|
||||
this.$target.on('snippet-option-change', () => {
|
||||
// Hides option if the bg image is removed in favor of a bg color
|
||||
this.updateUI();
|
||||
// this.img is used to compute dragging speed
|
||||
this.img.src = this._getSrcFromCssValue();
|
||||
});
|
||||
|
||||
// Resize overlay content on window resize because background images
|
||||
// change size, and on carousel slide because they sometimes take up
|
||||
@@ -2369,7 +2486,14 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({
|
||||
*
|
||||
* @see this.selectClass for params
|
||||
*/
|
||||
backgroundPositionOverlay: function (previewMode, widgetValue, params) {
|
||||
backgroundPositionOverlay: async function (previewMode, widgetValue, params) {
|
||||
// Updates the internal image
|
||||
await new Promise(resolve => {
|
||||
this.img = document.createElement('img');
|
||||
this.img.addEventListener('load', () => resolve());
|
||||
this.img.src = this._getSrcFromCssValue();
|
||||
});
|
||||
|
||||
const position = this.$target.css('background-position').split(' ').map(v => parseInt(v));
|
||||
// Convert % values to pixels (because mouse movement is in pixels)
|
||||
const delta = this._getBackgroundDelta();
|
||||
@@ -2394,25 +2518,16 @@ 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
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeVisibility: function () {
|
||||
return this._super(...arguments) && (this.$target.css('background-image') !== 'none');
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -503,21 +503,18 @@ options.registry.navTabs = options.Class.extend({
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
this.$el.filter('[data-remove-tab]').toggleClass('d-none', this.$tabPanes.length <= 2);
|
||||
_computeWidgetVisibility: async function (widgetName, params) {
|
||||
if (widgetName === 'remove_tab_opt') {
|
||||
return (this.$tabPanes.length > 2);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -1359,21 +1356,6 @@ 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
|
||||
@@ -1527,28 +1509,16 @@ options.registry.countdown = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
updateUIVisibility: async function () {
|
||||
await this._super(...arguments);
|
||||
const dataset = this.$target[0].dataset;
|
||||
|
||||
// End Action UI
|
||||
this.$el.find('[data-attribute-name="redirectUrl"]')
|
||||
.toggleClass('d-none', dataset.endAction !== 'redirect');
|
||||
this.$el.find('.toggle-edit-message')
|
||||
.toggleClass('d-none', dataset.endAction !== 'message');
|
||||
this.$el.find('[data-toggle-class="hide-countdown"]')
|
||||
.toggleClass('d-none', dataset.endAction === 'nothing');
|
||||
|
||||
// End Message UI
|
||||
this.updateUIEndMessage();
|
||||
|
||||
// Styling UI
|
||||
this.$el.find('[data-attribute-name="layoutBackground"], [data-attribute-name="progressBarStyle"]')
|
||||
.toggleClass('d-none', dataset.layout === 'clean' || dataset.layout === 'text');
|
||||
this.$el.find('[data-attribute-name="layoutBackgroundColor"]')
|
||||
.toggleClass('d-none', dataset.layoutBackground === 'none');
|
||||
this.$el.find('[data-attribute-name="progressBarWeight"], [data-attribute-name="progressBarColor"]')
|
||||
.toggleClass('d-none', dataset.progressBarStyle === 'none');
|
||||
},
|
||||
/**
|
||||
* @see this.updateUI
|
||||
@@ -1677,35 +1647,24 @@ options.registry.topMenuTransparency = options.Class.extend({
|
||||
});
|
||||
|
||||
options.registry.topMenuColor = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
var def = this._super.apply(this, arguments);
|
||||
this.$target.on('snippet-option-change', function () {
|
||||
self.onFocus();
|
||||
});
|
||||
return def;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
await new Promise(resolve => {
|
||||
_computeVisibility: async function () {
|
||||
const show = await this._super(...arguments);
|
||||
if (!show) {
|
||||
return false;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
this.trigger_up('action_demand', {
|
||||
actionName: 'get_page_option',
|
||||
params: ['header_overlay'],
|
||||
onSuccess: value => {
|
||||
this.$el.toggleClass('d-none', !value);
|
||||
resolve();
|
||||
},
|
||||
onSuccess: value => resolve(!!value),
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -1959,29 +1918,10 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
|
||||
// Only show options which are useful to the current cover
|
||||
_.each(this.$el.children(), el => {
|
||||
var $el = $(el);
|
||||
|
||||
if (!$el.is('[data-change]')) {
|
||||
$el.removeClass('d-none');
|
||||
|
||||
['size', 'filters', 'text_size', 'text_align'].forEach(optName => {
|
||||
var $opts = $el.find('[data-cover-opt="' + optName + '"]');
|
||||
var notAllowed = (this.$target.data('use_' + optName) !== 'True');
|
||||
|
||||
if ($opts.length && (!this.$target.hasClass('o_record_has_cover') || notAllowed)) {
|
||||
$el.addClass('d-none');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
this.$el.find('[data-clear]').toggleClass('d-none', !this.$target.hasClass('o_record_has_cover'));
|
||||
|
||||
// Update saving dataset
|
||||
this.$target[0].dataset.coverClass = this.$el.find('.active[data-cover-opt="size"]').data('selectClass') || '';
|
||||
this.$target[0].dataset.textSizeClass = this.$el.find('.active[data-cover-opt="text_size"]').data('selectClass') || '';
|
||||
this.$target[0].dataset.textAlignClass = this.$el.find('.active[data-cover-opt="text_align"]').data('selectClass') || '';
|
||||
this.$target[0].dataset.coverClass = this.$el.find('[data-cover-opt-name="size"] .active').data('selectClass') || '';
|
||||
this.$target[0].dataset.textSizeClass = this.$el.find('[data-cover-opt-name="text_size"] .active').data('selectClass') || '';
|
||||
this.$target[0].dataset.textAlignClass = this.$el.find('[data-cover-opt-name="text_align"] .active').data('selectClass') || '';
|
||||
this.$target[0].dataset.filterValue = this.$filterValueOpts.filter('.active').data('filterValue') || 0.0;
|
||||
this.$target[0].dataset.filterColor = this.$filterColorOpts.filter('.active').data('filterColor') || '';
|
||||
},
|
||||
@@ -2010,6 +1950,19 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetVisibility: function (widgetName, params) {
|
||||
const hasCover = this.$target.hasClass('o_record_has_cover');
|
||||
if (widgetName === 'remove_cover_opt') {
|
||||
return hasCover;
|
||||
} else if (params.coverOptName) {
|
||||
var notAllowed = (this.$target.data(`use_${params.coverOptName}`) !== 'True');
|
||||
return (hasCover && !notAllowed);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -2096,9 +2049,8 @@ options.registry.InnerChart = options.Class.extend({
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
// Get the 2 color pickers to hide in updateUI
|
||||
this.backSelectEl = this.el.querySelector('we-select[data-attribute-name="backgroundColor"]');
|
||||
this.borderSelectEl = this.el.querySelector('we-select[data-attribute-name="borderColor"]');
|
||||
this.backSelectEl = this.el.querySelector('[data-name="chart_bg_color_opt"]');
|
||||
this.borderSelectEl = this.el.querySelector('[data-name="chart_border_color_opt"]');
|
||||
|
||||
// Build matrix content
|
||||
this.tableEl = this.el.querySelector('we-matrix table');
|
||||
@@ -2130,16 +2082,15 @@ options.registry.InnerChart = options.Class.extend({
|
||||
if (!this.lastEditableSelectedInput.closest('table') || this.colorPaletteSelectedInput && !this.colorPaletteSelectedInput.closest('table')) {
|
||||
this._setDefaultSelectedInput();
|
||||
}
|
||||
|
||||
await this._super(...arguments);
|
||||
|
||||
// prevent the columns from becoming too small.
|
||||
this.tableEl.classList.toggle('o_we_matrix_five_col', this.tableEl.querySelectorAll('tr:first-child th').length > 5);
|
||||
this.el.querySelector('[data-attribute-name="stacked"]').classList.toggle('d-none', !this._isStackableChart() || this._getColumnCount() === 1);
|
||||
// Disable color on inputs that don't use them
|
||||
const notDisplayed = !this.colorPaletteSelectedInput;
|
||||
this.backSelectEl.classList.toggle('d-none', notDisplayed);
|
||||
this.borderSelectEl.classList.toggle('d-none', notDisplayed);
|
||||
|
||||
this.backSelectEl.querySelector('we-title').textContent = this._isPieChart() ? _t("Data Background Color") : _t("Dataset Background Color");
|
||||
this.borderSelectEl.querySelector('we-title').textContent = this._isPieChart() ? _t("Data Border Color") : _t("Dataset Border Color");
|
||||
|
||||
// Dataset/Cell color
|
||||
this.tableEl.querySelectorAll('input').forEach(el => el.style.border = '');
|
||||
const selector = this._isPieChart() ? 'td input' : 'tr:first-child input';
|
||||
@@ -2198,6 +2149,21 @@ options.registry.InnerChart = options.Class.extend({
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetVisibility: function (widgetName, params) {
|
||||
switch (widgetName) {
|
||||
case 'stacked_chart_opt': {
|
||||
return this._getColumnCount() > 1;
|
||||
}
|
||||
case 'chart_bg_color_opt':
|
||||
case 'chart_border_color_opt': {
|
||||
return !!this.colorPaletteSelectedInput;
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* Sets and reloads the data on the canvas if it has changed.
|
||||
* Used in matrix related method.
|
||||
@@ -2365,13 +2331,6 @@ options.registry.InnerChart = options.Class.extend({
|
||||
_isPieChart: function () {
|
||||
return ['pie', 'doughnut'].includes(this.$target[0].dataset.type);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @return {boolean}
|
||||
*/
|
||||
_isStackableChart: function () {
|
||||
return ['bar', 'horizontalBar'].includes(this.$target[0].dataset.type);
|
||||
},
|
||||
/**
|
||||
* Return the number of column minus header and button
|
||||
* @private
|
||||
|
||||
@@ -1170,7 +1170,7 @@
|
||||
|
||||
<div data-js="navTabs" data-selector="section .row > div" data-target=".nav-tabs">
|
||||
<we-button data-add-tab="1" data-no-preview="true">Add Tab</we-button>
|
||||
<we-button data-remove-tab="1" data-no-preview="true">Remove Tab</we-button>
|
||||
<we-button data-remove-tab="1" data-name="remove_tab_opt" data-no-preview="true">Remove Tab</we-button>
|
||||
</div>
|
||||
|
||||
<div data-js="gallery" data-selector=".o_gallery">
|
||||
@@ -1179,11 +1179,11 @@
|
||||
<we-divider/>
|
||||
<we-select string="Mode">
|
||||
<we-button data-mode="nomode">Float</we-button>
|
||||
<we-button data-mode="masonry">Masonry</we-button>
|
||||
<we-button data-mode="grid">Grid</we-button>
|
||||
<we-button data-mode="slideshow">Slideshow</we-button>
|
||||
<we-button data-mode="masonry" data-name="masonry_mode_opt">Masonry</we-button>
|
||||
<we-button data-mode="grid" data-name="grid_mode_opt">Grid</we-button>
|
||||
<we-button data-mode="slideshow" data-name="slideshow_mode_opt">Slideshow</we-button>
|
||||
</we-select>
|
||||
<we-select string="Slideshow speed">
|
||||
<we-select string="Slideshow speed" data-dependencies="slideshow_mode_opt">
|
||||
<we-button data-interval="1000">1s</we-button>
|
||||
<we-button data-interval="2000">2s</we-button>
|
||||
<we-button data-interval="3000">3s</we-button>
|
||||
@@ -1191,7 +1191,7 @@
|
||||
<we-button data-interval="10000">10s</we-button>
|
||||
<we-button data-interval="0">Disable autoplay</we-button>
|
||||
</we-select>
|
||||
<we-select string="Columns">
|
||||
<we-select string="Columns" data-dependencies="masonry_mode_opt, grid_mode_opt">
|
||||
<we-button data-columns="1">1</we-button>
|
||||
<we-button data-columns="2">2</we-button>
|
||||
<we-button data-columns="3">3</we-button>
|
||||
@@ -1199,7 +1199,7 @@
|
||||
<we-button data-columns="6">6</we-button>
|
||||
<we-button data-columns="12">12</we-button>
|
||||
</we-select>
|
||||
<we-select string="Images spacing" class="o_w_image_spacing_option">
|
||||
<we-select string="Images spacing" data-dependencies="!slideshow_mode_opt">
|
||||
<we-button data-select-class="o_spc-none">None</we-button>
|
||||
<we-button data-select-class="o_spc-small">Small</we-button>
|
||||
<we-button data-select-class="o_spc-medium">Medium</we-button>
|
||||
@@ -1282,12 +1282,12 @@
|
||||
<we-row>
|
||||
<we-select data-no-preview="true">
|
||||
<we-button data-background-type="cover">Cover</we-button>
|
||||
<we-button data-background-type="repeat-pattern">Repeat pattern</we-button>
|
||||
<we-button data-background-type="repeat-pattern" data-name="background_repeat_opt">Repeat pattern</we-button>
|
||||
</we-select>
|
||||
<we-button class="fa fa-fw fa-arrows" title="Background Position"
|
||||
data-background-position-overlay="true" data-no-preview="true"/>
|
||||
</we-row>
|
||||
<we-multi data-css-property="background-size">
|
||||
<we-multi data-css-property="background-size" data-dependencies="background_repeat_opt">
|
||||
<we-input string="Width" data-select-style="auto" data-unit="px"/>
|
||||
<we-input string="Height" data-select-style="auto" data-unit="px"/>
|
||||
</we-multi>
|
||||
@@ -1338,12 +1338,14 @@
|
||||
<div data-selector="section .row > div"
|
||||
data-exclude=".s_col_no_bgcolor, .s_col_no_bgcolor.row > div, .o_gallery .row > div">
|
||||
<we-row string="Border" class="o_we_inline">
|
||||
<we-colorpicker data-select-style="true" data-css-property="border-color" data-color-prefix="border-"/>
|
||||
<we-input class="o_we_inline"
|
||||
<we-colorpicker data-dependencies="border_width_opt"
|
||||
data-select-style="true" data-css-property="border-color" data-color-prefix="border-"/>
|
||||
<we-input class="o_we_inline" data-name="border_width_opt"
|
||||
data-select-style="0" data-css-property="border-width"
|
||||
data-unit="px" data-extra-class="border"/>
|
||||
</we-row>
|
||||
<we-input string="Rounded" class="o_we_inline"
|
||||
data-dependencies="border_width_opt"
|
||||
data-select-style="0" data-css-property="border-radius"
|
||||
data-unit="px" data-extra-class="rounded"/>
|
||||
<we-checkbox string="Shadow" data-select-class="shadow"/>
|
||||
@@ -1421,41 +1423,41 @@
|
||||
|
||||
<div data-js="CoverProperties" data-selector=".o_record_cover_container" data-no-check="true">
|
||||
<we-button data-change="true" data-no-preview="true">Change Cover</we-button>
|
||||
<we-button data-clear="true" data-no-preview="true">Remove Cover</we-button>
|
||||
<we-select string="Size">
|
||||
<we-button data-cover-opt="size" data-select-class="o_record_has_cover cover_full">Full Screen</we-button>
|
||||
<we-button data-cover-opt="size" class="o_record_cover_opt_size_default" data-select-class="o_record_has_cover cover_mid">Half Screen</we-button>
|
||||
<we-button data-cover-opt="size" data-select-class="o_record_has_cover cover_auto">Fit text</we-button>
|
||||
<we-button data-clear="true" data-no-preview="true" data-name="remove_cover_opt">Remove Cover</we-button>
|
||||
<we-select string="Size" data-cover-opt-name="size">
|
||||
<we-button data-select-class="o_record_has_cover cover_full">Full Screen</we-button>
|
||||
<we-button class="o_record_cover_opt_size_default" data-select-class="o_record_has_cover cover_mid">Half Screen</we-button>
|
||||
<we-button data-select-class="o_record_has_cover cover_auto">Fit text</we-button>
|
||||
</we-select>
|
||||
<we-select string="Filter Intensity">
|
||||
<we-button data-cover-opt="filters" data-filter-value="0.0">None</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-value="0.2">Low</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-value="0.4">Medium</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-value="0.6">High</we-button>
|
||||
<we-select string="Filter Intensity" data-cover-opt-name="filters">
|
||||
<we-button data-filter-value="0.0">None</we-button>
|
||||
<we-button data-filter-value="0.2">Low</we-button>
|
||||
<we-button data-filter-value="0.4">Medium</we-button>
|
||||
<we-button data-filter-value="0.6">High</we-button>
|
||||
</we-select>
|
||||
<we-select string="Filter Color">
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_black">Black</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_none">White</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="bg-primary">Primary</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="bg-secondary">Secondary</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_blue">Blue</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_yellow">Yellow</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_red">Red</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_purple">Purple</we-button>
|
||||
<we-button data-cover-opt="filters" data-filter-color="oe_green">Green</we-button>
|
||||
<we-select string="Filter Color" data-cover-opt-name="filters">
|
||||
<we-button data-filter-color="oe_black">Black</we-button>
|
||||
<we-button data-filter-color="oe_none">White</we-button>
|
||||
<we-button data-filter-color="bg-primary">Primary</we-button>
|
||||
<we-button data-filter-color="bg-secondary">Secondary</we-button>
|
||||
<we-button data-filter-color="oe_blue">Blue</we-button>
|
||||
<we-button data-filter-color="oe_yellow">Yellow</we-button>
|
||||
<we-button data-filter-color="oe_red">Red</we-button>
|
||||
<we-button data-filter-color="oe_purple">Purple</we-button>
|
||||
<we-button data-filter-color="oe_green">Green</we-button>
|
||||
</we-select>
|
||||
<we-select string="Text Size">
|
||||
<we-button data-cover-opt="text_size" data-select-class="o_record_cover_font_hero">Hero</we-button>
|
||||
<we-button data-cover-opt="text_size" data-select-class="o_record_cover_font_huge">Huge</we-button>
|
||||
<we-button data-cover-opt="text_size" data-select-class="o_record_cover_font_big">Big</we-button>
|
||||
<we-button data-cover-opt="text_size" data-select-class="">Regular</we-button>
|
||||
<we-button data-cover-opt="text_size" data-select-class="o_record_cover_font_small">Small</we-button>
|
||||
<we-button data-cover-opt="text_size" data-select-class="o_record_cover_font_tiny">Tiny</we-button>
|
||||
<we-select string="Text Size" data-cover-opt-name="text_size">
|
||||
<we-button data-select-class="o_record_cover_font_hero">Hero</we-button>
|
||||
<we-button data-select-class="o_record_cover_font_huge">Huge</we-button>
|
||||
<we-button data-select-class="o_record_cover_font_big">Big</we-button>
|
||||
<we-button data-select-class="">Regular</we-button>
|
||||
<we-button data-select-class="o_record_cover_font_small">Small</we-button>
|
||||
<we-button data-select-class="o_record_cover_font_tiny">Tiny</we-button>
|
||||
</we-select>
|
||||
<we-select string="Text Alignment">
|
||||
<we-button data-cover-opt="text_align" data-select-class="">Left</we-button>
|
||||
<we-button data-cover-opt="text_align" data-select-class="text-center">Centered</we-button>
|
||||
<we-button data-cover-opt="text_align" data-select-class="text-right">Right</we-button>
|
||||
<we-select string="Text Alignment" data-cover-opt-name="text_align">
|
||||
<we-button data-select-class="">Left</we-button>
|
||||
<we-button data-select-class="text-center">Centered</we-button>
|
||||
<we-button data-select-class="text-right">Right</we-button>
|
||||
</we-select>
|
||||
</div>
|
||||
|
||||
@@ -1471,14 +1473,16 @@
|
||||
<we-datetimepicker string="Due Date" data-select-data-attribute="" data-attribute-name="endTime"/>
|
||||
<we-row>
|
||||
<we-select string="What should happen at the end?" data-no-preview="true">
|
||||
<we-button data-end-action="nothing">Nothing</we-button>
|
||||
<we-button data-end-action="redirect">Redirect</we-button>
|
||||
<we-button data-end-action="nothing" data-name="no_end_action_opt">Nothing</we-button>
|
||||
<we-button data-end-action="redirect" data-name="redirect_end_action_opt">Redirect</we-button>
|
||||
<we-button data-end-action="message">Show Message</we-button>
|
||||
</we-select>
|
||||
<we-button class="far fa-edit fa-fw align-self-end toggle-edit-message" title="Edit Message"/>
|
||||
</we-row>
|
||||
<we-checkbox string="Hide countdown at the end" data-select-class="hide-countdown"/>
|
||||
<we-urlpicker string="URL" data-select-data-attribute="/my-awesome-page" data-attribute-name="redirectUrl"/>
|
||||
<we-checkbox string="Hide countdown at the end" data-dependencies="!no_end_action_opt"
|
||||
data-select-class="hide-countdown"/>
|
||||
<we-urlpicker string="URL" data-dependencies="redirect_end_action_opt"
|
||||
data-select-data-attribute="/my-awesome-page" data-attribute-name="redirectUrl"/>
|
||||
<we-select string="Size" data-attribute-name="size">
|
||||
<we-button data-select-data-attribute="80">Small</we-button>
|
||||
<we-button data-select-data-attribute="120">Medium</we-button>
|
||||
@@ -1491,27 +1495,29 @@
|
||||
</we-select>
|
||||
<we-colorpicker string="Text Color" data-attribute-name="textColor" data-select-data-attribute=""/>
|
||||
<we-select string="Layout">
|
||||
<we-button data-layout="circle">Circle</we-button>
|
||||
<we-button data-layout="boxes">Boxes</we-button>
|
||||
<we-button data-layout="circle" data-name="circle_layout_opt">Circle</we-button>
|
||||
<we-button data-layout="boxes" data-name="boxes_layout_opt">Boxes</we-button>
|
||||
<we-button data-layout="clean">Clean</we-button>
|
||||
<we-button data-layout="text">Text Inline</we-button>
|
||||
</we-select>
|
||||
<we-select string="Layout Background" data-attribute-name="layoutBackground">
|
||||
<we-select string="Layout Background" data-attribute-name="layoutBackground" data-dependencies="circle_layout_opt, boxes_layout_opt">
|
||||
<we-button data-select-data-attribute="inner">Inner</we-button>
|
||||
<we-button data-select-data-attribute="plain">Plain</we-button>
|
||||
<we-button data-select-data-attribute="none">None</we-button>
|
||||
<we-button data-select-data-attribute="none" data-name="no_background_layout_opt">None</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Layout Background Color" data-attribute-name="layoutBackgroundColor" data-select-data-attribute=""/>
|
||||
<we-select string="Progress Bar Style" data-attribute-name="progressBarStyle">
|
||||
<we-colorpicker string="Layout Background Color" data-dependencies="!no_background_layout_opt"
|
||||
data-attribute-name="layoutBackgroundColor" data-select-data-attribute=""/>
|
||||
<we-select string="Progress Bar Style" data-attribute-name="progressBarStyle" data-dependencies="circle_layout_opt, boxes_layout_opt">
|
||||
<we-button data-select-data-attribute="surrounded">Surrounded</we-button>
|
||||
<we-button data-select-data-attribute="disappear">Disappearing</we-button>
|
||||
<we-button data-select-data-attribute="none">None</we-button>
|
||||
<we-button data-select-data-attribute="none" data-name="no_progressbar_style_opt">None</we-button>
|
||||
</we-select>
|
||||
<we-select string="Progress Bar Weight" data-attribute-name="progressBarWeight">
|
||||
<we-select string="Progress Bar Weight" data-attribute-name="progressBarWeight" data-dependencies="!no_progressbar_style_opt">
|
||||
<we-button data-select-data-attribute="thin">Thin</we-button>
|
||||
<we-button data-select-data-attribute="thick">Thick</we-button>
|
||||
</we-select>
|
||||
<we-colorpicker string="Progress Bar Color" data-attribute-name="progressBarColor" data-select-data-attribute=""/>
|
||||
<we-colorpicker string="Progress Bar Color" data-dependencies="!no_progressbar_style_opt"
|
||||
data-attribute-name="progressBarColor" data-select-data-attribute=""/>
|
||||
</div>
|
||||
|
||||
<!-- Chart Inner -->
|
||||
@@ -1522,14 +1528,15 @@
|
||||
data-css-property="background-color"
|
||||
data-color-prefix="bg-"/>
|
||||
<we-select string="Type" data-attribute-name="type" data-attribute-default-value="bar" data-no-preview="true">
|
||||
<we-button data-select-data-attribute="bar">Bar Vertical</we-button>
|
||||
<we-button data-select-data-attribute="horizontalBar">Bar Horizontal</we-button>
|
||||
<we-button data-select-data-attribute="bar" data-name="bar_chart_opt">Bar Vertical</we-button>
|
||||
<we-button data-select-data-attribute="horizontalBar" data-name="horizontal_bar_chart_opt">Bar Horizontal</we-button>
|
||||
<we-button data-select-data-attribute="line">Line</we-button>
|
||||
<we-button data-select-data-attribute="pie">Pie</we-button>
|
||||
<we-button data-select-data-attribute="doughnut">Doughnut</we-button>
|
||||
<we-button data-select-data-attribute="radar">Radar</we-button>
|
||||
</we-select>
|
||||
<we-checkbox string="Stacked" data-select-data-attribute="false|true" data-attribute-name="stacked" data-no-preview="true"/>
|
||||
<we-checkbox string="Stacked" data-name="stacked_chart_opt" data-dependencies="bar_chart_opt, horizontal_bar_chart_opt"
|
||||
data-select-data-attribute="false|true" data-attribute-name="stacked" data-no-preview="true"/>
|
||||
<we-select string="Legend" data-attribute-name="legendPosition" data-attribute-default-value="top" data-no-preview="true">
|
||||
<we-button data-select-data-attribute="none">None</we-button>
|
||||
<we-button data-select-data-attribute="top">Top</we-button>
|
||||
@@ -1549,11 +1556,11 @@
|
||||
</tr>
|
||||
</table>
|
||||
</we-matrix>
|
||||
<we-colorpicker string="Background"
|
||||
<we-colorpicker string="Background" data-name="chart_bg_color_opt"
|
||||
data-color-change=""
|
||||
data-attribute-name="backgroundColor"
|
||||
data-no-preview="true"/>
|
||||
<we-colorpicker string="Border"
|
||||
<we-colorpicker string="Border" data-name="chart_border_color_opt"
|
||||
data-color-change=""
|
||||
data-attribute-name="borderColor"
|
||||
data-no-preview="true"/>
|
||||
|
||||
Reference in New Issue
Block a user