Attachment Since v4.1.0

A preview card for an attached image or file, used in the prompt input before sending and in the user message after.

Usage

import {
BuiAttachment,
BuiAttachmentFooter,
BuiAttachmentPreview,
BuiAttachmentRemove,
} from '@/bui/ui/attachment';
<mat-card buiAttachment>
<div buiAttachmentPreview>
<mat-icon svgIcon="file-text" />
</div>
<div buiAttachmentFooter>
<mat-icon svgIcon="file-text" />
<span>quarterly-report.pdf</span>
</div>
<button matIconButton buiAttachmentRemove aria-label="Remove">
<mat-icon svgIcon="x" />
</button>
</mat-card>

Example

The preview takes an img as a cover thumbnail, a pre as a content excerpt, or a mat-icon as a fallback. An image card has no footer; a file card names the file in one.

In a user message, the cards sit above the message content and leave out the remove button.

# Schedule

| Day | Task | Owner |
| --- | --- | --- |
| Mon | Invoice run | Billing team |
| Tue | Reconciliation | Bookkeeper |
| Wed | Client calls | Account lead |
| Thu | Reporting | Billing team |

Can you turn these into a launch week plan?

Sure. I will use the banner for the announcement, keep the schedule owners as they are, and pull the revenue targets from the quarterly report.

<div buiMessage align="end">
<div buiMessageContent>
<div class="flex flex-wrap justify-end gap-2">
<mat-card buiAttachment>
<div buiAttachmentPreview>
<img src="launch-banner.png" alt="" />
</div>
</mat-card>
<mat-card buiAttachment>
<div buiAttachmentPreview>
<pre>{{ excerpt }}</pre>
</div>
<div buiAttachmentFooter>
<mat-icon svgIcon="file-text" />
<span>SCHEDULE.md</span>
</div>
</mat-card>
<mat-card buiAttachment>
<div buiAttachmentPreview>
<mat-icon svgIcon="file-text" />
</div>
<div buiAttachmentFooter>
<mat-icon svgIcon="file-text" />
<span>quarterly-report.pdf</span>
</div>
</mat-card>
</div>
<div buiMessageBubble>
<p>Can you turn these into a launch week plan?</p>
</div>
</div>
</div>

In a prompt input

buiPromptInputAttachments takes a full row above the input of a prompt input. The cards stay in one row that scrolls sideways.

The application owns picking files and removing them. The remove button only renders; handle its click to drop the attachment.

# Schedule

| Day | Task | Owner |
| --- | --- | --- |
| Mon | Invoice run | Billing team |
| Tue | Reconciliation | Bookkeeper |
| Wed | Client calls | Account lead |
| Thu | Reporting | Billing team |
<form buiPromptInput>
@if (attachments().length) {
<div buiPromptInputAttachments>
@for (attachment of attachments(); track attachment) {
<mat-card buiAttachment>
<div buiAttachmentPreview>
@if (attachment.image) {
<img [src]="attachment.image" alt="" />
} @else {
<mat-icon svgIcon="file-text" />
}
</div>
@if (!attachment.image) {
<div buiAttachmentFooter>
<mat-icon svgIcon="file-text" />
<span>{{ attachment.name }}</span>
</div>
}
<button
matIconButton
type="button"
buiAttachmentRemove
aria-label="Remove"
(click)="remove(attachment)"
>
<mat-icon svgIcon="x" />
</button>
</mat-card>
}
</div>
}
<input #picker type="file" multiple hidden (change)="add(picker.files!)" />
<button matIconButton type="button" aria-label="Attach" (click)="picker.click()">
<mat-icon svgIcon="plus" />
</button>
<div buiPromptInputEditor placeholder="Ask anything"></div>
<button matIconButton class="primary" aria-label="Send">
<mat-icon svgIcon="corner-down-left" />
</button>
</form>
readonly attachments = signal<Attachment[]>([]);

add(files: FileList) {
this.attachments.update((attachments) => [
...attachments,
...Array.from(files, (file) => ({
name: file.name,
image: file.type.startsWith('image/')
? URL.createObjectURL(file)
: undefined,
})),
]);
}

remove(attachment: Attachment) {
this.attachments.update((attachments) =>
attachments.filter((a) => a !== attachment)
);
}

API Reference

buiAttachment

The card, on a mat-card. Takes its border, background and shadow from the card theme.

buiAttachmentPreview

The top area, holding a thumbnail, an excerpt or an icon.

buiAttachmentFooter

An icon and the file name in a span, truncated when long. Optional.

buiAttachmentRemove

A small round matIconButton on the top end corner, shown on hover or keyboard focus. Optional.