diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index d78b0b5ff0b..b755bc9b014 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -313,7 +313,9 @@ var SnippetEditor = Widget.extend({
if (this.isDestroyed()) {
return;
}
- await this.toggleTargetVisibility(!this.$target.hasClass('o_snippet_invisible'));
+ await this.toggleTargetVisibility(!this.$target.hasClass('o_snippet_invisible')
+ && !this.$target.hasClass('o_snippet_mobile_invisible')
+ && !this.$target.hasClass('o_snippet_desktop_invisible'));
const proms = _.map(this.styles, option => {
return option.cleanForSave();
});
@@ -2162,7 +2164,14 @@ var SnippetsMenu = Widget.extend({
this.invisibleDOMMap = new Map();
const $invisibleDOMPanelEl = $(this.invisibleDOMPanelEl);
$invisibleDOMPanelEl.find('.o_we_invisible_entry').remove();
- const $invisibleSnippets = globalSelector.all().find('.o_snippet_invisible').addBack('.o_snippet_invisible');
+ let isMobile;
+ this.trigger_up('service_context_get', {
+ callback: (ctx) => {
+ isMobile = ctx['isMobile'];
+ },
+ });
+ const invisibleSelector = `.o_snippet_invisible, ${isMobile ? '.o_snippet_mobile_invisible' : '.o_snippet_desktop_invisible'}`;
+ const $invisibleSnippets = globalSelector.all().find(invisibleSelector).addBack(invisibleSelector);
$invisibleDOMPanelEl.toggleClass('d-none', !$invisibleSnippets.length);
@@ -3875,6 +3884,12 @@ var SnippetsMenu = Widget.extend({
for (const gridItemEl of gridItemEls) {
gridUtils._reloadLazyImages(gridItemEl);
}
+ for (const invisibleOverrideEl of this.getEditableArea().find('.o_snippet_mobile_invisible, .o_snippet_desktop_invisible')) {
+ invisibleOverrideEl.classList.remove('o_snippet_override_invisible');
+ invisibleOverrideEl.dataset.invisible = '1';
+ }
+ // This is async but using the main editor mutex.
+ this._updateInvisibleDOM();
},
/**
* Undo..
diff --git a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
index c9714bb94d3..0accc487c96 100644
--- a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
+++ b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
@@ -674,6 +674,17 @@ export class WysiwygAdapterComponent extends ComponentAdapter {
_onContextGet(event) {
return event.data.callback(this._context);
}
+ /**
+ * Retrieves the website service context.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onServiceContextGet(ev) {
+ ev.data.callback({
+ isMobile: this.websiteService.context.isMobile,
+ });
+ }
/**
* Discards changes and reload the iframe.
*
@@ -841,6 +852,7 @@ WysiwygAdapterComponent.prototype.events = {
'gmap_api_key_request': '_onRootEventRequest',
'request_save': '_onSaveRequest',
'context_get': '_onContextGet',
+ 'service_context_get': '_onServiceContextGet',
'action_demand': '_handleAction',
'request_cancel': '_onCancelRequest',
'snippet_will_be_cloned': '_onSnippetWillBeCloned',
diff --git a/addons/website/static/src/img/snippets_options/desktop_invisible.svg b/addons/website/static/src/img/snippets_options/desktop_invisible.svg
new file mode 100644
index 00000000000..f4c01ab7666
--- /dev/null
+++ b/addons/website/static/src/img/snippets_options/desktop_invisible.svg
@@ -0,0 +1,5 @@
+
diff --git a/addons/website/static/src/img/snippets_options/desktop_visible.svg b/addons/website/static/src/img/snippets_options/desktop_visible.svg
new file mode 100644
index 00000000000..3875c52e124
--- /dev/null
+++ b/addons/website/static/src/img/snippets_options/desktop_visible.svg
@@ -0,0 +1,3 @@
+
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index a023efe85e8..2c21bee43fe 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -2294,36 +2294,61 @@ options.registry.topMenuColor = options.Class.extend({
});
/**
- * Manage the visibility of snippets on mobile.
+ * Manage the visibility of snippets on mobile/desktop.
*/
-options.registry.MobileVisibility = options.Class.extend({
- isTopOption: true,
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * @override
- */
- async updateUI() {
- await this._super(...arguments);
- const $button = this.$el.find('we-button');
- $button.attr('title', $button.hasClass('active') ? _t("Visible on mobile") : _t("Hidden on mobile"));
- },
+options.registry.DeviceVisibility = options.Class.extend({
//--------------------------------------------------------------------------
// Options
//--------------------------------------------------------------------------
/**
- * Allows to show or hide the associated snippet in mobile display mode.
+ * Toggles the device visibility.
*
* @see this.selectClass for parameters
*/
- showOnMobile(previewMode, widgetValue, params) {
- const classes = `d-none d-md-${this.$target.css('display')}`;
- this.$target.toggleClass(classes, !widgetValue);
+ async toggleDeviceVisibility(previewMode, widgetValue, params) {
+ this.$target[0].classList.remove('d-none', 'd-md-none',
+ 'o_snippet_mobile_invisible', 'o_snippet_desktop_invisible',
+ 'o_snippet_override_invisible',
+ );
+ const style = getComputedStyle(this.$target[0]);
+ this.$target[0].classList.remove(`d-md-${style['display']}`);
+ if (widgetValue === 'no_desktop') {
+ this.$target[0].classList.add('d-md-none', 'o_snippet_desktop_invisible');
+ } else if (widgetValue === 'no_mobile') {
+ this.$target[0].classList.add(`d-md-${style['display']}`, 'd-none', 'o_snippet_mobile_invisible');
+ }
+
+ // Update invisible elements.
+ let isMobile;
+ this.trigger_up('service_context_get', {
+ callback: (ctx) => {
+ isMobile = ctx['isMobile'];
+ },
+ });
+ this.trigger_up('snippet_option_visibility_update', {show: widgetValue !== (isMobile ? 'no_mobile' : 'no_desktop')});
+ },
+ /**
+ * @override
+ */
+ async onTargetHide() {
+ this.$target[0].classList.remove('o_snippet_override_invisible');
+ },
+ /**
+ * @override
+ */
+ async onTargetShow() {
+ if (this.$target[0].classList.contains('o_snippet_mobile_invisible')
+ || this.$target[0].classList.contains('o_snippet_desktop_invisible')) {
+ this.$target[0].classList.add('o_snippet_override_invisible');
+ }
+ },
+ /**
+ * @override
+ */
+ cleanForSave() {
+ this.$target[0].classList.remove('o_snippet_override_invisible');
},
//--------------------------------------------------------------------------
@@ -2334,10 +2359,16 @@ options.registry.MobileVisibility = options.Class.extend({
* @override
*/
async _computeWidgetState(methodName, params) {
- if (methodName === 'showOnMobile') {
+ if (methodName === 'toggleDeviceVisibility') {
const classList = [...this.$target[0].classList];
- return classList.includes('d-none') &&
- classList.some(className => className.startsWith('d-md-')) ? '' : 'true';
+ if (classList.includes('d-none') &&
+ classList.some(className => className.startsWith('d-md-'))) {
+ return 'no_mobile';
+ }
+ if (classList.includes('d-md-none')) {
+ return 'no_desktop';
+ }
+ return '';
}
return await this._super(...arguments);
},
@@ -2874,7 +2905,9 @@ options.registry.ConditionalVisibility = options.Class.extend({
* @override
*/
async onTargetHide() {
- this.$target[0].classList.add('o_conditional_hidden');
+ if (this.$target[0].classList.contains('o_snippet_invisible')) {
+ this.$target[0].classList.add('o_conditional_hidden');
+ }
},
/**
* @override
@@ -2891,7 +2924,9 @@ options.registry.ConditionalVisibility = options.Class.extend({
// snippet will be shown naturally (as the CSS rules won't apply).
// Without this, the "eye" icon of the visibility panel would be shut
// when entering edit mode.
- this.trigger_up('snippet_option_visibility_update', { show: true });
+ if (this.$target[0].classList.contains('o_snippet_invisible')) {
+ this.trigger_up('snippet_option_visibility_update', { show: true });
+ }
},
//--------------------------------------------------------------------------
diff --git a/addons/website/static/src/scss/website.wysiwyg.scss b/addons/website/static/src/scss/website.wysiwyg.scss
index afa96c78faa..fa2c3c2e13c 100644
--- a/addons/website/static/src/scss/website.wysiwyg.scss
+++ b/addons/website/static/src/scss/website.wysiwyg.scss
@@ -33,6 +33,32 @@ html[data-edit_translations="1"] {
}
}
+.o_snippet_override_invisible {
+ display: block !important;
+ opacity: 70%;
+ position: relative;
+
+ &::before {
+ position: absolute;
+ // Content is 0px wide => use available width.
+ width: -webkit-fill-available;
+ width: -moz-available;
+ right: 20px;
+ z-index: 100;
+ background-color: $o-we-accent;
+ font-size: 0px;
+ content: "."; // Content is mandatory.
+ }
+ &.d-md-none::before {
+ height: 50px;
+ -webkit-mask: url("/website/static/src/img/snippets_options/desktop_invisible.svg") no-repeat 100% 100%;
+ }
+ &:not(.d-md-none)::before {
+ height: 30px;
+ -webkit-mask: url("/website/static/src/img/snippets_options/mobile_invisible.svg") no-repeat 100% 100%;
+ }
+}
+
// SNIPPET PANEL
$i: 1;
@each $font-name, $font-config in $o-theme-font-configs {
@@ -89,7 +115,7 @@ $i: 1;
}
}
- we-button.o_we_mobile {
+ we-button.o_we_device {
> div {
// FIXME maybe there is some refactoring to do so that all SVG icons
// can be properly aligned with extra CSS rules...
@@ -97,21 +123,19 @@ $i: 1;
align-items: center;
svg {
- width: 15px;
- fill: $o-we-color-danger;
+ width: 12px;
+ fill: $o-we-sidebar-content-field-clickable-color;
margin-bottom: 0;
- margin-left: -3px;
&:hover {
- fill: darken($o-we-color-danger, 7.5%);
+ fill: $o-we-sidebar-content-field-pressed-color;
}
}
}
- &.active > div > svg {
- fill: $o-we-sidebar-content-field-color;
-
+ &.active > div svg {
+ fill: $o-we-color-danger;
&:hover {
- fill: $o-we-sidebar-content-field-pressed-color;
+ fill: darken($o-we-color-danger, 7.5%);
}
}
}
diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js
index b4aef8c34e9..a1c91ef48f9 100644
--- a/addons/website/static/src/snippets/s_website_form/options.js
+++ b/addons/website/static/src/snippets/s_website_form/options.js
@@ -1503,8 +1503,8 @@ options.registry.WebsiteFormSubmitRequired = DisableOverlayButtonOption.extend({
}
});
-// Disable "Shown on Mobile" option if for an hidden field
-options.registry.MobileVisibility.include({
+// Disable "Shown on Mobile/Desktop" option if for an hidden field
+options.registry.DeviceVisibility.include({
//--------------------------------------------------------------------------
// Private
diff --git a/addons/website/views/snippets/snippets.xml b/addons/website/views/snippets/snippets.xml
index 936495b1b7b..51bf8a45f4d 100644
--- a/addons/website/views/snippets/snippets.xml
+++ b/addons/website/views/snippets/snippets.xml
@@ -589,14 +589,6 @@