defmodule Paleta.Components.Modal do use Phoenix.Component # import Phoenix.LiveView.Helpers alias Phoenix.LiveView.JS alias Paleta.Components.Icon @doc """ Renders a modal. ## Examples <.modal id="confirm-modal"> This is a modal. JS commands may be passed to the `:on_cancel` to configure the closing/cancel event, for example: <.modal id="confirm" on_cancel={JS.navigate(~p"/posts")}> This is another modal. """ attr(:id, :string, default: "modal") attr(:title, :string, required: true) attr(:show, :boolean, default: false) attr(:on_cancel, JS, default: %JS{}) attr(:width, :atom, default: :small, values: [:small, :medium, :large]) attr(:return_to, :string, default: nil) slot(:inner_block, required: true) def modal(assigns) do assigns = assigns |> assign(:on_cancel, on_cancel(assigns)) |> assign_width_class() ~H"""
""" end ## JS Commands def show(js \\ %JS{}, selector) do JS.show(js, to: selector, transition: {"transition-all transform ease-out duration-300", "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95", "opacity-100 translate-y-0 sm:scale-100"} ) end defp on_cancel(%{return_to: nil, on_cancel: on_cancel}), do: on_cancel defp on_cancel(%{return_to: return_to}) do JS.patch(return_to) end def hide(js \\ %JS{}, selector) do JS.hide(js, to: selector, time: 200, transition: {"transition-all transform ease-in duration-200", "opacity-100 translate-y-0 sm:scale-100", "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"} ) end def show_modal(js \\ %JS{}, id) when is_binary(id) do js |> JS.show(to: "##{id}") |> JS.show( to: "##{id}-bg", transition: {"transition-all transform ease-out duration-300", "opacity-0", "opacity-100"} ) |> show("##{id}-container") |> JS.add_class("overflow-hidden", to: "body") |> JS.focus_first(to: "##{id}-content") end def hide_modal(js \\ %JS{}, id) do js |> JS.hide( to: "##{id}-bg", transition: {"transition-all transform ease-in duration-200", "opacity-100", "opacity-0"} ) |> hide("##{id}-container") |> JS.hide(to: "##{id}", transition: {"block", "block", "hidden"}) |> JS.remove_class("overflow-hidden", to: "body") |> JS.pop_focus() end attr(:label, :string, default: "Cancel") attr(:modal_id, :string, default: "modal") def close_modal_button(assigns) do ~H""" <%= @label %> """ end attr(:label, :string, default: "Save") attr(:in_progress_label, :string, default: "Saving...") def save_modal_button(assigns) do ~H""" """ end attr(:label, :string, default: "Delete") attr(:in_progress_label, :string, default: "Deleting...") def delete_modal_button(assigns) do ~H""" """ end slot(:inner_block, required: true) def modal_actions(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end attr(:action, :atom, default: :edit) def default_modal_actions(assigns) do ~H""" <.close_modal_button /> <.save_modal_button :if={@action != :delete} /> <.delete_modal_button :if={@action == :delete} /> """ end def assign_width_class(%{width: width} = assigns) do class = case width do :small -> "w-full max-w-lg" :medium -> "w-2/3" :large -> "w-3/4" end assigns |> assign(:width_class, class) end end