# Formular

```tsx
import {
  ActionGroup,
  ColumnLayout,
  Heading,
  Label,
  Section,
  TextField,
} from "@mittwald/flow-react-components";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";

export default () => {
  const form = useForm<{
    firstName: string;
    lastName: string;
    street: string;
    houseNumber: string;
    zip: string;
    city: string;
    email: string;
    phone?: string;
  }>({
    defaultValues: {
      firstName: "",
      lastName: "",
      street: "",
      houseNumber: "",
      zip: "",
      city: "",
      email: "",
      phone: "",
    },
  });

  const Field = typedField(form);

  return (
    <Form
      form={form}
      onSubmit={() => console.log("submitted")}
    >
      <Section>
        <Heading>Rechnungsadresse</Heading>
        <ColumnLayout l={[1, 1]}>
          <Field
            name="firstName"
            rules={{
              required: "Bitte gib einen Vornamen ein",
            }}
          >
            <TextField>
              <Label>Vorname</Label>
            </TextField>
          </Field>
          <Field
            name="lastName"
            rules={{
              required: "Bitte gib einen Nachnamen ein",
            }}
          >
            <TextField>
              <Label>Nachname</Label>
            </TextField>
          </Field>
        </ColumnLayout>
        <ColumnLayout l={[1, 1]}>
          <ColumnLayout m={[2, 1]} s={[2, 1]}>
            <Field
              name="street"
              rules={{
                required: "Bitte gib eine Straße ein",
              }}
            >
              <TextField>
                <Label>Straße</Label>
              </TextField>
            </Field>
            <Field
              name="houseNumber"
              rules={{
                required: "Bitte gib eine Hausnr. ein",
              }}
            >
              <TextField>
                <Label>Hausnummer</Label>
              </TextField>
            </Field>
          </ColumnLayout>
          <ColumnLayout m={[1, 2]} s={[1, 2]}>
            <Field
              name="zip"
              rules={{
                required: "Bitte gib eine Postleitzahl ein",
              }}
            >
              <TextField>
                <Label>Postleitzahl</Label>
              </TextField>
            </Field>
            <Field
              name="city"
              rules={{
                required: "Bitte gib einen Ort ein",
              }}
            >
              <TextField>
                <Label>Ort</Label>
              </TextField>
            </Field>
          </ColumnLayout>
        </ColumnLayout>
        <ColumnLayout l={[1, 1]}>
          <Field
            name="email"
            rules={{
              required: "Bitte gib eine E-Mail-Adresse ein",
            }}
          >
            <TextField type="email">
              <Label>E-Mail-Adresse</Label>
            </TextField>
          </Field>
          <Field name="phone">
            <TextField>
              <Label>Telefonnummer</Label>
            </TextField>
          </Field>
        </ColumnLayout>
        <ActionGroup>
          <SubmitButton>Speichern</SubmitButton>
        </ActionGroup>
      </Section>
    </Form>
  );
}
```

---

# Verwendung

