Packages
petal_components
2.2.1
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/pagination.ex
defmodule PetalComponents.Pagination do
@moduledoc """
Pagination is the method of splitting up content into discrete pages. It specifies the total number of pages and inidicates to a user the current page within the context of total pages.
"""
use Phoenix.Component
import PetalComponents.PaginationInternal
alias PetalComponents.Link
import PetalComponents.Icon
attr :path, :string, default: "/:page", doc: "page path"
attr :class, :any, default: nil, doc: "parent div CSS class"
attr :link_type, :string,
default: "a",
values: ["a", "live_patch", "live_redirect", "button"]
attr :event, :boolean,
default: false,
doc:
"whether to use `phx-click` events instead of linking. Enabling this will disable `link_type` and `path`."
attr :target, :any,
default: nil,
doc:
"the LiveView/LiveComponent to send the event to. Example: `@myself`. Will be ignored if `event` is not enabled."
attr :total_pages, :integer, default: nil, doc: "sets a total page count"
attr :current_page, :integer, default: nil, doc: "sets the current page"
attr :sibling_count, :integer, default: 1, doc: "sets a sibling count"
attr :boundary_count, :integer, default: 1, doc: "sets a boundary count"
attr :show_boundary_chevrons, :boolean,
default: false,
doc: "whether to show prev & next buttons at boundary pages"
attr :rest, :global
@doc """
In the `path` param you can specify :page as the place your page number will appear.
e.g "/posts/:page" => "/posts/1"
"""
def pagination(assigns) do
~H"""
<div {@rest} class={["pc-pagination", @class]}>
<ul class="pc-pagination__inner">
<%= for item <- get_pagination_items(@total_pages, @current_page, @sibling_count, @boundary_count) do %>
<%= if item.type == "prev" and (item.enabled? or @show_boundary_chevrons) do %>
<div>
<Link.a
phx-click={if @event, do: "goto-page"}
phx-target={if @event, do: @target}
phx-value-page={item.number}
link_type={if @event, do: "button", else: @link_type}
to={if not @event, do: get_path(@path, item.number, @current_page)}
class="pc-pagination__item__previous"
disabled={!item.enabled?}
>
<.icon name="hero-chevron-left-solid" class="pc-pagination__item__previous__chevron" />
</Link.a>
</div>
<% end %>
<%= if item.type == "page" do %>
<li>
<%= if item.current? do %>
<span class={get_box_class(item)}><%= item.number %></span>
<% else %>
<Link.a
phx-click={if @event, do: "goto-page"}
phx-target={if @event, do: @target}
phx-value-page={item.number}
link_type={if @event, do: "button", else: @link_type}
to={if not @event, do: get_path(@path, item.number, @current_page)}
class={get_box_class(item)}
>
<%= item.number %>
</Link.a>
<% end %>
</li>
<% end %>
<%= if item.type == "..." do %>
<li>
<span class="pc-pagination__item__ellipsis">
...
</span>
</li>
<% end %>
<%= if item.type == "next" and (item.enabled? or @show_boundary_chevrons) do %>
<div>
<Link.a
phx-click={if @event, do: "goto-page"}
phx-target={if @event, do: @target}
phx-value-page={item.number}
link_type={if @event, do: "button", else: @link_type}
to={if not @event, do: get_path(@path, item.number, @current_page)}
class="pc-pagination__item__next"
disabled={!item.enabled?}
>
<.icon name="hero-chevron-right-solid" class="pc-pagination__item__next__chevron" />
</Link.a>
</div>
<% end %>
<% end %>
</ul>
</div>
"""
end
defp get_box_class(item) do
base_classes = "pc-pagination__item"
active_classes =
if item.current?,
do: "pc-pagination__item--is-current",
else: "pc-pagination__item--is-not-current"
rounded_classes =
case item do
%{first?: true, last?: true} ->
"pc-pagination__item--with-single-box"
%{first?: true, last?: false} ->
"pc-pagination__item--with-multiple-boxes--left"
%{first?: false, last?: true} ->
"pc-pagination__item--with-multiple-boxes--right"
_ ->
"pc-pagination__item--rounded-catch-all"
end
[base_classes, active_classes, rounded_classes]
end
defp get_path(path, page_number, current_page) when is_binary(path) do
# replace on `%3Apage` or `:page` in case we receive an URI encoded path
fun = &String.replace(path, ~r/%3Apage|:page/, Integer.to_string(&1))
get_path(fun, page_number, current_page)
end
defp get_path(fun, "previous", current_page) when is_function(fun, 1) do
get_path(fun, current_page - 1, current_page)
end
defp get_path(fun, "next", current_page) when is_function(fun, 1) do
get_path(fun, current_page + 1, current_page)
end
defp get_path(fun, page_number, _current_page) when is_function(fun, 1) do
then(page_number, fun)
end
end