You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
116 lines
3.3 KiB
116 lines
3.3 KiB
/**
|
|
* アプリケーションの共通レイアウトを提供するコンポーネント
|
|
* ヘッダー(AppBar)とメインコンテンツ領域を含む基本的なページ構造を定義
|
|
*/
|
|
import React, { useState } from 'react';
|
|
import {
|
|
AppBar,
|
|
Toolbar,
|
|
Typography,
|
|
Container,
|
|
Box,
|
|
Button,
|
|
Drawer,
|
|
List,
|
|
ListItemText,
|
|
ListItemIcon,
|
|
ListItemButton,
|
|
Divider,
|
|
IconButton
|
|
} from '@mui/material';
|
|
import {
|
|
Menu as MenuIcon,
|
|
ListAlt as ListAltIcon,
|
|
} from '@mui/icons-material';
|
|
import { useNavigate, Outlet, useLocation } from 'react-router-dom';
|
|
|
|
const Layout: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
|
|
/**
|
|
* ログアウト処理を行うハンドラー関数
|
|
* ローカルストレージからトークンを削除し、ログインページにリダイレクト
|
|
*/
|
|
const handleLogout = () => {
|
|
localStorage.removeItem('token');
|
|
navigate('/login');
|
|
};
|
|
|
|
/**
|
|
* 画面遷移処理を行うハンドラー関数
|
|
* 指定されたパスに遷移し、サイドメニューを閉じる
|
|
*/
|
|
const handleNavigate = (path: string) => {
|
|
navigate(path);
|
|
setDrawerOpen(false);
|
|
};
|
|
|
|
// 現在のパスに基づいてメニュー項目が選択状態かどうかを判定
|
|
const isSelected = (path: string): boolean => {
|
|
return location.pathname === path;
|
|
};
|
|
|
|
// メニューを開閉するハンドラー
|
|
const toggleDrawer = () => {
|
|
setDrawerOpen(!drawerOpen);
|
|
};
|
|
|
|
return (
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
|
{/* ヘッダー部分 - アプリ名とログアウトボタンを表示 */}
|
|
<AppBar position="static" elevation={0}>
|
|
<Toolbar>
|
|
<IconButton
|
|
edge="start"
|
|
color="inherit"
|
|
aria-label="menu"
|
|
onClick={toggleDrawer}
|
|
sx={{ mr: 2 }}
|
|
>
|
|
<MenuIcon />
|
|
</IconButton>
|
|
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
|
ToDoアプリ
|
|
</Typography>
|
|
<Button color="inherit" onClick={handleLogout}>
|
|
ログアウト
|
|
</Button>
|
|
</Toolbar>
|
|
</AppBar>
|
|
|
|
{/* サイドメニュー */}
|
|
<Drawer
|
|
anchor="left"
|
|
open={drawerOpen}
|
|
onClose={() => setDrawerOpen(false)}
|
|
>
|
|
<Box
|
|
sx={{ width: 250 }}
|
|
role="presentation"
|
|
>
|
|
<List>
|
|
<ListItemButton
|
|
onClick={() => handleNavigate('/tasks')}
|
|
selected={isSelected('/tasks')}
|
|
>
|
|
<ListItemIcon><ListAltIcon /></ListItemIcon>
|
|
<ListItemText primary="タスク一覧" />
|
|
</ListItemButton>
|
|
<Divider />
|
|
</List>
|
|
</Box>
|
|
</Drawer>
|
|
|
|
{/* メインコンテンツ領域 - 子ルートのコンポーネントがここに表示される */}
|
|
<Box component="main" sx={{ flexGrow: 1, bgcolor: 'background.default', py: 3 }}>
|
|
<Container>
|
|
<Outlet /> {/* React Router の Outlet - 子ルートのコンポーネントがここにレンダリングされる */}
|
|
</Container>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default Layout; |