defmodule RiverConnectWeb.Components.UI.Pagination do
@moduledoc """
Implementation of pagination component for navigation between pages of content.
## Examples:
<.pagination>
<.pagination_content>
<.pagination_item>
<.pagination_previous href="#" />
<.pagination_item>
<.pagination_link href="#" is_active={@page == 1}>1
<.pagination_item>
<.pagination_link href="#" is_active={@page == 2}>2
<.pagination_item>
<.pagination_link href="#" is_active={@page == 3}>3
<.pagination_item>
<.pagination_ellipsis />
<.pagination_item>
<.pagination_next href="#" />
"""
use RiverConnectWeb.Components.UI, :component
@doc """
Renders a pagination component.
"""
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def pagination(assigns) do
~H"""
"""
end
@doc """
Renders pagination content wrapper.
"""
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def pagination_content(assigns) do
~H"""
{render_slot(@inner_block)}
"""
end
@doc """
Renders a pagination item container.
"""
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def pagination_item(assigns) do
~H"""
{render_slot(@inner_block)}
"""
end
@doc """
Renders a pagination link.
## Options
* `:is_active` - Whether this link represents the current page
* `:size` - Link size variant (default, sm, lg, icon)
"""
attr :"is-active", :boolean, default: false
attr :size, :string, default: "icon", values: ~w(default sm lg icon)
attr :class, :string, default: nil
attr :rest, :global
slot :inner_block, required: true
def pagination_link(assigns) do
is_active = assigns[:"is-active"] in [true, "true"]
assigns =
assigns
|> assign(
:variation_class,
variant(%{size: assigns[:size], variant: (is_active && "outline") || "ghost"})
)
|> assign(:"is-active", is_active)
~H"""
<.link
aria-current={(assigns[:"is-active"] && "page") || ""}
class={
classes([
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50",
@variation_class,
@class
])
}
{@rest}
>
{render_slot(@inner_block)}
"""
end
@doc """
Renders a next page button.
"""
attr :class, :string, default: nil
attr :rest, :global
def pagination_next(assigns) do
~H"""
<.pagination_link
aria-label="Go to next page"
size="default"
class={classes(["gap-1 pr-2.5", @class])}
{@rest}
>
Next
"""
end
@doc """
Renders a previous page button.
"""
attr :class, :string, default: nil
attr :rest, :global
def pagination_previous(assigns) do
~H"""
<.pagination_link
aria-label="Go to previous page"
size="default"
class={classes(["gap-1 pr-2.5", @class])}
{@rest}
>
Previous
"""
end
@doc """
Renders an ellipsis for page ranges.
"""
attr :class, :string, default: nil
attr :rest, :global
def pagination_ellipsis(assigns) do
~H"""
More pages
"""
end
@variants %{
variant: %{
"outline" =>
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
"ghost" => "hover:bg-accent hover:text-accent-foreground"
},
size: %{
"default" => "h-9 px-4 py-2",
"sm" => "h-8 rounded-md px-3 text-xs",
"lg" => "h-10 rounded-md px-8",
"icon" => "h-9 w-9"
}
}
defp variant(props) do
variants =
Map.take(props, ~w(variant size)a)
Enum.map_join(variants, " ", fn {key, value} -> @variants[key][value] end)
end
end