Created
April 1, 2025 08:33
-
-
Save samwho/66513859aa222b497d7ea64ecc774875 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import GUI from "lil-gui"; | |
| import { div } from "./DOM"; | |
| let id = 0; | |
| function getUniqueID(): string { | |
| return `element-${id++}`; | |
| } | |
| export class BaseElement extends HTMLElement { | |
| gui?: GUI; | |
| debug!: boolean; | |
| onDisconnectCallbacks: (() => void)[] = []; | |
| _initDone = false; | |
| afterInitCallbacks: (() => void)[] = []; | |
| isParsed() { | |
| let el: Node | null = this; | |
| while (true) { | |
| if (el.nextSibling) return true; | |
| el = el.parentNode; | |
| if (!el) return false; | |
| } | |
| } | |
| connectedCallback() { | |
| if (this.ownerDocument.readyState === "complete" || this.isParsed()) { | |
| this.parsedCallback(); | |
| return; | |
| } | |
| const callback = () => { | |
| observer.disconnect(); | |
| this.ownerDocument.removeEventListener("DOMContentLoaded", callback); | |
| this.parsedCallback(); | |
| }; | |
| const observer = new MutationObserver(() => { | |
| if (this.isParsed()) { | |
| callback(); | |
| } | |
| }); | |
| this.ownerDocument.addEventListener("DOMContentLoaded", () => { | |
| callback(); | |
| }); | |
| if (!this.parentNode) { | |
| throw new Error("Element must have a parent node"); | |
| } | |
| observer.observe(this.parentNode, { childList: true, subtree: true }); | |
| } | |
| parsedCallback() { | |
| if (!this.id) { | |
| this.id = getUniqueID(); | |
| } | |
| this.debug = this.getAttribute("debug") === "true"; | |
| if (this.debug) { | |
| this.gui = new GUI({ | |
| container: this, | |
| width: this.clientWidth, | |
| }); | |
| } | |
| let resizing = false; | |
| const elementObserver = new ResizeObserver((entries) => { | |
| if (resizing) return; | |
| resizing = true; | |
| requestAnimationFrame(() => { | |
| try { | |
| this.onResize(); | |
| } finally { | |
| resizing = false; | |
| } | |
| }); | |
| }); | |
| elementObserver.observe(this); | |
| this.onDisconnect(() => elementObserver.disconnect()); | |
| let pageResizing = false; | |
| const windowResizeHandler = async () => { | |
| if (pageResizing) return; | |
| pageResizing = true; | |
| requestAnimationFrame(() => { | |
| try { | |
| this.onPageResize(); | |
| } finally { | |
| pageResizing = false; | |
| } | |
| }); | |
| }; | |
| window.addEventListener("resize", windowResizeHandler); | |
| this.onDisconnect(() => { | |
| window.removeEventListener("resize", windowResizeHandler); | |
| }); | |
| const intersectionObserver = new IntersectionObserver( | |
| async (entries) => { | |
| for (const entry of entries) { | |
| if (entry.isIntersecting) { | |
| this.onVisible(); | |
| } else { | |
| this.onHidden(); | |
| } | |
| } | |
| }, | |
| { | |
| threshold: 0, | |
| rootMargin: "5px", | |
| } | |
| ); | |
| intersectionObserver.observe(this); | |
| this.onDisconnect(() => { | |
| intersectionObserver.disconnect(); | |
| }); | |
| this.init(); | |
| this._initDone = true; | |
| for (const callback of this.afterInitCallbacks) { | |
| callback(); | |
| } | |
| } | |
| onDisconnect(callback: () => void) { | |
| this.onDisconnectCallbacks.push(callback); | |
| } | |
| disconnectedCallback() { | |
| for (const callback of this.onDisconnectCallbacks) { | |
| callback(); | |
| } | |
| } | |
| afterInit(callback: () => void) { | |
| if (this._initDone) { | |
| callback(); | |
| return; | |
| } | |
| this.afterInitCallbacks.push(callback); | |
| } | |
| init() {} | |
| onResize() {} | |
| onPageResize() {} | |
| onVisible() {} | |
| onHidden() {} | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment