[FIX] web_editor, website: add cover/contain option for grid images

As a fix, this introduces a new option for images in grid mode. When an
image is alone in a column which later becomes a grid item, or when an
image is directly added as a grid item via the dedicated option, the
image used `object-fit: cover` so that when the image is resized as a
grid item, it fits the dimensions of the selected grid area.

This confused some users as the image did not appear the same depending
on the screen size. Indeed, if an image is set to take 5 columns as
width and 3 rows as height, it does not ensure the ratio in pixels to
stay the same depending on the screen size: the grid is always made of
12 columns (so the width of each column depends on the screen width),
while the rows are always 50px tall. We could enforce the ratio of grid
items to stay the same or limit the ratio loss by reducing the 50px
height on smaller breakpoints but that would actually increase another
problem: for colored text grid items, their text would more easily
overflow their fixed area.

In the end, using the grid mode is about compromises, even if it allows
to achieve nice things and to have more freedom. This commit although
adds a new option to mitigate the issue for images: users now have the
possibility to switch the default "cover" mode of grid images to
"contain": in that mode, the whole image will always be visible (and
thus keeps the same ratio) and is just "contained" in the grid area
which is selected as dimension.

opw-3069234

closes odoo/odoo#107172

X-original-commit: faf19ef7f87fc043fb9a814516e01b8eeafd9b61
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
qsm-odoo
2022-12-06 13:34:59 +01:00
parent 7f8321aab0
commit dc140ef115
5 changed files with 87 additions and 7 deletions
@@ -1302,7 +1302,7 @@ var SnippetEditor = Widget.extend({
gridUtils._resizeGrid(rowEl);
} else if (this.$target[0].classList.contains('o_grid_item') && this.dropped) {
// Case when dropping a grid item in a non-grid dropzone.
this.$target[0].classList.remove('o_grid_item', 'o_grid_item_image');
this.$target[0].classList.remove('o_grid_item', 'o_grid_item_image', 'o_grid_item_image_contain');
this.$target[0].style.removeProperty('grid-area');
}
@@ -1342,7 +1342,7 @@ var SnippetEditor = Widget.extend({
if (this.$target[0].classList.contains('o_grid_item')) {
// Case when a grid column is dropped near a non-grid
// dropzone.
this.$target[0].classList.remove('o_grid_item', 'o_grid_item_image');
this.$target[0].classList.remove('o_grid_item', 'o_grid_item_image', 'o_grid_item_image_contain');
this.$target[0].style.removeProperty('z-index');
this.$target[0].style.removeProperty('grid-area');
}
@@ -2656,6 +2656,7 @@ var SnippetsMenu = Widget.extend({
_computeSnippetTemplates: function (html) {
var self = this;
var $html = $(html);
this._patchForComputeSnippetTemplates($html);
var $scroll = $html.siblings('#o_scroll');
this.templateOptions = [];
@@ -2800,6 +2801,15 @@ var SnippetsMenu = Widget.extend({
this.$el.addClass('o_loaded');
$(this.el.ownerDocument.body).addClass('editor_has_snippets');
},
/**
* Eases patching the XML definition for snippets and options in stable
* versions. Note: in the future, we will probably move to other ways to
* define snippets and options.
*
* @private
* @param {jQuery}
*/
_patchForComputeSnippetTemplates($html) {},
/**
* Creates a snippet editor to associated to the given snippet. If the given
* snippet already has a linked snippet editor, the function only returns
@@ -5143,7 +5143,7 @@ registry.layout_column = SnippetOptionWidget.extend({
gridUtils._reloadLazyImages(columnEl);
// Removing the grid properties.
columnEl.classList.remove('o_grid_item', 'o_grid_item_image');
columnEl.classList.remove('o_grid_item', 'o_grid_item_image', 'o_grid_item_image_contain');
columnEl.style.removeProperty('grid-area');
columnEl.style.removeProperty('z-index');
}
@@ -57,8 +57,15 @@
--gutter-x: 30px;
}
.o_grid_item_image > img:not([data-shape]) {
width: 100% !important;
height: 100% !important;
object-fit: cover !important;
.o_grid_item_image {
> img {
width: 100% !important;
height: 100% !important;
object-fit: cover !important;
}
&.o_grid_item_image_contain > img,
> img[data-shape] {
object-fit: contain !important;
}
}
@@ -3612,6 +3612,61 @@ options.registry.SwitchableViews = options.Class.extend({
}
});
options.registry.GridImage = options.Class.extend({
//--------------------------------------------------------------------------
// Options
//--------------------------------------------------------------------------
/**
* @see this.selectClass for parameters
*/
changeGridImageMode(previewMode, widgetValue, params) {
const imageGridItemEl = this._getImageGridItem();
if (imageGridItemEl) {
imageGridItemEl.classList.toggle('o_grid_item_image_contain', widgetValue === 'contain');
}
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Returns the parent column if it is marked as a grid item containing an
* image.
*
* @returns {?HTMLElement}
*/
_getImageGridItem() {
const parentEl = this.$target[0].parentNode;
if (parentEl && parentEl.classList.contains('o_grid_item_image')) {
return parentEl;
}
return null;
},
/**
* @override
*/
_computeVisibility() {
return this._super(...arguments)
&& !!this._getImageGridItem()
&& !('shape' in this.$target[0].dataset);
},
/**
* @override
*/
_computeWidgetState(methodName, params) {
if (methodName === 'changeGridImageMode') {
const imageGridItemEl = this._getImageGridItem();
return imageGridItemEl && imageGridItemEl.classList.contains('o_grid_item_image_contain')
? 'contain'
: 'cover';
}
return this._super(...arguments);
},
});
return {
UrlPickerUserValueWidget: UrlPickerUserValueWidget,
FontFamilyPickerUserValueWidget: FontFamilyPickerUserValueWidget,
@@ -1346,6 +1346,14 @@
</we-select>
</div>
<!-- Image position in grid -->
<div data-js="GridImage" data-selector="img">
<we-select string="Position">
<we-button data-change-grid-image-mode="cover">Cover</we-button>
<we-button data-change-grid-image-mode="contain">Contain</we-button>
</we-select>
</div>
<!-- Website Animate -->
<div data-js="WebsiteAnimate"
data-selector=".o_animable, section .row > div, img, .fa, .btn, .o_animated_text"