diff --git a/crates/storybook2/src/stories/components.rs b/crates/storybook2/src/stories/components.rs index b7f008ac08..d4f3ae10b9 100644 --- a/crates/storybook2/src/stories/components.rs +++ b/crates/storybook2/src/stories/components.rs @@ -14,6 +14,7 @@ pub mod tab; pub mod tab_bar; pub mod terminal; pub mod title_bar; +pub mod toast; pub mod toolbar; pub mod traffic_lights; pub mod workspace; diff --git a/crates/storybook2/src/stories/components/toast.rs b/crates/storybook2/src/stories/components/toast.rs new file mode 100644 index 0000000000..6226e0e31f --- /dev/null +++ b/crates/storybook2/src/stories/components/toast.rs @@ -0,0 +1,31 @@ +use std::marker::PhantomData; +use std::sync::Arc; + +use ui::prelude::*; +use ui::{Label, Toast, ToastOrigin}; + +use crate::story::Story; + +#[derive(Element)] +pub struct ToastStory { + state_type: PhantomData, +} + +impl ToastStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + Story::container(cx) + .child(Story::title_for::<_, Toast>(cx)) + .child(Story::label(cx, "Default")) + .child(Toast::new( + ToastOrigin::Bottom, + |_, _| vec![Label::new("label").into_any()], + Box::new(()), + )) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index feacea235d..89c762316e 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -52,6 +52,7 @@ pub enum ComponentStory { TabBar, Terminal, TitleBar, + Toast, Toolbar, TrafficLights, Workspace, @@ -82,6 +83,7 @@ impl ComponentStory { Self::TabBar => components::tab_bar::TabBarStory::new().into_any(), Self::Terminal => components::terminal::TerminalStory::new().into_any(), Self::TitleBar => components::title_bar::TitleBarStory::new().into_any(), + Self::Toast => components::toast::ToastStory::new().into_any(), Self::Toolbar => components::toolbar::ToolbarStory::new().into_any(), Self::TrafficLights => components::traffic_lights::TrafficLightsStory::new().into_any(), Self::Workspace => components::workspace::WorkspaceStory::new().into_any(), diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 9e28d30662..a913a9e2f8 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -20,6 +20,7 @@ mod tab; mod tab_bar; mod terminal; mod title_bar; +mod toast; mod toolbar; mod traffic_lights; mod workspace; @@ -46,6 +47,7 @@ pub use tab::*; pub use tab_bar::*; pub use terminal::*; pub use title_bar::*; +pub use toast::*; pub use toolbar::*; pub use traffic_lights::*; pub use workspace::*; diff --git a/crates/ui2/src/components/toast.rs b/crates/ui2/src/components/toast.rs new file mode 100644 index 0000000000..7a227f8463 --- /dev/null +++ b/crates/ui2/src/components/toast.rs @@ -0,0 +1,66 @@ +use crate::prelude::*; + +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] +pub enum ToastOrigin { + #[default] + Bottom, + BottomRight, +} + +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] +pub enum ToastVariant { + #[default] + Toast, + Status, +} + +/// A toast is a small, temporary window that appears to show a message to the user +/// or indicate a required action. +/// +/// Toasts should not persist on the screen for more than a few seconds unless +/// they are actively showing the a process in progress. +/// +/// Only one toast may be visible at a time. +#[derive(Element)] +pub struct Toast { + origin: ToastOrigin, + children: HackyChildren, + payload: HackyChildrenPayload, +} + +impl Toast { + pub fn new( + origin: ToastOrigin, + children: HackyChildren, + payload: HackyChildrenPayload, + ) -> Self { + Self { + origin, + children, + payload, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let color = ThemeColor::new(cx); + + let mut div = div(); + + if self.origin == ToastOrigin::Bottom { + div = div.right_1_2(); + } else { + div = div.right_4(); + } + + div.absolute() + .bottom_4() + .flex() + .py_2() + .px_1p5() + .min_w_40() + .rounded_md() + .fill(color.elevated_surface) + .max_w_64() + .children_any((self.children)(cx, self.payload.as_ref())) + } +} diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 0d51ab5eba..204bdb834e 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -30,6 +30,26 @@ impl SystemColor { } } +#[derive(Clone, Copy)] +pub struct ThemeColor { + pub border: Hsla, + pub border_variant: Hsla, + /// The background color of an elevated surface, like a modal, tooltip or toast. + pub elevated_surface: Hsla, +} + +impl ThemeColor { + pub fn new(cx: &WindowContext) -> Self { + let theme = theme(cx); + + Self { + border: theme.lowest.base.default.border, + border_variant: theme.lowest.variant.default.border, + elevated_surface: theme.middle.base.default.background, + } + } +} + #[derive(Default, PartialEq, EnumIter, Clone, Copy)] pub enum HighlightColor { #[default]