1use dioxus::prelude::*;
2use freya_elements::{
3 self as dioxus_elements,
4 events::{
5 Key,
6 KeyboardEvent,
7 },
8 MouseEvent,
9};
10use freya_hooks::{
11 theme_with,
12 use_animation,
13 use_applied_theme,
14 AnimNum,
15 ButtonThemeWith,
16 Ease,
17 Function,
18 PopupTheme,
19 PopupThemeWith,
20};
21
22use crate::{
23 Button,
24 CrossIcon,
25};
26
27#[allow(non_snake_case)]
29#[component]
30pub fn PopupBackground(children: Element, onclick: EventHandler<MouseEvent>) -> Element {
31 rsx!(rect {
32 layer: "-2000",
33 rect {
34 onclick,
35 height: "100v",
36 width: "100v",
37 background: "rgb(0, 0, 0, 150)",
38 position: "global",
39 position_top: "0",
40 position_left: "0",
41 }
42 rect {
43 height: "100v",
44 width: "100v",
45 position: "global",
46 position_top: "0",
47 position_left: "0",
48 main_align: "center",
49 cross_align: "center",
50 {children}
51 }
52 })
53}
54
55#[allow(non_snake_case)]
92#[component]
93pub fn Popup(
94 theme: Option<PopupThemeWith>,
96 children: Element,
98 oncloserequest: Option<EventHandler>,
100 #[props(default = true)]
102 show_close_button: bool,
103 #[props(default = true)]
105 close_on_escape_key: bool,
106) -> Element {
107 let animations = use_animation(|conf| {
108 conf.auto_start(true);
109 (
110 AnimNum::new(0.85, 1.)
111 .time(150)
112 .ease(Ease::Out)
113 .function(Function::Quad),
114 AnimNum::new(40., 1.)
115 .time(150)
116 .ease(Ease::Out)
117 .function(Function::Quad),
118 AnimNum::new(0.2, 1.)
119 .time(150)
120 .ease(Ease::Out)
121 .function(Function::Quad),
122 )
123 });
124 let PopupTheme {
125 background,
126 color,
127 cross_fill,
128 width,
129 height,
130 } = use_applied_theme!(&theme, popup);
131
132 let scale = animations.get();
133 let (scale, margin, opacity) = &*scale.read();
134
135 let request_to_close = move || {
136 if let Some(oncloserequest) = &oncloserequest {
137 oncloserequest.call(());
138 }
139 };
140
141 let onglobalkeydown = move |event: KeyboardEvent| {
142 if close_on_escape_key && event.key == Key::Escape {
143 request_to_close()
144 }
145 };
146
147 rsx!(
148 PopupBackground {
149 onclick: move |_| request_to_close(),
150 rect {
151 scale: "{scale.read()} {scale.read()}",
152 margin: "{margin.read()} 0 0 0",
153 opacity: "{opacity.read()}",
154 padding: "14",
155 corner_radius: "8",
156 background: "{background}",
157 color: "{color}",
158 shadow: "0 4 5 0 rgb(0, 0, 0, 30)",
159 width: "{width}",
160 height: "{height}",
161 onglobalkeydown,
162 if show_close_button {
163 rect {
164 height: "0",
165 width: "fill",
166 cross_align: "end",
167 Button {
168 theme: theme_with!(ButtonTheme {
169 padding: "6".into(),
170 margin: "0".into(),
171 width: "30".into(),
172 height: "30".into(),
173 corner_radius: "999".into(),
174 shadow: "none".into()
175 }),
176 onpress: move |_| request_to_close(),
177 CrossIcon {
178 fill: cross_fill
179 }
180 }
181 }
182 }
183 {children}
184 }
185 }
186 )
187}
188
189#[allow(non_snake_case)]
191#[component]
192pub fn PopupTitle(children: Element) -> Element {
193 rsx!(
194 rect {
195 font_size: "18",
196 margin: "4 2 8 2",
197 font_weight: "bold",
198 {children}
199 }
200 )
201}
202
203#[allow(non_snake_case)]
205#[component]
206pub fn PopupContent(children: Element) -> Element {
207 rsx!(
208 rect {
209 font_size: "15",
210 margin: "6 2",
211 {children}
212 }
213 )
214}
215
216#[cfg(test)]
217mod test {
218 use std::time::Duration;
219
220 use dioxus::prelude::use_signal;
221 use freya::prelude::*;
222 use freya_elements::events::keyboard::{
223 Code,
224 Key,
225 Modifiers,
226 };
227 use freya_testing::prelude::*;
228 use tokio::time::sleep;
229
230 #[tokio::test]
231 pub async fn popup() {
232 fn popup_app() -> Element {
233 let mut show_popup = use_signal(|| false);
234
235 rsx!(
236 if *show_popup.read() {
237 Popup {
238 oncloserequest: move |_| {
239 show_popup.set(false)
240 },
241 label {
242 "Hello, World!"
243 }
244 }
245 }
246 Button {
247 onpress: move |_| show_popup.set(true),
248 label {
249 "Open"
250 }
251 }
252 )
253 }
254
255 let mut utils = launch_test(popup_app);
256 utils.wait_for_update().await;
257
258 assert_eq!(utils.sdom().get().layout().size(), 4);
260
261 utils.click_cursor((15., 15.)).await;
263 sleep(Duration::from_millis(150)).await;
264 utils.wait_for_update().await;
265
266 assert_eq!(utils.sdom().get().layout().size(), 12);
268
269 utils.click_cursor((25., 25.)).await;
270
271 assert_eq!(utils.sdom().get().layout().size(), 4);
273
274 utils.click_cursor((15., 15.)).await;
276
277 utils.push_event(TestEvent::Keyboard {
279 name: EventName::KeyDown,
280 key: Key::ArrowDown,
281 code: Code::ArrowDown,
282 modifiers: Modifiers::empty(),
283 });
284 utils.wait_for_update().await;
285 assert_eq!(utils.sdom().get().layout().size(), 12);
287
288 utils.push_event(TestEvent::Keyboard {
290 name: EventName::KeyDown,
291 key: Key::Escape,
292 code: Code::Escape,
293 modifiers: Modifiers::empty(),
294 });
295 utils.wait_for_update().await;
296 assert_eq!(utils.sdom().get().layout().size(), 4);
298 }
299}