diff --git a/package.json b/package.json index 41075dff8..7bca98fe0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hashbrown-cms", "repository": "https://github.com/HashBrownCMS/hashbrown-cms.git", - "version": "1.4.5", + "version": "1.4.6", "description": "A free and open-source headless CMS", "main": "hashbrown.js", "scripts": { diff --git a/src/Client/Entity/Resource/Media.js b/src/Client/Entity/Resource/Media.js index 9cdf1ccc5..2c9fad411 100644 --- a/src/Client/Entity/Resource/Media.js +++ b/src/Client/Entity/Resource/Media.js @@ -125,6 +125,30 @@ class Media extends require('Common/Entity/Resource/Media') { HashBrown.Service.EventService.trigger('resource', this.id); } + + /** + * Waits for an image to load + * + * @param {String} url + * @param {Boolean} ignoreErrors + */ + static waitForImage(url, ignoreErrors = false) { + if(!url) { return; } + + return new Promise((resolve, reject) => { + let img = new Image(); + + img.onload = resolve; + + if(ignoreErrors) { + img.onerror = resolve; + } else { + img.onerror = reject; + } + + img.src = url; + }); + } } module.exports = Media; diff --git a/src/Client/Entity/View/ResourceEditor/MediaEditor.js b/src/Client/Entity/View/ResourceEditor/MediaEditor.js index dbad69144..cfe49c068 100644 --- a/src/Client/Entity/View/ResourceEditor/MediaEditor.js +++ b/src/Client/Entity/View/ResourceEditor/MediaEditor.js @@ -67,6 +67,22 @@ class MediaEditor extends HashBrown.Entity.View.ResourceEditor.ResourceEditorBas ]; } + /** + * Restores the scroll position + */ + async restoreScrollPosition() { + for(let image of this.element.querySelectorAll('img.widget--media__preview__source, img.widget--image')) { + await HashBrown.Entity.Resource.Media.waitForImage(image.src, true); + } + + await new Promise((resolve) => { requestAnimationFrame(resolve); }); + await new Promise((resolve) => { requestAnimationFrame(resolve); }); + await new Promise((resolve) => { requestAnimationFrame(resolve); }); + await new Promise((resolve) => { requestAnimationFrame(resolve); }); + + await super.restoreScrollPosition(); + } + /** * Event: Clicked start tour */ diff --git a/src/Client/Entity/View/ResourceEditor/ResourceEditorBase.js b/src/Client/Entity/View/ResourceEditor/ResourceEditorBase.js index 7b047f282..8099272bc 100644 --- a/src/Client/Entity/View/ResourceEditor/ResourceEditorBase.js +++ b/src/Client/Entity/View/ResourceEditor/ResourceEditorBase.js @@ -42,6 +42,7 @@ class ResourceEditorBase extends HashBrown.Entity.View.ViewBase { super.structure(); this.def(Boolean, 'isDirty', false); + this.def(Number, 'cachedScrollPosition', 0); } /** @@ -153,35 +154,47 @@ class ResourceEditorBase extends HashBrown.Entity.View.ViewBase { } /** - * Override render to maintain scroll position + * Caches the scroll position */ - render() { - // Cache scroll position + cacheScrollPosition() { let body = this.namedElements.body; if(body instanceof HashBrown.Entity.View.ViewBase) { body = body.element; } - let scrollTop = 0; + this.cachedScrollPosition = 0; if(body) { - scrollTop = body.scrollTop; + this.cachedScrollPosition = body.scrollTop; } + } - super.render(); + /** + * Restores the scroll position + */ + async restoreScrollPosition() { + let body = this.namedElements.body; + + if(body instanceof HashBrown.Entity.View.ViewBase) { + body = body.element; + } - // Restore scroll position - requestAnimationFrame(() => { - body = this.namedElements.body; + if(body) { + body.scrollTop = this.cachedScrollPosition; + } + } - if(body instanceof HashBrown.Entity.View.ViewBase) { - body = body.element; - } - - if(body) { - body.scrollTop = scrollTop; - } + /** + * Override render to maintain scroll position + */ + render() { + this.cacheScrollPosition(); + + super.render(); + + requestAnimationFrame(async () => { + await this.restoreScrollPosition(); if(this.element) { this.element.classList.toggle('locked', this.model && this.model.isLocked);