# List

Die List bildet einen strukturierten Rahmen für mehrere ListItems und bietet Funktionen wie Sortierung, Filter und Suche.

## Overview

# Playground

Mit `typedList<T>` lässt sich eine List für einen bestimmten Datentyp erzeugen.

Im Tab Develop stehen verschiedene Anleitungen zum technischen Aufbau bereit.

```tsx
import {
  ActionGroup,
  AlertBadge,
  Avatar,
  Button,
  ContextMenu,
  Heading,
  IconDomain,
  IconSubdomain,
  MenuItem,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={4}
      aria-label="Domains"
      defaultViewMode="list"
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />
      <ActionGroup>
        <Button color="accent">Anlegen</Button>
      </ActionGroup>
      <DomainList.Search />
      <DomainList.Filter
        property="type"
        mode="some"
        name="Typ"
      />
      <DomainList.Sorting
        property="hostname"
        name="Alphabetisch"
        direction="asc"
        directionName="aufsteigend"
        defaultEnabled
      />
      <DomainList.Sorting
        property="hostname"
        name="Alphabetisch"
        direction="desc"
        directionName="absteigend"
      />
      <DomainList.Table>
        <DomainList.TableHeader>
          <DomainList.TableColumn>
            Name
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Type
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            TLD
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Hostname
          </DomainList.TableColumn>
        </DomainList.TableHeader>

        <DomainList.TableBody>
          <DomainList.TableRow>
            <DomainList.TableCell>
              {(domain) => domain.domain}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.type}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.tld}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.hostname}
            </DomainList.TableCell>
          </DomainList.TableRow>
        </DomainList.TableBody>
      </DomainList.Table>
      <DomainList.Item
        textValue={(domain) => domain.domain}
        showTiles
      >
        {(domain) => (
          <DomainList.ItemView>
            <Avatar
              color={
                domain.type === "Domain" ? "blue" : "teal"
              }
            >
              {domain.type === "Domain" ? (
                <IconDomain />
              ) : (
                <IconSubdomain />
              )}
            </Avatar>
            <Heading>
              {domain.hostname}
              {!domain.verified && (
                <AlertBadge status="warning">
                  Unverifiziert
                </AlertBadge>
              )}
            </Heading>
            <Text>{domain.type}</Text>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
              <MenuItem>Löschen</MenuItem>
            </ContextMenu>
          </DomainList.ItemView>
        )}
      </DomainList.Item>
    </DomainList.List>
  );
}
```

---

# Ansichten

Die Liste unterstützt die Ansichten **Liste, Raster** und **Tabelle**. Wird mehr
als eine Ansicht verwendet, kann über ein Menü zwischen den Ansichten gewechselt
werden. Die Default-Ansicht wird über das Property `defaultViewMode` festgelegt.

## Listenansicht

Nutze `<List.Item />`, um die List in der Listenansicht darzustellen.

```tsx
import {
  AlertBadge,
  Avatar,
  ContextMenu,
  Heading,
  IconDomain,
  IconSubdomain,
  MenuItem,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={4}
      aria-label="Domains"
      hidePagination
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />

      <DomainList.Item
        textValue={(domain) => domain.domain}
      >
        {(domain) => (
          <DomainList.ItemView>
            <Avatar
              color={
                domain.type === "Domain" ? "blue" : "teal"
              }
            >
              {domain.type === "Domain" ? (
                <IconDomain />
              ) : (
                <IconSubdomain />
              )}
            </Avatar>
            <Heading>
              {domain.hostname}
              {!domain.verified && (
                <AlertBadge status="warning">
                  Unverifiziert
                </AlertBadge>
              )}
            </Heading>
            <Text>{domain.type}</Text>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
              <MenuItem>Löschen</MenuItem>
            </ContextMenu>
          </DomainList.ItemView>
        )}
      </DomainList.Item>
    </DomainList.List>
  );
}
```

## Rasteransicht

Für die Rasteransicht wird ebenfalls das `<List.Item />` verwendet. Nutze
`showTiles`, um diese Ansicht zu aktivieren. Die Listenansicht kann deaktiviert
werden, indem `showList` auf `false` gesetzt wird.

Über das `maxTileWidth`-Property lässt sich die maximale Breite der Kacheln
steuern.

