DevactionUI.ToastGroup (devaction_ui v0.2.3)

Copy Markdown View Source

A pile of toasts that opens into a list when you point at it: notices pushed from the server that never cover the screen. Put one toast_group/1 in the layout and push toasts to it from any LiveView with put_toast/4, update_toast/3, dismiss_toast/2 and clear_toasts/1.

A new toast enters from below and pushes the others behind the pile. Closed, only the front one shows and up to two peek behind it, smaller and without text. With the pointer over it, or the focus inside, the pile opens into a list with a 12px gap that does not close it. The timers pause while it is open and while the tab is hidden, and go on from where they stopped. Alt+T moves the focus to the pile, Esc closes the focused toast and a drag sideways (past 80px) dismisses it. A :risk toast is announced at once (role="alert"), the rest politely (role="status") inside a region with aria-live="polite". A :loading toast never leaves by itself: update_toast/3 turns it into :ok or :risk with the same id.

Variants: :default, :ok, :warn, :risk, :info, :ai and :loading (a spinning icon). An action is action: %{label: "Undo", event: "undo", value: %{id: 9}}: the button sends event with value back to the LiveView (or to target) and closes the toast.

The library has no language: every text (title, description, action label) comes from the caller, and the close button takes its name from dismiss_label. The icons are drawn here as defaults and replaced by the :icon slot (it receives the variant) and the :close_icon slot. The list itself belongs to the browser (phx-update="ignore"), so a re-render never disturbs a pile that is open. From the page, without the server: window.dispatchEvent(new CustomEvent("dui:toast", {detail: {title: "Copied"}})).

<DevactionUI.ToastGroup.toast_group flash={@flash} label="Notifications (Alt+T)" dismiss_label="Close" />

socket |> put_toast(:ok, "Review published", description: "PR #214")
socket |> put_toast(:loading, "Building report", id: "report")
socket |> update_toast("report", variant: :ok, title: "Report ready")

Summary

Functions

Closes every toast, :loading ones included. opts: :group, see put_toast/4.

Closes the toast with id. opts: :group, see put_toast/4.

Pushes a toast to the group of the page. opts: :id (to update or dismiss it later), :description, :duration (milliseconds, 0 keeps it until it is closed; :loading ignores it), :action (%{label: "Undo", event: "undo", value: %{id: 9}}) and :group.

The group of toasts: one per layout, filled by put_toast/4 and its siblings.

Changes the toast with id in place: any of :variant, :title, :description, :duration and :action (and :group, see put_toast/4). Changing the variant or the duration restarts its timer, so a :loading toast turned :ok closes after the group's duration.

Types

action()

@type action() :: %{
  :label => String.t(),
  :event => String.t(),
  optional(:value) => map()
}

variant()

@type variant() :: :default | :ok | :warn | :risk | :info | :ai | :loading

Functions

clear_toasts(socket, opts \\ [])

Closes every toast, :loading ones included. opts: :group, see put_toast/4.

dismiss_toast(socket, id, opts \\ [])

Closes the toast with id. opts: :group, see put_toast/4.

put_toast(socket, variant, title, opts \\ [])

Pushes a toast to the group of the page. opts: :id (to update or dismiss it later), :description, :duration (milliseconds, 0 keeps it until it is closed; :loading ignores it), :action (%{label: "Undo", event: "undo", value: %{id: 9}}) and :group.

LiveView delivers server events to every hook of the page, so a page with more than one group names the one it means with group: "id-of-the-group"; without it every group shows the toast.

toast_group(assigns)

@spec toast_group(map()) :: Phoenix.LiveView.Rendered.t()

The group of toasts: one per layout, filled by put_toast/4 and its siblings.

Attributes

  • id (:string) - Defaults to "toasts".
  • flash (:map) - the flash of the socket: info shows as an :ok toast and error as a :risk one. Defaults to %{}.
  • position (:string) - Defaults to "bottom-right". Must be one of "bottom-right", "bottom-left", "bottom-center", "top-right", "top-left", or "top-center".
  • visible (:integer) - how many show in the closed pile: the front one and the ones peeking behind it. Defaults to 3.
  • duration (:integer) - milliseconds before a toast closes by itself; 0 keeps them until they are closed. Defaults to 4000.
  • label (:string) (required) - the region's name for assistive technology, such as Notifications (Alt+T).
  • dismiss_label (:string) (required) - the name of the close button of each toast.
  • target (:any) - the @myself (or selector) that receives the events of the toast actions. Defaults to nil.
  • inline (:boolean) - lay the group out in the flow (relative) instead of fixed to the viewport. Defaults to false.
  • hotkey (:boolean) - whether Alt+T moves the focus here; turn it off in all but one group when a page has several. Defaults to true.
  • class (:any) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • icon - replaces the icon of each variant (:let={variant}: :ok, :warn, :risk, :info, :ai, :loading).
  • close_icon - replaces the × of the close button.

update_toast(socket, id, patch)

Changes the toast with id in place: any of :variant, :title, :description, :duration and :action (and :group, see put_toast/4). Changing the variant or the duration restarts its timer, so a :loading toast turned :ok closes after the group's duration.