Skip to main content

dioxus_element_plug/components/
alert.rs

1use dioxus::prelude::*;
2
3// Alert and message CSS class constants
4pub const ALERT: &str = "el-alert";
5pub const ALERT_SUCCESS: &str = "el-alert--success";
6pub const ALERT_INFO: &str = "el-alert--info";
7pub const ALERT_WARNING: &str = "el-alert--warning";
8pub const ALERT_ERROR: &str = "el-alert--error";
9pub const ALERT_DISMISSIBLE: &str = "is-dismissible";
10pub const ALERT_CLOSE: &str = "el-alert__close-btn";
11pub const ALERT_ICON: &str = "el-alert__icon";
12pub const ALERT_DESCRIPTION: &str = "el-alert__description";
13
14/// Alert types matching theme-chalk styles
15#[derive(Clone, PartialEq)]
16pub enum AlertType {
17    Success,
18    Warning,
19    Info,
20    Error,
21}
22
23impl AlertType {
24    pub fn as_class(&self) -> &'static str {
25        match self {
26            AlertType::Success => "el-alert--success",
27            AlertType::Warning => "el-alert--warning",
28            AlertType::Info => "el-alert--info",
29            AlertType::Error => "el-alert--error",
30        }
31    }
32}
33
34/// Alert props
35#[derive(Props, Clone, PartialEq)]
36pub struct AlertProps {
37    /// Alert title
38    pub title: String,
39
40    /// Alert content description
41    #[props(default)]
42    pub description: Option<String>,
43
44    /// Alert type/style
45    #[props(default = AlertType::Info)]
46    pub alert_type: AlertType,
47
48    /// Whether the alert can be closed
49    #[props(default = false)]
50    pub closable: bool,
51
52    /// Whether to show icon
53    #[props(default = false)]
54    pub show_icon: bool,
55
56    /// Whether the alert is center aligned
57    #[props(default = false)]
58    pub center: bool,
59
60    /// Close event handler
61    #[props(default)]
62    pub on_close: Option<EventHandler<MouseEvent>>,
63
64    /// Additional CSS classes
65    #[props(default)]
66    pub class: Option<String>,
67
68    /// Inline styles
69    #[props(default)]
70    pub style: Option<String>,
71}
72
73/// An alert component for displaying messages
74///
75/// This component displays important information with different styles
76/// for success, warning, info, and error states.
77///
78/// ## Example
79///
80/// ```rust,ignore
81/// use dioxus_element_plug::components::alert::{Alert, AlertType};
82///
83/// rsx! {
84///     Alert {
85///         title: "Success!".to_string(),
86///         description: Some("Operation completed successfully".to_string()),
87///         alert_type: AlertType::Success,
88///         closable: true,
89///         on_close: move |_| println!("Alert closed"),
90///     }
91/// }
92/// ```
93#[component]
94pub fn Alert(props: AlertProps) -> Element {
95    let mut class_names = vec![ALERT];
96    
97    class_names.push(props.alert_type.as_class());
98    
99    if props.closable {
100        class_names.push("is-closable");
101    }
102    
103    if props.show_icon {
104        class_names.push("with-icon");
105    }
106    
107    if props.center {
108        class_names.push("is-center");
109    }
110    
111    if let Some(ref custom_class) = props.class {
112        class_names.push(custom_class);
113    }
114    
115    let class_string = class_names.join(" ");
116    let style_string = props.style.as_ref().cloned().unwrap_or_default();
117    
118    let icon_class = match props.alert_type {
119        AlertType::Success => "el-icon-success",
120        AlertType::Warning => "el-icon-warning",
121        AlertType::Info => "el-icon-info",
122        AlertType::Error => "el-icon-error",
123    };
124    
125    rsx! {
126        div {
127            class: "{class_string}",
128            style: "{style_string}",
129            role: "alert",
130            
131            if props.show_icon {
132                div {
133                    class: "el-alert__icon",
134                    i {
135                        class: "{icon_class}"
136                    }
137                }
138            }
139            
140            div {
141                class: "el-alert__content",
142                
143                h4 {
144                    class: "el-alert__title",
145                    "{props.title}"
146                }
147                
148                if let Some(ref desc) = props.description {
149                    p {
150                        class: "el-alert__description",
151                        "{desc}"
152                    }
153                }
154            }
155            
156            if props.closable {
157                button {
158                    class: "el-alert__close-btn",
159                    type: "button",
160                    onclick: move |event| {
161                        if let Some(handler) = props.on_close {
162                            handler.call(event);
163                        }
164                    },
165                    "×"
166                }
167            }
168        }
169    }
170}
171
172/// Callout component for important information
173#[derive(Props, Clone, PartialEq)]
174pub struct CalloutProps {
175    /// Callout content
176    pub children: Element,
177
178    /// Callout title
179    #[props(default)]
180    pub title: Option<String>,
181
182    /// Callout type
183    #[props(default = AlertType::Info)]
184    pub callout_type: AlertType,
185
186    /// Whether to show icon
187    #[props(default = true)]
188    pub show_icon: bool,
189
190    /// Additional CSS classes
191    #[props(default)]
192    pub class: Option<String>,
193
194    /// Inline styles
195    #[props(default)]
196    pub style: Option<String>,
197}
198
199/// A callout component for highlighting important content
200///
201/// This component provides a more prominent way to display important
202/// information with optional title and icon.
203#[component]
204pub fn Callout(props: CalloutProps) -> Element {
205    let mut class_names = vec!["el-callout".to_string()];
206    
207    class_names.push(props.callout_type.as_class().to_string());
208    
209    if props.show_icon {
210        class_names.push("with-icon".to_string());
211    }
212    
213    if let Some(ref custom_class) = props.class {
214        class_names.push(custom_class.to_string());
215    }
216    
217    let class_string = class_names.join(" ");
218    let style_string = props.style.as_ref().cloned().unwrap_or_default();
219    
220    let icon_class = match props.callout_type {
221        AlertType::Success => "el-icon-check-circle",
222        AlertType::Warning => "el-icon-warning-outline",
223        AlertType::Info => "el-icon-info-circle",
224        AlertType::Error => "el-icon-error-circle",
225    };
226    
227    rsx! {
228        div {
229            class: "{class_string}",
230            style: "{style_string}",
231            
232            if props.show_icon {
233                i {
234                    class: "{icon_class} el-callout__icon"
235                }
236            }
237            
238            if let Some(ref title_text) = props.title {
239                h4 {
240                    class: "el-callout__title",
241                    "{title_text}"
242                }
243            }
244            
245            div {
246                class: "el-callout__content",
247                {props.children}
248            }
249        }
250    }
251}