diff --git a/addons/mass_mailing/static/src/js/mass_mailing_snippets.js b/addons/mass_mailing/static/src/js/mass_mailing_snippets.js index ad4864d0b07..407416f3547 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_snippets.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_snippets.js @@ -127,7 +127,7 @@ options.registry.mass_mailing_table_item = options.Class.extend({ // Adding compatibility for the outlook compliance of mailings. // Commit of such compatibility : a14f89c8663c9cafecb1cc26918055e023ecbe42 -options.registry.background = options.registry.background.extend({ +options.registry.BackgroundImage = options.registry.BackgroundImage.extend({ start: function () { this._super(); if (this.snippets && this.snippets.split('.')[0] === "mass_mailing") { diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index cc0a154a08d..7ebf45964b5 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -130,6 +130,8 @@ function createPropertyProxy(obj, propertyName, value) { //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +const NULL_ID = '__NULL__'; + /** * Base class for components to be used in snippet options widgets to retrieve * user values. @@ -304,7 +306,7 @@ const UserValueWidget = Widget.extend({ * @returns {boolean} */ isActive: function () { - return !!this._value; + return this._value && this._value !== NULL_ID; }, /** * Indicates if the widget can contain sub user value widgets or not. @@ -719,7 +721,7 @@ const BaseSelectionUserValueWidget = UserValueWidget.extend({ */ setValue(value, methodName) { this._userValueWidgets.forEach(widget => { - widget.setValue('__NULL__', methodName); + widget.setValue(NULL_ID, methodName); }); for (const widget of [...this._userValueWidgets].reverse()) { widget.setValue(value, methodName); @@ -1304,10 +1306,9 @@ const ColorpickerUserValueWidget = SelectUserValueWidget.extend({ }); const ImagepickerUserValueWidget = UserValueWidget.extend({ - tagName: 'we-imagepicker', + tagName: 'we-button', events: { - 'click .o_we_edit_image': '_onEditImage', - 'click .o_we_remove_image': '_onRemoveImage', + 'click': '_onEditImage', }, /** @@ -1315,18 +1316,13 @@ const ImagepickerUserValueWidget = UserValueWidget.extend({ */ start: async function () { await this._super(...arguments); + const allowedSelector = this.el.dataset.allowVideos; this.allowVideos = allowedSelector ? this.$target.is(allowedSelector) : false; - this.editImageButton = document.createElement('we-button'); - this.editImageButton.classList.add('o_we_edit_image', 'fa', 'fa-fw', 'fa-edit'); - - this.removeImageButton = document.createElement('we-button'); - this.removeImageButton.classList.add('o_we_remove_image', 'fa', 'fa-fw', 'fa-times'); - this.removeImageButton.title = _t("Remove"); - - this.containerEl.appendChild(this.editImageButton); - this.containerEl.appendChild(this.removeImageButton); + const iconEl = document.createElement('i'); + iconEl.classList.add('fa', 'fa-fw', 'fa-camera'); + this.containerEl.appendChild(iconEl); }, /** * @override @@ -1342,9 +1338,9 @@ const ImagepickerUserValueWidget = UserValueWidget.extend({ /** * @override */ - _updateUI: async function () { + async _updateUI() { await this._super(...arguments); - this.removeImageButton.classList.toggle('d-none', !this.isActive()); + this.el.classList.toggle('active', this.isActive()); }, //-------------------------------------------------------------------------- @@ -1370,8 +1366,8 @@ const ImagepickerUserValueWidget = UserValueWidget.extend({ noDocuments: true, noVideos: !this.allowVideos, isForBgVideo: true, - res_model: $editable.data('oe-model'), - res_id: $editable.data('oe-id'), + 'res_model': $editable.data('oe-model'), + 'res_id': $editable.data('oe-id'), }, dummyEl).open(); mediaDialog.on('save', this, data => { if (data.bgVideoSrc) { @@ -1387,16 +1383,6 @@ const ImagepickerUserValueWidget = UserValueWidget.extend({ this._onUserValueChange(); }); }, - /** - * Called when the remove background button is clicked. - * - * @private - */ - _onRemoveImage: function (ev) { - this._value = ''; - this.isVideo = false; - this._onUserValueChange(ev); - }, }); const DatetimePickerUserValueWidget = InputUserValueWidget.extend({ @@ -2543,44 +2529,6 @@ const registry = {}; //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -/** - * Marks color levels of any element that may get or has a color classes. This - * is done for the specific main colorpicker option so that those are marked on - * snippet drop (so that base snippet definition do not need to care about that) - * and on first focus (for compatibility). - */ -registry.MainColorpicker = SnippetOptionWidget.extend({ - /** - * @override - */ - start: function () { - this._markColorLevel(); - return this._super(...arguments); - }, - /** - * @override - */ - onBuilt: function () { - this._markColorLevel(); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Adds a specific class indicating the element is colored so that nested - * color classes work (we support one-level). Removing it is not useful, - * technically the class can be added on anything that *may* receive a color - * class: this does not come with any CSS rule. - * - * @private - */ - _markColorLevel: function () { - this.$target.addClass('o_colored_level'); - }, -}); - registry.sizing = SnippetOptionWidget.extend({ /** * @override @@ -2975,7 +2923,9 @@ const ImageHandlerOption = SnippetOptionWidget.extend({ } const dataURL = await applyModifications(img); const weight = dataURL.split(',')[1].length / 4 * 3; - this.$el.find('.o_we_image_weight').text(`${(weight / 1024).toFixed(1)}kb`); + const $weight = this.$el.find('.o_we_image_weight'); + $weight.find('> span').text(`${(weight / 1024).toFixed(1)}kb`); + $weight.removeClass('d-none'); img.classList.add('o_modified_image_to_save'); return loadImage(dataURL, img); }, @@ -3220,7 +3170,7 @@ registry.BackgroundOptimize = ImageHandlerOption.extend({ /** * Handles the edition of snippet's background image. */ -registry.background = SnippetOptionWidget.extend({ +registry.BackgroundImage = SnippetOptionWidget.extend({ /** * @override */ @@ -3648,6 +3598,44 @@ registry.BackgroundPosition = SnippetOptionWidget.extend({ }, }); +/** + * Marks color levels of any element that may get or has a color classes. This + * is done for the specific main colorpicker option so that those are marked on + * snippet drop (so that base snippet definition do not need to care about that) + * and on first focus (for compatibility). + */ +registry.ColoredLevelBackground = registry.BackgroundImage.extend({ + /** + * @override + */ + start: function () { + this._markColorLevel(); + return this._super(...arguments); + }, + /** + * @override + */ + onBuilt: function () { + this._markColorLevel(); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Adds a specific class indicating the element is colored so that nested + * color classes work (we support one-level). Removing it is not useful, + * technically the class can be added on anything that *may* receive a color + * class: this does not come with any CSS rule. + * + * @private + */ + _markColorLevel: function () { + this.$target.addClass('o_colored_level'); + }, +}); + /** * Allows to replace a text value with the name of a database record. * @todo replace this mechanism with real backend m2o field ? @@ -3889,6 +3877,7 @@ return { SnippetOptionWidget: SnippetOptionWidget, snippetOptionRegistry: registry, + NULL_ID: NULL_ID, UserValueWidget: UserValueWidget, userValueWidgetsRegistry: userValueWidgetsRegistry, diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss index 28cff34a1de..c82fb464f6d 100644 --- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss +++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss @@ -841,6 +841,10 @@ body.editor_enable.editor_has_snippets { we-select.o_we_user_value_widget { position: static; } + + &.o_we_full_row > div { + flex: 1 1 auto; + } } %o-we-inline { @@ -871,6 +875,17 @@ body.editor_enable.editor_has_snippets { } } } + + .o_we_image_weight { + margin-top: $o-we-sidebar-content-field-spacing; + text-align: right; + color: $o-we-color-success; + line-height: $o-we-sidebar-content-field-height; + + > .fa { + margin-right: $o-we-sidebar-content-field-label-spacing; + } + } } > .o_we_invisible_el_panel { diff --git a/addons/web_editor/static/tests/test_utils.js b/addons/web_editor/static/tests/test_utils.js index 8ea0f5c70f5..59b1c38e38b 100644 --- a/addons/web_editor/static/tests/test_utils.js +++ b/addons/web_editor/static/tests/test_utils.js @@ -66,7 +66,7 @@ const SNIPPETS_TEMPLATE = `
-
+
Background Image diff --git a/addons/web_editor/views/snippets.xml b/addons/web_editor/views/snippets.xml index 98396f98002..36c47ebd1f7 100644 --- a/addons/web_editor/views/snippets.xml +++ b/addons/web_editor/views/snippets.xml @@ -33,17 +33,75 @@
- diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index 0626c0fba11..e698c503ace 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -511,19 +511,7 @@ options.Class.include({ }, }); -options.registry.BackgroundOptimize.include({ - /** - * @override - */ - _computeVisibility() { - if (this.$target.hasClass('o_background_video')) { - return false; - } - return this._super(...arguments); - }, -}); - -options.registry.background.include({ +options.registry.BackgroundImage.include({ background: async function (previewMode, widgetValue, params) { if (previewMode === 'reset' && this.videoSrc) { return this._setBgVideo(false, this.videoSrc); @@ -591,6 +579,18 @@ options.registry.background.include({ }, }); +options.registry.BackgroundOptimize.include({ + /** + * @override + */ + _computeVisibility() { + if (this.$target.hasClass('o_background_video')) { + return false; + } + return this._super(...arguments); + }, +}); + options.registry.SwitchTheme = options.Class.extend({ isTopOption: true, @@ -679,6 +679,14 @@ options.registry.Theme = options.Class.extend({ await this._reloadBundles(); }, + async enableImagepicker(previewMode, widgetValue, params) { + if (widgetValue) { + // TODO improve: here we make a hack so that a hidden imagepicker + // widget opens... + const widget = this._requestUserValueWidgets(widgetValue)[0]; + widget.$el.click(); + } + }, /** * @see this.selectClass for parameters */ @@ -789,6 +797,15 @@ options.registry.Theme = options.Class.extend({ } return this._super(...arguments); }, + /** + * @override + */ + async _computeWidgetVisibility(widgetName, params) { + if (widgetName === 'body_bg_image_opt') { + return false; + } + return this._super(...arguments); + }, /** * @private * @param {DOMElement} node @@ -1367,7 +1384,7 @@ options.registry.parallax = options.Class.extend({ */ onFocus: function () { this.trigger_up('option_update', { - optionNames: ['background', 'BackgroundPosition'], + optionNames: ['BackgroundImage', 'BackgroundPosition'], name: 'target', data: this.$target.find('> .s_parallax_bg'), }); diff --git a/addons/website/views/snippets/s_badge.xml b/addons/website/views/snippets/s_badge.xml index 8933720e561..614e2afa5f4 100644 --- a/addons/website/views/snippets/s_badge.xml +++ b/addons/website/views/snippets/s_badge.xml @@ -8,7 +8,7 @@ - - - - -