Packages
petal_components
4.5.0
4.7.0
4.6.2
4.6.1
4.6.0
4.5.0
4.4.0
4.3.0
4.2.1
4.2.0
4.1.2
4.1.1
4.1.0
4.0.12
4.0.11
4.0.10
4.0.6
4.0.5
4.0.4
4.0.3
4.0.1
3.2.2
3.2.1
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
2.9.3
2.9.2
retired
2.9.1
retired
2.9.0
retired
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.2
2.1.1
2.1.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.9.3
1.9.2
1.9.1
1.9.0
1.8.0
1.7.1
1.7.0
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.0
1.2.14
1.2.13
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.19.10
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.7
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.1
0.14.0
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.0
Shadcn-style Phoenix LiveView components that AI assistants can actually use. Pair with the MCP server so AI coding tools can inspect the real component API.
Current section
Files
Jump to
Current section
Files
lib/petal_components/tabs.ex
defmodule PetalComponents.Tabs do
use Phoenix.Component
alias Phoenix.LiveView.JS
alias PetalComponents.Link
attr(:underline, :boolean,
default: false,
doc: "underlines your tabs (legacy for variant=\"underline\")"
)
attr(:variant, :string,
default: nil,
values: [nil, "pill", "underline", "segmented"],
doc:
"tab style: pill (default), underline, or segmented - active tab is a raised white pill on a muted track. Overrides the legacy underline flag"
)
attr(:class, :any, default: nil, doc: "CSS class")
attr(:rest, :global)
slot(:inner_block, required: false)
def tabs(assigns) do
assigns =
assign(assigns, :resolved_variant, resolve_variant(assigns.variant, assigns.underline))
~H"""
<nav
{@rest}
class={[
"pc-tabs",
@resolved_variant == "underline" && "pc-tabs--underline",
@resolved_variant == "segmented" && "pc-tabs--segmented",
@class
]}
role="tablist"
>
{render_slot(@inner_block)}
</nav>
"""
end
defp resolve_variant(nil, true), do: "underline"
defp resolve_variant(nil, _underline), do: "pill"
defp resolve_variant(variant, _underline), do: variant
attr(:class, :any, default: nil, doc: "CSS class")
attr(:label, :string, default: nil, doc: "labels your tab")
attr(:link_type, :string,
default: "a",
values: ["a", "live_patch", "live_redirect"]
)
attr(:to, :string, default: nil, doc: "link path")
attr(:number, :integer, default: nil, doc: "indicates a number next to your tab")
attr(:underline, :boolean,
default: false,
doc: "underlines your tab (legacy for variant=\"underline\")"
)
attr(:variant, :string,
default: nil,
values: [nil, "pill", "underline", "segmented"],
doc: "match the parent tabs variant. Overrides the legacy underline flag"
)
attr(:is_active, :boolean, default: false, doc: "indicates the current tab")
attr(:disabled, :boolean, default: false, doc: "disables your tab")
attr :on_change, JS,
default: %JS{},
doc: "JS commands to run when this tab is selected"
attr(:rest, :global, include: ~w(method download hreflang ping referrerpolicy rel target type))
slot(:inner_block, required: false)
def tab(assigns) do
assigns =
if assigns.on_change.ops != [] do
update(assigns, :rest, fn rest -> Map.put(rest, :"phx-click", assigns.on_change) end)
else
assigns
end
assigns =
assign(assigns, :resolved_variant, resolve_variant(assigns.variant, assigns.underline))
~H"""
<Link.a
link_type={@link_type}
label={@label}
to={@to}
class={get_tab_class(@is_active, @resolved_variant) ++ [@class]}
disabled={@disabled}
role="tab"
aria-selected={to_string(@is_active)}
{@rest}
>
<%= if @number do %>
{render_slot(@inner_block) || @label}
<span class={get_tab_number_class(@is_active, @resolved_variant == "underline")}>
{@number}
</span>
<% else %>
{render_slot(@inner_block) || @label}
<% end %>
</Link.a>
"""
end
defp get_tab_class(is_active, "pill") do
[
"pc-tab__pill",
if(is_active, do: "pc-tab__pill--is-active", else: "pc-tab__pill--is-not-active")
]
end
defp get_tab_class(is_active, "segmented") do
[
"pc-tab__segment",
if(is_active, do: "pc-tab__segment--is-active", else: "pc-tab__segment--is-not-active")
]
end
defp get_tab_class(is_active, "underline") do
base_classes = "pc-tab__underline"
active_classes =
if is_active,
do: "pc-tab__underline--is-active",
else: "pc-tab__underline--is-not-active"
underline_classes =
if is_active,
do: "pc-tab__underline--with-underline-and-is-active",
else: "pc-tab__underline--with-underline-and-is-not-active"
[base_classes, active_classes, underline_classes]
end
# Underline
defp get_tab_number_class(is_active, true) do
base_classes = "pc-tab__number"
active_classes =
if is_active,
do: "pc-tab__number__underline--is-active",
else: "pc-tab__number__underline--is-not-active"
underline_classes =
if is_active,
do: "pc-tab__number__underline--with-underline-and-is-active",
else: "pc-tab__number__underline--with-underline-and-is-not-active"
[base_classes, active_classes, underline_classes]
end
# Pill
defp get_tab_number_class(is_active, false) do
base_classes = "pc-tab__number"
active_classes =
if is_active,
do: "pc-tab__number__pill--is-active",
else: "pc-tab__number__pill--is-not-active"
[base_classes, active_classes]
end
end