```tsx
import {
  AlertBadge,
  Avatar,
  ContextMenu,
  Heading,
  IconDomain,
  IconSubdomain,
  MenuItem,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={4}
      aria-label="Domains"
      hidePagination
      defaultViewMode="tiles"
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />
      <DomainList.Item
        textValue={(domain) => domain.domain}
        showTiles
        showList={false}
      >
        {(domain) => (
          <DomainList.ItemView>
            <Avatar
              color={
                domain.type === "Domain" ? "blue" : "teal"
              }
            >
              {domain.type === "Domain" ? (
                <IconDomain />
              ) : (
                <IconSubdomain />
              )}
            </Avatar>
            <Heading>
              {domain.hostname}
              {!domain.verified && (
                <AlertBadge status="warning">
                  Unverifiziert
                </AlertBadge>
              )}
            </Heading>
            <Text>{domain.type}</Text>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
              <MenuItem>Löschen</MenuItem>
            </ContextMenu>
          </DomainList.ItemView>
        )}
      </DomainList.Item>
    </DomainList.List>
  );
}
```

## Tabellenansicht

Nutze `<List.Table />`, um die List als
[Table](/04-components/structure/table/overview) darzustellen.

```tsx
import { typedList } from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={4}
      defaultViewMode="table"
      aria-label="Domains"
      hidePagination
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />
      <DomainList.Table>
        <DomainList.TableHeader>
          <DomainList.TableColumn>
            Name
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Type
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            TLD
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Hostname
          </DomainList.TableColumn>
        </DomainList.TableHeader>

        <DomainList.TableBody>
          <DomainList.TableRow>
            <DomainList.TableCell>
              {(domain) => domain.domain}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.type}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.tld}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.hostname}
            </DomainList.TableCell>
          </DomainList.TableRow>
        </DomainList.TableBody>
      </DomainList.Table>
    </DomainList.List>
  );
}
```

---

# ListItems

In einer List lassen sich ListItems mit unterschiedlichen Interaktions- und
Aufbaumöglichkeiten einsetzen.

## Mit Link

Ein ListItem bietet das Property `href` an, um das Element zu verlinken.

```tsx
import {
  Avatar,
  ContextMenu,
  Heading,
  IconDomain,
  MenuItem,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const List = typedList<Domain>();

  return (
    <List.List
      batchSize={2}
      hidePagination
      aria-label="Domains"
      getItemId={(domain) => domain.id}
    >
      <List.StaticData data={domains} />
      <List.Item
        href={() => "#"}
        textValue={(domain) => domain.domain}
      >
        {(domain) => (
          <List.ItemView>
            <Avatar>
              <IconDomain />
            </Avatar>
            <Heading>{domain.hostname}</Heading>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
            </ContextMenu>
          </List.ItemView>
        )}
      </List.Item>
    </List.List>
  );
}
```

## Mit Accordion

Das Accordion-Verhalten wird über die `accordion`-Property aktiviert. Dadurch
lässt sich ein ListItem per Klick ein- oder ausklappen. Der erweiterte Inhalt
wird in `<Content slot="bottom" />` platziert.

```tsx
import {
  Avatar,
  Content,
  Heading,
  IconDomain,
  ListItemView,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const List = typedList<Domain>();

  return (
    <List.List
      batchSize={2}
      hidePagination
      accordion
      aria-label="Domains"
      getItemId={(domain) => domain.id}
    >
      <List.StaticData data={domains} />
      <List.Item textValue={(domain) => domain.domain}>
        {(domain) => (
          <ListItemView>
            <Avatar>
              <IconDomain />
            </Avatar>
            <Heading>{domain.hostname}</Heading>
            <Text>{domain.type}</Text>
            <Content slot="bottom">Mehr Inhalt</Content>
          </ListItemView>
        )}
      </List.Item>
    </List.List>
  );
}
```

## Mit Checkboxen

[Checkboxen](/04-components/form-controls/checkbox/overview) in einem ListItem
werden automatisch am Anfang der Zeile angeordnet. Die Funktionalität der
Checkbox wird nicht von der List gesteuert und muss individuell implementiert
werden. Achte bei der Implementierung jedoch darauf, dass die gesamte Zeile zur
Auswahl des Elements genutzt werden kann. Nutze dafür `onAction` der List.

