import React from "react"; export type EditorJsParagraphBlock = { id?: string; type: "paragraph"; data: { text?: string } }; export type EditorJsHeaderBlock = { id?: string; type: "header"; data: { text?: string; level?: number } }; export type EditorJsListBlock = { id?: string; type: "list"; data: { style?: "ordered" | "unordered"; items?: string[] } }; export type EditorJsQuoteBlock = { id?: string; type: "quote"; data: { text?: string; caption?: string; alignment?: string } }; export type EditorJsLinkBlock = { id?: string; type: "linkTool"; data: { link?: string; meta?: { title?: string; description?: string; image?: { url?: string } } } }; export type EditorJsImageBlock = { id?: string; type: "image"; data: { file?: { url?: string }; caption?: string; withBorder?: boolean; withBackground?: boolean; stretched?: boolean } }; export type EditorJsDelimiterBlock = { id?: string; type: "delimiter"; data?: Record }; export type EditorJsTableBlock = { id?: string; type: "table"; data: { withHeadings?: boolean; content?: string[][] } }; export type EditorJsCodeBlock = { id?: string; type: "code"; data: { code?: string } }; export type EditorJsGenericBlock = { id?: string; type: string; data?: Record | null }; export type EditorJsBlock = | EditorJsParagraphBlock | EditorJsHeaderBlock | EditorJsListBlock | EditorJsQuoteBlock | EditorJsLinkBlock | EditorJsImageBlock | EditorJsDelimiterBlock | EditorJsTableBlock | EditorJsCodeBlock | EditorJsGenericBlock; export type EditorJsDoc = { time?: number; blocks?: EditorJsBlock[]; version?: string }; export function parseEditorJsToSections(content: string | null | undefined) { if (!content) return null; try { const parsed = JSON.parse(content) as EditorJsDoc; const blocks = parsed.blocks || []; if (!blocks.length) return null; const sections: Array<{ title: string; description: React.ReactNode }> = []; let currentTitle = ""; let currentContent: React.ReactNode[] = []; const flushCurrentSection = () => { if (currentTitle && currentContent.length > 0) { sections.push({ title: currentTitle, description: (
{currentContent.map((content, idx) => (
{content}
))}
) }); currentContent = []; } }; blocks.forEach((b: EditorJsBlock, idx: number) => { const key = b.id || String(idx); if (b.type === "header") { const headerData = (b as EditorJsHeaderBlock).data; const level = Math.min(6, Math.max(1, Number(headerData?.level) || 2)); const html = String(headerData?.text || ""); // H3 starts a new section if (level === 3) { flushCurrentSection(); // Strip HTML tags for the title, but keep the HTML for rendering currentTitle = html.replace(/<[^>]*>/g, ""); } else if (level === 1) { // H1 is the main title, skip it as it's already in the page heading return; } else { // Other headers become content if (level === 2) { currentContent.push(

); } else if (level === 4) { currentContent.push(

); } else if (level === 5) { currentContent.push(

); } else { currentContent.push(
); } } } else if (b.type === "paragraph") { const paragraphData = (b as EditorJsParagraphBlock).data; const html = String(paragraphData?.text || ""); currentContent.push(

); } else if (b.type === "list") { const listData = (b as EditorJsListBlock).data || {}; const style = listData.style === "ordered" ? "ol" : "ul"; const items: string[] = Array.isArray(listData.items) ? listData.items : []; if (style === "ol") { currentContent.push(

    {items.map((it, i) => (
  1. ))}
); } else { currentContent.push(
    {items.map((it, i) => (
  • ))}
); } } else if (b.type === "quote") { const quoteData = (b as EditorJsQuoteBlock).data || {}; const text = String(quoteData.text || ""); const caption = String(quoteData.caption || ""); const alignment = quoteData.alignment || "left"; currentContent.push(
{caption ? ( ) : null}
); } else if (b.type === "linkTool") { const linkData = (b as EditorJsLinkBlock).data || {}; const link = linkData.link || ""; const title = linkData.meta?.title || link; const description = linkData.meta?.description || ""; const image = linkData.meta?.image?.url || ""; currentContent.push(
{image && ( {title} )}

{title}

{description && (

{description}

)}

{new URL(link).hostname}

); } else if (b.type === "image") { const imageData = (b as EditorJsImageBlock).data || {}; const url = imageData.file?.url || ""; const caption = imageData.caption || ""; const withBorder = imageData.withBorder; const withBackground = imageData.withBackground; const stretched = imageData.stretched; if (url) { currentContent.push(
{caption {caption && (
{caption}
)}
); } } else if (b.type === "delimiter") { currentContent.push(
); } else if (b.type === "table") { const tableData = (b as EditorJsTableBlock).data || {}; const withHeadings = tableData.withHeadings || false; const content = tableData.content || []; if (content.length > 0) { currentContent.push(
{withHeadings && content[0] && ( {content[0].map((cell, cellIdx) => ( ))} )} {content.slice(withHeadings ? 1 : 0).map((row, rowIdx) => ( {row.map((cell, cellIdx) => ( ))} ))}
{cell}
{cell}
); } } else if (b.type === "code") { const codeData = (b as EditorJsCodeBlock).data || {}; const code = codeData.code || ""; currentContent.push(
            {code}
          
); } }); // Flush the last section flushCurrentSection(); return sections; } catch { return null; } }