Formコンポーネント

デザインシステムに準拠した統一的なフォームコンポーネント群のデモとテスト

Input

テキスト入力コンポーネント。様々なタイプとアイコンに対応

Input Example

TSX
import { Input, FormLabel, FormGroup } from "@/components/Form";

// 基本的な使用例
<div>
  <FormLabel htmlFor="text" size="md">テキスト入力</FormLabel>
  <FormGroup>
    <Input
      id="text"
      type="text"
      size="md"
      placeholder="テキストを入力..."
      value={text}
      onChange={(e) => setText(e.target.value)}
    />
  </FormGroup>
</div>

// アイコン付き
<div>
  <FormLabel htmlFor="search" size="md">検索</FormLabel>
  <FormGroup>
    <Input
      id="search"
      type="search"
      size="md"
      leftIcon="search"
      placeholder="検索..."
      value={search}
      onChange={(e) => setSearch(e.target.value)}
    />
  </FormGroup>
</div>

// エラー表示
<div>
  <FormLabel htmlFor="email" size="md" required>メールアドレス</FormLabel>
  <FormGroup error={error}>
    <Input
      id="email"
      type="email"
      size="md"
      validation={error ? "error" : "none"}
      placeholder="email@example.com"
      value={email}
      onChange={handleEmailChange}
    />
  </FormGroup>
</div>

Inputのデモ

8文字以上で入力してください

サイズバリエーション

Textarea

複数行テキスト入力コンポーネント。自動リサイズ機能付き

Textarea Example

TSX
import { Textarea, FormLabel, FormGroup } from "@/components/Form";

// 基本的な使用例
<div>
  <FormLabel htmlFor="message" size="md">メッセージ</FormLabel>
  <FormGroup>
    <Textarea
      id="message"
      size="md"
      placeholder="メッセージを入力..."
      value={message}
      onChange={(e) => setMessage(e.target.value)}
    />
  </FormGroup>
</div>

// 自動リサイズ
<div>
  <FormLabel htmlFor="description" size="md">説明</FormLabel>
  <FormGroup help="自動でリサイズされます(最大10行)">
    <Textarea
      id="description"
      size="md"
      autoResize
      minRows={3}
      maxRows={10}
      placeholder="説明を入力..."
      value={description}
      onChange={(e) => setDescription(e.target.value)}
    />
  </FormGroup>
</div>

// リサイズ制御
<div>
  <FormLabel htmlFor="comment" size="md">コメント</FormLabel>
  <FormGroup>
    <Textarea
      id="comment"
      size="md"
      resize="vertical"
      placeholder="コメントを入力..."
      value={comment}
      onChange={(e) => setComment(e.target.value)}
    />
  </FormGroup>
</div>

Textareaのデモ

入力内容に応じて自動的に高さが変わります(最大10行)
右下のハンドルで縦方向にリサイズできます

サイズバリエーション

リサイズタイプ

リサイズ不可(none)

縦方向のみ(vertical)

横方向のみ(horizontal)

両方向(both)

Select

セレクトボックスコンポーネント

Select Example

TSX
import { Select, FormLabel, FormGroup } from "@/components/Form";

// 基本的な使用例
<div>
  <FormLabel htmlFor="country" size="md">国を選択</FormLabel>
  <FormGroup>
    <Select
      id="country"
      size="md"
      placeholder="選択してください"
      value={country}
      onChange={(e) => setCountry(e.target.value)}
      options={[
        { value: "jp", label: "日本" },
        { value: "us", label: "アメリカ" },
        { value: "uk", label: "イギリス" },
        { value: "fr", label: "フランス" },
      ]}
    />
  </FormGroup>
</div>

