1use gpui3::AnyElement;
2use smallvec::SmallVec;
3
4use crate::prelude::*;
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(Element)]
26pub struct Toast<S: 'static + Send + Sync> {
27 origin: ToastOrigin,
28 children: SmallVec<[AnyElement<S>; 2]>,
29}
30
31impl<S: 'static + Send + Sync> Toast<S> {
32 pub fn new(origin: ToastOrigin) -> Self {
33 Self {
34 origin,
35 children: SmallVec::new(),
36 }
37 }
38
39 fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
40 let color = ThemeColor::new(cx);
41
42 let mut div = div();
43
44 if self.origin == ToastOrigin::Bottom {
45 div = div.right_1_2();
46 } else {
47 div = div.right_2();
48 }
49
50 div.z_index(5)
51 .absolute()
52 .bottom_9()
53 .flex()
54 .py_1()
55 .px_1p5()
56 .rounded_lg()
57 .shadow_md()
58 .overflow_hidden()
59 .fill(color.elevated_surface)
60 .children(self.children.drain(..))
61 }
62}
63
64impl<S: 'static + Send + Sync> ParentElement for Toast<S> {
65 type State = S;
66
67 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
68 &mut self.children
69 }
70}
71
72#[cfg(feature = "stories")]
73pub use stories::*;
74
75#[cfg(feature = "stories")]
76mod stories {
77 use std::marker::PhantomData;
78
79 use crate::{Label, Story};
80
81 use super::*;
82
83 #[derive(Element)]
84 pub struct ToastStory<S: 'static + Send + Sync + Clone> {
85 state_type: PhantomData<S>,
86 }
87
88 impl<S: 'static + Send + Sync + Clone> ToastStory<S> {
89 pub fn new() -> Self {
90 Self {
91 state_type: PhantomData,
92 }
93 }
94
95 fn render(
96 &mut self,
97 _view: &mut S,
98 cx: &mut ViewContext<S>,
99 ) -> impl Element<ViewState = S> {
100 Story::container(cx)
101 .child(Story::title_for::<_, Toast<S>>(cx))
102 .child(Story::label(cx, "Default"))
103 .child(Toast::new(ToastOrigin::Bottom).child(Label::new("label")))
104 }
105 }
106}