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""" """ 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