Corex. Pagination
(Corex v0.1.0-rc.1)
View Source
Phoenix implementation of Zag.js Pagination.
Pagination and Carousel both use 1-based page (first page is 1). Page numbers and ellipses are rendered on the server so the control is complete before the hook runs.
Anatomy
Minimal
<.pagination class="pagination" count={95} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>Controlled page
<.pagination
class="pagination"
count={18}
page={@page}
page_size={4}
controlled
on_page_change="pagination_controlled_changed"
>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>def mount(_params, _session, socket) do
{:ok, assign(socket, :page, 1)}
end
def handle_event("pagination_controlled_changed", %{"page" => page}, socket) do
{:noreply, assign(socket, :page, page)}
endLink mode
Set type={:link}, to, and optional page_param / page_size_param. The hook builds href on each control (SSR and client).
redirect | Behavior |
|---|---|
:href (default) | Full page load |
:patch | data-phx-link="patch" (same as <.navigate type="patch">) |
:navigate | data-phx-link="redirect" (same as <.navigate type="navigate">) |
For assigns-driven state, prefer type={:button}, controlled, and on_page_change instead of link mode.
Translation
<.pagination
class="pagination"
count={95}
page_size={10}
translation={%Corex.Pagination.Translation{
prev_trigger_label: Corex.Gettext.gettext("Previous page"),
next_trigger_label: Corex.Gettext.gettext("Next page"),
item_label:
Corex.Gettext.gettext("Page %{page} of %{total_pages}",
page: "%{page}",
total_pages: "%{total_pages}"
)
}}
>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>API
Requires a stable id on <.pagination>.
| Function | Action | Returns |
|---|---|---|
set_page/2 | Set active page (client) | %Phoenix.LiveView.JS{} |
set_page/3 | Set active page (server) | socket |
set_page_size/2 | Set page size (client) | %Phoenix.LiveView.JS{} |
set_page_size/3 | Set page size (server) | socket |
go_to_next_page/1 | Next page (client) | %Phoenix.LiveView.JS{} |
go_to_next_page/2 | Next page (server) | socket |
go_to_prev_page/1 | Previous page (client) | %Phoenix.LiveView.JS{} |
go_to_prev_page/2 | Previous page (server) | socket |
go_to_first_page/1 | First page (client) | %Phoenix.LiveView.JS{} |
go_to_first_page/2 | First page (server) | socket |
go_to_last_page/1 | Last page (client) | %Phoenix.LiveView.JS{} |
go_to_last_page/2 | Last page (server) | socket |
set_page
<.action phx-click={Corex.Pagination.set_page("pagination-api-bind", 5)} class="button button--sm">5</.action>
<.pagination id="pagination-api-bind" class="pagination" count={95} page={5} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>def handle_event("pagination_api_page_3", _, socket) do
{:noreply, Corex.Pagination.set_page(socket, "pagination-api-srv", 3)}
endEvents
Pick an event name and pass it to on_* on <.pagination>.
Server events
| Event | When | Payload |
|---|---|---|
on_page_change="pagination_page_changed" | Active page changes | %{"id" => id, "page" => page} |
on_page_size_change="pagination_page_size_changed" | Page size changes | %{"id" => id, "page_size" => page_size} |
on_page_change
<.pagination class="pagination" count={95} page_size={10} on_page_change="pagination_page_changed">
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>def handle_event("pagination_page_changed", %{"id" => id, "page" => page}, socket) do
{:noreply, assign(socket, :page, page)}
endClient events
| Event | When | event.detail |
|---|---|---|
on_page_change_client="pagination-page-changed" | Active page changes | id, page, page_size |
on_page_size_change_client="pagination-page-size-changed" | Page size changes | id, page_size |
on_page_change_client
<.pagination
id="pagination-events-client"
class="pagination"
count={95}
page_size={10}
on_page_change_client="pagination-page-changed"
>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>document.getElementById("pagination-events-client")?.addEventListener("pagination-page-changed", (e) => {
console.log(e.detail);
});Patterns
Controlled
Bind page (and optionally page_size) with controlled (true, :all, :page, or :page_size) and handle on_page_change so assigns stay the source of truth.
<.pagination
class="pagination"
count={18}
page={@page}
page_size={4}
controlled
on_page_change="pagination_controlled_changed"
>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>Slice list data on the server (Zag api.slice is client-only):
offset = (page - 1) * page_size
Enum.slice(items, offset, page_size)Patch (URL)
Use type={:link}, controlled, to, and redirect={:patch} so page and page size sync from query params via handle_params/3.
<.pagination
class="pagination"
count={18}
page={@page}
page_size={4}
controlled={:all}
type={:link}
to="/posts"
redirect={:patch}
>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>Style
Target parts with data-scope and data-part, or import pagination.css and stack modifiers on the host.
[data-scope="pagination"][data-part="root"] {}
[data-scope="pagination"][data-part="item"] {}
[data-scope="pagination"][data-part="ellipsis"] {}
[data-scope="pagination"][data-part="prev-trigger"][data-disabled] {}
[data-scope="pagination"][data-part="next-trigger"][data-disabled] {}@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/pagination.css";Stack modifiers on <.pagination class="pagination ...">.
Color
| Modifier | Classes |
|---|---|
| Default | pagination |
| Accent | pagination pagination--accent |
| Brand | pagination pagination--brand |
| Alert | pagination pagination--alert |
| Success | pagination pagination--success |
| Info | pagination pagination--info |
Size
| Modifier | Classes |
|---|---|
| Default | pagination |
| SM | pagination pagination--sm |
| MD | pagination pagination--md |
| LG | pagination pagination--lg |
| XL | pagination pagination--xl |
Text
| Modifier | Classes |
|---|---|
| Default | pagination |
| SM | pagination pagination--text-sm |
| XL | pagination pagination--text-xl |
| 2XL | pagination pagination--text-2xl |
| 4XL | pagination pagination--text-4xl |
Rounded
| Modifier | Classes |
|---|---|
| Default | pagination |
| None | pagination pagination--rounded-none |
| SM | pagination pagination--rounded-sm |
| MD | pagination pagination--rounded-md |
| LG | pagination pagination--rounded-lg |
| XL | pagination pagination--rounded-xl |
| Full | pagination pagination--rounded-full |
Max width
| Modifier | Classes |
|---|---|
| Default | pagination |
| SM | pagination max-w-sm |
| MD | pagination max-w-md |
| LG | pagination max-w-lg |
| XL | pagination max-w-xl |
Summary
API
Go to page 1 from phx-click. Dispatches corex:pagination:go-to-first-page.
Go to first page from handle_event (pagination_go_to_first_page).
Go to the last page from phx-click. Dispatches corex:pagination:go-to-last-page.
Go to last page from handle_event (pagination_go_to_last_page).
Go to the next page from phx-click. Dispatches corex:pagination:go-to-next-page.
Go to the next page from handle_event (pagination_go_to_next_page).
Go to the previous page from phx-click. Dispatches corex:pagination:go-to-prev-page.
Go to the previous page from handle_event (pagination_go_to_prev_page).
Set the current 1-based page from phx-click. Dispatches corex:pagination:set-page with detail.page.
Set the current page from handle_event (pagination_set_page).
Change page size from phx-click. Dispatches corex:pagination:set-page-size with detail.page_size.
Change page size from handle_event (pagination_set_page_size).
Components
Attributes
id(:string)count(:integer) (required) - Total number of data items.page(:integer) - Active page (1-based). Defaults to1.page_size(:integer) - Items per page. Defaults to10.sibling_count(:integer) - Pages beside the active page. Defaults to1.boundary_count(:integer) - Pages at the start and end. Defaults to1.controlled(:any) - false — uncontrolled (default). true or :all — server drives both page and page_size. :page — server drives page only; use withon_page_change. :page_size — server drives page_size only; use withon_page_size_change.Defaults to
false.type(:atom) - Trigger element type passed to Zag (:buttonor:link). Defaults to:button. Must be one of:button, or:link.to(:string) - Base path for link mode; hook builds query URLs frompage_paramandpage_size_param. Defaults tonil.page_param(:string) - Query param name for page in link mode. Defaults to"page".page_size_param(:string) - Query param name for page size in link mode. Defaults to"page_size".redirect(:atom) - Navigation kind whentypeis:linkand LiveView is connected. Defaults to:href. Must be one of:href,:patch, or:navigate.dir(:string) - Defaults tonil.Must be one ofnil,"ltr", or"rtl".on_page_change(:string) - Defaults tonil.on_page_change_client(:string) - Defaults tonil.on_page_size_change(:string) - Defaults tonil.on_page_size_change_client(:string) - Defaults tonil.translation(Corex.Pagination.Translation) - Override translatable strings. Defaults tonil.Global attributes are accepted.
Slots
prev(required) - Accepts attributes:class(:string)
next(required) - Accepts attributes:class(:string)
ellipsis(required) - Accepts attributes:class(:string)
API
Go to page 1 from phx-click. Dispatches corex:pagination:go-to-first-page.
<.action phx-click={Corex.Pagination.go_to_first_page("my-pagination")}>First</.action>
<.pagination id="my-pagination" class="pagination" count={95} page={1} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>
Go to first page from handle_event (pagination_go_to_first_page).
def handle_event("first", _params, socket) do
{:noreply, Corex.Pagination.go_to_first_page(socket, "my-pagination")}
end
Go to the last page from phx-click. Dispatches corex:pagination:go-to-last-page.
<.action phx-click={Corex.Pagination.go_to_last_page("my-pagination")}>Last</.action>
<.pagination id="my-pagination" class="pagination" count={95} page={1} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>
Go to last page from handle_event (pagination_go_to_last_page).
def handle_event("last", _params, socket) do
{:noreply, Corex.Pagination.go_to_last_page(socket, "my-pagination")}
end
Go to the next page from phx-click. Dispatches corex:pagination:go-to-next-page.
<.action phx-click={Corex.Pagination.go_to_next_page("my-pagination")}>Next</.action>
<.pagination id="my-pagination" class="pagination" count={95} page={1} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>
Go to the next page from handle_event (pagination_go_to_next_page).
def handle_event("next", _params, socket) do
{:noreply, Corex.Pagination.go_to_next_page(socket, "my-pagination")}
end
Go to the previous page from phx-click. Dispatches corex:pagination:go-to-prev-page.
<.action phx-click={Corex.Pagination.go_to_prev_page("my-pagination")}>Previous</.action>
<.pagination id="my-pagination" class="pagination" count={95} page={1} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>
Go to the previous page from handle_event (pagination_go_to_prev_page).
def handle_event("prev", _params, socket) do
{:noreply, Corex.Pagination.go_to_prev_page(socket, "my-pagination")}
end
Set the current 1-based page from phx-click. Dispatches corex:pagination:set-page with detail.page.
<.action phx-click={Corex.Pagination.set_page("my-pagination", 2)}>Page 2</.action>
<.pagination id="my-pagination" class="pagination" count={95} page={1} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>document.getElementById("my-pagination")?.dispatchEvent(
new CustomEvent("corex:pagination:set-page", {
bubbles: false,
detail: { page: 2 },
})
);
Set the current page from handle_event (pagination_set_page).
def handle_event("set_page", %{"page" => page}, socket) do
{:noreply, Corex.Pagination.set_page(socket, "my-pagination", String.to_integer(page))}
end
Change page size from phx-click. Dispatches corex:pagination:set-page-size with detail.page_size.
<.action phx-click={Corex.Pagination.set_page_size("my-pagination", 20)}>20 per page</.action>
<.pagination id="my-pagination" class="pagination" count={95} page={1} page_size={10}>
<:prev><.heroicon name="hero-chevron-left" /></:prev>
<:next><.heroicon name="hero-chevron-right" /></:next>
<:ellipsis><.heroicon name="hero-ellipsis-horizontal" /></:ellipsis>
</.pagination>
Change page size from handle_event (pagination_set_page_size).
def handle_event("set_size", _params, socket) do
{:noreply, Corex.Pagination.set_page_size(socket, "my-pagination", 25)}
end