patternfly_yew/utils/
global_close.rs

1use std::ops::Deref;
2use wasm_bindgen::prelude::*;
3use wasm_bindgen::JsCast;
4use web_sys::window;
5use yew::prelude::*;
6
7const CLICK_TYPE: &str = "mousedown";
8
9/// Helper to trigger a close operation, when the user clicks on the global space.
10///
11/// This can be e.g. used for a drop down menu, where the component should be closed when the user
12/// clicks outside of the dropped down content.
13///
14/// In order to use this, you need to define and assign the [`NodeRef`] to an element which is
15/// considered the "inside". When the user clicks "outside" of the referenced element, it will
16/// execute the callback.
17///
18/// When the instance is dropped, the callback will no longer be fired.
19///
20/// When creating the structure, you can pass in a new [`NodeRef`], and you can deref and clone
21/// later using the function [`GlobalClose::node_ref`].
22pub struct GlobalClose {
23    node_ref: NodeRef,
24    listener: Closure<dyn Fn(MouseEvent)>,
25}
26
27impl GlobalClose {
28    pub fn new(node_ref: NodeRef, callback: Callback<()>) -> Self {
29        let cloned_ref = node_ref.clone();
30        let listener = Closure::wrap(Box::new(move |e: MouseEvent| {
31            if let Some(control_ref) = cloned_ref.get() {
32                if !control_ref.contains(e.target().as_ref().and_then(|t| t.dyn_ref())) {
33                    callback.emit(());
34                }
35            }
36        }) as Box<dyn Fn(MouseEvent)>);
37
38        if let Some(cb) = listener.as_ref().dyn_ref() {
39            window()
40                .unwrap()
41                .add_event_listener_with_callback(CLICK_TYPE, cb)
42                .ok();
43        }
44
45        Self { node_ref, listener }
46    }
47
48    pub fn node_ref(&self) -> NodeRef {
49        self.node_ref.clone()
50    }
51}
52
53impl Drop for GlobalClose {
54    fn drop(&mut self) {
55        if let Some(cb) = self.listener.as_ref().dyn_ref() {
56            window()
57                .unwrap()
58                .remove_event_listener_with_callback(CLICK_TYPE, cb)
59                .ok();
60        }
61    }
62}
63
64impl Deref for GlobalClose {
65    type Target = NodeRef;
66
67    fn deref(&self) -> &Self::Target {
68        &self.node_ref
69    }
70}