a tool for shared writing and social publishing
1"use client";
2import { useEffect, useState } from "react";
3import { isIOS } from "src/utils/isDevice";
4
5export function ViewportSizeLayout(props: { children: React.ReactNode }) {
6 let viewheight = useViewportSize().height;
7 let difference = useViewportDifference();
8 return (
9 <div
10 style={{
11 height:
12 isIOS() && difference !== 0
13 ? `calc(${viewheight}px + 10px)`
14 : "calc(100% + env(safe-area-inset-top)",
15 }}
16 >
17 {props.children}
18 </div>
19 );
20}
21
22function useViewportDifference(): number {
23 let [difference, setDifference] = useState(0);
24
25 useEffect(() => {
26 // Use visualViewport api to track available height even on iOS virtual keyboard opening
27 let onResize = () => {
28 setDifference(window.innerHeight - getViewportSize().height);
29 };
30
31 if (!visualViewport) {
32 window.addEventListener("resize", onResize);
33 } else {
34 visualViewport.addEventListener("resize", onResize);
35 }
36
37 return () => {
38 if (!visualViewport) {
39 window.removeEventListener("resize", onResize);
40 } else {
41 visualViewport.removeEventListener("resize", onResize);
42 }
43 };
44 }, []);
45
46 return difference;
47}
48
49function getViewportSize() {
50 if (typeof window === "undefined") return { width: 0, height: 0 };
51 return {
52 width: visualViewport?.width || window?.innerWidth,
53 height: visualViewport?.height || window?.innerHeight,
54 };
55}
56
57export function useViewportSize(): {
58 width: number;
59 height: number;
60} {
61 let [size, setSize] = useState(() => getViewportSize());
62
63 useEffect(() => {
64 // Use visualViewport api to track available height even on iOS virtual keyboard opening
65 let onResize = () => {
66 setSize((size) => {
67 let newSize = getViewportSize();
68 if (newSize.width === size.width && newSize.height === size.height) {
69 return size;
70 }
71 return newSize;
72 });
73 };
74
75 if (!visualViewport) {
76 window.addEventListener("resize", onResize);
77 } else {
78 visualViewport.addEventListener("resize", onResize);
79 }
80
81 return () => {
82 if (!visualViewport) {
83 window.removeEventListener("resize", onResize);
84 } else {
85 visualViewport.removeEventListener("resize", onResize);
86 }
87 };
88 }, []);
89
90 return size;
91}