Skip to content

Commit 5697107

Browse files
committed
add image size tag in image option header
1 parent 2e9b9d5 commit 5697107

File tree

3 files changed

+79
-28
lines changed

3 files changed

+79
-28
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Component, useState } from "@odoo/owl";
2+
import { useDomState } from "@html_builder/core/utils";
3+
import { loadImageDataURL, getImageSizeFromCache } from "@html_editor/utils/image_processing";
4+
import { KeepLast } from "@web/core/utils/concurrency";
5+
6+
export class ImageSizeTag extends Component {
7+
static template = "website.ImageSizeTag";
8+
setup() {
9+
this.keepLast = new KeepLast();
10+
this.state = useState({ size: 0 });
11+
useDomState((imageEl) => this.updateImageSize(imageEl));
12+
this.updateImageSize(this.env.getEditingElement());
13+
}
14+
15+
async updateImageSize(imageEl) {
16+
const src = imageEl.src;
17+
await this.keepLast.add(loadImageDataURL(src));
18+
this.state.size = Math.round((getImageSizeFromCache(src) / 1024) * 10) / 10;
19+
}
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<t t-name="website.ImageSizeTag">
4+
<span class="badge text-bg-dark" title="Size"><t t-out="state.size"/> kB</span>
5+
</t>
6+
</templates>

addons/website/static/src/builder/plugins/image/image_tool_option_plugin.js

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { cropperDataFieldsWithAspectRatio, isGif, loadImage } from "@html_editor/utils/image_processing";
1+
import {
2+
cropperDataFieldsWithAspectRatio,
3+
isGif,
4+
loadImage,
5+
} from "@html_editor/utils/image_processing";
26
import { registry } from "@web/core/registry";
37
import { Plugin } from "@html_editor/plugin";
48
import { ImageToolOption } from "./image_tool_option";
@@ -11,6 +15,7 @@ import {
1115
} from "@html_builder/utils/option_sequence";
1216
import { ReplaceMediaOption, searchSupportedParentLinkEl } from "./replace_media_option";
1317
import { computeMaxDisplayWidth } from "./image_format_option";
18+
import { ImageSizeTag } from "./image_size_tag";
1419

1520
export const REPLACE_MEDIA_SELECTOR = "img, .media_iframe_video, span.fa, i.fa";
1621
export const REPLACE_MEDIA_EXCLUDE =
@@ -28,6 +33,10 @@ class ImageToolOptionPlugin extends Plugin {
2833
];
2934
static shared = ["canHaveHoverEffect"];
3035
resources = {
36+
builder_header_middle_buttons: {
37+
Component: ImageSizeTag,
38+
selector: "img",
39+
},
3140
builder_options: [
3241
withSequence(REPLACE_MEDIA, {
3342
OptionComponent: ReplaceMediaOption,
@@ -49,32 +58,45 @@ class ImageToolOptionPlugin extends Plugin {
4958
on_media_dialog_saved_handlers: async (elements, { node }) => {
5059
for (const image of elements) {
5160
if (image && image.tagName === "IMG") {
52-
const updateImageAttributes = await this.dependencies.imagePostProcess.processImage({
53-
img: image,
54-
newDataset: {
55-
formatMimetype: "image/webp",
56-
},
57-
// TODO Using a callback is currently needed to avoid
58-
// the extra RPC that would occur if loadImageInfo was
59-
// called before processImage as well. This flow can be
60-
// simplified if image infos are somehow cached.
61-
onImageInfoLoaded: async (dataset) => {
62-
if (!dataset.originalSrc || !dataset.originalId) {
63-
return true;
64-
}
65-
const original = await loadImage(dataset.originalSrc);
66-
const maxWidth = dataset.width ? image.naturalWidth : original.naturalWidth;
67-
const optimizedWidth = Math.min(maxWidth, computeMaxDisplayWidth(node || this.editable));
68-
if (!["image/gif", "image/svg+xml"].includes(dataset.mimetypeBeforeConversion)) {
69-
// Convert to recommended format and width.
70-
dataset.resizeWidth = optimizedWidth;
71-
} else if (dataset.shape && dataset.mimetypeBeforeConversion !== "image/gif") {
72-
dataset.resizeWidth = optimizedWidth;
73-
} else {
74-
return true;
75-
}
76-
},
77-
});
61+
const updateImageAttributes =
62+
await this.dependencies.imagePostProcess.processImage({
63+
img: image,
64+
newDataset: {
65+
formatMimetype: "image/webp",
66+
},
67+
// TODO Using a callback is currently needed to avoid
68+
// the extra RPC that would occur if loadImageInfo was
69+
// called before processImage as well. This flow can be
70+
// simplified if image infos are somehow cached.
71+
onImageInfoLoaded: async (dataset) => {
72+
if (!dataset.originalSrc || !dataset.originalId) {
73+
return true;
74+
}
75+
const original = await loadImage(dataset.originalSrc);
76+
const maxWidth = dataset.width
77+
? image.naturalWidth
78+
: original.naturalWidth;
79+
const optimizedWidth = Math.min(
80+
maxWidth,
81+
computeMaxDisplayWidth(node || this.editable)
82+
);
83+
if (
84+
!["image/gif", "image/svg+xml"].includes(
85+
dataset.mimetypeBeforeConversion
86+
)
87+
) {
88+
// Convert to recommended format and width.
89+
dataset.resizeWidth = optimizedWidth;
90+
} else if (
91+
dataset.shape &&
92+
dataset.mimetypeBeforeConversion !== "image/gif"
93+
) {
94+
dataset.resizeWidth = optimizedWidth;
95+
} else {
96+
return true;
97+
}
98+
},
99+
});
78100
updateImageAttributes();
79101
}
80102
}
@@ -91,7 +113,10 @@ class ImageToolOptionPlugin extends Plugin {
91113
onClose: resolve,
92114
onSave: async (newDataset) => {
93115
resolve(
94-
this.dependencies.imagePostProcess.processImage({ img, newDataset })
116+
this.dependencies.imagePostProcess.processImage({
117+
img,
118+
newDataset,
119+
})
95120
);
96121
},
97122
});

0 commit comments

Comments
 (0)