PhoenixDevToolbar.Panel behaviour (PhoenixDevToolbar v0.0.2)

Copy Markdown View Source

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
end

Events 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 its init/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

handle_events(events, t)

@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.

handle_info(message, t)

(optional)
@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.

handle_props(t)

(optional)
@callback handle_props(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()

@shown?, @visible? or @page changed. Defaults to doing nothing.

handle_reset(events, t)

@callback handle_reset(
  events :: [PhoenixDevToolbar.Event.t()],
  Phoenix.LiveView.Socket.t()
) ::
  Phoenix.LiveView.Socket.t()

The page changed: replace everything shown with events.

Functions

duration(us)

@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.

send_after(module, id, message, ms)

@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.