Current section
Files
Jump to
Current section
Files
priv/templates/components/toast.ex.eex
defmodule <%= @module_name %>.Components.UI.Toast do
@moduledoc """
Toast notification component with PhiaToast vanilla JS hook.
Ejected from PhiaUI — you own this copy. Customise freely.
## Setup
Add once to your root layout (inside `<body>`):
<.toast id="phia-toast-viewport" />
Trigger from LiveView:
push_event(socket, "phia-toast", %{
title: "Saved!",
description: "Your changes were saved.",
variant: "success",
duration_ms: 4000
})
## Hook setup
import PhiaToast from "./hooks/toast"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { PhiaToast }
})
"""
use Phoenix.Component
import <%= @module_name %>.ClassMerger, only: [cn: 1]
import <%= @module_name %>.Components.UI.Icon, only: [icon: 1]
attr :id, :string, required: true
attr :max_toasts, :integer, default: 5
attr :variant, :atom, default: :default, values: [:default, :success, :destructive, :warning]
attr :class, :string, default: nil
attr :rest, :global
def toast(assigns) do
~H"""
<div
id={@id}
phx-hook="PhiaToast"
role="status"
aria-live="polite"
aria-atomic="false"
data-max-toasts={@max_toasts}
data-variant={@variant}
class={cn([
"fixed bottom-0 right-0 z-[100] flex max-h-screen flex-col-reverse gap-2 p-4",
"sm:flex-col md:max-w-[420px]",
@class
])}
{@rest}
>
</div>
"""
end
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def toast_title(assigns) do
~H"""
<div class={cn(["text-sm font-semibold", @class])} {@rest}>
<%%= render_slot(@inner_block) %>
</div>
"""
end
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def toast_description(assigns) do
~H"""
<div class={cn(["text-sm opacity-90", @class])} {@rest}>
<%%= render_slot(@inner_block) %>
</div>
"""
end
attr :on_click, :string, required: true
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def toast_action(assigns) do
~H"""
<button
type="button"
phx-click={@on_click}
class={cn([
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border",
"bg-transparent px-3 text-sm font-medium transition-colors",
"hover:bg-secondary focus:outline-none focus:ring-1",
@class
])}
{@rest}
>
<%%= render_slot(@inner_block) %>
</button>
"""
end
attr :class, :string, default: nil
attr :rest, :global
def toast_close(assigns) do
~H"""
<button
type="button"
aria-label="Close"
data-toast-close
class={cn([
"absolute right-1 top-1 rounded-md p-1 opacity-0 transition-opacity",
"hover:opacity-100 focus:opacity-100 focus:outline-none",
@class
])}
{@rest}
>
<.icon name="x" size={:sm} />
</button>
"""
end
end