64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
import { convertLexicalToHTML } from "@payloadcms/richtext-lexical/html";
|
|
import { richTextToPlain } from "@carpa/typesense";
|
|
import { stateHtmlConverters } from "./textStateConverter";
|
|
|
|
/**
|
|
* Split a Payload document's Lexical `content` into one paragraph document per
|
|
* top-level block. Each keeps the block's HTML (for display) and its plain
|
|
* text (for search), tagged with the parent id (under `parentField`), locale
|
|
* and order. Empty/spacer blocks are skipped, and `order` is contiguous.
|
|
*
|
|
* Colors/fonts are carried on each text node's TextStateFeature `$` state and
|
|
* emitted as `text-[#hex]`/`font-<name>` classes by `stateHtmlConverters`
|
|
* (pure, jsdom-free — safe in the Payload/Next live-sync path). This is the
|
|
* single source for both migrated and admin-authored content.
|
|
*
|
|
* `parentField` is the relationship key on the child docs, e.g. "activity" for
|
|
* activities_paragraphs or "conference" for conferences_paragraphs.
|
|
*/
|
|
export function splitParagraphs(
|
|
doc: Record<string, any>,
|
|
parentField: string,
|
|
): Record<string, any>[] {
|
|
const content = doc?.content;
|
|
const root = content?.root;
|
|
if (!root || !Array.isArray(root.children)) return [];
|
|
|
|
const parentId = String(doc.id);
|
|
const locale = doc.locale ? String(doc.locale) : undefined;
|
|
const paragraphs: Record<string, any>[] = [];
|
|
let order = 0;
|
|
|
|
for (const node of root.children) {
|
|
const text = richTextToPlain(node).trim();
|
|
if (!text) continue; // skip empty spacer blocks
|
|
|
|
const html = convertLexicalToHTML({
|
|
data: { ...content, root: { ...root, children: [node] } } as any,
|
|
disableContainer: true,
|
|
// Emit color/font from each text node's TextStateFeature `$` state.
|
|
converters: stateHtmlConverters as any,
|
|
}).trim();
|
|
|
|
paragraphs.push({
|
|
id: `${parentId}-${order}`,
|
|
html: html || undefined,
|
|
text,
|
|
locale,
|
|
[parentField]: parentId,
|
|
order,
|
|
});
|
|
order++;
|
|
}
|
|
|
|
return paragraphs;
|
|
}
|
|
|
|
/** Split an activity's content into activities_paragraphs docs. */
|
|
export const splitActivityParagraphs = (doc: Record<string, any>) =>
|
|
splitParagraphs(doc, "activity");
|
|
|
|
/** Split a conference's content into conferences_paragraphs docs. */
|
|
export const splitConferenceParagraphs = (doc: Record<string, any>) =>
|
|
splitParagraphs(doc, "conference");
|