```tsx
import {
  Avatar,
  Checkbox,
  Heading,
  IconDomain,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";
import { useState } from "react";

export default () => {
  const List = typedList<Domain>();

  const [selectedDomains, setSelectedDomains] = useState<
    Domain[]
  >([]);

  const onSelected = (
    domain: Domain,
    selected: boolean,
  ) => {
    if (selected) {
      setSelectedDomains((prev) => [...prev, domain]);
    } else {
      setSelectedDomains((prev) =>
        prev.filter((d) => d.id !== domain.id),
      );
    }
  };

  const isSelected = (domain: Domain) => {
    return (
      selectedDomains.find((d) => d.id === domain.id) !==
      undefined
    );
  };

  return (
    <List.List
      hidePagination
      batchSize={2}
      aria-label="Domains"
      onAction={(domain) => {
        onSelected(domain, !isSelected(domain));
      }}
      getItemId={(domain) => domain.id}
    >
      <List.StaticData data={domains} />
      <List.Item
        showTiles
        textValue={(domain) => domain.hostname}
      >
        {(domain) => (
          <List.ItemView>
            <Checkbox
              isSelected={isSelected(domain)}
              onChange={(value) =>
                onSelected(domain, value)
              }
              aria-label={`${domain.hostname} auswählen`}
            />
            <Avatar>
              <IconDomain />
            </Avatar>
            <Heading>{domain.hostname}</Heading>
            <Text>{domain.type}</Text>
          </List.ItemView>
        )}
      </List.Item>

      <List.Table>
        <List.TableHeader>
          <List.TableColumn>
            <Checkbox
              aria-label="Alle auswählen"
              onChange={(v) =>
                setSelectedDomains(v ? domains : [])
              }
            />
          </List.TableColumn>
          <List.TableColumn>Domain</List.TableColumn>
        </List.TableHeader>
        <List.TableBody>
          <List.TableRow>
            <List.TableCell>
              {(domain) => (
                <Checkbox
                  isSelected={isSelected(domain)}
                  onChange={(value) =>
                    onSelected(domain, value)
                  }
                  aria-label={`${domain.hostname} auswählen`}
                />
              )}
            </List.TableCell>
            <List.TableCell>
              {(domain) => domain.hostname}
            </List.TableCell>
          </List.TableRow>
        </List.TableBody>
      </List.Table>
    </List.List>
  );
}
```

## Mit Content Slots

In einem ListItem kann zusätzlicher `<Content/ >` (Top und Bottom Content)
platziert werden. Die Position wird über das `slot`-Property gesteuert.

```tsx
import {
  Avatar,
  Content,
  ContextMenu,
  Heading,
  IconDomain,
  MenuItem,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const List = typedList<Domain>();

  return (
    <List.List
      batchSize={2}
      hidePagination
      aria-label="Domains"
      getItemId={(domain) => domain.id}
    >
      <List.StaticData data={domains} />
      <List.Item
        showTiles
        textValue={(domain) => domain.domain}
      >
        {(domain) => (
          <List.ItemView>
            <Avatar>
              <IconDomain />
            </Avatar>
            <Heading>{domain.hostname}</Heading>

            <Content slot="top">Top Content</Content>
            <Content slot="bottom">Bottom Content</Content>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
            </ContextMenu>
          </List.ItemView>
        )}
      </List.Item>
    </List.List>
  );
}
```

## Mit ColumnLayout

Dem ListItem können die
[ColumnLayout](/04-components/structure/column-layout/overview) Properties `s`,
`m` und `l` mitgegeben werden, um das Seitenverhältnis sowie das
Umbruchverhalten von Header und Content zu steuern.

```tsx
import {
  Avatar,
  Content,
  ContextMenu,
  Heading,
  IconEmail,
  Label,
  MenuItem,
  ProgressBar,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const List = typedList<{ mail: string }>();

  return (
    <List.List
      batchSize={2}
      aria-label="E-Mail-Adressen"
      hidePagination
    >
      <List.StaticData
        data={[
          { mail: "john@doe.com" },
          { mail: "max@mustermann.de" },
        ]}
      />
      <List.Item textValue={(mail) => mail.mail}>
        {(mail) => (
          <List.ItemView l={[3, 1]} m={[2, 1]} s={[1]}>
            <Avatar>
              <IconEmail />
            </Avatar>
            <Heading>{mail.mail}</Heading>

            <Content>
              <ProgressBar size="s" value={50}>
                <Label>Speicherplatz</Label>
              </ProgressBar>
            </Content>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
            </ContextMenu>
          </List.ItemView>
        )}
      </List.Item>
    </List.List>
  );
}
```

