{
  "version": 3,
  "sources": ["../../src/canvas/mobile-placement.ts"],
  "sourcesContent": ["import { RefObject, useCallback, useLayoutEffect, useRef, useState } from 'react'\nimport { PORTRAIT_BREAKPOINT, useBreakpoint, useContainer } from 'tldraw'\nimport { getVisibleViewport } from '../ui/visual-viewport'\n\n/**\n * Mobile mode for the commenting surfaces \u2014 the same breakpoint gate as tldraw's mobile toolbar and\n * style panel (which also honors `forceMobile` on `<Tldraw>`). The commenting layer can mount\n * without tldraw's default UI (`hideUi`, custom UI), where the breakpoint provider is absent and\n * `useBreakpoint` throws; fall back to desktop there, matching the pre-mobile rendering those hosts\n * always had.\n */\nexport function useIsMobileCommenting(): boolean {\n\tlet breakpoint: number\n\ttry {\n\t\t// The call is unconditional \u2014 the try only guards the provider's absence, not hook order.\n\t\t// oxlint-disable-next-line react-hooks/rules-of-hooks\n\t\tbreakpoint = useBreakpoint()\n\t} catch {\n\t\treturn false\n\t}\n\treturn breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM\n}\n\nconst VIEWPORT_MARGIN = 8\n\n/**\n * Keep a canvas-anchored panel \u2014 the pending composer or an open thread popover \u2014 within the\n * visible viewport on mobile. The software keyboard shrinks the *visual* viewport while leaving the\n * layout viewport (and CSS `dvh`) untouched, so a panel placed at a fixed offset from its pin can\n * end up behind the keyboard. On mobile this clamps the panel's top-left into the visible box so it\n * stays on-screen above the keyboard. Desktop is untouched: when `enabled` is false the base point\n * is returned unchanged, tracking the camera exactly as before.\n *\n * `base` is the panel's desired top-left in container-relative viewport coordinates (what the call\n * site would otherwise write straight into `left`/`top`).\n */\nexport function useMobilePlacement(\n\tref: RefObject<HTMLElement | null>,\n\tbase: { x: number; y: number },\n\tenabled: boolean\n): { left: number; top: number } {\n\tconst container = useContainer()\n\t// Destructured so the effects below depend on the two numbers rather than the object, which the\n\t// call sites build fresh on every render.\n\tconst { x: baseX, y: baseY } = base\n\tconst [placed, setPlaced] = useState<{ left: number; top: number }>(() => ({\n\t\tleft: baseX,\n\t\ttop: baseY,\n\t}))\n\t// The camera moves `base` every frame while panning. Reading it from a ref keeps `update` stable\n\t// across those frames, so the observers below are set up once per panel rather than being torn\n\t// down and rebuilt each frame.\n\tconst baseRef = useRef({ x: baseX, y: baseY })\n\n\tconst update = useCallback(() => {\n\t\tif (!enabled) return\n\t\tconst el = ref.current\n\t\tif (!el) return\n\t\tconst win = container.ownerDocument.defaultView ?? window\n\t\tconst { x, y } = baseRef.current\n\n\t\t// Panel coordinates are container-relative; the visual viewport is window-relative.\n\t\tconst cRect = container.getBoundingClientRect()\n\t\tconst vp = getVisibleViewport(win)\n\t\tconst top = vp.top - cRect.top + VIEWPORT_MARGIN\n\t\tconst bottom = vp.bottom - cRect.top - VIEWPORT_MARGIN\n\t\tconst left = vp.left - cRect.left + VIEWPORT_MARGIN\n\t\tconst right = vp.right - cRect.left - VIEWPORT_MARGIN\n\n\t\tconst w = el.offsetWidth\n\t\tconst h = el.offsetHeight\n\n\t\tconst nextLeft = Math.max(left, Math.min(x, right - w))\n\t\t// The keyboard only ever covers space from the bottom, so it can pull the panel up (when its\n\t\t// bottom would be hidden) but must never push it down below its natural spot. `y` is the\n\t\t// floor: any spurious rise in `top` (e.g. iOS scrolling the page to reveal a focused input)\n\t\t// is capped here, so a panel that already clears the keyboard doesn't move at all.\n\t\tconst nextTop = Math.min(y, Math.max(top, bottom - h))\n\t\tsetPlaced((prev) =>\n\t\t\tprev.left === nextLeft && prev.top === nextTop ? prev : { left: nextLeft, top: nextTop }\n\t\t)\n\t}, [container, ref, enabled])\n\n\t// Re-place as the camera moves the panel's base point.\n\tuseLayoutEffect(() => {\n\t\tbaseRef.current = { x: baseX, y: baseY }\n\t\tupdate()\n\t}, [baseX, baseY, update])\n\n\t// Re-place when the panel grows (replies, edits), when the visual viewport changes (keyboard,\n\t// pinch-zoom), or when the window resizes.\n\tuseLayoutEffect(() => {\n\t\tif (!enabled) return\n\t\tconst el = ref.current\n\t\tif (!el) return\n\t\tconst win = container.ownerDocument.defaultView ?? window\n\t\tconst ro = new ResizeObserver(update)\n\t\tro.observe(el)\n\t\tconst vv = win.visualViewport\n\t\tvv?.addEventListener('resize', update)\n\t\tvv?.addEventListener('scroll', update)\n\t\twin.addEventListener('resize', update)\n\t\treturn () => {\n\t\t\tro.disconnect()\n\t\t\tvv?.removeEventListener('resize', update)\n\t\t\tvv?.removeEventListener('scroll', update)\n\t\t\twin.removeEventListener('resize', update)\n\t\t}\n\t}, [container, ref, enabled, update])\n\n\t// Desktop keeps its fixed placement, recomputed each render so it tracks the pin as the camera\n\t// moves.\n\tif (!enabled) return { left: baseX, top: baseY }\n\treturn placed\n}\n"],
  "mappings": "AAAA,SAAoB,aAAa,iBAAiB,QAAQ,gBAAgB;AAC1E,SAAS,qBAAqB,eAAe,oBAAoB;AACjE,SAAS,0BAA0B;AAS5B,SAAS,wBAAiC;AAChD,MAAI;AACJ,MAAI;AAGH,iBAAa,cAAc;AAAA,EAC5B,QAAQ;AACP,WAAO;AAAA,EACR;AACA,SAAO,aAAa,oBAAoB;AACzC;AAEA,MAAM,kBAAkB;AAajB,SAAS,mBACf,KACA,MACA,SACgC;AAChC,QAAM,YAAY,aAAa;AAG/B,QAAM,EAAE,GAAG,OAAO,GAAG,MAAM,IAAI;AAC/B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwC,OAAO;AAAA,IAC1E,MAAM;AAAA,IACN,KAAK;AAAA,EACN,EAAE;AAIF,QAAM,UAAU,OAAO,EAAE,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,QAAM,SAAS,YAAY,MAAM;AAChC,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,MAAM,UAAU,cAAc,eAAe;AACnD,UAAM,EAAE,GAAG,EAAE,IAAI,QAAQ;AAGzB,UAAM,QAAQ,UAAU,sBAAsB;AAC9C,UAAM,KAAK,mBAAmB,GAAG;AACjC,UAAM,MAAM,GAAG,MAAM,MAAM,MAAM;AACjC,UAAM,SAAS,GAAG,SAAS,MAAM,MAAM;AACvC,UAAM,OAAO,GAAG,OAAO,MAAM,OAAO;AACpC,UAAM,QAAQ,GAAG,QAAQ,MAAM,OAAO;AAEtC,UAAM,IAAI,GAAG;AACb,UAAM,IAAI,GAAG;AAEb,UAAM,WAAW,KAAK,IAAI,MAAM,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC;AAKtD,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,CAAC,CAAC;AACrD;AAAA,MAAU,CAAC,SACV,KAAK,SAAS,YAAY,KAAK,QAAQ,UAAU,OAAO,EAAE,MAAM,UAAU,KAAK,QAAQ;AAAA,IACxF;AAAA,EACD,GAAG,CAAC,WAAW,KAAK,OAAO,CAAC;AAG5B,kBAAgB,MAAM;AACrB,YAAQ,UAAU,EAAE,GAAG,OAAO,GAAG,MAAM;AACvC,WAAO;AAAA,EACR,GAAG,CAAC,OAAO,OAAO,MAAM,CAAC;AAIzB,kBAAgB,MAAM;AACrB,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,MAAM,UAAU,cAAc,eAAe;AACnD,UAAM,KAAK,IAAI,eAAe,MAAM;AACpC,OAAG,QAAQ,EAAE;AACb,UAAM,KAAK,IAAI;AACf,QAAI,iBAAiB,UAAU,MAAM;AACrC,QAAI,iBAAiB,UAAU,MAAM;AACrC,QAAI,iBAAiB,UAAU,MAAM;AACrC,WAAO,MAAM;AACZ,SAAG,WAAW;AACd,UAAI,oBAAoB,UAAU,MAAM;AACxC,UAAI,oBAAoB,UAAU,MAAM;AACxC,UAAI,oBAAoB,UAAU,MAAM;AAAA,IACzC;AAAA,EACD,GAAG,CAAC,WAAW,KAAK,SAAS,MAAM,CAAC;AAIpC,MAAI,CAAC,QAAS,QAAO,EAAE,MAAM,OAAO,KAAK,MAAM;AAC/C,SAAO;AACR;",
  "names": []
}
