# Modal

Ein Modal zeigt Inhalte zentriert als Overlay über der Hauptseite an, wodurch der Nutzer sich voll auf den Inhalt des Modals konzentrieren kann.

## Overview

# Playground

Verwende `<Modal />`, um ein Modal darzustellen. Da ein Modal in der Regel nur
durch eine Aktion des Nutzers erscheint, wird ein `<ModalTrigger />` in
Kombination z. B. einem `<Button />` benötigt.

```tsx
import {
  Action,
  ActionGroup,
  Button,
  Content,
  Heading,
  Label,
  Modal,
  ModalTrigger,
  Section,
  Text,
  TextField,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/04-components/actions/action/examples/lib";

<ModalTrigger>
  <Button>Modal öffnen</Button>
  <Modal>
    <Heading>Organisation anlegen</Heading>
    <Content>
      <Section>
        <Text>
          Eine Organisation kannst du dir wie ein
          Unternehmen vorstellen. An diesem Ort verwaltest
          du deine Mitarbeiter, Zahlungsmodalitäten und
          kannst deine Rechnungen einsehen.
        </Text>
        <TextField isRequired>
          <Label>Organisationsname</Label>
        </TextField>
      </Section>
    </Content>
    <ActionGroup>
      <Action closeModal>
        <Action onAction={sleepLong}>
          <Button color="accent">
            Organisation anlegen
          </Button>
        </Action>
        <Button variant="soft" color="secondary">
          Abbrechen
        </Button>
      </Action>
    </ActionGroup>
  </Modal>
</ModalTrigger>
```

---

# Variants

## OffCanvas

Neben der klassischen Darstellung lässt sich das Modal auch als OffCanvas
öffnen.

```tsx
import {
  Action,
  ActionGroup,
  Button,
  Content,
  Heading,
  Label,
  Modal,
  ModalTrigger,
  Section,
  Text,
  TextField,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/04-components/actions/action/examples/lib";

<ModalTrigger>
  <Button>OffCanvas öffnen</Button>
  <Modal offCanvas>
    <Heading>Organisation anlegen</Heading>
    <Content>
      <Section>
        <Text>
          Eine Organisation kannst du dir wie ein
          Unternehmen vorstellen. An diesem Ort verwaltest
          du deine Mitarbeiter, Zahlungsmodalitäten und
          kannst deine Rechnungen einsehen.
        </Text>
        <TextField>
          <Label>Organisationsname</Label>
        </TextField>
      </Section>
    </Content>
    <ActionGroup>
      <Action closeModal>
        <Action onAction={sleepLong}>
          <Button color="accent">
            Organisation anlegen
          </Button>
        </Action>
        <Button variant="soft" color="secondary">
          Abbrechen
        </Button>
      </Action>
    </ActionGroup>
  </Modal>
</ModalTrigger>
```

---

# Sizes

Das Modal gibt es in zwei Standardgrößen: **S** und **M**. Beide Größen sind
sowohl als klassisches Modal als auch in der OffCanvas-Variante verfügbar.

