Current section

Files

Jump to
moon lib moon design bottom_sheet.ex
Raw

lib/moon/design/bottom_sheet.ex

defmodule Moon.Design.BottomSheet do
@moduledoc false
use Moon.StatefulComponent
@doc "Whether the BottomSheet is open or not"
prop(is_open, :boolean, default: false)
@doc "Called when the BottomSheet is dismissed"
prop(on_close, :event)
@doc """
Deprecated: Whether the BottomSheet has a shadow or not
"""
prop(has_shadow, :boolean, default: false)
@doc """
Deprecated: The size of the BottomSheet, sm, md, lg or custom pixel/percentage/rem value
"""
prop(size, :string,
values!: [
"sm",
"md",
"lg"
],
default: "sm"
)
@doc "Tailwind classes for customization"
prop(class, :css_class)
@doc "Data-testid attribute for DOM element"
prop(testid, :string)
@doc """
Experimental: makes BottomSheet behave as Modal on some screen widths,
please reffer to https://tailwindcss.com/docs/screens
"""
prop(as_modal_on, :string, values: ~w(sm md lg xl 2xl))
@doc "Panel of BottomSheet, see BottomSheet.Panel"
slot(panel, required: true)
@doc "Backdrop of BottomSheet, see BottomSheet.Backdrop"
slot(backdrop)
# internal API
@doc "Internal API, used for transition of the element"
data(is_closing, :boolean, default: false)
def render(assigns) do
~F"""
<div
{=@id}
phx-hook="Bottomsheet"
data-is_open={@is_open}
data-is_closing={"#{@is_closing}"}
class={merge([
"fixed z-[99999] inset-0 hidden",
modal_classes(@as_modal_on),
@class
]) <> " flex"}
data-testid={@testid}
>
<#slot {@backdrop} />
<#slot
{@panel}
context_put={
on_close: @on_close || %{name: "start_closing_bottom_sheet", target: @myself},
has_shadow: @has_shadow,
as_modal_on: @as_modal_on,
size: @size
}
/>
</div>
"""
end
def open(bottom_sheet_id) do
send_update(__MODULE__, id: bottom_sheet_id, is_open: true)
end
def close(bottom_sheet_id) do
send_update(__MODULE__, id: bottom_sheet_id, is_closing: true)
end
def handle_event("set_open", _, socket) do
{:noreply, assign(socket, is_open: true)}
end
def handle_event("start_closing_bottom_sheet", _, socket) do
{:noreply, assign(socket, is_closing: true)}
end
def handle_event("stop_closing", _, socket) do
{:noreply, assign(socket, is_closing: false)}
end
def handle_event("set_close", _, socket) do
{:noreply, assign(socket, is_open: false, is_closing: false)}
end
defp modal_classes(nil), do: []
defp modal_classes(size) do
~w(hidden items-center justify-center) |> Enum.map(&"#{size}:#{&1}")
end
end