Verwende diesen Baustein, wenn Nutzer zusammengehörige Daten eingeben,
bearbeiten oder übermitteln. Im mStudio steht ein Formular meist in einem
Overlay zum [Anlegen & Bearbeiten](https://flow.mittwald.de/templates/overlays/anlegen-bearbeiten).

---

# Aufbau

- **Form** – Ein [Form](https://flow.mittwald.de/components/react-hook-form/form) klammert die Felder
  und verbindet sie über [Field](https://flow.mittwald.de/components/react-hook-form/field) mit React
  Hook Form. Es sammelt die Werte und übergibt sie beim Absenden.
- **Struktur** – [Sections](https://flow.mittwald.de/components/structure/section) gruppieren
  zusammengehörige Felder, ein
  [ColumnLayout](https://flow.mittwald.de/components/structure/column-layout) ordnet zusammengehörige
  Felder in eine Zeile – etwa Straße und Hausnummer oder PLZ und Ort. Ordne die
  Felder nach Wichtigkeit und stelle Zusammengehöriges nah beieinander.
- **Absenden** – Eine [ActionGroup](https://flow.mittwald.de/components/actions/action-group) trägt den
  [SubmitButton](https://flow.mittwald.de/components/react-hook-form/submit-button). Die Validierung
  erfolgt standardmäßig erst beim Absenden. Quick Submit sendet das Formular
  zusätzlich per ⌘/Strg + Enter aus jedem Feld ab – besonders hilfreich in einer
  [TextArea](https://flow.mittwald.de/components/form-controls/text-area) oder einem
  [MarkdownEditor](https://flow.mittwald.de/components/form-controls/markdown-editor).

### Field

Diese Punkte kehren über alle Form Controls hinweg wieder:

- **Label** – Ein gutes [Label](https://flow.mittwald.de/components/content/label) vermittelt alle
  notwendigen Informationen klar und prägnant (max. 2 Wörter). Felder sind
  standardmäßig Pflichtfelder; optionale Felder tragen den Zusatz „(optional)"
  am Label. Lässt sich die Anforderung aus dem Kontext erschließen, kann ein
  sichtbares Label entfallen – dann muss das Form Control über `aria-labelledby`
  verknüpft oder mit `aria-label` beschrieben sein.
- **Fields & Controls** – Das Design System bietet viele wiederverwendbare Form
  Controls: Fields wie [TextField](https://flow.mittwald.de/components/form-controls/text-field),
  [NumberField](https://flow.mittwald.de/components/form-controls/number-field) oder
  [PasswordCreationField](https://flow.mittwald.de/components/form-controls/password-creation-field)
  sowie Controls wie [Checkbox](https://flow.mittwald.de/components/form-controls/checkbox),
  [Select](https://flow.mittwald.de/components/form-controls/select) oder
  [RadioGroup](https://flow.mittwald.de/components/form-controls/radio-group). Alle findest du unter
  Components → Form Controls.
- **FieldDescription** – Die `<FieldDescription />` ist ein optionaler Hilfstext
  unterhalb des Form Controls. Verwende sie sparsam: nur, wenn das Label allein
  eine Frage offenlässt – etwa ein Beispiel, ein Format, eine Einheit, eine
  Einschränkung oder eine Konsequenz.
- **FieldError** – Ein ungültiges Field zeigt immer eine Fehlermeldung über die
  `<FieldError />`. Sie erklärt verständlich, warum die Eingabe ungültig ist,
  und hilft bei der Korrektur. Formulierungshinweise stehen in der Guideline zu
  [Fehlermeldungen](https://flow.mittwald.de/foundations/content-guidelines/fehlermeldungen).
- **Placeholder** – Ein Placeholder ersetzt kein Label: Er verschwindet bei der
  Eingabe und wird von Assistenztechnologien oft nicht zuverlässig erkannt.
  Nutze ein `Label` für essenzielle Anforderungen und eine `FieldDescription`
  für Beispiele oder Formatierungshinweise. Eine Ausnahme ist das
  [SearchField](https://flow.mittwald.de/components/form-controls/search-field) mit seinem
  kennzeichnenden Leading-Icon.

---

# Varianten und Abwandlung

## Mit Validierung

Nutze für die Validierung in der Regel
[React Hook Form](https://flow.mittwald.de/components/react-hook-form/form). Alternativ stehen
`isRequired` (für Pflichtfelder) und `validate` (für eigene Validierungen) zur
Verfügung. Bei ungültiger Eingabe wird das Field invalidiert; über die
`FieldError` kann eine Fehlermeldung ausgegeben werden.

```tsx
import {
  ActionGroup,
  Heading,
  Label,
  Section,
  TextArea,
} from "@mittwald/flow-react-components";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";

export default () => {
  const form = useForm<{
    publicKey: string;
  }>({
    defaultValues: {
      publicKey: "",
    },
  });

  const Field = typedField(form);

  return (
    <Form
      form={form}
      onSubmit={() => console.log("submitted")}
    >
      <Section>
        <Heading>Validierung</Heading>
        <Field
          name="publicKey"
          rules={{
            required: "Bitte gib einen SSH-Key ein",
            validate: {
              isValid: (key) =>
                key.startsWith("rsa")
                  ? true
                  : "Der SSH-Key muss mit 'rsa' anfangen",
            },
            minLength: {
              value: 10,
              message:
                "Der SSH-Key muss mindestens 10 Zeichen lang sein",
            },
          }}
        >
          <TextArea maxLength={8000} showCharacterCount>
            <Label>SSH-Key</Label>
          </TextArea>
        </Field>

        <ActionGroup>
          <SubmitButton>Speichern</SubmitButton>
        </ActionGroup>
      </Section>
    </Form>
  );
}
```

## Progressive Disclosure

Felder erscheinen erst, wenn sie gebraucht werden. Ein übergeordnetes Control
wie eine [RadioGroup](https://flow.mittwald.de/components/form-controls/radio-group) oder eine
[Checkbox](https://flow.mittwald.de/components/form-controls/checkbox) blendet die abhängigen Felder ein
und hält das Formular so schlank.

```tsx
import {
  ActionGroup,
  ColumnLayout,
  Heading,
  Label,
  RadioButton,
  RadioGroup,
  Section,
  Text,
  TextField,
} from "@mittwald/flow-react-components";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm, useWatch } from "react-hook-form";

export default () => {
  const form = useForm<{
    paymentMethod: "invoice" | "debit";
    accountHolder: string;
    iban: string;
  }>({
    defaultValues: {
      paymentMethod: "invoice",
      accountHolder: "",
      iban: "",
    },
  });

  const Field = typedField(form);

  const watchedPaymentMethod = useWatch({
    control: form.control,
    name: "paymentMethod",
  });

  return (
    <Form
      form={form}
      onSubmit={() => console.log("submitted")}
    >
      <Section>
        <Heading>Zahlungsart</Heading>
        <Field name="paymentMethod">
          <RadioGroup l={[1, 1]} aria-label="Zahlungsart">
            <RadioButton value="invoice">
              Rechnung
            </RadioButton>
            <RadioButton value="debit">
              Lastschrift
            </RadioButton>
          </RadioGroup>
        </Field>

        {watchedPaymentMethod === "invoice" && (
          <Text>
            Bitte bezahle deine Rechnungen innerhalb von 14
            Tagen.
          </Text>
        )}

        {watchedPaymentMethod === "debit" && (
          <ColumnLayout m={[1, 1]}>
            <Field
              name="accountHolder"
              rules={{
                required:
                  "Bitte gib einen Kontoinhaber ein",
              }}
            >
              <TextField>
                <Label>Kontoinhaber</Label>
              </TextField>
            </Field>
            <Field
              name="iban"
              rules={{
                required: "Bitte gib eine IBAN ein",
              }}
            >
              <TextField>
                <Label>IBAN</Label>
              </TextField>
            </Field>
          </ColumnLayout>
        )}

        <ActionGroup>
          <SubmitButton>Speichern</SubmitButton>
        </ActionGroup>
      </Section>
    </Form>
  );
}
```
