defmodule Shino.UI.Dialog do @moduledoc """ Provides dialog related components. > Displays content underneath inert. ## Related components * `Shino.UI.Sheet` ## Availale components Components are divided into two categories: control components and style components. Control components: * `` * `` * `` * `` * `` Style components: * `` * `` * `` * `` ## Opening and closing a sheet For opening a sheet, you can use: * `:default_open` attr of `` component * `` component For closing a sheet, you can use: * `` component * `` component * the builtin user interactions: * clicking outside of the dialog * pressing ESC key ## Examples Use with a trigger: ```heex <.button>Open profile diaglog Edit profile Make changes to your profile here, and click save when you're done.
<% # ... %>
<.button type="submit">Save
``` Use with routing: ```heex Edit profile Make changes to your profile here, and click save when you're done.
<% # ... %>
<.button type="submit">Save
``` ## References * [shadcn/ui - Dialog](https://ui.shadcn.com/docs/components/dialog) * [@radix-ui/primitives - Dialog](https://www.radix-ui.com/primitives/docs/components/dialog) """ use Shino.UI, :component defmodule Root do @moduledoc false defstruct [:id, :on_cancel] end @doc """ The root contains all the parts of a dialog. """ attr :id, :string, required: true attr :default_open, :boolean, default: false attr :on_cancel, JS, default: %JS{} attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def root(assigns) do ~H"""
JS.exec("data-hide")} phx-mounted={@default_open && JS.exec("data-show")} phx-remove={JS.exec("data-hide")} class={@class} {@rest} > <%= render_slot(@inner_block, %Root{id: @id, on_cancel: @on_cancel}) %>
""" end @doc """ Renders a sheet trigger. """ attr :for, Root, required: true attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def trigger(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end @doc """ Renders a dialog content. """ attr :for, Root, required: true attr :class, :string, default: nil slot :inner_block, required: true def content(assigns) do ~H"""
""" end @doc """ Renders a dialog close. """ attr :for, Root, required: true attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def close(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end @doc """ Renders a default close for dialog. As its name implied, it's a default component, so it doesn't accept any kind of customization. """ attr :for, Root, required: true def default_close(assigns) do ~H""" """ end @doc """ Renders a dialog header. """ attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def header(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end @doc """ Renders a dialog title. """ attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def title(assigns) do ~H"""

<%= render_slot(@inner_block) %>

""" end @doc """ Renders a dialog description. """ attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def description(assigns) do ~H"""

<%= render_slot(@inner_block) %>

""" end @doc """ Renders a dialog footer. """ attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def footer(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end defp show_dialog(js \\ %JS{}, id) do selector_content = "##{id}-content" selector_overlay = "##{id}-overlay" selector_dialog = "##{id}-dialog" transition_overlay = {"transition-all transform ease-in-out duration-300", "opacity-0", "opacity-100"} transition_dialog = {"transition-all transform ease-in-out duration-300", "translate-y-4 opacity-0", "translate-y-0 opacity-100"} js |> JS.remove_class("hidden", to: selector_content) |> JS.transition(transition_overlay, to: selector_overlay, time: 300) |> JS.transition(transition_dialog, to: selector_dialog, time: 300) |> JS.add_class("overflow-hidden", to: "body") |> JS.focus_first(to: selector_dialog) end defp hide_dialog(js \\ %JS{}, id) do selector_content = "##{id}-content" selector_overlay = "##{id}-overlay" selector_dialog = "##{id}-dialog" transition_overlay = {"transition-all transform ease-in-out duration-200", "opacity-100", "opacity-0"} transition_dialog = {"transition-all transform ease-in-out duration-200", "translate-y-0 opacity-100", "translate-y-4 opacity-0"} js |> JS.add_class("hidden", to: selector_content, transition: {"duration-200", "", ""}, time: 200 ) |> JS.transition(transition_overlay, to: selector_overlay, time: 200) |> JS.transition(transition_dialog, to: selector_dialog, time: 200) |> JS.remove_class("overflow-hidden", to: "body") |> JS.pop_focus() end end