dioxus_mdx/components/
response_field.rs1use dioxus::prelude::*;
4use dioxus_free_icons::{Icon, icons::ld_icons::*};
5
6use crate::parser::{ExpandableNode, ResponseFieldNode};
7
8#[derive(Props, Clone, PartialEq)]
10pub struct DocResponseFieldProps {
11 pub field: ResponseFieldNode,
13 #[props(default = 0)]
15 pub depth: usize,
16}
17
18#[component]
20pub fn DocResponseField(props: DocResponseFieldProps) -> Element {
21 let field = &props.field;
22
23 let description_html = if !field.content.is_empty() {
24 markdown::to_html_with_options(&field.content, &markdown::Options::gfm())
25 .unwrap_or_else(|_| field.content.clone())
26 } else {
27 String::new()
28 };
29
30 let indent_class = if props.depth > 0 {
31 "ml-4 border-l-2 border-base-300 pl-4"
32 } else {
33 ""
34 };
35
36 rsx! {
37 div { class: "py-3 {indent_class}",
38 div { class: "flex items-start gap-2 flex-wrap",
39 code { class: "font-mono font-semibold text-base-content bg-base-300 px-2 py-0.5 rounded",
41 "{field.name}"
42 }
43 span { class: "badge badge-outline badge-sm",
45 "{field.field_type}"
46 }
47 if field.required {
49 span { class: "badge badge-success badge-sm",
50 "required"
51 }
52 }
53 }
54 if !description_html.is_empty() {
56 div {
57 class: "prose prose-sm max-w-none mt-2 text-base-content/80",
58 dangerous_inner_html: description_html,
59 }
60 }
61 if let Some(expandable) = &field.expandable {
63 DocExpandable {
64 expandable: expandable.clone(),
65 depth: props.depth + 1,
66 }
67 }
68 }
69 }
70}
71
72#[derive(Props, Clone, PartialEq)]
74pub struct DocExpandableProps {
75 pub expandable: ExpandableNode,
77 #[props(default = 0)]
79 pub depth: usize,
80}
81
82#[component]
84pub fn DocExpandable(props: DocExpandableProps) -> Element {
85 let mut expanded = use_signal(|| false);
86 let expandable = &props.expandable;
87
88 let chevron_class = if expanded() {
89 "size-4 text-base-content/50 transform rotate-90 transition-transform"
90 } else {
91 "size-4 text-base-content/50 transition-transform"
92 };
93
94 rsx! {
95 div { class: "mt-3 border border-base-300 rounded-lg overflow-hidden",
96 button {
98 class: "w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-base-200 transition-colors text-sm",
99 onclick: move |_| expanded.set(!expanded()),
100 Icon { class: chevron_class, icon: LdChevronRight }
101 span { class: "font-medium text-base-content/70",
102 "{expandable.title}"
103 }
104 }
105 if expanded() {
107 div { class: "px-3 pb-3 border-t border-base-300 bg-base-200/30",
108 for (i, field) in expandable.fields.iter().enumerate() {
109 DocResponseField {
110 key: "{i}",
111 field: field.clone(),
112 depth: props.depth,
113 }
114 }
115 }
116 }
117 }
118 }
119}