import { isElement, isFunction } from "@thednp/shorty"; //#region package.json var version = "1.1.3"; //#endregion //#region src/index.ts const errorString = "PositionObserver Error"; /** * The PositionObserver class is a utility class that observes the position * of DOM elements and triggers a callback when their position changes. */ var PositionObserver = class { entries; static version = version; /** `PositionObserver.tick` */ _t; /** `PositionObserver.root` */ _r; /** `PositionObserver.root.clientWidth` */ _w; /** `PositionObserver.root.clientHeight` */ _h; /** `IntersectionObserver.options.rootMargin` */ _rm; /** `IntersectionObserver.options.threshold` */ _th; /** `PositionObserver.callback` */ _c; /** * The constructor takes two arguments, a `callback`, which is called * whenever the position of an observed element changes and an `options` object. * The callback function takes an array of `PositionObserverEntry` objects * as its first argument and the PositionObserver instance as its second argument. * * @param callback the callback that applies to all targets of this observer * @param options the options of this observer */ constructor(callback, options) { if (!isFunction(callback)) throw new Error(`${errorString}: ${callback} is not a function.`); this.entries = /* @__PURE__ */ new Map(); this._c = callback; this._t = 0; const root = isElement(options?.root) ? options.root : document?.documentElement; this._r = root; this._rm = options?.rootMargin; this._th = options?.threshold; this._w = root.clientWidth; this._h = root.clientHeight; } /** * Start observing the position of the specified element. * If the element is not currently attached to the DOM, * it will NOT be added to the entries. * * @param target an `Element` target */ observe = (target) => { if (!isElement(target)) throw new Error(`${errorString}: ${target} is not an instance of Element.`); /* istanbul ignore else @preserve - a guard must be set */ if (!this._r.contains(target)) return; this._n(target).then((ioEntry) => { /* istanbul ignore else @preserve - don't allow duplicate entries */ if (ioEntry.boundingClientRect && !this.getEntry(target)) this.entries.set(target, ioEntry); /* istanbul ignore else @preserve */ if (!this._t) this._t = requestAnimationFrame(this._rc); }); }; /** * Stop observing the position of the specified element. * * @param target an `Element` target */ unobserve = (target) => { /* istanbul ignore else @preserve */ if (this.entries.has(target)) this.entries.delete(target); }; /** * Private method responsible for all the heavy duty, * the observer's runtime. * `PositionObserver.runCallback` */ _rc = () => { /* istanbul ignore if @preserve - a guard must be set */ if (!this.entries.size) { this._t = 0; return; } const { clientWidth, clientHeight } = this._r; const queue = new Promise((resolve) => { const updates = []; this.entries.forEach(({ target, boundingClientRect: oldBoundingBox }) => { /* istanbul ignore if @preserve - a guard must be set when target has been removed */ if (!this._r.contains(target)) return; this._n(target).then((ioEntry) => { /* istanbul ignore if @preserve - make sure to only count visible entries */ if (!ioEntry.isIntersecting) return; const { left, top } = ioEntry.boundingClientRect; /* istanbul ignore else @preserve - only schedule entries that changed position */ if (oldBoundingBox.top !== top || oldBoundingBox.left !== left || this._w !== clientWidth || this._h !== clientHeight) { this.entries.set(target, ioEntry); updates.push(ioEntry); } }); }); this._w = clientWidth; this._h = clientHeight; resolve(updates); }); this._t = requestAnimationFrame(async () => { const updates = await queue; /* istanbul ignore else @preserve */ if (updates.length) this._c(updates, this); this._rc(); }); }; /** * Check intersection status and resolve it * right away. * * `PositionObserver.newEntryForTarget` * * @param target an `Element` target */ _n = (target) => { return new Promise((resolve) => { new IntersectionObserver(([ioEntry], ob) => { ob.disconnect(); resolve(ioEntry); }, { threshold: this._th, rootMargin: this._rm }).observe(target); }); }; /** * Find the entry for a given target. * * @param target an `HTMLElement` target */ getEntry = (target) => this.entries.get(target); /** * Immediately stop observing all elements. */ disconnect = () => { cancelAnimationFrame(this._t); this.entries.clear(); this._t = 0; }; }; //#endregion export { PositionObserver as default }; //# sourceMappingURL=index.js.map