```tsx
import {
  Action,
  ActionGroup,
  Button,
  ColumnLayout,
  Content,
  DatePicker,
  FieldDescription,
  Heading,
  Label,
  Link,
  Modal,
  ModalTrigger,
  Option,
  RadioButton,
  RadioGroup,
  Section,
  Segment,
  SegmentedControl,
  Select,
  Switch,
  Text,
  TextField,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/04-components/actions/action/examples/lib";

export default () => {
  return (
    <>
      <ModalTrigger>
        <Button>Modal S</Button>
        <Modal size="s">
          <Heading>
            Möchtest du die Bestellung wirklich abbrechen?
          </Heading>
          <Content>
            <Section>
              <Text>
                Deine eingegebenen Daten werden nicht
                gespeichert.
              </Text>
            </Section>
          </Content>
          <ActionGroup>
            <Action closeModal>
              <Action onAction={sleepLong}>
                <Button color="danger">
                  Bestellung abbrechen
                </Button>
              </Action>
              <Button variant="soft" color="secondary">
                Bestellung fortsetzen
              </Button>
            </Action>
          </ActionGroup>
        </Modal>
      </ModalTrigger>

      <ModalTrigger>
        <Button>Modal M</Button>
        <Modal size="m">
          <Heading>Backup anlegen</Heading>
          <Content>
            <Section>
              <Text>
                Das Backup enthält alle Dateien deines
                Dateisystems und den Inhalt deiner
                Datenbanken. Dei Erstellung eines Backups
                dauert in der Regel einige Minuten.
              </Text>
              <ColumnLayout m={[1, 1]}>
                <TextField>
                  <Label>Beschreibung</Label>
                </TextField>
                <Select isRequired>
                  <Label>Speicherdauer</Label>
                  <Option>7 Tage</Option>
                  <Option>14 Tage</Option>
                  <Option>30 Tage</Option>
                  <Option>6 Monate</Option>
                  <Option>12 Monate</Option>
                </Select>
              </ColumnLayout>
            </Section>
          </Content>
          <ActionGroup>
            <Action closeModal>
              <Action onAction={sleepLong}>
                <Button color="accent">
                  Backup anlegen
                </Button>
              </Action>
              <Button variant="soft" color="secondary">
                Abbrechen
              </Button>
            </Action>
          </ActionGroup>
        </Modal>
      </ModalTrigger>

      <ModalTrigger>
        <Button>OffCanvas S</Button>
        <Modal size="s" offCanvas>
          <Heading>Dashboard-Einstellungen</Heading>
          <Content>
            <Section>
              <Heading>Widget-Sichtbarkeit</Heading>
              <Text>
                Aktiviere und deaktiviere die Widgets, die
                du wirklich benötigst. So bestimmst du
                selbst, wie dein Dashboard aussehen soll.
              </Text>
              <ColumnLayout s={[1]} gap="xl">
                <ColumnLayout s={[1]} gap="s">
                  <Switch>Erste Schritte</Switch>
                  <Text>
                    Im Onboarding erklären wir dir alles
                    Wichtige im mStudio.
                  </Text>
                  <Link>Erste Schritte starten</Link>
                </ColumnLayout>

                <ColumnLayout s={[1]} gap="s">
                  <Switch defaultSelected>
                    mittwald Status
                  </Switch>
                  <Text>
                    Wir informieren dich über Wartung und
                    Störungen.
                  </Text>
                </ColumnLayout>

                <ColumnLayout s={[1]} gap="s">
                  <Switch>mittwald Produkt-Slider</Switch>
                  <Text>
                    Im Produkt-Slider erhälst du
                    Informationen und einen schnellen
                    Einstieg in weitere mittwald Produkte.
                  </Text>
                </ColumnLayout>

                <ColumnLayout s={[1]} gap="s">
                  <Switch defaultSelected>
                    Neue Features
                  </Switch>
                  <Text>
                    Wir entwickeln das mStudio stetig weiter
                    Alle kommenden Features findest du auf
                    der <Link>Roadmap</Link>.
                  </Text>
                  <Link>Changelog öffnen</Link>
                </ColumnLayout>

                <ColumnLayout s={[1]} gap="s">
                  <Switch defaultSelected>
                    Neue Blogbeiträge
                  </Switch>
                  <Text>
                    Wir zeigen dir den neuesten mittwald
                    Blogartikel an.
                  </Text>
                  <Link>Blogartikel öffnen</Link>
                </ColumnLayout>

                <ColumnLayout s={[1]} gap="s">
                  <Switch>Lastschift Hinweis</Switch>
                  <Text>
                    Wir informieren über die neue
                    Möglichkeit, deine Rechnungen per
                    Lastschrift zu bezahlen.
                  </Text>
                </ColumnLayout>
              </ColumnLayout>
            </Section>
          </Content>
          <ActionGroup>
            <Action closeModal>
              <Button variant="soft" color="secondary">
                Schließen
              </Button>
            </Action>
          </ActionGroup>
        </Modal>
      </ModalTrigger>

      <ModalTrigger>
        <Button>OffCanvas M</Button>
        <Modal size="m" offCanvas>
          <Heading>SFTP-Benutzer anlegen</Heading>
          <Content>
            <Section>
              <Heading>Beschreibung</Heading>
              <Text>
                Mit einem SFTP-Benutzer kannst du dich mit
                deinem Projekt verbinden, um z.B. Dateien
                hochzuladen.
              </Text>
              <ColumnLayout m={[1, 1]}>
                <TextField isRequired>
                  <Label>Bezeichnung</Label>
                </TextField>
                <DatePicker>
                  <Label>Ablaufdatum</Label>
                  <FieldDescription>
                    Nach diesem Datum wird der SFTP-Benutzer
                    gelöscht.
                  </FieldDescription>
                </DatePicker>
              </ColumnLayout>

              <Heading>Authentifizierung</Heading>
              <Text>
                Wähle zwischen der Authentifikation per
                Passwort oder über einen SSH-Key.
              </Text>
              <SegmentedControl
                value="password"
                aria-label="Authentifizierung"
              >
                <Segment value="password">Passwort</Segment>
                <Segment value="ssh">SSH-Key</Segment>
              </SegmentedControl>
              <ColumnLayout s={[1, 1]}>
                <TextField isRequired>
                  <Label>Passwort</Label>
                </TextField>
              </ColumnLayout>

              <Heading>Berechtigungen</Heading>
              <Text>
                Wähle hier die Berechtigungen aus, mit denen
                der SFTP-Benutzer zugreifen darf.
              </Text>
              <RadioGroup
                s={[1, 1]}
                defaultValue="read&write"
                aria-label="Berechtigungen"
              >
                <RadioButton value="write">
                  <Text>Lesezugriff</Text>
                  <Content>
                    Der SFTP-Benutzer kann Dateien einsehen
                    und herunterladen.
                  </Content>
                </RadioButton>
                <RadioButton value="read&write">
                  <Text>Lese- und Schreibzugriff</Text>
                  <Content>
                    Der SFTP-Benutzer kann Dateien einsehen,
                    bearbeiten, hoch und herunterladen.
                  </Content>
                </RadioButton>
              </RadioGroup>

              <Heading>Verzeichnisauswahl</Heading>
              <Text>
                Hier legst du das Verzeichnis fest, auf das
                der SFTP-Benutzer Zugriff hat.
              </Text>
              <TextField isRequired>
                <Label>Pfad</Label>
              </TextField>
            </Section>
          </Content>
          <ActionGroup>
            <Action closeModal>
              <Action onAction={sleepLong}>
                <Button color="accent">
                  SFTP-Benutzer anlegen
                </Button>
              </Action>
              <Button variant="soft" color="secondary">
                Abbrechen
              </Button>
            </Action>
          </ActionGroup>
        </Modal>
      </ModalTrigger>
    </>
  );
}
```

