defmodule Phoenix.UI.Components.Card do
@moduledoc """
Provides card component.
"""
import Phoenix.UI.Components.{Avatar, AvatarGroup, Paper, Typography}
use Phoenix.UI, :component
attr(:element, :string, default: "div")
attr(:extend_class, :string)
attr(:rest, :global)
slot(:inner_block, required: true)
@doc """
Renders card component.
## Examples
```
<.card
elevation={3}
square={true}
variant="elevated"
>
content
</.card>
```
"""
@spec card(Socket.assigns()) :: Rendered.t()
def card(assigns) do
extend_class = build_class(~w(
card overflow-hidden
#{Map.get(assigns, :extend_class)}
))
assigns = assign(assigns, :extend_class, extend_class)
~H"""
<.paper extend_class={@extend_class} element={@element} {@rest}>
<%= render_slot(@inner_block) %>
</.paper>
"""
end
attr(:element, :string, default: "div")
attr(:extend_class, :string)
attr(:rest, :global)
slot(:action)
slot(:avatar_group)
slot(:avatar)
slot(:subtitle)
slot(:title)
@doc """
Renders card header component.
## Examples
```
<.card>
<.card_header>
header
</.card_header>
...
</.card>
```
"""
@spec card_header(Socket.assigns()) :: Rendered.t()
def card_header(assigns) do
assigns =
assigns
|> assign_class(~w(
card-header flex items-center p-4
))
|> normalize_card_header_action()
|> normalize_card_header_avatar_group()
|> normalize_card_header_avatar()
|> normalize_card_header_subtitle()
|> normalize_card_header_title()
~H"""
<.dynamic_tag class={@class} name={@element} {@rest}>
<%= for avatar <- @avatar do %>
<.avatar {avatar} />
<% end %>
<%= for avatar_group <- @avatar_group do %>
<.avatar_group {avatar_group} />
<% end %>
<div class="flex-1">
<%= for title <- @title do %>
<.typography {title}>
<%= render_slot(title) %>
</.typography>
<% end %>
<%= for subtitle <- @subtitle do %>
<.typography {subtitle}>
<%= render_slot(subtitle) %>
</.typography>
<% end %>
</div>
<%= for action <- @action do %>
<%= render_slot(action) %>
<% end %>
</.dynamic_tag>
"""
end
attr(:element, :string, default: "img")
attr(:extend_class, :string)
attr(:rest, :global)
@doc """
Renders card media component.
## Examples
```
<.card>
...
<.card_media/>
...
</.card>
```
"""
@spec card_media(Socket.assigns()) :: Rendered.t()
def card_media(assigns) do
assigns = assign_class(assigns, ~w(
card-media block bg-cover bg-no-repeat bg-center object-cover w-full
))
~H"""
<.dynamic_tag class={@class} name={@element} {@rest} />
"""
end
attr(:element, :string, default: "div")
attr(:extend_class, :string)
attr(:rest, :global)
@doc """
Renders card content component.
## Examples
```
<.card>
...
<.card_content>
content
</.card_content>
...
</.card>
```
"""
@spec card_content(Socket.assigns()) :: Rendered.t()
def card_content(assigns) do
assigns = assign_class(assigns, ~w(
card-content p-4
))
~H"""
<.dynamic_tag class={@class} name={@element} {@rest}>
<%= render_slot(@inner_block) %>
</.dynamic_tag>
"""
end
attr(:element, :string, default: "div")
attr(:extend_class, :string)
attr(:rest, :global)
@doc """
Renders card actions component.
## Examples
```
<.card>
...
<.card_action>
content
</.card_action>
...
</.card>
```
"""
@spec card_action(Socket.assigns()) :: Rendered.t()
def card_action(assigns) do
assigns = assign_class(assigns, ~w(
card-action p-4
))
~H"""
<.dynamic_tag class={@class} name={@element} {@rest}>
<%= render_slot(@inner_block) %>
</.dynamic_tag>
"""
end
### Normalize Card Header Avatar Group ##########################
defp normalize_card_header_avatar_group(%{avatar_group: [avatar_group | _]} = assigns) do
extend_class = build_class(~w(
card-header-avatar_group mr-4
#{Map.get(avatar_group, :extend_class)}
))
assign(assigns, :avatar_group, [Map.put(avatar_group, :extend_class, extend_class)])
end
defp normalize_card_header_avatar_group(assigns), do: assigns
### Normalize Card Header Avatar ##########################
defp normalize_card_header_avatar(%{avatar: [avatar | _]} = assigns) do
extend_class = build_class(~w(
card-header-avatar mr-4
#{Map.get(avatar, :extend_class)}
))
assign(assigns, :avatar, [Map.put(avatar, :extend_class, extend_class)])
end
defp normalize_card_header_avatar(assigns), do: assigns
### Normalize Card Header Title ##########################
defp normalize_card_header_title(%{title: [title | _]} = assigns) do
extend_class = build_class(~w(
card-header-title
#{Map.get(title, :extend_class)}
))
slot =
title
|> Map.put_new(:align, "left")
|> Map.put_new(:margin, false)
|> Map.put_new(:variant, "p")
|> Map.put_new(:element, "h3")
|> Map.put(:extend_class, extend_class)
assign(assigns, :title, [slot])
end
defp normalize_card_header_title(assigns), do: assigns
### Normalize Card Header Subtitle ##########################
defp normalize_card_header_subtitle(%{subtitle: [subtitle | _]} = assigns) do
extend_class = build_class(~w(
card-header-subtitle
#{Map.get(subtitle, :extend_class)}
))
slot =
subtitle
|> Map.put_new(:align, "left")
|> Map.put_new(:margin, false)
|> Map.put_new(:variant, "p")
|> Map.put(:extend_class, extend_class)
assign(assigns, :subtitle, [slot])
end
defp normalize_card_header_subtitle(assigns), do: assigns
### Normalize Card Header Action ##########################
defp normalize_card_header_action(%{action: [action | _]} = assigns) do
assign(assigns, :action, [action])
end
defp normalize_card_header_action(assigns), do: assigns
end