use crate::icon::{Icon, IconKind};
use crate::locale::use_locale;
use dioxus::prelude::*;
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum AlertVariant {
#[default]
Info,
Success,
Warning,
Danger,
}
impl AlertVariant {
#[must_use]
pub fn scss_class(&self) -> &'static str {
match self {
AlertVariant::Info => "sd-alert--info",
AlertVariant::Success => "sd-alert--success",
AlertVariant::Warning => "sd-alert--warning",
AlertVariant::Danger => "sd-alert--danger",
}
}
#[must_use]
pub fn title_class(&self) -> &'static str {
match self {
AlertVariant::Info => "sd-alert__title--info",
AlertVariant::Success => "sd-alert__title--success",
AlertVariant::Warning => "sd-alert__title--warning",
AlertVariant::Danger => "sd-alert__title--danger",
}
}
#[must_use]
pub fn icon_color_class(&self) -> &'static str {
match self {
AlertVariant::Info => "sd-alert__icon--info",
AlertVariant::Success => "sd-alert__icon--success",
AlertVariant::Warning => "sd-alert__icon--warning",
AlertVariant::Danger => "sd-alert__icon--danger",
}
}
#[must_use]
pub fn glow_class(&self) -> &'static str {
match self {
AlertVariant::Info => "sd-alert--glow-info",
AlertVariant::Success => "sd-alert--glow-success",
AlertVariant::Warning => "sd-alert--glow-warning",
AlertVariant::Danger => "sd-alert--glow-danger",
}
}
#[must_use]
pub fn icon_kind(&self) -> IconKind {
match self {
AlertVariant::Info => IconKind::Info,
AlertVariant::Success => IconKind::Check,
AlertVariant::Warning => IconKind::AlertTriangle,
AlertVariant::Danger => IconKind::AlertCircle,
}
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
AlertVariant::Info => "info",
AlertVariant::Success => "success",
AlertVariant::Warning => "warning",
AlertVariant::Danger => "danger",
}
}
#[must_use]
pub fn default_prefix(&self) -> &'static str {
match self {
AlertVariant::Info => "Info",
AlertVariant::Success => "Success",
AlertVariant::Warning => "Warning",
AlertVariant::Danger => "Error",
}
}
}
#[component]
pub fn Alert(
#[props(default)]
id: Option<String>,
#[props(default)]
title: Option<String>,
#[props(default)]
variant: AlertVariant,
#[props(default = true)]
live: bool,
#[props(default)]
severity_label: Option<String>,
#[props(default)]
heading_level: Option<u8>,
#[props(default)]
close_label: Option<String>,
#[props(default)]
class: String,
#[props(default)]
glow: Option<bool>,
on_close: Option<EventHandler<MouseEvent>>,
children: Element,
) -> Element {
let locale = use_locale();
let alert_id = crate::a11y::use_unique_id("sd-alert", id);
let title_id = format!("{alert_id}-title");
let title_variant_class = variant.title_class();
let icon_color_class = variant.icon_color_class();
#[cfg(feature = "theme")]
let theme_val = crate::theme::use_theme()();
#[cfg(feature = "theme")]
let radius_class = theme_val.radius_scale.md();
#[cfg(feature = "theme")]
let base_override = theme_val.resolve_component_base("alert").unwrap_or("");
#[cfg(feature = "theme")]
let class_modifiers = theme_val.resolve_class_modifiers("alert", &class);
#[cfg(feature = "theme")]
let variant_class = theme_val.resolve_variant_class("alert", variant.name(), variant.scss_class());
#[cfg(feature = "theme")]
let glow_class = if theme_val.is_component_glow_enabled("alert", glow) {
variant.glow_class().to_string()
} else {
String::new()
};
#[cfg(not(feature = "theme"))]
let base_override = "";
#[cfg(not(feature = "theme"))]
let class_modifiers = String::new();
#[cfg(not(feature = "theme"))]
let variant_class = variant.scss_class().to_string();
#[cfg(not(feature = "theme"))]
let radius_class = "";
#[cfg(not(feature = "theme"))]
let glow_class = if glow.unwrap_or(false) {
variant.glow_class().to_string()
} else {
String::new()
};
let icon_kind = variant.icon_kind();
let role = if live {
match variant {
AlertVariant::Danger | AlertVariant::Warning => Some("alert"),
AlertVariant::Info | AlertVariant::Success => Some("status"),
}
} else {
None
};
let aria_live = if live {
match variant {
AlertVariant::Danger | AlertVariant::Warning => Some("assertive"),
AlertVariant::Info | AlertVariant::Success => Some("polite"),
}
} else {
None
};
let prefix_text = severity_label.as_deref().unwrap_or_else(|| variant.default_prefix());
let close_btn_label = close_label.unwrap_or_else(|| locale.close.clone());
rsx! {
div {
role: role,
"aria-live": aria_live,
"aria-labelledby": if title.is_some() { Some(title_id.as_str()) } else { None },
class: "sd-alert {base_override} {variant_class} {glow_class} {radius_class} {class_modifiers} {class}",
span {
class: "sd-alert__icon {icon_color_class}",
"aria-hidden": "true",
Icon { kind: icon_kind, size: 18 }
}
div { class: "sd-alert__content",
span { class: "sd-visually-hidden", "{prefix_text}: " }
if let Some(t) = &title {
match heading_level {
Some(lvl) => rsx! {
div {
id: "{title_id}",
role: "heading",
"aria-level": "{lvl}",
class: "sd-alert__title {title_variant_class}",
"{t}"
}
},
None => rsx! {
div {
id: "{title_id}",
class: "sd-alert__title {title_variant_class}",
"{t}"
}
},
}
}
div { class: "sd-alert__body", {children} }
}
if let Some(close_handler) = on_close {
button {
r#type: "button",
class: "sd-alert__close",
"aria-label": "{close_btn_label}",
title: "{close_btn_label}",
onclick: move |e| close_handler.call(e),
Icon { kind: IconKind::X, size: 14 }
}
}
}
}
}