toast.rs

  1use crate::prelude::*;
  2use gpui::{prelude::*, AnyElement, RenderOnce};
  3use gpui::{Div, Element};
  4use smallvec::SmallVec;
  5
  6#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
  7pub enum ToastOrigin {
  8    #[default]
  9    Bottom,
 10    BottomRight,
 11}
 12
 13/// Don't use toast directly:
 14///
 15/// - For messages with a required action, use a `NotificationToast`.
 16/// - For messages that convey information, use a `StatusToast`.
 17///
 18/// A toast is a small, temporary window that appears to show a message to the user
 19/// or indicate a required action.
 20///
 21/// Toasts should not persist on the screen for more than a few seconds unless
 22/// they are actively showing the a process in progress.
 23///
 24/// Only one toast may be visible at a time.
 25#[derive(RenderOnce)]
 26pub struct Toast<V: 'static> {
 27    origin: ToastOrigin,
 28    children: SmallVec<[AnyElement<V>; 2]>,
 29}
 30
 31impl<V: 'static> Component<V> for Toast<V> {
 32    type Rendered = Div<V>;
 33
 34    fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
 35        let mut div = div();
 36
 37        if self.origin == ToastOrigin::Bottom {
 38            div = div.right_1_2();
 39        } else {
 40            div = div.right_2();
 41        }
 42
 43        div.z_index(5)
 44            .absolute()
 45            .bottom_9()
 46            .flex()
 47            .py_1()
 48            .px_1p5()
 49            .rounded_lg()
 50            .shadow_md()
 51            .overflow_hidden()
 52            .bg(cx.theme().colors().elevated_surface_background)
 53            .children(self.children)
 54    }
 55}
 56
 57impl<V: 'static> Toast<V> {
 58    pub fn new(origin: ToastOrigin) -> Self {
 59        Self {
 60            origin,
 61            children: SmallVec::new(),
 62        }
 63    }
 64
 65    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
 66        let mut div = div();
 67
 68        if self.origin == ToastOrigin::Bottom {
 69            div = div.right_1_2();
 70        } else {
 71            div = div.right_2();
 72        }
 73
 74        div.z_index(5)
 75            .absolute()
 76            .bottom_9()
 77            .flex()
 78            .py_1()
 79            .px_1p5()
 80            .rounded_lg()
 81            .shadow_md()
 82            .overflow_hidden()
 83            .bg(cx.theme().colors().elevated_surface_background)
 84            .children(self.children)
 85    }
 86}
 87
 88impl<V: 'static> ParentElement<V> for Toast<V> {
 89    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
 90        &mut self.children
 91    }
 92}
 93
 94#[cfg(feature = "stories")]
 95pub use stories::*;
 96
 97#[cfg(feature = "stories")]
 98mod stories {
 99    use gpui::{Div, Render};
100
101    use crate::{Label, Story};
102
103    use super::*;
104
105    pub struct ToastStory;
106
107    impl Render<Self> for ToastStory {
108        type Element = Div<Self>;
109
110        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
111            Story::container(cx)
112                .child(Story::title_for::<_, Toast<Self>>(cx))
113                .child(Story::label(cx, "Default"))
114                .child(Toast::new(ToastOrigin::Bottom).child(Label::new("label")))
115        }
116    }
117}