dioxus_mdx/components/
renderer.rs1use dioxus::prelude::*;
4
5use super::slugify;
6use crate::components::{
7 DocAccordionGroup, DocCallout, DocCardGroup, DocCodeBlock, DocCodeGroup, DocExpandable,
8 DocParamField, DocRequestExample, DocResponseExample, DocResponseField, DocSteps, DocTabs,
9 DocUpdate, OpenApiViewer,
10};
11use crate::parser::{CardGroupNode, DocNode, parse_mdx};
12
13fn inject_heading_ids(html: &str) -> String {
15 let re = regex::Regex::new(r"<(h[2-4])>(.*?)</h[2-4]>").unwrap();
16 re.replace_all(html, |caps: ®ex::Captures| {
17 let tag = &caps[1];
18 let inner = &caps[2];
19 let plain = regex::Regex::new(r"<[^>]+>")
21 .unwrap()
22 .replace_all(inner, "");
23 let id = slugify(&plain);
24 format!("<{tag} id=\"{id}\">{inner}</{tag}>")
25 })
26 .into_owned()
27}
28
29#[derive(Props, Clone, PartialEq)]
31pub struct DocNodeRendererProps {
32 pub node: DocNode,
34}
35
36#[component]
38pub fn DocNodeRenderer(props: DocNodeRendererProps) -> Element {
39 match &props.node {
40 DocNode::Markdown(md) => {
41 let html = markdown::to_html_with_options(md, &markdown::Options::gfm())
42 .unwrap_or_else(|_| md.clone());
43 let html = inject_heading_ids(&html);
44 rsx! {
45 div {
46 class: "prose-content",
47 dangerous_inner_html: html,
48 }
49 }
50 }
51 DocNode::Callout(callout) => {
52 rsx! {
53 DocCallout {
54 callout_type: callout.callout_type,
55 content: callout.content.clone(),
56 }
57 }
58 }
59 DocNode::Card(card) => {
60 rsx! {
62 DocCardGroup {
63 group: CardGroupNode {
64 cols: 1,
65 cards: vec![card.clone()],
66 }
67 }
68 }
69 }
70 DocNode::CardGroup(group) => {
71 rsx! {
72 DocCardGroup { group: group.clone() }
73 }
74 }
75 DocNode::Tabs(tabs) => {
76 rsx! {
77 DocTabs { tabs: tabs.clone() }
78 }
79 }
80 DocNode::Steps(steps) => {
81 rsx! {
82 DocSteps { steps: steps.clone() }
83 }
84 }
85 DocNode::AccordionGroup(group) => {
86 rsx! {
87 DocAccordionGroup { group: group.clone() }
88 }
89 }
90 DocNode::CodeBlock(block) => {
91 rsx! {
92 DocCodeBlock { block: block.clone() }
93 }
94 }
95 DocNode::CodeGroup(group) => {
96 rsx! {
97 DocCodeGroup { group: group.clone() }
98 }
99 }
100 DocNode::ParamField(field) => {
101 rsx! {
102 DocParamField { field: field.clone() }
103 }
104 }
105 DocNode::ResponseField(field) => {
106 rsx! {
107 DocResponseField { field: field.clone() }
108 }
109 }
110 DocNode::Expandable(expandable) => {
111 rsx! {
112 DocExpandable { expandable: expandable.clone() }
113 }
114 }
115 DocNode::RequestExample(example) => {
116 rsx! {
117 DocRequestExample { example: example.clone() }
118 }
119 }
120 DocNode::ResponseExample(example) => {
121 rsx! {
122 DocResponseExample { example: example.clone() }
123 }
124 }
125 DocNode::Update(update) => {
126 rsx! {
127 DocUpdate { update: update.clone() }
128 }
129 }
130 DocNode::OpenApi(openapi) => {
131 rsx! {
132 OpenApiViewer {
133 spec: openapi.spec.clone(),
134 tags: openapi.tags.clone(),
135 show_schemas: openapi.show_schemas,
136 }
137 }
138 }
139 }
140}
141
142#[derive(Props, Clone, PartialEq)]
144pub struct DocContentProps {
145 pub nodes: Vec<DocNode>,
147}
148
149#[component]
151pub fn DocContent(props: DocContentProps) -> Element {
152 rsx! {
153 div { class: "doc-content",
154 for (i, node) in props.nodes.iter().enumerate() {
155 DocNodeRenderer { key: "{i}", node: node.clone() }
156 }
157 }
158 }
159}
160
161#[derive(Props, Clone, PartialEq)]
163pub struct MdxContentProps {
164 pub content: String,
166}
167
168#[component]
186pub fn MdxContent(props: MdxContentProps) -> Element {
187 let nodes = parse_mdx(&props.content);
188
189 rsx! {
190 DocContent { nodes: nodes }
191 }
192}
193
194#[component]
196pub fn MdxRenderer(content: String) -> Element {
197 let nodes = parse_mdx(&content);
198
199 rsx! {
200 DocContent { nodes: nodes }
201 }
202}