"""
end
attr(:f, :map, required: true, doc: "the map of flash messages")
attr(:corner, :atom,
values: [:top_left, :top_right, :bottom_left, :bottom_right],
default: :top_right,
doc: "the corner to display the toasts"
)
attr(:toast_class_fn, :any,
default: &__MODULE__.toast_class_fn/1,
doc: "function to override the look of the toasts"
)
defp flashes(assigns) do
~H"""
<.toast
corner={@corner}
class_fn={@toast_class_fn}
duration={0}
kind={:info}
title="Info"
phx-update="ignore"
flash={@f}
/>
<.toast
corner={@corner}
class_fn={@toast_class_fn}
duration={0}
kind={:error}
title="Error"
phx-update="ignore"
flash={@f}
/>
<.toast
corner={@corner}
class_fn={@toast_class_fn}
id="client-error"
kind={:error}
title="We can't find the internet"
phx-update="ignore"
phx-disconnected={show(".phx-client-error #client-error")}
phx-connected={hide("#client-error")}
hidden
>
Attempting to reconnect
<.svg name="hero-arrow-path" class="inline-block ml-1 h-3 w-3 animate-spin" />
<.toast
corner={@corner}
class_fn={@toast_class_fn}
id="server-error"
kind={:error}
title="Something went wrong!"
phx-update="ignore"
phx-disconnected={show(".phx-server-error #server-error")}
phx-connected={hide("#server-error")}
hidden
>
Hang in there while we get back on track
<.svg name="hero-arrow-path" class="inline-block ml-1 h-3 w-3 animate-spin" />
"""
end
attr(:id, :string, doc: "the optional id of flash container")
attr(:flash, :map, default: %{}, doc: "the map of flash messages to display")
attr(:title, :string, default: nil)
attr(:kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup")
attr(:rest, :global, doc: "the arbitrary HTML attributes to add to the flash container")
attr(:target, :any, default: nil, doc: "the target for the phx-click event")
attr(:duration, :integer,
default: 6000,
doc: "the time in milliseconds before the message is automatically dismissed"
)
attr(:class_fn, :any,
required: true,
doc: "function to override the look of the toasts"
)
attr(:corner, :atom, required: true, doc: "the corner to display the toasts")
attr(:icon, :any, default: nil, doc: "the optional icon to render in the flash message")
attr(:action, :any, default: nil, doc: "the optional action to render in the flash message")
attr(:component, :any, default: nil, doc: "the optional component to render the flash message")
slot(:inner_block, doc: "the optional inner block that renders the flash message")
defp toast(assigns) do
assigns =
assigns
|> assign_new(:id, fn -> "flash-#{assigns.kind}" end)
~H"""
<%= if @component do %>
<%= @component.(Map.merge(assigns, %{body: msg})) %>
<% else %>
<%= if @icon do %>
<%= @icon.(assigns) %>
<% end %>
<%= @title %>
<%= msg %>
<%= if @action do %>
<%= @action.(assigns) %>
<% end %>
<% end %>
"""
end
# todo: fix bug where refreshing causes the disconnected toast to show
@impl Phoenix.LiveComponent
def render(assigns) do
# flex
# "fixed z-50 max-h-screen w-full p-4 md:max-w-[420px] pointer-events-auto z-50 pointer-events-none fixed gap-2"
default_classes =
"fixed z-50 max-h-screen w-full p-4 md:max-w-[420px] pointer-events-none grid origin-center"
class =
case assigns[:corner] do
:bottom_left ->
"#{default_classes} items-end bottom-0 left-0 flex-col-reverse sm:top-auto"
:bottom_right ->
"#{default_classes} items-end bottom-0 right-0 flex-col-reverse sm:top-auto"
:top_left ->
"#{default_classes} items-start top-0 left-0 flex-col sm:bottom-auto"
:top_right ->
"#{default_classes} items-start top-0 right-0 flex-col sm:bottom-auto"
end
assigns = assign(assigns, :class, class)
~H"""
<.toast
:for={
{dom_id,
%LiveToast{
kind: k,
msg: body,
title: title,
icon: icon,
action: action,
duration: duration,
component: component
}} <- @streams.toasts
}
id={dom_id}
data-count={@toast_count}
duration={duration}
kind={k}
class_fn={@toast_class_fn}
component={component}
icon={icon}
action={action}
corner={@corner}
title={
if title do
title
else
nil
end
}
target={@myself}
>
<%= body %>
<.flashes f={@f} corner={@corner} toast_class_fn={@toast_class_fn} />
"""
end
attr(:name, :string, required: true, doc: "the name of the icon")
attr(:rest, :global, doc: "other html attributes")
defp svg(%{name: "hero-x-mark-solid"} = assigns) do
~H"""
"""
end
defp svg(%{name: "hero-arrow-path"} = assigns) do
~H"""
"""
end
defp show(js \\ %JS{}, selector) do
JS.show(js,
to: selector,
display: "flex",
transition:
{"transition-all transform ease-out duration-300",
"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
"opacity-100 translate-y-0 sm:scale-100"}
)
end
defp hide(js \\ %JS{}, selector) do
JS.hide(js,
to: selector,
time: 200,
transition:
{"transition-all transform ease-in duration-200",
"opacity-100 translate-y-0 sm:scale-100",
"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"}
)
end
end