radix_leptos_primitives/components/
alert_dialog.rs

1use crate::utils::merge_classes;
2use leptos::callback::Callback;
3use leptos::children::Children;
4use leptos::prelude::*;
5
6/// AlertDialog component - Modal alert dialogs for user confirmations
7///
8/// The AlertDialog component provides accessible modal dialogs for critical user actions
9/// like confirmations, warnings, and important information display.
10///
11/// # Features
12/// - Accessible modal dialog with proper ARIA attributes
13/// - Focus management and keyboard navigation
14/// - Multiple variants (default, destructive, warning)
15/// - Customizable actions and content
16/// - Backdrop click handling
17/// - Escape key handling
18///
19/// # Example
20///
21/// ```rust
22/// use radix_leptos_primitives::*;
23///
24/// #[component]
25/// fn MyComponent() -> impl IntoView {
26///     let (show_dialog, set_show_dialog) = create_signal(false);
27///
28///     view! {
29///         <Button on_click=move |_| set_show_dialog.set(true)>
30///             "Delete Item"
31///         </Button>
32///         
33///         <AlertDialog
34///             open=show_dialog
35///             onopen_change=move |open| set_show_dialog.set(open)
36///             variant=AlertDialogVariant::Destructive
37///         >
38///             <AlertDialogTitle>"Delete Item"</AlertDialogTitle>
39///             <AlertDialogDescription>
40///                 "Are you sure you want to delete this item? This action cannot be undone."
41///             </AlertDialogDescription>
42///             <AlertDialogFooter>
43///                 <Button variant=ButtonVariant::Outline on_click=move |_| set_show_dialog.set(false)>
44///                     "Cancel"
45///                 </Button>
46///                 <Button variant=ButtonVariant::Destructive on_click=move |_| {
47///                     // Delete logic here
48///                     set_show_dialog.set(false);
49///                 }>
50///                     "Delete"
51///                 </Button>
52///             </AlertDialogFooter>
53///         </AlertDialog>
54///     }
55/// }
56/// ```
57
58#[derive(Debug, Clone, Copy, PartialEq)]
59pub enum AlertDialogVariant {
60    Default,
61    Destructive,
62    Warning,
63}
64
65impl AlertDialogVariant {
66    pub fn as_str(&self) -> &'static str {
67        match self {
68            AlertDialogVariant::Default => "default",
69            AlertDialogVariant::Destructive => "destructive",
70            AlertDialogVariant::Warning => "warning",
71        }
72    }
73}
74
75/// AlertDialog root component
76#[component]
77pub fn AlertDialog(
78    #[prop(optional)] class: Option<String>,
79    #[prop(optional)] style: Option<String>,
80    #[prop(optional)] children: Option<Children>,
81    #[prop(optional)] open: Option<bool>,
82    #[prop(optional)] variant: Option<AlertDialogVariant>,
83    #[prop(optional)] onopen_change: Option<Callback<bool>>,
84) -> impl IntoView {
85    let open = open.unwrap_or(false);
86    let variant = variant.unwrap_or(AlertDialogVariant::Default);
87    let onopen_change = onopen_change.unwrap_or_else(|| Callback::new(|_| {}));
88
89    let class = merge_classes(vec!["alert-dialog", variant.as_str()].to_vec());
90}
91
92/// AlertDialog title component
93#[component]
94pub fn AlertDialogTitle(
95    #[prop(optional)] class: Option<String>,
96    #[prop(optional)] style: Option<String>,
97    #[prop(optional)] children: Option<Children>,
98) -> impl IntoView {
99    let class = merge_classes(vec!["alert-dialog-title", class.as_deref().unwrap_or("")].to_vec());
100
101    view! {
102        <h2
103            id="alert-dialog-title"
104            class=class
105            style=style
106        >
107            {children.map(|c| c())}
108        </h2>
109    }
110}
111
112/// AlertDialog description component
113#[component]
114pub fn AlertDialogDescription(
115    #[prop(optional)] class: Option<String>,
116    #[prop(optional)] style: Option<String>,
117    #[prop(optional)] children: Option<Children>,
118) -> impl IntoView {
119    let class =
120        merge_classes(vec!["alert-dialog-description", class.as_deref().unwrap_or("")].to_vec());
121
122    view! {
123        <p
124            id="alert-dialog-description"
125            class=class
126            style=style
127        >
128            {children.map(|c| c())}
129        </p>
130    }
131}
132
133/// AlertDialog footer component
134#[component]
135pub fn AlertDialogFooter(
136    #[prop(optional)] class: Option<String>,
137    #[prop(optional)] style: Option<String>,
138    #[prop(optional)] children: Option<Children>,
139) -> impl IntoView {
140    let class = merge_classes(vec!["alert-dialog-footer", class.as_deref().unwrap_or("")].to_vec());
141
142    view! {
143        <div
144            class=class
145            style=style
146        >
147            {children.map(|c| c())}
148        </div>
149    }
150}
151
152/// AlertDialog action component
153#[component]
154pub fn AlertDialogAction(
155    #[prop(optional)] class: Option<String>,
156    #[prop(optional)] style: Option<String>,
157    #[prop(optional)] children: Option<Children>,
158    #[prop(optional)] on_click: Option<Callback<()>>,
159) -> impl IntoView {
160    let on_click = on_click.unwrap_or_else(|| Callback::new(|_| {}));
161
162    let class = merge_classes(vec!["alert-dialog-action", class.as_deref().unwrap_or("")].to_vec());
163
164    view! {
165        <button
166            class=class
167            style=style
168            on:click=move |_| on_click.run(())
169        >
170            {children.map(|c| c())}
171        </button>
172    }
173}
174
175/// AlertDialog cancel component
176#[component]
177pub fn AlertDialogCancel(
178    #[prop(optional)] class: Option<String>,
179    #[prop(optional)] style: Option<String>,
180    #[prop(optional)] children: Option<Children>,
181    #[prop(optional)] on_click: Option<Callback<()>>,
182) -> impl IntoView {
183    let on_click = on_click.unwrap_or_else(|| Callback::new(|_| {}));
184
185    let class = merge_classes(vec!["alert-dialog-cancel", class.as_deref().unwrap_or("")].to_vec());
186
187    view! {
188        <button
189            class=class
190            style=style
191            on:click=move |_| on_click.run(())
192        >
193            {children.map(|c| c())}
194        </button>
195    }
196}
197
198// Helper function to merge CSS classes
199
200#[cfg(test)]
201mod tests {
202    use proptest::prelude::*;
203
204    #[test]
205    fn test_alert_dialog_component_creation() {}
206
207    #[test]
208    fn test_alert_dialog_with_variant_component_creation() {}
209
210    proptest! {
211        #[test]
212        fn test_alert_dialog_props(___class in ".*", ___style in ".*") {
213
214        }
215
216        #[test]
217        fn test_alert_dialogopen_state(__open: bool, ___variant_index in 0..3usize) {
218
219        }
220
221        #[test]
222        fn test_alert_dialog_variants(___variant_index in 0..3usize) {
223
224        }
225
226        #[test]
227        fn test_alert_dialog_title_props(___class in ".*", ___style in ".*") {
228
229        }
230
231        #[test]
232        fn test_alert_dialog_description_props(___class in ".*", ___style in ".*") {
233
234        }
235
236        #[test]
237        fn test_alert_dialog_footer_props(___class in ".*", ___style in ".*") {
238
239        }
240
241        #[test]
242        fn test_alert_dialog_action_props(___class in ".*", ___style in ".*") {
243
244        }
245
246        #[test]
247        fn test_alert_dialog_cancel_props(___class in ".*", ___style in ".*") {
248
249        }
250    }
251}