diff --git a/app/components/RichText.tsx b/app/components/RichText.tsx index aa4daf7..970c11d 100644 --- a/app/components/RichText.tsx +++ b/app/components/RichText.tsx @@ -1,7 +1,10 @@ import Image from "next/image"; +import Link from "next/link"; import { RichText as LexicalRichText, type JSXConvertersFunction } from "@payloadcms/richtext-lexical/react"; import type { TOCSection } from "./SectionTOC"; import { QuoteLabel } from "./QuoteLabel"; +import { StepArrow } from "./StepArrow"; +import { STEP_ICONS, SYMBOLIC_ICONS } from "./icons/StepIcons"; // Switched 2026-07-24 from a small hand-rolled Lexical JSON->JSX walker to // Payload's own official React renderer + custom JSXConverters — needed @@ -89,12 +92,29 @@ type ImageBlockFields = { image: MediaRef; caption?: string | null }; type ImageGalleryBlockFields = { images: { image: MediaRef; caption?: string | null }[] }; type VideoEmbedBlockFields = { url: string; caption?: string | null }; type QuoteBlockFields = { text: string; label?: string | null }; +type IconBlockFields = { icon: string }; +type PillListBlockFields = { items: { id?: string | null; label: string }[] }; +type ChecklistImageBlockFields = { image: MediaRef; items: { id?: string | null; text: string }[] }; +type TableBlockFields = { labelHeader: string; valueHeader: string; rows: { id?: string | null; label: string; value: string }[] }; +type StepRowBlockFields = { + items: { id?: string | null; icon: string; title: string; subtitle?: string | null; description: string }[]; +}; +type CtaCardBlockFields = { eyebrow: string; title: string; description?: string | null; href: string }; function BlockCaption({ caption }: { caption?: string | null }) { if (!caption) return null; return
{caption}
; } +// Same checkmark as PageBlocks.tsx's own — see that file's comment. +function CheckIcon() { + return ( + + ); +} + // Same visual treatment as the QuoteBlock converter below (and the native // blockquote case it replaces going forward) — see that converter's own // comment for why both still exist. @@ -250,6 +270,121 @@ function buildConverters(quoteLabel: string): JSXConvertersFunction { ); }, + // Same page-builder blocks as PageBlocks.tsx (Pages.layout) — see + // that file's own comment on each block's visual treatment, mirrored + // here field-for-field since it's the exact same Block config + // registered a second time (Posts.content's BlocksFeature) so blog + // posts can use them mid-article too. `testimonialsRef` is the one + // page-builder block deliberately NOT included: it needs an async + // data fetch, and this converter set is shared with LiveRichText's + // client-side live-preview rendering (see blog's LivePostContent.tsx), + // where an async component would break — PageBlocks.tsx only gets + // away with it by awaiting outside the sync per-block switch, in its + // own async server component. + icon: ({ node }: { node: { fields: unknown } }) => { + const fields = node.fields as IconBlockFields; + const SymbolIcon = SYMBOLIC_ICONS[fields.icon]; + return SymbolIcon ?{item.text}
+| {fields.labelHeader} | +{fields.valueHeader} | +
|---|---|
| {row.label} | +{row.value} | +
{item.title}
+ {item.subtitle &&{item.subtitle}
} +{item.description}
+{fields.eyebrow}
+{fields.title}
+ {fields.description &&{fields.description}
} +