basic browser routing

This commit is contained in:
azykov@mail.ru 2026-06-04 09:50:08 +03:00
parent d7b484e1be
commit 409664e67d
No known key found for this signature in database
3 changed files with 123 additions and 8 deletions

View File

@ -21,6 +21,7 @@
"npm": "^11.16.0", "npm": "^11.16.0",
"react": "^19.2.6", "react": "^19.2.6",
"react-dom": "^19.2.6", "react-dom": "^19.2.6",
"react-router-dom": "^7.16.0",
"three": "^0.184.0", "three": "^0.184.0",
"uuid": "^14.0.0" "uuid": "^14.0.0"
}, },

View File

@ -41,6 +41,9 @@ importers:
react-dom: react-dom:
specifier: ^19.2.6 specifier: ^19.2.6
version: 19.2.6(react@19.2.6) version: 19.2.6(react@19.2.6)
react-router-dom:
specifier: ^7.16.0
version: 7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
three: three:
specifier: ^0.184.0 specifier: ^0.184.0
version: 0.184.0 version: 0.184.0
@ -679,6 +682,10 @@ packages:
convert-source-map@2.0.0: convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie@1.1.1:
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
engines: {node: '>=18'}
cross-env@7.0.3: cross-env@7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
@ -1180,6 +1187,23 @@ packages:
peerDependencies: peerDependencies:
react: ^19.2.6 react: ^19.2.6
react-router-dom@7.16.0:
resolution: {integrity: sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
react-dom: '>=18'
react-router@7.16.0:
resolution: {integrity: sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
react-dom: '>=18'
peerDependenciesMeta:
react-dom:
optional: true
react-use-measure@2.1.7: react-use-measure@2.1.7:
resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==}
peerDependencies: peerDependencies:
@ -1223,6 +1247,9 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
set-cookie-parser@2.7.2:
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
shebang-command@2.0.0: shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -2023,6 +2050,8 @@ snapshots:
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
cookie@1.1.1: {}
cross-env@7.0.3: cross-env@7.0.3:
dependencies: dependencies:
cross-spawn: 7.0.6 cross-spawn: 7.0.6
@ -2387,6 +2416,20 @@ snapshots:
react: 19.2.6 react: 19.2.6
scheduler: 0.27.0 scheduler: 0.27.0
react-router-dom@7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
react-router: 7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react-router@7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
cookie: 1.1.1
react: 19.2.6
set-cookie-parser: 2.7.2
optionalDependencies:
react-dom: 19.2.6(react@19.2.6)
react-use-measure@2.1.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6): react-use-measure@2.1.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies: dependencies:
react: 19.2.6 react: 19.2.6
@ -2434,6 +2477,8 @@ snapshots:
semver@7.8.1: {} semver@7.8.1: {}
set-cookie-parser@2.7.2: {}
shebang-command@2.0.0: shebang-command@2.0.0:
dependencies: dependencies:
shebang-regex: 3.0.0 shebang-regex: 3.0.0

View File

@ -1,12 +1,81 @@
import { BrowserRouter, Link, Outlet, Route, Routes, useNavigate, useParams } from 'react-router-dom';
import { useEffect } from 'react';
import { reaction } from 'mobx';
import { ThreeView } from './components/ThreeView'; import { ThreeView } from './components/ThreeView';
import './App.scss';
import { Toolbar } from './components/Toolbar'; import { Toolbar } from './components/Toolbar';
import { state } from './state';
import './App.scss';
export const App = function () { function StateToUrlSync() {
const navigate = useNavigate();
useEffect(() => reaction(
() => ({
isGame: state.isGamePlaying,
selectedId: state.worldEditor.selectedObjectId,
}),
({ isGame, selectedId }) => {
if (isGame) navigate('/game');
else if (selectedId) navigate(`/editor/object/${selectedId}`);
else navigate('/editor');
},
), []);
return null;
}
function EditorRoute() {
useEffect(() => {
if (state.isGamePlaying) state.stopGame();
state.worldEditor.resetSelectedObject();
}, []);
return null;
}
function EditorObjectRoute() {
const { id } = useParams<{ id: string }>();
useEffect(() => {
if (!id) return;
if (state.isGamePlaying) state.stopGame();
const mode = state.worldEditor.selectedObjectId === id
? state.worldEditor.selectedObjectMode ?? 'translate'
: 'translate';
state.worldEditor.setSelectedObject(id, mode);
}, [id]);
return null;
}
function GameRoute() {
useEffect(() => {
if (!state.isGamePlaying) state.startGame();
}, []);
return null;
}
function HomePage() {
return <Link to="/editor">Open Editor</Link>;
}
function EditorLayout() {
return ( return (
<> <>
<Outlet />
<ThreeView /> <ThreeView />
<Toolbar /> <Toolbar />
</> </>
) );
} }
export const App = function () {
return (
<BrowserRouter>
<StateToUrlSync />
<Routes>
<Route path="/" element={<HomePage />} />
<Route element={<EditorLayout />}>
<Route path="/editor" element={<EditorRoute />} />
<Route path="/editor/object/:id" element={<EditorObjectRoute />} />
<Route path="/game" element={<GameRoute />} />
</Route>
</Routes>
</BrowserRouter>
);
};