diff options
| author | Volpeon <git@volpeon.ink> | 2026-07-26 11:57:13 +0200 |
|---|---|---|
| committer | Volpeon <git@volpeon.ink> | 2026-07-26 11:57:13 +0200 |
| commit | 5c899b4a5c2c07398151960b2b86d238b49d5bf4 (patch) | |
| tree | ccc743f27ba234a25396cb24b6124c139d13b027 /src/deps/strapi/blocks.tsx | |
| parent | Use Corvos 0.1.4 (diff) | |
| download | corvos-website-5c899b4a5c2c07398151960b2b86d238b49d5bf4.tar.gz corvos-website-5c899b4a5c2c07398151960b2b86d238b49d5bf4.tar.bz2 corvos-website-5c899b4a5c2c07398151960b2b86d238b49d5bf4.zip | |
Diffstat (limited to 'src/deps/strapi/blocks.tsx')
| -rw-r--r-- | src/deps/strapi/blocks.tsx | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/deps/strapi/blocks.tsx b/src/deps/strapi/blocks.tsx new file mode 100644 index 0000000..bb60f71 --- /dev/null +++ b/src/deps/strapi/blocks.tsx | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | import { ContentImage } from "$/ui/objects/ContentImage.tsx"; | ||
| 2 | |||
| 3 | import { type Node, type DefaultInlineNode } from "./schema.ts"; | ||
| 4 | |||
| 5 | export function walkBlocks( | ||
| 6 | content: (Node | DefaultInlineNode)[], | ||
| 7 | fn: (_: Node | DefaultInlineNode) => unknown, | ||
| 8 | ): void { | ||
| 9 | for (const node of content) { | ||
| 10 | fn(node); | ||
| 11 | |||
| 12 | switch (node.type) { | ||
| 13 | case "heading": | ||
| 14 | case "code": | ||
| 15 | case "quote": | ||
| 16 | case "paragraph": | ||
| 17 | case "list": | ||
| 18 | case "list-item": | ||
| 19 | case "link": { | ||
| 20 | return walkBlocks(node.children, fn); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | function blocksNodeToTSX(node: Node | DefaultInlineNode) { | ||
| 27 | switch (true) { | ||
| 28 | case node.type === "heading": { | ||
| 29 | switch (node.level) { | ||
| 30 | case 1: | ||
| 31 | return <h1>{blocksToTSX(node.children)}</h1>; | ||
| 32 | case 2: | ||
| 33 | return <h2>{blocksToTSX(node.children)}</h2>; | ||
| 34 | case 3: | ||
| 35 | return <h3>{blocksToTSX(node.children)}</h3>; | ||
| 36 | case 4: | ||
| 37 | return <h4>{blocksToTSX(node.children)}</h4>; | ||
| 38 | case 5: | ||
| 39 | return <h5>{blocksToTSX(node.children)}</h5>; | ||
| 40 | default: | ||
| 41 | return <h6>{blocksToTSX(node.children)}</h6>; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | case node.type === "image" && !!node.image.caption: { | ||
| 46 | return ( | ||
| 47 | <figure> | ||
| 48 | <ContentImage src={node.image.url} alt={node.image.alternativeText ?? undefined} /> | ||
| 49 | <figcaption>{node.image.caption}</figcaption> | ||
| 50 | </figure> | ||
| 51 | ); | ||
| 52 | } | ||
| 53 | |||
| 54 | case node.type === "image": { | ||
| 55 | return <ContentImage src={node.image.url} alt={node.image.alternativeText ?? undefined} />; | ||
| 56 | } | ||
| 57 | |||
| 58 | case node.type === "code": { | ||
| 59 | return ( | ||
| 60 | <pre> | ||
| 61 | <code>{blocksToTSX(node.children)}</code> | ||
| 62 | </pre> | ||
| 63 | ); | ||
| 64 | } | ||
| 65 | |||
| 66 | case node.type === "quote": { | ||
| 67 | return <blockquote>{blocksToTSX(node.children)}</blockquote>; | ||
| 68 | } | ||
| 69 | |||
| 70 | case node.type === "paragraph" && | ||
| 71 | node.children.length === 1 && | ||
| 72 | node.children[0]?.type === "text" && | ||
| 73 | node.children[0].text === "---": { | ||
| 74 | return <hr />; | ||
| 75 | } | ||
| 76 | |||
| 77 | case node.type === "paragraph": { | ||
| 78 | return <p>{blocksToTSX(node.children)}</p>; | ||
| 79 | } | ||
| 80 | |||
| 81 | case node.type === "list" && node.format === "ordered": { | ||
| 82 | return <ol>${blocksToTSX(node.children)}</ol>; | ||
| 83 | } | ||
| 84 | |||
| 85 | case node.type === "list": { | ||
| 86 | return <ul>${blocksToTSX(node.children)}</ul>; | ||
| 87 | } | ||
| 88 | |||
| 89 | case node.type === "list-item": { | ||
| 90 | return <li>${blocksToTSX(node.children)}</li>; | ||
| 91 | } | ||
| 92 | |||
| 93 | case node.type === "link": { | ||
| 94 | return <a href={node.url}>{blocksToTSX(node.children)}</a>; | ||
| 95 | } | ||
| 96 | |||
| 97 | case node.type === "text": { | ||
| 98 | return [ | ||
| 99 | node.bold ? "strong" : undefined, | ||
| 100 | node.code ? "code" : undefined, | ||
| 101 | node.italic ? "em" : undefined, | ||
| 102 | node.strikethrough ? "del" : undefined, | ||
| 103 | node.underline ? "u" : undefined, | ||
| 104 | ].reduce((res: JSX.Element, Tag) => (Tag ? <Tag>{res}</Tag> : res), node.text); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | export function blocksToTSX( | ||
| 110 | content: (Node | DefaultInlineNode)[], | ||
| 111 | filter: (node: Node | DefaultInlineNode) => boolean = () => true, | ||
| 112 | ): JSX.Element { | ||
| 113 | return content | ||
| 114 | .filter(filter) | ||
| 115 | .map(blocksNodeToTSX) | ||
| 116 | .filter((s) => s !== undefined); | ||
| 117 | } | ||
| 118 | |||
| 119 | export const Blocks = ({ | ||
| 120 | content, | ||
| 121 | filter = () => true, | ||
| 122 | }: { | ||
| 123 | content: (Node | DefaultInlineNode)[]; | ||
| 124 | filter?: (node: Node | DefaultInlineNode) => boolean; | ||
| 125 | }) => blocksToTSX(content, filter); | ||
