dioxus_mdx/components/openapi/
endpoint_card.rs1use dioxus::prelude::*;
4use dioxus_free_icons::{Icon, icons::ld_icons::*};
5
6use crate::parser::ApiOperation;
7
8use super::method_badge::MethodBadge;
9use super::parameters_list::ParametersList;
10use super::request_body::RequestBodySection;
11use super::responses_list::ResponsesList;
12
13#[derive(Props, Clone, PartialEq)]
15pub struct EndpointCardProps {
16 pub operation: ApiOperation,
18}
19
20#[component]
22pub fn EndpointCard(props: EndpointCardProps) -> Element {
23 let mut is_expanded = use_signal(|| false);
24 let op = &props.operation;
25
26 rsx! {
27 div { class: "border border-base-300 rounded-lg overflow-hidden my-3",
28 button {
30 class: "w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-base-200/50 transition-colors",
31 onclick: move |_| is_expanded.set(!is_expanded()),
32
33 Icon {
35 class: if is_expanded() { "size-4 text-base-content/50 transform rotate-90 transition-transform shrink-0" } else { "size-4 text-base-content/50 transition-transform shrink-0" },
36 icon: LdChevronRight
37 }
38
39 MethodBadge { method: op.method }
41
42 code { class: "font-mono text-sm text-base-content",
44 "{op.path}"
45 }
46
47 if op.deprecated {
49 span { class: "badge badge-warning badge-sm",
50 "deprecated"
51 }
52 }
53
54 if let Some(summary) = &op.summary {
56 span { class: "text-sm text-base-content/60 truncate ml-auto max-w-[40%]",
57 "{summary}"
58 }
59 }
60 }
61
62 if is_expanded() {
64 div { class: "border-t border-base-300",
65 div { class: "px-4 py-3 bg-base-200/30",
67 if let Some(summary) = &op.summary {
68 h4 { class: "font-semibold text-base-content",
69 "{summary}"
70 }
71 }
72 if let Some(desc) = &op.description {
73 p { class: "mt-2 text-sm text-base-content/70",
74 "{desc}"
75 }
76 }
77
78 if let Some(op_id) = &op.operation_id {
80 div { class: "mt-2",
81 span { class: "text-xs text-base-content/50", "Operation ID: " }
82 code { class: "text-xs font-mono text-base-content/70",
83 "{op_id}"
84 }
85 }
86 }
87 }
88
89 if !op.parameters.is_empty() {
91 div { class: "px-4 py-3 border-t border-base-300",
92 h5 { class: "text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2",
93 Icon { class: "size-4", icon: LdSettings2 }
94 "Parameters"
95 }
96 ParametersList { parameters: op.parameters.clone() }
97 }
98 }
99
100 if let Some(body) = &op.request_body {
102 div { class: "px-4 py-3 border-t border-base-300",
103 h5 { class: "text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2",
104 Icon { class: "size-4", icon: LdUpload }
105 "Request Body"
106 }
107 RequestBodySection { body: body.clone() }
108 }
109 }
110
111 if !op.responses.is_empty() {
113 div { class: "px-4 py-3 border-t border-base-300",
114 h5 { class: "text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2",
115 Icon { class: "size-4", icon: LdDownload }
116 "Responses"
117 }
118 ResponsesList { responses: op.responses.clone() }
119 }
120 }
121 }
122 }
123 }
124 }
125}