PhoenixKitWeb.Components.Core.SortSelector (phoenix_kit v1.7.115)

Copy Markdown View Source

Sort bar for list LiveViews — a field-picker <.select> plus a direction- toggle button (chevron up / down). No wrapper chrome so it integrates cleanly into a <.table_default toggleable> :toolbar_title slot.

How events fire

Race-free by design. Each control sends ONLY the field it controls:

  • Field <.select> fires phx-change with params == %{"sort_by" => "..."}
  • Direction button fires phx-click with params == %{"sort_dir" => "..."}

The LV handler derives the missing field from socket.assigns instead of trusting stale DOM — so clicking the arrow while a change event is mid- flight can never clobber the in-flight change.

def handle_event("sort_form", params, socket) do
  field_str = params["sort_by"] || Atom.to_string(socket.assigns.sort_by)
  dir_str   = params["sort_dir"] || Atom.to_string(socket.assigns.sort_dir)
  # cast / validate, then push_patch with both values
  ...
end

Loading state

The button shows a spinner during in-flight clicks via Phoenix's auto- applied .phx-click-loading class (no consumer code required). When the field select fires, the form gets .phx-change-loading and the select dims via the same mechanism. Both states fade automatically when the LV acks the event.

Attributes

  • sort_by — Current sort field. Accepts atom or string. Required.
  • sort_dir — Current direction (:asc | :desc | "asc" | "desc"). Anything else falls back to :asc. Required.

  • options — List of {field, label} tuples for the field select. field may be atom or string; label is coerced via to_string/1. Bad rows (non-tuple) are silently dropped. Empty list renders an empty <select>. Required.
  • event — Phoenix event name fired on both field change and direction flip. Default "sort_form".
  • target — Optional phx-target for LiveComponents.
  • class — Extra classes on the inner <form> element.

Edge cases handled

  • Atom-or-string inputs: sort_by and sort_dir are normalised internally; the consumer doesn't need to convert.
  • Unknown direction: any value outside the known set renders as :asc (conservative — surfaces a stable icon instead of silently rendering as descending on bad input).
  • Bad option shape: non-tuple rows skipped; atom-or-string labels coerced. One bad row doesn't blow up the whole select.
  • Nil / wrong type options: returns empty list, doesn't crash.

Example

<.sort_selector
  sort_by={@sort_by}
  sort_dir={@sort_dir}
  options={@sort_options}
/>

Summary

Functions

sort_selector(assigns)

Attributes

  • sort_by (:any) (required) - Current sort field (atom or string).
  • sort_dir (:any) (required) - Current direction (:asc/:desc or 'asc'/'desc').
  • options (:list) (required) - List of {field, label} tuples — field may be atom or string.
  • event (:string) - Defaults to "sort_form".
  • target (:any) - Defaults to nil.
  • class (:string) - Defaults to nil.
  • manual_field (:any) - Atom or string field key that represents "manual" ordering (e.g. :sort_order). When sort_by matches, the direction toggle is replaced by a static drag-handle hint icon — direction has no meaning for a user-specified order. Defaults to nil.