Skip to main content

dioxus_mdx/components/
steps.rs

1//! Steps component for sequential documentation guides.
2
3use dioxus::prelude::*;
4use regex::Regex;
5
6use crate::components::DocNodeRenderer;
7use crate::parser::{DocNode, StepsNode};
8
9/// Props for DocSteps component.
10#[derive(Props, Clone, PartialEq)]
11pub struct DocStepsProps {
12    /// Steps data.
13    pub steps: StepsNode,
14}
15
16/// Sequential steps component using DaisyUI steps or custom styling.
17#[component]
18pub fn DocSteps(props: DocStepsProps) -> Element {
19    rsx! {
20        div { class: "my-8",
21            // Use div instead of ol to avoid default list numbering
22            div { class: "relative border-l-2 border-primary/20 ml-5 space-y-8",
23                for (i, step) in props.steps.steps.iter().enumerate() {
24                    div { key: "{i}", class: "relative pl-10",
25                        // Step number circle - positioned to overlap the border line
26                        span {
27                            class: "absolute left-0 top-0 -translate-x-1/2 flex items-center justify-center w-7 h-7 bg-primary text-primary-content rounded-full font-semibold text-sm shadow-sm",
28                            "{i + 1}"
29                        }
30                        // Step content
31                        div {
32                            // Step title
33                            h4 { class: "font-semibold text-base text-base-content mb-2",
34                                // Clean up step title (remove "Step X:" prefix if present)
35                                {clean_step_title(&step.title)}
36                            }
37                            // Step body (render as markdown with nested components)
38                            StepContent { content: step.content.clone() }
39                        }
40                    }
41                }
42            }
43        }
44    }
45}
46
47/// Clean up step title by removing redundant prefixes.
48fn clean_step_title(title: &str) -> String {
49    // Remove "Step N:" or "Step N." prefix
50    let re = Regex::new(r"^Step\s+\d+[:.]\s*").unwrap();
51    re.replace(title, "").trim().to_string()
52}
53
54/// Props for StepContent.
55#[derive(Props, Clone, PartialEq)]
56struct StepContentProps {
57    content: Vec<DocNode>,
58}
59
60/// Render step content which may contain nested MDX components.
61#[component]
62fn StepContent(props: StepContentProps) -> Element {
63    rsx! {
64        div { class: "prose prose-sm max-w-none",
65            for (i, node) in props.content.iter().enumerate() {
66                DocNodeRenderer { key: "{i}", node: node.clone() }
67            }
68        }
69    }
70}