Last active
November 23, 2023 15:49
-
-
Save StreetStrider/c1f4a1eaa26bebaab5b7c2a881b851d9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ISC © 2023, Strider. | |
/* eslint-disable complexity */ | |
const values = Object.values | |
import { useEffect, useRef } from 'react' | |
import { useParams } from 'react-router-dom' | |
import { useLocation } from 'react-router-dom' | |
import { useNavigate } from 'react-router-dom' | |
import { generatePath } from 'react-router-dom' | |
import { State } from './State' | |
export type Config = | |
{ | |
path: string, | |
mapping: Mappings, | |
states: States, | |
} | |
export type Mappings = Record<string, Mapping<any>> | |
export type Mapping <T> = | |
{ | |
load (uri: string): T, | |
dump (value: T): string | null, | |
fail? (e: Error): string | void, | |
} | |
export type States = Record<string, State<any>> | |
export default function urimap (config: Config) | |
{ | |
const path = config.path | |
const mapping = config.mapping | |
const states = config.states | |
const keys = path.match(/:\w+/g)!.map(key => key.slice(1)) | |
const params = useParams() | |
const location = useLocation() | |
const navigate = useNavigate() | |
const first = useRef(true) | |
useEffect(() => | |
{ | |
for (const key of keys) | |
{ | |
const mapper = mapping[key] | |
const state = states[key] | |
const repr = mapper.dump(state.value) | |
const repr_uri = (params[key] as string) | |
if (repr_uri === repr) continue | |
try | |
{ | |
var value = mapper.load(repr_uri) | |
} | |
catch (e) | |
{ | |
const fail = mapper.fail | |
if (! fail) | |
{ | |
throw e | |
} | |
const redirect = fail(e as Error) | |
if (redirect) | |
{ | |
navigate(redirect, { replace: true }) | |
} | |
return | |
} | |
state.set(value) | |
} | |
} | |
, [ params ]) | |
//, values(params)) | |
useEffect(() => | |
{ | |
if (first.current) | |
{ | |
first.current = false | |
return | |
} | |
const reprs: Record<string, string | null> = {} | |
for (const key of keys) | |
{ | |
const mapper = mapping[key] | |
const state = states[key] | |
reprs[key] = mapper.dump(state.value) | |
if (reprs[key] === null) | |
{ | |
return | |
} | |
} | |
// try | |
// { | |
var path_to = generatePath(path, reprs as any) | |
// } | |
/*catch (e) | |
{ | |
console.error('urimap: cannot generatePath(?)', path, reprs) | |
return | |
}*/ | |
if (location.pathname !== path_to) | |
{ | |
const replace = trailing_slash_fixing(location.pathname, path_to) | |
navigate(path_to, { replace }) | |
} | |
} | |
, values(states)) | |
} | |
function trailing_slash_fixing (prev: string, next: string) | |
{ | |
if (prev === next) return true | |
const L1 = prev.length | |
const L2 = next.length | |
if (Math.abs(L1 - L2) > 1) return false | |
const min = Math.min(L1, L2) | |
if (prev.slice(0, min) !== next.slice(0, min)) return false | |
if (L1 > L2) | |
{ | |
return (prev.slice(min) === '/') | |
} | |
else | |
{ | |
return (next.slice(min) === '/') | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
/:var/
)use
in the name