{"version":3,"file":"index.mjs","names":["callback: PositionObserverCallback","options?: PositionObserverOptions","target: Element","updates: IntersectionObserverEntry[]"],"sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n  \"name\": \"@thednp/position-observer\",\n  \"version\": \"1.1.0\",\n  \"author\": \"thednp\",\n  \"license\": \"MIT\",\n  \"description\": \"🏯 PositionObserver is a JavaScript tool that provides a way to asynchronously observe changes in the position of a target element within its viewport.\",\n  \"homepage\": \"https://thednp.github.io/position-observer/\",\n  \"source\": \"./src/index.ts\",\n  \"main\": \"./dist/index.js\",\n  \"module\": \"./dist/index.mjs\",\n  \"types\": \"./dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"pre-test\": \"pnpm clean-coverage\",\n    \"dev\": \"vite --open ./demo/index.html --port 8577\",\n    \"serve\": \"vite --open ./docs/index.html --port 8577\",\n    \"test\": \"pnpm pre-test && vitest --config vitest.config.ts\",\n    \"test-ui\": \"pnpm pre-test && vitest --config vitest.config-ui.ts --browser=chrome\",\n    \"clean-coverage\": \"rm -rf coverage .nyc_output\",\n    \"badges\": \"npx -p dependency-version-badge update-badge typescript vitest vite\",\n    \"build\": \"tsdown\",\n    \"build_\": \"vite build\",\n    \"build:all\": \"pnpm build && pnpm build:docs\",\n    \"build:docs\": \"vite build demo --config demo/vite.config.ts\",\n    \"lint\": \"deno lint src && pnpm check:ts\",\n    \"format\": \"deno fmt src\",\n    \"check:ts\": \"tsc --noEmit\",\n    \"prepublishOnly\": \"pnpm update --latest && pnpm lint && pnpm format && pnpm build:all && pnpm badges\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/thednp/position-observer/issues\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\",\n    \"registry\": \"https://registry.npmjs.org/\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/thednp/position-observer.git\"\n  },\n  \"keywords\": [\n    \"position-observer\",\n    \"observer\",\n    \"intersection\",\n    \"resize\",\n    \"position\",\n    \"typescript\"\n  ],\n  \"dependencies\": {\n    \"@thednp/shorty\": \"^2.0.11\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^24.0.0\",\n    \"@vitest/browser\": \"^3.2.3\",\n    \"@vitest/coverage-istanbul\": \"^3.2.3\",\n    \"@vitest/ui\": \"^3.2.3\",\n    \"playwright\": \"^1.53.0\",\n    \"tsdown\": \"^0.12.7\",\n    \"typescript\": \"5.8.3\",\n    \"vite\": \"^6.3.5\",\n    \"vitest\": \"^3.2.3\"\n  },\n  \"packageManager\": \"pnpm@8.6.12\",\n  \"engines\": {\n    \"node\": \">=16\",\n    \"pnpm\": \">=8.6.0\"\n  }\n}\n","import { isElement, isFunction } from \"@thednp/shorty\";\nimport { version } from \"../package.json\";\n\nexport type PositionObserverCallback = (\n  entries: IntersectionObserverEntry[],\n  observer: PositionObserver,\n) => void;\n\nconst callbackModes = [\"all\", \"intersecting\", \"update\"] as const;\ntype CallbackMode = typeof callbackModes[number];\ntype CallbackModeIndex = 0 | 1 | 2;\n\nexport type PositionObserverOptions = {\n  root?: Element; // PositionObserver root only, IntersectionObserver root is always the document\n  rootMargin?: IntersectionObserverInit[\"rootMargin\"];\n  threshold?: IntersectionObserverInit[\"threshold\"];\n  callbackMode?: CallbackMode;\n};\n\nconst errorString = \"PositionObserver Error\";\n\n/**\n * The PositionObserver class is a utility class that observes the position\n * of DOM elements and triggers a callback when their position changes.\n */\nexport default class PositionObserver {\n  public entries: Map<Element, IntersectionObserverEntry>;\n  public static version = version;\n  /** `PositionObserver.tick` */\n  protected _t: number;\n  /** `PositionObserver.root` */\n  protected _r: Element;\n  /** `PositionObserver.callbackMode` */\n  protected _cm: CallbackModeIndex;\n  /** `PositionObserver.root.clientWidth` */\n  protected _w: number;\n  /** `PositionObserver.root.clientHeight` */\n  protected _h: number;\n  /** `IntersectionObserver.options.rootMargin` */\n  protected _rm: string | undefined;\n  /** `IntersectionObserver.options.threshold` */\n  protected _th: number | number[] | undefined;\n  /** `PositionObserver.callback` */\n  protected _c: PositionObserverCallback;\n\n  /**\n   * The constructor takes two arguments, a `callback`, which is called\n   * whenever the position of an observed element changes and an `options` object.\n   * The callback function takes an array of `PositionObserverEntry` objects\n   * as its first argument and the PositionObserver instance as its second argument.\n   *\n   * @param callback the callback that applies to all targets of this observer\n   * @param options the options of this observer\n   */\n  constructor(\n    callback: PositionObserverCallback,\n    options?: PositionObserverOptions,\n  ) {\n    if (!isFunction(callback)) {\n      throw new Error(`${errorString}: ${callback} is not a function.`);\n    }\n    this.entries = new Map<Element, IntersectionObserverEntry>();\n    this._c = callback;\n    this._t = 0;\n    const root = isElement(options?.root)\n      ? options.root\n      /* istanbul ignore next @preserve */\n      : document?.documentElement;\n    this._r = root;\n    this._rm = options?.rootMargin;\n    this._th = options?.threshold;\n    /* istanbul ignore next @preserve */\n    this._cm = callbackModes.indexOf(\n      options?.callbackMode || \"intersecting\",\n    ) as CallbackModeIndex;\n    this._w = root.clientWidth;\n    this._h = root.clientHeight;\n  }\n\n  /**\n   * Start observing the position of the specified element.\n   * If the element is not currently attached to the DOM,\n   * it will NOT be added to the entries.\n   *\n   * @param target an `Element` target\n   */\n  public observe = (target: Element) => {\n    if (!isElement(target)) {\n      throw new Error(\n        `${errorString}: ${target} is not an instance of Element.`,\n      );\n    }\n\n    /* istanbul ignore else @preserve - a guard must be set */\n    if (!this._r.contains(target)) return;\n\n    // define a new entry\n    // push the entry into the queue\n    this._n(target).then((ioEntry) => {\n      /* istanbul ignore else @preserve - don't allow duplicate entries */\n      if (ioEntry.boundingClientRect && !this.getEntry(target)) {\n        this.entries.set(target, ioEntry);\n      }\n\n      /* istanbul ignore else @preserve */\n      if (!this._t) this._t = requestAnimationFrame(this._rc);\n    });\n  };\n\n  /**\n   * Stop observing the position of the specified element.\n   *\n   * @param target an `Element` target\n   */\n  public unobserve = (target: Element) => {\n    /* istanbul ignore else @preserve */\n    if (this.entries.has(target)) this.entries.delete(target);\n  };\n\n  /**\n   * Private method responsible for all the heavy duty,\n   * the observer's runtime.\n   * `PositionObserver.runCallback`\n   */\n  protected _rc = () => {\n    /* istanbul ignore if @preserve - a guard must be set */\n    if (!this.entries.size) {\n      this._t = 0;\n      return;\n    }\n    const { clientWidth, clientHeight } = this._r;\n\n    const queue = new Promise<IntersectionObserverEntry[]>((resolve) => {\n      const updates: IntersectionObserverEntry[] = [];\n      this.entries.forEach(\n        (\n          {\n            target,\n            boundingClientRect: oldBoundingBox,\n            isIntersecting: oldIsIntersecting,\n          },\n        ) => {\n          /* istanbul ignore if @preserve - a guard must be set when target has been removed */\n          if (!this._r.contains(target)) return;\n\n          this._n(target).then((ioEntry) => {\n            /* istanbul ignore if @preserve - make sure to only count visible entries */\n            if (!ioEntry.isIntersecting) {\n              if (this._cm === 1) { // 1 = \"intersecting\"\n                return;\n              } else if (this._cm === 2) { // 2 = \"update\"\n                if (oldIsIntersecting) {\n                  this.entries.set(target, ioEntry);\n                  updates.push(ioEntry);\n                }\n                return;\n              }\n            }\n            // 0 = \"all\"\n            const { left, top } = ioEntry.boundingClientRect;\n\n            /* istanbul ignore else @preserve - only schedule entries that changed position */\n            if (\n              oldBoundingBox.top !== top || oldBoundingBox.left !== left ||\n              this._w !== clientWidth || this._h !== clientHeight\n            ) {\n              this.entries.set(target, ioEntry);\n              updates.push(ioEntry);\n            }\n          });\n        },\n      );\n      // update root client width & height\n      this._w = clientWidth;\n      this._h = clientHeight;\n\n      resolve(updates);\n    });\n\n    this._t = requestAnimationFrame(async () => {\n      // execute the queue\n      const updates: IntersectionObserverEntry[] = await queue;\n\n      // only execute the callback if position actually changed\n      /* istanbul ignore else @preserve */\n      if (updates.length) this._c(updates, this);\n\n      this._rc();\n    });\n  };\n\n  /**\n   * Check intersection status and resolve it\n   * right away.\n   *\n   * `PositionObserver.newEntryForTarget`\n   *\n   * @param target an `Element` target\n   */\n  protected _n = (target: Element) => {\n    return new Promise<IntersectionObserverEntry>((resolve) => {\n      const intersectionObserver = new IntersectionObserver(\n        ([ioEntry], ob) => {\n          ob.disconnect();\n          resolve(ioEntry);\n        },\n        {\n          // IntersectionObserver.root is always the document\n          // root: target.ownerDocument || document,\n          threshold: this._th,\n          rootMargin: this._rm,\n        },\n      );\n\n      intersectionObserver.observe(target);\n    });\n  };\n\n  /**\n   * Find the entry for a given target.\n   *\n   * @param target an `HTMLElement` target\n   */\n  public getEntry = (target: Element) => this.entries.get(target);\n\n  /**\n   * Immediately stop observing all elements.\n   */\n  public disconnect = () => {\n    cancelAnimationFrame(this._t);\n    this.entries.clear();\n    this._t = 0;\n  };\n}\n"],"mappings":"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"}