**Size S:** Ein Modal in der Size S (Breite 660 px) wird zum Beispiel für
einfache Abfragen, ob etwas gelöscht werden soll, verwendet.

**Size M:** Ein Modal in der Size M (Breite 900 px) wird für komplexere Dialoge
(z. B. Erstellungs-Prozesse mit mehreren Eingabefeldern) verwendet.

---

# Kombiniere mit ...

## Controller

Neben dem `<ModalTrigger />` kann das Modal auch über einen Controller gesteuert
werden.

Dieser Controller steht auch in Modals zur Verfügung, die über den ModalTrigger
geöffnet wurden.

```tsx
import {
  Action,
  ActionGroup,
  Button,
  Content,
  Heading,
  Label,
  Modal,
  Section,
  Text,
  TextField,
  useModalController,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/04-components/actions/action/examples/lib";

export default () => {
  const controller = useModalController();

  return (
    <>
      <Button onPress={controller.open}>
        Modal öffnen
      </Button>

      <Modal controller={controller}>
        <Heading>Organisation anlegen</Heading>
        <Content>
          <Section>
            <Text>
              Eine Organisation kannst du dir wie ein
              Unternehmen vorstellen. An diesem Ort
              verwaltest du deine Mitarbeiter,
              Zahlungsmodalitäten und kannst deine
              Rechnungen einsehen.
            </Text>
            <TextField isRequired>
              <Label>Organisationsname</Label>
            </TextField>
          </Section>
        </Content>
        <ActionGroup>
          <Action closeModal>
            <Action onAction={sleepLong}>
              <Button color="accent">
                Organisation anlegen
              </Button>
            </Action>
            <Button variant="soft" color="secondary">
              Abbrechen
            </Button>
          </Action>
        </ActionGroup>
      </Modal>
    </>
  );
}
```

## Form

Verwende eine Form innerhalb eines Modals wie im folgenden Beispiel.

