defmodule LiveRender.Components.Callout do
use LiveRender.Component,
name: "callout",
description: "Highlighted callout box for tips, warnings, notes, or key information",
schema: [
type: [
type: {:in, [:info, :tip, :warning, :important]},
default: :info,
doc: "Callout type"
],
title: [type: :string, doc: "Callout title"],
content: [type: :string, required: true, doc: "Callout body text"]
]
use Phoenix.Component
def render(assigns) do
~H"""
<%= callout_icon(@type) %>
<%= @title %>
<%= @content %>
"""
end
defp callout_class(:info), do: "border-blue-500 bg-blue-50 dark:bg-blue-950/50"
defp callout_class(:tip), do: "border-emerald-500 bg-emerald-50 dark:bg-emerald-950/50"
defp callout_class(:warning), do: "border-amber-500 bg-amber-50 dark:bg-amber-950/50"
defp callout_class(:important), do: "border-purple-500 bg-purple-50 dark:bg-purple-950/50"
defp callout_icon(:info), do: "ℹ️"
defp callout_icon(:tip), do: "💡"
defp callout_icon(:warning), do: "⚠️"
defp callout_icon(:important), do: "⭐"
end
defmodule LiveRender.Components.Timeline do
use LiveRender.Component,
name: "timeline",
description: "Vertical timeline showing ordered events, steps, or milestones",
schema: [
items: [
type: {:list, :map},
required: true,
doc:
"List of items with title, description (optional), date (optional), status (optional: completed | current | upcoming)"
]
]
use Phoenix.Component
def render(assigns) do
assigns = update(assigns, :items, fn items -> if is_list(items), do: items, else: [] end)
~H"""
<%= item["title"] %>
<%= item["date"] %>
<%= item["description"] %>
"""
end
defp dot_class("completed"), do: "bg-emerald-500"
defp dot_class("current"), do: "bg-blue-500"
defp dot_class("upcoming"), do: "bg-muted-foreground/30"
defp dot_class(_), do: "bg-muted-foreground"
end
defmodule LiveRender.Components.Accordion do
use LiveRender.Component,
name: "accordion",
description: "Expandable sections for organizing detailed content",
schema: [
items: [
type: {:list, :map},
required: true,
doc: "List of items with title and content"
]
]
use Phoenix.Component
def render(assigns) do
assigns = update(assigns, :items, fn items -> if is_list(items), do: items, else: [] end)
~H"""
<%= item["title"] %>
▼
<%= item["content"] %>
"""
end
end
defmodule LiveRender.Components.Progress do
use LiveRender.Component,
name: "progress",
description: "Progress bar",
schema: [
value: [type: :integer, required: true, doc: "Current value (0-100)"],
label: [type: :string, doc: "Label text"]
]
use Phoenix.Component
def render(assigns) do
pct = min(max(parse_value(assigns[:value]), 0), 100)
assigns = assign(assigns, :pct, pct)
~H"""
<%= @label %>
<%= @pct %>%
"""
end
defp parse_value(v) when is_integer(v), do: v
defp parse_value(v) when is_float(v), do: round(v)
defp parse_value(v) when is_binary(v) do
case Float.parse(v) do
{n, _} -> round(n)
:error -> 0
end
end
defp parse_value(_), do: 0
end
defmodule LiveRender.Components.Alert do
use LiveRender.Component,
name: "alert",
description: "Alert message",
schema: [
title: [type: :string, doc: "Alert title"],
message: [type: :string, required: true, doc: "Alert message"],
variant: [
type: {:in, [:info, :success, :warning, :error]},
default: :info,
doc: "Alert type"
]
]
use Phoenix.Component
def render(assigns) do
assigns = assign_new(assigns, :title, fn -> nil end)
~H"""
<%= @title %>
<%= @message %>
"""
end
defp alert_class(:success),
do:
"border-green-200 bg-green-50 text-green-900 dark:border-green-800 dark:bg-green-950 dark:text-green-100"
defp alert_class(:warning),
do:
"border-yellow-200 bg-yellow-50 text-yellow-900 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-100"
defp alert_class(:error),
do: "border-destructive/50 bg-destructive/10 text-destructive"
defp alert_class(_),
do:
"border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-100"
end