diff --git a/packages/dito/app/components/AbsatzRenderer.tsx b/packages/dito/app/components/AbsatzRenderer.tsx new file mode 100644 index 00000000..34bbebe4 --- /dev/null +++ b/packages/dito/app/components/AbsatzRenderer.tsx @@ -0,0 +1,84 @@ +import { BlocksContent } from "@strapi/blocks-react-renderer"; +import { ReactElement } from "react"; + +type InternalNode = { + type: string; + children: (TextNode | InternalNode)[]; + format?: "unordered" | "ordered"; +}; +type TextNode = { type: "text"; text: string }; + +const isInternalNode = (node: InternalNode | TextNode): node is InternalNode => + node.type !== "text"; + +/* Splits a string containing custom <1>... markup into HTML elements. + * Replaces the tags for the relevant principles with elements. + * Returns the rest as . + */ +const renderTextWithMarks = ( + text: string, + principlesToFilter: number[], +): ReactElement[] => { + const parts = text.split(/(<\d>.*?<\/\d>)/g); + return parts.map((part, index) => { + const match = part.match(/^<(\d)>(.*?)<\/\1>$/); + if (match) { + const num = match[1]; + const content = match[2]; + return principlesToFilter.includes(Number(num)) ? ( + + {content} + + ) : ( + {content} + ); + } + return {part}; + }); +}; + +/* Traverses the tree structure of Strapi BlockContent and renders the elements as nodes. + * Internal nodes have children, for which the renderNode function is called recursively. + * Leaf nodes are TextNodes, whose string content will be transformed by renderTextWithMarks. + * Only supports a very limited set of Strapi BlockEditor elements. + */ +// TODO: check nested lists +// TODO: write tests +const renderNode = ( + node: InternalNode | TextNode, + principlesToFilter: number[], + index: number, +): ReactElement | ReactElement[] => { + if (isInternalNode(node)) { + const children = node.children.map((child, index) => + renderNode(child, principlesToFilter, index), + ); + switch (node.type) { + case "paragraph": + return

{children}

; + case "list": + return node.format === "ordered" ? ( +
    {children}
+ ) : ( + + ); + case "list-item": + return
  • {children}
  • ; + default: + return <>{children}; + } + } + return renderTextWithMarks(node.text, principlesToFilter); +}; + +export default function AbsatzRenderer({ + text, + principlesToFilter, +}: { + text: BlocksContent; + principlesToFilter: number[]; +}) { + return text.map((block, index) => + renderNode(block, principlesToFilter, index), + ); +} diff --git a/packages/dito/app/components/ParagraphList.tsx b/packages/dito/app/components/ParagraphList.tsx index 760b1389..0b4b9db5 100644 --- a/packages/dito/app/components/ParagraphList.tsx +++ b/packages/dito/app/components/ParagraphList.tsx @@ -1,16 +1,8 @@ import DetailsSummary from "@digitalcheck/shared/components/DetailsSummary.tsx"; import Heading from "@digitalcheck/shared/components/Heading.tsx"; -import { - type BlocksContent, - BlocksRenderer, -} from "@strapi/blocks-react-renderer"; +import { BlocksRenderer } from "@strapi/blocks-react-renderer"; import type { Paragraph, Prinzip } from "../utils/strapiData.server.ts"; - -const AbsatzRenderer = ({ text }: { text: BlocksContent }) => { - const blockOutput = BlocksRenderer({ content: text }); - console.log("AbsatzRenderer", blockOutput); - return blockOutput; -}; +import AbsatzRenderer from "./AbsatzRenderer.tsx"; function Paragraph({ paragraph, @@ -46,7 +38,12 @@ function Paragraph({ 0} - content={} + content={ + + } /> ))}