feat(unit-3): Layout & Navigation — sidebar, mobile overlay, theme toggle
- Remove Topbar; move UserMenu + LanguageSwitcher to sidebar footer - Add ThemeToggle (dark/light) with localStorage persistence - Add useTheme hook; no-flash inline script in index.html - Add MobileBar (hamburger + app name, mobile-only) - Add SidebarOverlay (slide-in from left, backdrop closes it) - Sidebar: role-filtered nav (BR-U3-01–06), onClose prop for mobile - AppLayout: desktop sidebar-only layout, mobile bar + overlay - i18n: theme.* and nav.openMenu/closeMenu keys (NL + EN) - Tests: 43/43 passing (14 new — useTheme, Sidebar roles, AppLayout) Stories: US-08, US-18, US-19 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Sidebar } from './Sidebar';
|
||||
|
||||
interface SidebarOverlayProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function SidebarOverlay({ onClose }: SidebarOverlayProps) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger enter animation on next frame
|
||||
const id = requestAnimationFrame(() => setVisible(true));
|
||||
return () => cancelAnimationFrame(id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-40 md:hidden" data-testid="sidebar-overlay">
|
||||
<div
|
||||
className="absolute inset-0 bg-black/50"
|
||||
onClick={onClose}
|
||||
data-testid="sidebar-backdrop"
|
||||
/>
|
||||
<div
|
||||
className={`absolute left-0 top-0 h-full w-64 shadow-xl transition-transform duration-200 ${visible ? 'translate-x-0' : '-translate-x-full'}`}
|
||||
>
|
||||
<Sidebar onClose={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user