From 4a2a8e89cc9ce24763c922fd49bb570f79fbdc46 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Mon, 9 Oct 2023 21:25:52 +0200 Subject: [PATCH] router positions --- src/components/overlays/OverlayRouter.tsx | 18 +++ .../positions/OverlayAnchorPosition.tsx | 82 ++++++++++++++ .../positions/OverlayMobilePosition.tsx | 20 ++++ src/hooks/useOverlayRouter.ts | 1 + src/pages/developer/TestView.tsx | 103 +++++++++--------- 5 files changed, 174 insertions(+), 50 deletions(-) create mode 100644 src/components/overlays/OverlayRouter.tsx create mode 100644 src/components/overlays/positions/OverlayAnchorPosition.tsx create mode 100644 src/components/overlays/positions/OverlayMobilePosition.tsx diff --git a/src/components/overlays/OverlayRouter.tsx b/src/components/overlays/OverlayRouter.tsx new file mode 100644 index 00000000..f9272dcf --- /dev/null +++ b/src/components/overlays/OverlayRouter.tsx @@ -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 {content}; + return {content}; +} diff --git a/src/components/overlays/positions/OverlayAnchorPosition.tsx b/src/components/overlays/positions/OverlayAnchorPosition.tsx new file mode 100644 index 00000000..80f98391 --- /dev/null +++ b/src/components/overlays/positions/OverlayAnchorPosition.tsx @@ -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(null); + const [left, setLeft] = useState(0); + const [top, setTop] = useState(0); + const [cardRect, setCardRect] = useState(null); + const [anchorRect, setAnchorRect] = useState(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) { + setAnchorRect(ev.detail); + } + document.addEventListener(evtStr, listen as any); + return () => { + document.removeEventListener(evtStr, listen as any); + }; + }, [props.id]); + + return ( +
+ {props.children} +
+ ); +} diff --git a/src/components/overlays/positions/OverlayMobilePosition.tsx b/src/components/overlays/positions/OverlayMobilePosition.tsx new file mode 100644 index 00000000..68534714 --- /dev/null +++ b/src/components/overlays/positions/OverlayMobilePosition.tsx @@ -0,0 +1,20 @@ +import classNames from "classnames"; +import { ReactNode } from "react"; + +interface MobilePositionProps { + children?: ReactNode; + className?: string; +} + +export function OverlayMobilePosition(props: MobilePositionProps) { + return ( +
+ {props.children} +
+ ); +} diff --git a/src/hooks/useOverlayRouter.ts b/src/hooks/useOverlayRouter.ts index c4791168..10bbdde7 100644 --- a/src/hooks/useOverlayRouter.ts +++ b/src/hooks/useOverlayRouter.ts @@ -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, diff --git a/src/pages/developer/TestView.tsx b/src/pages/developer/TestView.tsx index 4c64406f..6ce44c41 100644 --- a/src/pages/developer/TestView.tsx +++ b/src/pages/developer/TestView.tsx @@ -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() { > Open - -
+ +
- - -
-

HOME

- - -
-
- -
-

ONE

- -
-
- -
-

TWO

- -
-
+ + + +
+

HOME

+ + +
+
+ +
+

ONE

+ +
+
+ +
+

TWO

+ +
+
+