1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use yew::prelude::*;

/// A context to request a menu to close.
///
/// This is intended to be used by components implementing a drop-down style menu, where clicking
/// on a menu entry is intended to close the menu.
#[derive(Clone, PartialEq)]
pub struct CloseMenuContext {
    onclose: Callback<()>,
}

impl CloseMenuContext {
    pub fn new(onclose: Callback<()>) -> Self {
        Self { onclose }
    }

    /// Close the expanded menu
    pub fn close(&self) {
        self.onclose.emit(());
    }
}

/// Access the menu context.
///
/// This will only return a non-none value when called from a component nested in a component
/// supporting this context.
#[hook]
pub fn use_close_menu_context() -> Option<CloseMenuContext> {
    use_context()
}

#[derive(Clone, PartialEq)]
pub struct UseCloseMenu {
    pub context: Option<CloseMenuContext>,
}

impl UseCloseMenu {
    pub fn close(&self) {
        if let Some(context) = &self.context {
            context.close();
        } else {
            log::warn!("Ignored request to close menu: no context was found")
        }
    }
}

/// Allow closing the menu.
///
/// **NOTE**: If the hook is used inside a component which is not wrapped with a component
/// providing a [`CloseMenuContext`], then all operations become a no-op.
#[hook]
pub fn use_close_menu() -> UseCloseMenu {
    UseCloseMenu {
        context: use_context(),
    }
}

/// Provide a stable callback for closing the menu.
///
/// **NOTE**: If the hook is used inside a component which is not wrapped with a component
/// providing a [`CloseMenuContext`], then all operations become a no-op.
#[hook]
pub fn use_close_menu_callback() -> Callback<()> {
    let context = use_close_menu();
    use_callback(context, |(), context| context.close())
}