# Listen, Raster und Tabellen

Mithilfe der List können komplexe Listen-, Raster- und Tabellenansichten
erstellt werden. So erhalten User eine übersichtliche Darstellung verschiedener
Elemente auf einen Blick und können schnell zu einer Detailseite navigieren.
Außerdem verfügen alle drei Ansichtsarten über die Möglichkeit zu suchen, zu
filtern und zu sortieren.

---

# Zentrale Merkmale

Die zwei Components [List](/04-components/structure/list/overview) und
[Table](/04-components/structure/table/overview) dienen der kompakten Auflistung
von Elementen. In der Verwendung ist die List jedoch häufiger relevanter.

Die **Table** stellt eine einzelne, eigenständige Tabelle dar, die einer
klassischen Zeilen- und Spaltenstruktur folgt.

Die **List** besitzt einen komplexeren Aufbau. Sie umfasst drei
Ansichtsvarianten: **Listen-, Raster-** und **Tabellenansicht** – wobei die
Tabellenansicht die Component Table in die List integriert. Sofern technisch
eingefügt, können User selbst entscheiden, welche Ansicht angezeigt wird.
Darüber hinaus unterstützt die List mehrere Funktionen wie Sortierung, Filterung
und Suche, um die Inhalte interaktiv zu steuern.

Häufig sind Elemente in List und Table verlinkt, sodass User schnell zu weiteren
Seiten – wie z. B. [Detailseiten](/03-patterns/01-patterns/detail-page) –
navigieren können. Außerdem lassen sich häufige Aktionen direkt an den
jeweiligen Elementen ausführen.

## Listenansicht

Die Listenansicht ist besonders geeignet, wenn viele Elemente übersichtlich,
platzsparend und gleichzeitig ansprechend dargestellt werden sollen. Sie
repräsentiert häufig größere Elemente, bei denen es sich lohnt, einen
[Avatar](/04-components/content/avatar/overview) anzuzeigen.

Da die Listenansicht die wichtigsten Informationen kompakt vermittelt, wird sie
oft als Standardansicht einer List im mStudio verwendet. Um Usern weitere
Informationen bereitzustellen, führen die sogenannten **ListItems**
beispielsweise zu [Detailseiten](/03-patterns/01-patterns/detail-page).

```tsx
import {
  ActionGroup,
  Avatar,
  Button,
  Content,
  ContextMenu,
  ContextMenuTrigger,
  Flex,
  Heading,
  IconChevronDown,
  IconDomain,
  IconHome,
  IconInfo,
  IconMove,
  IconSubdomain,
  IconVhost,
  LayoutCard,
  ListItemView,
  MenuItem,
  Text,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const DomainList = typedList<{
    id: string;
    domain: string;
    type: string;
    target?: string;
  }>();

  return (
    <Flex direction="column" gap="m">
      <Heading level={1} color="dark">
        Domains
      </Heading>
      <LayoutCard>
        <DomainList.List
          aria-label="Domains"
          loadingItemsCount={3}
          getItemId={(domain) => domain.id}
        >
          <DomainList.StaticData
            data={[
              {
                id: "1",
                domain: "mjaofd6.project.space",
                type: "Projekt-Domain",
              },
              {
                id: "2",
                domain: "formel1.de",
                type: "Domain",
                target: "Mein WordPress",
              },
              {
                id: "3",
                domain: "schumi-fanclub.de",
                type: "Virtual Host",
                target: "Mein WordPress",
              },
            ]}
          />
          <DomainList.Search />
          <DomainList.Filter name="Typ" property="type" />
          <DomainList.Item
            textValue={(domain) => domain.domain}
          >
            {(domain) => (
              <ListItemView>
                <Avatar
                  color={
                    domain.type === "Projekt-Domain"
                      ? "violet"
                      : domain.type === "Subdomain"
                        ? "green"
                        : domain.type === "Domain"
                          ? "blue"
                          : "teal"
                  }
                >
                  {domain.type === "Projekt-Domain" ? (
                    <IconHome />
                  ) : domain.type === "Subdomain" ? (
                    <IconSubdomain />
                  ) : domain.type === "Domain" ? (
                    <IconDomain />
                  ) : (
                    <IconVhost />
                  )}
                </Avatar>
                <Heading>{domain.domain}</Heading>
                <Text>{domain.type}</Text>
                <Content>
                  <small>
                    {domain.target
                      ? domain.target
                      : "Kein Ziel"}
                  </small>
                </Content>
                <ContextMenu>
                  <MenuItem>
                    <IconInfo />
                    <Text>Details anzeigen</Text>
                  </MenuItem>
                </ContextMenu>
              </ListItemView>
            )}
          </DomainList.Item>
          <ActionGroup>
            <ContextMenuTrigger>
              <Button color="accent">
                <Text>Hinzufügen</Text>
                <IconChevronDown />
              </Button>
              <ContextMenu>
                <MenuItem>
                  <IconDomain />
                  <Text>Domain bestellen</Text>
                </MenuItem>
                <MenuItem>
                  <IconMove />
                  <Text>Domain umziehen</Text>
                </MenuItem>
                <MenuItem>
                  <IconVhost />
                  <Text>vHost einrichten</Text>
                </MenuItem>
                <MenuItem>
                  <IconSubdomain />
                  <Text>Subdomain anlegen</Text>
                </MenuItem>
              </ContextMenu>
            </ContextMenuTrigger>
          </ActionGroup>
        </DomainList.List>
      </LayoutCard>
    </Flex>
  );
}
```

