Skip to main content

dioxus_modal/
hooks.rs

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        // Only cleanup if this is the last reference.
73        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}