// The page links of (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 }