MishkaGervaz.Table.Behaviours.ActionType behaviour (MishkaGervaz v0.0.2)

Copy Markdown View Source

Behaviour for row action type renderers.

Implement this behaviour to create custom action types that render buttons/links in specific ways.

Example

defmodule MyApp.ActionTypes.Confirm do
  @behaviour MishkaGervaz.Table.Behaviours.ActionType
  use Phoenix.Component
  import MishkaGervaz.Helpers,
    only: [action_label: 1, resolve_confirm: 2, dynamic_component: 1]

  @impl true
  def render(assigns, action, record, ui, target) do
    master? = assigns[:state] && assigns[:state].master_user?

    assigns =
      %{__changed__: %{}}
      |> assign(:module, ui)
      |> assign(:function, :button)
      |> assign(:label, action_label(action))
      |> assign(:icon, action[:ui][:icon])
      |> assign(:class, action[:ui][:class] || "text-orange-600 hover:text-orange-800")
      |> assign(:phx_click, action[:event] || "confirm")
      |> assign(:phx_value_id, record.id)
      |> assign(:phx_target, target)
      |> assign(
        :data_confirm,
        (master? && resolve_confirm(action[:confirm], record)) || "Are you sure?"
      )

    ~H"""
    <.dynamic_component {assigns} />
    """
  end
end

Then use in DSL:

row_actions do
  action :archive, type: MyApp.ActionTypes.Confirm, confirm: "Archive this record?"
end

What belongs in the assigns map

Read whatever you need from the incoming assigns, then build a fresh map for the component. The map you build is splatted whole at the UI adapter, and button/1 declares attr :rest, :global — so every key it does not recognise is rendered as an HTML attribute of its own. Passing the incoming assigns through would put the whole table state in the markup.

Three kinds of key belong in it, and nothing else:

  • :module and :function, which MishkaGervaz.Helpers.dynamic_component/1 pops off to decide what to call. They never reach the markup.
  • The attributes the component declares — :label, :icon, :class, :variant.
  • Bindings named phx_* and data_*, which are dashed into phx-click, phx-value-id, phx-target and data-confirm on the way through.

A record id, a target or a confirm message therefore travels under its binding name — :phx_value_id, :phx_target, :data_confirm — not under a name of its own. :record_id, :target and :confirm are not attributes of anything and are rendered verbatim.

MishkaGervaz.Helpers.maybe_assign/3 is the way to set a key only when it has a value, so an absent option does not become an empty attribute.

Text the resource declared

Read the label with MishkaGervaz.Helpers.action_label/1 and the confirm message with MishkaGervaz.Helpers.resolve_confirm/2: a string is translated in the caller's locale and a function is called, as every built-in type does.

Accessing State

The assigns parameter includes the full table state:

  • assigns[:state] - The table state struct
  • assigns[:state].config[:identity][:route] - Base route for the resource
  • assigns[:state].master_user? - Whether current user is master

See MishkaGervaz.Table.Types.Action (registry), MishkaGervaz.Table.Entities.RowAction, MishkaGervaz.Table.Entities.BulkAction, and MishkaGervaz.Table.Behaviours.TypeRegistry.

Summary

Callbacks

render(assigns, action, record, ui, target)

@callback render(
  assigns :: map(),
  action :: map(),
  record :: map(),
  ui :: module(),
  target :: any()
) ::
  Phoenix.LiveView.Rendered.t()

Render the action button/link.

Parameters

  • assigns - Phoenix assigns map with :state, :ui, :myself keys
  • action - Action configuration map from DSL
  • record - The record this action is for
  • ui - UI adapter module for consistent styling
  • target - LiveComponent target for phx-target

Returns

Phoenix.LiveView.Rendered.t()