yew_bootstrap/component/
modal.rs1use web_sys::EventTarget;
2use yew::prelude::*;
3use gloo_events::EventListenerOptions;
4use gloo_utils::body;
5
6#[derive(Default, Clone, PartialEq, Eq)]
8pub enum ModalSize {
9 ExtraLarge,
10 Large,
11 #[default]
12 Normal,
13 Small,
14}
15
16pub struct Modal {
42 #[allow(dead_code)]
43 on_hide: OnHide,
44}
45
46pub struct ModalHeader { }
49
50pub struct ModalBody { }
53
54pub struct ModalFooter { }
57
58#[derive(Properties, Clone, PartialEq)]
60pub struct ModalFooterProps {
61 #[prop_or_default]
62 pub children: Children
63}
64
65impl Component for ModalFooter {
66 type Message = ();
67 type Properties = ModalFooterProps;
68
69 fn create(_ctx: &Context<Self>) -> Self {
70 Self {}
71 }
72
73 fn view(&self, ctx: &Context<Self>) -> Html {
74 let props = ctx.props();
75
76 html! {
77 <div class="modal-footer">
78 { for props.children.iter() }
79 </div>
80 }
81 }
82}
83
84#[derive(Properties, Clone, PartialEq)]
86pub struct ModalHeaderProps {
87 #[prop_or_default]
89 pub title: String,
90
91 #[prop_or_default]
93 pub id: String,
94}
95
96impl Component for ModalHeader {
97 type Message = ();
98 type Properties = ModalHeaderProps;
99
100 fn create(_ctx: &Context<Self>) -> Self {
101 Self {}
102 }
103
104 fn view(&self, ctx: &Context<Self>) -> Html {
105 let props = ctx.props();
106
107 html! {
108 <div class="modal-header">
109 <h5 class="modal-title" id={format!("#{}", props.id.clone())}>{props.title.clone()}</h5>
110 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
111 </div>
112 }
113 }
114}
115
116#[derive(Properties, Clone, PartialEq)]
118pub struct ModalBodyProps {
119 #[prop_or_default]
120 pub children: Children
121}
122
123impl Component for ModalBody {
124 type Message = ();
125 type Properties = ModalBodyProps;
126
127 fn create(_ctx: &Context<Self>) -> Self {
128 Self {}
129 }
130
131 fn view(&self, ctx: &Context<Self>) -> Html {
132 let props = ctx.props();
133
134 html! {
135 <div class="modal-body">
136 { for props.children.iter() }
137 </div>
138 }
139 }
140}
141
142pub struct OnHide {
143 #[allow(dead_code)]
144 listener: Option<gloo_events::EventListener>,
145}
146
147impl OnHide {
148 pub fn new(target: &EventTarget, callback: Option<Callback<Event>>) -> Self {
149 let Some(callback) = callback else {
150 return Self { listener: None };
151 };
152
153 let listener = {
154 let option = EventListenerOptions::enable_prevent_default();
155
156 Some(gloo_events::EventListener::new_with_options(target, "hide.bs.modal", option, move |_event| {
157 callback.emit(_event.clone());
158 }))
159 };
160
161 Self { listener }
162 }
163}
164
165#[derive(Properties, Clone, PartialEq)]
167pub struct ModalProps {
168 #[prop_or_default]
169 pub title: String,
170 #[prop_or_default]
172 pub id: String,
173 #[prop_or_default]
175 pub children: Children,
176 #[prop_or_default]
178 pub size: ModalSize,
179 #[prop_or_default]
181 pub on_hide: Option<Callback<Event>>,
182}
183
184impl Component for Modal {
185 type Message = ();
186 type Properties = ModalProps;
187
188 fn create(_ctx: &Context<Self>) -> Self {
189 let body = body();
190 Self { on_hide: OnHide::new(
191 &body,
192 _ctx.props().on_hide.clone(),
193 )}
194 }
195
196 fn view(&self, ctx: &Context<Self>) -> Html {
197 let props = ctx.props();
198
199 let mut dialog_classes = Classes::new();
200 dialog_classes.push("modal-dialog");
201
202 match props.size {
203 ModalSize::ExtraLarge => dialog_classes.push("modal-xl"),
204 ModalSize::Large => dialog_classes.push("modal-lg"),
205 ModalSize::Small => dialog_classes.push("modal-sm"),
206 _ => (),
207 }
208
209 html! {
210 <div class="modal" tabindex="-1" id={props.id.clone()}>
211 <div class={dialog_classes}>
212 <div class="modal-content">
213 { for props.children.iter() }
214 </div>
215 </div>
216 </div>
217 }
218 }
219}