# SegmentedControl

Ein SegmentedControl bietet dem User eine Einfachauswahl von 2 bis 5 kurzen Optionen. Je nach Einsatzzweck kann die Auswahl des Users den Inhalt unter dem Segmented Control verändern.

## Overview

# Playground

Verwende `<SegmentedControl />`, um ein SegmentedControl anzuzeigen.

```tsx
import {
  Label,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<SegmentedControl defaultValue="ssh-key">
  <Label>Authentifizierungsart</Label>
  <Segment value="ssh-key">SSH-Key</Segment>
  <Segment value="passwort">Passwort</Segment>
</SegmentedControl>
```

---

# Mit Inhaltsänderung

Unterhalb des SegmentedControls kann sich der Inhalt je nach Bedarf ändern.

```tsx
import {
  ColumnLayout,
  FieldDescription,
  Label,
  Section,
  Segment,
  SegmentedControl,
  TextField,
} from "@mittwald/flow-react-components";
import { useState } from "react";

export default () => {
  const [showContent, setShowContent] =
    useState<boolean>(false);

  return (
    <Section>
      <SegmentedControl
        defaultValue="lastschrift"
        onChange={() => setShowContent(!showContent)}
      >
        <Label>Zahlungsart</Label>
        <Segment value="lastschrift">Lastschrift</Segment>
        <Segment value="Rechnung">Rechnung</Segment>
        {showContent && (
          <FieldDescription>
            Wähle bitte eine Bankverbindung für die
            Bezahlung mit SEPA-Lastschrift aus.
          </FieldDescription>
        )}
      </SegmentedControl>
      {showContent && (
        <ColumnLayout>
          <TextField isRequired>
            <Label>Kontoinhaber</Label>
          </TextField>
          <TextField isRequired>
            <Label>IBAN</Label>
          </TextField>
        </ColumnLayout>
      )}
    </Section>
  );
}
```

---

# Mit FieldDescription

Innerhalb der `<SegmentedControl />` kann eine hilfreiche `<FieldDescription />`
eingebaut werden.

```tsx
import {
  FieldDescription,
  Label,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<SegmentedControl defaultValue="cloud">
  <Label>Speicherplatz</Label>
  <Segment value="cloud">Cloud</Segment>
  <Segment value="lokal">Lokal</Segment>
  <FieldDescription>
    Speicherplatz kann jederzeit geändert werden
  </FieldDescription>
</SegmentedControl>
```

---

# Disbaled

Einzelne Optionen oder die gesamte SegmentedControl können über `isDisabled`
disabled werden.

```tsx
import {
  Label,
  Section,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<Section>
  <SegmentedControl defaultValue="entwickler">
    <Label>Rolle</Label>
    <Segment value="entwickler">Entwickler</Segment>
    <Segment value="designer" isDisabled>
      Designer
    </Segment>
    <Segment value="geschäftsführer">
      Geschäftsführer
    </Segment>
    <Segment value="andere">Andere</Segment>
  </SegmentedControl>
  <SegmentedControl defaultValue="entwickler" isDisabled>
    <Label>Rolle</Label>
    <Segment value="entwickler">Entwickler</Segment>
    <Segment value="designer">Designer</Segment>
    <Segment value="geschäftsführer">
      Geschäftsführer
    </Segment>
    <Segment value="andere">Andere</Segment>
  </SegmentedControl>
</Section>
```

---

# Container Breakpoint Size

Das SegmentedControl springt in eine kompakte Variante um, sobald der Container
kleiner als der gesetzte Breakpoint ist. Im Default passiert das bei 550px, was
der `containerBreakpointSize="m"` entspricht. Über diese Property kann die Größe
mit Werten zwischen `xs` und `xl` überschrieben werden.

```tsx
import {
  ColumnLayout,
  Label,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<ColumnLayout m={[1, 1]}>
  <SegmentedControl
    defaultValue="entwickler"
    containerBreakpointSize="xl"
  >
    <Label>Rolle</Label>
    <Segment value="entwickler">Entwickler</Segment>
    <Segment value="geschäftsführer">
      Geschäftsführer
    </Segment>
    <Segment value="andere">Andere</Segment>
  </SegmentedControl>

  <SegmentedControl
    defaultValue="entwickler"
    containerBreakpointSize="xs"
  >
    <Label>Rolle</Label>
    <Segment value="entwickler">Entwickler</Segment>
    <Segment value="geschäftsführer">
      Geschäftsführer
    </Segment>
    <Segment value="andere">Andere</Segment>
  </SegmentedControl>
</ColumnLayout>
```

