modes #23
| @ -6,7 +6,7 @@ const Comp = React.lazy(()=>import("./deep/component.tsx")); | |||||||
| export default ()=> | export default ()=> | ||||||
| { | { | ||||||
|     return <div class="p-4 font-sans"> |     return <div class="p-4 font-sans"> | ||||||
|         <Iso.Metas title="Main Page!"/> |         <Iso.Meta.Metas title="Main Page!"/> | ||||||
|         <nav class="p-4"> |         <nav class="p-4"> | ||||||
|             <a class="text-red-500" href="/">Home</a> |             <a class="text-red-500" href="/">Home</a> | ||||||
|             <a href="/about">About</a> |             <a href="/about">About</a> | ||||||
| @ -20,7 +20,12 @@ export default ()=> | |||||||
|         <Iso.Switch> |         <Iso.Switch> | ||||||
|             <Iso.Case value="page"> |             <Iso.Case value="page"> | ||||||
|                 <Iso.Switch> |                 <Iso.Switch> | ||||||
|                     <Iso.Case value="about-us">About us!</Iso.Case> |                     <Iso.Case value="about-us"> | ||||||
|  |                         <> | ||||||
|  |                             <Iso.Meta.Metas title="About US"/> | ||||||
|  |                             About us! | ||||||
|  |                         </> | ||||||
|  |                     </Iso.Case> | ||||||
|                     <Iso.Case default>sorry no page</Iso.Case> |                     <Iso.Case default>sorry no page</Iso.Case> | ||||||
|                 </Iso.Switch> |                 </Iso.Switch> | ||||||
|             </Iso.Case> |             </Iso.Case> | ||||||
|  | |||||||
| @ -10,10 +10,10 @@ export default ()=> | |||||||
|     const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`); |     const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     console.log("render!!") |     console.log("component.tsx render!!") | ||||||
| 
 | 
 | ||||||
|     return <div class="p-4 text-red-500"> |     return <div class="p-4 text-red-500"> | ||||||
|         <Iso.Metas title="Component!"/> |         <Iso.Meta.Metas title="Component!"/> | ||||||
|         Component Route is: {routeGet.Path.toString()} |         Component Route is: {routeGet.Path.toString()} | ||||||
|         <button className="p-4 bg-green-500 text-white" onClick={e=>{countSet(countGet+1); routeSet(["lol", "idk"], {count:countGet+1});}}>{countGet}</button> |         <button className="p-4 bg-green-500 text-white" onClick={e=>{countSet(countGet+1); routeSet(["lol", "idk"], {count:countGet+1});}}>{countGet}</button> | ||||||
|         <a href="/page/about-us" className="p-2 text(lg blue-500) font-bold">a link</a> |         <a href="/page/about-us" className="p-2 text(lg blue-500) font-bold">a link</a> | ||||||
|  | |||||||
| @ -9,6 +9,6 @@ | |||||||
|         "@eno/iso": "http://localhost:4507/lib/iso.tsx" |         "@eno/iso": "http://localhost:4507/lib/iso.tsx" | ||||||
|     }, |     }, | ||||||
|     "tasks": { |     "tasks": { | ||||||
|         "dev": "deno run -A --unstable --reload=http://localhost:4507/ --no-lock app.tsx" |         "dev": "deno run -A --unstable --reload=http://localhost:4507/ --no-lock app.tsx --dev" | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										130
									
								
								lib/iso.tsx
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								lib/iso.tsx
									
									
									
									
									
								
							| @ -12,34 +12,89 @@ if(!window.innerWidth) | |||||||
|     import(import.meta.resolve("../../server.tsx")).then(()=>{console.log("...imported!");}); |     import(import.meta.resolve("../../server.tsx")).then(()=>{console.log("...imported!");}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type MetasInputs = { [Property in MetaKeys]?: string }; | ||||||
|  | type MetasModeArgs = {concatListed?:boolean; dropUnlisted?:boolean}; | ||||||
|  | type MetasStackItem = MetasModeArgs&MetasInputs&{id:string, depth:number} | ||||||
| type Meta = {title:string, description:string, keywords:string, image:string, canonical:string } | type Meta = {title:string, description:string, keywords:string, image:string, canonical:string } | ||||||
| type MetaKeys = keyof Meta; | type MetaKeys = keyof Meta; | ||||||
| export const Meta:Meta = { |  | ||||||
|     title:"", |  | ||||||
|     description:"", |  | ||||||
|     keywords:"", |  | ||||||
|     image:"", |  | ||||||
|     canonical:"" |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| type MetasInputs = { [Property in MetaKeys]?: string }; | export const Meta = | ||||||
| export const Metas =(props:{concatListed?:boolean; dropUnlisted?:boolean}&MetasInputs):null=> |  | ||||||
| { | { | ||||||
|     const    additive = props.concatListed ? (key:MetaKeys, value:string)=> Meta[key] += value : (key:MetaKeys, value:string)=> Meta[key] = value; |     Stack:[] as Array<MetasStackItem>, | ||||||
|     const subtractive = props.dropUnlisted ? (key:MetaKeys)=> Meta[key] = "" : (key:MetaKeys)=> {}; |     Meta: { | ||||||
| 
 |         title:"", | ||||||
|     Object.keys(Meta).forEach((key)=>{ |         description:"", | ||||||
|         const metaKey = key as MetaKeys; |         keywords:"", | ||||||
|         const propValue = props[metaKey]||""; |         image:"", | ||||||
|         propValue ? additive(metaKey, propValue) : subtractive(metaKey); |         canonical:"" | ||||||
|     }) |     } as Meta, | ||||||
|     if(window.innerWidth) |     Context: React.createContext([[], ()=>{}] as [Get:MetasStackItem[], Set:React.StateUpdater<MetasStackItem[]>]), | ||||||
|  |     Provider({children}:{children:Children}) | ||||||
|     { |     { | ||||||
|         document.title = Meta.title; |         const binding = React.useState([] as MetasStackItem[]); | ||||||
|     } |  | ||||||
|          |          | ||||||
|     return null; |         React.useEffect(()=>{ | ||||||
| } |             const stack = binding[0]; | ||||||
|  |             const last = stack[stack.length-1]; | ||||||
|  |             console.log("updating page title", stack); | ||||||
|  |             document.title = last?.title||""; | ||||||
|  |         }) | ||||||
|  |         return <Meta.Context.Provider value={binding}>{children}</Meta.Context.Provider>; | ||||||
|  |     }, | ||||||
|  |     Metas({concatListed=false, dropUnlisted=false, ...props}:MetasModeArgs&MetasInputs):null | ||||||
|  |     { | ||||||
|  |         const id = React.useId(); | ||||||
|  |         const [, metasSet] = React.useContext(Meta.Context); | ||||||
|  |         const {depth} = React.useContext(SwitchContext); | ||||||
|  | 
 | ||||||
|  |         React.useEffect(()=>{ | ||||||
|  |             metasSet((m)=>{ | ||||||
|  |                 console.log(`adding meta`, props, depth); | ||||||
|  |                 const clone = [...m]; | ||||||
|  |                 let i; | ||||||
|  |                 for(i=clone.length-1; i>-1; i--) | ||||||
|  |                 { | ||||||
|  |                     if(clone[i].depth <= depth) | ||||||
|  |                     { | ||||||
|  |                          | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 clone.splice(i+1, 0, {id, depth, concatListed, dropUnlisted, ...props}); | ||||||
|  |                 return clone; | ||||||
|  |             }); | ||||||
|  |             return ()=> | ||||||
|  |             { | ||||||
|  |                 metasSet((m)=>{ | ||||||
|  |                     const clone = [...m]; | ||||||
|  |                     const ind = clone.findIndex(i=>i.id === id);       | ||||||
|  |                     if(ind > -1) | ||||||
|  |                     { | ||||||
|  |                         console.log(`removing meta`, props, depth); | ||||||
|  |                         clone.splice(ind, 1); | ||||||
|  |                     }  | ||||||
|  |                     return clone; | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             }; | ||||||
|  |         }, []); | ||||||
|  | 
 | ||||||
|  |         React.useEffect(()=>{ | ||||||
|  |             metasSet((m)=>{ | ||||||
|  |                 const clone = [...m]; | ||||||
|  |                 const ind = clone.findIndex(i=>i.id === id);       | ||||||
|  |                 if(ind > -1) | ||||||
|  |                 { | ||||||
|  |                     console.log(`updating meta`, props, depth); | ||||||
|  |                     clone[ind] = {...clone[ind], ...props}; | ||||||
|  |                 }  | ||||||
|  |                 return clone; | ||||||
|  |             }); | ||||||
|  |         }, Object.keys(props).map( (key) => props[key as MetaKeys] )); | ||||||
|  | 
 | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export type Children = string | number | React.JSX.Element | React.JSX.Element[]; | export type Children = string | number | React.JSX.Element | React.JSX.Element[]; | ||||||
| @ -90,14 +145,19 @@ export const Router = { | |||||||
|             document.addEventListener("click", e=> |             document.addEventListener("click", e=> | ||||||
|             { |             { | ||||||
|                 const t = e.target as HTMLAnchorElement; |                 const t = e.target as HTMLAnchorElement; | ||||||
|                 if(t.href) |                 const path = e.composedPath() as HTMLAnchorElement[]; | ||||||
|  |                 for(let i=0; i<path.length; i++) | ||||||
|                 { |                 { | ||||||
|                     const u = new URL(t.href); |                     if(path[i].href) | ||||||
|                     if(u.origin == document.location.origin) |  | ||||||
|                     { |                     { | ||||||
|                         e.preventDefault(); |                         const u = new URL(t.href); | ||||||
|                         const parts = Router.Parse(u); |                         if(u.origin == document.location.origin) | ||||||
|                         routeUpdate(parts.Path, parts.Params, parts.Anchor); |                         { | ||||||
|  |                             e.preventDefault(); | ||||||
|  |                             const parts = Router.Parse(u); | ||||||
|  |                             routeUpdate(parts.Path, parts.Params, parts.Anchor); | ||||||
|  |                         } | ||||||
|  |                         return; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
| @ -161,7 +221,7 @@ export const Switch =({children}:{children:Children})=> | |||||||
|             } |             } | ||||||
|             if(childCase?.props?.default && !fallback) |             if(childCase?.props?.default && !fallback) | ||||||
|             { |             { | ||||||
|                 console.log(routeSegment); |                 //console.log(routeSegment);
 | ||||||
|                 fallback = childCaseChildren; |                 fallback = childCaseChildren; | ||||||
|             } |             } | ||||||
|         }  |         }  | ||||||
| @ -200,7 +260,7 @@ export const Fetch = { | |||||||
|             inCheck.Promise = fetch(URL, Init?Init:undefined).then(resp=>resp.json()).then((json)=>{ |             inCheck.Promise = fetch(URL, Init?Init:undefined).then(resp=>resp.json()).then((json)=>{ | ||||||
|                 inCheck.JSON = json; |                 inCheck.JSON = json; | ||||||
|                 inCheck.CachedAt = new Date().getTime(); |                 inCheck.CachedAt = new Date().getTime(); | ||||||
|                 console.log(`...cached!`); |                 //console.log(`...cached!`);
 | ||||||
|                 return inCheck; |                 return inCheck; | ||||||
|             }); |             }); | ||||||
|             return inCheck; |             return inCheck; | ||||||
| @ -211,7 +271,7 @@ export const Fetch = { | |||||||
|             // not in the cache
 |             // not in the cache
 | ||||||
|             // - listen
 |             // - listen
 | ||||||
| 
 | 
 | ||||||
|             console.log(`making new cache record...`); |             //console.log(`making new cache record...`);
 | ||||||
|             return [load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR, Seed}), false, true]; |             return [load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR, Seed}), false, true]; | ||||||
|         } |         } | ||||||
|         else if(check.CachedAt == 0) |         else if(check.CachedAt == 0) | ||||||
| @ -220,26 +280,26 @@ export const Fetch = { | |||||||
|             // - listen
 |             // - listen
 | ||||||
|             // - possibly init if there is something in JSON
 |             // - possibly init if there is something in JSON
 | ||||||
| 
 | 
 | ||||||
|             console.log(`currently being cached...`); |             //console.log(`currently being cached...`);
 | ||||||
|             return [check, check.JSON ? true : false, true]; |             return [check, check.JSON ? true : false, true]; | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             console.log(`found in cache...`); |             //console.log(`found in cache...`);
 | ||||||
|             let secondsAge = (new Date().getTime() - check.CachedAt)/1000; |             let secondsAge = (new Date().getTime() - check.CachedAt)/1000; | ||||||
|             if(secondsAge > check.CacheFor) |             if(secondsAge > check.CacheFor) | ||||||
|             { |             { | ||||||
|                 // cached but expired
 |                 // cached but expired
 | ||||||
|                 // - listen
 |                 // - listen
 | ||||||
|                 // - init
 |                 // - init
 | ||||||
|                 console.log(`...outdated...`); |                 //console.log(`...outdated...`);
 | ||||||
|                 return [load(check), true, true]; |                 return [load(check), true, true]; | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 // cached and ready
 |                 // cached and ready
 | ||||||
|                 // - init
 |                 // - init
 | ||||||
|                 console.log(`...retrieved!`); |                 //console.log(`...retrieved!`);
 | ||||||
|                 return [check, true, false]; |                 return [check, true, false]; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								server.tsx
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								server.tsx
									
									
									
									
									
								
							| @ -391,7 +391,7 @@ else if(App && TwindInst) | |||||||
|     `<!doctype html>
 |     `<!doctype html>
 | ||||||
|     <html lang="en"> |     <html lang="en"> | ||||||
|         <head> |         <head> | ||||||
|             <title>${Iso.Meta.title}</title> |             <title>${Iso.Meta.Meta.title}</title> | ||||||
|             <meta name="viewport" content="width=device-width, initial-scale=1.0"> |             <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|             <meta charset="utf-8"/> |             <meta charset="utf-8"/> | ||||||
|             <style data-twind>${results.css}</style> |             <style data-twind>${results.css}</style> | ||||||
| @ -403,11 +403,16 @@ else if(App && TwindInst) | |||||||
|                 import {hydrate, createElement as H} from "react"; |                 import {hydrate, createElement as H} from "react"; | ||||||
|                 import * as Twind from "https://esm.sh/v115/@twind/core@1.1.3/es2022/core.mjs"; |                 import * as Twind from "https://esm.sh/v115/@twind/core@1.1.3/es2022/core.mjs"; | ||||||
|                 import * as App from "@eno/app"; |                 import * as App from "@eno/app"; | ||||||
|                 import {Router, Fetch, CSS} from "@eno/iso"; |                 import {Router, Fetch, CSS, Meta} from "@eno/iso"; | ||||||
|                 Twind.install(App.CSS ? {...CSS, ...App.CSS} : CSS); |                 Twind.install(App.CSS ? {...CSS, ...App.CSS} : CSS); | ||||||
|                 Fetch.Seed(${JSON.stringify(seed)}); |                 Fetch.Seed(${JSON.stringify(seed)}); | ||||||
|                 const hmrWrap = H( ()=>H(App.default) ); |                 const hmrWrap = H( ()=>H(App.default) ); | ||||||
|                 hydrate( H(Router.Provider, null, hmrWrap), document.querySelector("#app")); |                 hydrate( | ||||||
|  |                     H(Router.Provider, null, | ||||||
|  |                         H(Meta.Provider, null, hmrWrap) | ||||||
|  |                     ), | ||||||
|  |                     document.querySelector("#app") | ||||||
|  |                 ); | ||||||
|             </script> |             </script> | ||||||
|         </body> |         </body> | ||||||
|     </html>`;
 |     </html>`;
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user