Attachment
A file or image attached to a message — a download link once posted, or a removable chip while staged in a composer.
Usage#
import { Attachment } from "neelam-ui";
<Attachment name="release-notes.pdf" size="248 KB" url="/release-notes.pdf" />name is the visible filename, and doubles as the alt text for an image
attachment. size is a human-readable string like "2.4 MB" — formatting bytes
is left to you, since the right units and locale vary.
Posted or pending#
The component has two modes, and onRemove is what selects between them:
- Without
onRemove— an attachment already posted in a message. Given aurlit becomes a real link, and an image renders full-size and clickable. - With
onRemove— a file staged in a composer before sending. A remove button appears, and an image switches to a small captioned thumbnail, because a pending upload is not something to click through to yet.
Links and downloads#
Given a url, the clickable element is a real <a> inside the component, not
a <div> with a click handler. The outer element stays a plain <div> either
way, so the forwarded ref's type does not depend on whether a prop was passed.
For type="file" the anchor carries download; for type="image" it opens in
a new tab.
download is same-origin only
Browsers honour the download attribute only for same-origin URLs, or ones a
CORS response explicitly permits. Point it at a cross-origin CDN and the file
will open rather than save — serve it from your own origin, or set the
appropriate headers.
Keyboard#
| Key | Behaviour |
|---|---|
| Tab | Reaches the file link (when url is set), then the remove button (when onRemove is set). |
| Enter | Follows the link — downloading a file, or opening an image in a new tab. |
| EnterSpace | Activates the remove button. |
An attachment with neither url nor onRemove is static content and is not
focusable at all, which is correct — there is nothing to do with it.
Accessibility#
- The remove button always has a name:
removeLabel, defaulting to"Remove {name}", so a row of chips does not announce three identical "Remove" buttons. - The file icon is
aria-hidden— the filename beside it is the content. - An image attachment uses
nameas itsalt, so the file is identified rather than announced as an unlabelled image. - External links carry
rel="noopener noreferrer".
Say when a link leaves the page
An image attachment opens in a new tab. If that is surprising in context, add a visually hidden "(opens in a new tab)" to the name — an unexpected context switch is disorienting for screen reader and screen magnifier users alike.
API reference#
| Prop | Type | Default |
|---|---|---|
namerequiredThe file's name — shown in the file row, and used as the image's `alt` text for an image attachment. | string | — |
onRemoveShows a remove button and calls this when it's clicked. Meant for a file staged in a composer before it's sent, not one already posted in a message — supplying it also switches an image attachment from the full-size linked photo to a small captioned thumbnail, since a pending upload isn't something to click through to yet. | (() => void) | — |
removeLabelAccessible label for the remove button. Defaults to `"Remove {name}"`. | string | — |
sizeA human-readable size, e.g. `"2.4 MB"`. | string | — |
typeRenders an image preview instead of the generic file row. Defaults to `"file"`. | enum | file |
urlWhere the file lives. Given a `url`, the attachment becomes a real link: `target="_blank"` for an image, `download` for a file. Browsers only honor `download` for same-origin URLs (or ones a CORS response explicitly permits) — see `DECISIONS.md`. | string | — |
Also accepts every native <div> attribute except children — the content is
built from name, size, and type.