1use crate::MODAL;
2use crate::abstracts::Modal;
3use crate::dioxus_core::use_hook_with_cleanup;
4use crate::fns::close;
5use dioxus::prelude::*;
6use std::{cell::RefCell, ops::Deref, rc::Rc};
7use web_sys::{EventTarget, KeyboardEvent};
8
9use wasm_bindgen::{
10 JsCast,
11 closure::Closure,
12 convert::{FromWasmAbi, RefFromWasmAbi},
13};
14
15pub fn _use_modal<U: 'static, V: 'static + Clone>(
16 component: fn(U, V, fn()) -> Element,
17 ctx: V,
18) -> Modal<U> {
19 Modal {
20 open: Box::new(move |item: U| {
21 *MODAL.write() = Some(component(item, ctx.clone(), close));
22 }),
23 close,
24 }
25}
26
27#[derive(Clone)]
28pub(super) struct EventListenerHandle {
29 cleanup: Rc<RefCell<Option<Box<dyn FnOnce()>>>>,
30}
31
32impl EventListenerHandle {
33 pub(super) fn new<EventKind, T>(
34 target_element: T,
35 event_name: &'static str,
36 mut callback: impl FnMut(EventKind) + 'static,
37 ) -> Self
38 where
39 EventKind: Sized + RefFromWasmAbi + FromWasmAbi + Clone + 'static,
40 T: Clone + Deref<Target = EventTarget> + std::fmt::Debug + 'static,
41 {
42 let closure = Closure::wrap(Box::new(move |event: EventKind| {
43 callback(event);
44 }) as Box<dyn FnMut(_)>);
45
46 if let Err(e) = target_element
47 .add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())
48 {
49 tracing::error!("failed to add event listener: {e:?}");
50 }
51
52 let cleanup = Rc::new(RefCell::new(Some(Box::new(move || {
53 if let Err(e) = target_element
54 .remove_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())
55 {
56 tracing::error!("failed to remove event listener: {e:?}");
57 }
58 }) as Box<dyn FnOnce()>)));
59 Self { cleanup }
60 }
61
62 pub(super) fn cleanup(&self) {
63 let cleanup = self.cleanup.borrow_mut().take();
64 if let Some(cleanup) = cleanup {
65 cleanup();
66 }
67 }
68}
69
70impl Drop for EventListenerHandle {
71 fn drop(&mut self) {
72 if Rc::strong_count(&self.cleanup) == 1 {
74 self.cleanup();
75 }
76 }
77}
78
79pub(crate) fn use_on_event<EventKind, T>(
80 target_element: &T,
81 event_name: &'static str,
82 mut callback: impl FnMut(EventKind) + 'static,
83) where
84 EventKind: Sized + RefFromWasmAbi + FromWasmAbi + Clone + 'static,
85 T: Clone + Deref<Target = EventTarget> + std::fmt::Debug + 'static,
86{
87 let hook = || {
88 EventListenerHandle::new(target_element.clone(), event_name, move |kind| {
89 callback(kind)
90 })
91 };
92
93 let cleanup = |f: EventListenerHandle| {
94 f.cleanup();
95 };
96
97 use_hook_with_cleanup(hook, cleanup);
98}
99
100pub(super) fn use_window_keydown(mut callback: impl FnMut(KeyboardEvent) + 'static) {
101 let window = gloo::utils::window();
102
103 let dioxus_callback = use_callback(move |event| {
104 callback(event);
105 });
106
107 use_on_event(&window, "keydown", move |event: KeyboardEvent| {
108 dioxus_callback.call(event);
109 });
110}
111
112#[macro_export]
113macro_rules! use_modal {
114 ($component:expr, $ctx:expr) => {
115 $crate::_use_modal($component, $ctx)
116 };
117 ($component:expr) => {
118 $crate::_use_modal($component, ())
119 };
120}