Corex.Pagination (Corex v0.1.2)

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)}
end

Set type={:link}, to, and optional page_param / page_size_param. The hook builds href on each control (SSR and client).

redirectBehavior
:href (default)Full page load
:patchdata-phx-link="patch" (same as <.navigate type="patch">)
:navigatedata-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>.

FunctionActionReturns
set_page/2Set active page (client)%Phoenix.LiveView.JS{}
set_page/3Set active page (server)socket
set_page_size/2Set page size (client)%Phoenix.LiveView.JS{}
set_page_size/3Set page size (server)socket
go_to_next_page/1Next page (client)%Phoenix.LiveView.JS{}
go_to_next_page/2Next page (server)socket
go_to_prev_page/1Previous page (client)%Phoenix.LiveView.JS{}
go_to_prev_page/2Previous page (server)socket
go_to_first_page/1First page (client)%Phoenix.LiveView.JS{}
go_to_first_page/2First page (server)socket
go_to_last_page/1Last page (client)%Phoenix.LiveView.JS{}
go_to_last_page/2Last 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)}
end

Events

Pick an event name and pass it to on_* on <.pagination>.

Server events

EventWhenPayload
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)}
end

Client events

EventWhenevent.detail
on_page_change_client="pagination-page-changed"Active page changesid, page, page_size
on_page_size_change_client="pagination-page-size-changed"Page size changesid, 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

ModifierClasses
Defaultpagination
Accentpagination pagination--accent
Brandpagination pagination--brand
Alertpagination pagination--alert
Successpagination pagination--success
Infopagination pagination--info

Size

ModifierClasses
Defaultpagination
SMpagination pagination--sm
MDpagination pagination--md
LGpagination pagination--lg
XLpagination pagination--xl

Text

ModifierClasses
Defaultpagination
SMpagination pagination--text-sm
XLpagination pagination--text-xl
2XLpagination pagination--text-2xl
4XLpagination pagination--text-4xl

Rounded

ModifierClasses
Defaultpagination
Nonepagination pagination--rounded-none
SMpagination pagination--rounded-sm
MDpagination pagination--rounded-md
LGpagination pagination--rounded-lg
XLpagination pagination--rounded-xl
Fullpagination pagination--rounded-full

Max width

ModifierClasses
Defaultpagination
SMpagination max-w-sm
MDpagination max-w-md
LGpagination max-w-lg
XLpagination 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

pagination(assigns)

Attributes

  • id (:string)

  • count (:integer) (required) - Total number of data items.

  • page (:integer) - Active page (1-based). Defaults to 1.

  • page_size (:integer) - Items per page. Defaults to 10.

  • sibling_count (:integer) - Pages beside the active page. Defaults to 1.

  • boundary_count (:integer) - Pages at the start and end. Defaults to 1.

  • controlled (:any) - false — uncontrolled (default). true or :all — server drives both page and page_size. :page — server drives page only; use with on_page_change. :page_size — server drives page_size only; use with on_page_size_change.

    Defaults to false.

  • type (:atom) - Trigger element type passed to Zag (:button or :link). Defaults to :button. Must be one of :button, or :link.

  • to (:string) - Base path for link mode; hook builds query URLs from page_param and page_size_param. Defaults to nil.

  • 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 when type is :link and LiveView is connected. Defaults to :href. Must be one of :href, :patch, or :navigate.

  • dir (:string) - Defaults to nil.Must be one of nil, "ltr", or "rtl".

  • on_page_change (:string) - Defaults to nil.

  • on_page_change_client (:string) - Defaults to nil.

  • on_page_size_change (:string) - Defaults to nil.

  • on_page_size_change_client (:string) - Defaults to nil.

  • translation (Corex.Pagination.Translation) - Override translatable strings. Defaults to nil.

  • 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_first_page(pagination_id)

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(socket, pagination_id)

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_last_page(pagination_id)

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(socket, pagination_id)

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_next_page(pagination_id)

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_next_page(socket, pagination_id)

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_prev_page(pagination_id)

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_prev_page(socket, pagination_id)

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_page(pagination_id, page)

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_page(socket, pagination_id, page)

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

set_page_size(pagination_id, page_size)

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>

set_page_size(socket, pagination_id, page_size)

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