50 lines
2.1 KiB
TypeScript
50 lines
2.1 KiB
TypeScript
import type { JSXConvertersFunction } 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`.
|
|
export const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
|
|
...defaultConverters,
|
|
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.
|
|
if (fields?.appearance !== 'button') {
|
|
const defaultLink = defaultConverters.link
|
|
return typeof defaultLink === 'function' ? defaultLink(args) : defaultLink
|
|
}
|
|
|
|
// 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`.
|
|
const href =
|
|
fields.linkType === 'internal' && typeof fields.doc?.value === 'object'
|
|
? `/${(fields.doc.value as { slug?: string }).slug ?? ''}`
|
|
: (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}
|
|
size="md"
|
|
schema="contrast"
|
|
target={fields.newTab ? '_blank' : '_self'}
|
|
>
|
|
{nodesToJSX({ nodes: node.children })}
|
|
</Button>
|
|
)
|
|
},
|
|
})
|