{ "version": 3, "sources": ["../src/observeElementRect.ts"], "sourcesContent": ["type Measurable = { getBoundingClientRect(): ClientRect };\n\n/**\n * Observes an element's rectangle on screen (getBoundingClientRect)\n * This is useful to track elements on the screen and attach other elements\n * that might be in different layers, etc.\n */\nfunction observeElementRect(\n /** The element whose rect to observe */\n elementToObserve: Measurable,\n /** The callback which will be called when the rect changes */\n callback: CallbackFn\n) {\n const observedData = observedElements.get(elementToObserve);\n\n if (observedData === undefined) {\n // add the element to the map of observed elements with its first callback\n // because this is the first time this element is observed\n observedElements.set(elementToObserve, { rect: {} as ClientRect, callbacks: [callback] });\n\n if (observedElements.size === 1) {\n // start the internal loop once at least 1 element is observed\n rafId = requestAnimationFrame(runLoop);\n }\n } else {\n // only add a callback for this element as it's already observed\n observedData.callbacks.push(callback);\n callback(elementToObserve.getBoundingClientRect());\n }\n\n return () => {\n const observedData = observedElements.get(elementToObserve);\n if (observedData === undefined) return;\n\n // start by removing the callback\n const index = observedData.callbacks.indexOf(callback);\n if (index > -1) {\n observedData.callbacks.splice(index, 1);\n }\n\n if (observedData.callbacks.length === 0) {\n // stop observing this element because there are no\n // callbacks registered for it anymore\n observedElements.delete(elementToObserve);\n\n if (observedElements.size === 0) {\n // stop the internal loop once no elements are observed anymore\n cancelAnimationFrame(rafId);\n }\n }\n };\n}\n\n// ========================================================================\n// module internals\n\ntype CallbackFn = (rect: ClientRect) => void;\n\ntype ObservedData = {\n rect: ClientRect;\n callbacks: Array;\n};\n\nlet rafId: number;\nconst observedElements: Map = new Map();\n\nfunction runLoop() {\n const changedRectsData: Array = [];\n\n // process all DOM reads first (getBoundingClientRect)\n observedElements.forEach((data, element) => {\n const newRect = element.getBoundingClientRect();\n\n // gather all the data for elements whose rects have changed\n if (!rectEquals(data.rect, newRect)) {\n data.rect = newRect;\n changedRectsData.push(data);\n }\n });\n\n // group DOM writes here after the DOM reads (getBoundingClientRect)\n // as DOM writes will most likely happen with the callbacks\n changedRectsData.forEach((data) => {\n data.callbacks.forEach((callback) => callback(data.rect));\n });\n\n rafId = requestAnimationFrame(runLoop);\n}\n// ========================================================================\n\n/**\n * Returns whether 2 rects are equal in values\n */\nfunction rectEquals(rect1: ClientRect, rect2: ClientRect) {\n return (\n rect1.width === rect2.width &&\n rect1.height === rect2.height &&\n rect1.top === rect2.top &&\n rect1.right === rect2.right &&\n rect1.bottom === rect2.bottom &&\n rect1.left === rect2.left\n );\n}\n\nexport { observeElementRect };\nexport type { Measurable };\n"], "mappings": ";AAOA,SAAS,mBAEP,kBAEA,UACA;AACA,QAAM,eAAe,iBAAiB,IAAI,gBAAgB;AAE1D,MAAI,iBAAiB,QAAW;AAG9B,qBAAiB,IAAI,kBAAkB,EAAE,MAAM,CAAC,GAAiB,WAAW,CAAC,QAAQ,EAAE,CAAC;AAExF,QAAI,iBAAiB,SAAS,GAAG;AAE/B,cAAQ,sBAAsB,OAAO;AAAA,IACvC;AAAA,EACF,OAAO;AAEL,iBAAa,UAAU,KAAK,QAAQ;AACpC,aAAS,iBAAiB,sBAAsB,CAAC;AAAA,EACnD;AAEA,SAAO,MAAM;AACX,UAAMA,gBAAe,iBAAiB,IAAI,gBAAgB;AAC1D,QAAIA,kBAAiB,OAAW;AAGhC,UAAM,QAAQA,cAAa,UAAU,QAAQ,QAAQ;AACrD,QAAI,QAAQ,IAAI;AACd,MAAAA,cAAa,UAAU,OAAO,OAAO,CAAC;AAAA,IACxC;AAEA,QAAIA,cAAa,UAAU,WAAW,GAAG;AAGvC,uBAAiB,OAAO,gBAAgB;AAExC,UAAI,iBAAiB,SAAS,GAAG;AAE/B,6BAAqB,KAAK;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AACF;AAYA,IAAI;AACJ,IAAM,mBAAkD,oBAAI,IAAI;AAEhE,SAAS,UAAU;AACjB,QAAM,mBAAwC,CAAC;AAG/C,mBAAiB,QAAQ,CAAC,MAAM,YAAY;AAC1C,UAAM,UAAU,QAAQ,sBAAsB;AAG9C,QAAI,CAAC,WAAW,KAAK,MAAM,OAAO,GAAG;AACnC,WAAK,OAAO;AACZ,uBAAiB,KAAK,IAAI;AAAA,IAC5B;AAAA,EACF,CAAC;AAID,mBAAiB,QAAQ,CAAC,SAAS;AACjC,SAAK,UAAU,QAAQ,CAAC,aAAa,SAAS,KAAK,IAAI,CAAC;AAAA,EAC1D,CAAC;AAED,UAAQ,sBAAsB,OAAO;AACvC;AAMA,SAAS,WAAW,OAAmB,OAAmB;AACxD,SACE,MAAM,UAAU,MAAM,SACtB,MAAM,WAAW,MAAM,UACvB,MAAM,QAAQ,MAAM,OACpB,MAAM,UAAU,MAAM,SACtB,MAAM,WAAW,MAAM,UACvB,MAAM,SAAS,MAAM;AAEzB;", "names": ["observedData"] }