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