## Rasteransicht

Die Rasteransicht ist sinnvoll, wenn die visuelle Darstellung im Vordergrund
steht. Das ListItem in Form einer **Tile** enthält hier bewusst nur wenige
Informationen, damit der [Avatar](/04-components/content/avatar/overview) bzw.
die visuelle Repräsentation des Elements ins Auge fällt. Sie eignet sich
besonders für Elemente, bei denen der User selbst ein Image hochgeladen hat oder
hochladen kann. Da nur wenige inhaltliche Informationen vermittelt werden,
eignet sich die Rasteransicht besonders gut zur Navigation.

```tsx
import {
  ActionGroup,
  Avatar,
  Button,
  Flex,
  Heading,
  Image,
  Initials,
  LayoutCard,
  ListItemView,
  Text,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const ProjectList = typedList<{
    id: string;
    description: string;
    avatar?: string;
  }>();

  return (
    <Flex direction="column" gap="m">
      <Heading level={1} color="dark">
        Projekte
      </Heading>
      <LayoutCard>
        <ProjectList.List
          aria-label="Projekte"
          loadingItemsCount={3}
          getItemId={(project) => project.id}
          defaultViewMode="tiles"
        >
          <ProjectList.StaticData
            data={[
              {
                id: "p-12345abc",
                description: "Green Portfolio",
                avatar:
                  "https://flow.mittwald.de/assets/mittwald_logo_rgb.jpg",
              },
              {
                id: "p-12345def",
                description: "Tante Mias Laden",
              },
              {
                id: "p-12345ghi",
                description: "User Experience Test",
              },
            ]}
          />
          <ProjectList.Search />
          <ProjectList.Sorting
            name="Alphabetisch"
            direction="asc"
            property="description"
            directionName="aufsteigend"
            defaultEnabled
          />
          <ProjectList.Sorting
            name="Alphabetisch"
            direction="desc"
            property="description"
            directionName="absteigend"
          />
          <ProjectList.Item
            textValue={(project) => project.description}
            showTiles
            showList={false}
          >
            {(project) => (
              <ListItemView>
                <Avatar>
                  {project.avatar ? (
                    <Image src={project.avatar} />
                  ) : (
                    <Initials>
                      {project.description}
                    </Initials>
                  )}
                </Avatar>
                <Heading>{project.description}</Heading>
                <Text>{project.id}</Text>
              </ListItemView>
            )}
          </ProjectList.Item>
          <ActionGroup>
            <Button color="accent">
              <Text>Anlegen</Text>
            </Button>
          </ActionGroup>
        </ProjectList.List>
      </LayoutCard>
    </Flex>
  );
}
```

## Tabellenansicht

Die Tabellenansicht eignet sich besonders für große Datenmengen, die schnell
erfasst werden müssen, während die optische Gestaltung eine untergeordnete Rolle
spielt. Die Spaltenüberschriften ermöglichen eine schnelle Zuordnung der
Informationen, wodurch die Darstellung eher technisch wirkt. Sie wird vor allem
für Detailinformationen genutzt, nicht für größere visuell hervorgehobene
Elemente. Die Tabellenansicht verwendet die Component
[Table](/04-components/structure/table/overview) als Bestandteil der
[List](/04-components/structure/list/overview).

