defmodule ExshomeWeb.Live.Modal do @moduledoc """ Adds modal support for every live page. """ alias Phoenix.LiveView alias Phoenix.LiveView.JS alias Phoenix.LiveView.Socket defstruct [:module, :params] @type t() :: %__MODULE__{ module: module(), params: %{String.t() => String.t()} } @type js_t() :: %JS{ops: list()} def on_mount(:default, _params, _session, %Socket{} = socket) do socket = socket |> LiveView.attach_hook(:modal_hook, :handle_event, &__MODULE__.handle_event/3) |> LiveView.attach_hook(:modal_hook, :handle_info, &__MODULE__.handle_info/2) |> LiveView.assign_new(:modal, fn -> nil end) {:cont, socket} end def handle_event("modal:close", _params, %Socket{} = socket) do {:halt, close_modal(socket)} end def handle_event(_, _params, %Socket{} = socket), do: {:cont, socket} def handle_info(:close_modal, %Socket{} = socket) do {:halt, LiveView.assign(socket, :modal, nil)} end def handle_info(_, %Socket{} = socket), do: {:cont, socket} @spec open_modal(Socket.t(), module(), map()) :: Socket.t() def open_modal(%Socket{} = socket, module, params \\ %{}) when is_atom(module) do socket |> LiveView.assign(:modal, %__MODULE__{module: module, params: params}) |> send_js(opening_transition()) end @spec close_modal(Socket.t()) :: Socket.t() def close_modal(%Socket{} = socket) do socket = send_js(socket, closing_transition()) socket |> modal_view_pid() |> send(:close_modal) socket end @spec send_js(Socket.t(), js_t()) :: Socket.t() def send_js(%Socket{} = socket, %JS{ops: ops}) do LiveView.push_event(socket, "js-event", %{data: Jason.encode!(ops)}) end @spec modal_view_pid(Socket.t()) :: pid() defp modal_view_pid(%Socket{parent_pid: nil}), do: self() defp modal_view_pid(%Socket{parent_pid: pid}) when is_pid(pid), do: pid defp opening_transition do %JS{} |> JS.show(to: "#modal", transition: "fade-in") |> JS.show(to: "#modal-content", transition: "fade-in-scale") end defp closing_transition do %JS{} |> JS.hide(to: "#modal", transition: "fade-out") |> JS.show(to: "#modal-content", transition: "fade-out-scale") end end