diff --git a/artifacts/tx-os/src/components/ui/dialog.tsx b/artifacts/tx-os/src/components/ui/dialog.tsx index 23e33173..55d88347 100644 --- a/artifacts/tx-os/src/components/ui/dialog.tsx +++ b/artifacts/tx-os/src/components/ui/dialog.tsx @@ -32,7 +32,7 @@ const DialogContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, children, style, ...props }, ref) => { - const { keyboardInset, width, height, offsetTop, offsetLeft } = useVisualViewport() + const { keyboardInset, height, offsetTop } = useVisualViewport() // Only react when an on-screen keyboard is actually present. iOS Safari // and Android Chrome both report a non-trivial bottom inset (>= ~150px) // when their software keyboard opens; tiny insets from browser chrome @@ -60,17 +60,24 @@ const DialogContent = React.forwardRef< // dialog's internal `flex flex-col` (header / scrollable middle // / sticky footer) then keeps Save/Cancel pinned above the // keyboard and the rest of the form scrolls inside. - // Use the visualViewport rectangle directly. position:fixed on - // iOS Safari is measured against the LAYOUT viewport, which - // diverges from the visual viewport when the soft keyboard is - // open AND when Safari pinch/auto-zooms a focused input. So we - // must explicitly set top/left/width to the visual viewport's - // offsetTop/offsetLeft/width and zero out the centering - // translates the base className applies. + // `position: fixed` on iOS is anchored to the LAYOUT viewport, + // which stays at `window.innerWidth` even while the visual + // viewport shrinks (keyboard, pinch-zoom, etc). If we set + // width/left from `visualViewport.width/offsetLeft` the dialog + // ends up rendered at a fraction of the screen — a narrow + // strip pinned to one corner — because those values describe + // the *visible* rectangle in layout pixels, not the dialog's + // own container. + // + // Pin horizontally to the layout viewport (0 / 100vw) so the + // dialog always spans the full width the iframe sees, and pin + // vertically to the visual viewport so it sits just above the + // keyboard. `maxHeight` uses `vv.height` so the dialog's + // internal scroll keeps the footer above the keyboard. top: `${offsetTop}px`, - left: `${offsetLeft}px`, - right: "auto", - width: `${width}px`, + left: 0, + right: 0, + width: "100vw", maxWidth: "none", maxHeight: `${height - 8}px`, transform: "none",