summaryrefslogtreecommitdiffstats
path: root/src/deps/strapi/blocks.tsx
diff options
context:
space:
mode:
authorVolpeon <git@volpeon.ink>2026-07-26 11:57:13 +0200
committerVolpeon <git@volpeon.ink>2026-07-26 11:57:13 +0200
commit5c899b4a5c2c07398151960b2b86d238b49d5bf4 (patch)
treeccc743f27ba234a25396cb24b6124c139d13b027 /src/deps/strapi/blocks.tsx
parentUse Corvos 0.1.4 (diff)
downloadcorvos-website-master.tar.gz
corvos-website-master.tar.bz2
corvos-website-master.zip
Better Strapi integrationHEADmaster
Diffstat (limited to 'src/deps/strapi/blocks.tsx')
-rw-r--r--src/deps/strapi/blocks.tsx125
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 @@
1import { ContentImage } from "$/ui/objects/ContentImage.tsx";
2
3import { type Node, type DefaultInlineNode } from "./schema.ts";
4
5export 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
26function 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
109export 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
119export const Blocks = ({
120 content,
121 filter = () => true,
122}: {
123 content: (Node | DefaultInlineNode)[];
124 filter?: (node: Node | DefaultInlineNode) => boolean;
125}) => blocksToTSX(content, filter);