use egui::{Color32, Response, RichText, Ui, Vec2};
use crate::icons::{Icon, icons};
use crate::tokens::DESIGN_TOKENS;
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
pub enum AlertKind {
#[default]
Info,
Success,
Warning,
Error,
}
impl AlertKind {
pub fn bg_color(&self) -> Color32 {
let tokens = &DESIGN_TOKENS.semantic.alert;
match self {
AlertKind::Info => tokens.info_bg,
AlertKind::Success => tokens.success_bg,
AlertKind::Warning => tokens.warning_bg,
AlertKind::Error => tokens.error_bg,
}
}
pub fn border_color(&self) -> Color32 {
let tokens = &DESIGN_TOKENS.semantic.alert;
match self {
AlertKind::Info => tokens.info_border,
AlertKind::Success => tokens.success_border,
AlertKind::Warning => tokens.warning_border,
AlertKind::Error => tokens.error_border,
}
}
pub fn icon_color(&self) -> Color32 {
let tokens = &DESIGN_TOKENS.semantic.alert;
match self {
AlertKind::Info => tokens.info_icon,
AlertKind::Success => tokens.success_icon,
AlertKind::Warning => tokens.warning_icon,
AlertKind::Error => tokens.error_icon,
}
}
pub fn icon(&self) -> &'static Icon {
match self {
AlertKind::Info => &icons::INFO,
AlertKind::Success => &icons::SETTINGS_STATUS_LINE,
AlertKind::Warning => &icons::STATUS_ERROR,
AlertKind::Error => &icons::STATUS_ERROR,
}
}
}
pub struct AlertResponse {
pub response: Response,
pub dismissed: bool,
}
pub struct Alert {
kind: AlertKind,
title: Option<String>,
message: String,
dismissible: bool,
show_icon: bool,
}
impl Alert {
pub fn new(kind: AlertKind, message: impl Into<String>) -> Self {
Self {
kind,
title: None,
message: message.into(),
dismissible: false,
show_icon: true,
}
}
pub fn info(message: impl Into<String>) -> Self {
Self::new(AlertKind::Info, message)
}
pub fn success(message: impl Into<String>) -> Self {
Self::new(AlertKind::Success, message)
}
pub fn warning(message: impl Into<String>) -> Self {
Self::new(AlertKind::Warning, message)
}
pub fn error(message: impl Into<String>) -> Self {
Self::new(AlertKind::Error, message)
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn dismissible(mut self, dismissible: bool) -> Self {
self.dismissible = dismissible;
self
}
pub fn show_icon(mut self, show: bool) -> Self {
self.show_icon = show;
self
}
pub fn show(self, ui: &mut Ui) -> AlertResponse {
let bg_color = self.kind.bg_color();
let border_color = self.kind.border_color();
let icon_color = self.kind.icon_color();
let frame = egui::Frame::new()
.fill(bg_color)
.stroke(egui::Stroke::new(
DESIGN_TOKENS.stroke.hairline,
border_color,
))
.corner_radius(DESIGN_TOKENS.rounding.md)
.inner_margin(egui::Margin::same(DESIGN_TOKENS.spacing.lg as i8));
let mut dismissed = false;
let inner_response = frame.show(ui, |ui| {
ui.horizontal(|ui| {
if self.show_icon {
let icon = self.kind.icon();
let icon_size = DESIGN_TOKENS.sizing.icon_md;
ui.add(icon.as_image_tinted(Vec2::splat(icon_size), icon_color));
ui.add_space(DESIGN_TOKENS.spacing.lg);
}
ui.vertical(|ui| {
if let Some(title) = &self.title {
ui.label(RichText::new(title).strong().color(icon_color));
ui.add_space(DESIGN_TOKENS.spacing.xs);
}
ui.label(&self.message);
});
if self.dismissible {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
let close_icon = &icons::CLOSE;
let close_btn = ui.add(close_icon.as_image_tinted(
Vec2::splat(DESIGN_TOKENS.sizing.icon_sm),
ui.style().visuals.widgets.noninteractive.fg_stroke.color,
));
if close_btn.clicked() {
dismissed = true;
}
});
}
});
});
AlertResponse {
response: inner_response.response,
dismissed,
}
}
}