---

# Kombiniere mit ...

## ContextualHelp

Benutze die [ContextualHelp](/04-components/overlays/contextual-help/overview)
Komponente, wenn du weitere Informationen bereitstellen möchtest, und diese zu
lang für die FieldDescription sind.

```tsx
import {
  Button,
  ContextualHelp,
  ContextualHelpTrigger,
  Heading,
  Label,
  Segment,
  SegmentedControl,
  Text,
} from "@mittwald/flow-react-components";

<SegmentedControl defaultValue="lastschrift">
  <Label>
    Zahlungsart
    <ContextualHelpTrigger>
      <Button />
      <ContextualHelp>
        <Heading>Weitere Informationen</Heading>
        <Text>
          Hier gibt es weitere Informationen, die zu lang
          für die FieldDescription sind.
        </Text>
      </ContextualHelp>
    </ContextualHelpTrigger>
  </Label>
  <Segment value="lastschrift">Lastschrift</Segment>
  <Segment value="Rechnung">Rechnung</Segment>
</SegmentedControl>
```

## React Hook Form

```tsx
import {
  Label,
  Section,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";
import { useForm } from "react-hook-form";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { sleep } from "@/content/04-components/actions/action/examples/lib";

export default () => {
  const form = useForm<{ payment: "debit" | "invoice" }>({
    defaultValues: { payment: "debit" },
  });
  const Field = typedField(form);

  return (
    <Section>
      <Form form={form} onSubmit={sleep}>
        <Field
          name="payment"
          rules={{
            required: "Bitte wähle eine Zahlungsart aus",
          }}
        >
          <SegmentedControl>
            <Label>Zahlungsart</Label>
            <Segment value="debit">Lastschrift</Segment>
            <Segment value="invoice">Rechnung</Segment>
          </SegmentedControl>
        </Field>
        <SubmitButton>Speichern</SubmitButton>
      </Form>
    </Section>
  );
}
```


## Develop

# Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | - | The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id). |
| `translate` | `"yes" \| "no"` | - | - |
| `className` | `ClassNameOrFunction<RadioGroupRenderProps>` | `'react-aria-RadioGroup'` | The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. |
| `style` | `StyleOrFunction<TooltipRenderProps>` | - | The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state. |
| `render` | `DOMRenderFunction<"div", TooltipRenderProps>` | - | Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate. |
| `dir` | `string` | - | - |
| `lang` | `string` | - | - |
| `hidden` | `boolean` | - | - |
| `inert` | `boolean` | - | - |
| `validationBehavior` | `"native" \| "aria"` | `'native'` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA. |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed by the user. |
| `isRequired` | `boolean` | - | Whether user input is required on the input before form submission. |
| `isInvalid` | `boolean` | - | Whether the input value is invalid. |
| `validate` | `((value: TimeValue) => true \| ValidationError)` | - | A function that returns an error message if a given value is invalid. Validation errors are displayed to the user when the form is submitted if `validationBehavior="native"`. For realtime validation, use the `isInvalid` prop instead. |
| `value` | `TimeValue` | - | The current value (controlled). |
| `defaultValue` | `TimeValue` | - | The default value (uncontrolled). |
| `name` | `string` | - | The name of the input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname). |
| `form` | `string` | - | The `<form>` element to associate the input with. The value of this attribute must be the id of a `<form>` in the same document. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#form). |
| `slot` | `string` | - | A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent. |
| `orientation` | `Orientation` | `'vertical'` | The axis the Radio Button(s) should align with. |
| `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` | - | - |
| `containerBreakpointSize` | `ContainerBreakpointSize` | - | - |

