1use dioxus::prelude::*;
2
3#[derive(Clone, Copy, PartialEq)]
4pub enum ModalSize {
5 Small,
6 Default,
7 Large,
8 ExtraLarge,
9}
10
11impl Into<&'static str> for ModalSize {
12 fn into(self) -> &'static str {
13 match self {
14 ModalSize::Small => "modal-sm",
15 ModalSize::Default => "",
16 ModalSize::Large => "modal-lg",
17 ModalSize::ExtraLarge => "modal-xl",
18 }
19 }
20}
21
22#[derive(Clone, Props, PartialEq)]
23pub struct ModalProps {
24 #[props(optional)]
25 id: String,
26 #[props(optional, default = "".to_string())]
27 class: String,
28 #[props(optional, default = ModalSize::Default)]
29 size: ModalSize,
30 #[props(optional, default = false)]
31 fade: bool,
32 #[props(optional, default = false)]
33 centered: bool,
34 #[props(optional, default = false)]
35 scrollable: bool,
36 #[props(optional, default = false)]
37 fullscreen: bool,
38 #[props(optional, default = false)]
39 static_backdrop: bool,
40 children: Element,
41}
42
43#[component]
44pub fn Modal(props: ModalProps) -> Element {
45 let mut modal_classes = vec!["modal".to_string()];
46 let mut dialog_classes = vec!["modal-dialog".to_string()];
47
48 if props.fade {
49 modal_classes.push("fade".to_string());
50 }
51
52 if props.centered {
53 dialog_classes.push("modal-dialog-centered".to_string());
54 }
55
56 if props.scrollable {
57 dialog_classes.push("modal-dialog-scrollable".to_string());
58 }
59
60 if props.fullscreen {
61 dialog_classes.push("modal-fullscreen".to_string());
62 } else {
63 let size_class: &str = props.size.into();
64 if !size_class.is_empty() {
65 dialog_classes.push(size_class.to_string());
66 }
67 }
68
69 if !props.class.is_empty() {
70 modal_classes.push(props.class.clone());
71 }
72
73 let modal_classes = modal_classes.join(" ");
74 let dialog_classes = dialog_classes.join(" ");
75 let backdrop_attr = if props.static_backdrop { "static" } else { "true" };
76
77 rsx! {
78 div {
79 id: props.id,
80 class: modal_classes,
81 tabindex: "-1",
82 role: "dialog",
83 "data-bs-backdrop": backdrop_attr,
84 "data-bs-keyboard": "true",
85 div {
86 class: dialog_classes,
87 role: "document",
88 div {
89 class: "modal-content",
90 {props.children}
91 }
92 }
93 }
94 }
95}
96
97#[derive(Clone, Props, PartialEq)]
98pub struct ModalHeaderProps {
99 #[props(optional)]
100 id: String,
101 #[props(optional, default = "".to_string())]
102 class: String,
103 #[props(optional, default = true)]
104 close_button: bool,
105 children: Element,
106}
107
108#[component]
109pub fn ModalHeader(props: ModalHeaderProps) -> Element {
110 let mut class_list = vec!["modal-header".to_string()];
111
112 if !props.class.is_empty() {
113 class_list.push(props.class.clone());
114 }
115
116 let class_list = class_list.join(" ");
117
118 rsx! {
119 div {
120 id: props.id,
121 class: class_list,
122 {props.children}
123 if props.close_button {
124 button {
125 r#type: "button",
126 class: "btn-close",
127 "data-bs-dismiss": "modal",
128 "aria-label": "Close",
129 }
130 }
131 }
132 }
133}
134
135#[derive(Clone, Props, PartialEq)]
136pub struct ModalBodyProps {
137 #[props(optional)]
138 id: String,
139 #[props(optional, default = "".to_string())]
140 class: String,
141 children: Element,
142}
143
144#[component]
145pub fn ModalBody(props: ModalBodyProps) -> Element {
146 let mut class_list = vec!["modal-body".to_string()];
147
148 if !props.class.is_empty() {
149 class_list.push(props.class.clone());
150 }
151
152 let class_list = class_list.join(" ");
153
154 rsx! {
155 div {
156 id: props.id,
157 class: class_list,
158 {props.children}
159 }
160 }
161}
162
163#[derive(Clone, Props, PartialEq)]
164pub struct ModalFooterProps {
165 #[props(optional)]
166 id: String,
167 #[props(optional, default = "".to_string())]
168 class: String,
169 children: Element,
170}
171
172#[component]
173pub fn ModalFooter(props: ModalFooterProps) -> Element {
174 let mut class_list = vec!["modal-footer".to_string()];
175
176 if !props.class.is_empty() {
177 class_list.push(props.class.clone());
178 }
179
180 let class_list = class_list.join(" ");
181
182 rsx! {
183 div {
184 id: props.id,
185 class: class_list,
186 {props.children}
187 }
188 }
189}
190
191#[derive(Clone, Props, PartialEq)]
192pub struct ModalTitleProps {
193 #[props(optional)]
194 id: String,
195 #[props(optional, default = "".to_string())]
196 class: String,
197 #[props(optional, default = "h1".to_string())]
198 tag: String,
199 children: Element,
200}
201
202#[component]
203pub fn ModalTitle(props: ModalTitleProps) -> Element {
204 let mut class_list = vec!["modal-title".to_string()];
205
206 if !props.class.is_empty() {
207 class_list.push(props.class.clone());
208 }
209
210 let class_list = class_list.join(" ");
211
212 match props.tag.as_str() {
213 "h2" => rsx! { h2 { id: props.id, class: class_list, {props.children} } },
214 "h3" => rsx! { h3 { id: props.id, class: class_list, {props.children} } },
215 "h4" => rsx! { h4 { id: props.id, class: class_list, {props.children} } },
216 "h5" => rsx! { h5 { id: props.id, class: class_list, {props.children} } },
217 "h6" => rsx! { h6 { id: props.id, class: class_list, {props.children} } },
218 _ => rsx! { h1 { id: props.id, class: class_list, {props.children} } },
219 }
220}