diff --git a/addons/web_editor/static/src/components/media_dialog/media_dialog.js b/addons/web_editor/static/src/components/media_dialog/media_dialog.js
index 102111d5819..06150f1e780 100644
--- a/addons/web_editor/static/src/components/media_dialog/media_dialog.js
+++ b/addons/web_editor/static/src/components/media_dialog/media_dialog.js
@@ -242,7 +242,9 @@ export class MediaDialogWrapper extends Component {
this.dialogs = useWowlService('dialog');
onRendered(() => {
- this.dialogs.add(MediaDialog, this.props);
+ this.dialogs.add(MediaDialog, this.props, {
+ onClose: this.props.close,
+ });
});
}
}
diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js
index a4e30eff3b9..2829d810b2a 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -3585,9 +3585,10 @@ const SnippetOptionWidget = Widget.extend({
*
* @param {string} name - an identifier for a type of update
* @param {*} data
- * @returns {Promise}
*/
notify: function (name, data) {
+ // We prefer to avoid refactoring this notify mechanism to make it
+ // asynchronous because the upcoming conversion to owl might remove it.
if (name === 'target') {
this.setTarget(data);
}
@@ -8392,7 +8393,7 @@ registry.CarouselHandler = registry.GalleryHandler.extend({
this.$target[0].querySelector(`.carousel-indicators li[data-bs-slide-to="${position}"]`)
.classList.add("active");
this.trigger_up("activate_snippet", {
- $snippet: $(this.$target[0].querySelector(".carousel-item.active")),
+ $snippet: $(this.$target[0].querySelector(".carousel-item.active img")),
ifInactiveOptions: true,
});
carouselEl.classList.add("slide");
diff --git a/addons/website/static/src/snippets/s_image_gallery/options.js b/addons/website/static/src/snippets/s_image_gallery/options.js
index ea0f02e8026..1122cb26384 100644
--- a/addons/website/static/src/snippets/s_image_gallery/options.js
+++ b/addons/website/static/src/snippets/s_image_gallery/options.js
@@ -4,6 +4,7 @@ import { MediaDialogWrapper } from "@web_editor/components/media_dialog/media_di
import { ComponentWrapper } from "web.OwlCompatibility";
import core from "web.core";
import options from "web_editor.snippets.options";
+import wUtils from "website.utils";
var _t = core._t;
var qweb = core.qweb;
@@ -66,9 +67,9 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
* Displays the images with the "masonry" layout.
*
* @private
+ * @returns {Promise}
*/
_masonry() {
- var self = this;
const imgs = this._getItemsGallery();
var columns = this._getColumns();
var colClass = 'col-lg-' + (12 / columns);
@@ -86,32 +87,36 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
// Dispatch images in columns by always putting the next one in the
// smallest-height column
- while (imgs.length) {
- var min = Infinity;
- var $lowest;
- cols.forEach((col) => {
- var $col = $(col);
- var height = $col.is(':empty') ? 0 : $col.find('img').last().offset().top + $col.find('img').last().height() - self.$target.offset().top;
- // Neutralize invisible sub-pixel height differences.
- height = Math.round(height);
- if (height < min) {
- min = height;
- $lowest = $col;
+ return new Promise(async resolve => {
+ for (const imgEl of imgs) {
+ let min = Infinity;
+ let smallestColEl;
+ for (const colEl of cols) {
+ const imgEls = colEl.querySelectorAll("img");
+ const lastImgRect = imgEls.length && imgEls[imgEls.length - 1].getBoundingClientRect();
+ const height = lastImgRect ? Math.round(lastImgRect.top + lastImgRect.height) : 0;
+ if (height < min) {
+ min = height;
+ smallestColEl = colEl;
+ }
}
- });
- // Only on Chrome: appended images are sometimes invisible and not
- // correctly loaded from cache, we use a clone of the image to force
- // the loading.
- $lowest.append(imgs.shift().cloneNode());
- }
+ // Only on Chrome: appended images are sometimes invisible
+ // and not correctly loaded from cache, we use a clone of the
+ // image to force the loading.
+ smallestColEl.append(imgEl.cloneNode());
+ await wUtils.onceAllImagesLoaded(this.$target);
+ }
+ resolve();
+ });
},
/**
* Allows to change the images layout. @see grid, masonry, nomode, slideshow
*
* @private
* @param {string} modeName
+ * @returns {Promise}
*/
- _setMode(modeName) {
+ async _setMode(modeName) {
modeName = modeName || 'slideshow'; // FIXME should not be needed
this.$target.css('height', '');
this.$target
@@ -124,9 +129,9 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
if (this.options.wysiwyg) {
this.options.wysiwyg.odooEditor.unbreakableStepUnactive();
}
- this[`_${modeName}`]();
+ await this[`_${modeName}`]();
this.trigger_up('cover_update');
- this._refreshPublicWidgets();
+ await this._refreshPublicWidgets();
},
/**
* Displays the images with the standard layout: floating images.
@@ -216,16 +221,18 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
itemsEls.forEach((img, index) => {
img.dataset.index = index;
});
- const currentMode = this._getMode();
- this._relayout();
- if (currentMode === "slideshow") {
- this._updateIndicatorAndActivateSnippet(newItemPosition);
- } else {
- this.trigger_up("activate_snippet", {
- $snippet: $(itemsEls[newItemPosition]),
- ifInactiveOptions: true,
- });
- }
+ this.trigger_up('snippet_edition_request', {exec: async () => {
+ await this._relayout();
+ if (this._getMode() === "slideshow") {
+ this._updateIndicatorAndActivateSnippet(newItemPosition);
+ } else {
+ const imageEl = this.$target[0].querySelector(`[data-index='${newItemPosition}']`);
+ this.trigger_up("activate_snippet", {
+ $snippet: $(imageEl),
+ ifInactiveOptions: true,
+ });
+ }
+ }});
},
/**
* Empties the container, adds the given content and returns the container.
@@ -245,7 +252,7 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
* @private
*/
_relayout() {
- this._setMode(this._getMode());
+ return this._setMode(this._getMode());
},
});
@@ -254,11 +261,15 @@ options.registry.gallery = options.registry.GalleryLayout.extend({
* @override
*/
start() {
+ const _super = this._super.bind(this);
+ let layoutPromise;
const containerEl = this.$target[0].querySelector(":scope > .container, :scope > .container-fluid, :scope > .o_container_small");
if (containerEl.querySelector(":scope > *:not(div)")) {
- this._relayout();
+ layoutPromise = this._relayout();
+ } else {
+ layoutPromise = Promise.resolve();
}
- return this._super.apply(this, arguments);
+ return layoutPromise.then(() => _super.apply(this, arguments));
},
/**
* @override
@@ -283,7 +294,7 @@ options.registry.gallery = options.registry.GalleryLayout.extend({
const nbColumns = parseInt(widgetValue || '1');
this.$target.attr('data-columns', nbColumns);
- this._relayout();
+ return this._relayout();
},
/**
* Allows to change the images layout. @see grid, masonry, nomode, slideshow
@@ -291,7 +302,7 @@ options.registry.gallery = options.registry.GalleryLayout.extend({
* @see this.selectClass for parameters
*/
mode(previewMode, widgetValue, params) {
- this._setMode(widgetValue);
+ return this._setMode(widgetValue);
},
//--------------------------------------------------------------------------
@@ -367,10 +378,10 @@ options.registry.GalleryImageList = options.registry.GalleryLayout.extend({
/**
* @override
*/
- onBuilt() {
- this._super(...arguments);
+ async onBuilt() {
+ await this._super(...arguments);
if (this.$target.find('.o_add_images').length) {
- this.addImages(false);
+ await this.addImages(false);
}
// TODO should consider the async parts
this._adaptNavigationIDs();
@@ -403,34 +414,39 @@ options.registry.GalleryImageList = options.registry.GalleryLayout.extend({
const $container = this.$('> .container, > .container-fluid, > .o_container_small');
const lastImage = this._getItemsGallery().at(-1);
let index = lastImage ? this._getIndex(lastImage) : -1;
- const dialog = new ComponentWrapper(this, MediaDialogWrapper, {
- multiImages: true,
- onlyImages: true,
- save: images => {
- let $newImageToSelect;
- for (const image of images) {
- const $img = $('
', {
- class: $images.length > 0 ? $images[0].className : 'img img-fluid d-block ',
- src: image.src,
- 'data-index': ++index,
- alt: image.alt || '',
- 'data-name': _t('Image'),
- style: $images.length > 0 ? $images[0].style.cssText : '',
- }).appendTo($container);
- if (!$newImageToSelect) {
- $newImageToSelect = $img;
+ return new Promise(resolve => {
+ let savedPromise = Promise.resolve();
+ const dialog = new ComponentWrapper(this, MediaDialogWrapper, {
+ multiImages: true,
+ onlyImages: true,
+ save: images => {
+ let $newImageToSelect;
+ for (const image of images) {
+ const $img = $('
', {
+ class: $images.length > 0 ? $images[0].className : 'img img-fluid d-block ',
+ src: image.src,
+ 'data-index': ++index,
+ alt: image.alt || '',
+ 'data-name': _t('Image'),
+ style: $images.length > 0 ? $images[0].style.cssText : '',
+ }).appendTo($container);
+ if (!$newImageToSelect) {
+ $newImageToSelect = $img;
+ }
}
- }
- if (images.length > 0) {
- this._relayout();
- this.trigger_up('cover_update');
- // Triggers the re-rendering of the thumbnail
- $newImageToSelect.trigger('image_changed');
-
- }
- },
+ if (images.length > 0) {
+ savedPromise = this._relayout();
+ this.trigger_up('cover_update');
+ // Triggers the re-rendering of the thumbnail
+ $newImageToSelect.trigger('image_changed');
+ }
+ },
+ close: () => {
+ savedPromise.then(resolve);
+ },
+ });
+ dialog.mount(this.el);
});
- dialog.mount(this.el);
},
/**
* Allows to remove all images. Restores the snippet to the way it was when
@@ -466,7 +482,9 @@ options.registry.GalleryImageList = options.registry.GalleryLayout.extend({
this._super(...arguments);
if (name === 'image_removed') {
data.$image.remove(); // Force the removal of the image before reset
- this._relayout();
+ this.trigger_up('snippet_edition_request', {exec: () => {
+ return this._relayout();
+ }});
}
},