```tsx
import {
  ActionGroup,
  AlertBadge,
  Button,
  Flex,
  Heading,
  LayoutCard,
  Text,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const TicketList = typedList<{
    id: string;
    description: string;
    createdBy: string;
    status: string;
  }>();

  return (
    <Flex direction="column" gap="m">
      <Heading level={1} color="dark">
        Tickets
      </Heading>
      <LayoutCard>
        <TicketList.List
          aria-label="Tickets"
          loadingItemsCount={3}
          getItemId={(ticket) => ticket.id}
          defaultViewMode="table"
        >
          <TicketList.StaticData
            data={[
              {
                id: "1",
                description:
                  "Meine Website läuft nicht mehr",
                createdBy: "Karl Heinz, 12.11.2025, 22:45",
                status: "Offen",
              },
              {
                id: "2",
                description: "Hilfe beim Aufbauen",
                createdBy:
                  "Isabell Meyer, 11.11.2025, 01:57",
                status: "Offen",
              },
              {
                id: "3",
                description: "Irgendwas stimmt nicht",
                createdBy: "Gerd Simon, 10.11.2025, 15:12",
                status: "Offen",
              },
            ]}
          />
          <TicketList.Search />
          <TicketList.Sorting
            name="Erstelldatum"
            defaultEnabled
            direction="asc"
            property="createdBy"
            directionName="älteste zuerst"
          />
          <TicketList.Sorting
            name="Erstelldatum"
            direction="desc"
            property="createdBy"
            directionName="neueste zuerst"
          />
          <TicketList.Filter
            name="Status"
            property="status"
          />
          <TicketList.Table>
            <TicketList.TableHeader>
              <TicketList.TableColumn>
                Betreff
              </TicketList.TableColumn>
              <TicketList.TableColumn>
                Erstellt
              </TicketList.TableColumn>
              <TicketList.TableColumn>
                Status
              </TicketList.TableColumn>
            </TicketList.TableHeader>
            <TicketList.TableBody>
              <TicketList.TableRow>
                <TicketList.TableCell>
                  {(ticket) => ticket.description}
                </TicketList.TableCell>
                <TicketList.TableCell>
                  {(ticket) => ticket.createdBy}
                </TicketList.TableCell>
                <TicketList.TableCell>
                  {(ticket) => (
                    <AlertBadge>{ticket.status}</AlertBadge>
                  )}
                </TicketList.TableCell>
              </TicketList.TableRow>
            </TicketList.TableBody>
          </TicketList.Table>
          <ActionGroup>
            <Button color="accent">
              <Text>Anlegen</Text>
            </Button>
          </ActionGroup>
        </TicketList.List>
      </LayoutCard>
    </Flex>
  );
}
```

---

# Position und Aufbau

[Lists](/04-components/structure/list/overview) und
[Tables](/04-components/structure/table/overview) werden vor allem für die
Aufzählung von Elementen verwendet – häufig als Einstiegsseiten zu
[Detailansichten](/03-patterns/01-patterns/detail-page).

Wenn die List oder Table auch ohne sichtbare
[Heading](/04-components/content/heading/overview) (z. B. durch den Seitentitel)
verständlich ist, muss eine versteckte Heading darüber platziert werden. In
diesem Fall sollte das passendes aria-label nicht vergessen werden.

Eine typische **List** ist wie folgt aufgebaut:

**Header:** Der Header kann
Steuerelemente/[Buttons](/04-components/actions/button/overview) für den Wechsel
der Ansicht (Listen-, Raster- oder Tabellenansicht) sowie für Sortierung und
Filterung beinhalten, wobei auch mehrere Filter gleichzeitig angewendet werden
können. Zusätzlich bietet der Header einen Action-Bereich, über den
beispielsweise Anlegeprozesse gestartet werden können.

**Body:** Im Body werden – abhängig von der ausgewählten Ansicht – entweder
**ListItems** (in der Listen- und Rasteransicht) oder die
[Table](/04-components/structure/table/overview)-Component angezeigt.

**Footer:** Unterhalb der Inhalte zeigt der Footer an, wie viele Einträge
bereits geladen sind und ermöglicht ein Nachladen weiterer Elemente. Um lange
Ladezeiten zu vermeiden, sollten nicht alle Einträge auf einmal geladen werden.
Standardmäßig empfiehlt sich eine Pagination mit 10 Elementen pro Seite.

## Suche, Filter, Sortierung und Actions

**Sortierung:** Die Sortierfunktion hilft, **ListItems** nach relevanten
Kriterien zu ordnen. Dabei sollte die Standardsortierung dem häufigsten
Anwendungsfall entsprechen, um den Einstieg zu erleichtern. Es sollten nur
Sortieroptionen angeboten werden, die dem User echten Mehrwert bringen. Außerdem
muss die aktive Sortierung klar erkennbar sein, damit die Orientierung jederzeit
einfach bleibt.

**Filter:** Filter ermöglichen es, Listenelemente gezielt einzugrenzen. Dabei
werden Mehrfachauswahlen als
[Checkboxen](/04-components/form-controls/checkbox/overview) und Einzeloptionen
als [RadioGroup](/04-components/form-controls/radio-group/overview) dargestellt.
Alle aktiven Filter werden als [Badges](/04-components/status/badge/overview)
angezeigt, sodass der User jederzeit sieht, welche Filter gerade aktiv sind.
Filter derselben Kategorie sollten logisch gruppiert werden, während Filter ohne
Kategorie unter einem gemeinsamen
Filter-[Button](/04-components/actions/button/overview) zusammengefasst werden.
Filter sind in der Regel UND-verknüpft.

