router changes

This commit is contained in:
2026-03-03 11:38:54 -06:00
parent 0713ad7521
commit bf1f26d851
6 changed files with 104 additions and 8 deletions

View File

@@ -4,6 +4,8 @@ import './index.css';
import reportWebVitals from './reportWebVitals';
import {Canvas} from "./index/Canvas";
import {Nav} from "./nav/Nav";
import {RouterProvider} from "./router/RouterContext";
import {Route} from "./router/Route";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
@@ -11,8 +13,12 @@ const root = ReactDOM.createRoot(
root.render(
<React.StrictMode>
<Nav/>
<Canvas/>
<RouterProvider>
<Nav/>
<Route path="/">
<Canvas/>
</Route>
</RouterProvider>
</React.StrictMode>
);

View File

@@ -5,6 +5,7 @@ import {Menu} from "./Menu";
import wave from "../img/menu-wave.svg";
import "./nav.scss";
import {motion} from "framer-motion";
import {Link} from "../router/Link";
export function Nav() {
const [active, setActive] = useState(false);
@@ -35,8 +36,8 @@ function MenuButton(props : { active: boolean, setActive: (_:boolean) => void })
function Title() {
return (
<a href="/" className="name click">
<Link href="/" className="name click">
<motion.h1>Tymur Arsentiev</motion.h1>
</a>
</Link>
);
}

18
src/router/Link.tsx Normal file
View File

@@ -0,0 +1,18 @@
import React, {PropsWithChildren} from "react";
import {useRouter} from "./RouterContext";
export function Link(props: {className: string, href: string, replace?: boolean } & PropsWithChildren) {
const {children, className, href, replace} = props;
const router = useRouter();
function onClick(event: React.MouseEvent) {
event.preventDefault();
if(replace) {
router.replacePage(props.href);
} else {
router.pushPage(props.href);
}
}
return <a onClick={onClick} href={href} className={className}>{children}</a>
}

12
src/router/Route.tsx Normal file
View File

@@ -0,0 +1,12 @@
import {PropsWithChildren} from "react";
import {useRouter} from "./RouterContext";
import {Path} from "./router";
export function Route(props: PropsWithChildren & {path: Path}) {
const {path, children} = props;
const router = useRouter();
return <>
{ router.current?.currentPage === path && children}
</>
}

View File

@@ -1,14 +1,63 @@
import {createContext, PropsWithChildren, useContext, useSyncExternalStore} from "react";
import {createContext, PropsWithChildren, useContext, useEffect, useSyncExternalStore} from "react";
import {HistoryState, Path, Router} from "./router";
import * as repl from "node:repl";
const RouterContext = createContext<{}>({});
const DEFAULT : Router = {
current: {
currentPage: window.location.pathname as Path
},
pushPage: function(){},
replacePage: function(){}
};
const RouterContext = createContext<Router>(DEFAULT);
export function useRouter() {
return useContext(RouterContext);
}
export function RouterProvider(props : PropsWithChildren) {
function subscribe(onChange: () => void) {
window.addEventListener("popstate", onChange);
return <RouterContext.Provider value={{}}>
// return unsubscribe function
return function() {
window.removeEventListener("popstate", onChange);
}
}
function getSnapshot(): HistoryState {
return window.history.state ?? DEFAULT;
}
function pushState(path: string) {
window.history.pushState({
currentPage: path as Path
},
"",
path);
}
function replaceState(path: string) {
window.history.replaceState({
currentPage: path as Path
},
"",
path);
}
export function RouterProvider(props : PropsWithChildren) {
const state = useSyncExternalStore<HistoryState>(subscribe, getSnapshot);
// start history with initial page
useEffect(() => {
replaceState(window.location.pathname);
}, []);
return <RouterContext.Provider value={{
replacePage: replaceState,
pushPage: pushState,
current: state
}}>
{props.children}
</RouterContext.Provider>
}

10
src/router/router.ts Normal file
View File

@@ -0,0 +1,10 @@
export type Router = {
replacePage: (path: string) => void;
pushPage: (path: string) => void;
current: HistoryState
}
export type Path = `/${string}`;
export type HistoryState = {
currentPage: Path
};