```tsx
import {
  Action,
  ActionGroup,
  Button,
  Content,
  Heading,
  Label,
  Modal,
  TextField,
  useModalController,
} from "@mittwald/flow-react-components";
import { useForm } from "react-hook-form";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";

export default () => {
  const controller = useModalController();

  const form = useForm<{ name: string }>();

  const Field = typedField(form);

  const handleSubmit = async () => {
    /** ... submit logic */
    return () => {
      // Close after successful submission
      controller.close();
    };
  };

  return (
    <>
      <Button onPress={controller.open}>
        Modal öffnen
      </Button>

      <Modal controller={controller}>
        <Form form={form} onSubmit={handleSubmit}>
          <Heading>Organisation anlegen</Heading>

          <Content>
            <Field
              name="name"
              rules={{
                required: "Bitte gib einen Namen ein",
              }}
            >
              <TextField>
                <Label>Name</Label>
              </TextField>
            </Field>
          </Content>

          <ActionGroup>
            <SubmitButton color="accent">
              Speichern
            </SubmitButton>
            <Action closeModal>
              <Button color="secondary" variant="soft">
                Abbrechen
              </Button>
            </Action>
          </ActionGroup>
        </Form>
      </Modal>
    </>
  );
}
```


## Develop

# Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"s" \| "m" \| "l"` | `"s"` | The size of the modal. |
| `offCanvas` | `boolean` | - | Whether the modal should be displayed as an off canvas. |
| `offCanvasOrientation` | `"left" \| "right"` | `"right"` | Whether the off canvas should be displayed on the right or left side of the screen. |
| `controller` | `OverlayController` | - | An overlay controller to control the modal state. |
| `slot` | `string` | - | Accepts "actionConfirm" to use the modal as a confirmation modal for an action. |
| `isDismissable` | `boolean` | - | Whether the modal can be closed by clicking outside of it. |
| `showCloseButton` | `boolean` | - | Whether the close button should be visible |
| `children` | `ReactNode` | - | - |
| `wrapWith` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | - | - |
| `ref` | `Ref<HTMLSpanElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see [React Docs](https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom) |
| `key` | `Key` | - | - |
| `className` | `string` | - | The elements class name. |
| `isOpen` | `boolean` | - | - |
| `isDefaultOpen` | `boolean` | - | - |

### Events

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `onOpenChange` | `OverlayOpenStateHandler` | - | - |
| `onClose` | `OverlayCloseHandler` | - | - |
| `onOpen` | `OverlayOpenHandler` | - | - |


## Guidelines

# Grundlagen

## Best practices

- Setze Modals nur ein, wenn sie wirklich notwendig sind. Ein geöffnetes Modal
  zieht die gesamte Aufmerksamkeit des Nutzers auf sich und lenkt von der
  restlichen Seite ab.
- Stelle umfangreiche Prozesse in einem OffCanvas dar.
- Überfordere den Nutzer nicht mit mehr als drei Aktionen im Modal-Footer.
- Stelle sicher, dass das Modal leicht zu schließen ist, damit der Nutzer nicht
  in eine Sackgasse gerät.
- Wähle eine verständliche und prägnante Überschrift und halte den restlichen
  Inhalt leicht verständlich.
- Es sollte sich maximal ein weiteres Modal über einem bereits geöffneten Modal
  öffnen.

## Verwendung

Verwende ein Modal, um ...

- Dialoge zur Abfrage anzuzeigen, z. B. um zu bestätigen, ob etwas wirklich
  gelöscht werden soll.
- Erstellungsprozesse zu ermöglichen, ohne die aktuelle Seite zu verlassen.
- Formulare zur Bearbeitung anzuzeigen.

## Modal vs. OffCanvas

Modal und OffCanvas erfüllen beide den Zweck, dem Nutzer Informationen in einem
Overlay zu vermitteln. Auch technisch sind sie identisch.

Dennoch sollte je nach Anwendungsfall zwischen beiden unterschieden werden, da
die unterschiedliche Darstellung verschiedene Vorteile mit sich bringt.

  

**ℹ️ Info**

- einfache Dialoge wie Abfragen oder kurze Anlege-Prozesse anzuzeigen.

    - kurze Bearbeitungsmöglichkeiten darzustellen.

    - Inhalte anzuzeigen, die kein Scrollen erfordern und für die die Breite des Modals ausreicht.

  

**ℹ️ Info**

- Inhalte anzuzeigen, die nicht gekürzt werden können und ein Scrollen
    erfordern.

    - sehr breite Inhalte anzuzeigen.

---

# Inhalt

In den meisten Fällen ist eine
[ActionGroup](/04-components/actions/action-group/overview) im Footer des Modals
erforderlich, um Eingaben zu bestätigen oder das Modal zu verlassen.

Beachte bei der Verwendung jedoch, dass manche Inhalte keine Bestätigung
erfordern, da ...

- manche Modals rein informativen Inhalt enthalten.
- einige Inhalte sich unmittelbar selber speichern und zu direkten Änderungen
  führen (z. B. Einstellungen in einem OffCanvas mittels
  [Switch](/04-components/form-controls/switch/overview)).
- bestimmte Eingaben sich automatisch selber validieren (z. B.
  2-FA-Authentifikation) und das Modal schließen.

In diesen Fällen benötigt die ActionGroup keine Hauptaktion.

**ℹ️ Info**

Manche Eingaben erfordern keine explizite Bestätigung. An diesen Stellen ist
  es ausreichend, nur einen „Schließen“-Button in der ActionGroup des Modal
  Footers anzuzeigen.

```tsx
import {
  Action,
  ActionGroup,
  Button,
  Heading,
  Section,
  Switch,
} from "@mittwald/flow-react-components";

