Attachment
A file or image chip showing its name, type, and size.
Installation
Included in poetry-ui — available as
poetry_attachment
the moment you've installed Poetry,
with no per-component step. To own the source and edit it, copy it into your app:
bin/rails g poetry:add attachment
Default
Open Default standalone
<%= poetry_attachment do |attachment| %>
<% attachment.with_media { poetry_icon(name: :file) } %>
<% attachment.with_title { "quarterly-report.pdf" } %>
<% attachment.with_description { "1.2 MB" } %>
<% end %>
Group
Open Group standalone
<%= poetry_attachment_group do %>
<%= poetry_attachment do |attachment| %>
<% attachment.with_media { poetry_icon(name: :"file-text") } %>
<% attachment.with_title { "onboarding-brief.pdf" } %>
<% attachment.with_description { "240 KB" } %>
<% end %>
<%= poetry_attachment do |attachment| %>
<% attachment.with_media(variant: :image) { poetry_icon(name: :image) } %>
<% attachment.with_title { "hero-cover.jpg" } %>
<% attachment.with_description { "1.8 MB" } %>
<% end %>
<%= poetry_attachment do |attachment| %>
<% attachment.with_media { poetry_icon(name: :music) } %>
<% attachment.with_title { "welcome-intro.mp3" } %>
<% attachment.with_description { "3.1 MB" } %>
<% end %>
<%= poetry_attachment do |attachment| %>
<% attachment.with_media { poetry_icon(name: :film) } %>
<% attachment.with_title { "product-demo.mp4" } %>
<% attachment.with_description { "12 MB" } %>
<% end %>
<% end %>
Idle dropzone
Open Idle dropzone standalone
<%= poetry_attachment(state: :idle) do |attachment| %>
<% attachment.with_media { poetry_icon(name: :plus) } %>
<% attachment.with_title { "Add a file" } %>
<% end %>
Sizes
Open Sizes standalone
<div class="flex flex-wrap items-end gap-3">
<%= poetry_attachment(size: :default) do |attachment| %>
<% attachment.with_media { poetry_icon(name: :"file-text") } %>
<% attachment.with_title { "meeting-notes.md" } %>
<% attachment.with_description { "8 KB" } %>
<% end %>
<%= poetry_attachment(size: :sm) do |attachment| %>
<% attachment.with_media { poetry_icon(name: :"file-text") } %>
<% attachment.with_title { "meeting-notes.md" } %>
<% attachment.with_description { "8 KB" } %>
<% end %>
<%= poetry_attachment(size: :xs) do |attachment| %>
<% attachment.with_media { poetry_icon(name: :"file-text") } %>
<% attachment.with_title { "meeting-notes.md" } %>
<% end %>
</div>
States
Open States standalone
<div class="flex flex-wrap items-center gap-3">
<%= poetry_attachment(state: :uploading) do |attachment| %>
<% attachment.with_media { poetry_icon(name: :file) } %>
<% attachment.with_title { "photo.png" } %>
<% attachment.with_description { "uploading…" } %>
<% end %>
<%= poetry_attachment(state: :error) do |attachment| %>
<% attachment.with_media { poetry_icon(name: :"circle-alert") } %>
<% attachment.with_title { "notes.txt" } %>
<% attachment.with_description { "Too large (max 10 MB)" } %>
<% end %>
</div>
Vertical with actions
Open Vertical with actions standalone
<%= poetry_attachment(orientation: :vertical) do |attachment| %>
<% attachment.with_media(variant: :image) { poetry_icon(name: :image) } %>
<% attachment.with_title { "screenshot.png" } %>
<% attachment.with_action(label: "Remove") { poetry_icon(name: :x) } %>
<% end %>
With trigger
Open With trigger standalone
<%= poetry_attachment do |attachment| %>
<% attachment.with_media { poetry_icon(name: :"file-text") } %>
<% attachment.with_title { "annual-report.pdf" } %>
<% attachment.with_description { "4.6 MB" } %>
<% attachment.with_trigger { tag.span("Preview annual-report.pdf", class: "sr-only") } %>
<% attachment.with_action(label: "Download") { poetry_icon(name: :download) } %>
<% attachment.with_action(label: "Remove") { poetry_icon(name: :x) } %>
<% end %>
API
Poetry::Ui::Attachment::Component — options are
constructor keywords (the poetry_* helper forwards them);
slots are composed inside the block. Generated from the gem's source documentation.
| Option | Type | Details | Description |
|---|---|---|---|
| orientation: | Symbol | one of: horizontal, vertical; defaults to :horizontal |
Row (:horizontal) or stacked thumbnail-card (:vertical) layout. |
| size: | Symbol | one of: default, sm, xs; defaults to :default |
The chip density axis. |
| state: | Symbol | one of: idle, uploading, processing, error, done; defaults to :done |
The upload lifecycle state; flip it by re-render or Turbo Stream replace, never in JS. |
Slots
| Writer | Description |
|---|---|
| with_action | Trailing icon actions - each renders a Button (ghost, icon-xs defaults) and requires label:. |
| with_description | Muted metadata under the title (size, type); in the error state, the failure explanation. |
| with_media | Leading visual: :icon (default) boxes an icon tile, :image wraps the caller's <img>. |
| with_title | The file name line. User content - never mark it html_safe. |
| with_trigger | Makes the whole chip the control - a stretched button (or anchor via tag: :a, href:) layered under the actions. Don't also wrap the chip in a link. |
Styling
Every part carries a stable data-slot attribute — target
[data-slot=…] from your own CSS to restyle it. State rides
data attributes on the parts below. This contract is verified against rendered DOM in CI.
| Part | Description |
|---|---|
| [data-slot=attachment] | The chip root - the server-owned upload lifecycle rides here (flip data-upload-state by re-render / Turbo Stream replace) |
| [data-slot=attachment-media] | The media slot's box - the icon tile or the caller's <img> |
| [data-slot=attachment-content] | Text column wrapping title/description - renders when either slot is set |
| [data-slot=attachment-title] | The file name line (title slot; user content, never html_safe) |
| [data-slot=attachment-description] | Muted metadata / failure copy under the title |
| [data-slot=attachment-actions] | Row of with_action poetry Buttons |
| [data-slot=attachment-trigger] | The whole-chip control (with_trigger: a button or tag: :a anchor) - wraps the picker/download affordance |
| [data-slot=attachment-status] | sr-only role=status announcement for the in-flight and error states (uploading/processing/error) |
State attributes
| Part | Attribute | Condition | Values |
|---|---|---|---|
| attachment | data-upload-state | always - the resolved state | idle · uploading · processing · error · done |
| attachment | data-size | always - the resolved size | default · sm · xs |
| attachment | data-orientation | always - the resolved orientation | horizontal · vertical |
| attachment-media | data-variant | always - the media variant | icon · image |