アプリ開発を楽しむ【#4:ヘッダー】
前回までの記事 アプリ開発を楽しむ【#1:アプリの概要】 アプリ開発を楽しむ【#2:環境構築1(React+TypeScript)】 アプリ開発を楽しむ【#3:環境構築2 (ESLint+Prettier)】
今回は、ヘッダーをつくっていきたいと思います。
完成イメージ
togo_appのコンテナを起動していない場合は起動させておいてください。
コンテナを起動
docker compose up -d
コンテナ内に入る
docker compose exec frontend bash
ローカルサーバーを起動
npm run dev
1.必要のない(使わない)ファイルを削除する
まず、必要のないファイルを削除しておきます。 frontend/app/src/App.css frontend/app/assets/react.svg frontend/app/public/vite.svg
2.ファビコンの変更など
①frontend/app/src/index.cssに書かれているcssはすべて削除します。 (ファイル自体は残しておいてください。)
②frontend/app/src/App.tsxをとりあえず次のようにしておきます。(エラーになってしまうので。。。)
const App = () => <div>ToGo App</div>;
export default App;
③frontend/app/public/favicon.svgをつくって次のコードをコピペしてください。 ファビコン(ブラウザのタブに表示されるアイコン)を変更しています。
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="#fff" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
④frontend/app/index.htmlを次のように編集します。ここを変更することで、ブラウザのタブに表示されるアイコンと文字(タイトル)が変わります。
<!-- ここから変更 -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<!-- ここまで変更 -->
<!-- ここから変更 -->
<title>ToGo App</title>
<!-- ここまで変更 -->
3.ヘッダーのコンポーネントをつくる
ヘッダーは、Material UIのApp barコンポーネントを利用します。 https://mui.com/material-ui/react-app-bar/
Headerコンポーネントは、frontend/app/components/layout/Header.tsxにつくります。 変更しているところは、コメントを入れてあるところになります。
import * as React from 'react';
import {
AppBar,
Box,
Button,
Container,
IconButton,
Link,
Toolbar,
Typography,
Menu,
MenuItem,
} from '@mui/material';
// ここから変更
import { Menu as MenuIcon, LocationOn as LocationOnIcon } from '@mui/icons-material';
// ここまで変更
// ここから変更
const pages = ['MyList', 'Posts', 'Map'];
const settings = ['Account', 'Logout'];
// ここまで変更
const Header = () => {
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(null);
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElNav(event.currentTarget);
};
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseNavMenu = () => {
setAnchorElNav(null);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
return (
<AppBar position="absolute">
<Container maxWidth="xl">
<Toolbar disableGutters>
// ここから変更(アイコンを変更)
<LocationOnIcon sx={{ display: { xs: 'none', md: 'flex' }, mr: 1 }} />
// ここまで変更
<Typography
variant="h6"
noWrap
component="a"
href="/"
sx={{
mr: 2,
display: { xs: 'none', md: 'flex' },
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.1rem',
color: 'inherit',
textDecoration: 'none',
}}
>
// ここから変更
TOGO LIST
// ここまで変更
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleOpenNavMenu}
color="inherit"
>
<MenuIcon />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorElNav}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
open={Boolean(anchorElNav)}
onClose={handleCloseNavMenu}
sx={{
display: { xs: 'block', md: 'none' },
}}
>
{pages.map((page) => (
<MenuItem key={page}>
<Typography textAlign="center">{page}</Typography>
</MenuItem>
))}
</Menu>
</Box>
// ここから変更(アイコンを変更)
<LocationOnIcon sx={{ display: { xs: 'flex', md: 'none' }, mr: 1 }} />
// ここまで変更
<Typography
variant="h5"
noWrap
component="a"
href=""
sx={{
mr: 2,
display: { xs: 'flex', md: 'none' },
flexGrow: 1,
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.1rem',
color: 'inherit',
textDecoration: 'none',
}}
>
// ここから変更
TOGO LIST
// ここまで変更
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
{pages.map((page) => (
<Button key={page} sx={{ my: 2, color: 'white', fontSize: '15px', display: 'block' }}>
{page}
</Button>
))}
</Box>
<Box sx={{ flexGrow: 0 }}>
// ここから変更
<Link color="inherit" underline="none" href="/login">
Login
</Link>
// ここまで変更
<Menu
sx={{ mt: '45px' }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{settings.map((setting) => (
<MenuItem key={setting} onClick={handleCloseUserMenu}>
<Typography textAlign="center">{setting}</Typography>
</MenuItem>
))}
</Menu>
</Box>
</Toolbar>
</Container>
</AppBar>
);
};
export default Header;
4.Headerコンポーネントを表示させる
frontend/app/src/App.tsxを次のように編集します。
先程(2の②)はとりあえず、次のようにしておきましたが、全て修正します。
const App = () => <div>ToGo App</div>;
export default App;
修正後は、次のとおりとなります。
import { Box, CssBaseline, Container } from '@mui/material';
import Header from './components/layout/Header';
const App = () => (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<Header />
<Box
component="main"
sx={{
backgroundColor: (theme) =>
theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900],
flexGrow: 1,
height: '100vh',
overflow: 'auto',
pt: { xs: 9, sm: 11, md: 12 },
}}
>
<Container maxWidth="lg">ToGo App</Container>
</Box>
</Box>
);
export default App;
先程つくったHeaderコンポーネントを読み込んで表示させています。 Box、CssBaseline、Container(Material UI)を使って、デザインを調整しています。
今回はここまでです。次回は、ToGoリストのコンポーネントをつくっていきたいと思います。
コードはGitHubに置いてありますのでよければ参考にしてください。 mainブランチは常に最新のものになります。 今回の内容はblog_4のブランチを参照してください。 https://github.com/KINE-M/togo_app