<StaticModal>
  <header className="flow--modal--header">
    <Heading>Einstellungen</Heading>
  </header>
  <div className="flow--modal--content">
    <Section>
      <Switch defaultSelected>
        Container Frontend anzeigen
      </Switch>
      <Switch>Extensions anzeigen</Switch>
    </Section>
  </div>
  <ActionGroup className="flow--modal--action-group">
    <Action closeModal>
      <Button variant="soft" color="secondary">
        Schließen
      </Button>
    </Action>
  </ActionGroup>
</StaticModal>
```

---

# Writing guidelines

## Heading

Beachte bei der [Heading](/04-components/content/heading/overview) folgende
Punkte:

- Halte Überschriften kurz und verständlich.
- Zeichne sie semantisch korrekt aus und halte die Reihenfolge (H2, H3, H4, ...)
  ein. Die erste Überschrift in einem Modal ist semantisch immer eine H2.
- HTML-Semantik und visuelle Darstellung können separat voneinander festgelegt
  werden.
- Beginne Lösch-Prozesse immer mit einer Frage.

  

**✅ Do**

```tsx
import { Heading } from "@mittwald/flow-react-components";

<>
  <Heading level={4}>E-Mail-Adresse bearbeiten</Heading>
  <Heading level={4}>Projekt wirklich löschen?</Heading>
  <Heading level={4}>
    Projekt wirklich löschen? - App auswählen
  </Heading>
</>
```

  

**⛔️ Don't**

```tsx
import { Heading } from "@mittwald/flow-react-components";

<>
  <Heading level={4}>
    Bearbeite hier deine E-Mail-Adresse
  </Heading>
  <Heading level={4}>Projektlöschvorgang starten</Heading>
  <Heading level={4}>Wähle hier eine App aus</Heading>
</>
```

## Text

Unter der Heading befindet sich oft ein beschreibender Text. Dabei sind folgende
Punkte zu beachten:

- Setze wichtige Informationen an den Anfang.
- Hebe wichtige Informationen in Bold hervor.
- Verzichte auf einen passiven Satzbau und formuliere aktiv.

---

# Behavior

## Responsive layout

Wenn die Bildschirmgröße weniger als 551 px beträgt, dann füllt das Modal die
gesamte Breite des Bildschirms aus und wird am unteren Rand des Bildschirms
angezeigt.

**ℹ️ Info**

```tsx
import {
  Action,
  ActionGroup,
  Button,
  Heading,
  Label,
  Section,
  Text,
  TextField,
} from "@mittwald/flow-react-components";

<StaticModal>
  <header className="flow--modal--header">
    <Heading>Organisation anlegen</Heading>
  </header>

  <div className="flow--modal--content">
    <Section>
      <Text>
        Eine Organisation kannst du dir wie ein Unternehmen
        vorstellen. An diesem Ort verwaltest du deine
        Mitarbeiter, Zahlungsmodalitäten und kannst deine
        Rechnungen einsehen.
      </Text>
      <TextField>
        <Label>Organisationsname</Label>
      </TextField>
    </Section>
  </div>
  <ActionGroup className="flow--modal--action-group">
    <Action closeModal>
      <Button color="accent">Organisation anlegen</Button>
      <Button variant="soft" color="secondary">
        Abbrechen
      </Button>
    </Action>
  </ActionGroup>
</StaticModal>
```

## Schließen eines Modals

Das Schließen eines Modals sollte einfach sein, besonders wenn im Modal nichts
Wichtiges vom Nutzer bestätigt werden muss. Es sollte möglich sein, es durch
einen Klick außerhalb des Modals oder durch Drücken der "Schließen"- oder
"Abbrechen"-Taste oder der Esc-Taste zu schließen.

