Improving route handling

This commit is contained in:
2026-08-07 19:59:24 +03:00
parent f5b051db3d
commit 7d378cb2d5
8 changed files with 102 additions and 19 deletions

58
package-lock.json generated
View File

@@ -11,6 +11,7 @@
"jotai": "^2.20.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-router-dom": "^7.17.0",
"sass": "^1.99.0"
},
"devDependencies": {
@@ -1247,6 +1248,19 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/cookie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"dev": true,
@@ -2209,6 +2223,44 @@
"react": "^19.2.6"
}
},
"node_modules/react-router": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz",
"integrity": "sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-router-dom": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.17.0.tgz",
"integrity": "sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw==",
"license": "MIT",
"dependencies": {
"react-router": "7.17.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"license": "MIT",
@@ -2287,6 +2339,12 @@
"semver": "bin/semver.js"
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/shebang-command": {
"version": "2.0.0",
"dev": true,

View File

@@ -13,6 +13,7 @@
"jotai": "^2.20.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-router-dom": "^7.17.0",
"sass": "^1.99.0"
},
"devDependencies": {

View File

@@ -1,5 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import { useAtomValue } from 'jotai';
import { useLocation, useNavigate } from 'react-router-dom';
import { useAtomValue, useAtom } from 'jotai';
import { activeSectionState, themeState } from './state/atoms';
import Navbar from './components/Navbar';
import Hero from './components/Hero';
@@ -19,11 +20,26 @@ const SECTIONS = {
const EXIT_DURATION = 200;
export default function App() {
const activeSection = useAtomValue(activeSectionState);
const location = useLocation();
const navigate = useNavigate();
const [activeSection, setActiveSection] = useAtom(activeSectionState);
const theme = useAtomValue(themeState);
const [displayedSection, setDisplayedSection] = useState(activeSection);
const timerRef = useRef(null);
// Sync URL path with activeSection
useEffect(() => {
const path = location.pathname.substring(1) || 'home';
if (Object.keys(SECTIONS).includes(path)) {
if (path !== activeSection) {
setActiveSection(path);
}
} else {
// Invalid path, redirect to home
navigate('/');
}
}, [location.pathname, activeSection, setActiveSection, navigate]);
useEffect(() => {
if (theme === 'light') {
document.documentElement.dataset.theme = 'light';

View File

@@ -20,10 +20,10 @@ export default function Hero() {
</p>
<div className={styles.cta}>
<a href="#projects" className={styles.btnPrimary}>
<a href="/projects" className={styles.btnPrimary}>
See my work
</a>
<a href="#contact" className={styles.btnGhost}>
<a href="/contact" className={styles.btnGhost}>
Get in touch
</a>
</div>

View File

@@ -1,4 +1,5 @@
import { useAtom } from 'jotai';
import { Link } from 'react-router-dom';
import { useAtom, useAtomValue } from 'jotai';
import { navOpenState, activeSectionState } from '../state/atoms';
import ThemeToggle from './ThemeToggle';
import styles from '../styles/components/Navbar.module.scss';
@@ -12,31 +13,27 @@ const NAV_LINKS = [
export default function Navbar() {
const [isOpen, setIsOpen] = useAtom(navOpenState);
const [activeSection, setActiveSection] = useAtom(activeSectionState);
function handleNav(section) {
setActiveSection(section);
setIsOpen(false);
}
const activeSection = useAtomValue(activeSectionState);
return (
<header className={styles.header}>
<nav className={styles.nav}>
<button className={styles.logo} onClick={() => handleNav('home')}>
<Link to="/" className={styles.logo} onClick={() => setIsOpen(false)}>
<span className={styles.logoBracket}>&lt;</span>
human id="veikko" class="lintujarvi"
<span className={styles.logoBracket}>/&gt;</span>
</button>
</Link>
<ul className={`${styles.links} ${isOpen ? styles.open : ''}`}>
{NAV_LINKS.map(({ label, section }) => (
<li key={section}>
<button
onClick={() => handleNav(section)}
<Link
to={`/${section}`}
onClick={() => setIsOpen(false)}
className={activeSection === section ? styles.active : undefined}
>
{label}
</button>
</Link>
</li>
))}
</ul>

View File

@@ -16,10 +16,10 @@ const PROJECTS = [
star: false,
},
{
name: 'Pro bono project - Runosaari festival website',
name: 'Runosaari festival website - Pro bono',
desc: "Supporting local culture initiatives have always been close to my heart. I'm proudly helping Runosaari festival with their website and online presence already for the sixth year!",
tags: ['React', 'Vite', 'Apache2', 'Ubuntu Server'],
href: '#',
href: 'https://www.runosaari.fi/',
star: false,
},
];

View File

@@ -1,10 +1,13 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
import './styles/main.scss';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
<Router>
<App />
</Router>
</StrictMode>,
);

View File

@@ -4,4 +4,12 @@ import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
assetFileNames: 'assets/[name]-[hash][extname]',
entryFileNames: 'assets/[name]-[hash].js',
},
},
},
})