summaryrefslogtreecommitdiffstats
path: root/src/deps/strapi/blocks.tsx
blob: bb60f714961a79ecd9ba01f48c9cbcaa6f4e7662 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { ContentImage } from "$/ui/objects/ContentImage.tsx";

import { type Node, type DefaultInlineNode } from "./schema.ts";

export function walkBlocks(
	content: (Node | DefaultInlineNode)[],
	fn: (_: Node | DefaultInlineNode) => unknown,
): void {
	for (const node of content) {
		fn(node);

		switch (node.type) {
			case "heading":
			case "code":
			case "quote":
			case "paragraph":
			case "list":
			case "list-item":
			case "link": {
				return walkBlocks(node.children, fn);
			}
		}
	}
}

function blocksNodeToTSX(node: Node | DefaultInlineNode) {
	switch (true) {
		case node.type === "heading": {
			switch (node.level) {
				case 1:
					return <h1>{blocksToTSX(node.children)}</h1>;
				case 2:
					return <h2>{blocksToTSX(node.children)}</h2>;
				case 3:
					return <h3>{blocksToTSX(node.children)}</h3>;
				case 4:
					return <h4>{blocksToTSX(node.children)}</h4>;
				case 5:
					return <h5>{blocksToTSX(node.children)}</h5>;
				default:
					return <h6>{blocksToTSX(node.children)}</h6>;
			}
		}

		case node.type === "image" && !!node.image.caption: {
			return (
				<figure>
					<ContentImage src={node.image.url} alt={node.image.alternativeText ?? undefined} />
					<figcaption>{node.image.caption}</figcaption>
				</figure>
			);
		}

		case node.type === "image": {
			return <ContentImage src={node.image.url} alt={node.image.alternativeText ?? undefined} />;
		}

		case node.type === "code": {
			return (
				<pre>
					<code>{blocksToTSX(node.children)}</code>
				</pre>
			);
		}

		case node.type === "quote": {
			return <blockquote>{blocksToTSX(node.children)}</blockquote>;
		}

		case node.type === "paragraph" &&
			node.children.length === 1 &&
			node.children[0]?.type === "text" &&
			node.children[0].text === "---": {
			return <hr />;
		}

		case node.type === "paragraph": {
			return <p>{blocksToTSX(node.children)}</p>;
		}

		case node.type === "list" && node.format === "ordered": {
			return <ol>${blocksToTSX(node.children)}</ol>;
		}

		case node.type === "list": {
			return <ul>${blocksToTSX(node.children)}</ul>;
		}

		case node.type === "list-item": {
			return <li>${blocksToTSX(node.children)}</li>;
		}

		case node.type === "link": {
			return <a href={node.url}>{blocksToTSX(node.children)}</a>;
		}

		case node.type === "text": {
			return [
				node.bold ? "strong" : undefined,
				node.code ? "code" : undefined,
				node.italic ? "em" : undefined,
				node.strikethrough ? "del" : undefined,
				node.underline ? "u" : undefined,
			].reduce((res: JSX.Element, Tag) => (Tag ? <Tag>{res}</Tag> : res), node.text);
		}
	}
}

export function blocksToTSX(
	content: (Node | DefaultInlineNode)[],
	filter: (node: Node | DefaultInlineNode) => boolean = () => true,
): JSX.Element {
	return content
		.filter(filter)
		.map(blocksNodeToTSX)
		.filter((s) => s !== undefined);
}

export const Blocks = ({
	content,
	filter = () => true,
}: {
	content: (Node | DefaultInlineNode)[];
	filter?: (node: Node | DefaultInlineNode) => boolean;
}) => blocksToTSX(content, filter);