refactor: use core components
This commit is contained in:
parent
f0be98316c
commit
b7c93f19cb
26 changed files with 1080 additions and 954 deletions
|
|
@ -60,15 +60,15 @@ defmodule MvWeb.CoreComponents do
|
|||
id={@id}
|
||||
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
|
||||
role="alert"
|
||||
class="z-50 toast toast-top toast-end"
|
||||
class="z-50 toast toast-bottom toast-end"
|
||||
{@rest}
|
||||
>
|
||||
<div class={[
|
||||
"alert w-80 sm:w-96 max-w-80 sm:max-w-96 text-wrap",
|
||||
@kind == :info && "alert-info",
|
||||
@kind == :error && "alert-error",
|
||||
@kind == :success && "bg-green-500 text-white",
|
||||
@kind == :warning && "bg-blue-100 text-blue-800 border border-blue-300"
|
||||
@kind == :success && "alert-success",
|
||||
@kind == :warning && "alert-warning"
|
||||
]}>
|
||||
<.icon :if={@kind == :info} name="hero-information-circle" class="size-5 shrink-0" />
|
||||
<.icon :if={@kind == :error} name="hero-exclamation-circle" class="size-5 shrink-0" />
|
||||
|
|
@ -90,33 +90,71 @@ defmodule MvWeb.CoreComponents do
|
|||
@doc """
|
||||
Renders a button with navigation support.
|
||||
|
||||
## Variants (Design Guidelines §5.2)
|
||||
- primary (main CTA)
|
||||
- secondary (supporting)
|
||||
- neutral (cancel/back)
|
||||
- ghost (low emphasis; table/toolbars)
|
||||
- outline (alternative CTA)
|
||||
- danger (destructive)
|
||||
- link (inline; rare)
|
||||
- icon (icon-only)
|
||||
|
||||
## Sizes
|
||||
- sm, md (default), lg
|
||||
|
||||
## Examples
|
||||
|
||||
<.button>Send!</.button>
|
||||
<.button phx-click="go" variant="primary">Send!</.button>
|
||||
<.button navigate={~p"/"}>Home</.button>
|
||||
<.button navigate={~p"/"} variant="secondary">Home</.button>
|
||||
<.button variant="ghost" size="sm">Edit</.button>
|
||||
<.button disabled={true}>Disabled</.button>
|
||||
"""
|
||||
attr :rest, :global, include: ~w(href navigate patch method data-testid)
|
||||
attr :variant, :string, values: ~w(primary)
|
||||
|
||||
attr :variant, :string,
|
||||
values: ~w(primary secondary neutral ghost outline danger link icon),
|
||||
default: "primary"
|
||||
|
||||
attr :size, :string, values: ~w(sm md lg), default: "md"
|
||||
attr :disabled, :boolean, default: false, doc: "Whether the button is disabled"
|
||||
slot :inner_block, required: true
|
||||
|
||||
def button(assigns) do
|
||||
rest = assigns.rest
|
||||
variants = %{"primary" => "btn-primary", nil => "btn-primary btn-soft"}
|
||||
assigns = assign(assigns, :class, Map.fetch!(variants, assigns[:variant]))
|
||||
variant = assigns[:variant] || "primary"
|
||||
size = assigns[:size] || "md"
|
||||
|
||||
variant_classes = %{
|
||||
"primary" => "btn-primary",
|
||||
"secondary" => "btn-secondary",
|
||||
"neutral" => "btn-neutral",
|
||||
"ghost" => "btn-ghost",
|
||||
"outline" => "btn-outline",
|
||||
"danger" => "btn-error",
|
||||
"link" => "btn-link",
|
||||
"icon" => "btn-ghost btn-square"
|
||||
}
|
||||
|
||||
size_classes = %{
|
||||
"sm" => "btn-sm",
|
||||
"md" => "",
|
||||
"lg" => "btn-lg"
|
||||
}
|
||||
|
||||
base_class = Map.fetch!(variant_classes, variant)
|
||||
size_class = size_classes[size]
|
||||
btn_class = [base_class, size_class] |> Enum.reject(&(&1 == "")) |> Enum.join(" ")
|
||||
|
||||
assigns = assign(assigns, :btn_class, btn_class)
|
||||
|
||||
if rest[:href] || rest[:navigate] || rest[:patch] do
|
||||
# For links, we can't use disabled attribute, so we use btn-disabled class
|
||||
# DaisyUI's btn-disabled provides the same styling as :disabled on buttons
|
||||
link_class =
|
||||
if assigns[:disabled],
|
||||
do: ["btn", assigns.class, "btn-disabled"],
|
||||
else: ["btn", assigns.class]
|
||||
do: ["btn", btn_class, "btn-disabled"],
|
||||
else: ["btn", btn_class]
|
||||
|
||||
# Prevent interaction when disabled
|
||||
# Remove navigation attributes to prevent "Open in new tab", "Copy link" etc.
|
||||
link_attrs =
|
||||
if assigns[:disabled] do
|
||||
rest
|
||||
|
|
@ -138,13 +176,49 @@ defmodule MvWeb.CoreComponents do
|
|||
"""
|
||||
else
|
||||
~H"""
|
||||
<button class={["btn", @class]} disabled={@disabled} {@rest}>
|
||||
<button class={["btn", @btn_class]} disabled={@disabled} {@rest}>
|
||||
{render_slot(@inner_block)}
|
||||
</button>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Wraps content with a DaisyUI tooltip. Use for icon-only actions, truncated content,
|
||||
or status badges that need explanation (Design Guidelines §8.2).
|
||||
|
||||
## Examples
|
||||
|
||||
<.tooltip content={gettext("Edit")}>
|
||||
<.button variant="icon" size="sm"><.icon name="hero-pencil" /></.button>
|
||||
</.tooltip>
|
||||
|
||||
<.tooltip content={@full_name} position="top">
|
||||
<span class="truncate max-w-32">{@full_name}</span>
|
||||
</.tooltip>
|
||||
"""
|
||||
attr :content, :string, required: true, doc: "Tooltip text (data-tip)"
|
||||
|
||||
attr :position, :string,
|
||||
values: ~w(top bottom left right),
|
||||
default: "bottom"
|
||||
|
||||
attr :wrap_class, :string, default: nil, doc: "Additional classes for the wrapper"
|
||||
slot :inner_block, required: true
|
||||
|
||||
def tooltip(assigns) do
|
||||
position_class = "tooltip tooltip-#{assigns.position}"
|
||||
wrap_class = [position_class, assigns.wrap_class] |> Enum.reject(&is_nil/1) |> Enum.join(" ")
|
||||
|
||||
assigns = assign(assigns, :wrap_class, wrap_class)
|
||||
|
||||
~H"""
|
||||
<div class={@wrap_class} data-tip={@content}>
|
||||
{render_slot(@inner_block)}
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a dropdown menu.
|
||||
|
||||
|
|
@ -437,7 +511,7 @@ defmodule MvWeb.CoreComponents do
|
|||
{@rest}
|
||||
/>{@label}<span
|
||||
:if={@is_required}
|
||||
class="text-red-700 tooltip tooltip-right"
|
||||
class="text-error tooltip tooltip-right"
|
||||
data-tip={gettext("This field is required")}
|
||||
>*</span>
|
||||
</span>
|
||||
|
|
@ -456,7 +530,7 @@ defmodule MvWeb.CoreComponents do
|
|||
<span :if={@label} class="mb-1 label">
|
||||
{@label}<span
|
||||
:if={@rest[:required]}
|
||||
class="text-red-700 tooltip tooltip-right"
|
||||
class="text-error tooltip tooltip-right"
|
||||
data-tip={gettext("This field cannot be empty")}
|
||||
>*</span>
|
||||
</span>
|
||||
|
|
@ -485,7 +559,7 @@ defmodule MvWeb.CoreComponents do
|
|||
<span :if={@label} class="mb-1 label">
|
||||
{@label}<span
|
||||
:if={@rest[:required]}
|
||||
class="text-red-700 tooltip tooltip-right"
|
||||
class="text-error tooltip tooltip-right"
|
||||
data-tip={gettext("This field cannot be empty")}
|
||||
>*</span>
|
||||
</span>
|
||||
|
|
@ -514,7 +588,7 @@ defmodule MvWeb.CoreComponents do
|
|||
<span :if={@label} class="mb-1 label">
|
||||
{@label}<span
|
||||
:if={@rest[:required]}
|
||||
class="text-red-700 tooltip tooltip-right"
|
||||
class="text-error tooltip tooltip-right"
|
||||
data-tip={gettext("This field cannot be empty")}
|
||||
>*</span>
|
||||
</span>
|
||||
|
|
@ -585,6 +659,11 @@ defmodule MvWeb.CoreComponents do
|
|||
@doc ~S"""
|
||||
Renders a table with generic styling.
|
||||
|
||||
When `row_click` is set, clicking a row (or a data cell) triggers the handler.
|
||||
The action column has no phx-click on its `<td>`, so action buttons do not trigger row navigation.
|
||||
For interactive elements inside other columns (e.g. checkboxes, buttons), use
|
||||
`Phoenix.LiveView.JS.stop_propagation()` in the element's phx-click so the row click is not fired.
|
||||
|
||||
## Examples
|
||||
|
||||
<.table id="users" rows={@users}>
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ defmodule MvWeb.Layouts do
|
|||
|
||||
def flash_group(assigns) do
|
||||
~H"""
|
||||
<div id={@id} aria-live="polite" class="z-50 flex flex-col gap-2 toast toast-top toast-end">
|
||||
<div id={@id} aria-live="polite" class="z-50 flex flex-col gap-2 toast toast-bottom toast-end">
|
||||
<.flash kind={:success} flash={@flash} />
|
||||
<.flash kind={:warning} flash={@flash} />
|
||||
<.flash kind={:info} flash={@flash} />
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
<div
|
||||
id="flash-group-root"
|
||||
aria-live="polite"
|
||||
class="z-50 flex flex-col gap-2 toast toast-top toast-end"
|
||||
class="z-50 flex flex-col gap-2 toast toast-bottom toast-end"
|
||||
>
|
||||
<.flash id="flash-success-root" kind={:success} flash={@flash} />
|
||||
<.flash id="flash-warning-root" kind={:warning} flash={@flash} />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue