mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 08:11:48 +01:00
router positions
This commit is contained in:
parent
68441b90e5
commit
4a2a8e89cc
18
src/components/overlays/OverlayRouter.tsx
Normal file
18
src/components/overlays/OverlayRouter.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
import { OverlayAnchorPosition } from "@/components/overlays/positions/OverlayAnchorPosition";
|
||||||
|
import { OverlayMobilePosition } from "@/components/overlays/positions/OverlayMobilePosition";
|
||||||
|
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||||
|
|
||||||
|
interface OverlayRouterProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OverlayRouter(props: OverlayRouterProps) {
|
||||||
|
const { isMobile } = useIsMobile();
|
||||||
|
const content = props.children;
|
||||||
|
|
||||||
|
if (isMobile) return <OverlayMobilePosition>{content}</OverlayMobilePosition>;
|
||||||
|
return <OverlayAnchorPosition id={props.id}>{content}</OverlayAnchorPosition>;
|
||||||
|
}
|
82
src/components/overlays/positions/OverlayAnchorPosition.tsx
Normal file
82
src/components/overlays/positions/OverlayAnchorPosition.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import { createOverlayAnchorEvent } from "@/components/overlays/OverlayAnchor";
|
||||||
|
|
||||||
|
interface AnchorPositionProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
id: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OverlayAnchorPosition(props: AnchorPositionProps) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [left, setLeft] = useState<number>(0);
|
||||||
|
const [top, setTop] = useState<number>(0);
|
||||||
|
const [cardRect, setCardRect] = useState<DOMRect | null>(null);
|
||||||
|
const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null);
|
||||||
|
|
||||||
|
const calculateAndSetCoords = useCallback(
|
||||||
|
(anchor: DOMRect, card: DOMRect) => {
|
||||||
|
const buttonCenter = anchor.left + anchor.width / 2;
|
||||||
|
const bottomReal = window.innerHeight - anchor.bottom;
|
||||||
|
|
||||||
|
setTop(
|
||||||
|
window.innerHeight - bottomReal - anchor.height - card.height - 30
|
||||||
|
);
|
||||||
|
setLeft(
|
||||||
|
Math.min(
|
||||||
|
buttonCenter - card.width / 2,
|
||||||
|
window.innerWidth - card.width - 30
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!anchorRect || !cardRect) return;
|
||||||
|
calculateAndSetCoords(anchorRect, cardRect);
|
||||||
|
}, [anchorRect, calculateAndSetCoords, cardRect]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref.current) return;
|
||||||
|
function checkBox() {
|
||||||
|
const divRect = ref.current?.getBoundingClientRect();
|
||||||
|
setCardRect(divRect ?? null);
|
||||||
|
}
|
||||||
|
checkBox();
|
||||||
|
const observer = new ResizeObserver(checkBox);
|
||||||
|
observer.observe(ref.current);
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const evtStr = createOverlayAnchorEvent(props.id);
|
||||||
|
if ((window as any)[evtStr]) setAnchorRect((window as any)[evtStr]);
|
||||||
|
function listen(ev: CustomEvent<DOMRect>) {
|
||||||
|
setAnchorRect(ev.detail);
|
||||||
|
}
|
||||||
|
document.addEventListener(evtStr, listen as any);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener(evtStr, listen as any);
|
||||||
|
};
|
||||||
|
}, [props.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
style={{
|
||||||
|
transform: `translateX(${left}px) translateY(${top}px)`,
|
||||||
|
}}
|
||||||
|
className={classNames([
|
||||||
|
"pointer-events-auto z-10 inline-block origin-top-left touch-none overflow-hidden",
|
||||||
|
props.className,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
20
src/components/overlays/positions/OverlayMobilePosition.tsx
Normal file
20
src/components/overlays/positions/OverlayMobilePosition.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
interface MobilePositionProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OverlayMobilePosition(props: MobilePositionProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames([
|
||||||
|
"pointer-events-auto z-10 inline-block origin-top-left touch-none overflow-hidden",
|
||||||
|
props.className,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -73,6 +73,7 @@ export function useInternalOverlayRouter(id: string) {
|
|||||||
export function useOverlayRouter(id: string) {
|
export function useOverlayRouter(id: string) {
|
||||||
const router = useInternalOverlayRouter(id);
|
const router = useInternalOverlayRouter(id);
|
||||||
return {
|
return {
|
||||||
|
id,
|
||||||
open: router.open,
|
open: router.open,
|
||||||
close: router.close,
|
close: router.close,
|
||||||
navigate: router.navigate,
|
navigate: router.navigate,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor";
|
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor";
|
||||||
import { Overlay, OverlayDisplay } from "@/components/overlays/OverlayDisplay";
|
import { Overlay, OverlayDisplay } from "@/components/overlays/OverlayDisplay";
|
||||||
import { OverlayPage } from "@/components/overlays/OverlayPage";
|
import { OverlayPage } from "@/components/overlays/OverlayPage";
|
||||||
|
import { OverlayRouter } from "@/components/overlays/OverlayRouter";
|
||||||
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||||
|
|
||||||
// simple empty view, perfect for putting in tests
|
// simple empty view, perfect for putting in tests
|
||||||
@ -18,57 +19,59 @@ export default function TestView() {
|
|||||||
>
|
>
|
||||||
Open
|
Open
|
||||||
</button>
|
</button>
|
||||||
<OverlayAnchor id="test">
|
<OverlayAnchor id={router.id}>
|
||||||
<div className="h-20 w-20 bg-white" />
|
<div className="h-20 w-20 mt-64 bg-white" />
|
||||||
</OverlayAnchor>
|
</OverlayAnchor>
|
||||||
<Overlay id="test">
|
<Overlay id={router.id}>
|
||||||
<OverlayPage id="test" path="/">
|
<OverlayRouter id={router.id}>
|
||||||
<div className="bg-blue-900 p-4">
|
<OverlayPage id={router.id} path="/" width={400} height={400}>
|
||||||
<p>HOME</p>
|
<div className="bg-blue-900 p-4">
|
||||||
<button
|
<p>HOME</p>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => {
|
type="button"
|
||||||
router.navigate("/two");
|
onClick={() => {
|
||||||
}}
|
router.navigate("/two");
|
||||||
>
|
}}
|
||||||
open page two
|
>
|
||||||
</button>
|
open page two
|
||||||
<button
|
</button>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => {
|
type="button"
|
||||||
router.navigate("/one");
|
onClick={() => {
|
||||||
}}
|
router.navigate("/one");
|
||||||
>
|
}}
|
||||||
open page one
|
>
|
||||||
</button>
|
open page one
|
||||||
</div>
|
</button>
|
||||||
</OverlayPage>
|
</div>
|
||||||
<OverlayPage id="test" path="/one">
|
</OverlayPage>
|
||||||
<div className="bg-blue-900 p-4">
|
<OverlayPage id={router.id} path="/one">
|
||||||
<p>ONE</p>
|
<div className="bg-blue-900 p-4">
|
||||||
<button
|
<p>ONE</p>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => {
|
type="button"
|
||||||
router.navigate("/");
|
onClick={() => {
|
||||||
}}
|
router.navigate("/");
|
||||||
>
|
}}
|
||||||
back home
|
>
|
||||||
</button>
|
back home
|
||||||
</div>
|
</button>
|
||||||
</OverlayPage>
|
</div>
|
||||||
<OverlayPage id="test" path="/two">
|
</OverlayPage>
|
||||||
<div className="bg-blue-900 p-4">
|
<OverlayPage id={router.id} path="/two">
|
||||||
<p>TWO</p>
|
<div className="bg-blue-900 p-4">
|
||||||
<button
|
<p>TWO</p>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => {
|
type="button"
|
||||||
router.navigate("/");
|
onClick={() => {
|
||||||
}}
|
router.navigate("/");
|
||||||
>
|
}}
|
||||||
back home
|
>
|
||||||
</button>
|
back home
|
||||||
</div>
|
</button>
|
||||||
</OverlayPage>
|
</div>
|
||||||
|
</OverlayPage>
|
||||||
|
</OverlayRouter>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
</OverlayDisplay>
|
</OverlayDisplay>
|
||||||
|
Loading…
Reference in New Issue
Block a user