Skip to main content

lib/shadix/components/alert.ex

defmodule Shadix.Components.Alert do
  @moduledoc """
  Alert component translated from the shadcn-ui "alert" component.

  Provides `alert/1`, `alert_title/1`, and `alert_description/1` as
  presentational Phoenix function components.
  """
  use Phoenix.Component
  import Shadix.Cn

  @base "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current"

  @variants %{
    "default" => "bg-card text-card-foreground",
    "destructive" =>
      "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current"
  }

  attr(:variant, :string, default: "default", values: ~w(default destructive))
  attr(:class, :string, default: nil)
  attr(:rest, :global)
  slot(:inner_block, required: true)

  def alert(assigns) do
    class = cn([@base, Map.fetch!(@variants, assigns.variant), assigns.class])
    assigns = assign(assigns, :computed_class, class)

    ~H"""
    <div role="alert" data-slot="alert" data-variant={@variant} class={@computed_class} {@rest}>
      {render_slot(@inner_block)}
    </div>
    """
  end

  attr(:class, :string, default: nil)
  attr(:rest, :global)
  slot(:inner_block, required: true)

  def alert_title(assigns) do
    class = cn(["col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", assigns.class])
    assigns = assign(assigns, :computed_class, class)

    ~H"""
    <div data-slot="alert-title" class={@computed_class} {@rest}>
      {render_slot(@inner_block)}
    </div>
    """
  end

  attr(:class, :string, default: nil)
  attr(:rest, :global)
  slot(:inner_block, required: true)

  def alert_description(assigns) do
    class =
      cn([
        "col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed",
        assigns.class
      ])

    assigns = assign(assigns, :computed_class, class)

    ~H"""
    <div data-slot="alert-description" class={@computed_class} {@rest}>
      {render_slot(@inner_block)}
    </div>
    """
  end
end