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"""
"""
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