AttachmentButton
A file-attach affordance. Press the circle and it grows into a rounded panel while the clip icon shrinks and slides into the corner, turning into a close button as a dashed drop zone fades in beneath it.
Usage
<AttachmentButton
panelSize={240}
spring="smooth"
/>How the motion works
One progress spring drives every number: the panel's edge length, the chip's size and its left/top offset, and the two glyphs' opacity and rotation. The chip's corner position is computed from the panel's own current size rather than sprung on its own path, which is what keeps the shrink and the slide reading as one move instead of two things that happen to overlap. Panel and chip share a single fixed radius instead of interpolating one: at the closed size that radius is exactly half the box, so it draws a circle for free, and at the open size the same number reads as an ordinary corner — nothing about the shape needs its own spring. The drop zone only starts fading in past the halfway point of the panel's growth, so it trails the move rather than starting from nothing at the first frame.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| open / defaultOpen | boolean | — | Controlled or initial state. |
| onOpenChange | (open: boolean) => void | — | Fires when the panel opens or closes. |
| onFilesSelected | (files: FileList) => void | — | Fires with files from the picker or a drop. |
| closedSize | number | 40 | Edge length of the resting circle, in px. |
| panelSize | number | 240 | Edge length of the expanded panel, in px. |
