Experimental
This API may change in any 0.0.x release. Changes are listed in the CHANGELOG.
A plugin's tab in the toolbar panel: a Phoenix.LiveComponent (ADR 05).
A plugin returns its panel module from PhoenixDevToolbar.Plugin.panel/0.
The toolbar renders it inside its own iframe, never in the host page, and
hands it the plugin's events as they arrive:
defmodule MyApp.Toolbar.JobsPanel do
use PhoenixDevToolbar.Panel
# Events have no :id field: give streams a DOM id from the unique seq.
@impl Phoenix.LiveComponent
def mount(socket), do: {:ok, stream_configure(socket, :jobs, dom_id: &"jobs-#{&1.seq}")}
@impl PhoenixDevToolbar.Panel
def handle_reset(events, socket),
do: stream(socket, :jobs, events, reset: true, limit: -500)
@impl PhoenixDevToolbar.Panel
def handle_events(events, socket),
do: Enum.reduce(events, socket, &stream_insert(&2, :jobs, &1, limit: -500))
@impl Phoenix.LiveComponent
def render(assigns) do
~H"""
<ul id="jobs" class="pdt-list" phx-update="stream">
<li id="jobs-empty" class="pdt-list__empty">No jobs yet.</li>
<li :for={{id, job} <- @streams.jobs} id={id} class="pdt-row">{job.data.worker}</li>
</ul>
"""
end
endEvents are the plugin's own, oldest first. Keep them in streams, never in
plain assigns: a page can produce thousands. handle_reset/2 gets every
stored event of a new page (the panel opened, live navigation), and runs
once with [] before the first render; handle_events/2 gets the new ones
since. DOM ids must be unique in the whole panel: prefix them with your
plugin's name and use the event's seq.
The toolbar assigns, before calling either callback:
@config: the plugin's config, as returned by itsinit/1@page: the page shown, a struct whose module is internal. Rely only on@page.root(the sid of its HTTP request) and@page.sids(the sids of the sessions that make up the page: the request, then its LiveView once connected; empty when the page's events have expired). Its other fields (transport,path,owner) are internal and may change@shown?: the tab is selected@visible?: the panel is open and the browser tab visible
handle_props/1 runs when only those change, for example to start or stop
polling. A panel can use every LiveComponent feature except update/2 and
update_many/1, which the toolbar defines: handle_event/3
(with phx-target={@myself}), start_async/3 and handle_async/3. For
timers, send_after/4 delivers a message to handle_info/2. Style it with the panel's pdt-
classes (the stable set is listed in the plugin guide): the panel
stylesheet is the only one loaded.
The behaviour is unstable before 1.0 (see the note at the top).
Summary
Callbacks
New events of the plugin since the last call, oldest first.
A message scheduled with send_after/4, or {:navigate, params} from
navigate/3. Defaults to ignoring it.
@shown?, @visible? or @page changed. Defaults to doing nothing.
The page changed: replace everything shown with events.
Functions
Formats a duration in microseconds (Event.duration) the way the toolbar
does: "0.04 ms", "12.3 ms", "2.50 s", "n/a" for nil.
Switches the panel to the tab of the plugin plugin_id and delivers
{:navigate, params} to that tab's handle_info/2. Returns socket
unchanged, for pipelines. Call it from the panel's own callbacks.
Delivers message to the panel module with DOM id id (its
@id) after ms milliseconds, through handle_info/2. Returns the
timer reference. Call it from the panel's own callbacks.
Callbacks
@callback handle_events( events :: [PhoenixDevToolbar.Event.t()], Phoenix.LiveView.Socket.t() ) :: Phoenix.LiveView.Socket.t()
New events of the plugin since the last call, oldest first.
@callback handle_info(message :: term(), Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
A message scheduled with send_after/4, or {:navigate, params} from
navigate/3. Defaults to ignoring it.
@callback handle_props(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
@shown?, @visible? or @page changed. Defaults to doing nothing.
@callback handle_reset( events :: [PhoenixDevToolbar.Event.t()], Phoenix.LiveView.Socket.t() ) :: Phoenix.LiveView.Socket.t()
The page changed: replace everything shown with events.
Functions
@spec duration(non_neg_integer() | nil) :: String.t()
Formats a duration in microseconds (Event.duration) the way the toolbar
does: "0.04 ms", "12.3 ms", "2.50 s", "n/a" for nil.
@spec send_after(module(), String.t(), term(), non_neg_integer()) :: reference()
Delivers message to the panel module with DOM id id (its
@id) after ms milliseconds, through handle_info/2. Returns the
timer reference. Call it from the panel's own callbacks.