mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-24 12:18:21 -05:00
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import * as React from "react";
|
|
import { isScrollLocked } from "~/modules/scroll-lock/scroll-lock";
|
|
import { keyboardIsOpen } from "~/utils/visual-viewport";
|
|
import { floatingBounds } from "./useFloatingLayer";
|
|
|
|
const PADDING_PROPERTY = "--floating-viewport-padding";
|
|
|
|
// Brings the anchor of an open popover back on screen if the mobile keyboard opens over it
|
|
export function useScrollIntoView(
|
|
isOpen: boolean,
|
|
getAnchor: () => Element | null,
|
|
) {
|
|
const getAnchorRef = React.useRef(getAnchor);
|
|
getAnchorRef.current = getAnchor;
|
|
|
|
React.useEffect(() => {
|
|
const viewport = window.visualViewport;
|
|
if (!isOpen || !viewport) return;
|
|
|
|
const onResize = () => {
|
|
if (!keyboardIsOpen()) return;
|
|
const anchor = getAnchorRef.current();
|
|
if (anchor) {
|
|
revealAboveKeyboard(anchor);
|
|
}
|
|
};
|
|
|
|
viewport.addEventListener("resize", onResize);
|
|
return () => viewport.removeEventListener("resize", onResize);
|
|
}, [isOpen]);
|
|
}
|
|
|
|
function revealAboveKeyboard(anchor: Element) {
|
|
anchor.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
if (isScrollLocked()) return;
|
|
|
|
const padding =
|
|
Number.parseFloat(
|
|
getComputedStyle(anchor).getPropertyValue(PADDING_PROPERTY),
|
|
) || 0;
|
|
const bounds = floatingBounds(anchor);
|
|
const rect = anchor.getBoundingClientRect();
|
|
const below = rect.bottom - (bounds.bottom - padding);
|
|
const above = bounds.top + padding - rect.top;
|
|
|
|
if (below > 0) {
|
|
window.scrollBy({ top: below });
|
|
} else if (above > 0) {
|
|
window.scrollBy({ top: -above });
|
|
}
|
|
}
|