yew_bs/components/
modal.rs1use yew::prelude::*;
2use web_sys::Element;
3use crate::interop::BsModal;
4#[derive(Properties, PartialEq)]
5pub struct ModalProps {
6 #[prop_or_default]
7 pub children: Children,
8 #[prop_or_default]
9 pub show: bool,
10 #[prop_or_default]
11 pub title: Option<AttrValue>,
12 #[prop_or_default]
13 pub on_hide: Option<Callback<()>>,
14 #[prop_or(true)]
15 pub close_button: bool,
16 #[prop_or_default]
17 pub centered: bool,
18 #[prop_or_default]
19 pub size: Option<String>,
20 #[prop_or_default]
21 pub fullscreen: bool,
22 #[prop_or_default]
23 pub scrollable: bool,
24 #[prop_or_default]
25 pub static_backdrop: bool,
26 #[prop_or_default]
27 pub class: Option<AttrValue>,
28 #[prop_or_default]
29 pub dialog_class: Option<AttrValue>,
30 #[prop_or_default]
31 pub node_ref: NodeRef,
32}
33#[function_component(Modal)]
34pub fn modal(props: &ModalProps) -> Html {
35 let modal_ref = use_node_ref();
36 let bs_modal = use_state(|| None::<BsModal>);
37 let prev_show = use_state(|| false);
38 {
39 let modal_ref = modal_ref.clone();
40 let bs_modal = bs_modal.clone();
41 let static_backdrop = props.static_backdrop;
42 use_effect(move || {
43 if let Some(element) = modal_ref.cast::<Element>() {
44 let options = js_sys::Object::new();
45 if static_backdrop {
46 let _ = js_sys::Reflect::set(
47 &options,
48 &"backdrop".into(),
49 &"static".into(),
50 );
51 }
52 bs_modal.set(Some(BsModal::new(&element, Some(&options.into()))));
53 }
54 || {}
55 });
56 }
57 {
58 let bs_modal = bs_modal.clone();
59 let prev_show = prev_show.clone();
60 let on_hide = props.on_hide.clone();
61 use_effect_with(
62 props.show,
63 move |show| {
64 if let Some(modal) = (*bs_modal).as_ref() {
65 if *show && !*prev_show {
66 modal.show();
67 prev_show.set(true);
68 } else if !*show && *prev_show {
69 modal.hide();
70 prev_show.set(false);
71 if let Some(on_hide) = on_hide {
72 on_hide.emit(());
73 }
74 }
75 }
76 || {}
77 },
78 );
79 }
80 let mut modal_classes = Classes::new();
81 modal_classes.push("modal");
82 modal_classes.push("fade");
83 if let Some(class) = &props.class {
84 modal_classes.push(class.to_string());
85 }
86 let mut dialog_classes = Classes::new();
87 dialog_classes.push("modal-dialog");
88 if props.centered {
89 dialog_classes.push("modal-dialog-centered");
90 }
91 if props.scrollable {
92 dialog_classes.push("modal-dialog-scrollable");
93 }
94 if props.fullscreen {
95 dialog_classes.push("modal-fullscreen");
96 } else if let Some(size) = &props.size {
97 dialog_classes.push(format!("modal-{}", size));
98 }
99 if let Some(dialog_class) = &props.dialog_class {
100 dialog_classes.push(dialog_class.to_string());
101 }
102 html! {
103 < div class = { modal_classes } tabindex = "-1" aria - hidden = { (! props.show)
104 .to_string() } ref = { modal_ref } data - bs - backdrop = { if props
105 .static_backdrop { "static" } else { "true" } } data - bs - keyboard = "true" > <
106 div class = { dialog_classes } > < div class = "modal-content" > if props.title
107 .is_some() || props.close_button { < div class = "modal-header" > if let
108 Some(title) = & props.title { < h5 class = "modal-title" > { title.clone() } </
109 h5 > } if props.close_button { < button type = "button" class = "btn-close" data
110 - bs - dismiss = "modal" aria - label = "Close" /> } </ div > } < div class =
111 "modal-body" > { for props.children.iter() } </ div > </ div > </ div > </ div >
112 }
113}
114#[derive(Properties, PartialEq)]
115pub struct ModalBodyProps {
116 #[prop_or_default]
117 pub children: Children,
118 #[prop_or_default]
119 pub class: Option<AttrValue>,
120 #[prop_or_default]
121 pub node_ref: NodeRef,
122}
123#[function_component(ModalBody)]
124pub fn modal_body(props: &ModalBodyProps) -> Html {
125 let mut classes = Classes::new();
126 classes.push("modal-body");
127 if let Some(class) = &props.class {
128 classes.push(class.to_string());
129 }
130 html! {
131 < div class = { classes } ref = { props.node_ref.clone() } > { for props.children
132 .iter() } </ div >
133 }
134}
135#[derive(Properties, PartialEq)]
136pub struct ModalFooterProps {
137 #[prop_or_default]
138 pub children: Children,
139 #[prop_or_default]
140 pub class: Option<AttrValue>,
141 #[prop_or_default]
142 pub node_ref: NodeRef,
143}
144#[function_component(ModalFooter)]
145pub fn modal_footer(props: &ModalFooterProps) -> Html {
146 let mut classes = Classes::new();
147 classes.push("modal-footer");
148 if let Some(class) = &props.class {
149 classes.push(class.to_string());
150 }
151 html! {
152 < div class = { classes } ref = { props.node_ref.clone() } > { for props.children
153 .iter() } </ div >
154 }
155}
156#[derive(Properties, PartialEq)]
157pub struct ModalHeaderProps {
158 #[prop_or_default]
159 pub children: Children,
160 #[prop_or(true)]
161 pub close_button: bool,
162 #[prop_or_default]
163 pub class: Option<AttrValue>,
164 #[prop_or_default]
165 pub node_ref: NodeRef,
166}
167#[function_component(ModalHeader)]
168pub fn modal_header(props: &ModalHeaderProps) -> Html {
169 let mut classes = Classes::new();
170 classes.push("modal-header");
171 if let Some(class) = &props.class {
172 classes.push(class.to_string());
173 }
174 html! {
175 < div class = { classes } ref = { props.node_ref.clone() } > { for props.children
176 .iter() } if props.close_button { < button type = "button" class = "btn-close"
177 data - bs - dismiss = "modal" aria - label = "Close" /> } </ div >
178 }
179}
180#[derive(Properties, PartialEq)]
181pub struct ModalTitleProps {
182 #[prop_or_default]
183 pub children: Children,
184 #[prop_or_default]
185 pub class: Option<AttrValue>,
186 #[prop_or_default]
187 pub node_ref: NodeRef,
188}
189#[function_component(ModalTitle)]
190pub fn modal_title(props: &ModalTitleProps) -> Html {
191 let mut classes = Classes::new();
192 classes.push("modal-title");
193 if let Some(class) = &props.class {
194 classes.push(class.to_string());
195 }
196 html! {
197 < h5 class = { classes } ref = { props.node_ref.clone() } > { for props.children
198 .iter() } </ h5 >
199 }
200}