"""
end
@doc """
Shows the flash group with standard titles and content.
## Examples
<.flash_group flash={@flash} />
"""
attr :flash, :map, required: true, doc: "the map of flash messages"
def flash_group(assigns) do
~H"""
"""
end
@doc """
Renders a message notice.
Similar to `flash/1`, but for permanent messages on the page.
## Examples
<.message_box kind={:info} message="🦊 in a 📦" />
<.message_box kind={:info}>
🦊 in a 📦
"""
attr :message, :string, default: nil
attr :kind, :atom, values: [:info, :success, :warning, :error]
slot :inner_block
def message_box(assigns) do
~H"""
<%= @message %>
<%= render_slot(@inner_block) %>
"""
end
@doc """
Creates a live region with the given role.
## Examples
<.live_region role="alert" />
<.live_region role="status" />
"""
def live_region(assigns) do
~H"""
"""
end
@doc """
Sends a message to be read by the screen reader by changing the text content of the live region
"""
def sr_message(js \\ %JS{}, message) do
JS.dispatch(js, "lb:set_text", to: "#live-region", detail: %{value: message})
end
@doc """
Wraps the given content in a modal dialog.
## Example
<.modal id="edit-modal" patch={...}>
<.live_component module={MyComponent} />
"""
attr :id, :string, required: true
attr :show, :boolean, default: false
attr :patch, :string, default: nil
attr :navigate, :string, default: nil
attr :class, :string, default: nil
attr :width, :atom, values: [:small, :medium, :big, :large], required: true
attr :rest, :global
slot :inner_block, required: true
def modal(assigns) do
~H"""
"""
end
@doc """
Renders text with a tiny label.
## Examples
<.labeled_text label="Name">Sherlock Holmes
"""
attr :label, :string, required: true
attr :one_line, :boolean,
default: false,
doc: "whether to force the text into a single scrollable line"
attr :class, :string, default: nil
slot :inner_block, required: true
def labeled_text(assigns) do
~H"""
<%= @label %>
<%= render_slot(@inner_block) %>
"""
end
@doc """
Renders a choice button that is either active or not.
## Examples
<.choice_button active={@tab == "my_tab"} phx-click="set_my_tab">
My tab
"""
attr :active, :boolean, required: true
attr :disabled, :boolean
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def choice_button(assigns) do
assigns =
assigns
|> assign_new(:disabled, fn -> assigns.active end)
~H"""
"""
end
@doc """
Renders an status indicator circle.
"""
attr :variant, :atom,
required: true,
values: [:success, :warning, :error, :inactive, :waiting, :progressing]
def status_indicator(assigns) do
~H"""
"""
end
@doc """
Returns background class based on the given variant.
See `status_indicator/1` for available variants.
"""
def status_circle_class(variant)
def status_circle_class(:success), do: "bg-green-bright-400"
def status_circle_class(:warning), do: "bg-yellow-bright-200"
def status_circle_class(:error), do: "bg-red-400"
def status_circle_class(:inactive), do: "bg-gray-500"
def status_circle_class(:waiting), do: "bg-gray-400"
def status_circle_class(:progressing), do: "bg-blue-500"
defp animated_status_circle_class(:waiting), do: "bg-gray-300"
defp animated_status_circle_class(:progressing), do: "bg-blue-400"
defp animated_status_circle_class(_other), do: nil
@doc """
Renders an informative box as a placeholder for a list.
"""
slot :inner_block, required: true
slot :actions
def no_entries(assigns) do
~H"""
"""
end
@doc """
Renders a circular spinner.
"""
attr :class, :string, default: nil
attr :rest, :global
def spinner(assigns) do
~H"""
"""
end
@doc """
Returns the text in singular or plural depending on the quantity.
## Examples
<.listing items={@packages}>
<:item :let={package}><%= package.name %>
<:singular_suffix>package
<:plural_suffix>packages
"""
attr :items, :list, required: true
slot :item, required: true
slot :plural_suffix
slot :singular_suffix
def listing(%{items: [_]} = assigns) do
~H"""
<%= render_slot(@item, hd(@items)) %>
<%= render_slot(@singular_suffix) %>
"""
end
def listing(%{items: [_, _ | _]} = assigns) do
{items, assigns} = Map.pop!(assigns, :items)
{leading, [second_to_last, last]} = Enum.split(items, -2)
assigns = assign(assigns, leading: leading, second_to_last: second_to_last, last: last)
~H"""
<%= for item <- @leading do %>
<%= render_slot(@item, item) %>,
<% end %>
<%= render_slot(@item, @second_to_last) %> and <%= render_slot(@item, @last) %>
<%= render_slot(@plural_suffix) %>
"""
end
@doc ~S"""
Renders a table with generic styling.
## Examples
<.table id="users" rows={@users}>
<:col :let={user} label="id"><%= user.id %>
<:col :let={user} label="username"><%= user.username %>
"""
attr :id, :string, required: true
attr :rows, :list, required: true
attr :row_id, :any, default: nil, doc: "the function for generating the row id"
attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"
attr :row_item, :any,
default: &Function.identity/1,
doc: "the function for mapping each row before calling the :col and :action slots"
slot :col, required: true do
attr :label, :string
end
slot :action, doc: "the slot for showing user actions in the last table column"
def table(assigns) do
assigns =
with %{rows: %Phoenix.LiveView.LiveStream{}} <- assigns do
assign(assigns, row_id: assigns.row_id || fn {id, _item} -> id end)
end
~H"""
<%= col[:label] %>
Actions
<%= render_slot(col, @row_item.(row)) %>
<%= render_slot(action, @row_item.(row)) %>
"""
end
@doc ~S"""
Renders a button.
## Examples
<.button>Click
<.button color="gray" outlined>Click
<.button color="gray" small>Click
"""
attr :disabled, :boolean, default: false
attr :color, :string, default: "blue", values: ~w(blue gray red)
attr :outlined, :boolean, default: false
attr :small, :boolean, default: false
attr :class, :string, default: nil
attr :rest, :global, include: ~w(href patch navigate download name)
slot :inner_block
def button(assigns)
when is_map_key(assigns.rest, :href) or is_map_key(assigns.rest, :patch) or
is_map_key(assigns.rest, :navigate) do
~H"""
<.link class={[button_classes(@small, @disabled, @color, @outlined), @class]} {@rest}>
<%= render_slot(@inner_block) %>
"""
end
def button(assigns) do
~H"""
"""
end
defp button_classes(small, disabled, color, outlined) do
[
if small do
"px-2 py-1 font-normal text-xs"
else
"px-5 py-2 font-medium text-sm"
end,
"inline-flex rounded-lg border whitespace-nowrap items-center justify-center gap-1.5",
if disabled do
"cursor-default pointer-events-none border-transparent bg-gray-100 text-gray-400"
else
case {color, outlined} do
{"blue", false} ->
"border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700"
{"red", false} ->
"border-transparent bg-red-600 text-white hover:bg-red-700 focus:bg-red-700"
{"gray", false} ->
"border-gray-200 bg-gray-100 text-gray-600 hover:bg-gray-200 focus:bg-gray-200"
{"blue", true} ->
"bg-blue-50 border-blue-600 text-blue-600 hover:bg-blue-100 focus:bg-blue-100"
{"red", true} ->
"bg-red-50 border-red-600 text-red-600 hover:bg-red-100 focus:bg-red-100"
{"gray", true} ->
"bg-transparent border-gray-300 text-gray-600 hover:bg-gray-100 focus:bg-gray-100"
end
end
]
end
@doc ~S"""
Renders an icon button.
## Examples
<.icon_button>
<.remix_icon icon="refresh-line" />
"""
attr :disabled, :boolean, default: false
attr :small, :boolean, default: false
attr :class, :string, default: nil
attr :rest, :global, include: ~w(href patch navigate download name)
slot :inner_block
def icon_button(assigns)
when is_map_key(assigns.rest, :href) or is_map_key(assigns.rest, :patch) or
is_map_key(assigns.rest, :navigate) do
~H"""
<.link class={[icon_button_classes(@small, @disabled), @class]} {@rest}>
<%= render_slot(@inner_block) %>
"""
end
def icon_button(assigns) do
~H"""
"""
end
defp icon_button_classes(small, disabled) do
[
unless small do
"text-xl"
end,
"p-1 flex items-center justify-center rounded-full leading-none",
if disabled do
"cursor-default text-gray-300"
else
"text-gray-500 hover:text-gray-900 focus:bg-gray-100"
end
]
end
# JS commands
@doc """
Toggles classes on elements.
"""
def toggle_class(js \\ %JS{}, names, opts \\ []) do
opts = Keyword.validate!(opts, [:to])
to = Keyword.fetch!(opts, :to)
names
|> String.split()
|> Enum.reduce(js, fn name, js ->
js
|> JS.remove_class(name, to: "#{to}.#{name}")
|> JS.add_class(name, to: "#{to}:not(.#{name})")
end)
end
@doc """
Pushes and executes the given `%Phoenix.LiveView.JS{}` on the client.
## Options
* `:to` - selector for elements to execute against. Defaults to
document body
"""
def exec_js(socket, js, opts \\ []) do
opts = Keyword.validate!(opts, [:to])
Phoenix.LiveView.push_event(socket, "lb:exec_js", %{js: Jason.encode!(js.ops), to: opts[:to]})
end
@doc """
Encodes value for hook prop attribute.
## Examples
"""
def hook_prop(value)
def hook_prop(%Phoenix.LiveComponent.CID{} = value) do
hook_prop(to_string(value))
end
def hook_prop(value) do
Jason.encode!(value)
end
end