[REF] web_editor, *: use widgets for left panel components
Before this commit, the rendering of inner content of the left panel
options was handled through _buildXXXElement methods in the options
widgets. Now they are handled through sub-widgets which also handle
the user events.
Each sub-widget is notified with the string value it should hold
according to the snippet option target and methods. Each sub-widget
notifies a string value following user interactions, which snippet
options can use to adapt their target through their option methods.
The current sub-widgets introduced by this commit are (others will
follow):
1) we-button
2) we-checkbox
3) we-input
4) we-select (typically containing multiple we-button widgets)
5) we-multi (typically containing multiple we-input widget)
Following this commit only 3 standard option methods remain, which can
be used with any meaningful widget:
1) selectClass
-> Same as before + handle the old 'toggleClass' method when used with
a checkbox-like widget.
2) selectStyle
-> Same as old setStyle, introduced recently (simply renamed)
3) selectDataAttribute
-> Handle both old selectDataAttribute and setDataAttribute, introduced
recently
The parameters these methods received are now:
- previewMode: same as before
- widgetValue: the string value the widget currently holds
- params: additional parameters (mainly dataset of the related xml)
+ params.possibleValues (all the possible values for the method,
meaninful for a select or a checkbox)
+ params.defaultValue (the value that the widget would give if it
even holds no value)
When defining a method in XML, the given value is the default value.
e.g. data-select-style="34px" data-css-property="margin-top"
-> if set on any widget, allows to retrieve a value from the user to
customize the margin-top, the default value being 34px.
-> if used with a we-input, allow to retrieve any value and an empty
value will be equal to 34px
-> if used on a checkbox, allow to toggle between no value and 34px.
-> ...
Hopefully, detailed documentation about all of this will follow. Code
documentation should already be helpful.
Last note: the _updateUI method (replacing the old _setActive) is now
async (so does all the functions calling it). In particular, the _select
method is now async.
Part of https://github.com/odoo/odoo/pull/40282
In preparation of task-2122935 and other tasks
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
odoo.define('web_editor.utils', function (require) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* window.getComputedStyle cannot work properly with CSS shortcuts (like
|
||||
* 'border-width' which is a shortcut for the top + right + bottom + left border
|
||||
* widths. If an option wants to customize such a shortcut, it should be listed
|
||||
* here with the non-shortcuts property it stands for, in order.
|
||||
*
|
||||
* @type {Object<string[]>}
|
||||
*/
|
||||
const CSS_SHORTHANDS = {
|
||||
'border-width': ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],
|
||||
'border-radius': ['border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'],
|
||||
};
|
||||
/**
|
||||
* Key-value mapping to list converters from an unit A to an unit B.
|
||||
* - The key is a string in the format '$1-$2' where $1 is the CSS symbol of
|
||||
* unit A and $2 is the CSS symbol of unit B.
|
||||
* - The value is a function that converts the received value (expressed in
|
||||
* unit A) to another value expressed in unit B. Two other parameters is
|
||||
* received: the css property on which the unit applies and the jQuery element
|
||||
* on which that css property may change.
|
||||
*/
|
||||
const CSS_UNITS_CONVERSION = {
|
||||
's-ms': () => 1000,
|
||||
'ms-s': () => 0.001,
|
||||
'rem-px': () => _computePxByRem(),
|
||||
'px-rem': () => _computePxByRem(true),
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the number of "px" needed to make a "rem" unit. Subsequent calls
|
||||
* returns the cached computed value.
|
||||
*
|
||||
* @param {boolean} [toRem=false]
|
||||
* @returns {float} - number of px by rem if 'toRem' is false
|
||||
* - the inverse otherwise
|
||||
*/
|
||||
function _computePxByRem(toRem) {
|
||||
if (_computePxByRem.PX_BY_REM === undefined) {
|
||||
const htmlStyle = window.getComputedStyle(document.documentElement);
|
||||
_computePxByRem.PX_BY_REM = parseFloat(htmlStyle['font-size']);
|
||||
}
|
||||
return toRem ? (1 / _computePxByRem.PX_BY_REM) : _computePxByRem.PX_BY_REM;
|
||||
}
|
||||
/**
|
||||
* Converts the given (value + unit) string to a numeric value expressed in
|
||||
* the other given css unit.
|
||||
*
|
||||
* e.g. fct('400ms', 's') -> 0.4
|
||||
*
|
||||
* @param {string} value
|
||||
* @param {string} unitTo
|
||||
* @param {string} [cssProp] - the css property on which the unit applies
|
||||
* @param {jQuery} [$target] - the jQuery element on which that css property
|
||||
* may change
|
||||
* @returns {number}
|
||||
*/
|
||||
function _convertValueToUnit(value, unitTo, cssProp, $target) {
|
||||
const m = _getNumericAndUnit(value);
|
||||
if (!m) {
|
||||
return NaN;
|
||||
}
|
||||
const numValue = parseFloat(m[0]);
|
||||
const valueUnit = m[1];
|
||||
return _convertNumericToUnit(numValue, valueUnit, unitTo, cssProp, $target);
|
||||
}
|
||||
/**
|
||||
* Converts the given numeric value expressed in the given css unit into
|
||||
* the corresponding numeric value expressed in the other given css unit.
|
||||
*
|
||||
* e.g. fct(400, 'ms', 's') -> 0.4
|
||||
*
|
||||
* @param {number} value
|
||||
* @param {string} unitFrom
|
||||
* @param {string} unitTo
|
||||
* @param {string} [cssProp] - the css property on which the unit applies
|
||||
* @param {jQuery} [$target] - the jQuery element on which that css property
|
||||
* may change
|
||||
* @returns {number}
|
||||
*/
|
||||
function _convertNumericToUnit(value, unitFrom, unitTo, cssProp, $target) {
|
||||
if (Math.abs(value) < Number.EPSILON || unitFrom === unitTo) {
|
||||
return value;
|
||||
}
|
||||
const converter = CSS_UNITS_CONVERSION[`${unitFrom}-${unitTo}`];
|
||||
if (converter === undefined) {
|
||||
throw new Error(`Cannot convert '${unitFrom}' units into '${unitTo}' units !`);
|
||||
}
|
||||
return value * converter(cssProp, $target);
|
||||
}
|
||||
/**
|
||||
* Returns the numeric value and unit of a css value.
|
||||
*
|
||||
* e.g. fct('400ms') -> [400, 'ms']
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {Array|null}
|
||||
*/
|
||||
function _getNumericAndUnit(value) {
|
||||
const m = value.trim().match(/^([0-9.]+)(\w*)$/);
|
||||
if (!m) {
|
||||
return null;
|
||||
}
|
||||
return [m[1], m[2]];
|
||||
}
|
||||
/**
|
||||
* Checks if two css values are equal.
|
||||
*
|
||||
* @param {string} value1
|
||||
* @param {string} value2
|
||||
* @param {string} [cssProp] - the css property on which the unit applies
|
||||
* @param {jQuery} [$target] - the jQuery element on which that css property
|
||||
* may change
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function _areCssValuesEqual(value1, value2, cssProp, $target) {
|
||||
// String comparison first
|
||||
if (value1 === value2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert the second value in the unit of the first one and compare
|
||||
// floating values
|
||||
const data = _getNumericAndUnit(value1);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
const numValue1 = data[0];
|
||||
const numValue2 = _convertValueToUnit(value2, data[1], cssProp, $target);
|
||||
return (Math.abs(numValue1 - numValue2) < Number.EPSILON);
|
||||
}
|
||||
|
||||
return {
|
||||
CSS_SHORTHANDS: CSS_SHORTHANDS,
|
||||
CSS_UNITS_CONVERSION: CSS_UNITS_CONVERSION,
|
||||
computePxByRem: _computePxByRem,
|
||||
convertValueToUnit: _convertValueToUnit,
|
||||
convertNumericToUnit: _convertNumericToUnit,
|
||||
getNumericAndUnit: _getNumericAndUnit,
|
||||
areCssValuesEqual: _areCssValuesEqual,
|
||||
};
|
||||
});
|
||||
@@ -144,6 +144,15 @@ var SnippetEditor = Widget.extend({
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether the snippet options are shown or not.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
areOptionsShown: function () {
|
||||
const lastIndex = this._customize$Elements.length - 1;
|
||||
return !!this._customize$Elements[lastIndex].parent().length;
|
||||
},
|
||||
/**
|
||||
* Notifies all the associated snippet options that the snippet has just
|
||||
* been dropped in the page.
|
||||
@@ -165,6 +174,17 @@ var SnippetEditor = Widget.extend({
|
||||
option.cleanForSave();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Closes all widgets of all options.
|
||||
*/
|
||||
closeWidgets: function () {
|
||||
if (!this.areOptionsShown()) {
|
||||
return;
|
||||
}
|
||||
Object.keys(this.styles).forEach(key => {
|
||||
this.styles[key].closeWidgets();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Makes the editor overlay cover the associated snippet.
|
||||
*/
|
||||
@@ -288,9 +308,7 @@ var SnippetEditor = Widget.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
var lastIndex = this._customize$Elements.length - 1;
|
||||
var optionsAlreadyShown = !!this._customize$Elements[lastIndex].parent().length;
|
||||
if (optionsAlreadyShown === show) {
|
||||
if (this.areOptionsShown() === show) {
|
||||
return;
|
||||
}
|
||||
this.trigger_up('update_customize_elements', {
|
||||
@@ -666,7 +684,6 @@ var SnippetsMenu = Widget.extend({
|
||||
id: 'oe_snippets',
|
||||
cacheSnippetTemplate: {},
|
||||
events: {
|
||||
'click we-select': '_onOptionTogglerClick',
|
||||
'click .o_install_btn': '_onInstallBtnClick',
|
||||
},
|
||||
custom_events: {
|
||||
@@ -684,6 +701,7 @@ var SnippetsMenu = Widget.extend({
|
||||
'hide_overlay': '_onHideOverlay',
|
||||
'block_preview_overlays': '_onBlockPreviewOverlays',
|
||||
'unblock_preview_overlays': '_onUnblockPreviewOverlays',
|
||||
'user_value_widget_opening': '_onUserValueWidgetOpening',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1668,21 +1686,6 @@ var SnippetsMenu = Widget.extend({
|
||||
_onCleanForSaveDemand: function (ev) {
|
||||
this.cleanForSave();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {*} ev
|
||||
*/
|
||||
_onOptionTogglerClick: function (ev) {
|
||||
var togglerEl = ev.currentTarget;
|
||||
if (togglerEl.tagName !== 'WE-TOGGLER') {
|
||||
togglerEl = togglerEl.querySelector('we-toggler');
|
||||
}
|
||||
|
||||
var $hierarchyTogglers = $(togglerEl).parents('we-select').children('we-toggler');
|
||||
this.$('we-select').children('we-toggler').not($hierarchyTogglers).removeClass('active');
|
||||
$hierarchyTogglers.not(togglerEl).addClass('active');
|
||||
togglerEl.classList.toggle('active');
|
||||
},
|
||||
/**
|
||||
* Called when the overlay dimensions/positions should be recomputed.
|
||||
*
|
||||
@@ -1836,6 +1839,13 @@ var SnippetsMenu = Widget.extend({
|
||||
this.customizePanel.classList.toggle('d-none', !customize);
|
||||
this.$('.o_we_customize_snippet_btn').toggleClass('active', customize);
|
||||
},
|
||||
/**
|
||||
* Called when an user value widget is being opened -> close all the other
|
||||
* user value widgets of all editors.
|
||||
*/
|
||||
_onUserValueWidgetOpening: function () {
|
||||
this.snippetEditors.forEach(editor => editor.closeWidgets());
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -398,6 +398,14 @@ body.editor_enable.editor_has_snippets {
|
||||
}
|
||||
}
|
||||
|
||||
we-row we-input {
|
||||
flex-wrap: wrap;
|
||||
|
||||
> we-title {
|
||||
min-width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
// FIXME temporary fix for m2o option for example
|
||||
position: static !important;
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script type="text/javascript" src="/web_editor/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/common/ace.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/common/utils.js"/>
|
||||
<script type="text/javascript" src="/web_editor/static/src/js/wysiwyg/root.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -881,7 +881,7 @@ registry.gallery = publicWidget.Widget.extend({
|
||||
srcs: urls,
|
||||
index: idx,
|
||||
dim: dimensions,
|
||||
interval: milliseconds,
|
||||
interval: milliseconds || 0,
|
||||
id: _.uniqueId('slideshow_'),
|
||||
}));
|
||||
$modal.modal({
|
||||
|
||||
@@ -19,14 +19,7 @@ options.Class.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setDataAttribute: function (previewMode, dataName, $opt) {
|
||||
this._super(...arguments);
|
||||
this._refreshPublicWidgets();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
selectDataAttribute: function (previewMode, value, $opt) {
|
||||
selectDataAttribute: function (previewMode, widgetValue, params) {
|
||||
this._super(...arguments);
|
||||
this._refreshPublicWidgets();
|
||||
},
|
||||
@@ -86,19 +79,20 @@ options.registry.background.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super(...arguments);
|
||||
if (this._hasBgvideo()) {
|
||||
this.$el.find('[data-choose-image]').addClass('active');
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
if (methodName === 'chooseImage') {
|
||||
return this._hasBgvideo() ? 'true' : '';
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* Updates the background video used by the snippet.
|
||||
*
|
||||
* @private
|
||||
* @see this.selectClass for parameters
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_setBgVideo: function (previewMode, value) {
|
||||
_setBgVideo: async function (previewMode, value) {
|
||||
this.$('> .o_bg_video_container').toggleClass('d-none', previewMode === true);
|
||||
|
||||
if (previewMode !== false) {
|
||||
@@ -113,7 +107,7 @@ options.registry.background.include({
|
||||
delete target.dataset.bgVideoSrc;
|
||||
}
|
||||
this._refreshPublicWidgets();
|
||||
this._updateUI();
|
||||
await this._updateUI();
|
||||
},
|
||||
/**
|
||||
* Returns whether the current target has a background video or not.
|
||||
@@ -142,15 +136,16 @@ options.registry.background.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onSaveMediaDialog: function (data) {
|
||||
_onSaveMediaDialog: async function (data) {
|
||||
if (!data.bgVideoSrc) {
|
||||
this._setBgVideo(false);
|
||||
this._super(...arguments);
|
||||
return;
|
||||
const _super = this._super.bind(this);
|
||||
const args = arguments;
|
||||
await this._setBgVideo(false);
|
||||
return _super(...args);
|
||||
}
|
||||
// if the user chose a video, only add the video without removing the
|
||||
// background
|
||||
this._setBgVideo(false, data.bgVideoSrc);
|
||||
await this._setBgVideo(false, data.bgVideoSrc);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -416,8 +411,8 @@ options.registry.CarouselItem = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
slide: function (previewMode, value) {
|
||||
switch (value) {
|
||||
slide: function (previewMode, widgetValue, params) {
|
||||
switch (widgetValue) {
|
||||
case 'left':
|
||||
this.$controls.filter('.carousel-control-prev')[0].click();
|
||||
break;
|
||||
@@ -458,7 +453,7 @@ options.registry.navTabs = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
addTab: function (previewMode, value, $opt) {
|
||||
addTab: function (previewMode, widgetValue, params) {
|
||||
var $activeItem = this.$navLinks.filter('.active').parent();
|
||||
var $activePane = this.$tabPanes.filter('.active');
|
||||
|
||||
@@ -477,7 +472,7 @@ options.registry.navTabs = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
removeTab: function (previewMode, value, $opt) {
|
||||
removeTab: function (previewMode, widgetValue, params) {
|
||||
var self = this;
|
||||
|
||||
var $activeLink = this.$navLinks.filter('.active');
|
||||
@@ -531,8 +526,8 @@ options.registry.navTabs = options.Class.extend({
|
||||
* @private
|
||||
* @override
|
||||
*/
|
||||
_updateUI: function () {
|
||||
this._super.apply(this, arguments);
|
||||
_updateUI: async function () {
|
||||
await this._super.apply(this, arguments);
|
||||
this.$el.filter('[data-remove-tab]').toggleClass('d-none', this.$tabPanes.length <= 2);
|
||||
},
|
||||
});
|
||||
@@ -607,14 +602,24 @@ options.registry.layout_column = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
selectCount: function (previewMode, value, $opt) {
|
||||
this._updateColumnCount(value - this.$target.children().length);
|
||||
selectCount: function (previewMode, widgetValue, params) {
|
||||
const nbColumns = parseInt(widgetValue);
|
||||
this._updateColumnCount(nbColumns - this.$target.children().length);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
if (methodName === 'selectCount') {
|
||||
return '' + this.$target.children().length;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* Adds new columns which are clones of the last column or removes the
|
||||
* last x columns.
|
||||
@@ -664,14 +669,6 @@ options.registry.layout_column = options.Class.extend({
|
||||
$columns.first().addClass('offset-lg-' + colOffset);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.$el.find('[data-select-count]').removeClass('active')
|
||||
.filter('[data-select-count=' + this.$target.children().length + ']').addClass('active');
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.parallax = options.Class.extend({
|
||||
@@ -931,7 +928,7 @@ options.registry.ul = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
toggleClass: function () {
|
||||
selectClass: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.trigger_up('widgets_stop_request', {
|
||||
@@ -1121,8 +1118,8 @@ options.registry.gallery = options.Class.extend({
|
||||
'data-index': ++index,
|
||||
}).appendTo($container);
|
||||
}
|
||||
self._reset();
|
||||
self.trigger_up('cover_update');
|
||||
this.mode('reset', this.getMode());
|
||||
this.trigger_up('cover_update');
|
||||
this._updateUI();
|
||||
});
|
||||
dialog.open();
|
||||
@@ -1133,11 +1130,11 @@ options.registry.gallery = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
columns: function (previewMode, value) {
|
||||
this.$target.attr('data-columns', value);
|
||||
columns: function (previewMode, widgetValue, params) {
|
||||
const nbColumns = parseInt(widgetValue || '1');
|
||||
this.$target.attr('data-columns', nbColumns);
|
||||
|
||||
var $activeMode = this.$el.find('.active[data-mode]');
|
||||
this.mode(null, $activeMode.data('mode'), $activeMode);
|
||||
this.mode(previewMode, this.getMode(), {}); // TODO improve
|
||||
},
|
||||
/**
|
||||
* Get the image target's layout mode (slideshow, masonry, grid or nomode).
|
||||
@@ -1182,8 +1179,8 @@ options.registry.gallery = options.Class.extend({
|
||||
* Allows to changes the interval of automatic slideshow (not active in
|
||||
* edit mode).
|
||||
*/
|
||||
interval: function (previewMode, value) {
|
||||
this.$target.find('.carousel:first').attr('data-interval', value);
|
||||
interval: function (previewMode, widgetValue) {
|
||||
this.$target.find('.carousel:first').attr('data-interval', widgetValue || '0');
|
||||
},
|
||||
/**
|
||||
* Displays the images with the "masonry" layout.
|
||||
@@ -1226,12 +1223,13 @@ options.registry.gallery = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
mode: function (previewMode, value, $opt) {
|
||||
mode: function (previewMode, widgetValue, params) {
|
||||
widgetValue = widgetValue || 'slideshow'; // FIXME should not be needed
|
||||
this.$target.css('height', '');
|
||||
this[value]();
|
||||
this[widgetValue]();
|
||||
this.$target
|
||||
.removeClass('o_nomode o_masonry o_grid o_slideshow')
|
||||
.addClass('o_' + value);
|
||||
.addClass('o_' + widgetValue);
|
||||
this.trigger_up('cover_update');
|
||||
},
|
||||
/**
|
||||
@@ -1322,11 +1320,11 @@ options.registry.gallery = options.Class.extend({
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
notify: function (name, data) {
|
||||
this._super.apply(this, arguments);
|
||||
notify: async function (name, data) {
|
||||
await this._super(...arguments);
|
||||
if (name === 'image_removed') {
|
||||
data.$image.remove(); // Force the removal of the image before reset
|
||||
this._reset();
|
||||
this.mode('reset', this.getMode());
|
||||
} else if (name === 'image_index_request') {
|
||||
var imgs = this._getImages();
|
||||
var position = _.indexOf(imgs, data.$image[0]);
|
||||
@@ -1351,7 +1349,7 @@ options.registry.gallery = options.Class.extend({
|
||||
// indexes were not the same as positions.
|
||||
$(img).attr('data-index', index);
|
||||
});
|
||||
this._reset();
|
||||
this.mode('reset', this.getMode());
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1359,6 +1357,44 @@ options.registry.gallery = options.Class.extend({
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
switch (methodName) {
|
||||
case 'mode': {
|
||||
let activeModeName = 'slideshow';
|
||||
for (const modeName of params.possibleValues) {
|
||||
if (this.$target.hasClass(`o_${modeName}`)) {
|
||||
activeModeName = modeName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.activeMode = activeModeName;
|
||||
return activeModeName;
|
||||
}
|
||||
case 'interval': {
|
||||
const carousel = this.$target[0].querySelector('.carousel');
|
||||
return (carousel && carousel.dataset.interval || '0');
|
||||
}
|
||||
case 'columns': {
|
||||
return `${this._getColumns()}`;
|
||||
}
|
||||
case 'styling': {
|
||||
const img = this.$target[0].querySelector('img');
|
||||
if (!img) {
|
||||
return '';
|
||||
}
|
||||
for (const className of params.possibleValues) {
|
||||
if (className && img.classList.contains(className)) {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* Returns the images, sorted by index.
|
||||
*
|
||||
@@ -1407,57 +1443,17 @@ options.registry.gallery = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super(...arguments);
|
||||
_updateUI: async function () {
|
||||
await this._super(...arguments);
|
||||
|
||||
var activeModeSelectors = [];
|
||||
for (const className of this.$target[0].classList) {
|
||||
if (className.startsWith('o_')) {
|
||||
activeModeSelectors.push('[data-mode="' + className.substring(2) + '"]');
|
||||
}
|
||||
}
|
||||
var activeMode = this.$el.find('[data-mode]')
|
||||
.removeClass('active')
|
||||
.filter(activeModeSelectors.join(', '))
|
||||
.addClass('active')
|
||||
.data('mode');
|
||||
this.$el.find('[data-interval]').closest('we-select')[0]
|
||||
.classList.toggle('d-none', this.activeMode !== 'slideshow');
|
||||
|
||||
var carousel = this.$target[0].querySelector('.carousel');
|
||||
var activeInterval = carousel ? (carousel.dataset.interval || 0) : undefined;
|
||||
var $intervalOptions = this.$el.find('[data-interval]');
|
||||
$intervalOptions.removeClass('active')
|
||||
.filter('[data-interval="' + activeInterval + '"]')
|
||||
.addClass('active');
|
||||
$intervalOptions.closest('we-select')[0]
|
||||
.classList.toggle('d-none', activeMode !== 'slideshow');
|
||||
|
||||
var columns = this._getColumns();
|
||||
var $columnOptions = this.$el.find('[data-columns]');
|
||||
$columnOptions.removeClass('active')
|
||||
.filter('[data-columns="' + columns + '"]')
|
||||
.addClass('active');
|
||||
$columnOptions.closest('we-select')[0]
|
||||
.classList.toggle('d-none', !(activeMode === 'grid' || activeMode === 'masonry'));
|
||||
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', activeMode === 'slideshow');
|
||||
|
||||
var $stylingOptions = this.$el.find('[data-styling]');
|
||||
$stylingOptions.removeClass('active');
|
||||
var img = this.$target[0].querySelector('img');
|
||||
var activeStyleSelectors = [];
|
||||
if (img) {
|
||||
for (const className of img.classList) {
|
||||
activeStyleSelectors.push('[data-styling="' + className + '"]');
|
||||
}
|
||||
}
|
||||
var $toEnable = activeStyleSelectors.length
|
||||
? $stylingOptions.filter(activeStyleSelectors.join(', '))
|
||||
: null;
|
||||
if (!$toEnable || !$toEnable.length) {
|
||||
$toEnable = $stylingOptions.first();
|
||||
}
|
||||
$toEnable.addClass('active');
|
||||
.classList.toggle('d-none', this.activeMode === 'slideshow');
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1486,14 +1482,14 @@ options.registry.gallery_img = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
position: function (previewMode, value) {
|
||||
position: function (previewMode, widgetValue, params) {
|
||||
this.trigger_up('deactivate_snippet');
|
||||
this.trigger_up('option_update', {
|
||||
optionName: 'gallery',
|
||||
name: 'image_index_request',
|
||||
data: {
|
||||
$image: this.$target,
|
||||
position: value,
|
||||
position: widgetValue,
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -1510,7 +1506,7 @@ options.registry.topMenuTransparency = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for params
|
||||
*/
|
||||
transparent: function (previewMode, value, $opt) {
|
||||
transparent: function (previewMode, widgetValue, params) {
|
||||
var self = this;
|
||||
this.trigger_up('action_demand', {
|
||||
actionName: 'toggle_page_option',
|
||||
@@ -1531,16 +1527,17 @@ options.registry.topMenuTransparency = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.trigger_up('action_demand', {
|
||||
actionName: 'get_page_option',
|
||||
params: ['header_overlay'],
|
||||
onSuccess: value => {
|
||||
this.$el.find('[data-transparent]').toggleClass('active', !!value);
|
||||
},
|
||||
});
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
if (methodName === 'transparent') {
|
||||
return new Promise(resolve => {
|
||||
this.trigger_up('action_demand', {
|
||||
actionName: 'get_page_option',
|
||||
params: ['header_overlay'],
|
||||
onSuccess: v => resolve(v ? 'true' : ''),
|
||||
});
|
||||
});
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1630,7 +1627,7 @@ options.registry.anchor = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
openAnchorDialog: function (previewMode, value, $opt) {
|
||||
openAnchorDialog: function (previewMode, widgetValue, params) {
|
||||
var self = this;
|
||||
var buttons = [{
|
||||
text: _t("Save & copy"),
|
||||
@@ -1757,14 +1754,14 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
clear: function (previewMode, value, $opt) {
|
||||
this.selectClass(previewMode, '', $());
|
||||
clear: function (previewMode, widgetValue, params) {
|
||||
this.$target.removeClass('o_record_has_cover');
|
||||
this.$image.css('background-image', '');
|
||||
},
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
change: function (previewMode, value, $opt) {
|
||||
change: function (previewMode, widgetValue, params) {
|
||||
var $image = $('<img/>');
|
||||
var background = this.$image.css('background-image');
|
||||
if (background && background !== 'none') {
|
||||
@@ -1780,8 +1777,7 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
var src = image.src;
|
||||
this.$image.css('background-image', src ? ('url(' + src + ')') : '');
|
||||
if (!this.$target.hasClass('o_record_has_cover')) {
|
||||
var $opt = this.$el.find('.o_record_cover_opt_size_default[data-select-class]');
|
||||
this.selectClass(previewMode, $opt.data('selectClass'), $opt);
|
||||
this.$el.find('.o_record_cover_opt_size_default[data-select-class]').click();
|
||||
}
|
||||
this._updateUI();
|
||||
});
|
||||
@@ -1789,16 +1785,16 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
filterValue: function (previewMode, value, $opt) {
|
||||
this.$filter.css('opacity', value);
|
||||
filterValue: function (previewMode, widgetValue, params) {
|
||||
this.$filter.css('opacity', widgetValue || 0);
|
||||
},
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
filterColor: function (previewMode, value, $opt) {
|
||||
filterColor: function (previewMode, widgetValue, params) {
|
||||
this.$filter.removeClass(this.filterColorClasses);
|
||||
if (value) {
|
||||
this.$filter.addClass(value);
|
||||
if (widgetValue) {
|
||||
this.$filter.addClass(widgetValue);
|
||||
}
|
||||
|
||||
var $firstVisibleFilterOpt = this.$filterValueOpts.eq(1);
|
||||
@@ -1812,12 +1808,32 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super.apply(this, arguments);
|
||||
_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 () {
|
||||
await this._super(...arguments);
|
||||
|
||||
// Only show options which are useful to the current cover
|
||||
_.each(this.$el.children(), el => {
|
||||
var $el = $(el);
|
||||
|
||||
@@ -1834,27 +1850,14 @@ options.registry.CoverProperties = options.Class.extend({
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.find('[data-clear]').toggleClass('d-none', !this.$target.hasClass('o_record_has_cover'));
|
||||
|
||||
this.$filterValueOpts.removeClass('active');
|
||||
this.$filterColorOpts.removeClass('active');
|
||||
|
||||
var activeFilterValue = this.$filterValueOpts
|
||||
.filter((i, el) => {
|
||||
return (parseFloat($(el).data('filterValue')).toFixed(1) === parseFloat(this.$filter.css('opacity')).toFixed(1));
|
||||
}).addClass('active').data('filterValue');
|
||||
|
||||
var activeFilterColor = this.$filterColorOpts
|
||||
.filter((i, el) => {
|
||||
return this.$filter.hasClass($(el).data('filterColor'));
|
||||
}).addClass('active').data('filterColor');
|
||||
|
||||
// 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.filterValue = activeFilterValue || 0.0;
|
||||
this.$target[0].dataset.filterColor = activeFilterColor || '';
|
||||
this.$target[0].dataset.filterValue = this.$filterValueOpts.filter('.active').data('filterValue') || 0.0;
|
||||
this.$target[0].dataset.filterColor = this.$filterColorOpts.filter('.active').data('filterColor') || '';
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1878,35 +1881,6 @@ options.registry.SectionStretch = options.Class.extend({
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Options
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
toggleContainerFluid: function (previewMode, value, $opt) {
|
||||
var isFluid = this.$el.find('[data-toggle-container-fluid]').hasClass('active');
|
||||
this.$target.toggleClass('container', !isFluid)
|
||||
.toggleClass('container-fluid', isFluid);
|
||||
if (previewMode !== 'reset') {
|
||||
this.$target.toggleClass('container').toggleClass('container-fluid');
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super.apply(this, arguments);
|
||||
var isFluid = this.$target.hasClass('container-fluid');
|
||||
this.$el.find('[data-toggle-container-fluid]').toggleClass('active', isFluid);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -1922,10 +1896,6 @@ options.registry.SnippetMove = options.Class.extend({
|
||||
$overlayArea.prepend($buttons[0]);
|
||||
$overlayArea.append($buttons[1]);
|
||||
|
||||
// TODO this is kinda hack but not worth a complex system while no
|
||||
// other use case is implemented.
|
||||
$buttons.on('click', this._onOptionSelection.bind(this));
|
||||
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
@@ -1938,8 +1908,8 @@ options.registry.SnippetMove = options.Class.extend({
|
||||
*
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
moveSnippet: function (previewMode, value, $opt) {
|
||||
switch (value) {
|
||||
moveSnippet: function (previewMode, widgetValue, params) {
|
||||
switch (widgetValue) {
|
||||
case 'prev':
|
||||
this.$target.prev().before(this.$target);
|
||||
break;
|
||||
|
||||
@@ -1108,7 +1108,7 @@
|
||||
<we-divider/>
|
||||
<we-button data-select-class="s_carousel_default">Default</we-button>
|
||||
</we-select>
|
||||
<we-input string="Speed" data-set-data-attribute="interval" data-unit="s" data-save-unit="ms" data-step="0.1"/>
|
||||
<we-input string="Speed" data-select-data-attribute="0s" data-attribute-name="interval" data-unit="s" data-save-unit="ms" data-step="0.1"/>
|
||||
</div>
|
||||
|
||||
<div data-js="CarouselItem"
|
||||
@@ -1238,8 +1238,10 @@
|
||||
<we-button class="fas fa-fw fa-expand-arrows-alt" title="Background Position"
|
||||
data-background-position-overlay="true" data-no-preview="true"/>
|
||||
</we-row>
|
||||
<we-input string="Background width" data-set-style="background-size" data-default-value="auto"/>
|
||||
<we-input string="Background height" data-set-style="background-size" data-default-value="auto"/>
|
||||
<we-multi data-css-property="background-size">
|
||||
<we-input string="Width" data-select-style="auto" data-unit="px"/>
|
||||
<we-input string="Height" data-select-style="auto" data-unit="px"/>
|
||||
</we-multi>
|
||||
</div>
|
||||
|
||||
<!-- Parallax -->
|
||||
@@ -1275,9 +1277,9 @@
|
||||
|
||||
<div data-selector="section .row > div"
|
||||
data-exclude=".s_col_no_bgcolor, .s_col_no_bgcolor.row > div, .o_gallery .row > div">
|
||||
<we-input string="Border" data-set-style="border-width" data-extra-class="border"/>
|
||||
<we-input string="Rounded" data-set-style="border-radius" data-extra-class="rounded"/>
|
||||
<we-checkbox string="Shadow" data-toggle-class="shadow"/>
|
||||
<we-input string="Border" data-select-style="0" data-css-property="border-width" data-unit="px" data-extra-class="border"/>
|
||||
<we-input string="Rounded" data-select-style="0" data-css-property="border-radius" data-unit="px" data-extra-class="rounded"/>
|
||||
<we-checkbox string="Shadow" data-select-class="shadow"/>
|
||||
</div>
|
||||
|
||||
<div data-js="sizing_y"
|
||||
@@ -1301,7 +1303,7 @@
|
||||
|
||||
<div data-js="ul"
|
||||
data-selector=":not(li) > ul:has(ul,ol), :not(li) > ol:has(ul,ol)">
|
||||
<we-checkbox string="Folded list" data-toggle-class="o_ul_folded"/>
|
||||
<we-checkbox string="Folded list" data-select-class="o_ul_folded"/>
|
||||
</div>
|
||||
|
||||
<div data-js="menu_data"
|
||||
@@ -1391,7 +1393,7 @@
|
||||
<div data-js="SectionStretch"
|
||||
data-selector="section"
|
||||
data-target="> .container, > .container-fluid">
|
||||
<we-checkbox string="Stretch" data-toggle-container-fluid=""/>
|
||||
<we-checkbox string="Stretch" data-select-class="container|container-fluid" data-no-preview="true"/>
|
||||
</div>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -5,44 +5,6 @@ var sOptions = require('web_editor.snippets.options');
|
||||
var wUtils = require('website.utils');
|
||||
|
||||
sOptions.registry.js_get_posts_selectBlog = sOptions.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var def = this._rpc({
|
||||
model: 'blog.blog',
|
||||
method: 'search_read',
|
||||
args: [wUtils.websiteDomain(this), ['name']],
|
||||
}).then(blogs => {
|
||||
var allBlogsEl = this.el.querySelector('[data-filter-by-blog-id="0"]');
|
||||
var menuEl = allBlogsEl.parentNode;
|
||||
for (const blog of blogs) {
|
||||
let el = allBlogsEl.cloneNode();
|
||||
el.dataset.filterByBlogId = blog.id;
|
||||
el.textContent = blog.name;
|
||||
menuEl.appendChild(el);
|
||||
}
|
||||
this._updateUI();
|
||||
});
|
||||
|
||||
return Promise.all([this._super.apply(this, arguments), def]);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Options
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
filterByBlogId: function (previewMode, value, $opt) {
|
||||
value = parseInt(value);
|
||||
this.$target.attr('data-filter-by-blog-id', value).data('filterByBlogId', value);
|
||||
this.trigger_up('widgets_start_request', {
|
||||
editableMode: true,
|
||||
$target: this.$target,
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -51,13 +13,21 @@ sOptions.registry.js_get_posts_selectBlog = sOptions.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var activeBlogId = this.$target.data('filterByBlogId') || 0;
|
||||
|
||||
this.$el.find('[data-filter-by-blog-id]').removeClass('active');
|
||||
this.$el.find('[data-filter-by-blog-id=' + activeBlogId + ']').addClass('active');
|
||||
_renderCustomXML: function (uiFragment) {
|
||||
return this._rpc({
|
||||
model: 'blog.blog',
|
||||
method: 'search_read',
|
||||
args: [wUtils.websiteDomain(this), ['name']],
|
||||
}).then(blogs => {
|
||||
const menuEl = uiFragment.querySelector('[name="blog_selection"]');
|
||||
for (const blog of blogs) {
|
||||
const el = document.createElement('we-button');
|
||||
el.dataset.selectDataAttribute = blog.id;
|
||||
el.textContent = blog.name;
|
||||
menuEl.appendChild(el);
|
||||
}
|
||||
return this._updateUI();
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -111,8 +111,8 @@ options.registry.CoverProperties.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateUI: function () {
|
||||
this._super(...arguments);
|
||||
_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"]');
|
||||
var $coverMid = this.$el.find('[data-select-class*="cover_mid"]');
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
<template id="s_latest_posts_options" inherit_id="website.snippet_options">
|
||||
<xpath expr="//div[@data-js='background']" position="before">
|
||||
<div data-js="js_get_posts_selectBlog" data-selector=".s_latest_posts, .s_latest_posts_big_picture" data-target=".js_get_posts" data-no-check="true">
|
||||
<we-select string="Choose a blog" data-no-preview="true">
|
||||
<we-button data-filter-by-blog-id="0">All blogs</we-button>
|
||||
<we-select string="Choose a blog" data-no-preview="true" name="blog_selection" data-attribute-name="filterByBlogId">
|
||||
<we-button data-select-data-attribute="0">All blogs</we-button>
|
||||
<!-- the blog list will be generated in js -->
|
||||
</we-select>
|
||||
</div>
|
||||
|
||||
@@ -91,7 +91,7 @@ odoo.define('website_form_editor', function (require) {
|
||||
},
|
||||
|
||||
// Choose a model modal
|
||||
website_form_model_modal: function (previewMode, value, $li) {
|
||||
website_form_model_modal: function (previewMode, widgetValue, params) {
|
||||
var self = this;
|
||||
this._rpc({
|
||||
model: "ir.model",
|
||||
@@ -224,7 +224,7 @@ odoo.define('website_form_editor', function (require) {
|
||||
},
|
||||
|
||||
// Choose a field modal
|
||||
website_form_field_modal: function (previewMode, value, $li) {
|
||||
website_form_field_modal: function (previewMode, widgetValue, params) {
|
||||
var self = this;
|
||||
|
||||
this.fields().then(function (fields) {
|
||||
@@ -261,13 +261,13 @@ odoo.define('website_form_editor', function (require) {
|
||||
},
|
||||
|
||||
// Create a custom field
|
||||
website_form_custom_field: function (previewMode, value, $li) {
|
||||
var default_field_name = 'Custom ' + $li.text();
|
||||
website_form_custom_field: function (previewMode, widgetValue, params) {
|
||||
var default_field_name = 'Custom ' + this.el.querySelector(`[data-website_form_custom_field="${widgetValue}"]`).textContent; // TODO improve
|
||||
this.append_field({
|
||||
name: default_field_name,
|
||||
string: default_field_name,
|
||||
custom: true,
|
||||
type: value,
|
||||
type: widgetValue,
|
||||
// Default values for x2many fields
|
||||
records: [
|
||||
{
|
||||
@@ -302,7 +302,7 @@ odoo.define('website_form_editor', function (require) {
|
||||
},
|
||||
|
||||
// Re-render the field and replace the current one
|
||||
// website_form_editor_field_reset: function(previewMode, value, $li) {
|
||||
// website_form_editor_field_reset: function(previewMode, widgetValue, params) {
|
||||
// var self = this;
|
||||
// var target_field_name = this.$target.find('.col-form-label').attr('for');
|
||||
// this.fields().then(function(fields){
|
||||
@@ -476,9 +476,9 @@ odoo.define('website_form_editor', function (require) {
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
website_form_choice_field_display: function (previewMode, value, $opt) {
|
||||
this.$target.toggleClass('o_website_form_flex_fw', value === 'vertical');
|
||||
this.$target[0].dataset.display = value;
|
||||
website_form_choice_field_display: function (previewMode, widgetValue, params) {
|
||||
this.$target.toggleClass('o_website_form_flex_fw', widgetValue === 'vertical');
|
||||
this.$target[0].dataset.display = widgetValue;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -488,12 +488,11 @@ odoo.define('website_form_editor', function (require) {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super(...arguments);
|
||||
this.$el.find('[data-website_form_choice_field_display]')
|
||||
.removeClass('active')
|
||||
.filter('[data-website_form_choice_field_display=' + this.$target.attr('data-display') + ']')
|
||||
.addClass('active');
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
if (methodName === 'website_form_choice_field_display') {
|
||||
return this.$target.attr('data-display');
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -502,7 +501,7 @@ odoo.define('website_form_editor', function (require) {
|
||||
xmlDependencies: ['/website_form/static/src/xml/website_form_editor.xml'],
|
||||
|
||||
// Option to toggle inputs required attribute
|
||||
website_form_field_require: function (previewMode, value, $li) {
|
||||
website_form_field_require: function (previewMode, widgetValue, params) {
|
||||
this.$target.find('.o_website_form_input').each(function (index, input) {
|
||||
input.required = !input.required;
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ odoo.define('website_form_editor.tour', function(require) {
|
||||
},
|
||||
{
|
||||
content: "Click on Hidden",
|
||||
trigger: "[data-toggle-class='o_website_form_field_hidden']"
|
||||
trigger: "[data-select-class='o_website_form_field_hidden']"
|
||||
},
|
||||
{
|
||||
content: "Check the resulting field",
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<!-- Field -->
|
||||
<div data-js='website_form_editor' data-selector=".form-field" data-drop-near=".form-field">
|
||||
<we-checkbox string="Hidden" data-no-preview="true"
|
||||
data-toggle-class="o_website_form_field_hidden"/>
|
||||
data-select-class="o_website_form_field_hidden"/>
|
||||
</div>
|
||||
|
||||
<!-- Add an option to display radio buttons / checkboxes horizontally or vertically -->
|
||||
@@ -72,7 +72,7 @@
|
||||
<div data-js='website_form_editor_field' data-selector=".form-field:not(.o_website_form_required)">
|
||||
<we-checkbox string="Required" data-no-preview="true"
|
||||
data-website_form_field_require=""
|
||||
data-toggle-class="o_website_form_required_custom"/>
|
||||
data-select-class="o_website_form_required_custom"/>
|
||||
</div>
|
||||
|
||||
<!-- Remove the duplicate options of model fields -->
|
||||
|
||||
@@ -113,8 +113,8 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for params
|
||||
*/
|
||||
setPpg: function (previewMode, value, $opt) {
|
||||
const ppg = parseInt($opt.find('input').val());
|
||||
setPpg: function (previewMode, widgetValue, params) {
|
||||
const ppg = parseInt(widgetValue);
|
||||
if (!ppg || ppg < 1) {
|
||||
return false;
|
||||
}
|
||||
@@ -129,12 +129,12 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for params
|
||||
*/
|
||||
setPpr: function (previewMode, value, $opt) {
|
||||
this.ppr = parseInt(value);
|
||||
setPpr: function (previewMode, widgetValue, params) {
|
||||
this.ppr = parseInt(widgetValue);
|
||||
this._rpc({
|
||||
route: '/shop/change_ppr',
|
||||
params: {
|
||||
'ppr': value,
|
||||
'ppr': this.ppr,
|
||||
},
|
||||
}).then(reload);
|
||||
},
|
||||
@@ -146,18 +146,16 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super(...arguments);
|
||||
|
||||
this.$el.find('[data-set-ppr]')
|
||||
.removeClass('active')
|
||||
.filter((i, el) => {
|
||||
var nbColumns = parseInt($(el).data('setPpr'));
|
||||
return nbColumns === this.ppr;
|
||||
})
|
||||
.addClass('active');
|
||||
|
||||
this.$el.find('[data-set-ppg] > input').val(this.ppg);
|
||||
_computeWidgetState: function (methodName, params) {
|
||||
switch (methodName) {
|
||||
case 'setPpg': {
|
||||
return this.ppg;
|
||||
}
|
||||
case 'setPpr': {
|
||||
return this.ppr;
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -172,29 +170,11 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
willStart: function () {
|
||||
this.ppr = this.$target.closest('[data-ppr]').data('ppr');
|
||||
this.productTemplateID = parseInt(this.$target.find('[data-oe-model="product.template"]').data('oe-id'));
|
||||
|
||||
var defs = [this._super.apply(this, arguments)];
|
||||
|
||||
defs.push(this._rpc({
|
||||
model: 'product.style',
|
||||
method: 'search_read',
|
||||
}).then(data => {
|
||||
var $menu = this.$el.find('[name="style"]');
|
||||
for (var k in data) {
|
||||
$menu.append(this._buildCheckboxElement(data[k]['name'], {
|
||||
dataAttributes: {
|
||||
'style': data[k]['id'],
|
||||
'toggleClass': data[k]['html_class'],
|
||||
},
|
||||
}));
|
||||
}
|
||||
this._updateUI();
|
||||
}));
|
||||
|
||||
return $.when.apply($, defs);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -212,24 +192,24 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for params
|
||||
*/
|
||||
style: function (previewMode, value, $opt) {
|
||||
style: function (previewMode, widgetValue, params) {
|
||||
this._rpc({
|
||||
route: '/shop/change_styles',
|
||||
params: {
|
||||
'id': this.productTemplateID,
|
||||
'style_id': value,
|
||||
'style_id': params.possibleValues[params.possibleValues.length - 1],
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @see this.selectClass for params
|
||||
*/
|
||||
changeSequence: function (previewMode, value, $opt) {
|
||||
changeSequence: function (previewMode, widgetValue, params) {
|
||||
this._rpc({
|
||||
route: '/shop/change_sequence',
|
||||
params: {
|
||||
id: this.productTemplateID,
|
||||
sequence: value,
|
||||
sequence: widgetValue,
|
||||
},
|
||||
}).then(reload);
|
||||
},
|
||||
@@ -241,8 +221,34 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_updateUI: function () {
|
||||
this._super.apply(this, arguments);
|
||||
_renderCustomWidgets: async function (uiFragment) {
|
||||
const checkboxes = [];
|
||||
return this._rpc({
|
||||
model: 'product.style',
|
||||
method: 'search_read',
|
||||
}).then(async data => {
|
||||
for (var k in data) {
|
||||
const checkboxWidget = this._registerUserValueWidget('we-checkbox', this, data[k]['name'], {
|
||||
dataAttributes: {
|
||||
'style': data[k]['id'],
|
||||
'selectClass': data[k]['html_class'],
|
||||
},
|
||||
});
|
||||
checkboxes.push(checkboxWidget);
|
||||
await checkboxWidget.appendTo(document.createDocumentFragment());
|
||||
}
|
||||
}).then(() => {
|
||||
const menuEl = uiFragment.querySelector('[name="style"]');
|
||||
for (const checkbox of checkboxes) {
|
||||
menuEl.appendChild(checkbox.el);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @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);
|
||||
@@ -277,8 +283,8 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({
|
||||
_onTableItemMouseEnter: function (ev) {
|
||||
var $td = $(ev.currentTarget);
|
||||
var $table = $td.closest("table");
|
||||
var x = $td.index()+1;
|
||||
var y = $td.parent().index()+1;
|
||||
var x = $td.index() + 1;
|
||||
var y = $td.parent().index() + 1;
|
||||
|
||||
var tr = [];
|
||||
for (var yi = 0; yi < y; yi++) {
|
||||
@@ -334,7 +340,7 @@ options.registry.ProductsSearchBar = options.Class.extend({
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
openSearchbarSettings: function (previewMode, value, $opt) {
|
||||
openSearchbarSettings: function (previewMode, widgetValue, params) {
|
||||
var self = this;
|
||||
new Dialog(this, {
|
||||
title: _t("Products Search Bar"),
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<div data-js="WebsiteSaleGridLayout"
|
||||
data-selector="#products_grid .o_wsale_products_grid_table_wrapper > table"
|
||||
data-no-check="true">
|
||||
<we-input string="Number of products" data-set-ppg="" data-unit="" data-no-preview="true"/>
|
||||
<we-input string="Number of products" data-set-ppg="" data-no-preview="true"/>
|
||||
<we-select string="Number of Columns" class="o_wsale_ppr_submenu" data-no-preview="true">
|
||||
<we-button data-set-ppr="2">2</we-button>
|
||||
<we-button data-set-ppr="3">3</we-button>
|
||||
|
||||
Reference in New Issue
Block a user