{"version":3,"sources":["../../../../src/elements/useElementBounding/core.ts"],"sourcesContent":["import { observable, type Observable } from \"@legendapp/state\";\nimport {\n  createSupported,\n  createObserve,\n  onUnmount,\n  peek,\n  type DeepMaybeObservable,\n} from \"@usels/core\";\nimport { normalizeTargets } from \"@shared/normalizeTargets\";\nimport { resolveWindowSource, type ConfigurableWindow } from \"@shared/configurable\";\nimport type { MaybeEventTarget } from \"../../types\";\nimport { createResizeObserver } from \"../useResizeObserver/core\";\nimport { createMutationObserver } from \"../useMutationObserver/core\";\nimport { createEventListener } from \"../../browser/useEventListener/core\";\n\nexport interface UseElementBoundingOptions extends ConfigurableWindow {\n  /** Reset all values to 0 when element unmounts. Default: true */\n  reset?: boolean;\n  /** Re-calculate on window resize. Default: true */\n  windowResize?: boolean;\n  /** Re-calculate on window scroll. Default: true */\n  windowScroll?: boolean;\n  /** Calculate immediately on mount. Default: true */\n  immediate?: boolean;\n  /** Use requestAnimationFrame to read rect after CSS transforms settle. Default: true */\n  useCssTransforms?: boolean;\n}\n\nexport interface UseElementBoundingReturn {\n  x$: Observable<number>;\n  y$: Observable<number>;\n  top$: Observable<number>;\n  right$: Observable<number>;\n  bottom$: Observable<number>;\n  left$: Observable<number>;\n  width$: Observable<number>;\n  height$: Observable<number>;\n  update: () => void;\n}\n\nconst ZERO = {\n  x: 0,\n  y: 0,\n  top: 0,\n  right: 0,\n  bottom: 0,\n  left: 0,\n  width: 0,\n  height: 0,\n};\n\n/**\n * Framework-agnostic bounding-rect tracker. Must be called inside a `useScope`\n * factory — ResizeObserver / MutationObserver / window listeners are registered\n * via the scope-aware `create*` helpers and are cleaned up automatically when\n * the scope disposes. Public API matches the legacy `useElementBounding` hook.\n */\nexport function createElementBounding(\n  target: MaybeEventTarget,\n  options?: DeepMaybeObservable<UseElementBoundingOptions>\n): UseElementBoundingReturn {\n  const opts$ = observable(options) as unknown as Observable<UseElementBoundingOptions>;\n\n  // Mount-time toggles — these do not need to be reactive: they gate whether\n  // the window scroll/resize listeners and reset behavior are enabled at all.\n  const peeked = opts$.peek() ?? {};\n  const reset = peeked.reset !== false;\n  const windowScroll = peeked.windowScroll !== false;\n  const windowResize = peeked.windowResize !== false;\n\n  const win$ = resolveWindowSource(opts$.window as unknown as Observable<unknown>);\n\n  // SSR / mount gate — `createSupported` stays `false` until `onBeforeMount`\n  // fires (client-side useLayoutEffect), then flips `true` only if a window\n  // with `requestAnimationFrame` is actually available. Used to guard all\n  // DOM reads and rAF scheduling so they never run during SSR render.\n  const isSupported$ = createSupported(() => {\n    const win = win$.peek();\n    return !!win && typeof win.requestAnimationFrame === \"function\";\n  });\n\n  const bounding$ = observable({ ...ZERO });\n\n  // Guards rAF callbacks from updating state after unmount.\n  let unmounted = false;\n  let rafHandle: number | null = null;\n\n  const recalculate = () => {\n    if (unmounted) return;\n    const el = peek(target) as Element | null;\n    if (!el || !(el instanceof Element)) {\n      if (reset) bounding$.assign({ ...ZERO });\n      return;\n    }\n    const rect = el.getBoundingClientRect();\n    bounding$.assign({\n      x: rect.x,\n      y: rect.y,\n      top: rect.top,\n      right: rect.right,\n      bottom: rect.bottom,\n      left: rect.left,\n      width: rect.width,\n      height: rect.height,\n    });\n  };\n\n  const update = () => {\n    // SSR guard — before onBeforeMount fires, `isSupported$` is false.\n    if (!isSupported$.peek()) return;\n    const win = win$.peek();\n    if (!win) return;\n    if (opts$.peek()?.useCssTransforms !== false) {\n      rafHandle = win.requestAnimationFrame(() => {\n        if (!unmounted) recalculate();\n      });\n    } else {\n      recalculate();\n    }\n  };\n\n  // Observe element size changes\n  createResizeObserver(target, update);\n\n  // Observe style/class attribute changes (e.g. CSS transitions, class toggles)\n  createMutationObserver(target, update, {\n    attributes: true,\n    attributeFilter: [\"style\", \"class\"],\n  });\n\n  // Observe window scroll / resize — gate at mount time so passing null\n  // skips the listener entirely when the corresponding toggle is false.\n  createEventListener(\n    windowScroll ? (win$ as unknown as Observable<unknown>) : null,\n    \"scroll\",\n    update,\n    { passive: true }\n  );\n  createEventListener(\n    windowResize ? (win$ as unknown as Observable<unknown>) : null,\n    \"resize\",\n    update,\n    { passive: true }\n  );\n\n  // React to target changes (register element as reactive dep via\n  // normalizeTargets) AND react to mount state (`isSupported$`) flipping true\n  // on onBeforeMount — which is how the initial post-mount read is scheduled\n  // without touching DOM / rAF during SSR render.\n  createObserve(() => {\n    if (!isSupported$.get()) return;\n    normalizeTargets(target);\n    update();\n  });\n\n  onUnmount(() => {\n    unmounted = true;\n    if (rafHandle !== null) {\n      win$.peek()?.cancelAnimationFrame(rafHandle);\n      rafHandle = null;\n    }\n    if (reset) bounding$.assign({ ...ZERO });\n  });\n\n  return {\n    x$: bounding$.x,\n    y$: bounding$.y,\n    top$: bounding$.top,\n    right$: bounding$.right,\n    bottom$: bounding$.bottom,\n    left$: bounding$.left,\n    width$: bounding$.width,\n    height$: bounding$.height,\n    update,\n  };\n}\n"],"mappings":"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","names":[]}