[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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user