Files
zopu-code/apps/web/src/hooks/workspace/use-visual-viewport.ts

53 lines
1.6 KiB
TypeScript

import type { CSSProperties } from "react";
import { useEffect, useState } from "react";
export interface VisualViewportFrame {
readonly height: number;
readonly offsetTop: number;
}
const readVisualViewport = (): VisualViewportFrame => ({
height: Math.round(window.visualViewport?.height ?? window.innerHeight),
offsetTop: Math.round(window.visualViewport?.offsetTop ?? 0),
});
export const visualViewportStyle = (
frame?: VisualViewportFrame
): CSSProperties => ({
height: frame ? `${frame.height}px` : "100dvh",
transform: frame?.offsetTop ? `translateY(${frame.offsetTop}px)` : undefined,
});
export const useVisualViewportStyle = (): CSSProperties => {
const [frame, setFrame] = useState<VisualViewportFrame>();
useEffect(() => {
const root = document.documentElement;
const viewport = window.visualViewport;
let animationFrame = 0;
const update = () => {
cancelAnimationFrame(animationFrame);
animationFrame = requestAnimationFrame(() => {
setFrame(readVisualViewport());
});
};
root.classList.add("workspace-viewport-lock");
update();
window.addEventListener("resize", update);
viewport?.addEventListener("resize", update);
viewport?.addEventListener("scroll", update);
return () => {
cancelAnimationFrame(animationFrame);
root.classList.remove("workspace-viewport-lock");
window.removeEventListener("resize", update);
viewport?.removeEventListener("resize", update);
viewport?.removeEventListener("scroll", update);
};
}, []);
return visualViewportStyle(frame);
};