#16 issue/router #18

Merged
SethTrowbridge merged 6 commits from issue/router into master 2023-04-18 19:39:56 -04:00
Showing only changes of commit 64a4a82ac8 - Show all commits

View File

@ -34,37 +34,47 @@ export const Metas =(props:{concatListed?:boolean; dropUnlisted?:boolean}&MetasI
} }
type RoutePath = Array<string>; type RoutePath = Array<string>;
type RouteParams = Record<string, string>; type RouteParams = Record<string, string|number|boolean>;
type RouteState = {URL:URL, Path:string[]}; type RouteState = {URL:URL, Path:RoutePath, Params:RouteParams, Anchor:string};
type RouteContext = [Route:RouteState, Update:(inPath?:RoutePath, inParams?:RouteParams)=>void]; type RouteContext = [Route:RouteState, Update:(inPath?:RoutePath, inParams?:RouteParams, inAnchor?:string)=>void];
type RouteProps = {children:typeof React.Children, url?:URL }; type RouteProps = {children:typeof React.Children, url?:URL };
export const Router = { 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) Provider(props:RouteProps)
{ {
const url = props.url || new URL(document.location.href); const [routeGet, routeSet] = React.useState(Router.Parse(props.url || new URL(document.location.href)));
const path = Router.Path(url); const [dirtyGet, dirtySet] = React.useState(false);
const [routeGet, routeSet] = React.useState({URL:url, Path:path} as RouteState); const routeUpdate:RouteContext[1] =(inPath, inParams, inAnchor)=>
//const routeUpdate:RouteContext[1] =(inURL:URL)=>routeSet({URL:inURL, Path:Router.Path(inURL)});
const routeUpdate:RouteContext[1] =(inPath?:RoutePath, inParams?:RouteParams)=>
{ {
const clone = new URL(routeGet.URL.href); const clone = new URL(routeGet.URL);
inPath && (clone.pathname = `/${inPath.join("/")}`); inPath && (clone.pathname = inPath.join("/"));
inParams && (clone.search = new URLSearchParams(inParams).toString()); inParams && (clone.search = new URLSearchParams(inParams as Record<string, string>).toString());
routeSet({ routeSet({
URL:clone, 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(()=>{ 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 <Router.Context.Provider value={[routeGet, routeUpdate]}> return <Router.Context.Provider value={[routeGet, routeUpdate]}>