router changes
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
|
||||
@@ -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
18
src/router/Link.tsx
Normal 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
12
src/router/Route.tsx
Normal 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}
|
||||
</>
|
||||
}
|
||||
@@ -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
10
src/router/router.ts
Normal 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
|
||||
};
|
||||
Reference in New Issue
Block a user