# VelvetKit

## Toast (`UI::Toast`)

A stacked, auto-dismissing message, pushed from the client or a Turbo Stream.

Add it: `bin/rails generate velvetkit:add toast`

Also adds: `icon`, `spinner`, `button`

Stimulus: `ui--toast`, `ui--toast-viewport`

### Arguments

```ruby
UI::Toast.new(variant: :default, title: nil, description: nil, duration: 5, dismissible: true, icon: true, class: nil, **html_attrs)
```

Slots: `with_action`

### Notes

UI::Toast — a single dismissible, auto-expiring message.

A toast has nowhere to appear on its own: render a UI::Toast::Viewport once
per page (e.g. in the layout), then push toasts into it either from the
server, by appending one in a Turbo Stream response —

  turbo_stream.append(UI::Toast::Viewport::ID, render(UI::Toast.new(variant: :success, title: "Saved")))

— or from the client, with the `toast()` helper exported by the
toast-viewport controller.

### Example: All Variants

```erb
<div class="flex flex-col gap-4 max-w-sm">
  <%= render(UI::Toast.new(variant: :default, title: "Default", description: "Heads up, something happened.")) %>
  <%= render(UI::Toast.new(variant: :success, title: "Success", description: "Your changes have been saved.")) %>
  <%= render(UI::Toast.new(variant: :destructive, title: "Destructive", description: "There was a problem with your request.")) %>
  <%= render(UI::Toast.new(variant: :warning, title: "Warning", description: "This action cannot be undone.")) %>
  <%= render(UI::Toast.new(variant: :info, title: "Info", description: "Here's some helpful information.")) %>
</div>
```

### Example: With Action

```erb
<div class="max-w-sm">
  <%= render(UI::Toast.new(variant: :default, title: "Update available", duration: 0)) do |toast| %>
    <% toast.with_action do %>
      <%= render(UI::Button.new(size: :sm)) { "Reload" } %>
    <% end %>
  <% end %>
</div>
```

### Example: Live Viewport

```erb
<div class="flex flex-wrap gap-2">
  <%
    demos = {
      default:     ["Notification", "Heads up, something happened."],
      success:     ["Saved", "Your changes have been saved."],
      destructive: ["Something broke", "There was a problem with your request."],
      warning:     ["Careful", "This action cannot be undone."],
      info:        ["FYI", "Here's some helpful information."]
    }
  %>
  <% demos.each do |variant, (title, description)| %>
    <% payload = { title: title, description: description, variant: variant }.to_json %>
    <%= render(UI::Button.new(variant: :outline, size: :sm,
          onclick: "window.dispatchEvent(new CustomEvent('toast:show', { detail: #{payload} }))")) { title } %>
  <% end %>
  <% persistent = { title: "Stays until dismissed", description: "duration: 0 skips the auto-dismiss timer.", variant: :default, duration: 0 }.to_json %>
  <%= render(UI::Button.new(variant: :outline, size: :sm,
        onclick: "window.dispatchEvent(new CustomEvent('toast:show', { detail: #{persistent} }))")) { "Persistent" } %>
</div>

<%= render(UI::Toast::Viewport.new) %>
```