Da für die ColumnLayout-Spalten auch `null` gesetzt werden kann, ist es möglich,
nicht zwingend benötigten Content in kleineren Ansichten auszublenden. In diesem
Fall werden auch die entsprechenden Content Slots nicht angezeigt.

```tsx
import {
  Avatar,
  Content,
  ContextMenu,
  Heading,
  IconEmail,
  Label,
  MenuItem,
  ProgressBar,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const List = typedList<{ mail: string }>();

  return (
    <div style={{ width: 400 }}>
      <List.List
        batchSize={2}
        aria-label="E-Mail-Adressen"
        hidePagination
      >
        <List.StaticData
          data={[
            { mail: "john@doe.com" },
            { mail: "max@mustermann.de" },
          ]}
        />
        <List.Item textValue={(mail) => mail.mail}>
          {(mail) => (
            <List.ItemView
              l={[3, 1]}
              m={[2, 1]}
              s={[1, null]}
            >
              <Avatar>
                <IconEmail />
              </Avatar>
              <Heading>{mail.mail}</Heading>

              <Content>
                <ProgressBar size="s" value={50}>
                  <Label>Speicherplatz</Label>
                </ProgressBar>
              </Content>

              <ContextMenu>
                <MenuItem>Details anzeigen</MenuItem>
              </ContextMenu>
            </List.ItemView>
          )}
        </List.Item>
      </List.List>
    </div>
  );
}
```

## Mit ActionGroup

Verwende eine ActionGroup innerhalb des `<Content />`, um
[Buttons](/04-components/actions/button/overview) in der List zu platzieren.

```tsx
import {
  ActionGroup,
  Avatar,
  Button,
  Content,
  Heading,
  IconDomain,
  typedList,
  IconEdit,
  IconDelete,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const List = typedList<Domain>();

  return (
    <List.List
      batchSize={2}
      hidePagination
      aria-label="Domains"
      getItemId={(domain) => domain.id}
    >
      <List.StaticData data={domains} />
      <List.Item textValue={(domain) => domain.domain}>
        {(domain) => (
          <List.ItemView>
            <Avatar>
              <IconDomain />
            </Avatar>
            <Heading>{domain.hostname}</Heading>

            <Content>
              <ActionGroup>
                <Button
                  aria-label="Bearbeiten"
                  variant="plain"
                  color="secondary"
                >
                  <IconEdit />
                </Button>
                <Button
                  aria-label="Löschen"
                  variant="plain"
                  color="secondary"
                >
                  <IconDelete />
                </Button>
              </ActionGroup>
            </Content>
          </List.ItemView>
        )}
      </List.Item>
    </List.List>
  );
}
```

---

# Einstellmöglichkeiten

Die List bietet **Sortierung, Filter, Suche** und **Pagination** an.
Detaillierte Anleitungen zu den einzelnen Einstellmöglichkeiten stehen unter dem
Develop-Tab der List zur Verfügung.

