Shadcn/uiを使った入力フォームを作っていきます。
ディレクトリ構成
└── ReactApp/
├── node-modules
├── public
└── src/
├── assets
├── components\ui
├── lib
├── App.tsx
├── Form.tsx
├── index.css
└── ...(略)
npmを使ったパッケージ導入
Shadcn/uiの各コンポーネントの導入
Shadcn/uiの各コンポーネントをパッケージ管理ツールnpmを使ってインストールしていきます。
●Buttonコンポーネント
npx shadcn@latest add button
●Cardコンポーネント
npx shadcn@latest add card
●Fieldコンポーネント
npx shadcn@latest add field
●Inputコンポーネント
npx shadcn@latest add input
●Input-Groupコンポーネント
npx shadcn@latest add input-group
hookform/resolversの導入
react-hook-formの導入
sonnerの導入
zodの導入
コード
src/App.tsx
import React,{ useState } from 'react';
import Login from './Login';
import Register from './Register';
import axios from 'axios';
import { BugReportForm } from './Form';
const App = ()=>{
return (
<div className="flex justify-center items-center min-h-screen p-8 bg-gray-50">
<BugReportForm />
</div>
)
}
export default App;
src/form.tsx
import * as React from "react"
import { zodResolver } from "@hookform/resolvers/zod"
import { Controller, useForm } from "react-hook-form"
import { toast } from "sonner"
import * as z from "zod"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
InputGroup,
InputGroupAddon,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"
const formSchema = z.object({
title: z
.string()
.min(5, "Titleは、少なくとも5文字以上入力してください。")
.max(32, "Titleは、最大32文字以下入力してください。"),
description: z
.string()
.min(20, "アンケート内容欄は、少なくとも20文字以上入力してください。")
.max(100, "アンケート内容欄は、最大100文字以下入力してください。"),
})
export function BugReportForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
title: "",
description: "",
},
})
function onSubmit(data: z.infer<typeof formSchema>) {
toast("You submitted the following values:", {
description: (
<pre className="bg-code text-code-foreground mt-2 w-[320px] overflow-x-auto rounded-md p-4">
<code>{JSON.stringify(data, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>アンケート</CardTitle>
<CardDescription>
アンケートの入力をお願いいたします。
</CardDescription>
</CardHeader>
<CardContent>
<form id="form-rhf-demo" onSubmit={form.handleSubmit(onSubmit)}>
<FieldGroup>
<Controller
name="title"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="form-rhf-demo-title">
タイトル
</FieldLabel>
<Input
{...field}
id="form-rhf-demo-title"
aria-invalid={fieldState.invalid}
placeholder="タイトルを入力"
autoComplete="off"
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
name="description"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="form-rhf-demo-description">
アンケート内容
</FieldLabel>
<InputGroup>
<InputGroupTextarea
{...field}
id="form-rhf-demo-description"
placeholder="アンケート内容を入力"
rows={6}
className="min-h-24 resize-none"
aria-invalid={fieldState.invalid}
/>
<InputGroupAddon align="block-end">
<InputGroupText className="tabular-nums">
{field.value.length}/100 characters
</InputGroupText>
</InputGroupAddon>
</InputGroup>
<FieldDescription>
アンケートにご協力いただきありがとうございます。
</FieldDescription>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
</FieldGroup>
</form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => form.reset()}>
Reset
</Button>
<Button type="submit" form="form-rhf-demo">
Submit
</Button>
</Field>
</CardFooter>
</Card>
)
}
サイト
■Shadcn/ui
■npm公式サイト
