GrPagination
Splits a long list into pages and manages navigation between them.
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 —
showPageSizewith the set frompageSizes; - the volume has to be known —
showTotalprints “41–60 of 137” next to the navigation; - there is little room —
compactreplaces the numbers with a “current / total” indicator; - there are many pages —
showJumpergives a jump by number instead of stepping through.
When to take something else
| Need | Take |
|---|---|
| The list is loaded by scrolling | GrList with virtual and source |
| There are few rows and they all fit | GrTable |
| Sections are switched rather than pages of data | GrTabs |
| The course of a long operation has to be shown | GrProgressBar |
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
Loading…
0 changed· state is in the URL
<GrPagination
:total="137"
:pageSize="10"
:page="3"
size="xs"
:siblingCount="0"
/>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-totalanswers «how many are there» — thetotalslot words it for orders —show-page-sizetrades pages for scrolling, andsibling-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.00An audit log of 122 pages
With 122 pages the number is often known — from a link in a ticket — so
show-jumpertakes 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_239122:50Grace Wu signed inevt_239021:43Jonas Weber invited a memberevt_238920:36Mina Ortiz signed inevt_238819:29Daniel Kim invited a memberevt_238718:22Grace Wu signed inevt_2386Reviews on a phone
On a phone a row of page numbers neither fits nor helps:
compactleaves 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
| Prop | Type | default | Description |
|---|---|---|---|
disabled | boolean | undefined | false | Dims the whole pagination: the numbers, the buttons, the page-size select and the jump field. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | The height of the buttons, the jump field and the select on the control scale. Unset — from `GrConfigProvider`, otherwise `md`. |
ariaLabel | string | undefined | undefined | The 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. |
compact | boolean | undefined | false | The compact variant: instead of numbered pages a "current / total" indicator is shown — convenient in tight places (mobile, table toolbars). |
pageSizes | GrPaginationPageSizeOption[] | 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. |
siblingCount | number | undefined | 1 | How many neighbouring pages to show around the current one. `1` by default. |
boundaryCount | number | undefined | 1 | How many edge pages to always show at each edge. `1` by default. |
showJumper | boolean | undefined | false | Show a "go to page" field with a quick jump by the entered number. |
showPageSize | boolean | undefined | false | Show the page-size select. |
showTotal | boolean | undefined | false | Show the range of the items displayed — "1–20 of 137". The `#total` slot is stronger. |
jumperLabel | string | undefined | undefined | The i18n label before the jump field. By default — `gr.pagination.jumpTo`. |
pagerequiredv-model | number | — | The current page, counted from one (`v-model:page`). Out of range it is drawn clamped to the edge. |
pageSizerequiredv-model | number | — | Items per page (`v-model:pageSize`) — the divisor for the number of pages and the range. |
totalrequired | number | — | The total number of items, not pages: the number of pages is calculated from it and `pageSize`. |
Slots
| Slot | Type | Description |
|---|---|---|
total | { from: number; to: number; total: number; } | The range of the items displayed as a whole — instead of the string from the locale. |
Events
| Event | Signature | Description |
|---|---|---|
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 type { GrPaginationPageSizeOption, GrPaginationProps, GrPaginationEmits } from '@feugene/granularity/components/GrPagination'Filedist/types/components/GrPagination/GrPagination.vue.d.ts
type GrPaginationPageSizeOption = number | {
value: number;
label: string;
};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;
}interface GrPaginationEmits {
(e: 'update:page', value: number): void;
(e: 'update:pageSize', value: number): void;
}Filedist/types/components/GrPagination/config.d.ts
const grPaginationConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrPagination">;Filedist/types/components/GrPagination/defaults.d.ts
interface GrPaginationConfigurableProps {
size: GrComponentSize;
}Filedist/types/components/GrPagination/grPaginationStyles.d.ts
type GrPaginationSize = GrComponentSize;Filedist/types/components/GrPagination/safelist.d.ts
const grPaginationSafelist: string[];Accessibility
APG pattern
—
Keys
Enter
Full contract of the package: docs/keyboard.md ↗
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:
<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
| Prop | What it adds |
|---|---|
showPageSize | a select for the page size; off by default — basic pagination is numbers only |
pageSizes accepts both numbers and “value + label” pairs:
<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:
<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:
size | cluster |
|---|---|
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.
Props in this example