|
defmodule AdminUI.Card do |
|
@moduledoc """ |
|
Implement of card components from https://ui.shadcn.com/docs/components/card |
|
""" |
|
use AdminUI.Component |
|
use Gettext, backend: Octafest.Gettext |
|
|
|
import AdminUI.Icon |
|
import AdminUI.Typography |
|
import AdminUI.Menu |
|
import AdminUI.DropdownMenu |
|
import AdminUI.Button |
|
|
|
@doc """ |
|
Render dropdown menu |
|
|
|
|
|
## Examples: |
|
|
|
<.card id={@user.id} action={[{:patch, ~q"/"}, {:sr_label, dgettext("admin", "Say hello") ]} > |
|
<.card_header> |
|
<.card_accent_text>Title</.card_accent_text> |
|
<.card_menu> |
|
<.card_menu_action icon="lucide-settings" patch={~q"/"}> |
|
<%= dgettext("admin", "Settings") %> |
|
</.card_menu_action> |
|
</.card_menu> |
|
</.card_header> |
|
|
|
<.card_footer> |
|
<.card_muted_text> |
|
<%= dgettext("admin", "Edited %{time}", time: @user.updated_at) %> |
|
</.card_muted_text> |
|
</.card_footer> |
|
</.card> |
|
""" |
|
attr :id, :string, default: nil |
|
attr :action, :list, default: nil |
|
attr :rest, :global |
|
|
|
slot :inner_block, required: true |
|
|
|
@spec card(map()) :: Phoenix.LiveView.Rendered.t() |
|
def card(assigns) do |
|
~H""" |
|
<li |
|
id={"card-#{@id}"} |
|
class="group/card rounded-xl gap-2 bg-background relative grid [grid-template-areas:'header_menu''main_main''footer_action'] grid-rows-auto grid-cols-[1fr_min-content] border-2 border-transparent p-4 hovered-action:border-accent pressed-action:bg-accent shadow-md hovered-action:shadow-accent shadow-primary/5" |
|
{@rest} |
|
> |
|
<.card_action :if={@action} {@action} /> |
|
<%= render_slot(@inner_block) %> |
|
</li> |
|
""" |
|
end |
|
|
|
slot :inner_block, required: true |
|
|
|
@spec card_accent_text(map()) :: Phoenix.LiveView.Rendered.t() |
|
def card_accent_text(assigns) do |
|
~H""" |
|
<div class="text-foreground group-hovered-action/card:text-accent-foreground"> |
|
<%= render_slot(@inner_block) %> |
|
</div> |
|
""" |
|
end |
|
|
|
slot :inner_block, required: true |
|
|
|
@spec card_muted_text(map()) :: Phoenix.LiveView.Rendered.t() |
|
def card_muted_text(assigns) do |
|
~H""" |
|
<div class="text-muted-foreground group-hovered-action/card:text-foreground"> |
|
<%= render_slot(@inner_block) %> |
|
</div> |
|
""" |
|
end |
|
|
|
attr :sr_label, :string, required: true |
|
attr :label, :string, default: nil |
|
attr :class, :any, default: nil |
|
|
|
attr :rest, :global, |
|
include: |
|
~w(navigate patch href replace method csrf_token download hreflang referrerpolicy rel target type) |
|
|
|
def card_action(assigns) do |
|
~H""" |
|
<.link |
|
{@rest} |
|
data-action |
|
class="[grid-area:action] justify-self-end self-end before:absolute before:inset-0 hovered:outline-none hovered:ring-0" |
|
> |
|
<div class="relative"><span class="sr-only"><%= @sr_label %></span></div> |
|
<div class="text-muted-foreground pointer-events-none underline group-hovered-action/card:text-accent-foreground flex items-center"> |
|
<.text text={@label || dgettext("admin", "View")} /> |
|
<.icon icon="lucide-chevron-right" class="size-4" /> |
|
</div> |
|
</.link> |
|
""" |
|
end |
|
|
|
slot :inner_block, required: true |
|
|
|
def card_menu(assigns) do |
|
~H""" |
|
<.dropdown_menu class="[grid-area:menu] justify-self-end opacity-5 group-hovered/card:opacity-100 z-10"> |
|
<.dropdown_menu_trigger> |
|
<.button variant="btn-outline" size="btn-icon" class="overflow-hidden rounded-full"> |
|
<.icon icon="lucide-settings" class="size-5" /> |
|
</.button> |
|
</.dropdown_menu_trigger> |
|
<.dropdown_menu_content side="bottom" align="end"> |
|
<.menu> |
|
<.menu_group> |
|
<%= render_slot(@inner_block) %> |
|
</.menu_group> |
|
</.menu> |
|
</.dropdown_menu_content> |
|
</.dropdown_menu> |
|
""" |
|
end |
|
|
|
attr :icon, :string, default: nil |
|
attr :class, :string, default: nil |
|
attr :disabled, :boolean, default: false |
|
|
|
attr :rest, :global, |
|
include: |
|
~w(disabled form name value download hreflang referrerpolicy rel target type navigate patch href replace method type csrf_token) |
|
|
|
slot :inner_block, required: true |
|
|
|
def card_menu_action(%{"phx-click": nil} = assigns) do |
|
~H""" |
|
<.link {@rest}> |
|
<.menu_item class={@class} disabled={@disabled}> |
|
<.icon :if={@icon} icon={@icon} class="size-4 mr-2" /> |
|
<%= render_slot(@inner_block) %> |
|
</.menu_item> |
|
</.link> |
|
""" |
|
end |
|
|
|
def card_menu_action(assigns) do |
|
~H""" |
|
<button type="button" disabled={@disabled} class="block w-full" {@rest}> |
|
<.menu_item class={@class} disabled={@disabled}> |
|
<.icon :if={@icon} icon={@icon} class="size-4 mr-2" /> |
|
<%= render_slot(@inner_block) %> |
|
</.menu_item> |
|
</button> |
|
""" |
|
end |
|
|
|
slot :inner_block, required: true |
|
|
|
def card_footer(assigns) do |
|
~H""" |
|
<div class="[grid-area:footer]"> |
|
<hr class="mb-2 w-6 border-border group-hovered-action/card:border-primary" /> |
|
<div class="flex items-center"> |
|
<%= render_slot(@inner_block) %> |
|
</div> |
|
</div> |
|
""" |
|
end |
|
|
|
slot :inner_block, required: true |
|
|
|
def card_header(assigns) do |
|
~H""" |
|
<div class="flex items-center justify-between [grid-area:header]"> |
|
<%= render_slot(@inner_block) %> |
|
</div> |
|
""" |
|
end |
|
|
|
attr :text, :string, required: true |
|
|
|
def card_footer_muted_text(assigns) do |
|
~H""" |
|
<.card_footer> |
|
<.card_muted_text> |
|
<.text text={@text} /> |
|
</.card_muted_text> |
|
</.card_footer> |
|
""" |
|
end |
|
end |