HextaUI

Attachment

Datei- und Bildkarten für Uploads, mit Fortschritt, Zuständen, Aktionen, einem Trigger über die ganze Karte und Namen, die ihre Dateiendung behalten.

sales-dashboard.pdfPDF · 2.4 MB
import { IconFileText, IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

export function AttachmentDemo() {
  return (
    <Attachment>
      <AttachmentMedia>
        <IconFileText />
      </AttachmentMedia>
      <AttachmentContent>
        <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>
        <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>
      </AttachmentContent>
      <AttachmentActions>
        <AttachmentAction aria-label="Remove sales-dashboard.pdf">
          <IconX />
        </AttachmentAction>
      </AttachmentActions>
    </Attachment>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/attachment.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"
<Attachment>
  <AttachmentMedia>
    <IconFileText />
  </AttachmentMedia>
  <AttachmentContent>
    <AttachmentTitle>report.pdf</AttachmentTitle>
    <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>
  </AttachmentContent>
  <AttachmentActions>
    <AttachmentAction aria-label="Remove report.pdf">
      <IconX />
    </AttachmentAction>
  </AttachmentActions>
</Attachment>
Attachment
├── AttachmentMedia
├── AttachmentContent
│   ├── AttachmentTitle
│   └── AttachmentDescription
├── AttachmentActions
│   └── AttachmentAction
└── AttachmentTrigger

AttachmentGroup
└── Attachment

Bild

variant="image" auf <AttachmentMedia /> zeigt ein quadratisches Vorschaubild, das beim Laden schimmert. Setze orientation="vertical" für eine Kachel mit der Vorschau oben.

import { IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

export function AttachmentImage() {
  return (
    <div className="flex flex-wrap items-start justify-center gap-3">
      <Attachment>
        <AttachmentMedia variant="image">
          <img src="/preview/landscape.svg" alt="" />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>dolomites.png</AttachmentTitle>
          <AttachmentDescription>PNG · 1.1 MB</AttachmentDescription>
        </AttachmentContent>
      </Attachment>
      <Attachment orientation="vertical">
        <AttachmentMedia variant="image">
          <img src="/preview/landscape.svg" alt="" />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>dolomites.png</AttachmentTitle>
          <AttachmentDescription>PNG · 1.1 MB</AttachmentDescription>
        </AttachmentContent>
        <AttachmentActions>
          <AttachmentAction aria-label="Remove dolomites.png">
            <IconX />
          </AttachmentAction>
        </AttachmentActions>
      </Attachment>
      <Attachment orientation="vertical" state="uploading" progress={64}>
        <AttachmentMedia variant="image">
          <img src="/preview/landscape.svg" alt="" />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>summit.png</AttachmentTitle>
          <AttachmentDescription>Uploading… 64%</AttachmentDescription>
        </AttachmentContent>
      </Attachment>
    </div>
  )
}

Zustände

state deckt den gesamten Upload-Lebenszyklus ab. Inaktive Dateien erhalten einen gestrichelten Rahmen, Titel beim Hochladen und Verarbeiten schimmern, und bei Fehlern werden Media und Beschreibung rot.

import { IconFileText, IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
  type AttachmentState,
} from "@/components/ui/attachment"

const files: { state: AttachmentState; description: string }[] = [
  { state: "idle", description: "Waiting to upload" },
  { state: "uploading", description: "Uploading… 42%" },
  { state: "processing", description: "Processing" },
  { state: "error", description: "Upload failed · file too large" },
  { state: "done", description: "PDF · 2.4 MB" },
]

export function AttachmentStates() {
  return (
    <div className="flex flex-col gap-2">
      {files.map(({ state, description }) => (
        <Attachment
          key={state}
          state={state}
          progress={state === "uploading" ? 42 : undefined}
        >
          <AttachmentMedia>
            <IconFileText />
          </AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>{`${state}-report.pdf`}</AttachmentTitle>
            <AttachmentDescription>{description}</AttachmentDescription>
          </AttachmentContent>
          <AttachmentActions>
            <AttachmentAction aria-label={`Remove ${state}-report.pdf`}>
              <IconX />
            </AttachmentAction>
          </AttachmentActions>
        </Attachment>
      ))}
    </div>
  )
}

Upload-Ablauf

Übergib progress, solange state="uploading" gilt, um einen dünnen Fortschrittsbalken am unteren Rand zu zeichnen. Bildvorschauen bleiben abgedunkelt, bis der Upload abgeschlossen ist.

"use client"

import * as React from "react"
import { IconTable } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
  type AttachmentState,
} from "@/components/ui/attachment"
import { Button } from "@/components/ui/button"

const descriptions: Record<AttachmentState, string> = {
  idle: "Ready to upload",
  uploading: "Uploading…",
  processing: "Processing",
  error: "Upload failed · connection lost",
  done: "CSV · 840 KB",
}

export function AttachmentUploadFlow() {
  const [state, setState] = React.useState<AttachmentState>("idle")
  const [progress, setProgress] = React.useState(0)
  const timer = React.useRef<ReturnType<typeof setInterval>>(undefined)

  React.useEffect(() => () => clearInterval(timer.current), [])

  const start = (fail: boolean) => {
    clearInterval(timer.current)
    setState("uploading")
    setProgress(0)
    let value = 0
    timer.current = setInterval(() => {
      value += 12
      if (fail && value >= 60) {
        clearInterval(timer.current)
        setState("error")
        return
      }
      if (value >= 100) {
        clearInterval(timer.current)
        setProgress(100)
        setState("processing")
        setTimeout(() => setState("done"), 900)
        return
      }
      setProgress(value)
    }, 250)
  }

  return (
    <div className="flex flex-col items-start gap-3">
      <Attachment state={state} progress={progress}>
        <AttachmentMedia>
          <IconTable />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>customers-2026.csv</AttachmentTitle>
          <AttachmentDescription>
            {state === "uploading"
              ? `Uploading… ${progress}%`
              : descriptions[state]}
          </AttachmentDescription>
        </AttachmentContent>
      </Attachment>
      <div className="flex gap-2">
        <Button size="sm" onClick={() => start(false)}>
          Start upload
        </Button>
        <Button size="sm" variant="outline" onClick={() => start(true)}>
          Upload that fails
        </Button>
      </div>
    </div>
  )
}

Größen

size skaliert Padding, Media und Text gemeinsam und hält die inneren Ecken konzentrisch zur Karte.

import { IconFileText, IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

const sizes = ["default", "sm", "xs"] as const

export function AttachmentSizes() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-3">
      {sizes.map((size) => (
        <Attachment key={size} size={size}>
          <AttachmentMedia>
            <IconFileText />
          </AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>invoice.pdf</AttachmentTitle>
            <AttachmentDescription>{size}</AttachmentDescription>
          </AttachmentContent>
          <AttachmentActions>
            <AttachmentAction aria-label="Remove invoice.pdf">
              <IconX />
            </AttachmentAction>
          </AttachmentActions>
        </Attachment>
      ))}
    </div>
  )
}

Lange Namen

Lange Dateinamen werden vor der Endung gekürzt, sodass man weiterhin eine .pdf von einer .zip unterscheiden kann. Fahre über den Namen, um ihn vollständig zu lesen.

import { IconFileText } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

export function AttachmentLongNames() {
  return (
    <div className="flex w-full max-w-xs flex-col gap-2">
      <Attachment>
        <AttachmentMedia>
          <IconFileText />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>
            quarterly-sales-report-final-v3-approved-by-finance.pdf
          </AttachmentTitle>
          <AttachmentDescription>PDF · 5.2 MB</AttachmentDescription>
        </AttachmentContent>
      </Attachment>
      <Attachment>
        <AttachmentMedia>
          <IconFileText />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>
            a-file-without-any-extension-at-all-just-a-long-name
          </AttachmentTitle>
          <AttachmentDescription>File · 12 KB</AttachmentDescription>
        </AttachmentContent>
      </Attachment>
    </div>
  )
}

Gruppe

<AttachmentGroup /> ordnet Anhänge in einer Reihe an, die seitlich scrollt, mit ausblendenden Rändern und Elementen, die einrasten.

import { IconFileZip } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentContent,
  AttachmentDescription,
  AttachmentGroup,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

const files = Array.from(
  { length: 10 },
  (_, index) => `export-${index + 1}.zip`
)

export function AttachmentGroupDemo() {
  return (
    <AttachmentGroup className="w-full rounded-lg border">
      {files.map((name) => (
        <Attachment key={name}>
          <AttachmentMedia>
            <IconFileZip />
          </AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>{name}</AttachmentTitle>
            <AttachmentDescription>ZIP · 18 MB</AttachmentDescription>
          </AttachmentContent>
        </Attachment>
      ))}
    </AttachmentGroup>
  )
}

Trigger

<AttachmentTrigger /> macht die ganze Karte klickbar, zum Beispiel um eine Vorschau oder einen Link zu öffnen, während die Aktionen darauf unabhängig bleiben. Gib ihr ein aria-label.

import { IconDownload, IconFileZip, IconX } from "@tabler/icons-react"

import {
  AlertDialog,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
  AttachmentTrigger,
} from "@/components/ui/attachment"

export function AttachmentTriggerDemo() {
  return (
    <div className="flex flex-wrap justify-center gap-3">
      <AlertDialog>
        <Attachment>
          <AttachmentMedia>
            <IconFileZip />
          </AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>research-summary.zip</AttachmentTitle>
            <AttachmentDescription>ZIP · 4.8 MB</AttachmentDescription>
          </AttachmentContent>
          <AttachmentActions>
            <AttachmentAction aria-label="Remove research-summary.zip">
              <IconX />
            </AttachmentAction>
          </AttachmentActions>
          <AlertDialogTrigger
            render={
              <AttachmentTrigger aria-label="Preview research-summary.zip" />
            }
          />
        </Attachment>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>research-summary.zip</AlertDialogTitle>
            <AlertDialogDescription>
              12 files · 4.8 MB · Uploaded today
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Close</AlertDialogCancel>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
      <Attachment>
        <AttachmentMedia variant="image">
          <img src="/preview/landscape.svg" alt="" />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>workspace.png</AttachmentTitle>
          <AttachmentDescription>Opens in a new tab</AttachmentDescription>
        </AttachmentContent>
        <AttachmentActions>
          <AttachmentAction aria-label="Download workspace.png">
            <IconDownload />
          </AttachmentAction>
        </AttachmentActions>
        <AttachmentTrigger
          render={
            <a
              href="/preview/landscape.svg"
              target="_blank"
              rel="noreferrer"
              aria-label="Open workspace.png"
            />
          }
        />
      </Attachment>
    </div>
  )
}

Composer

Dateien, die nach dem Laden der Seite hinzukommen, poppen ein, und der Rest der Reihe rutscht sanft nach, wenn eine entfernt wird.

"use client"

import * as React from "react"
import { IconFileText, IconPlus, IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentGroup,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"
import { Button } from "@/components/ui/button"

export function AttachmentComposer() {
  const [files, setFiles] = React.useState([
    { id: 1, name: "brief.pdf" },
    { id: 2, name: "moodboard.fig" },
    { id: 3, name: "timeline.xlsx" },
  ])

  return (
    <div className="flex w-full flex-col gap-3">
      <AttachmentGroup className="rounded-lg border">
        {files.map((file) => (
          <Attachment key={file.id} size="sm">
            <AttachmentMedia>
              <IconFileText />
            </AttachmentMedia>
            <AttachmentContent>
              <AttachmentTitle>{file.name}</AttachmentTitle>
              <AttachmentDescription>Attached</AttachmentDescription>
            </AttachmentContent>
            <AttachmentActions>
              <AttachmentAction
                aria-label={`Remove ${file.name}`}
                onClick={() =>
                  setFiles((current) =>
                    current.filter((item) => item.id !== file.id)
                  )
                }
              >
                <IconX />
              </AttachmentAction>
            </AttachmentActions>
          </Attachment>
        ))}
      </AttachmentGroup>
      <Button
        size="sm"
        variant="outline"
        className="self-start"
        onClick={() =>
          setFiles((current) => [
            ...current,
            { id: Date.now(), name: `notes-${current.length + 1}.txt` },
          ])
        }
      >
        <IconPlus data-icon="inline-start" />
        Add file
      </Button>
    </div>
  )
}

Defektes Bild

Wenn eine Bildvorschau nicht lädt, fällt sie auf ein Datei-Icon zurück.

import {
  Attachment,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

export function AttachmentBrokenImage() {
  return (
    <Attachment>
      <AttachmentMedia variant="image">
        <img src="/preview/missing.png" alt="" />
      </AttachmentMedia>
      <AttachmentContent>
        <AttachmentTitle>missing.png</AttachmentTitle>
        <AttachmentDescription>Preview unavailable</AttachmentDescription>
      </AttachmentContent>
    </Attachment>
  )
}

Stresstest

Feindselige Namen in einer schmalen Spalte, alle 60 ms ein zufälliger Zustand und 200 Elemente in einer Gruppe. Nichts läuft über, und das Layout hält.

"use client"

import * as React from "react"
import { IconFileText, IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentGroup,
  AttachmentMedia,
  AttachmentTitle,
  type AttachmentState,
} from "@/components/ui/attachment"
import { Button } from "@/components/ui/button"

const names = [
  "x".repeat(300) + ".pdf",
  "archive.tar.gz",
  "🎉🎉🎉 party photo final FINAL (2).png",
  "<script>alert(1)</script>.html",
  "a".repeat(400),
  ".gitignore",
  "تقرير-المبيعات-الربعي-النهائي-للإدارة.pdf",
  "名前がとても長いファイルの例です.docx",
  "",
  "file.with.many.dots.in.its.name.json",
]

const states: AttachmentState[] = [
  "idle",
  "uploading",
  "processing",
  "error",
  "done",
]

const bulk = Array.from(
  { length: 200 },
  (_, index) => `bulk-export-${index + 1}.csv`
)

export function AttachmentStress() {
  const [tick, setTick] = React.useState(0)
  const [running, setRunning] = React.useState(false)

  React.useEffect(() => {
    if (!running) {
      return
    }
    const timer = setInterval(() => setTick((value) => value + 1), 60)
    return () => clearInterval(timer)
  }, [running])

  return (
    <div className="flex w-full flex-col items-start gap-3">
      <div className="flex w-56 flex-col gap-2 rounded-lg border border-dashed p-2">
        {names.map((name, index) => {
          const state = running
            ? states[(tick + index) % states.length]
            : "done"
          return (
            <Attachment
              key={index}
              state={state}
              progress={((tick * 37 + index * 13) % 160) - 30}
            >
              <AttachmentMedia>
                <IconFileText />
              </AttachmentMedia>
              <AttachmentContent>
                <AttachmentTitle>{name}</AttachmentTitle>
                <AttachmentDescription>
                  {state === "error"
                    ? "Upload failed because the server rejected this extremely long file name"
                    : state}
                </AttachmentDescription>
              </AttachmentContent>
              <AttachmentActions>
                <AttachmentAction aria-label={`Remove ${name || "file"}`}>
                  <IconX />
                </AttachmentAction>
              </AttachmentActions>
            </Attachment>
          )
        })}
      </div>
      <Button size="sm" variant="outline" onClick={() => setRunning(!running)}>
        {running ? "Stop chaos" : "Start chaos"}
      </Button>
      <AttachmentGroup className="w-full rounded-lg border">
        {bulk.map((name) => (
          <Attachment key={name} size="xs">
            <AttachmentMedia>
              <IconFileText />
            </AttachmentMedia>
            <AttachmentContent>
              <AttachmentTitle>{name}</AttachmentTitle>
            </AttachmentContent>
          </Attachment>
        ))}
      </AttachmentGroup>
    </div>
  )
}

Rechts nach links

Media, Inhalt und Aktionen werden gespiegelt, und der Fortschrittsbalken füllt sich von der Startkante aus.

import { IconFileText, IconX } from "@tabler/icons-react"

import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"

export function AttachmentRtl() {
  return (
    <div dir="rtl">
      <Attachment>
        <AttachmentMedia>
          <IconFileText />
        </AttachmentMedia>
        <AttachmentContent>
          <AttachmentTitle>تقرير-المبيعات-الربعي-النهائي.pdf</AttachmentTitle>
          <AttachmentDescription>PDF · 2.4 ميغابايت</AttachmentDescription>
        </AttachmentContent>
        <AttachmentActions>
          <AttachmentAction aria-label="إزالة الملف">
            <IconX />
          </AttachmentAction>
        </AttachmentActions>
      </Attachment>
    </div>
  )
}
TasteAktion
TabSetzt den Fokus auf jede Aktion und dann auf den Trigger. Verborgene Aktionen auf vertikalen Kacheln erscheinen, sobald der Fokus in die Karte wechselt.
EnterSpaceAktiviert die fokussierte Aktion oder den fokussierten Trigger.
  • Während des Uploads hat der Fortschrittsbalken role="progressbar" und wird nach dem Titel benannt.
  • String-Titel erhalten ein title-Attribut mit dem vollständigen Namen, sodass gekürzte Namen weiterhin lesbar sind.
  • Reine Icon-Aktionen und der Trigger haben keinen Text, gib ihnen deshalb immer ein aria-label, das die Datei benennt.
  • Bei reduzierter Bewegung erscheinen Anhänge ohne Einpoppen, der Fortschrittsbalken springt statt zu gleiten, und Gruppen ordnen sich sofort neu an.

Jeder Teil außer Group, Action und Trigger rendert ein einfaches Element und akzeptiert dessen Attribute. Die Kartenstile werden als attachmentVariants exportiert.

PropTypStandard
state
"idle" | "uploading" | "processing" | "error" | "done""done"
progress0 bis 100. Wird nur während des Hochladens angezeigt und auf diesen Bereich begrenzt.
number–
size
"default" | "sm" | "xs""default"
orientation
"horizontal" | "vertical""horizontal"
AttributBeschreibung
data-slot="attachment"Die Card in CSS ansprechen.
data-stateDer aktuelle Zustand.
data-sizeDie aktuelle Größe.
data-orientationDie aktuelle Orientierung.
data-slot="attachment-progress"Der Fortschrittsbalken, vorhanden während des Uploads.
--attachment-radiusDer Eckenradius der Karte. Die inneren Ecken werden davon abgeleitet.
PropTypStandard
variantimage umschließt sein Kind mit einem quadratischen AspectRatio und einem Datei-Icon als Fallback.
"icon" | "image""icon"
AttributBeschreibung
data-slot="attachment-media"Das Media in CSS ansprechen.
data-variantDie aktuelle Variante.

Eine <div>, die Titel und Beschreibung enthält und die verbleibende Breite einnimmt.

AttributBeschreibung
data-slot="attachment-content"Den Inhalt in CSS ansprechen.
PropTypStandard
childrenEin String wird so aufgeteilt, dass seine Dateiendung nie gekürzt wird.
ReactNode–
titleWird beim Hovern angezeigt. Standardmäßig der vollständige Name.
stringchildren
idWird erzeugt, wenn nicht angegeben. Der Fortschrittsbalken wird danach benannt.
string–
AttributBeschreibung
data-slot="attachment-title"Den Titel in CSS ansprechen.

Eine einzeilige <span> für Größe, Typ oder Status. Sie wird gekürzt, wenn der Platz ausgeht.

AttributBeschreibung
data-slot="attachment-description"Die Beschreibung in CSS ansprechen.

Eine <div> für die Aktions-Buttons. Bei vertikalen Kacheln schwebt sie über der Vorschau und erscheint auf Geräten mit Maus beim Hovern oder Fokussieren.

AttributBeschreibung
data-slot="attachment-actions"Die Aktionen in CSS ansprechen.

Eine <Button />, die über dem Trigger sitzt. Sie akzeptiert alle Button-Props.

PropTypStandard
variant
ButtonVariant"ghost"
size
ButtonSize"icon-xs"
AttributBeschreibung
data-slot="attachment-action"Actions in CSS ansprechen.

Eine unsichtbare Ebene, die die ganze Karte abdeckt. Kombiniere sie mit einem Dialog-Trigger, einem Link oder einem beliebigen Button.

PropTypStandard
render
ReactElement | (props, state) => ReactElement<button type="button">
AttributBeschreibung
data-slot="attachment-trigger"Den Trigger in CSS ansprechen.

Eine horizontale <ScrollArea />, die an jedem Anhang einrastet und die Reihe animiert, wenn Elemente hinzukommen oder entfernt werden. Sie akzeptiert alle ScrollArea-Props außer scrollbars.

AttributBeschreibung
data-slot="attachment-group"Die Gruppe in CSS ansprechen.
data-slot="attachment-group-list"Die Reihe, die die Anhänge enthält.

In Blocks verwendet

Blocks, die auf Attachment aufbauen.