diff --git a/lib/iso.tsx b/lib/iso.tsx index 3f5f020..0bab3f3 100644 --- a/lib/iso.tsx +++ b/lib/iso.tsx @@ -34,37 +34,47 @@ export const Metas =(props:{concatListed?:boolean; dropUnlisted?:boolean}&MetasI } type RoutePath = Array; -type RouteParams = Record; -type RouteState = {URL:URL, Path:string[]}; -type RouteContext = [Route:RouteState, Update:(inPath?:RoutePath, inParams?:RouteParams)=>void]; +type RouteParams = Record; +type RouteState = {URL:URL, Path:RoutePath, Params:RouteParams, Anchor:string}; +type RouteContext = [Route:RouteState, Update:(inPath?:RoutePath, inParams?:RouteParams, inAnchor?:string)=>void]; type RouteProps = {children:typeof React.Children, url?:URL }; export const Router = { - Path(url:URL) + Parse(url:URL):RouteState { - return url.pathname.substring(1, url.pathname.endsWith("/") ? url.pathname.length-1 : url.pathname.length).split("/"); + const Path = url.pathname.substring(1, url.pathname.endsWith("/") ? url.pathname.length-1 : url.pathname.length).split("/"); + const Params:RouteParams = {}; + new URLSearchParams(url.search).forEach((k, v)=> Params[k] = v); + const Anchor = url.hash.substring(1); + return {URL:url, Path, Params, Anchor} as RouteState; }, - Context:React.createContext([{URL:new URL("https://original.route/"), Path:[]}, ()=>{}] as RouteContext), + Context:React.createContext([{URL:new URL("https://original.route/"), Path:[], Params:{}, Anchor:""}, ()=>{}] as RouteContext), Provider(props:RouteProps) { - const url = props.url || new URL(document.location.href); - const path = Router.Path(url); + const [routeGet, routeSet] = React.useState(Router.Parse(props.url || new URL(document.location.href))); + const [dirtyGet, dirtySet] = React.useState(false); - const [routeGet, routeSet] = React.useState({URL:url, Path:path} as RouteState); - //const routeUpdate:RouteContext[1] =(inURL:URL)=>routeSet({URL:inURL, Path:Router.Path(inURL)}); - const routeUpdate:RouteContext[1] =(inPath?:RoutePath, inParams?:RouteParams)=> + const routeUpdate:RouteContext[1] =(inPath, inParams, inAnchor)=> { - const clone = new URL(routeGet.URL.href); - inPath && (clone.pathname = `/${inPath.join("/")}`); - inParams && (clone.search = new URLSearchParams(inParams).toString()); + const clone = new URL(routeGet.URL); + inPath && (clone.pathname = inPath.join("/")); + inParams && (clone.search = new URLSearchParams(inParams as Record).toString()); routeSet({ URL:clone, - Path: inPath ? inPath : routeGet.Path + Path: inPath || routeGet.Path, + Params: inParams || routeGet.Params, + Anchor: inAnchor || routeGet.Anchor }); + dirtySet(true); }; - React.useEffect(()=>history.pushState({Path:routeGet.Path, Params:routeGet.URL.search}, "", routeGet.URL), [routeGet.URL.href]); + // when the state changes, update the page url React.useEffect(()=>{ - window.addEventListener("popstate", ()=>routeUpdate()); + history.pushState({...routeGet, URL:undefined}, "", routeGet.URL); + }, [routeGet.URL.href]); + + React.useEffect(()=>{ + // when the history changes, update the state + window.addEventListener("popstate", ({state})=>{routeUpdate(state.Path, state.Params, state.Anchor)}); }, []); return