@popeindustries/lit-html
Version:
Seamlessly and efficiently use @popeindustries/lit-html-server rendered HTML to hydrate lit-html templates in the browser
86 lines (77 loc) • 2.59 kB
JavaScript
/**
* @typedef { import('./lazy-hydration-mixin.js').CustomElementBase } CustomElementBase
*/
/** @type { IntersectionObserver } */
let intersectionObserver;
/**
* Custom element mixin for adding lazy (partial/deferred) hydration support.
* @param { CustomElementBase } Base
*/
export function lazyHydrationMixin(Base) {
return class LazyHydrationElement extends Base {
static get observedAttributes() {
return [...(super.observedAttributes ?? []), 'hydrate:defer'];
}
/**
* Constructor
*/
constructor() {
super();
/** @type { number | undefined } */
this._idleCallbackID = undefined;
}
connectedCallback() {
if (!this.hasAttribute('hydrate:defer')) {
this.handleHydrationReady();
}
}
/**
* @param { string } name
* @param { string | null } oldValue
* @param { string | null } value
*/
attributeChangedCallback(name, oldValue, value) {
if (name === 'hydrate:defer' && value === null) {
this.handleHydrationReady();
}
super.attributeChangedCallback?.(name, oldValue, value);
}
disconnectedCallback() {
if (this._idleCallbackID !== undefined) {
globalThis.cancelIdleCallback(this._idleCallbackID);
this._idleCallbackID = undefined;
}
if (intersectionObserver !== undefined) {
// No exception thrown if not currently observed
intersectionObserver.unobserve(this);
}
super.disconnectedCallback?.();
}
handleHydrationReady() {
if (this.hasAttribute('hydrate:idle') && 'requestIdleCallback' in globalThis) {
this.removeAttribute('hydrate:idle');
this._idleCallbackID = globalThis.requestIdleCallback(() => {
this.triggerSuperConnectedCallback();
});
} else if (this.hasAttribute('hydrate:visible') && 'IntersectionObserver' in globalThis) {
if (intersectionObserver === undefined) {
intersectionObserver = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
intersectionObserver.unobserve(entry.target);
/** @type { LazyHydrationElement } */ (entry.target).triggerSuperConnectedCallback();
}
}
});
}
this.removeAttribute('hydrate:visible');
intersectionObserver.observe(this);
} else {
this.triggerSuperConnectedCallback();
}
}
triggerSuperConnectedCallback() {
super.connectedCallback?.();
}
};
}