GrPagination

  • core
  • navigation
  • API · 14 props
  • ⌨ keys

Splits a long list into pages and manages navigation between them.

import { GrPagination } from '@feugene/granularity/components/GrPagination'

Overview

When to take it

  • the list does not fit on the screen — page numbers with the middle truncated;
  • the user chooses the page size — showPageSize with the set from pageSizes;
  • the volume has to be known — showTotal prints “41–60 of 137” next to the navigation;
  • there is little room — compact replaces the numbers with a “current / total” indicator;
  • there are many pages — showJumper gives a jump by number instead of stepping through.

When to take something else

NeedTake
The list is loaded by scrollingGrList with virtual and source
There are few rows and they all fitGrTable
Sections are switched rather than pages of dataGrTabs
The course of a long operation has to be shownGrProgressBar

page, pageSize and total arrive from the outside: the component only asks for them to be changed. It knows nothing about the data and cannot know — the application loads it.

Playground controls come from the API, not from hand

Stage

Loading…

0 changed· state is in the URL

Examples 3 each with a live demo and its code

  • An orders table with its footer

    The component takes the total and the page size and works out the rest — the number of pages is not a prop, because two sources of one truth part ways on the first filter. show-total answers «how many are there» — the total slot words it for orders — show-page-size trades pages for scrolling, and sibling-count="0" keeps the footer on one line.

    #4821Tailspin ToysOverdue€217.25
    #4820Wingtip ToysPaid€180.00
    #4819Adventure WorksShipped€143.75
    #4818Northwind TradersRefunded€106.50
    #4817Contoso LtdOverdue€69.25
    #4816Fabrikam IncPaid€932.00
    #4815Tailspin ToysShipped€895.75
    #4814Wingtip ToysRefunded€858.50
    #4813Adventure WorksOverdue€821.25
    #4812Northwind TradersPaid€784.00
  • An audit log of 122 pages

    With 122 pages the number is often known — from a link in a ticket — so show-jumper takes it directly: type 97 and press Enter. The first and the last page stay in reach around the current one.

    Audit log2,431 security events of the workspace · newest first
    23:57Daniel Kim invited a memberevt_2391
    22:50Grace Wu signed inevt_2390
    21:43Jonas Weber invited a memberevt_2389
    20:36Mina Ortiz signed inevt_2388
    19:29Daniel Kim invited a memberevt_2387
    18:22Grace Wu signed inevt_2386
  • Reviews on a phone

    On a phone a row of page numbers neither fits nor helps: compact leaves the arrows and «3 / 14».

    Reviews · 4.6 ★ from 42
    Grace W. · ★★★★★Bought a second one in navy.

API generated from the package · cannot drift

Props

PropTypedefaultDescription
disabledboolean | undefinedfalseDims the whole pagination: the numbers, the buttons, the page-size select and the jump field.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedThe height of the buttons, the jump field and the select on the control scale. Unset — from `GrConfigProvider`, otherwise `md`.
ariaLabelstring | undefinedundefinedThe name of the navigation landmark. By default — `gr.pagination.label`; it is worth setting where there are several paginations on the page and they have to be told apart.
compactboolean | undefinedfalseThe compact variant: instead of numbered pages a "current / total" indicator is shown — convenient in tight places (mobile, table toolbars).
pageSizesGrPaginationPageSizeOption[] | undefined[10, 20, 50]The page size options. A number — the label equals the value; a pair — a label of your own with the same value ("50 / page"), which could not be assembled otherwise.
siblingCountnumber | undefined1How many neighbouring pages to show around the current one. `1` by default.
boundaryCountnumber | undefined1How many edge pages to always show at each edge. `1` by default.
showJumperboolean | undefinedfalseShow a "go to page" field with a quick jump by the entered number.
showPageSizeboolean | undefinedfalseShow the page-size select.
showTotalboolean | undefinedfalseShow the range of the items displayed — "1–20 of 137". The `#total` slot is stronger.
jumperLabelstring | undefinedundefinedThe i18n label before the jump field. By default — `gr.pagination.jumpTo`.
pagerequiredv-modelnumber—The current page, counted from one (`v-model:page`). Out of range it is drawn clamped to the edge.
pageSizerequiredv-modelnumber—Items per page (`v-model:pageSize`) — the divisor for the number of pages and the range.
totalrequirednumber—The total number of items, not pages: the number of pages is calculated from it and `pageSize`.

Slots

SlotTypeDescription
total{ from: number; to: number; total: number; }The range of the items displayed as a whole — instead of the string from the locale.

Events

EventSignatureDescription
update:page(event: "update:page", value: number): void—
update:pageSize(event: "update:pageSize", value: number): void—

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrPaginationPageSizeOption, GrPaginationProps, GrPaginationEmits } from '@feugene/granularity/components/GrPagination'

Filedist/types/components/GrPagination/GrPagination.vue.d.ts

