live_toast
live_toast copied to clipboard
Better custom component support
This is a WIP, but mainly I was trying to have a completely custom component for all flashes/toasts.
I realized that the DX was weird because I had to pass the component each time. Worse than that, the X icon was still being rendered even with a custom component.
So I'm trying to improve this by:
- Allowing
componentto be passed straight toLiveToast.toast_groupfunction - Use that by default if the update received in the LiveComponent doesn't include a component
- Not rendering the close button when custom component is passed (breaking change)
- Passing
phx-*attributes to be used for closing the toast down to the component, so one can place the close button/action wherever they want. - Setting a more minimalist classes on the wrapping div when component is passed (breaking change).
This is still missing a lot of important details. I also was failing to run the tests because some Jason issue that I'll look into after.
But with this setup I was able to customize the toast completely with the following toast component:
defmodule PursonalWeb.CoreComponents.Toast do
use Phoenix.Component
import Heroicons.LiveView
import Turboprop.Variants
@variant_config [
slots: [
base: [
"group",
"pointer-events-auto",
"relative",
"flex",
"w-full",
"items-center",
"justify-between",
"space-x-4",
"overflow-hidden",
"rounded-md",
"p-6",
"pr-8",
"shadow-lg",
"transition-all",
"mt-4"
],
cross: [
"absolute",
"right-2",
"top-2",
"rounded-md",
"p-1",
"opacity-0",
"transition-opacity",
"focus:opacity-100",
"focus:outline-none",
"focus:ring-2",
"group-hover:opacity-100"
]
],
variants: [
variant: [
default: [
base: "bg-background text-foreground border",
cross: "text-foreground/50 hover:text-foreground"
],
destructive: [
base: "bg-destructive text-destructive-foreground",
cross: "text-destructive-foreground/50 hover:text-destructive-foreground"
]
]
]
]
attr :id, :string, doc: "the optional id of flash container"
attr :title, :string, default: nil
attr :body, :string, default: ""
attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
attr :action, :any, doc: "Optinal action component"
attr :close_args, :any, default: []
def toast(assigns) do
assigns =
assigns
|> assign(:base_variant_class, toast_variant(assigns, :base))
|> assign(:cross_variant_class, toast_variant(assigns, :cross))
~H"""
<div role="status" aria-live="off" aria-atomic="true" tabindex="0" class={@base_variant_class}>
<div class="grid gap-1">
<div :if={@title} class="text-sm font-semibold" data-part="title"><%= @title %></div>
<div class="text-sm opacity-90"><%= @body %></div>
</div>
<button type="button" class={@cross_variant_class} {@close_args}>
<.icon name="x-mark" type="solid" class="h-5 w-5" />
</button>
</div>
"""
end
defp toast_variant(assigns, slot) do
variant(@variant_config, slot: slot, variant: variant_name(assigns))
end
defp variant_name(%{kind: :error}), do: :destructive
defp variant_name(_), do: :default
end