[IMP] web_editor: replace Width option by Format option

Before this commit two options of images are named "Width":
- the one that adjusts the image resolution
- the one that changes the `width` CSS property

This commit makes the following changes:
- the option about the image resolution is renamed to "Format"
- the "Format" option combines the image resolution and the file format
- an option item gives access to the initially uploaded file format and
resolution
- for all sizes the image format is set to `image/webp`
- for the uploaded size, an `image/webp` version of it is available too
- upon upload, the default selection is an `image/webp` version of the
image

task-2774352

Part-of: odoo/odoo#85494
This commit is contained in:
Benoit Socias
2023-07-15 05:10:45 +02:00
parent d1292a96a6
commit 4d3819b221
3 changed files with 44 additions and 22 deletions
@@ -5677,8 +5677,11 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
/**
* @see this.selectClass for parameters
*/
selectWidth(previewMode, widgetValue, params) {
this._getImg().dataset.resizeWidth = widgetValue;
selectFormat(previewMode, widgetValue, params) {
const values = widgetValue.split(' ');
const image = this._getImg();
image.dataset.resizeWidth = values[0];
image.dataset.mimetype = values[1];
return this._applyOptions();
},
/**
@@ -5747,8 +5750,8 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
});
switch (methodName) {
case 'selectWidth':
return img.naturalWidth;
case 'selectFormat':
return img.naturalWidth + ' ' + this._getImageMimetype(img);
case 'setFilter':
return img.dataset.filter;
case 'glFilter':
@@ -5776,9 +5779,9 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
if (!this.originalSrc || !this._isImageSupportedForProcessing(img)) {
return;
}
const $select = $(uiFragment).find('we-select[data-name=width_select_opt]');
(await this._computeAvailableWidths()).forEach(([value, label]) => {
$select.append(`<we-button data-select-width="${value}">${label}</we-button>`);
const $select = $(uiFragment).find('we-select[data-name=format_select_opt]');
(await this._computeAvailableFormats()).forEach(([value, [label, targetFormat]]) => {
$select.append(`<we-button data-select-format="${Math.round(value)} ${targetFormat}" class="o_we_badge_at_end">${label} <span class="badge rounded-pill text-bg-dark">${targetFormat.split('/')[1]}</span></we-button>`);
});
if (!['image/jpeg', 'image/webp'].includes(this._getImageMimetype(img))) {
@@ -5789,26 +5792,31 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
}
},
/**
* Returns a list of valid widths for a given image.
* Returns a list of valid formats for a given image.
*
* @private
*/
async _computeAvailableWidths() {
async _computeAvailableFormats() {
const img = this._getImg();
const original = await loadImage(this.originalSrc);
const maxWidth = img.dataset.width ? img.naturalWidth : original.naturalWidth;
const optimizedWidth = Math.min(maxWidth, this._computeMaxDisplayWidth());
this.optimizedWidth = optimizedWidth;
const widths = {
128: '128px',
256: '256px',
512: '512px',
1024: '1024px',
1920: '1920px',
128: ['128px', 'image/webp'],
256: ['256px', 'image/webp'],
512: ['512px', 'image/webp'],
1024: ['1024px', 'image/webp'],
1920: ['1920px', 'image/webp'],
};
widths[img.naturalWidth] = sprintf(_t("%spx"), img.naturalWidth);
widths[optimizedWidth] = sprintf(_t("%spx (Suggested)"), optimizedWidth);
widths[maxWidth] = sprintf(_t("%spx (Original)"), maxWidth);
widths[img.naturalWidth] = [sprintf(_t("%spx"), img.naturalWidth), 'image/webp'];
widths[optimizedWidth] = [sprintf(_t("%spx (Suggested)"), optimizedWidth), 'image/webp'];
widths[maxWidth] = [sprintf(_t("%spx (Original)"), maxWidth), img.dataset.originalMimetype];
if (img.dataset.originalMimetype !== 'image/webp') {
// Avoid a key collision by subtracting 0.1 - putting the webp
// above the original format one of the same size.
widths[maxWidth - 0.1] = [sprintf(_t("%spx"), maxWidth), 'image/webp'];
}
return Object.entries(widths)
.filter(([width]) => width <= maxWidth)
.sort(([v1], [v2]) => v1 - v2);
@@ -5863,6 +5871,9 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
}
this.originalId = img.dataset.originalId;
this.originalSrc = img.dataset.originalSrc;
if (!img.dataset.originalMimetype) {
img.dataset.originalMimetype = img.dataset.mimetype;
}
},
/**
* Sets the image's width to its suggested size.
@@ -5872,7 +5883,12 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
async _autoOptimizeImage() {
await this._loadImageInfo();
await this._rerenderXML();
this._getImg().dataset.resizeWidth = this.optimizedWidth;
const img = this._getImg();
if (!['image/gif', 'image/svg+xml'].includes(img.dataset.mimetype)) {
// Convert to recommended format and width.
img.dataset.mimetype = 'image/webp';
img.dataset.resizeWidth = this.optimizedWidth;
}
await this._applyOptions();
await this.updateUI();
},
@@ -5944,7 +5960,7 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
return params.optionsPossibleValues.glFilter
|| 'customFilter' in params.optionsPossibleValues
|| params.optionsPossibleValues.setQuality
|| widgetName === 'width_select_opt';
|| widgetName === 'format_select_opt';
},
});
@@ -1132,6 +1132,12 @@
}
}
&.o_we_badge_at_end > div {
display: flex;
width: 100%;
justify-content: space-between;
}
&:not(.d-none) ~ we-button {
// Use a border-top instead of a margin-top as when the
// mouse goes from one select button to another, the
+3 -3
View File
@@ -118,10 +118,10 @@
data-max="2000"
data-step="100"/>
<t t-set="width_label">Width</t>
<we-select t-att-string="width_label"
<t t-set="format_label">Format</t>
<we-select t-att-string="format_label"
t-att-class="indent and 'o_we_sublevel_2'"
data-name="width_select_opt"/>
data-name="format_select_opt"/>
<t t-set="quality_label">Quality</t>
<we-range t-att-string="quality_label"