Skip to main content

euv_ui/component/popconfirm/view/
fn.rs

1use super::*;
2
3/// A confirm bubble anchored to its trigger.
4///
5/// The trigger is the component's children and the `c_euv_popconfirm`
6/// wrapper anchors the bubble to it. The bubble body is always mounted:
7/// opening and closing is a reactive class swap between
8/// `c_euv_popconfirm_body_open` and `c_euv_popconfirm_body_closed`, never
9/// a conditional that removes the bubble from the tree. The open state is
10/// owned by the component through a hook, so the caller only supplies the
11/// question, the two button labels and the two callbacks.
12///
13/// # Arguments
14///
15/// - `VirtualNode<EuvPopconfirmProps>` - The props node containing the question and the callbacks.
16///
17/// # Returns
18///
19/// - `VirtualNode` - The popconfirm virtual DOM tree.
20#[component]
21pub fn euv_popconfirm(node: VirtualNode<EuvPopconfirmProps>) -> VirtualNode {
22    let EuvPopconfirmProps {
23        title,
24        ok_label,
25        cancel_label,
26        on_ok,
27        on_cancel,
28    }: EuvPopconfirmProps = node.try_get_props().unwrap_or_default();
29    let children: VirtualNode = node.get_children().into();
30    let open: Signal<bool> = HookContext::use_hook(|| Signal::create(false));
31    let has_ok: bool = !ok_label.is_empty();
32    let has_cancel: bool = !cancel_label.is_empty();
33    let toggle_handler: Option<Rc<dyn Fn(Event)>> = popconfirm_toggle_handler(open);
34    let ok_handler: Rc<dyn Fn(Event)> = popconfirm_ok_handler(on_ok, open);
35    let cancel_handler: Rc<dyn Fn(Event)> = popconfirm_cancel_handler(on_cancel, open);
36    html! {
37        div {
38            class: c_euv_popconfirm()
39            span {
40                onclick: toggle_handler
41                children
42            }
43            div {
44                class: c_euv_popconfirm_body()
45                class: if { open } {
46                    c_euv_popconfirm_body_open()
47                } else {
48                    c_euv_popconfirm_body_closed()
49                }
50                div {
51                    class: c_euv_popconfirm_text()
52                    title
53                }
54                div {
55                    class: c_euv_popconfirm_actions()
56                    if has_ok {
57                        euv_button {
58                            variant: EuvButtonVariant::Primary
59                            label: ok_label
60                            onclick: Some(ok_handler)
61                        }
62                    }
63                    if has_cancel {
64                        euv_button {
65                            variant: EuvButtonVariant::Outline
66                            label: cancel_label
67                            onclick: Some(cancel_handler)
68                        }
69                    }
70                }
71            }
72        }
73    }
74}
75
76/// Builds the click handler that toggles the bubble open and closed.
77///
78/// The event type is a parameter so the DOM-free test suite can drive the
79/// handler with a stand-in event; the component always instantiates it
80/// with [`Event`].
81///
82/// # Arguments
83///
84/// - `Signal<bool>` - The open state of the bubble.
85///
86/// # Returns
87///
88/// - `Option<Rc<dyn Fn(E)>>` - The toggle click handler.
89pub fn popconfirm_toggle_handler<E: 'static>(open: Signal<bool>) -> Option<Rc<dyn Fn(E)>> {
90    Some(Rc::new(move |_: E| {
91        let is_open: bool = open.get();
92        open.set(!is_open);
93    }))
94}
95
96/// Builds the click handler that confirms the action and closes the bubble.
97///
98/// The callback is fired first, so the caller sees the confirmation
99/// before the bubble is taken out of the open state.
100///
101/// # Arguments
102///
103/// - `Option<Rc<dyn Fn()>>` - The confirm callback, fired when supplied.
104/// - `Signal<bool>` - The open state of the bubble.
105///
106/// # Returns
107///
108/// - `Rc<dyn Fn(E)>` - The confirm click handler.
109pub fn popconfirm_ok_handler<E: 'static>(
110    on_ok: Option<Rc<dyn Fn()>>,
111    open: Signal<bool>,
112) -> Rc<dyn Fn(E)> {
113    Rc::new(move |_: E| {
114        if let Some(callback) = &on_ok {
115            callback();
116        }
117        open.set(false);
118    })
119}
120
121/// Builds the click handler that cancels the action and closes the bubble.
122///
123/// The callback is fired first, so the caller sees the cancellation
124/// before the bubble is taken out of the open state.
125///
126/// # Arguments
127///
128/// - `Option<Rc<dyn Fn()>>` - The cancel callback, fired when supplied.
129/// - `Signal<bool>` - The open state of the bubble.
130///
131/// # Returns
132///
133/// - `Rc<dyn Fn(E)>` - The cancel click handler.
134pub fn popconfirm_cancel_handler<E: 'static>(
135    on_cancel: Option<Rc<dyn Fn()>>,
136    open: Signal<bool>,
137) -> Rc<dyn Fn(E)> {
138    Rc::new(move |_: E| {
139        if let Some(callback) = &on_cancel {
140            callback();
141        }
142        open.set(false);
143    })
144}