use iced::{
Border, Element, Length,
widget::{button, column, container, row, text},
};
use snora_design::{Tone, Tokens};
use super::style;
struct NoticeAction<Message> {
label: String,
on_press: Message,
}
pub struct Notice<'a, Message> {
tokens: &'a Tokens,
tone: Tone,
title: Option<String>,
body: String,
action: Option<NoticeAction<Message>>,
dismiss: Option<Message>,
}
impl<'a, Message: Clone + 'a> Notice<'a, Message> {
#[must_use]
pub fn new(tokens: &'a Tokens, tone: Tone, body: impl Into<String>) -> Self {
Self {
tokens,
tone,
title: None,
body: body.into(),
action: None,
dismiss: None,
}
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn action(mut self, label: impl Into<String>, on_press: Message) -> Self {
self.action = Some(NoticeAction { label: label.into(), on_press });
self
}
#[must_use]
pub fn dismiss(mut self, on_press: Message) -> Self {
self.dismiss = Some(on_press);
self
}
#[must_use]
pub fn render(self) -> Element<'a, Message> {
let t = self.tokens;
let p = &t.palette;
let accent_color = style::color::to_iced_color(match self.tone {
Tone::Accent => p.accent,
Tone::Success => p.success,
Tone::Warning => p.warning,
Tone::Danger => p.danger,
Tone::Info => p.info,
Tone::Neutral => p.border,
});
let text_color = style::color::to_iced_color(p.text_primary);
let surface = style::color::to_iced_color(p.surface);
let mut content_col: Vec<Element<'a, Message>> = Vec::new();
if let Some(title) = self.title {
content_col.push(
text(title)
.size(style::text::label_size(t))
.color(text_color)
.into(),
);
}
content_col.push(
text(self.body)
.size(style::text::body_size(t))
.color(text_color)
.into(),
);
let mut controls: Vec<Element<'a, Message>> = Vec::new();
if let Some(act) = self.action {
let tok = t.clone();
controls.push(
button(
text(act.label)
.size(style::text::label_size(t))
.color(accent_color),
)
.on_press(act.on_press)
.style(move |_theme, status| style::button::ghost(&tok, status))
.into(),
);
}
if let Some(on_dismiss) = self.dismiss {
let tok = t.clone();
controls.push(
button(text("×").size(style::text::label_size(t)).color(text_color))
.on_press(on_dismiss)
.style(move |_theme, status| style::button::ghost(&tok, status))
.into(),
);
}
let content_area: Element<'a, Message> =
column(content_col).spacing(t.spacing.xs).into();
let mut main_row_children: Vec<Element<'a, Message>> = Vec::new();
let bar_color = accent_color;
main_row_children.push(
container(iced::widget::space())
.width(4.0)
.height(Length::Fill)
.style(move |_| iced::widget::container::Style {
background: Some(bar_color.into()),
border: Border::default().rounded(0.0),
..Default::default()
})
.into(),
);
main_row_children.push(
container(content_area)
.padding([t.spacing.sm, t.spacing.md])
.width(Length::Fill)
.into(),
);
if !controls.is_empty() {
main_row_children.push(
container(
row(controls).spacing(t.spacing.xs).align_y(iced::Alignment::Center),
)
.padding([t.spacing.sm, t.spacing.sm])
.align_y(iced::alignment::Vertical::Center)
.into(),
);
}
let radius = t.radius.md;
let border_color = accent_color;
container(row(main_row_children).height(Length::Shrink))
.style(move |_| iced::widget::container::Style {
background: Some(surface.into()),
border: Border::default()
.rounded(radius)
.color(border_color)
.width(1.0),
..Default::default()
})
.into()
}
}
#[cfg(test)]
mod tests;