// グループ化
<div>
  <FormLabel htmlFor="category" size="md">カテゴリを選択</FormLabel>
  <FormGroup>
    <Select
      id="category"
      size="md"
      placeholder="選択してください"
      value={category}
      onChange={(e) => setCategory(e.target.value)}
      optionGroups={[
        {
          label: "プログラミング",
          options: [
            { value: "js", label: "JavaScript" },
            { value: "ts", label: "TypeScript" },
            { value: "python", label: "Python" },
          ],
        },
        {
          label: "デザイン",
          options: [
            { value: "figma", label: "Figma" },
            { value: "sketch", label: "Sketch" },
            { value: "xd", label: "Adobe XD" },
          ],
        },
      ]}
    />
  </FormGroup>
</div>

Selectのデモ

グループ化されたオプション

サイズバリエーション

Checkbox

チェックボックスコンポーネント(単一・グループ・indeterminate状態)

Checkbox Example

TSX
import { Checkbox, CheckboxGroup } from "@/components/Form";

// 単一Checkbox
<Checkbox
  id="terms"
  label="利用規約に同意する"
  checked={agreed}
  onChange={(e) => setAgreed(e.target.checked)}
/>

// デフォルトでチェック済み
<Checkbox
  id="newsletter"
  label="ニュースレターを受け取る"
  checked={newsletter}
  onChange={(e) => setNewsletter(e.target.checked)}
/>

// indeterminate状態(一部選択)
<Checkbox
  id="select-all"
  label="すべて選択"
  indeterminate={isSomeFruitsSelected}
  checked={isAllFruitsSelected}
  onChange={(e) => handleSelectAllFruits(e.target.checked)}
/>

// CheckboxGroup - 縦レイアウト
<CheckboxGroup
  options={[
    { value: "apple", label: "りんご" },
    { value: "banana", label: "バナナ" },
    { value: "orange", label: "オレンジ" },
  ]}
  value={fruits}
  onChange={setFruits}
  direction="vertical"
/>

// CheckboxGroup - 横レイアウト
<CheckboxGroup
  options={[
    { value: "email", label: "メール通知" },
    { value: "sms", label: "SMS通知" },
    { value: "push", label: "プッシュ通知" },
  ]}
  value={notifications}
  onChange={setNotifications}
  direction="horizontal"
/>

Checkboxのデモ

利用規約: 未同意
ニュースレター: 受け取る
選択中の果物: apple
通知設定: email, push

Radio

ラジオボタンコンポーネント(単一・グループ・横/縦レイアウト)

Radio Example

TSX
import { Radio, RadioGroup } from "@/components/Form";

// 単一Radio
<Radio
  id="plan-free"
  name="plan"
  value="free"
  label="無料プラン"
  checked={plan === "free"}
  onChange={(e) => setPlan(e.target.value)}
/>

<Radio
  id="plan-pro"
  name="plan"
  value="pro"
  label="プロプラン"
  checked={plan === "pro"}
  onChange={(e) => setPlan(e.target.value)}
/>

// RadioGroup - 縦レイアウト
<RadioGroup
  name="size"
  options={[
    { value: "small", label: "小" },
    { value: "medium", label: "中" },
    { value: "large", label: "大" },
  ]}
  value={size}
  onChange={setSize}
  direction="vertical"
/>

// RadioGroup - 横レイアウト
<RadioGroup
  name="color"
  options={[
    { value: "red", label: "赤" },
    { value: "blue", label: "青" },
    { value: "green", label: "緑" },
  ]}
  value={color}
  onChange={setColor}
  direction="horizontal"
/>

Radioのデモ

Small
Medium(デフォルト)
Large
選択中のプラン: free
選択中のサイズ: medium
選択中の色: blue

RangeInput

レンジスライダーコンポーネント

RangeInput Example

TSX
import { RangeInput, FormLabel, FormGroup } from "@/components/Form";

// 基本的な使用例
<div>
  <FormLabel htmlFor="volume" size="md">
    音量: {volume}
  </FormLabel>
  <FormGroup>
    <RangeInput
      id="volume"
      min="0"
      max="100"
      step="1"
      value={volume}
      onChange={(e) => setVolume(parseInt(e.target.value))}
    />
  </FormGroup>
</div>