```tsx
import {
  ActionGroup,
  AlertBadge,
  Avatar,
  Button,
  ContextMenu,
  Heading,
  IconDomain,
  IconSubdomain,
  MenuItem,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={4}
      aria-label="Domains"
      defaultViewMode="list"
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />
      <ActionGroup>
        <Button color="accent">Anlegen</Button>
      </ActionGroup>
      <DomainList.Search />
      <DomainList.Filter
        property="type"
        mode="some"
        name="Typ"
      />
      <DomainList.Filter
        property="tld"
        mode="some"
        name="TLD"
        priority="secondary"
      />
      <DomainList.Sorting
        property="hostname"
        name="Alphabetisch"
        direction="asc"
        defaultEnabled
        directionName="aufsteigend"
      />
      <DomainList.Sorting
        property="hostname"
        name="Alphabetisch"
        direction="desc"
        directionName="absteigend"
      />
      <DomainList.Table>
        <DomainList.TableHeader>
          <DomainList.TableColumn>
            Name
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Type
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            TLD
          </DomainList.TableColumn>
          <DomainList.TableColumn>
            Hostname
          </DomainList.TableColumn>
        </DomainList.TableHeader>

        <DomainList.TableBody>
          <DomainList.TableRow>
            <DomainList.TableCell>
              {(domain) => domain.domain}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.type}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.tld}
            </DomainList.TableCell>
            <DomainList.TableCell>
              {(domain) => domain.hostname}
            </DomainList.TableCell>
          </DomainList.TableRow>
        </DomainList.TableBody>
      </DomainList.Table>
      <DomainList.Item
        textValue={(domain) => domain.domain}
        showTiles
      >
        {(domain) => (
          <DomainList.ItemView>
            <Avatar
              color={
                domain.type === "Domain" ? "blue" : "teal"
              }
            >
              {domain.type === "Domain" ? (
                <IconDomain />
              ) : (
                <IconSubdomain />
              )}
            </Avatar>
            <Heading>
              {domain.hostname}
              {!domain.verified && (
                <AlertBadge status="warning">
                  Unverifiziert
                </AlertBadge>
              )}
            </Heading>
            <Text>{domain.type}</Text>

            <ContextMenu>
              <MenuItem>Details anzeigen</MenuItem>
              <MenuItem>Löschen</MenuItem>
            </ContextMenu>
          </DomainList.ItemView>
        )}
      </DomainList.Item>
    </DomainList.List>
  );
}
```

## Sortierung

Nutze `<List.Sorting />` innerhalb der List, um eine Sortiermethode anzulegen.

## Filter

Über `<List.Filter />` lassen sich Filtermöglichkeiten für die List anlegen.
Über `priority` kann eingestellt werden, ob Filter immer sichtbar sein sollen
(primary) oder nur in einem "Alle Filter" Modal angezeigt werden (secondary).
"Alle Filter" wird automatisch angezeigt, sobald es secondary Filter gibt.

### Date Range Filter

Mit dem `mode="dateRange"` kannst du einen Filter definieren, der es ermöglicht,
einen Zeitraum auszuwählen. So lassen sich Einträge gezielt zwischen einem
Start- und Enddatum eingrenzen.

```tsx
import { typedList } from "@mittwald/flow-react-components";
import {
  type CalendarDate,
  getLocalTimeZone,
  today,
} from "@internationalized/date";

export default () => {
  const InvoiceList = typedList<{
    id: string;
    date: CalendarDate;
  }>();

  return (
    <InvoiceList.List
      aria-label="Rechnungen"
      defaultViewMode="table"
      getItemId={(domain) => domain.id}
    >
      <InvoiceList.StaticData
        data={[
          {
            id: "RG100000",
            date: today(getLocalTimeZone()),
          },
          {
            id: "RG100001",
            date: today(getLocalTimeZone()).subtract({
              days: 7,
            }),
          },
          {
            id: "RG100002",
            date: today(getLocalTimeZone()).subtract({
              days: 14,
            }),
          },
        ]}
      />
      <InvoiceList.Filter
        property="date"
        mode="dateRange"
        name="Datum"
        dateRangeOptions={{
          maxValue: today(getLocalTimeZone()),
        }}
      />
      <InvoiceList.Table>
        <InvoiceList.TableHeader>
          <InvoiceList.TableColumn>
            Rechnung
          </InvoiceList.TableColumn>
          <InvoiceList.TableColumn>
            Datum
          </InvoiceList.TableColumn>
        </InvoiceList.TableHeader>

        <InvoiceList.TableBody>
          <InvoiceList.TableRow>
            <InvoiceList.TableCell>
              {(invoice) => invoice.id}
            </InvoiceList.TableCell>
            <InvoiceList.TableCell>
              {(invoice) =>
                `${invoice.date.day}.${invoice.date.month}.${invoice.date.year}`
              }
            </InvoiceList.TableCell>
          </InvoiceList.TableRow>
        </InvoiceList.TableBody>
      </InvoiceList.Table>
    </InvoiceList.List>
  );
}
```

## Suche

