Attachment
Uploaded files, images, and folders with progress, retry, removal, and previews.
Usage
import { Attachment } from '@commenda/pathfinder/attachment';
<Attachment
item={{
id: 'filing-pdf',
filename: 'GST F5 return.pdf',
mediaType: 'application/pdf',
}}
onPreview={openPreview}
onRemove={removeAttachment}
onRetry={retryUpload}
/>;The application owns uploads, cancellation, retry, storage URLs, and viewer content. Callbacks receive the original item with its stable ID. Updating an item's upload value changes the pill without replacing its identity.
Upload lifecycle
Use upload: { status: 'uploading', progress: 42 } for measured progress, progress: null for indeterminate progress, and { status: 'failed', error: 'Connection lost' } for failure. Omit upload, or set its status to ready, after success. Failed uploads remain visible; clicking retries only when onRetry is supplied. Set the uploading state promptly when retry begins.
Sent attachments
Omit onRemove after sending. Hover previews and the main preview action remain available. Images use the image file-type icon in the pill; the actual picture belongs in the preview.
Preview composition
previewConfig={{ content: <FileSummary />, isLoading }} supplies the hover/focus preview. onPreview can open Pathfinder's Sheet or your existing viewer. No PDF viewer, fetcher, or file URL resolver is bundled. The example uses a Sheet with app-supplied content.
Validate before upload
import { AttachmentValidation } from '@commenda/pathfinder/attachment';
const result = AttachmentValidation.validate(
selectedFiles,
{ accept: ['.pdf', 'image/*'], maxBytes: 10_000_000, maxCount: 10 },
existingAttachments.length
);
// Start uploads for result.accepted.
// Summarize result.rejected in one application-owned Toast.Validation preserves file objects and selection order. Rejections contain file and reasons (type, size, count). Extensions are case-insensitive, size limits are inclusive, and count includes existing attachments plus accepted candidates. File extensions can be checked when browsers omit MIME metadata. This is pre-upload feedback; the server must enforce its own upload constraints.
API reference
| Prop | Type | Behavior |
|---|---|---|
itemRequired | AttachmentItem | Required stable id and original filename; optional mediaType, url, and byte size |
onPreview | (
item: AttachmentItem
) => void | Ready pill activation; host opens its viewer |
onRetry | (
item: AttachmentItem
) => void | Failed pill activation; host performs retry |
onRemove | (
item: AttachmentItem
) => void | Separate remove control, including during uploads |
previewConfig | ObjectView fields | Optional host-owned preview content |
className | string | Outer pill styling |
previewConfigObject fields
| Field | Type | Requirement |
|---|---|---|
content | ReactNode | Required |
isLoading | boolean | Optional |
Design and accessibility rules
- ATTACH-01: Figma 569:26294. Height 28px, radius 6px, 0.5px pearl-300 border, Label/12 Strong. Keep the original filename and extension; truncate at 240px with full name available to assistive technology and native hover text.
- ATTACH-02: Uploading replaces the file icon with ProgressRing. Failure uses StatusFailed, a 1px red-300 border and red-800 text. Keep failures in the list; a Toast must not replace them.
- ATTACH-03: Removal is a separate named button. The host cancels outstanding upload work and ignores stale completion results after removal.
- ATTACH-04: These are user-added inputs. Agent-returned files are artifacts and belong to the output components in a later phase.
- Preview and retry use native buttons with keyboard activation. Removal never triggers preview. Existing Popover and Sheet primitives supply focus and reduced-motion behavior.
Migration notes
Map existing key to id, fileName to filename, and mimeType to mediaType. Keep opaque fileRef resolution inside your onPreview or preview-content adapter; do not treat it as a public URL. Existing per-item callbacks can be delegated by ID. Actual consumer migration is out of scope.