**Suche:** Die Suchfunktion erleichtert das schnelle Auffinden einzelner
Elemente innerhalb der List. Sie ergänzt die Sortier- und Filterfunktionen, um
die Navigation besonders bei umfangreichen Listen effizient zu gestalten.

**Actions:** Die [List](/04-components/structure/list/overview) kann eine
[ActionGroup](/04-components/actions/action-group/overview) mit verschiedenen
[Buttons](/04-components/actions/button/overview) enthalten, die sich auf den
Inhalt der List beziehen. Insbesondere Anlegeprozesse (Pattern:
[Anlegen und Bearbeiten](/03-patterns/01-patterns/anlegeprozess)) sollten hier
über einen Button gestartet werden.

```tsx
import {
  ActionGroup,
  Avatar,
  Button,
  Flex,
  Heading,
  Initials,
  LayoutCard,
  ListItemView,
  Text,
  typedList,
} from "@mittwald/flow-react-components";

export default () => {
  const ServerList = typedList<{
    id: string;
    description: string;
    customer: string;
    projectCount: number;
  }>();

  return (
    <Flex direction="column" gap="m">
      <Heading level={1} color="dark">
        Server
      </Heading>
      <LayoutCard>
        <ServerList.List
          aria-label="Server"
          loadingItemsCount={3}
          getItemId={(server) => server.id}
          defaultViewMode="tiles"
        >
          <ServerList.StaticData
            data={[
              {
                id: "s-12345abc",
                description: "Blue Server",
                customer: "Meine Organisation",
                projectCount: 10,
              },
              {
                id: "s-12345def",
                description: "Hot Pink Server",
                customer: "Meine Organisation",
                projectCount: 2,
              },
              {
                id: "s-12345ghi",
                description: "Green Server",
                customer: "Andere Organisation",
                projectCount: 5,
              },
            ]}
          />
          <ServerList.Search />
          <ServerList.Sorting
            name="Alphabetisch"
            defaultEnabled
            direction="asc"
            property="description"
            directionName="aufsteigend"
          />
          <ServerList.Sorting
            name="Alphabetisch"
            direction="desc"
            property="description"
            directionName="absteigend"
          />
          <ServerList.Filter
            name="Filter"
            property="customer"
          />
          <ServerList.Item
            textValue={(server) => server.description}
            showTiles
          >
            {(server) => (
              <ListItemView>
                <Avatar
                  color={
                    server.description === "Blue Server"
                      ? "blue"
                      : server.description ===
                          "Hot Pink Server"
                        ? "lilac"
                        : "green"
                  }
                >
                  <Initials>{server.description}</Initials>
                </Avatar>
                <Heading>{server.description}</Heading>
                <Text>{server.id}</Text>
                <Text>{server.projectCount} Projekte</Text>
              </ListItemView>
            )}
          </ServerList.Item>
          <ActionGroup>
            <Button color="accent">
              <Text>Tarif bestellen</Text>
            </Button>
          </ActionGroup>
        </ServerList.List>
      </LayoutCard>
    </Flex>
  );
}
```

---

# Erstes Anlegen

Wenn eine [List](/04-components/structure/list/overview) oder
[Table](/04-components/structure/table/overview) noch keine Elemente enthält,
sollte dies durch eine
[IllustratedMessage](/04-components/content/illustrated-message/overview) oder
einen kurzen Hinweistext (wenn wenig Platz vorhanden) sichtbar gemacht werden.
Die Nachricht sollte erklären, warum aktuell keine Einträge vorhanden sind.
Außerdem sollte sie dem User aufzeigen, welche Inhalte angelegt werden können
oder welche Schritte er unternehmen kann. Zusätzlich sollte, wenn möglich, ein
[Button](/04-components/actions/button/overview) oder
[Link](/04-components/navigation/link/overview) angeboten werden, damit der User
beispielsweise den Anlege-Prozess starten kann.

```tsx
import {
  ActionGroup,
  Button,
  Flex,
  Heading,
  IconProject,
  IllustratedMessage,
  LayoutCard,
  Text,
} from "@mittwald/flow-react-components";

export default () => {
  return (
    <Flex direction="column" gap="m">
      <Heading level={1} color="dark">
        Projekte
      </Heading>
      <LayoutCard>
        <IllustratedMessage>
          <IconProject />
          <Heading>Erstes Projekt anlegen</Heading>
          <Text>
            Du hast noch keine Projekte angelegt! Lege ein
            Projekt an und schon kannst du mit dem
            Entwickeln loslegen.
          </Text>
          <ActionGroup>
            <Button color="accent">Anlegen</Button>
            <Button
              slot="secondary"
              color="secondary"
              variant="soft"
            >
              Tarif bestellen
            </Button>
          </ActionGroup>
        </IllustratedMessage>
      </LayoutCard>
    </Flex>
  );
}
```
