Animating list items with LiveView streams

Nice solution :purple_heart:! Explicit server-pushed events give much more granular control than phx-mounted and phx-remove.

I think this is very correct.

Sometimes I choose to use a global event listener, so that I have a single listener that applies the action to an element given a selector, instead of multiple instances of the hook. Note that when server sent events bubble up to the window they get the phx: prefix (JavaScript interoperability — Phoenix LiveView v1.1.16).

For example, in app.js:

window.addEventListener("phx:fade_in", event => {
  const el = document.querySelector(event.detail.selector);
  if (!el) return;
  liveSocket.js().transition(el, ["ease-out duration-200", "opacity-0 scale-95", "opacity-100 scale-100"]);
});

On the server side:

    socket
    |> push_event("fade_in", %{"selector" => "#urls-#{url.id}"})

A more generic listener could even exec any set of JS commands stored in an attribute (or sent along the event payload):

window.addEventListener("phx:exec", event => {
  const el = document.querySelector(event.detail.selector);
  if (!el) return;
  liveSocket.execJS(el, event.detail.js || el.getAttribute(event.detail.attr));
});

On the server side:

<div
  id={@id}
  data-phx-show={JS.transition({"ease-out duration-200", "opacity-0 scale-95", "opacity-100 scale-100"})}
>...</div>
    socket
    |> push_event("exec", %{
      "selector" => "#urls-#{url.id}",
      "attr" => "data-phx-show"
    })

Or (see Make `JS.t()` a public data structure, or json serializable):

    socket
    |> push_event("exec", %{
      "selector" => "#urls-#{url.id}",
      "js" =>
        JS.transition({"ease-out duration-200", "opacity-0 scale-95", "opacity-100 scale-100"}).ops
        |> JSON.encode!()
    })