Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/improsocial/impro
An extensible Bluesky client for web impro.social
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566import { canHover } from "/js/utils.js";
// Delegated hover detection for any element inside `rootEl` that matches// `selector`. Fires `onEnter(target)` when the pointer enters a matching// element, `onLeave(target)` when it leaves.export class HoverObserver { constructor(rootEl, { selector, onEnter, onLeave, ignore } = {}) { if (!rootEl || !selector) { throw new Error("HoverObserver requires rootEl and selector"); } this.rootEl = rootEl; this.selector = selector; this.onEnter = onEnter; this.onLeave = onLeave; this.ignore = ignore ?? (() => false); this.currentTarget = null;
this._onPointerMove = this._onPointerMove.bind(this); this._onPointerOut = this._onPointerOut.bind(this); rootEl.addEventListener("pointermove", this._onPointerMove); rootEl.addEventListener("pointerout", this._onPointerOut); }
disconnect() { this.rootEl.removeEventListener("pointermove", this._onPointerMove); this.rootEl.removeEventListener("pointerout", this._onPointerOut); this._leaveCurrent(); }
// Force a leave. Consumers use this after their own external dismiss // signals (mouseup, route change) so a subsequent pointer move onto the // same target still fires a fresh enter. clear() { this._leaveCurrent(); }
_onPointerMove(event) { if (!canHover()) return; const target = event.target?.closest?.(this.selector); if (target && target === this.currentTarget) return; if (!target || this.ignore(target)) { if (this.currentTarget && !this.currentTarget.contains(event.target)) { this._leaveCurrent(); } return; } if (this.currentTarget) this._leaveCurrent(); this.currentTarget = target; this.onEnter?.(target); }
_onPointerOut(event) { if (!this.currentTarget) return; const related = event.relatedTarget; if (related && this.currentTarget.contains(related)) return; this._leaveCurrent(); }
_leaveCurrent() { if (!this.currentTarget) return; const target = this.currentTarget; this.currentTarget = null; this.onLeave?.(target); }}