more overlay system testing

Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
mrjvs 2023-10-08 20:12:31 +02:00
parent a05191e1c4
commit d9855cb244
6 changed files with 67 additions and 29 deletions

View File

@ -3,11 +3,11 @@ import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { Transition } from "@/components/Transition"; import { Transition } from "@/components/Transition";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
export interface OverlayProps { export interface OverlayProps {
id: string;
children?: ReactNode; children?: ReactNode;
onClose?: () => void;
show?: boolean;
darken?: boolean; darken?: boolean;
} }
@ -16,6 +16,7 @@ export function OverlayDisplay(props: { children: ReactNode }) {
} }
export function Overlay(props: OverlayProps) { export function Overlay(props: OverlayProps) {
const router = useOverlayRouter(props.id);
const [portalElement, setPortalElement] = useState<Element | null>(null); const [portalElement, setPortalElement] = useState<Element | null>(null);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const target = useRef<Element | null>(null); const target = useRef<Element | null>(null);
@ -37,9 +38,9 @@ export function Overlay(props: OverlayProps) {
if (e.currentTarget !== e.target) return; if (e.currentTarget !== e.target) return;
if (!startedTarget) return; if (!startedTarget) return;
if (!startedTarget.isEqualNode(e.currentTarget as Element)) return; if (!startedTarget.isEqualNode(e.currentTarget as Element)) return;
if (props.onClose) props.onClose(); router.close();
}, },
[props] [router]
); );
useEffect(() => { useEffect(() => {
@ -47,7 +48,12 @@ export function Overlay(props: OverlayProps) {
setPortalElement(element ?? document.body); setPortalElement(element ?? document.body);
}, []); }, []);
const backdrop = ( return (
<div ref={ref}>
{portalElement
? createPortal(
<Transition show={router.isOverlayActive()} animation="none">
<div className="popout-wrapper pointer-events-auto fixed inset-0 z-[999] select-none">
<Transition animation="fade" isChild> <Transition animation="fade" isChild>
<div <div
onClick={click} onClick={click}
@ -57,15 +63,6 @@ export function Overlay(props: OverlayProps) {
})} })}
/> />
</Transition> </Transition>
);
return (
<div ref={ref}>
{portalElement
? createPortal(
<Transition show={props.show} animation="none">
<div className="popout-wrapper pointer-events-auto fixed inset-0 z-[999] select-none">
{backdrop}
<Transition animation="slide-up" className="h-0" isChild> <Transition animation="slide-up" className="h-0" isChild>
{props.children} {props.children}
</Transition> </Transition>

View File

@ -13,7 +13,7 @@ interface Props {
active?: boolean; // true if a child view is loaded active?: boolean; // true if a child view is loaded
} }
export function FloatingView(props: Props) { export function OverlayPage(props: Props) {
const { isMobile } = useIsMobile(); const { isMobile } = useIsMobile();
const width = !isMobile ? `${props.width}px` : "100%"; const width = !isMobile ? `${props.width}px` : "100%";
return ( return (

View File

@ -1,3 +0,0 @@
export function LeftSideControls(props: {children: React.ReactNode}) {
}

View File

@ -4,7 +4,6 @@ export function useOverlayRouter(id: string) {
const [route, setRoute] = useQueryParam("r"); const [route, setRoute] = useQueryParam("r");
const routeParts = (route ?? "").split("/").filter((v) => v.length > 0); const routeParts = (route ?? "").split("/").filter((v) => v.length > 0);
const routerActive = routeParts.length > 0 && routeParts[0] === id; const routerActive = routeParts.length > 0 && routeParts[0] === id;
const currentPage = routeParts[routeParts.length - 1] ?? "/";
function navigate(path: string) { function navigate(path: string) {
const newRoute = [id, ...path.split("/").filter((v) => v.length > 0)]; const newRoute = [id, ...path.split("/").filter((v) => v.length > 0)];
@ -20,7 +19,7 @@ export function useOverlayRouter(id: string) {
} }
function isCurrentPage(page: string) { function isCurrentPage(page: string) {
return routerActive && page === currentPage; return routerActive && route === `/${id}${page}`;
} }
function isLoaded(page: string) { function isLoaded(page: string) {
@ -40,7 +39,11 @@ export function useOverlayRouter(id: string) {
} }
function close() { function close() {
navigate("/"); setRoute(null);
}
function open() {
setRoute(`/${id}`);
} }
return { return {
@ -51,5 +54,6 @@ export function useOverlayRouter(id: string) {
isCurrentPage, isCurrentPage,
pageProps, pageProps,
isActive, isActive,
open,
}; };
} }

View File

@ -1,5 +1,5 @@
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { useLocation } from "react-router-dom"; import { useHistory, useLocation } from "react-router-dom";
export function useQueryParams() { export function useQueryParams() {
const loc = useLocation(); const loc = useLocation();
@ -19,6 +19,7 @@ export function useQueryParams() {
export function useQueryParam(param: string) { export function useQueryParam(param: string) {
const params = useQueryParams(); const params = useQueryParams();
const location = useLocation(); const location = useLocation();
const router = useHistory();
const currentValue = params[param]; const currentValue = params[param];
const set = useCallback( const set = useCallback(
@ -26,9 +27,11 @@ export function useQueryParam(param: string) {
const parsed = new URLSearchParams(location.search); const parsed = new URLSearchParams(location.search);
if (value) parsed.set(param, value); if (value) parsed.set(param, value);
else parsed.delete(param); else parsed.delete(param);
location.search = parsed.toString(); router.push({
search: parsed.toString(),
});
}, },
[param, location] [param, location, router]
); );
return [currentValue, set] as const; return [currentValue, set] as const;

View File

@ -1,4 +1,41 @@
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor";
import { Overlay, OverlayDisplay } from "@/components/overlays/OverlayDisplay";
import { OverlayPage } from "@/components/overlays/OverlayPage";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
// simple empty view, perfect for putting in tests // simple empty view, perfect for putting in tests
export default function TestView() { export default function TestView() {
return <div />; const router = useOverlayRouter("test");
const pages = ["", "/one", "/two"];
return (
<OverlayDisplay>
<div className="h-[400px] w-[800px] flex justify-center items-center">
<button
type="button"
onClick={() => {
router.open();
}}
>
Open
</button>
<button
type="button"
onClick={() => {
router.navigate(pages[Math.floor(pages.length * Math.random())]);
}}
>
random page
</button>
<OverlayAnchor id="test">
<div className="h-20 w-20 bg-white" />
</OverlayAnchor>
<Overlay id="test">
<OverlayPage {...router.pageProps("")}>Home</OverlayPage>
<OverlayPage {...router.pageProps("/one")}>Page one</OverlayPage>
<OverlayPage {...router.pageProps("/two")}>Page two</OverlayPage>
</Overlay>
</div>
</OverlayDisplay>
);
} }