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}