Verwende `<List.Search />` innerhalb der List, um ein
[SearchField](/04-components/form-controls/search-field/overview) anzuzeigen.
Standardmäßig wird die Suche automatisch gestartet. Soll die Suche nur beim
Drücken auf Enter ausgelöst werden, kann das Property `autoSubmit` auf `false`
gesetzt werden.

## Pagination

Die Pagination ist standardmäßig bei jeder List aktiviert, kann jedoch über
`hidePagination` deaktiviert werden. Über die `batchSize`-Property kann
festgelegt werden, wie viele Einträge gleichzeitig angezeigt werden sollen.

## Empty View

Mit `emptyView` kann eine benutzerdefinierte Ansicht angezeigt werden, wenn die
Liste keine Einträge enthält. So lässt sich beispielsweise eine Illustration mit
einem Hinweistext anzeigen, um die Nutzer zu informieren, dass keine Daten
vorhanden sind, und ihnen gegebenenfalls Tipps zu geben, wie sie Daten
hinzufügen können.

In der Regel sollte als Empty View eine IllustratedMessage verwendet werden, um
eine konsistente Nutzererfahrung zu gewährleisten.

```tsx
import {
  Heading,
  IconDomain,
  IllustratedMessage,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import { type Domain } from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const List = typedList<Domain>();

  const emptyView = (
    <IllustratedMessage>
      <IconDomain />
      <Heading>Keine Domains gefunden</Heading>
      <Text>Füge neue Domains hinzu, um zu starten.</Text>
    </IllustratedMessage>
  );

  return (
    <List.List aria-label="Domains" emptyView={emptyView}>
      <List.StaticData data={[]} />
      <List.Item>{() => null}</List.Item>
    </List.List>
  );
}
```

---

# Kombiniere mit ...

## ActionGroup

Verwende `<ActionGroup />` innerhalb der List, um eine
[ActionGroup](/04-components/actions/action-group/overview) anzuzeigen. Hier
können eine oder mehrere Aktionen definiert werden, die sich direkt auf die
Liste beziehen.

```tsx
import {
  ActionGroup,
  Avatar,
  Button,
  Heading,
  IconDomain,
  Text,
  typedList,
} from "@mittwald/flow-react-components";
import {
  type Domain,
  domains,
} from "@/content/04-components/structure/list/examples/domainApi";

export default () => {
  const DomainList = typedList<Domain>();

  return (
    <DomainList.List
      batchSize={2}
      hidePagination
      aria-label="Domains"
      getItemId={(domain) => domain.id}
    >
      <DomainList.StaticData data={domains} />
      <ActionGroup>
        <Button color="accent">Anlegen</Button>
      </ActionGroup>
      <DomainList.Item
        textValue={(domain) => domain.domain}
      >
        {(domain) => (
          <DomainList.ItemView>
            <Avatar>
              <IconDomain />
            </Avatar>
            <Heading>{domain.hostname}</Heading>
            <Text>{domain.type}</Text>
          </DomainList.ItemView>
        )}
      </DomainList.Item>
    </DomainList.List>
  );
}
```

## Summary

Verwende eine `<ListSummary />`, um eine Zusammenfassung anzuzeigen,
beispielsweise die Gesamtsumme der Beträge. Über das `position`-Property wird
festgelegt, ob die Summary oberhalb oder unterhalb der List erscheint.

```tsx
import {
  Flex,
  Heading,
  ListItemView,
  ListSummary,
  Text,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const InvoiceList = typedList<{
    id: string;
    amount: string;
  }>();

  return (
    <InvoiceList.List
      batchSize={2}
      hidePagination
      aria-label="Rechnungen"
      getItemId={(invoice) => invoice.id}
    >
      <ListSummary position="bottom">
        <Flex justify="end">
          <Text>
            <strong>Gesamt: 37,00 €</strong>
          </Text>
        </Flex>
      </ListSummary>
      <InvoiceList.StaticData
        data={[
          {
            id: "Rechnung 1",
            amount: "25,00 €",
          },
          {
            id: "Rechnung 2",
            amount: "12,00 €",
          },
        ]}
      />

      <InvoiceList.Item textValue={(invoice) => invoice.id}>
        {(invoice) => (
          <ListItemView>
            <Heading>{invoice.id}</Heading>
            <Text>{invoice.amount}</Text>
          </ListItemView>
        )}
      </InvoiceList.Item>
    </InvoiceList.List>
  );
}
```

