- post, index, category, tag and author pages render <SeoBreadcrumbs> at the top of their content while journal.breadcrumbs is on (default); false leaves the trail to the site's layout, the BreadcrumbList data is unchanged - JournalPagination renders <SeoPagination class-prefix="journal-pagination"> with the journal's labels: the same class names, plus page 1, the last page, gap items, aria labels and head rel prev/next, with links in the CMS's pagination form - with vue-seo-layer's seo.pagination.form 'path' the module registers <listing>/<segment>/:page routes (journalPagedRoutes), and the listing pages read the page from the :page param or ?page (pageFromRoute) - the category pillar text renders on page 1 only, the description on every page; an h1 in it becomes h2 while the category row's content_h1_to_h2 is on (absent counts as on)
48 lines
1.9 KiB
TypeScript
48 lines
1.9 KiB
TypeScript
// Page-number rules for Journal listings. Pages use ?page=N, or the path
|
|
// form <listing>/<segment>/N when the site's pagination form is path (the
|
|
// page route's :page param). Anything but a positive integer, and any page
|
|
// past the last one, is a real 404.
|
|
|
|
/**
|
|
* Parses the ?page query value: 1 when absent, the integer for a string of
|
|
* digits greater than 0, and null (a 404) for anything else: non-numeric,
|
|
* empty, 0, negative, decimal, repeated (array) or beyond the safe range.
|
|
*/
|
|
export function parsePageParam(raw: unknown): number | null {
|
|
if (raw === undefined || raw === null) return 1
|
|
if (typeof raw !== 'string' || !/^\d+$/.test(raw)) return null
|
|
const n = Number(raw)
|
|
if (!Number.isSafeInteger(n) || n < 1) return null
|
|
return n
|
|
}
|
|
|
|
/**
|
|
* The page of a listing route: its :page param in the path form, else the
|
|
* ?page query value, parsed as parsePageParam does (null is a 404).
|
|
*/
|
|
export function pageFromRoute(route: { params?: Record<string, unknown>; query?: Record<string, unknown> }): number | null {
|
|
const param = route.params?.page
|
|
if (param !== undefined && param !== null && param !== '') return parsePageParam(param)
|
|
return parsePageParam(route.query?.page)
|
|
}
|
|
|
|
/**
|
|
* True when `page` lies past the last page. Page 1 is always in range, so an
|
|
* empty listing renders its empty state instead of a 404.
|
|
*/
|
|
export function pageOutOfRange(page: number, lastPage: number): boolean {
|
|
return page > 1 && page > lastPage
|
|
}
|
|
|
|
/**
|
|
* The page numbers to render around `current`: at most `size` consecutive
|
|
* pages, clipped to 1..last and kept full-width near either end.
|
|
*/
|
|
export function paginationWindow(current: number, last: number, size: number = 5): number[] {
|
|
if (last < 1 || size < 1) return []
|
|
const width = Math.min(size, last)
|
|
let start = Math.max(1, current - Math.floor(width / 2))
|
|
if (start + width - 1 > last) start = last - width + 1
|
|
return Array.from({ length: width }, (_, i) => start + i)
|
|
}
|