Browse Source

router positions

pull/497/head
mrjvs 2 years ago
parent
commit
4a2a8e89cc
  1. 18
      src/components/overlays/OverlayRouter.tsx
  2. 82
      src/components/overlays/positions/OverlayAnchorPosition.tsx
  3. 20
      src/components/overlays/positions/OverlayMobilePosition.tsx
  4. 1
      src/hooks/useOverlayRouter.ts
  5. 103
      src/pages/developer/TestView.tsx

18
src/components/overlays/OverlayRouter.tsx

@ -0,0 +1,18 @@ @@ -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

@ -0,0 +1,82 @@ @@ -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

@ -0,0 +1,20 @@ @@ -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>
);
}

1
src/hooks/useOverlayRouter.ts

@ -73,6 +73,7 @@ export function useInternalOverlayRouter(id: string) { @@ -73,6 +73,7 @@ export function useInternalOverlayRouter(id: string) {
export function useOverlayRouter(id: string) {
const router = useInternalOverlayRouter(id);
return {
id,
open: router.open,
close: router.close,
navigate: router.navigate,

103
src/pages/developer/TestView.tsx

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor";
import { Overlay, OverlayDisplay } from "@/components/overlays/OverlayDisplay";
import { OverlayPage } from "@/components/overlays/OverlayPage";
import { OverlayRouter } from "@/components/overlays/OverlayRouter";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
// simple empty view, perfect for putting in tests
@ -18,57 +19,59 @@ export default function TestView() { @@ -18,57 +19,59 @@ export default function TestView() {
>
Open
</button>
<OverlayAnchor id="test">
<div className="h-20 w-20 bg-white" />
<OverlayAnchor id={router.id}>
<div className="h-20 w-20 mt-64 bg-white" />
</OverlayAnchor>
<Overlay id="test">
<OverlayPage id="test" path="/">
<div className="bg-blue-900 p-4">
<p>HOME</p>
<button
type="button"
onClick={() => {
router.navigate("/two");
}}
>
open page two
</button>
<button
type="button"
onClick={() => {
router.navigate("/one");
}}
>
open page one
</button>
</div>
</OverlayPage>
<OverlayPage id="test" path="/one">
<div className="bg-blue-900 p-4">
<p>ONE</p>
<button
type="button"
onClick={() => {
router.navigate("/");
}}
>
back home
</button>
</div>
</OverlayPage>
<OverlayPage id="test" path="/two">
<div className="bg-blue-900 p-4">
<p>TWO</p>
<button
type="button"
onClick={() => {
router.navigate("/");
}}
>
back home
</button>
</div>
</OverlayPage>
<Overlay id={router.id}>
<OverlayRouter id={router.id}>
<OverlayPage id={router.id} path="/" width={400} height={400}>
<div className="bg-blue-900 p-4">
<p>HOME</p>
<button
type="button"
onClick={() => {
router.navigate("/two");
}}
>
open page two
</button>
<button
type="button"
onClick={() => {
router.navigate("/one");
}}
>
open page one
</button>
</div>
</OverlayPage>
<OverlayPage id={router.id} path="/one">
<div className="bg-blue-900 p-4">
<p>ONE</p>
<button
type="button"
onClick={() => {
router.navigate("/");
}}
>
back home
</button>
</div>
</OverlayPage>
<OverlayPage id={router.id} path="/two">
<div className="bg-blue-900 p-4">
<p>TWO</p>
<button
type="button"
onClick={() => {
router.navigate("/");
}}
>
back home
</button>
</div>
</OverlayPage>
</OverlayRouter>
</Overlay>
</div>
</OverlayDisplay>

Loading…
Cancel
Save