- <SeoPagination> (UI-SPEC L2): previous and next (left out at the ends), page 1, the last page and five pages around the current one with gap items; nothing when last <= 1; props current, last, labels, classPrefix, locale - usePagination(): hrefs in the CMS form (resolve answer, then site answer, then seo.pagination), other query parameters kept, page 1 unnumbered, <link rel=prev/next> in the head - the CMS head fills %pages% in the title and description once the listing's page count is known (seoFillPages) - seoPaginationWindow, seoPageHref, seoPageNumber; L2 default styles
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
// The page links of <SeoPagination> (UI-SPEC L2, D-42): five consecutive
|
|
// pages around the current one, plus page 1 and the last page when they
|
|
// fall outside, with a gap item where pages are skipped, so every page is
|
|
// within two hops of page 1 for a crawler. Pure, so `node --test` drives
|
|
// it. Names start with `seo` because app/utils is auto-imported into the
|
|
// consuming app (vue-journal-layer has its own paginationWindow).
|
|
|
|
export const SEO_PAGINATION_GAP = 'gap' as const
|
|
|
|
export type SeoPaginationItem = number | typeof SEO_PAGINATION_GAP
|
|
|
|
/**
|
|
* The items for page `current` of `last`: page numbers in order and 'gap'
|
|
* between page 1 or the last page and the window when pages are skipped.
|
|
* Empty when there is nothing to paginate (last < 1).
|
|
*/
|
|
export function seoPaginationWindow(current: number, last: number, size = 5): SeoPaginationItem[] {
|
|
if (!Number.isInteger(last) || last < 1) return []
|
|
const cur = Math.min(Math.max(Number.isInteger(current) ? current : 1, 1), last)
|
|
const width = Math.min(Math.max(1, size), last)
|
|
let start = Math.max(1, cur - Math.floor(width / 2))
|
|
if (start + width - 1 > last) start = last - width + 1
|
|
const end = start + width - 1
|
|
const out: SeoPaginationItem[] = []
|
|
if (start > 1) {
|
|
out.push(1)
|
|
if (start > 2) out.push(SEO_PAGINATION_GAP)
|
|
}
|
|
for (let p = start; p <= end; p++) out.push(p)
|
|
if (end < last) {
|
|
if (end < last - 1) out.push(SEO_PAGINATION_GAP)
|
|
out.push(last)
|
|
}
|
|
return out
|
|
}
|