- Next.js 16 App Router site with the ANOUMA design system - Payload CMS (PostgreSQL) for offers, events, posts and page content - WebRTC video-call system with custom signaling server - SMTP email reminders and booking-request notifications - Customer accounts, calendar-based availability, and booking workflow
93 lines
2.0 KiB
TypeScript
93 lines
2.0 KiB
TypeScript
/**
|
|
* Minimal builders for Lexical's SerializedEditorState JSON — just enough to
|
|
* seed richText fields from plain paragraph/heading strings (see lib/texte.ts).
|
|
* Shape verified against node_modules/lexical's Serialized*Node types.
|
|
*/
|
|
|
|
type TextNode = {
|
|
type: "text";
|
|
version: 1;
|
|
text: string;
|
|
format: 0;
|
|
detail: 0;
|
|
mode: "normal";
|
|
style: "";
|
|
};
|
|
|
|
type ParagraphNode = {
|
|
type: "paragraph";
|
|
version: 1;
|
|
children: TextNode[];
|
|
direction: "ltr";
|
|
format: "";
|
|
indent: 0;
|
|
};
|
|
|
|
type HeadingNode = {
|
|
type: "heading";
|
|
tag: "h2" | "h3";
|
|
version: 1;
|
|
children: TextNode[];
|
|
direction: "ltr";
|
|
format: "";
|
|
indent: 0;
|
|
};
|
|
|
|
function textNode(text: string): TextNode {
|
|
return { type: "text", version: 1, text, format: 0, detail: 0, mode: "normal", style: "" };
|
|
}
|
|
|
|
function paragraphNode(text: string): ParagraphNode {
|
|
return {
|
|
type: "paragraph",
|
|
version: 1,
|
|
children: [textNode(text)],
|
|
direction: "ltr",
|
|
format: "",
|
|
indent: 0,
|
|
};
|
|
}
|
|
|
|
function headingNode(text: string, tag: "h2" | "h3" = "h2"): HeadingNode {
|
|
return {
|
|
type: "heading",
|
|
tag,
|
|
version: 1,
|
|
children: [textNode(text)],
|
|
direction: "ltr",
|
|
format: "",
|
|
indent: 0,
|
|
};
|
|
}
|
|
|
|
function root(children: (ParagraphNode | HeadingNode)[]) {
|
|
return {
|
|
root: {
|
|
type: "root",
|
|
children,
|
|
direction: "ltr" as const,
|
|
format: "" as const,
|
|
indent: 0,
|
|
version: 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function lexicalFromParagraphs(paragraphs: readonly string[]) {
|
|
return root(paragraphs.map((p) => paragraphNode(p)));
|
|
}
|
|
|
|
export function lexicalFromSections(
|
|
intro: readonly string[],
|
|
sections?: readonly { heading: string; paragraphs: readonly string[] }[],
|
|
) {
|
|
const children: (ParagraphNode | HeadingNode)[] = intro.map((p) => paragraphNode(p));
|
|
if (sections) {
|
|
for (const section of sections) {
|
|
children.push(headingNode(section.heading));
|
|
children.push(...section.paragraphs.map((p) => paragraphNode(p)));
|
|
}
|
|
}
|
|
return root(children);
|
|
}
|