### Events

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `onClick` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onClickCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onAuxClick` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onAuxClickCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onContextMenu` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onContextMenuCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onDoubleClick` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onDoubleClickCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseDown` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseDownCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseEnter` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseLeave` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseMove` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseMoveCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOut` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOutCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOver` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOverCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseUp` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseUpCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onTouchCancel` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchCancelCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchEnd` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchEndCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchMove` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchMoveCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchStart` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchStartCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onPointerDown` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerDownCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerMove` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerMoveCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerUp` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerUpCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerCancel` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerCancelCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerEnter` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerLeave` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOver` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOverCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOut` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOutCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onGotPointerCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onGotPointerCaptureCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onLostPointerCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onLostPointerCaptureCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onScroll` | `UIEventHandler<HTMLDivElement>` | - | - |
| `onScrollCapture` | `UIEventHandler<HTMLDivElement>` | - | - |
| `onWheel` | `WheelEventHandler<HTMLDivElement>` | - | - |
| `onWheelCapture` | `WheelEventHandler<HTMLDivElement>` | - | - |
| `onAnimationStart` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationStartCapture` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationEnd` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationEndCapture` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationIteration` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationIterationCapture` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onTransitionCancel` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionCancelCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionEnd` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionEndCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionRun` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionRunCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionStart` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionStartCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onFocus` | `((e: FocusEvent<Element, Element>) => void)` | - | Handler that is called when the element receives focus. |
| `onBlur` | `((e: FocusEvent<Element, Element>) => void)` | - | Handler that is called when the element loses focus. |
| `onFocusChange` | `((isFocused: boolean) => void)` | - | Handler that is called when the element's focus status changes. |
| `onChange` | `((value: TimeValue) => void)` | - | Handler that is called when the value changes. |

### Accessibility

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `aria-label` | `string` | - | Defines a string value that labels the current element. |
| `aria-labelledby` | `string` | - | Identifies the element (or elements) that labels the current element. |
| `aria-describedby` | `string` | - | Identifies the element (or elements) that describes the object. |
| `aria-details` | `string` | - | Identifies the element (or elements) that provide a detailed, extended description for the object. |
| `aria-errormessage` | `string` | - | Identifies the element that provides an error message for the object. |


## Guidelines

# Grundlagen

## Best practices

Achte bei der Verwendung eines SegmentedControl darauf, dass...

- maximal 5 Optionen angezeigt werden.
- die Optionen so kurz wie möglich benannt werden. 1 bis 3 Wörter sollten
  ausreichen.
- ein [Label](/04-components/content/label/overview) verwendet wird, falls die
  Optionen im Zusammenhang nicht verstanden werden. Falls kein Label verwendet
  wird, muss ein AriaLabel gesetzt werden.
- eine logische Reihenfolge gewählt wird. Häufig sollte die wichtigste Option an
  erster Stelle stehen.

## Verwendung

Verwende einen SegmentedControl, um ...

- eine platzsparende Einfachauswahlmöglichkeit anzubieten.
- einen Teil des Contents unterhalb zu ändern oder zu filtern. Ein
  SegmentedControl ist kein Ersatz für
  [Tabs](/04-components/navigation/tabs/overview), welche den gesamten Content
  einer Seite ändern.

## SegmentedControl vs. RadioGroup

Flow bietet verschiedene Auswahl-Components an. Das SegmentedControl und die
[RadioGroup](/04-components/form-controls/radio-group/overview) haben viele
Gemeinsamkeiten. Die Cards unter dem Text geben hilfreiche Anhaltspunkte zur
Unterscheidung.

  

**ℹ️ Info**

- eine Einfachauswahl von 2 bis 5 Optionen anzuzeigen. - Optionen anzuzeigen,
    die aus 1 bis 2 Wörtern bestehen.
    - Optionen anzuzeigen, die den darunterliegenden Inhalt verändern.

  

**ℹ️ Info**

- eine Einfachauswahl von 2 bis ungefähr 7 Optionen anzuzeigen.
    - Optionen mit unterschiedlicher Textlänge darzustellen.

---

# Anwendung

## Position

Das SegmentedControl wird häufig in Formularen oder als Ansichtsfilter
verwendet. Bei der Positionierung ist Folgendes zu beachten:

- In der Regel nimmt das SegmentedControl die gesamte Breite des
  Content-Bereiches ein. In Ausnahmefällen kann das SegmentedControl auch
  schmaler dargestellt werden, z. B. wenn es nicht in Formularen, sondern nur
  als Ansichtsfilter über Diagrammen verwendet wird.
- Wenn Inhalte mithilfe des SegmentedControls geändert werden sollen, muss das
  SegmentedControl direkt über dem zu ändernden Inhaltsbereich stehen.

**✅ Do**

Das SegmentedControl steht über dem angepassten Inhalt.

```tsx
import {
  ColumnLayout,
  FieldDescription,
  Label,
  Section,
  Segment,
  SegmentedControl,
  TextField,
} from "@mittwald/flow-react-components";
import { useState } from "react";

