start new router/metas
This commit is contained in:
		
							parent
							
								
									11f896f12b
								
							
						
					
					
						commit
						b77dd89437
					
				
							
								
								
									
										88
									
								
								iso-router.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								iso-router.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| import * as Signal from "@preact/signals"; | ||||
| import * as React from "react"; | ||||
| 
 | ||||
| ///////// Metas
 | ||||
| type MetaFields = {title?:string, description?:string}; | ||||
| type MetaRecord = MetaFields&{id:string} | ||||
| const Stack = [] as MetaRecord[]; | ||||
| const StackPush =(m:MetaRecord)=> {Stack.push(m); Update();} | ||||
| const StackPop =(id:string)=> {Stack.splice(Stack.findIndex( item => item.id === id ), 1); Update();} | ||||
| const Update =()=> document.title = Stack[Stack.length-1]?.title || "---"; | ||||
| Update(); | ||||
| export const useMeta=(fields:MetaFields)=> | ||||
| { | ||||
|     const id = React.useId(); | ||||
|     React.useEffect(()=>{ | ||||
|         StackPush({...fields, id}); | ||||
|         return ()=>StackPop(id); | ||||
|     }, []); | ||||
| } | ||||
| export const Meta =(props:MetaFields)=> { useMeta(props); return null; } | ||||
| 
 | ||||
| //////// Router
 | ||||
| //// Create Signals
 | ||||
| export const pageURL = Signal.signal(new URL(globalThis.location.href || "")); | ||||
| export const pagePath = Signal.signal([] as string[]); | ||||
| Signal.effect(()=> pagePath.value = pageURL.value.pathname.split("/").filter(part=>part!="")); | ||||
| 
 | ||||
| //// Add handlers
 | ||||
| globalThis.addEventListener("click", e=> | ||||
| { | ||||
|     (e.composedPath() as HTMLAnchorElement[]).find((step)=>{ | ||||
|         if(step.href) | ||||
|         { | ||||
|             const url = new URL(step.href); | ||||
|             if(url.origin == document.location.origin) | ||||
|             { | ||||
|                 e.preventDefault(); | ||||
|                 history.pushState({}, "", url); | ||||
|                 pageURL.value = url; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     }) | ||||
| }); | ||||
| globalThis.addEventListener("popstate", _=> pageURL.value = new URL(globalThis.location.href) ); | ||||
| 
 | ||||
| /// Rendering context
 | ||||
| type RouteContextData = { | ||||
|     /** Current nested depth of the router */ nestedDepth:number, | ||||
|     /** Index into the page path to start matching routes */ pathIndex:number, | ||||
|     /** Collection of keys from matched routes ("page/:key/") */ keys: Record<string, string> | ||||
| } | ||||
| const context = React.createContext({nestedDepth:0, pathIndex:0, keys:{}} as RouteContextData); | ||||
| export const useRoute =()=> React.useContext(context); | ||||
| export const Route =(props:{path:string[], children:React.ReactNode|React.ReactNode[]}):React.JSX.Element|null=> | ||||
| { | ||||
|     // Match the current page url, with a route.
 | ||||
|     const {nestedDepth, pathIndex} = React.useContext(context); | ||||
|     const pagePathRange = pagePath.value.slice(pathIndex); | ||||
|     const emptyMatch = !props.path.length && !pagePathRange.length; | ||||
|     const comparisonSize = Math.min(props.path.length, pagePath.value.length); | ||||
| 
 | ||||
|     if(comparisonSize == 0 && emptyMatch === false) // one of the arrays is empty and one is not
 | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     const keys = {} as Record<string, string>; // not doing anything with this currently
 | ||||
|     for(let i=0; i<comparisonSize; i++) | ||||
|     { | ||||
|         const partPage = pagePathRange[i]; | ||||
|         const partRoute = props.path[i]; | ||||
|         if(partPage !== partRoute) // theres a mismatch
 | ||||
|         { | ||||
|             if(partRoute?.startsWith(":")) // mismatch because variable capture, consider it a match and capture
 | ||||
|             { | ||||
|                 const key = partRoute?.substring(1); | ||||
|                 key && (keys[key] = partPage); | ||||
|             } | ||||
|             else if(partRoute !== "*") // otherwise quit if its not a wildcard section
 | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return <context.Provider value={{nestedDepth:nestedDepth+1, pathIndex:pathIndex+comparisonSize, keys}}>{props.children}</context.Provider> | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user