195 lines · 6.2 KB · Rust
RawDownload
use crate::prelude::*;use gpui::{AnyElement, IntoElement, ParentElement, Styled}; /// Severity levels that determine the style of the banner.#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum Severity {    Info,    Success,    Warning,    Error,} /// Banners provide informative and brief messages without interrupting the user./// This component offers four severity levels that can be used depending on the message.////// # Usage Example////// ```/// use ui::{Banner};//////    Banner::new()///     .severity(Severity::Info)///     .children(Label::new("This is an informational message"))///     .action_slot(///         Button::new("learn-more", "Learn More")///             .icon(IconName::ArrowUpRight)///             .icon_size(IconSize::XSmall)///             .icon_position(IconPosition::End),///     )/// ```#[derive(IntoElement, RegisterComponent)]pub struct Banner {    severity: Severity,    children: Vec<AnyElement>,    icon: Option<(IconName, Option<Color>)>,    action_slot: Option<AnyElement>,} impl Banner {    /// Creates a new `Banner` component with default styling.    pub fn new() -> Self {        Self {            severity: Severity::Info,            children: Vec::new(),            icon: None,            action_slot: None,        }    }     /// Sets the severity of the banner.    pub fn severity(mut self, severity: Severity) -> Self {        self.severity = severity;        self    }     /// Sets an icon to display in the banner with an optional color.    pub fn icon(mut self, icon: IconName, color: Option<impl Into<Color>>) -> Self {        self.icon = Some((icon, color.map(|c| c.into())));        self    }     /// A slot for actions, such as CTA or dismissal buttons.    pub fn action_slot(mut self, element: impl IntoElement) -> Self {        self.action_slot = Some(element.into_any_element());        self    }} impl ParentElement for Banner {    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {        self.children.extend(elements)    }} impl RenderOnce for Banner {    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {        let base = h_flex()            .py_0p5()            .rounded_sm()            .flex_wrap()            .justify_between()            .border_1();         let (icon, icon_color, bg_color, border_color) = match self.severity {            Severity::Info => (                IconName::Info,                Color::Muted,                cx.theme().status().info_background.opacity(0.5),                cx.theme().colors().border.opacity(0.5),            ),            Severity::Success => (                IconName::Check,                Color::Success,                cx.theme().status().success.opacity(0.1),                cx.theme().status().success.opacity(0.2),            ),            Severity::Warning => (                IconName::Warning,                Color::Warning,                cx.theme().status().warning_background.opacity(0.5),                cx.theme().status().warning_border.opacity(0.4),            ),            Severity::Error => (                IconName::XCircle,                Color::Error,                cx.theme().status().error.opacity(0.1),                cx.theme().status().error.opacity(0.2),            ),        };         let mut container = base.bg(bg_color).border_color(border_color);         let mut content_area = h_flex().id("content_area").gap_1p5().overflow_x_scroll();         if self.icon.is_none() {            content_area =                content_area.child(Icon::new(icon).size(IconSize::XSmall).color(icon_color));        }         content_area = content_area.children(self.children);         if let Some(action_slot) = self.action_slot {            container = container                .pl_2()                .pr_0p5()                .gap_2()                .child(content_area)                .child(action_slot);        } else {            container = container.px_2().child(div().w_full().child(content_area));        }         container    }} impl Component for Banner {    fn scope() -> ComponentScope {        ComponentScope::Notification    }     fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {        let severity_examples = vec![            single_example(                "Default",                Banner::new()                    .child(Label::new("This is a default banner with no customization"))                    .into_any_element(),            ),            single_example(                "Info",                Banner::new()                    .severity(Severity::Info)                    .child(Label::new("This is an informational message"))                    .action_slot(                        Button::new("learn-more", "Learn More")                            .icon(IconName::ArrowUpRight)                            .icon_size(IconSize::XSmall)                            .icon_position(IconPosition::End),                    )                    .into_any_element(),            ),            single_example(                "Success",                Banner::new()                    .severity(Severity::Success)                    .child(Label::new("Operation completed successfully"))                    .action_slot(Button::new("dismiss", "Dismiss"))                    .into_any_element(),            ),            single_example(                "Warning",                Banner::new()                    .severity(Severity::Warning)                    .child(Label::new("Your settings file uses deprecated settings"))                    .action_slot(Button::new("update", "Update Settings"))                    .into_any_element(),            ),            single_example(                "Error",                Banner::new()                    .severity(Severity::Error)                    .child(Label::new("Connection error: unable to connect to server"))                    .action_slot(Button::new("reconnect", "Retry"))                    .into_any_element(),            ),        ];         Some(            example_group(severity_examples)                .vertical()                .into_any_element(),        )    }}