feat: finalizations for M0-M2
This commit is contained in:
@@ -5,4 +5,5 @@ pub mod tab_bar;
|
||||
pub mod status_bar;
|
||||
pub mod panel;
|
||||
pub mod project_selector;
|
||||
pub mod toast;
|
||||
pub mod welcome;
|
||||
|
||||
104
crates/bds-ui/src/views/toast.rs
Normal file
104
crates/bds-ui/src/views/toast.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use iced::widget::{button, container, row, text, Space};
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::state::toast::{Toast, ToastLevel};
|
||||
|
||||
/// Background color per toast severity.
|
||||
fn toast_bg(level: ToastLevel) -> Color {
|
||||
match level {
|
||||
ToastLevel::Info => Color::from_rgb(0.16, 0.22, 0.34),
|
||||
ToastLevel::Success => Color::from_rgb(0.12, 0.30, 0.16),
|
||||
ToastLevel::Warning => Color::from_rgb(0.38, 0.30, 0.10),
|
||||
ToastLevel::Error => Color::from_rgb(0.38, 0.14, 0.14),
|
||||
}
|
||||
}
|
||||
|
||||
/// Border color per toast severity.
|
||||
fn toast_border(level: ToastLevel) -> Color {
|
||||
match level {
|
||||
ToastLevel::Info => Color::from_rgb(0.25, 0.40, 0.65),
|
||||
ToastLevel::Success => Color::from_rgb(0.20, 0.55, 0.25),
|
||||
ToastLevel::Warning => Color::from_rgb(0.65, 0.50, 0.15),
|
||||
ToastLevel::Error => Color::from_rgb(0.65, 0.20, 0.20),
|
||||
}
|
||||
}
|
||||
|
||||
fn toast_style(level: ToastLevel) -> impl Fn(&Theme) -> container::Style {
|
||||
move |_theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(toast_bg(level))),
|
||||
border: Border {
|
||||
color: toast_border(level),
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..container::Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn dismiss_btn(_theme: &Theme, status: button::Status) -> button::Style {
|
||||
let color = match status {
|
||||
button::Status::Hovered => Color::WHITE,
|
||||
_ => Color::from_rgb(0.65, 0.65, 0.70),
|
||||
};
|
||||
button::Style {
|
||||
background: Some(Background::Color(Color::TRANSPARENT)),
|
||||
text_color: color,
|
||||
border: Border::default(),
|
||||
..button::Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Render the toast stack as an overlay element.
|
||||
///
|
||||
/// Returns `None` when no toasts are visible.
|
||||
pub fn view(toasts: &[Toast]) -> Option<Element<'static, Message>> {
|
||||
if toasts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let items: Vec<Element<'static, Message>> = toasts
|
||||
.iter()
|
||||
.map(|toast| {
|
||||
let level = toast.level;
|
||||
let dismiss = button(text("\u{2715}").size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::DismissToast(toast.id))
|
||||
.padding([2, 4])
|
||||
.style(dismiss_btn);
|
||||
|
||||
container(
|
||||
row![
|
||||
text(toast.message.clone())
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced)
|
||||
.color(Color::WHITE),
|
||||
Space::with_width(Length::Fill),
|
||||
dismiss,
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(Alignment::Center)
|
||||
.padding([6, 12]),
|
||||
)
|
||||
.width(Length::Fixed(420.0))
|
||||
.style(toast_style(level))
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(
|
||||
container(
|
||||
container(
|
||||
iced::widget::Column::with_children(items)
|
||||
.spacing(4)
|
||||
.align_x(Alignment::Center),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.align_x(Alignment::Center)
|
||||
.padding(Padding { top: 8.0, right: 0.0, bottom: 0.0, left: 0.0 }),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Shrink)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,8 @@ use bds_core::model::{Media, Post, Project};
|
||||
use crate::app::Message;
|
||||
use crate::state::navigation::{OutputEntry, PanelTab, SidebarView, TaskSnapshot};
|
||||
use crate::state::tabs::Tab;
|
||||
use crate::views::{activity_bar, panel, project_selector, sidebar, status_bar, tab_bar, welcome};
|
||||
use crate::state::toast::Toast;
|
||||
use crate::views::{activity_bar, panel, project_selector, sidebar, status_bar, tab_bar, toast, welcome};
|
||||
|
||||
/// Main content area background.
|
||||
fn content_bg(_theme: &Theme) -> container::Style {
|
||||
@@ -67,6 +68,8 @@ pub fn view(
|
||||
project_dropdown_open: bool,
|
||||
// i18n
|
||||
locale: UiLocale,
|
||||
// Toasts
|
||||
toasts: &[Toast],
|
||||
) -> Element<'static, Message> {
|
||||
// Activity bar (leftmost column)
|
||||
let activity = activity_bar::view(sidebar_view, locale);
|
||||
@@ -181,12 +184,25 @@ pub fn view(
|
||||
None
|
||||
};
|
||||
|
||||
// Collect overlays: dropdowns and toasts
|
||||
let mut overlays: Vec<Element<'static, Message>> = Vec::new();
|
||||
|
||||
if let Some(toast_overlay) = toast::view(toasts) {
|
||||
overlays.push(toast_overlay);
|
||||
}
|
||||
|
||||
if let Some(overlay) = overlay {
|
||||
stack![base_layout, overlay]
|
||||
overlays.push(overlay);
|
||||
}
|
||||
|
||||
if overlays.is_empty() {
|
||||
base_layout
|
||||
} else {
|
||||
let mut layers = vec![base_layout];
|
||||
layers.extend(overlays);
|
||||
stack(layers)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
base_layout
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user