From 65056cb9ea07f27bb1f80a24bdc544cb4b2ce236 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Mon, 16 Mar 2026 14:59:19 +0700 Subject: [PATCH] feat: Implement IntersectionObserver for lazy loading of image sign items instead of immediate triggering. --- static/src/js/sign_image_upload.js | 90 +++++++++++++++--------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/static/src/js/sign_image_upload.js b/static/src/js/sign_image_upload.js index 3f97341..396f9f8 100755 --- a/static/src/js/sign_image_upload.js +++ b/static/src/js/sign_image_upload.js @@ -5,69 +5,69 @@ import { PDFIframe } from "@sign/components/sign_request/PDF_iframe"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; /** - * Shared logic for triggered lazy loading of images. - * This ensures that when a sign item element is created or scrolled into view, - * its image is loaded from the data-src attribute. + * Robust lazy loading trigger for image sign items. */ -const lazyLoadMixin = { - _triggerLazyLoad(el) { - if (!el) return; - const lazyImg = el.querySelector('.o_sign_image_lazy'); - if (lazyImg && lazyImg.dataset.src) { - const currentSrc = lazyImg.getAttribute('src'); - // Check if it's currently showing the transparent placeholder - if (!currentSrc || currentSrc.startsWith('data:image/gif')) { - lazyImg.src = lazyImg.dataset.src; - lazyImg.onload = () => { - lazyImg.classList.remove('o_sign_image_loading'); - el.classList.remove('o_sign_image_loading_container'); - lazyImg.style.opacity = 1; - }; - } - } - }, - - refreshSignItemsForPage(page) { - super.refreshSignItemsForPage(page); - if (this.signItems && this.signItems[page]) { - for (const id in this.signItems[page]) { - const item = this.signItems[page][id]; - if (item && item.el) { - this._triggerLazyLoad(item.el); - } - } +const triggerLazyLoad = (el) => { + if (!el) return; + const lazyImg = el.querySelector('.o_sign_image_lazy'); + if (lazyImg && lazyImg.dataset.src) { + const currentSrc = lazyImg.getAttribute('src'); + if (!currentSrc || currentSrc.startsWith('data:image/gif')) { + lazyImg.src = lazyImg.dataset.src; + lazyImg.onload = () => { + lazyImg.classList.remove('o_sign_image_loading'); + el.classList.remove('o_sign_image_loading_container'); + lazyImg.style.opacity = 1; + }; } } }; -// 1. Patch the base PDFIframe for shared lazy-loading logic (covers read-only views) -patch(PDFIframe.prototype, { - ...lazyLoadMixin, +/** + * Setup IntersectionObserver for an element. + */ +const setupLazyObserver = (iframeInstance, el) => { + const lazyImg = el.querySelector('.o_sign_image_lazy'); + if (!lazyImg || !lazyImg.dataset.src) return; + + // Use the viewer container as root if possible + const root = iframeInstance.root ? iframeInstance.root.querySelector('#viewerContainer') : null; - // We also hook into enableCustom to trigger lazy load for initial items + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + triggerLazyLoad(el); + observer.disconnect(); + } + }); + }, { + root: root, + rootMargin: '200px' + }); + observer.observe(el); +}; + +// 1. Patch PDFIframe for read-only views +patch(PDFIframe.prototype, { enableCustom(signItem) { - super.enableCustom(signItem); + // Base PDFIframe.enableCustom is empty, no need for super if (signItem.data.type === 'image') { - this._triggerLazyLoad(signItem.el); + setupLazyObserver(this, signItem.el); } } }); -// 2. Patch SignablePDFIframe for upload handlers and specific behaviors (covers signing views) +// 2. Patch SignablePDFIframe for active signer views patch(SignablePDFIframe.prototype, { - // Note: Mixin methods are already inherited if not overwritten, - // but SignablePDFIframe might need its own hooks. - enableCustom(signItem) { - super.enableCustom(signItem); + super.enableCustom(signItem); // Safe because SignablePDFIframe extends PDFIframe if (signItem.data.type === 'image') { const el = signItem.el; const data = signItem.data; - // Add data-type attribute for CSS targeting el.setAttribute('data-type', 'image'); - // Initial font size adjustment for placeholder text + // Initial font size adjustment for placeholder const adjustFontSize = () => { const placeholder = el.querySelector('.o_placeholder'); if (placeholder) { @@ -144,8 +144,8 @@ patch(SignablePDFIframe.prototype, { }); } - // Immediately trigger load if value exists - this._triggerLazyLoad(el); + // Also setup lazy observer for signer view + setupLazyObserver(this, el); } },