defmodule PhoenixDuskmoon.Component.DataDisplay.Card do @moduledoc """ Card component using el-dm-card custom element. Provides card containers with optional header, content, and footer sections. ## Examples <.dm_card> <:title>Star Wars Star Wars is an American epic space opera... <.dm_card image="/poster.jpg"> <:title>Movie Title <:action><.dm_btn>Watch """ use Phoenix.Component import PhoenixDuskmoon.Component.DataEntry.Form, only: [dm_alert: 1] @doc """ Generates a card container. ## Examples <.dm_card> <:title>Card Title Card content here. <:action><.dm_btn>Action """ @doc type: :component attr(:id, :any, default: nil, doc: "HTML id attribute") attr(:class, :any, default: nil, doc: "Additional CSS classes") attr(:body_class, :any, default: nil, doc: "CSS classes for card body") attr(:variant, :string, default: nil, values: [nil, "compact", "side", "bordered", "glass"], doc: "Card layout variant" ) attr(:shadow, :string, default: nil, values: [nil, "none", "sm", "md", "lg", "xl", "2xl"], doc: "Card shadow size" ) attr(:interactive, :boolean, default: false, doc: "Make card clickable/hoverable" ) attr(:padding, :string, default: nil, values: [nil, "none", "sm", "md", "lg"], doc: "Card padding size" ) attr(:image, :string, default: nil, doc: "Card image URL") attr(:image_alt, :string, default: "", doc: "Card image alt text") attr(:rest, :global) slot(:title, required: false, doc: "Card title content" ) do attr(:id, :any, doc: "Title element id") attr(:class, :any, doc: "Title CSS classes") end slot(:action, required: false, doc: "Card action buttons" ) do attr(:id, :any, doc: "Action container id") attr(:class, :any, doc: "Action container CSS classes") end slot(:inner_block, required: false, doc: "Card body content") def dm_card(assigns) do ~H""" {@image_alt} {render_slot(title)} <%= if @body_class do %>
{render_slot(@inner_block)}
<% else %> {render_slot(@inner_block)} <% end %> {render_slot(action)}
""" end @doc """ Renders a card with async value support. Shows loading skeleton while data is being fetched, error state on failure, and renders content when data is available. ## Examples <.dm_async_card :let={data} assign={@data}> <:title>User Profile {data.name} """ @doc type: :component attr(:id, :any, default: nil, doc: "HTML id attribute") attr(:class, :any, default: nil, doc: "additional CSS classes for the card") attr(:body_class, :any, default: nil, doc: "additional CSS classes for the card body") attr(:skeleton_class, :any, default: nil, doc: "CSS classes for skeleton loader") attr(:assign, :any, default: nil, doc: "Phoenix.LiveView.AsyncResult assign") attr(:variant, :string, default: nil, doc: "card style variant") attr(:shadow, :string, default: nil, doc: "card shadow depth") attr(:interactive, :boolean, default: false, doc: "make card clickable/hoverable") attr(:padding, :string, default: nil, doc: "card padding size") attr(:image, :string, default: nil, doc: "card image URL") attr(:image_alt, :string, default: "", doc: "alt text for the card image") attr(:rest, :global) slot(:inner_block, required: true, doc: "card body content") slot(:title, required: false, doc: "card title content") do attr(:id, :any, doc: "title element id") attr(:class, :any, doc: "title CSS classes") end slot(:action, required: false, doc: "card action buttons") do attr(:id, :any, doc: "action container id") attr(:class, :any, doc: "action container CSS classes") end def dm_async_card(assigns) do ~H""" <.async_result assign={@assign}> <:loading>
{render_slot(title)}
<:failed :let={reason}> {render_slot(title)} <.dm_alert variant="error"> {reason |> inspect()} {@image_alt} {render_slot(title)} <%= if @body_class do %>
{render_slot(@inner_block, Map.get(@assign, :result))}
<% else %> {render_slot(@inner_block, Map.get(@assign, :result))} <% end %> {render_slot(action, Map.get(@assign, :result))}
""" end end