React + material-ui(MUI)でDrawerコンポーネントをつかった左メニュー作成
ReactでMarkdownアプリを作っています。今回はDrawerコンポーネントをつかったサイドメニューを作成します。
今回作成するメニューの内容は固定です。React+material-ui(MUI)を使ったアプリを作成されている方の参考になればと思います。
前回の内容は以下を参照してください。
今回の手順は以下のとおりです。
目次
サイドナビゲーションの作成
サイドナビゲーションを作成します。
作成する場所は、「components/uilibs/organisms」の配下です。
ファイル名は、「LeftNaviagtion.tsx」とします。
project/
┣ front/
┃ ┣ Dockerfile
┃ ┗ src/
┃ ┗ markdown-app/
┃ ┗ src/
┃ ┣ components/
┃ ┃ ┗ uilibs/
┃ ┃ ┗ organisms/
┃ ┃ ┗ LeftNaviagtion.tsx
・・・略・・・
ファイルの内容です。
import { FC } from "react";
import {
Box,
Divider,
Drawer,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Toolbar,
} from "@mui/material";
import MenuBookIcon from '@mui/icons-material/MenuBook';
import CodeIcon from '@mui/icons-material/Code';
import HealthAndSafetyIcon from '@mui/icons-material/HealthAndSafety';
import { useNavigate } from "react-router-dom";
interface Props {
setUrlLocation: React.Dispatch<React.SetStateAction<string>>;
}
export const LeftNaviagtion: FC<Props> = (props) => {
const { setUrlLocation } = props;
const drawerWidth: number = 240;
const navigate = useNavigate();
const onClickItem1 = () => { setUrlLocation('勉強'); navigate('勉強') };
const onClickItem2 = () => { setUrlLocation('プログラミング'); navigate('プログラミング') };
const onClickItem3 = () => { setUrlLocation('体調管理'); navigate('体調管理') };
return (
<Drawer variant="permanent" sx={{ width: drawerWidth, flexShrink: 0, [`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box' } }}>
<Toolbar />
<Box sx={{ width: drawerWidth, foverflow: 'auto', backgroundColor: "secondary.main", height: "100vh" }}>
<List component="nav">
<ListItemButton onClick={onClickItem1} >
<ListItemIcon sx={{ color: (theme) => theme.palette.primary.contrastText }} >
<MenuBookIcon />
</ListItemIcon>
<ListItemText sx={{ color: (theme) => theme.palette.primary.contrastText }} primary="勉強" />
</ListItemButton>
<ListItemButton onClick={onClickItem2}>
<ListItemIcon sx={{ color: (theme) => theme.palette.primary.contrastText }}>
<CodeIcon />
</ListItemIcon>
<ListItemText sx={{ color: (theme) => theme.palette.primary.contrastText }} primary="プログラミング" />
</ListItemButton>
<ListItemButton onClick={onClickItem3}>
<ListItemIcon sx={{ color: (theme) => theme.palette.primary.contrastText }}>
<HealthAndSafetyIcon />
</ListItemIcon>
<ListItemText sx={{ color: (theme) => theme.palette.primary.contrastText }} primary="体調管理" />
</ListItemButton>
</List>
<Divider />
</Box>
</Drawer>
);
};
「Drawer」コンポーネントの直下に「Toolbar」のコンポーネントを配置します。
後述するヘッダナビゲーションの「AppBar」の「position」を"relative"から"fixed"に変更します。
変更することで、ヘッダーナビゲーションが固定されます。固定されることで高さがズレます。
ズレた高さは「Toolbar」をつかって調整します。
「Box」コンポーネント以下がメニューの内容になります。
ヘッダーナビゲーションの変更
ヘッダーの下の左側にサイドナビゲーションを作成します。
最初にヘッダナビゲーションを修正します。
ヘッダナビゲーション(HeaderNavigation.tsx)の内容です。
import { FC } from "react";
import {
AppBar,
Toolbar,
Typography
} from "@mui/material";
import { HomeNavigation } from "../molecules/HomeNavi";
export const HeaderNavigation: FC = () => {
const StyleToolBar = { display: "flex", justifyContent: "space-between" };
return (
<AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
<Toolbar sx={StyleToolBar}>
<Typography>markdown</Typography>
<HomeNavigation />
</Toolbar>
</AppBar>
);
};
ヘッダナビゲーションの「AppBar」の「position」を"relative"から"fixed"に変更します。
「z-index」を「Drawer」より+1にします。これによりサイドバーの上にヘッダーナビゲーションが上に表示されます。
ヘッダナビゲーションをfixedに変更することで呼び出している各テンプレートも改修が必要になります。
Markdown表示用テンプレートの変更
Markdown表示用テンプレートの改修します。
ファイル名は、「ViewTemplate.tsx」とします。
ファイルの内容です。
import { FC, useState } from "react";
import remarkGfm from "remark-gfm";
import ReactMarkdown from "react-markdown";
import { Box, Container, ThemeProvider, Toolbar, CssBaseline } from "@mui/material";
import { MarkdownType } from "../../types/MarkdownType.type";
import { Theme } from "../uilibs/theme/Theme";
import { SubHeaderNavigation } from "../uilibs/organisms/SubHeaderNavigation";
import { HeaderNavigation } from "../uilibs/organisms/HeaderNavigation";
import { LeftNaviagtion } from "../uilibs/organisms/LeftNaviagtion";
interface Props {
markdown: MarkdownType;
setMarkdown: React.Dispatch<React.SetStateAction<MarkdownType>>;
editMode: boolean;
insertMode: boolean;
setInsertMode: React.Dispatch<React.SetStateAction<boolean>>;
onClickSwitchMode: (event: React.MouseEvent<HTMLButtonElement>) => void;
setUrlLocation: React.Dispatch<React.SetStateAction<string>>;
};
export const ViewTemplate: FC<Props> = (props) => {
const { markdown, setMarkdown, editMode, insertMode, setInsertMode, onClickSwitchMode, setUrlLocation } = props;
return (
<ThemeProvider theme={Theme}>
<Box sx={{ display: "flex" }}>
<CssBaseline />
<HeaderNavigation />
<LeftNaviagtion setUrlLocation={setUrlLocation} />
<Box component="main" sx={{ flexGrow: 1 }}>
<Toolbar />
<SubHeaderNavigation markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} />
<Container className="markdown-view" maxWidth="xl">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown.body}</ReactMarkdown>
</Container>
</Box>
</Box>
</ThemeProvider>
);
};
前述した「LeftNaviagtion」コンポーネントを配置します。
パラメータで「setUrlLocation」を渡します。
「main」の「Box」コンポーネントの直下に「ToolBar」を配置します。
ヘッダーナビゲーションを「Fixed」に変更したことによる高さの調整のために配置します。
Markdown編集用テンプレートの変更
Markdown表示用テンプレートの改修します。
ファイル名は、「EditTemplate.tsx」とします。
ファイルの内容です。
import { ThemeProvider } from "@emotion/react";
import { Container, Toolbar } from "@mui/material";
import { Box } from "@mui/system";
import { FC, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { MarkdownType } from "../../types/MarkdownType.type";
import { MarkdownEditTextFiled } from "../uilibs/atoms/MarkdownEditTextFiled";
import { HeaderNavigation } from "../uilibs/organisms/HeaderNavigation";
import { SubHeaderNavigation } from "../uilibs/organisms/SubHeaderNavigation";
import { Theme } from "../uilibs/theme/Theme";
interface Props {
markdown: MarkdownType;
setMarkdown: React.Dispatch<React.SetStateAction<MarkdownType>>;
editMode: boolean;
insertMode: boolean;
setInsertMode: React.Dispatch<React.SetStateAction<boolean>>;
onClickSwitchMode: (event: React.MouseEvent<HTMLButtonElement>) => void;
};
export const EditTemplate: FC<Props> = (props) => {
const { markdown, setMarkdown, editMode, insertMode, setInsertMode, onClickSwitchMode } = props;
const StyleTemplateBox = { display: "flex", flexDirection: "column", height: "100vh" };
const StyleMainBox = { display: "flex", flexDirection: "row", height: "100vh", width: '100%' };
const StyleLeftBox = { height: "100vh", width: '50%', backgroundColor: Theme.palette.primary.main };
const StyleRightBox = { height: "100vh", width: '50%' };
const onChangeValue = (event: React.ChangeEvent<HTMLTextAreaElement>) => { setMarkdown({ url: markdown.url, title: markdown.title, body: event.target.value }); };
useEffect(() => {
}, [markdown.body]);
return (
<ThemeProvider theme={Theme}>
<Box sx={StyleTemplateBox}>
<HeaderNavigation />
<Toolbar />
<SubHeaderNavigation markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} />
<Box sx={StyleMainBox}>
<Box sx={StyleLeftBox}>
<Container>
<MarkdownEditTextFiled markdown={markdown} onChangeValue={onChangeValue} />
</Container >
</Box>
<Box sx={StyleRightBox}>
<Container className="markdown-view" >
<ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown.body}</ReactMarkdown>
</Container >
</Box>
</Box>
</Box>
</ThemeProvider>
);
};
「HeaderNavigation」コンポーネントの直下に「ToolBar」を配置します。
ヘッダーナビゲーションを「Fixed」に変更したことによる高さの調整のために配置します。
トップテンプレートの変更
トップテンプレートの改修します。
ファイル名は、「EditTemplate.tsx」とします。
ファイルの内容です。
import { FC } from "react"
import { Container, ThemeProvider, Toolbar } from "@mui/material";
import { Box } from "@mui/material";
import { Theme } from "../uilibs/theme/Theme";
import { MarkdownType } from "../../types/MarkdownType.type"
import { HeaderNavigation } from "../uilibs/organisms/HeaderNavigation";
import { MarkdownCardList } from "../uilibs/organisms/MarkdownCardList";
interface Props {
markdowns: Array<MarkdownType>
};
export const TopTemplate: FC<Props> = (props) => {
const { markdowns } = props;
const theme = Theme;
const styleContainer = { display: "flex", flexDirection: "row", paddingTop: "10px" };
return (
<ThemeProvider theme={theme}>
<Box>
<HeaderNavigation />
<Container maxWidth="xl" sx={styleContainer}>
<Toolbar />
<MarkdownCardList markdowns={markdowns} />
</Container>
</Box>
</ThemeProvider>
);
};
「Container」コンポーネントの直下に「ToolBar」を配置します。
ヘッダーナビゲーションを「Fixed」に変更したことによる高さの調整のために配置します。
Markdownページの変更
Markdownページの改修します。
ファイル名は、「Markdown.tsx」とします。
ファイルの内容です。
import { FC, useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import axios from "axios";
import { MarkdownType } from "../../types/MarkdownType.type";
import { EditTemplate } from "../templates/EditTemplate";
import { ViewTemplate } from "../templates/ViewTemplate";
export const Markdown: FC = () => {
const [editMode, setEditMode] = useState<boolean>(false);
const [markdown, setMarkdown] = useState<MarkdownType>({ url: '', title: '', body: '' });
const [insertMode, setInsertMode] = useState<boolean>(false);
const [urlLocation, setUrlLocation] = useState<string>("");
const location = useLocation();
useEffect(() => {
axios.get<MarkdownType>("http://localhost:5000/markdown" + location.pathname)
.then(res => {
if (!Object.keys(res.data).length) {
setMarkdown({ url: decodeURI(location.pathname), title: '', body: '' });
setEditMode(true);
setInsertMode(true);
} else {
setMarkdown(res.data);
setUrlLocation(res.data.url);
}
})
}, [urlLocation])
const onClickSwitchMode = () => { setEditMode(!editMode) }
return (
<>
{editMode ?
<EditTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} /> :
<ViewTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} setUrlLocation={setUrlLocation} />
}
</>
);
};
「urlLocation」の「useState」を追加します。
これまでは、他ページからの遷移を考えていました。
メニューを追加することで、URLは異なりますがテンプレートは同じテンプレートを使うことになります。
useEffectで初期表示のときのみレンダリングする調整を行っていたため、画面がレンダリングされませんでした。
urlLocationを追加することでurlLocationで値が変更されたときにレンダリングする対応を行いました。
画面確認
docker-compose upを使って各コンテナを起動します。
docker-compose.ymlファイルがある場所で以下のコマンドを実行します。
docker-compose up
起動したら以下のURLをブラウザに入力します。
http://localhost:3000
以下ブラウザの表示です。
markdownページの表示モードです。
まとめ
今回はDrawerコンポーネントをつかったサイドメニューを作成しました。
メニューは固定になっていますが、今後管理画面を作った管理についても検討します。
0 件のコメント:
コメントを投稿
コメントをお待ちしています。