fix: internal link
This commit is contained in:
parent
776bc6ff25
commit
a85c0e609a
1 changed files with 35 additions and 18 deletions
|
|
@ -1,41 +1,58 @@
|
|||
import type { JSXConvertersFunction } from '@payloadcms/richtext-lexical/react'
|
||||
import { LinkJSXConverter } from '@payloadcms/richtext-lexical/react'
|
||||
import type { SerializedLinkNode } from '@payloadcms/richtext-lexical'
|
||||
import { Button } from '@/components/Button/Button'
|
||||
|
||||
// Lexical link nodes carry their editable metadata (url, newTab, linkType, ...)
|
||||
// in `fields`. We extend that shape with the custom `appearance` select that is
|
||||
// added to LinkFeature in `payload.config.ts`, so editors can mark a link as a
|
||||
// call-to-action button.
|
||||
type LinkFields = SerializedLinkNode['fields'] & {
|
||||
appearance?: 'link' | 'button'
|
||||
}
|
||||
|
||||
// Custom JSX converters passed to <RichText /> wherever rich text is rendered.
|
||||
// Only the `link` converter is overridden — every other node keeps Payload's
|
||||
// default rendering via the spread of `defaultConverters`.
|
||||
// Maps an internal Payload document to its public URL based on collection slug.
|
||||
const internalDocToHref = ({ linkNode }: { linkNode: SerializedLinkNode }): string => {
|
||||
const { doc } = linkNode.fields
|
||||
if (!doc?.value || typeof doc.value !== 'object') return '#'
|
||||
|
||||
const document = doc.value as Record<string, unknown>
|
||||
|
||||
switch (doc.relationTo) {
|
||||
case 'group':
|
||||
return `/gruppe/${document.slug ?? document.id}`
|
||||
case 'event':
|
||||
return `/veranstaltungen/${document.id}`
|
||||
case 'worship':
|
||||
return `/gottesdienst/${document.id}`
|
||||
case 'pages':
|
||||
return `/${document.slug ?? ''}`
|
||||
case 'parish':
|
||||
return `/gemeinde/${document.slug ?? document.id}`
|
||||
case 'blog':
|
||||
return `/blog/${document.id}`
|
||||
default:
|
||||
return '#'
|
||||
}
|
||||
}
|
||||
|
||||
const linkConverters = LinkJSXConverter({ internalDocToHref })
|
||||
|
||||
export const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
|
||||
...defaultConverters,
|
||||
...linkConverters,
|
||||
link: (args) => {
|
||||
const { node, nodesToJSX } = args
|
||||
const fields = node.fields as LinkFields
|
||||
|
||||
// Normal link → delegate to Payload's built-in link converter.
|
||||
// The default converter from the package is a function, but the type
|
||||
// also allows a static ReactNode, so we narrow before calling.
|
||||
// Normal link → delegate to the internalDocToHref-aware link converter.
|
||||
if (fields?.appearance !== 'button') {
|
||||
const defaultLink = defaultConverters.link
|
||||
return typeof defaultLink === 'function' ? defaultLink(args) : defaultLink
|
||||
const linkConverter = linkConverters.link
|
||||
return typeof linkConverter === 'function' ? linkConverter(args) : linkConverter
|
||||
}
|
||||
|
||||
// Button link → resolve href the same way Payload's default converter does:
|
||||
// internal links point at the related doc's slug, custom links use `url`.
|
||||
// Button link → resolve href using internalDocToHref for internal links.
|
||||
const href =
|
||||
fields.linkType === 'internal' && typeof fields.doc?.value === 'object'
|
||||
? `/${(fields.doc.value as { slug?: string }).slug ?? ''}`
|
||||
fields.linkType === 'internal'
|
||||
? internalDocToHref({ linkNode: node })
|
||||
: (fields.url ?? '#')
|
||||
|
||||
// Render the existing Button component. Schema and size are intentionally
|
||||
// hardcoded — editors only choose link vs. button, not the styling.
|
||||
return (
|
||||
<Button
|
||||
href={href}
|
||||
|
|
|
|||
Loading…
Reference in a new issue