Files
FastUI/react/fastui/hooks/locationContext.tsx
Samuel Colvin eb6147512c moving python
2023-11-12 20:15:23 +00:00

75 lines
2.0 KiB
TypeScript

import { createContext, ReactNode, useEffect, useState, useCallback, useContext } from 'react'
import { ErrorContext } from './error'
function parseLocation(): string {
const { href, origin } = window.location
// remove origin from the beginning of href
return href.slice(origin.length)
}
export interface LocationState {
fullPath: string
goto: (pushPath: string) => void
}
const initialPath = parseLocation()
const initialState = {
fullPath: initialPath,
goto: () => null,
}
export const LocationContext = createContext<LocationState>(initialState)
export function LocationProvider({ children }: { children: ReactNode }) {
const [fullPath, setFullPath] = useState(initialPath)
const { setError } = useContext(ErrorContext)
const onPopState = useCallback(() => {
const fullPath = parseLocation()
setError(null)
setFullPath(fullPath)
}, [setError, setFullPath])
useEffect(() => {
window.addEventListener('popstate', onPopState)
return () => {
window.removeEventListener('popstate', onPopState)
}
}, [onPopState])
const value: LocationState = {
fullPath,
goto: useCallback(
(pushPath: string) => {
let newPath = pushPath
if (!newPath.startsWith('/')) {
// get rid of `.` and `./` at the beginning of the path
if (newPath.startsWith('.')) {
newPath = newPath.slice(1)
if (newPath.startsWith('/')) {
newPath = newPath.slice(1)
}
}
const oldPath = new URL(window.location.href).pathname
// we're now sure newPath does not start with a `/`
if (oldPath.endsWith('/')) {
newPath = oldPath + newPath
} else {
newPath = oldPath + '/' + newPath
}
}
window.history.pushState(null, '', newPath)
setError(null)
setFullPath(newPath)
},
[setError],
),
}
return <LocationContext.Provider value={value}>{children}</LocationContext.Provider>
}