Files
vue-seo-layer/app/composables/useCmsSeo.ts
Jakub Zych f2d13d6264 feat: pagination component and composable
- <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
2026-11-04 11:59:51 +01:00

34 lines
1.6 KiB
TypeScript

// The page head from the CMS resolve answer: title, description, robots,
// canonical, Open Graph (with og:locale and its alternates), X/Twitter,
// verification meta, <html lang>, hreflang links and exactly one JSON-LD
// script holding one @graph (the CMS nodes plus those added through
// useSchemaNode, merged by @id). A listing's %pages% in the CMS title and
// description becomes its number of pages once usePagination() knows it. One entry with tagPriority 'critical', so
// its tags win over the same tags a page, a layer or a module sets; a value
// the answer lacks adds no tag and the page's own stays. The layer's app
// plugin (seo-head) is the only caller: pages and other layers never set CMS
// values themselves.
import type { Ref } from 'vue'
import { mergeGraph } from '../utils/graph.ts'
import { seoFillPages, seoHeadInput } from '../utils/seo.ts'
import type { SeoResolveAnswer } from '../utils/types.ts'
export function useCmsSeo(answer: Ref<SeoResolveAnswer | null>): void {
const origin = useRequestURL().origin
const nodes = useSchemaNodes()
const pages = useSeoPages()
useHead(
computed(() => {
const head = seoFillPages(seoHeadInput(answer.value, origin), pages.value)
const cmsGraph = answer.value?.action === 'ok' ? answer.value.jsonld : null
const graph = mergeGraph(cmsGraph, nodes())
// unhead serializes the object and escapes "<" inside it, so a value
// such as "</script>" cannot end the script element.
const script = graph ? [{ type: 'application/ld+json', key: 'seo-jsonld', innerHTML: graph }] : []
return { ...head, script }
}),
{ tagPriority: 'critical' },
)
}