From 409664e67dfa016aa7657c90f0a7c9df9adebe25 Mon Sep 17 00:00:00 2001 From: "azykov@mail.ru" Date: Thu, 4 Jun 2026 09:50:08 +0300 Subject: [PATCH] basic browser routing --- package.json | 1 + pnpm-lock.yaml | 45 ++++++++++++++++++++++++++ src/App.tsx | 85 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 123 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 5e4de86..c0ab5f5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "npm": "^11.16.0", "react": "^19.2.6", "react-dom": "^19.2.6", + "react-router-dom": "^7.16.0", "three": "^0.184.0", "uuid": "^14.0.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55f7fe8..230f488 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: react-dom: specifier: ^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: specifier: ^0.184.0 version: 0.184.0 @@ -679,6 +682,10 @@ packages: convert-source-map@2.0.0: 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: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -1180,6 +1187,23 @@ packages: peerDependencies: 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: resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} peerDependencies: @@ -1223,6 +1247,9 @@ packages: engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2023,6 +2050,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.1.1: {} + cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -2387,6 +2416,20 @@ snapshots: react: 19.2.6 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): dependencies: react: 19.2.6 @@ -2434,6 +2477,8 @@ snapshots: semver@7.8.1: {} + set-cookie-parser@2.7.2: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 diff --git a/src/App.tsx b/src/App.tsx index 497805e..280361d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 './App.scss'; import { Toolbar } from './components/Toolbar'; +import { state } from './state'; +import './App.scss'; + +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 Open Editor; +} + +function EditorLayout() { + return ( + <> + + + + + ); +} export const App = function () { - return ( - <> - - - - ) -} + return ( + + + + } /> + }> + } /> + } /> + } /> + + + + ); +};