// 温度設定(小数点対応)
<div>
  <FormLabel htmlFor="temperature" size="md">
    温度: {temperature}°C
  </FormLabel>
  <FormGroup>
    <RangeInput
      id="temperature"
      min="10"
      max="30"
      step="0.5"
      value={temperature}
      onChange={(e) => setTemperature(parseFloat(e.target.value))}
    />
  </FormGroup>
</div>

RangeInputのデモ

0〜100で調整
0.5°C刻みで調整

視覚的なフィードバック

明るさ: 75%

Complete Form Example

すべてのフォームコンポーネントを使った完全なフォーム例

Complete Form

TSX
import {
  Input,
  Textarea,
  Select,
  CheckboxGroup,
  RadioGroup,
  FormLabel,
  FormGroup,
  Checkbox,
} from "@/components/Form";
import { FormButton } from "@/components/Button";

function ContactForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    category: "",
    plan: "free",
    features: [],
    message: "",
    agreed: false,
  });

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    // バリデーション & 送信処理
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-6">
      {/* 名前 */}
      <div>
        <FormLabel htmlFor="name" size="md" required>
          お名前
        </FormLabel>
        <FormGroup error={errors.name}>
          <Input
            id="name"
            type="text"
            size="md"
            validation={errors.name ? "error" : "none"}
            value={formData.name}
            onChange={(e) => setFormData({...formData, name: e.target.value})}
          />
        </FormGroup>
      </div>

      {/* メール */}
      <div>
        <FormLabel htmlFor="email" size="md" required>
          メールアドレス
        </FormLabel>
        <FormGroup error={errors.email}>
          <Input
            id="email"
            type="email"
            size="md"
            leftIcon="mail"
            validation={errors.email ? "error" : "none"}
            value={formData.email}
            onChange={(e) => setFormData({...formData, email: e.target.value})}
          />
        </FormGroup>
      </div>

      {/* カテゴリ */}
      <div>
        <FormLabel htmlFor="category" size="md" required>
          カテゴリ
        </FormLabel>
        <FormGroup error={errors.category}>
          <Select
            id="category"
            size="md"
            placeholder="選択してください"
            validation={errors.category ? "error" : "none"}
            value={formData.category}
            onChange={(e) => setFormData({...formData, category: e.target.value})}
            options={[...]}
          />
        </FormGroup>
      </div>

      {/* プラン */}
      <div>
        <FormLabel htmlFor="plan" size="md">
          プラン選択
        </FormLabel>
        <RadioGroup
          name="plan"
          options={[...]}
          value={formData.plan}
          onChange={(value) => setFormData({...formData, plan: value})}
        />
      </div>

      {/* 機能 */}
      <div>
        <FormLabel htmlFor="features" size="md">
          必要な機能
        </FormLabel>
        <CheckboxGroup
          options={[...]}
          value={formData.features}
          onChange={(value) => setFormData({...formData, features: value})}
        />
      </div>

      {/* メッセージ */}
      <div>
        <FormLabel htmlFor="message" size="md" required>
          メッセージ
        </FormLabel>
        <FormGroup error={errors.message}>
          <Textarea
            id="message"
            size="md"
            autoResize
            validation={errors.message ? "error" : "none"}
            value={formData.message}
            onChange={(e) => setFormData({...formData, message: e.target.value})}
          />
        </FormGroup>
      </div>

      {/* 同意 */}
      <FormGroup error={errors.agreed}>
        <Checkbox
          id="agreed"
          label="利用規約に同意する"
          checked={formData.agreed}
          onChange={(e) => setFormData({...formData, agreed: e.target.checked})}
        />
      </FormGroup>

      {/* 送信ボタン */}
      <div className="flex gap-3">
        <FormButton
          type="submit"
          variant="primary"
          size="md"
          loading={isSubmitting}
          disabled={!formData.agreed}
        >
          送信
        </FormButton>
        <FormButton
          type="button"
          variant="secondary"
          size="md"
          onClick={handleReset}
        >
          リセット
        </FormButton>
      </div>
    </form>
  );
}

完全なフォーム

後から変更可能です
詳しい内容をご記入ください