# Attachment group

Package: @commenda/pathfinder@0.1.0
Page: /docs/components/attachment-group
Figma: https://www.figma.com/design/dn7BkGYEfIyP0o2zfesfVh/The-System?node-id=569-26690

```tsx
import { AttachmentGroup } from '@commenda/pathfinder/attachment-group';

<AttachmentGroup
  items={attachments}
  onRemove={removeAttachment}
  onPreview={openPreview}
  maxRows={3}
/>;
```


## Usage [#usage]

```tsx
import { AttachmentGroup } from '@commenda/pathfinder/attachment-group';

<AttachmentGroup
  items={attachments}
  maxRows={3}
  onRemove={removeAttachment}
  onRetry={retryUpload}
  onPreview={openPreview}
  getPreview={(item) => ({ content: <FileSummary attachment={item} /> })}
/>;
```

The group measures actual pill widths and reserves room for +N within the last permitted row. It recomputes after container resize, font loading, or item changes. IDs must be unique and stable. Input order is preserved; grouping files into folder records is application-owned.

## Sent-message alignment [#sent-message-alignment]

`align="end"` aligns toward the inline end and suppresses all removal controls, including overflow checkboxes. Preview actions continue to work. Place the group 6px above the message bubble in the conversation composition.


```tsx
<AttachmentGroup
  items={attachments}
  align="end"
  onPreview={openPreview}
/>;
```


## Overflow behavior [#overflow-behavior]

+N opens a named Popover listing hidden attachments. Uncheck an item to request removal in an editable group; select its pill to preview or retry. This uses a Popover with Checkbox rather than the action-only Dropdown so selection and preview remain distinct controls. The app updates `items` after removal; if it rejects the request, the attachment remains selected.

The initial server render provides +N access; measurement takes place before the client paints. Hidden measurement labels are excluded from the accessibility tree and contain no focusable controls.

## API reference [#api-reference]

| Prop | Type | Requirement | Behavior |
| --- | --- | --- | --- |
| `items` | `readonly AttachmentItem[]` | required | Required; empty arrays render nothing |
| `align` | `"start" \| "end"` | optional | Start default; end is a non-removable sent group |
| `maxRows` | `1 \| 2 \| 3` | optional | Default 3; +N stays inside this row budget |
| `onPreview` | `((item: AttachmentItem) => void)` | optional | Same lifecycle callbacks as Attachment |
| `onRetry` | `((item: AttachmentItem) => void)` | optional | Same lifecycle callbacks as Attachment |
| `onRemove` | `((item: AttachmentItem) => void)` | optional | Same lifecycle callbacks as Attachment |
| `getPreview` | `((item: AttachmentItem) => AttachmentProps["previewConfig"])` | optional | Optional per-item hover/focus preview |
| `label` | `string` | optional | Accessible group name; defaults to Attachments |
| `className` | `string` | optional | Outer layout |

## Design rules [#design-rules]

- **ATTACH-GROUP-01:** Figma 569:26690. Wrap with a 6px gap, cap at three rows, and expose every remaining item through +N. Never clip a fourth row of keyboard-focusable items.
- **ATTACH-GROUP-02:** Composer alignment enables removal when supplied; sent alignment removes editing controls but retains preview.
- Retain failed items and their retry action. Width calculations use original labels and update when item state changes.

## Migration notes [#migration-notes]

Existing `{ key, fileName, mimeType, fileRef }` arrays map to AttachmentItem without changing persisted data. Keep existing folder aggregation before the group. Overflow, measurement, and keyboard management are handled inside Pathfinder.
