dioxus_bootstrap_css/alert.rs
1use dioxus::prelude::*;
2
3use crate::types::Color;
4
5/// Bootstrap Alert component.
6///
7/// # Bootstrap HTML → Dioxus
8///
9/// | HTML | Dioxus |
10/// |---|---|
11/// | `<div class="alert alert-success">` | `Alert { color: Color::Success, "Text" }` |
12/// | `<div class="alert alert-danger alert-dismissible">` | `Alert { color: Color::Danger, dismissible: true, "Text" }` |
13///
14/// ```rust,no_run
15/// # use dioxus::prelude::*;
16/// # use dioxus_bootstrap_css::prelude::*;
17/// # fn _doctest() -> Element {
18/// rsx! {
19/// Alert { color: Color::Success, "Operation completed!" }
20/// Alert { color: Color::Danger, dismissible: true,
21/// strong { "Error! " }
22/// "Something went wrong."
23/// }
24/// // With dismiss callback:
25/// Alert { color: Color::Warning, dismissible: true,
26/// on_dismiss: move |_| { /* clear error state */ },
27/// "Dismissible with callback."
28/// }
29/// }
30/// # }
31/// ```
32#[derive(Clone, PartialEq, Props)]
33pub struct AlertProps {
34 /// Alert color variant.
35 #[props(default = Color::Primary)]
36 pub color: Color,
37 /// Optional heading, rendered as Bootstrap's `.alert-heading` above the
38 /// body. Bootstrap documents this class for exactly this case — a titled
39 /// callout whose heading inherits the alert's colour — so expressing it is
40 /// the crate's job. Empty (the default) renders no heading at all, leaving
41 /// a flat-text alert byte-identical to what it was before.
42 #[props(default)]
43 pub heading: String,
44 /// Show a dismiss button. When clicked, the alert hides itself.
45 #[props(default)]
46 pub dismissible: bool,
47 /// Callback when the dismiss button is clicked. Use this to clear external state.
48 #[props(default)]
49 pub on_dismiss: Option<EventHandler<()>>,
50 /// Additional CSS classes.
51 #[props(default)]
52 pub class: String,
53 /// Any additional HTML attributes.
54 #[props(extends = GlobalAttributes)]
55 attributes: Vec<Attribute>,
56 /// Child elements.
57 pub children: Element,
58}
59
60#[component]
61pub fn Alert(props: AlertProps) -> Element {
62 let mut visible = use_signal(|| true);
63
64 if !*visible.read() {
65 return rsx! {};
66 }
67
68 let dismiss_class = if props.dismissible {
69 " alert-dismissible fade show"
70 } else {
71 ""
72 };
73
74 let full_class = if props.class.is_empty() {
75 format!("alert alert-{}{dismiss_class}", props.color)
76 } else {
77 format!("alert alert-{}{dismiss_class} {}", props.color, props.class)
78 };
79
80 rsx! {
81 div {
82 class: "{full_class}",
83 role: "alert",
84 ..props.attributes,
85 if !props.heading.is_empty() {
86 h4 { class: "alert-heading", "{props.heading}" }
87 }
88 {props.children}
89 if props.dismissible {
90 button {
91 class: "btn-close",
92 r#type: "button",
93 "aria-label": "Close",
94 onclick: move |_| {
95 visible.set(false);
96 if let Some(handler) = &props.on_dismiss {
97 handler.call(());
98 }
99 },
100 }
101 }
102 }
103 }
104}