Skip to main content

ui/components/
alert_dialog.rs

1use smallvec::SmallVec;
2
3use crate::{Modal, ModalFooter, ModalHeader, prelude::*};
4
5/// A thin preset on [`Modal`] with Action/Cancel footer slots.
6///
7/// Does not duplicate `modal.rs` internals — composes `ModalHeader` and
8/// `ModalFooter` with fixed action/cancel buttons.
9#[derive(IntoElement, RegisterComponent)]
10pub struct AlertDialog {
11    id: SharedString,
12    title: SharedString,
13    description: Option<SharedString>,
14    children: SmallVec<[AnyElement; 2]>,
15    action_label: SharedString,
16    cancel_label: SharedString,
17    destructive: bool,
18}
19
20impl AlertDialog {
21    pub fn new(id: impl Into<SharedString>, title: impl Into<SharedString>) -> Self {
22        Self {
23            id: id.into(),
24            title: title.into(),
25            description: None,
26            children: SmallVec::new(),
27            action_label: "Continue".into(),
28            cancel_label: "Cancel".into(),
29            destructive: false,
30        }
31    }
32
33    pub fn description(mut self, description: impl Into<SharedString>) -> Self {
34        self.description = Some(description.into());
35        self
36    }
37
38    pub fn action_label(mut self, label: impl Into<SharedString>) -> Self {
39        self.action_label = label.into();
40        self
41    }
42
43    pub fn cancel_label(mut self, label: impl Into<SharedString>) -> Self {
44        self.cancel_label = label.into();
45        self
46    }
47
48    pub fn destructive(mut self, destructive: bool) -> Self {
49        self.destructive = destructive;
50        self
51    }
52}
53
54impl ParentElement for AlertDialog {
55    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
56        self.children.extend(elements)
57    }
58}
59
60impl RenderOnce for AlertDialog {
61    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
62        let mut header = ModalHeader::new()
63            .headline(self.title)
64            .show_dismiss_button(false);
65        if let Some(description) = self.description {
66            header = header.description(description);
67        }
68
69        let action = self.action_label.clone();
70        let cancel = self.cancel_label.clone();
71        let destructive = self.destructive;
72
73        div().w(px(440.)).child(
74            Modal::new(self.id, None)
75                .header(header)
76                .children(self.children)
77                .footer(
78                    ModalFooter::new().end_slot(
79                        h_flex()
80                            .gap_2()
81                            .child(Button::new("alert-cancel", cancel.clone()).color(Color::Muted))
82                            .child({
83                                let btn = Button::new("alert-action", action.clone());
84                                if destructive {
85                                    btn.danger()
86                                } else {
87                                    btn.primary()
88                                }
89                            }),
90                    ),
91                ),
92        )
93    }
94}
95
96impl Component for AlertDialog {
97    fn scope() -> ComponentScope {
98        ComponentScope::Overlays
99    }
100
101    fn description() -> Option<&'static str> {
102        Some("A modal alert preset with action and cancel buttons, built on Modal.")
103    }
104
105    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
106        Some(
107            v_flex()
108                .gap_6()
109                .child(
110                    AlertDialog::new("alert-basic", "Are you absolutely sure?")
111                        .description("This action cannot be undone.")
112                        .action_label("Continue")
113                        .cancel_label("Cancel"),
114                )
115                .child(
116                    AlertDialog::new("alert-destructive", "Delete account?")
117                        .description("This will permanently delete your account and all data.")
118                        .action_label("Delete")
119                        .destructive(true),
120                )
121                .into_any_element(),
122        )
123    }
124}