Add routing
This commit is contained in:
9
bun.lock
9
bun.lock
@@ -7,6 +7,7 @@
|
||||
"dependencies": {
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-router-dom": "^7.12.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
@@ -275,6 +276,8 @@
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
@@ -415,6 +418,10 @@
|
||||
|
||||
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
|
||||
|
||||
"react-router": ["react-router@7.12.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw=="],
|
||||
|
||||
"react-router-dom": ["react-router-dom@7.12.0", "", { "dependencies": { "react-router": "7.12.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
|
||||
@@ -423,6 +430,8 @@
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
"react-dom": "^19.2.0",
|
||||
"react-router-dom": "^7.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
|
||||
28
src/App.tsx
28
src/App.tsx
@@ -1,27 +1,21 @@
|
||||
import { useState } from 'react';
|
||||
import Sidebar, { type AppId } from './components/Sidebar';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import ImageApprox from './apps/ImageApprox/ImageApprox';
|
||||
import SnakeAI from './apps/SnakeAI/SnakeAI';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
const [currentApp, setCurrentApp] = useState<AppId>('image-approx');
|
||||
|
||||
const renderApp = () => {
|
||||
switch (currentApp) {
|
||||
case 'image-approx':
|
||||
return <ImageApprox />;
|
||||
case 'snake-ai':
|
||||
return <SnakeAI />;
|
||||
default:
|
||||
return <div>App not found</div>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="app-layout">
|
||||
<Sidebar currentApp={currentApp} onAppChange={setCurrentApp} />
|
||||
<main className="app-main">{renderApp()}</main>
|
||||
<Sidebar />
|
||||
<main className="app-main">
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/image-approx" replace />} />
|
||||
<Route path="/image-approx" element={<ImageApprox />} />
|
||||
<Route path="/snake-ai" element={<SnakeAI />} />
|
||||
<Route path="*" element={<div>App not found</div>} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import './Sidebar.css';
|
||||
|
||||
export type AppId = 'image-approx' | 'snake-ai';
|
||||
|
||||
export interface AppInfo {
|
||||
id: AppId;
|
||||
path: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
description: string;
|
||||
@@ -12,24 +14,21 @@ export interface AppInfo {
|
||||
export const APPS: AppInfo[] = [
|
||||
{
|
||||
id: 'image-approx',
|
||||
path: '/image-approx',
|
||||
name: 'Image Approximation',
|
||||
icon: '🎨',
|
||||
description: 'Evolve triangles to approximate images',
|
||||
},
|
||||
{
|
||||
id: 'snake-ai',
|
||||
path: '/snake-ai',
|
||||
name: 'Neural Network Snake',
|
||||
icon: '🐍',
|
||||
description: 'Evolve neural networks to play Snake',
|
||||
},
|
||||
];
|
||||
|
||||
interface SidebarProps {
|
||||
currentApp: AppId;
|
||||
onAppChange: (appId: AppId) => void;
|
||||
}
|
||||
|
||||
export default function Sidebar({ currentApp, onAppChange }: SidebarProps) {
|
||||
export default function Sidebar() {
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<div className="sidebar-header">
|
||||
@@ -39,15 +38,15 @@ export default function Sidebar({ currentApp, onAppChange }: SidebarProps) {
|
||||
|
||||
<nav className="sidebar-nav">
|
||||
{APPS.map((app) => (
|
||||
<button
|
||||
<NavLink
|
||||
key={app.id}
|
||||
className={`nav-item ${currentApp === app.id ? 'active' : ''}`}
|
||||
onClick={() => onAppChange(app.id)}
|
||||
to={app.path}
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
title={app.description}
|
||||
>
|
||||
<span className="nav-icon">{app.icon}</span>
|
||||
<span className="nav-name">{app.name}</span>
|
||||
</button>
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user