GrPaginationPageSizeOptiontype
type GrPaginationPageSizeOption = number | {
    value: number;
    label: string;
};
GrPaginationPropsinterface
interface GrPaginationProps {
    page: number;
    pageSize: number;
    total: number;
    pageSizes?: GrPaginationPageSizeOption[];
    siblingCount?: number;
    boundaryCount?: number;
    compact?: boolean;
    showJumper?: boolean;
    showPageSize?: boolean;
    showTotal?: boolean;
    jumperLabel?: string;
    ariaLabel?: string;
    disabled?: boolean;
    size?: GrPaginationSize;
}
GrPaginationEmitsinterface
interface GrPaginationEmits {
    (e: 'update:page', value: number): void;
    (e: 'update:pageSize', value: number): void;
}

Filedist/types/components/GrPagination/config.d.ts

grPaginationConfigconst
const grPaginationConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrPagination">;

Filedist/types/components/GrPagination/defaults.d.ts

GrPaginationConfigurablePropsinterface
interface GrPaginationConfigurableProps {
    size: GrComponentSize;
}

Filedist/types/components/GrPagination/grPaginationStyles.d.ts

GrPaginationSizetype
type GrPaginationSize = GrComponentSize;

Filedist/types/components/GrPagination/safelist.d.ts

grPaginationSafelistconst
const grPaginationSafelist: string[];

Accessibility

APG pattern

—

Keys

Enter

Usage from the library prose

A controlled component

page, pageSize and total arrive from the outside, and the component only asks for them to be changed:

vue
<GrPagination
  v-model:page="page"
  v-model:page-size="pageSize"
  :total="total"
/>

Precisely v-model:page and not v-model: the page is a named model, and the component has no modelValue prop. A miss is visible as a modelvalue attribute on the root <div> — an undeclared prop travels there through fallthrough; in dev mode the component additionally prints a warning.

The number of pages is computed from total and pageSize. The numbers are truncated by the boundary/sibling algorithm: the first and the last pages are always visible plus siblingCount neighbours around the current one; boundaryCount sets how many of the edge ones to show. A single skipped number is drawn as a number rather than as an ellipsis — the row does not jump.

A non-numeric input is replaced by the default: a page that did not arrive gives the first page, total gives zero and pageSize gives one, and every case is explained with a warning in dev mode. Without that an undefined would spread across the numbers and the status as NaN, while the markup would stay plausible to the eye.

A page out of range is rendered clamped to [1, pageCount]: until the parent has pulled the value up, there is an active button all the same. When the number of pages has decreased (total fell or pageSize grew), the component additionally emits update:page with the last available page — if page lives in the URL or in a store, that navigation will happen by itself.

What to show
PropWhat it adds
showPageSizea select for the page size; off by default — basic pagination is numbers only

pageSizes accepts both numbers and “value + label” pairs:

vue
<GrPagination :page-sizes="[10, 20, { value: 50, label: '50 / page' }]" show-page-size />

A pair is needed where the label is not equal to the number. It could not be assembled from a bare number[], and the workaround — a GrSelect of your own beside it — lost the link with the pagination. | showTotal | “41–60 of 137” to the left of the navigation | | showJumper | a “go to page” field: Enter or the loss of focus applies the number, and one out of range is clamped | | compact | a “current / total” indicator instead of the numbers — for mobile and for table toolbars |

The range as a whole is replaced by the #total slot — it receives from, to and total:

vue
<GrPagination :total="total" show-total>
  <template #total="{ from, to, total }">
    Orders {{ from }}–{{ to }} of {{ total }}
  </template>
</GrPagination>

disabled dims everything at once: the numbers, the buttons, the select and the jump field.

Wrapping at a narrow width

A pagination row consists of four groups: the total (showTotal), the page size (showPageSize), the navigation and the jump (showJumper). The row wraps only between the groups. The navigation — first, back, the pages, forward, last — is one unbreakable cluster: “back” does not move to another line away from its pages, and at a narrow width the cluster moves to a line of its own as a whole and is pressed to the end.

The cluster never becomes wider than the row: if it does not fit even on its own, it scrolls sideways without a visible bar. At such a width switch on compact — “current / total” instead of the numbers — or reduce siblingCount. Reference points for siblingCount: 1 (up to seven number positions) — the width of the cluster with the numbers:

sizecluster
xs≈ 365px
sm≈ 440px
md≈ 525px
lg≈ 595px

With compact the cluster at md takes about 275px.

The component does not measure the width itself: a number of positions that depended on a measurement would differ between the server and the client. The breakpoint is chosen by the page layout.

Accessibility

The root is a role="navigation" with a name from the locale (gr.pagination.label). If there are two paginations on the page (above and below a table), set ariaLabel — otherwise there will be two identical landmarks in the overview of a screen reader.

The numbers lie in a list (<ul role="list">), so a screen reader reports their number; the ellipses are excluded from it (aria-hidden). The current page is marked aria-current="page".

A change of page is announced by a live area: in the compact mode it is carried by the visible indicator, and in the ordinary one by a hidden “Page N of M” string (gr.pagination.status).

The sizes

size (xs…lg) is read from GrConfigProvider (componentDefaults.GrPagination.size). The box of a number is the same height as a GrButton of the same name (28/32/40/44): the numbers and the navigation buttons stand in one row, and were the scales to diverge, the row would drift. That is also why sm and md are different sizes rather than the same height with a different type size.