Skip to main content

dioxus_mdx/components/openapi/
responses_list.rs

1//! Responses list component for API endpoint documentation.
2
3use dioxus::prelude::*;
4use dioxus_free_icons::{Icon, icons::ld_icons::*};
5
6use crate::parser::ApiResponse;
7
8use super::schema_viewer::SchemaViewer;
9
10/// Props for ResponsesList component.
11#[derive(Props, Clone, PartialEq)]
12pub struct ResponsesListProps {
13    /// The responses to display.
14    pub responses: Vec<ApiResponse>,
15}
16
17/// List of API responses with status codes.
18#[component]
19pub fn ResponsesList(props: ResponsesListProps) -> Element {
20    if props.responses.is_empty() {
21        return rsx! {};
22    }
23
24    rsx! {
25        div { class: "space-y-2",
26            for response in &props.responses {
27                ResponseItem { key: "{response.status_code}", response: response.clone() }
28            }
29        }
30    }
31}
32
33/// Props for ResponseItem component.
34#[derive(Props, Clone, PartialEq)]
35pub struct ResponseItemProps {
36    /// The response to display.
37    pub response: ApiResponse,
38}
39
40/// Single response item with collapsible content.
41#[component]
42pub fn ResponseItem(props: ResponseItemProps) -> Element {
43    let mut is_expanded = use_signal(|| false);
44    let response = &props.response;
45    let badge_class = response.status_badge_class();
46
47    let has_content = !response.content.is_empty();
48
49    rsx! {
50        div { class: "border border-base-300 rounded-lg overflow-hidden",
51            // Header
52            button {
53                class: "w-full flex items-center gap-3 px-3 py-2 text-left hover:bg-base-200 transition-colors",
54                disabled: !has_content,
55                onclick: move |_| {
56                    if has_content {
57                        is_expanded.set(!is_expanded());
58                    }
59                },
60
61                // Expand icon
62                if has_content {
63                    Icon {
64                        class: if is_expanded() { "size-4 text-base-content/50 transform rotate-90 transition-transform" } else { "size-4 text-base-content/50 transition-transform" },
65                        icon: LdChevronRight
66                    }
67                }
68
69                // Status code badge
70                span { class: "badge {badge_class} badge-sm font-mono font-bold",
71                    "{response.status_code}"
72                }
73
74                // Description
75                span { class: "text-sm text-base-content/70 flex-1",
76                    "{response.description}"
77                }
78            }
79
80            // Content
81            if is_expanded() && has_content {
82                div { class: "border-t border-base-300 bg-base-200/30",
83                    for content in &response.content {
84                        div { class: "p-3",
85                            // Media type
86                            div { class: "mb-2",
87                                code { class: "text-xs font-mono text-base-content/50",
88                                    "{content.media_type}"
89                                }
90                            }
91
92                            // Schema
93                            if let Some(schema) = &content.schema {
94                                SchemaViewer {
95                                    schema: schema.clone(),
96                                    expanded: true,
97                                }
98                            }
99
100                            // Example
101                            if let Some(example) = &content.example {
102                                div { class: "mt-3 p-2 bg-base-300 rounded",
103                                    span { class: "text-xs text-base-content/50 font-semibold", "Example" }
104                                    pre { class: "mt-1 text-xs font-mono text-secondary overflow-x-auto whitespace-pre-wrap",
105                                        "{example}"
106                                    }
107                                }
108                            }
109                        }
110                    }
111                }
112            }
113        }
114    }
115}