Skip to main content

markdown_dx/
lib.rs

1mod components;
2mod options;
3mod settings;
4mod store;
5
6pub use options::Options;
7
8use {
9    components::{
10        Blockquote, Code, Definition, Delete, Emphasis, FootnoteDefinition, FootnoteReference,
11        Footnotes, Header, Headings, Html, Image, ImageReference, InlineCode, InlineMath, Link,
12        LinkReference, List, ListItem, Math, Paragraph, Strong, Table, TableCell, TableRow, Text,
13        Toml, Yaml,
14    },
15    dioxus::prelude::*,
16    markdown::mdast::Node,
17    settings::Settings,
18};
19
20#[derive(PartialEq, Clone, Props)]
21pub struct MarkdownProps {
22    /// The Markdown document
23    src: ReadSignal<String>,
24
25    /// Parsing options abstraction for
26    /// [`markdown`]
27    #[props(default)]
28    options: Options,
29}
30
31/// # Markdown Component
32///
33/// Render a markdown document in a
34/// `<div>` container.
35///
36/// ## Example
37///
38/// ```rust
39/// # use dioxus::prelude::*;
40/// # use crate::Markdown;
41///
42/// #[component]
43/// pub fn Page() -> Element {
44///     let md = "markdown as string";
45///     rsx! {
46///         Markdown { src: md }
47///     }
48/// }
49/// ```
50///
51/// ## Props
52///
53/// 1. `src`: the markdown as String
54/// 2. `options` (optional): configure the rendered output, see [Options](crate::Options)
55/// 3. `settings` (optional): configure the markdown parsing see [Settings](crate::Settings)
56#[component]
57pub fn Markdown(props: MarkdownProps) -> Element {
58    let node = match markdown::to_mdast(&props.src.read(), &Settings::default().into()) {
59        Ok(v) => v,
60        Err(err) => {
61            return rsx! {
62                h1 { "Error" }
63                strong { "{err}" }
64            };
65        }
66    };
67
68    use_context_provider(|| props.options.clone());
69    use_context_provider(|| Signal::new(store::Html::default()));
70    use_context_provider(|| Signal::new(store::Headings::default()));
71    use_context_provider(|| Signal::new(store::References::default()));
72    use_context_provider(|| Signal::new(store::Footnotes::default()));
73    use_context_provider(|| Signal::new(store::Frontmatter::default()));
74    use_context_provider(store::Math::default);
75
76    rsx! {
77        if !props.options.disable_default_theme {
78            document::Link { rel: "stylesheet", href: asset!("/assets/styles.css") }
79        }
80        div { id: "markdown-dx",
81            if props.options.include_frontmatter_header {
82                Header {}
83            }
84            DxNode { src: node }
85            hr {}
86            Footnotes {}
87        }
88    }
89}
90
91/// Renders a markdown node.
92#[component]
93fn DxNode(src: Node) -> Element {
94    match src {
95        Node::Root(v) => rsx! {
96            ChildNodes { src: v.children }
97        },
98        Node::Blockquote(src) => rsx! {
99            Blockquote { src }
100        },
101        Node::Break(_src) => rsx! {
102            br {}
103        },
104        Node::Code(src) => rsx! {
105            Code { src }
106        },
107        Node::Definition(src) => rsx! {
108            Definition { src }
109        },
110        Node::Delete(src) => rsx! {
111            Delete { src }
112        },
113        Node::Emphasis(src) => rsx! {
114            Emphasis { src }
115        },
116        Node::FootnoteDefinition(src) => rsx! {
117            FootnoteDefinition { src }
118        },
119        Node::FootnoteReference(src) => rsx! {
120            FootnoteReference { src }
121        },
122        Node::Heading(src) => rsx! {
123            Headings { src }
124        },
125        Node::Html(src) => rsx! {
126            Html { src }
127        },
128        Node::Image(src) => rsx! {
129            Image { src }
130        },
131        Node::ImageReference(src) => rsx! {
132            ImageReference { src }
133        },
134        Node::InlineCode(src) => rsx! {
135            InlineCode { src }
136        },
137        Node::InlineMath(src) => rsx! {
138            InlineMath { src }
139        },
140        Node::Link(src) => rsx! {
141            Link { src }
142        },
143        Node::LinkReference(src) => rsx! {
144            LinkReference { src }
145        },
146        Node::List(src) => rsx! {
147            List { src }
148        },
149        Node::ListItem(src) => rsx! {
150            ListItem { src }
151        },
152        Node::Math(src) => rsx! {
153            Math { src }
154        },
155        Node::MdxFlowExpression(src) => rsx! {
156            h1 { "Unimplemented: MdxFlowExpression" }
157            pre { "{src:#?}" }
158        },
159        Node::MdxJsxFlowElement(src) => rsx! {
160            h1 { "Unimplemented: MdxJsxFlowElement" }
161            pre { "{src:#?}" }
162        },
163        Node::MdxJsxTextElement(src) => rsx! {
164            h1 { "Unimplemented: MdxJsxTextElement" }
165            pre { "{src:#?}" }
166        },
167        Node::MdxTextExpression(src) => rsx! {
168            h1 { "Unimplemented: MdxTextExpression" }
169            pre { "{src:#?}" }
170        },
171        Node::MdxjsEsm(src) => rsx! {
172            h1 { "Unimplemented: MdxjsEsm" }
173            pre { "{src:#?}" }
174        },
175        Node::Paragraph(src) => rsx! {
176            Paragraph { src }
177        },
178        Node::Strong(src) => rsx! {
179            Strong { src }
180        },
181        Node::Table(src) => rsx! {
182            Table { src }
183        },
184        Node::TableCell(src) => rsx! {
185            TableCell { src, id: "" }
186        },
187        Node::TableRow(src) => rsx! {
188            TableRow { src }
189        },
190        Node::Text(src) => rsx! {
191            Text { src }
192        },
193        Node::ThematicBreak(_src) => rsx! {
194            hr {}
195        },
196        Node::Toml(src) => rsx! {
197            Toml { src }
198        },
199        Node::Yaml(src) => rsx! {
200            Yaml { src }
201        },
202    }
203}
204
205/// Convenience component to render all
206/// children nodes.
207#[component]
208fn ChildNodes(src: Vec<Node>) -> Element {
209    rsx! {
210        for node in src {
211            DxNode { src: node }
212        }
213    }
214}