vizia_core/views/
markdown.rs1#![cfg(feature = "markdown")]
2
3use std::cell::RefCell;
4
5use comrak::nodes::{Ast, NodeValue};
6use comrak::{Arena, Options, parse_document};
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 .alignment(Alignment::Left)
127 .height(Auto);
128 }
129
130 NodeValue::Code(code) => {
131 TextSpan::new(cx, &code.literal.to_owned(), |_| {}).class("code");
132 }
133
134 NodeValue::CodeBlock(code_block) => {
135 let mut code = code_block.literal.to_owned();
136 code.pop().unwrap();
137 ScrollView::new(cx, |cx| {
138 Label::new(cx, code).class("code");
139 })
140 .show_vertical_scrollbar(false)
141 .height(Auto)
142 .width(Stretch(1.0));
143 }
144
145 NodeValue::Link(link) => {
146 let url = link.url.clone();
147 TextSpan::new(cx, "", |cx| {
148 for child in node.children() {
149 parse_node(cx, child, list_level);
150 }
151 })
152 .cursor(CursorIcon::Hand)
153 .pointer_events(PointerEvents::Auto)
154 .on_press(move |_| {
155 open::that(url.as_str()).unwrap();
156 })
157 .class("link");
158 }
159
160 NodeValue::SoftBreak => {
161 TextSpan::new(cx, "\n", |cx| {
162 for child in node.children() {
163 parse_node(cx, child, list_level);
164 }
165 });
166 }
167
168 NodeValue::Table(_table) => {
169 VStack::new(cx, |cx| {
170 for child in node.children() {
171 parse_node(cx, child, list_level);
172 }
173 })
174 .class("table")
175 .width(Stretch(1.0))
176 .height(Auto);
177 }
178
179 NodeValue::TableRow(headers) => {
180 HStack::new(cx, |cx| {
181 for child in node.children() {
182 parse_node(cx, child, list_level);
183 }
184 })
185 .class("table-row")
186 .toggle_class("table-headers", *headers)
187 .width(Stretch(1.0))
188 .height(Auto);
189 Divider::horizontal(cx);
190 }
191
192 NodeValue::TableCell => {
193 Label::rich(cx, "", |cx| {
194 for child in node.children() {
195 parse_node(cx, child, list_level);
196 }
197 })
198 .class("table-cell")
199 .width(Stretch(1.0));
200 }
201
202 _ => {}
203 }
204}