vizia_core/views/
markdown.rs
1#![cfg(feature = "markdown")]
2
3use std::cell::RefCell;
4
5use comrak::nodes::{Ast, NodeValue};
6use comrak::{parse_document, Arena, Options};
7
8use crate::prelude::*;
9
10pub struct Markdown {}
12
13impl Markdown {
14 pub fn new<'a>(cx: &'a mut Context, document: &str) -> Handle<'a, Self> {
16 Self {}
17 .build(cx, |cx| {
18 let arena = Arena::new();
20
21 let mut options = Options::default();
22 options.extension.strikethrough = true;
23 options.extension.table = true;
24
25 let root = parse_document(&arena, document, &options);
27
28 for node in root.children() {
29 parse_node(cx, node, 0);
30 }
31 })
32 .height(Auto)
33 }
34}
35
36impl View for Markdown {
37 fn element(&self) -> Option<&'static str> {
38 Some("markdown")
39 }
40}
41
42fn parse_node<'a>(
43 cx: &mut Context,
44 node: &'a comrak::arena_tree::Node<'a, RefCell<Ast>>,
45 list_level: usize,
46) {
47 match &node.data.borrow().value {
48 NodeValue::Paragraph => {
49 Label::rich(cx, "", |cx| {
50 for child in node.children() {
51 parse_node(cx, child, list_level);
52 }
53 })
54 .class("p");
55 }
56
57 NodeValue::Heading(heading) => {
58 Label::rich(cx, "", |cx| {
59 for child in node.children() {
60 parse_node(cx, child, list_level);
61 }
62 })
63 .class(match heading.level {
64 1 => "h1",
65 2 => "h2",
66 3 => "h3",
67 4 => "h4",
68 5 => "h5",
69 6 => "h6",
70 _ => "h6",
71 });
72 }
73
74 NodeValue::Text(text) => {
75 TextSpan::new(cx, text, |_| {}).class("span");
76 }
77
78 NodeValue::Emph => {
79 TextSpan::new(cx, "", |cx| {
80 for child in node.children() {
81 parse_node(cx, child, list_level);
82 }
83 })
84 .class("emph");
85 }
86
87 NodeValue::Strong => {
88 TextSpan::new(cx, "", |cx| {
89 for child in node.children() {
90 parse_node(cx, child, list_level);
91 }
92 })
93 .class("strong");
94 }
95
96 NodeValue::Strikethrough => {
97 TextSpan::new(cx, "", |cx| {
98 for child in node.children() {
99 parse_node(cx, child, list_level);
100 }
101 })
102 .class("strikethrough");
103 }
104
105 NodeValue::List(_list) => {
106 VStack::new(cx, |cx| {
107 for child in node.children() {
108 parse_node(cx, child, list_level);
109 }
110 })
111 .height(Auto)
112 .left(Pixels(20.0));
113 }
114
115 NodeValue::Item(_list) => {
116 HStack::new(cx, |cx| {
117 Label::new(cx, "\u{2022} ").width(Auto);
118 VStack::new(cx, |cx| {
119 for child in node.children() {
120 parse_node(cx, child, list_level + 1);
121 }
122 })
123 .height(Auto);
124 })
125 .class("li")
126 .height(Auto);
127 }
128
129 NodeValue::Code(code) => {
130 TextSpan::new(cx, &code.literal.to_owned(), |_| {}).class("code");
131 }
132
133 NodeValue::CodeBlock(code_block) => {
134 let mut code = code_block.literal.to_owned();
135 code.pop().unwrap();
136 ScrollView::new(cx, |cx| {
137 Label::new(cx, code).class("code");
138 })
139 .show_vertical_scrollbar(false)
140 .height(Auto)
141 .width(Stretch(1.0));
142 }
143
144 NodeValue::Link(link) => {
145 let url = link.url.clone();
146 TextSpan::new(cx, "", |cx| {
147 for child in node.children() {
148 parse_node(cx, child, list_level);
149 }
150 })
151 .cursor(CursorIcon::Hand)
152 .pointer_events(PointerEvents::Auto)
153 .on_press(move |_| {
154 open::that(url.as_str()).unwrap();
155 })
156 .class("link");
157 }
158
159 NodeValue::SoftBreak => {
160 TextSpan::new(cx, "\n", |cx| {
161 for child in node.children() {
162 parse_node(cx, child, list_level);
163 }
164 });
165 }
166
167 NodeValue::Table(_table) => {
168 VStack::new(cx, |cx| {
169 for child in node.children() {
170 parse_node(cx, child, list_level);
171 }
172 })
173 .class("table")
174 .width(Stretch(1.0))
175 .height(Auto);
176 }
177
178 NodeValue::TableRow(headers) => {
179 HStack::new(cx, |cx| {
180 for child in node.children() {
181 parse_node(cx, child, list_level);
182 }
183 })
184 .class("table-row")
185 .toggle_class("table-headers", *headers)
186 .width(Stretch(1.0))
187 .height(Auto);
188 Divider::horizontal(cx);
189 }
190
191 NodeValue::TableCell => {
192 Label::rich(cx, "", |cx| {
193 for child in node.children() {
194 parse_node(cx, child, list_level);
195 }
196 })
197 .class("table-cell")
198 .width(Stretch(1.0));
199 }
200
201 _ => {}
202 }
203}