export default () => {
  const [showContent, setShowContent] =
    useState<boolean>(false);

  return (
    <Section>
      <SegmentedControl
        defaultValue="lastschrift"
        onChange={() => setShowContent(!showContent)}
      >
        <Label>Zahlungsart</Label>
        <Segment value="lastschrift">Lastschrift</Segment>
        <Segment value="Rechnung">Rechnung</Segment>
        {showContent && (
          <FieldDescription>
            Wähle bitte eine Bankverbindung für die
            Bezahlung mit SEPA-Lastschrift aus.
          </FieldDescription>
        )}
      </SegmentedControl>
      {showContent && (
        <ColumnLayout>
          <TextField isRequired>
            <Label>Kontoinhaber</Label>
          </TextField>
          <TextField isRequired>
            <Label>IBAN</Label>
          </TextField>
        </ColumnLayout>
      )}
    </Section>
  );
}
```

## Hierarchie

Ein SegmentedControl darf maximal 5 Optionen enthalten. Bei der Reihenfolge der
Optionen ist Folgendes zu beachten:

- Die wichtigste Option sollte an erster Stelle stehen und wird automatisch
  vorselektiert.
- Nicht nur das gesamte SegmentedControl kann disabled sein, sondern auch
  einzelne Optionen. Wenn eine Option **disabled** ist, sollte es von der
  Ausgangssituation für den User logisch sein, warum er diese Option nicht
  auswählen kann.

## Feedback

Der selektierte Zustand einer Option ist an der geänderten Farbe und dem
Häkchensymbol zu erkennen. Aus diesem Grund sollte das Icon nicht verändert
werden. In der aktuellen Version des SegmentedControls sind keine dekorativen
oder alleinstehenden Icons vorgesehen.

---

# Writing guidelines

## Text

Das SegmentedControl hat Text in den Optionen und im
[Label](/04-components/content/label/overview). Dabei ist Folgendes zu beachten:

- Wenn ein Label verwendet wird, sollte es so kurz wie möglich ausformuliert
  sein. Der Text des Labels sollte nie mehr als 1-3 Wörter umfassen. Ein Label
  kann ausgelassen werden, wenn die Optionen auch ohne Label ausreichend
  verständlich sind.
- Im besten Fall sollte eine Option aus einem selbsterklärenden Wort bestehen.
  Wenn nötig, können auch 2-3 Wörter verwendet werden. Es ist jedoch darauf zu
  achten, dass der Text nicht zweizeilig wird und zusammen mit dem Icon genügend
  Platz zur Verfügung steht.

  

**✅ Do**

Das Label und die Antwortmöglichkeiten sind prägnant und verständlich.

```tsx
import {
  Label,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<SegmentedControl defaultValue="individuell">
  <Label>Einstellung</Label>
  <Segment value="standard">Standard</Segment>
  <Segment value="individuell">Individuell</Segment>
</SegmentedControl>
```

  

**⛔️ Don't**

Kürzer ist oft besser. Versuche, so wenig Wörter wie möglich im Label und in
    den Optionen zu verwenden.

```tsx
import {
  Label,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<SegmentedControl defaultValue="individuell">
  <Label>Wähle deine Einstellungsart aus:</Label>
  <Segment value="standard">Standard-Einstellungen</Segment>
  <Segment value="individuell">
    Individuell einstellbare Einstellungen
  </Segment>
</SegmentedControl>
```

---

# Behavior

## Responsive layout

Sobald die Breite des Containers kleiner als 551px ist, werden alle Optionen im
SegmentedControl untereinander dargestellt. Dieser Wert kann mit
`containerBreakpointSize` angepasst werden. Auch bei schmaleren Containerns
sollte vermieden werden, dass der Text zweizeilig wird oder nicht mehr lesbar
ist.

**ℹ️ Info**

```tsx
import {
  Label,
  Segment,
  SegmentedControl,
} from "@mittwald/flow-react-components";

<SegmentedControl defaultValue="ssh-key">
  <Label>Authentifizierungsart</Label>
  <Segment value="ssh-key">SSH-Key</Segment>
  <Segment value="passwort">Passwort</Segment>
</SegmentedControl>
```

---

# Accessibility

Falls SegmentedControl ohne [Label](/04-components/content/label/overview)
verwendet wird, muss ein AriaLabel gepflegt werden.

