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