今回は、Material UIのTextFeildやAvater、ChipでFormのレイアウトを作成していきます!
Material UIは過去記事でも紹介しているので、今回は総集編みたいな感じになると思います。
まだ、React TypeScriptの開発環境を未構築の方は、「ReactとTypeScriptとRedux Toolkitの開発環境構築をしよう!」を参考にしてみてください!
本記事はReact・TypeScript・Redux Toolkitの利用を前提に実装をしていきます。
構築からデプロイまでやっていきますので、必要に応じて他の記事も参考にしてみてください。
学習に使った本はこちら!
Amazon Kindle Unlimited でもReactの本はいっぱいあるぞ!
ページを作成しよう!
まずは、フォームを表示するページを作成しましょう。
もし過去の記事から見てくれている方であれば、Sample2Page.tsxは作られていると思いますので、必要な箇所だけ参考にしてもらえればと思います。
必要なファイルを用意する。
まずは、フォームを表示するページを作成するために、Sample2Page.tsxとSample2Page.module.scssを作成します。
- components>pages>sample2Page>Sample2Page.tsxを作成
- components>pages>sample2Page>Sample2Page.module.scssを作成
必要なファイルを用意する。
まずは、フォームを表示するページを作成するために、Sample2Page.tsxとSample2Page.module.scssを作成します。
- components>pages>sample2Page>Sample2Page.tsxを作成
- components>pages>sample2Page>Sample2Page.module.scssを作成
Sample2Page.tsxを修正する。
先ほど作成したSample2Page.tsxに雛形コードを追加していきます。
- 詳細は基本的にソースコードを参照してください!
- 前回までと唯一違う点は、<form></form>です。
入力・送信フォームには、ほぼ必須のタグになります。
import React from "react";
import { RouteComponentProps } from "react-router-dom";
import { Link } from "react-router-dom";
import Button from "@material-ui/core/Button";
import { Path } from "../../../Routes";
type PropsTypes = RouteComponentProps;
const Sample2Page = ({ history }: PropsTypes): JSX.Element => {
return <div>
<form>
//ここにFormを実装していく
</form>
</div>;
};
export default Sample2Page;
SUBMITボタンとCANCELボタンを追加する。
雛形を追加したSample2Page.tsxに、SUBMITボタンとCANCELボタンを作成しましょう。
- SUMBITボタンは、type=”submit”にすることでフォームの内容を送信できるようになります。
- CANCELは、type=”button”として、onClickにキャンセルの実装をしていきます。
import React from "react";
// react-router-dom
import { RouteComponentProps } from "react-router-dom";
import { Link } from "react-router-dom";
import { Path } from "../../../Routes";
// material-ui
import Button from "@material-ui/core/Button";
// styels
import styles from "./Sample2Page.module.scss";
type PropsTypes = RouteComponentProps;
const Sample2Page = ({ history }: PropsTypes): JSX.Element => {
return (
<div>
<form className={styles.form}>
<div className={styles.button_wrapper}>
<Button
type="submit"
variant="contained"
color="primary"
className={styles.submit_button}
>
SUBMIT
</Button>
<Button
type="button"
variant="contained"
color="secondary"
onClick={() => null}
className={styles.cancel_button}
>
CANCEL
</Button>
</div>
</form>
</div>
);
};
export default Sample2Page;
ボタンのスタイルを修正する。
今のままでも良いのですが、marginを入れたり、右寄せにしておきたいのでStyleを追加していきます。
Sample2Page.module.scssを修正していきましょう!
- ここは特殊なことはしていないので詳細は省きます。
- 各ボタンの間隔を空けて、右寄せにしています。
.root {
.form {
.button_wrapper {
display: flex;
justify-content: flex-end;
align-items: flex-end;
margin: 20px;
.submit_button,
.cancel_button {
margin: 5px;
}
}
}
}
GridとBoxでレイアウトを整える。
GridとBoxを使って、今回作成するFormのレイアウトを作成していきましょう!
本来なら適切な粒度でコンポーネント分けするのですが、説明の関係上全てSample2Page.tsxに実装していきます。
Grid単位でコンポーネント分けしていれば、コードリーディングもしやすくなると思いますよ!
- 詳細は、画面を見た方がわかりやすいと思うので、動作確認してみましょう!
import React from "react";
// react-router-dom
import { RouteComponentProps } from "react-router-dom";
import { Link } from "react-router-dom";
import { Path } from "../../../Routes";
// material-ui
import { Button, Grid, Box, Card } from "@material-ui/core";
// styels
import styles from "./Sample2Page.module.scss";
type PropsTypes = RouteComponentProps;
const Sample2Page = ({ history }: PropsTypes): JSX.Element => {
return (
<div className={styles.root}>
<form className={styles.form}>
<Grid container justifyContent="center" alignItems="center" spacing={3}>
<Grid container spacing={3} item xs={3}>
<Grid item xs={12}>
<Card className={styles.card}>画像アップロード</Card>
</Grid>
</Grid>
<Grid container spacing={3} item xs={9}>
<Grid item xs={12}>
<Card className={styles.card}>名前</Card>
<Card className={styles.card}>ふりがな</Card>
<Card className={styles.card}>生年月日</Card>
<Card className={styles.card}>出身地</Card>
<Card className={styles.card}>入社年月日</Card>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={12}>
<Card className={styles.card}>Github</Card>
<Card className={styles.card}>Twitter</Card>
<Card className={styles.card}>Facebook</Card>
<Card className={styles.card}>Instagram</Card>
<Card className={styles.card}>Website</Card>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={12}>
<Card className={styles.card}>PieChart1</Card>
<Card className={styles.card}>PieChart2</Card>
<Card className={styles.card}>PieChart3</Card>
<Card className={styles.card}>PieChart4</Card>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={12}>
<Card className={styles.card}>chipCard</Card>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={12}>
<Card className={styles.card}>Text1</Card>
<Card className={styles.card}>Text2</Card>
<Card className={styles.card}>Text3</Card>
</Grid>
</Grid>
</Grid>
<Box component="span" m={2} className={styles.button_wrapper}>
<Button
type="submit"
variant="contained"
color="primary"
className={styles.submit_button}
>
SUBMIT
</Button>
<Button
type="button"
variant="contained"
color="secondary"
onClick={() => null}
className={styles.cancel_button}
>
CANCEL
</Button>
</Box>
</form>
</div>
);
};
export default Sample2Page;
動作確認をする。
ここまでできたら動作確認をしていきましょう!
画像アップロードと名前等の部分は、Gridで3:9にしています。
それ以外は基本的に12になっているので問題ないかなと思います。
次は、Cardそれぞれに入力フォームを入れていくということじゃな?
入力ボタン・入力欄を作成しよう!
作成したページに入力ボタンや入力欄を実装していきましょう!
Avatarボタンを作成する。
画像アップロードのCardの場所に、Avaterを使ったボタンを作成していきます。
AvaterをIconButtonでラップしてあげることで、AvaterをIconButtonとして利用することができます。
今後は、Avaterにはアイコンなどが入ってくる想定であり、設定されているものを表示してあげるのにも便利です。
<Grid item xs={12}>
<div className={styles.upload_button}>
<Tooltip title="add">
<IconButton className={styles.icon_button}>
<Avatar className={styles.avatar}>
<AddAPhotoIcon />
</Avatar>
</IconButton>
</Tooltip>
</div>
</Grid>
Sample2Page.module.scssも修正しておきましょう。
.root {
.form {
.upload_button {
display: flex;
justify-content: center;
align-items: center;
.icon_button {
.avatar {
width: 150px;
height: 150px;
}
}
}
.button_wrapper {
display: flex;
justify-content: flex-end;
align-items: flex-end;
margin: 20px;
.submit_button,
.cancel_button {
margin: 5px;
}
}
}
}
動作確認するとこんな感じになっていると思います。
Tooltipもつけているので、マウスオーバーでツールチップが表示されますね!
TextFieldを作成する。
Material UIのText Fieldを使ってテキスト入力欄を作成していきます。
Text Fieldもサンプルがたくさんありますが、今回はシンプルなものを参考にしています。
<Grid item xs={12}>
<TextField
margin="normal"
id="name"
label="名前"
fullWidth
className={styles.text_field}
/>
<TextField
margin="normal"
id="kana"
label="ふりがな"
fullWidth
className={styles.text_field}
/>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
margin="normal"
id="birthday"
label="生年月日"
format="MM/dd/yyyy"
value={new Date("2014-08-18T21:11:54")}
onChange={() => null}
fullWidth
/>
</MuiPickersUtilsProvider>
<TextField
margin="normal"
id="birthplace"
label="出身地"
fullWidth
className={styles.text_field}
/>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
margin="normal"
id="birthday"
label="生年月日"
format="MM/dd/yyyy"
value={new Date("2014-08-18T21:11:54")}
onChange={() => null}
fullWidth
/>
</MuiPickersUtilsProvider>
</Grid>
画面を確認すると入力欄が表示されたと思います。
入力欄が大きいような気もしますが、一旦これで進めていきます。
アイコン付きTextFieldを作成する。
上記で作成したTextFieldでも良いのですが、せっかくなのでアイコン付きのTextFieldも作成しておきます!
アイコンはお好きなものをインポートしてもらえれば良いと思います。
<Grid item xs={12}>
<TextField
margin="normal"
id="github"
label="GitHub"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<GitHubIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="twitter"
label="Twitter"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<TwitterIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="facebook"
label="Facebook"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FacebootIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="instagram"
label="Instagram"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<InstagramIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="web"
label="Web"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<WebIcon />
</InputAdornment>
),
}}
/>
</Grid>
こんな感じのアイコン付きTextFieldが作れたかなと思います。
Chipをボタンとして利用できるようにする。
前回までを見てくれている方であれば、Chipの使い方は理解できていると思いますが、次はChipをボタンとして利用していこうと思います。
interface ChipData {
key: number;
label: string;
level: number;
}
...省略...
const [chipData, setChipData] = React.useState<ChipData[]>([
{ key: 0, label: "Angular", level: 1 },
{ key: 1, label: "jQuery", level: 2 },
{ key: 2, label: "Polymer", level: 3 },
{ key: 3, label: "React", level: 2 },
{ key: 4, label: "Vue.js", level: 1 },
]);
...省略...
<Grid item xs={12}>
<Card className={styles.chip_card}>
<CardHeader title="chipselect" className={styles.card_header} />
<CardContent>
{chipData.map((data) => {
let icon = (
<SentimentVeryDissatisfiedIcon
className={styles.verydissatisfied_icon}
/>
);
if (data.level === 1) {
icon = (
<SentimentVerySatisfiedIcon
className={styles.verysatisfied_icon}
/>
);
} else if (data.level === 2) {
icon = (
<SentimentSatisfiedIcon
className={styles.satisfied_icon}
/>
);
}
return (
<Chip
icon={icon}
label={data.label}
onClick={() => null}
className={styles.chip}
/>
);
})}
</CardContent>
</Card>
</Grid>
Sample2Page.module.scssも修正しておきましょう。
.root {
.form {
.upload_button {
display: flex;
justify-content: center;
align-items: center;
.icon_button {
.avatar {
width: 150px;
height: 150px;
}
}
}
.chip_card {
background-color: transparent;
border: 1px solid;
.chip {
margin: 5px;
}
.verydissatisfied_icon {
color: gray;
}
.verysatisfied_icon {
color: blue;
}
.satisfied_icon {
color: salmon;
}
}
.button_wrapper {
display: flex;
justify-content: flex-end;
align-items: flex-end;
margin: 20px;
.submit_button,
.cancel_button {
margin: 5px;
}
}
}
}
画面を確認するとChipが表示されており、ボタンとして利用できる状態になっていると思います。
(onClickを入れていないので、押下しても何も起きません。)
他の入力欄も実装する。
PieChartとTextと書かれている部分は、基本的にTextFeildを使ったものと何も変わらないのでソースコードのみ貼っておきます。
PieChartCard用
<Grid container spacing={3} item xs={12}>
<Grid item xs={3}>
<TextField
margin="normal"
id="title1"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents1"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages1"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="title2"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents2"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages2"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="title3"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents3"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages3"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="title4"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents4"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages4"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
</Grid>
TextCard用
<Grid container spacing={3} item xs={12}>
<Grid item xs={3}>
<TextField
margin="normal"
id="textcard_title1"
label="テキストタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={9}>
<TextField
margin="normal"
id="textcard_contents1"
label="内容"
multiline
fullWidth
variant="outlined"
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="textcard_title2"
label="テキストタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={9}>
<TextField
margin="normal"
id="textcard_contents2"
label="内容"
multiline
fullWidth
variant="outlined"
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="textcard_title3"
label="テキストタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={9}>
<TextField
margin="normal"
id="textcard_contents3"
label="内容"
multiline
fullWidth
variant="outlined"
className={styles.text_field}
/>
</Grid>
</Grid>
【おまけ】全体コードを確認する。
最終的なコードをあげておきます。
最後に全体をCardでラップして、スタイルで背景色を変更していますので、気になる方はこちらを参考にしてみてください。
import React from "react";
// react-router-dom
import { RouteComponentProps } from "react-router-dom";
import { Link } from "react-router-dom";
import { Path } from "../../../Routes";
// material-ui
import {
Button,
Grid,
Box,
Card,
CardHeader,
CardContent,
Tooltip,
IconButton,
Avatar,
TextField,
InputAdornment,
Chip,
Divider,
} from "@material-ui/core";
import {
DateRange as DateRangeIcon,
Room as RoomIcon,
Business as BusinessIcon,
GitHub as GitHubIcon,
Twitter as TwitterIcon,
Facebook as FacebootIcon,
Instagram as InstagramIcon,
Web as WebIcon,
AddAPhoto as AddAPhotoIcon,
SentimentVerySatisfied as SentimentVerySatisfiedIcon,
SentimentSatisfied as SentimentSatisfiedIcon,
SentimentVeryDissatisfied as SentimentVeryDissatisfiedIcon,
} from "@material-ui/icons";
// styels
import styles from "./Sample2Page.module.scss";
// date-io
import DateFnsUtils from "@date-io/date-fns";
import {
MuiPickersUtilsProvider,
KeyboardTimePicker,
KeyboardDatePicker,
} from "@material-ui/pickers";
type PropsTypes = RouteComponentProps;
interface ChipData {
key: number;
label: string;
level: number;
}
const Sample2Page = ({ history }: PropsTypes): JSX.Element => {
const [chipData, setChipData] = React.useState<ChipData[]>([
{ key: 0, label: "Angular", level: 1 },
{ key: 1, label: "jQuery", level: 2 },
{ key: 2, label: "Polymer", level: 3 },
{ key: 3, label: "React", level: 2 },
{ key: 4, label: "Vue.js", level: 1 },
]);
return (
<div className={styles.root}>
<form className={styles.form}>
<Grid container justifyContent="center" alignItems="center" spacing={3}>
<Grid item xs={11}>
<Card className={styles.form_card}>
<CardHeader title="登録フォーム" className={styles.card_header} />
<Divider />
<CardContent>
<Grid
container
justifyContent="center"
alignItems="center"
spacing={3}
>
<Grid container spacing={3} item xs={3}>
<Grid item xs={12}>
<div className={styles.upload_button}>
<Tooltip title="add">
<IconButton className={styles.icon_button}>
<Avatar className={styles.avatar}>
<AddAPhotoIcon />
</Avatar>
</IconButton>
</Tooltip>
</div>
</Grid>
</Grid>
<Grid container spacing={3} item xs={9}>
<Grid item xs={12}>
<TextField
margin="normal"
id="name"
label="名前"
fullWidth
className={styles.text_field}
/>
<TextField
margin="normal"
id="kana"
label="ふりがな"
fullWidth
className={styles.text_field}
/>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
margin="normal"
id="birthday"
label="生年月日"
format="MM/dd/yyyy"
value={new Date("2014-08-18T21:11:54")}
onChange={() => null}
fullWidth
/>
</MuiPickersUtilsProvider>
<TextField
margin="normal"
id="birthplace"
label="出身地"
fullWidth
className={styles.text_field}
/>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
margin="normal"
id="birthday"
label="生年月日"
format="MM/dd/yyyy"
value={new Date("2014-08-18T21:11:54")}
onChange={() => null}
fullWidth
/>
</MuiPickersUtilsProvider>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={12}>
<TextField
margin="normal"
id="github"
label="GitHub"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<GitHubIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="twitter"
label="Twitter"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<TwitterIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="facebook"
label="Facebook"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FacebootIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="instagram"
label="Instagram"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<InstagramIcon />
</InputAdornment>
),
}}
/>
<TextField
margin="normal"
id="web"
label="Web"
fullWidth
className={styles.text_field}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<WebIcon />
</InputAdornment>
),
}}
/>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={3}>
<TextField
margin="normal"
id="title1"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents1"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages1"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="title2"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents2"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages2"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="title3"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents3"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages3"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="title4"
label="PieChartタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={7}>
<TextField
margin="normal"
id="contents4"
label="内容"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={2}>
<TextField
margin="normal"
id="percentages4"
label="%"
variant="filled"
fullWidth
className={styles.text_field}
/>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={12}>
<Card className={styles.chip_card}>
<CardHeader
title="chipselect"
className={styles.card_header}
/>
<CardContent>
{chipData.map((data) => {
let icon = (
<SentimentVeryDissatisfiedIcon
className={styles.verydissatisfied_icon}
/>
);
if (data.level === 1) {
icon = (
<SentimentVerySatisfiedIcon
className={styles.verysatisfied_icon}
/>
);
} else if (data.level === 2) {
icon = (
<SentimentSatisfiedIcon
className={styles.satisfied_icon}
/>
);
}
return (
<Chip
icon={icon}
label={data.label}
onClick={() => null}
className={styles.chip}
/>
);
})}
</CardContent>
</Card>
</Grid>
</Grid>
<Grid container spacing={3} item xs={12}>
<Grid item xs={3}>
<TextField
margin="normal"
id="textcard_title1"
label="テキストタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={9}>
<TextField
margin="normal"
id="textcard_contents1"
label="内容"
multiline
fullWidth
variant="outlined"
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="textcard_title2"
label="テキストタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={9}>
<TextField
margin="normal"
id="textcard_contents2"
label="内容"
multiline
fullWidth
variant="outlined"
className={styles.text_field}
/>
</Grid>
<Grid item xs={3}>
<TextField
margin="normal"
id="textcard_title3"
label="テキストタイトル"
fullWidth
className={styles.text_field}
/>
</Grid>
<Grid item xs={9}>
<TextField
margin="normal"
id="textcard_contents3"
label="内容"
multiline
fullWidth
variant="outlined"
className={styles.text_field}
/>
</Grid>
</Grid>
</Grid>
</CardContent>
<Box component="span" m={2} className={styles.button_wrapper}>
<Button
type="submit"
variant="contained"
color="primary"
className={styles.submit_button}
>
SUBMIT
</Button>
<Button
type="button"
variant="contained"
color="secondary"
onClick={() => null}
className={styles.cancel_button}
>
CANCEL
</Button>
</Box>
</Card>
</Grid>
</Grid>
</form>
</div>
);
};
export default Sample2Page;
.root {
.form {
.form_card {
.upload_button {
display: flex;
justify-content: center;
align-items: center;
.icon_button {
.avatar {
width: 150px;
height: 150px;
}
}
}
.chip_card {
background-color: transparent;
border: 1px solid;
.chip {
margin: 5px;
}
.verydissatisfied_icon {
color: gray;
}
.verysatisfied_icon {
color: blue;
}
.satisfied_icon {
color: salmon;
}
}
.button_wrapper {
display: flex;
justify-content: flex-end;
align-items: flex-end;
margin: 20px;
.submit_button,
.cancel_button {
margin: 5px;
}
}
}
}
}
最終的な画面はこちら!
まとめ
今回は、TextFeildやAvaterボタン、Chipボタンを実装してFormのレイアウトを作成していきました。
一部、省略した部分もありますが、全体のコードを載せておいたので、そちらから最終コードは確認してください。
今回は、全て同一ファイルに記述していきましたが、実際は適度な粒度でコンポーネント分けした方がいいですね。
本記事はReact・TypeScript・Redux Toolkitの利用を前提に実装をしていきます。
構築からデプロイまでやっていきますので、必要に応じて他の記事も参考にしてみてください。
また、今回のReactを勉強するにあたり利用した教材をあげています。
主に本(Amazon Unlimitedを含む)とUdemyの動画教材を利用しています。ここには私なりに理解した内容をもとに独自に内容を考えて共有しているので興味ある方は、本やUdemyを購入して勉強してみることをおすすめします。
学習に使った本はこちら!
Amazon Kindle Unlimited でもReactの本はいっぱいあるぞ!