pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
at main 137 lines 3.4 kB view raw
1import { useCallback, useEffect, useMemo } from "react"; 2 3import { useQueryParam } from "@/hooks/useQueryParams"; 4import { useOverlayStore } from "@/stores/overlay/store"; 5 6function splitPath(path: string, prefix?: string): string[] { 7 const parts = [prefix ?? "", ...path.split("/")]; 8 return parts.filter((v) => v.length > 0); 9} 10 11function joinPath(path: string[]): string { 12 return `/${path.join("/")}`; 13} 14 15export function useRouterAnchorUpdate(id: string) { 16 const [route] = useQueryParam("r"); 17 const setAnchorPoint = useOverlayStore((s) => s.setAnchorPoint); 18 const routerActive = useMemo( 19 () => !!route && route.startsWith(`/${id}`), 20 [route, id], 21 ); 22 23 const update = useCallback(() => { 24 if (!routerActive) return; 25 const anchor = document.getElementById(`__overlayRouter::${id}`); 26 if (anchor) { 27 const rect = anchor.getBoundingClientRect(); 28 setAnchorPoint({ 29 h: rect.height, 30 w: rect.width, 31 x: rect.x, 32 y: rect.y, 33 }); 34 } 35 }, [routerActive, setAnchorPoint, id]); 36 37 useEffect(() => { 38 update(); 39 }, [routerActive, update]); 40 41 useEffect(() => { 42 function resizeEvent() { 43 update(); 44 } 45 window.addEventListener("resize", resizeEvent); 46 return () => { 47 window.removeEventListener("resize", resizeEvent); 48 }; 49 }, [update]); 50} 51 52export function useInternalOverlayRouter(id: string) { 53 const [route, setRoute] = useQueryParam("r"); 54 const transition = useOverlayStore((s) => s.transition); 55 const setTransition = useOverlayStore((s) => s.setTransition); 56 const routerActive = !!route && route.startsWith(`/${id}`); 57 58 function makePath(path: string) { 59 return joinPath(splitPath(path, id)); 60 } 61 62 function navigate(path: string) { 63 const oldRoute = route; 64 const newRoute = joinPath(splitPath(path, id)); 65 setTransition({ 66 from: oldRoute ?? "/", 67 to: newRoute, 68 }); 69 setRoute(newRoute); 70 } 71 72 function showBackwardsTransition(path: string) { 73 if (!transition) return "none"; 74 const current = joinPath(splitPath(path, id)); 75 76 if (current === transition.to && transition.from.startsWith(transition.to)) 77 return "yes"; 78 if ( 79 current === transition.from && 80 transition.to.startsWith(transition.from) 81 ) 82 return "yes"; 83 return "no"; 84 } 85 86 function isCurrentPage(path: string) { 87 return routerActive && route === joinPath(splitPath(path, id)); 88 } 89 90 function isOverlayActive() { 91 return routerActive; 92 } 93 94 const close = useCallback( 95 (preventRouteClear?: boolean) => { 96 if (route && !preventRouteClear) setRoute(null); 97 setTransition(null); 98 }, 99 [setRoute, route, setTransition], 100 ); 101 102 const open = useCallback( 103 (defaultRoute = "/") => { 104 setTransition(null); 105 setRoute(joinPath(splitPath(defaultRoute, id))); 106 }, 107 [id, setRoute, setTransition], 108 ); 109 110 const activeRoute = routerActive 111 ? joinPath(splitPath(route.slice(`/${id}`.length))) 112 : "/"; 113 114 return { 115 activeRoute, 116 showBackwardsTransition, 117 isCurrentPage, 118 isOverlayActive, 119 navigate, 120 close, 121 open, 122 makePath, 123 currentRoute: route, 124 }; 125} 126 127export function useOverlayRouter(id: string) { 128 const router = useInternalOverlayRouter(id); 129 return { 130 id, 131 route: router.activeRoute, 132 isRouterActive: router.isOverlayActive(), 133 open: router.open, 134 close: router.close, 135 navigate: router.navigate, 136 }; 137}