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
use markdown::mdast::Node;
use markdown::*;
use yew::prelude::*;
#[derive(Properties, PartialEq)]
pub struct MarkdownProps {
pub source: String,
pub render: Callback<String, Html>,
}
#[function_component(Markdown)]
pub fn markdown(props: &MarkdownProps) -> Html {
let ast = use_memo(
|string| to_mdast(&string, &ParseOptions {
constructs: Constructs {
gfm_table: true,
..Constructs::gfm()
},
..ParseOptions::gfm()
}),
props.source.clone(),
);
if let Ok(node) = &*ast {
let node = node.clone();
let render = props.render.clone();
html! {
<MarkdownNode {node} {render} />
}
} else {
html! {{"Error"}}
}
}
#[derive(Properties, PartialEq)]
pub struct MarkdownNodesProps {
pub nodes: Vec<Node>,
pub render: Callback<String, Html>,
}
#[function_component(MarkdownNodes)]
pub fn markdown_nodes(props: &MarkdownNodesProps) -> Html {
props
.nodes
.iter()
.cloned()
.map(|node| html!{ <MarkdownNode {node} render={props.render.clone()} /> })
.collect::<Html>()
}
#[derive(Properties, PartialEq)]
pub struct MarkdownNodeProps {
pub node: Node,
pub render: Callback<String, Html>,
}
#[function_component(MarkdownNode)]
pub fn markdown_node(props: &MarkdownNodeProps) -> Html {
let render = props.render.clone();
match &props.node {
Node::Root(root) => html!(
<div class="markdown">
<MarkdownNodes nodes={root.children.clone()} {render} />
</div>
),
Node::BlockQuote(quote) => {
let nodes = quote.children.clone();
html!(<blockquote><MarkdownNodes {nodes} {render} /></blockquote>)
},
Node::List(list) if !list.ordered => {
let nodes = list.children.clone();
html!(<ul><MarkdownNodes {nodes} {render} /></ul>)
},
Node::InlineCode(code) => html!(<code>{&code.value}</code>),
Node::Delete(delete) => html!(<strike><MarkdownNodes nodes={delete.children.clone()} {render} /></strike>),
Node::Heading(heading) => {
let nodes = heading.children.clone();
match heading.depth {
1 => html!{<h1><MarkdownNodes {nodes} {render} /></h1>},
2 => html!{<h2><MarkdownNodes {nodes} {render} /></h2>},
3 => html!{<h3><MarkdownNodes {nodes} {render} /></h3>},
4 => html!{<h4><MarkdownNodes {nodes} {render} /></h4>},
5 => html!{<h5><MarkdownNodes {nodes} {render} /></h5>},
6 => html!{<h6><MarkdownNodes {nodes} {render} /></h6>},
_ => html!({"Unsupported heading"}),
}
},
Node::Text(text) => html!({&text.value}),
Node::Paragraph(paragraph) => {
let nodes = paragraph.children.clone();
html!{<p><MarkdownNodes {nodes} {render} /></p>}
},
Node::ThematicBreak(_) => html!{<hr />},
Node::Emphasis(emphasis) => {
let nodes = emphasis.children.clone();
html!{<em><MarkdownNodes {nodes} {render} /></em>}
},
Node::Strong(strong) => {
let nodes = strong.children.clone();
html!{<strong><MarkdownNodes {nodes} {render} /></strong>}
},
Node::Code(code) => html!(<pre><code>{&code.value}</code></pre>),
Node::Link(link) => html!(<a href={link.url.clone()}><MarkdownNodes nodes={link.children.clone()} {render} /></a>),
Node::Break(_) => html!(<br />),
Node::List(list) if list.ordered => {
let nodes = list.children.clone();
html!(<ol><MarkdownNodes {nodes} {render} /></ol>)
},
Node::ListItem(item) => {
let nodes = item.children.clone();
html!(<li><MarkdownNodes {nodes} {render} /></li>)
},
Node::Html(html) => render.emit(html.value.clone()),
node => html!{{format!("Error: {node:?}")}},
}
}