diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 91fa4a85..70b23852 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -550,7 +550,8 @@ impl StoryContainer { cx: &mut Context, ) { struct Info; - let note = Notification::new(format!("You have clicked panel info on: {}", self.name)) + let note = Notification::new() + .message(format!("You have clicked panel info on: {}", self.name)) .id::(); window.push_notification(note, cx); } @@ -567,8 +568,9 @@ impl StoryContainer { } struct Search; - let note = - Notification::new(format!("You have toggled search on: {}", self.name)).id::(); + let note = Notification::new() + .message(format!("You have toggled search on: {}", self.name)) + .id::(); window.push_notification(note, cx); } } diff --git a/crates/story/src/notification_story.rs b/crates/story/src/notification_story.rs index 5fd38ecb..679417c0 100644 --- a/crates/story/src/notification_story.rs +++ b/crates/story/src/notification_story.rs @@ -6,11 +6,19 @@ use gpui::{ use gpui_component::{ button::{Button, ButtonVariants as _}, notification::{Notification, NotificationType}, + text::TextView, ContextModal as _, }; use crate::section; +const NOTIFICATION_MARKDOWN: &str = r#" +This is a custom notification. +- List item 1 +- List item 2 +- [Click here](https://github.com/longbridge/gpui-component) +"#; + pub struct NotificationStory { focus_handle: FocusHandle, } @@ -130,9 +138,10 @@ impl Render for NotificationStory { struct TestNotification; window.push_notification( - Notification::new("There was a problem with your request.") + Notification::new() .id::() .title("Uh oh! Something went wrong.") + .message("There was a problem with your request.") .autohide(false) .action(|_, cx| { Button::new("try-again").label("Try again").on_click( @@ -151,5 +160,23 @@ impl Render for NotificationStory { })), ), ) + .child( + section("Custom Notification").child( + Button::new("show-notify-custom") + .label("Show Custom Notification") + .on_click(cx.listener(|_, _, window, cx| { + window.push_notification( + Notification::new().content(|_, _| { + TextView::markdown( + "notification-markdown", + NOTIFICATION_MARKDOWN, + ) + .into_any_element() + }), + cx, + ) + })), + ), + ) } } diff --git a/crates/ui/src/notification.rs b/crates/ui/src/notification.rs index 1a4265c1..043ab1c4 100644 --- a/crates/ui/src/notification.rs +++ b/crates/ui/src/notification.rs @@ -6,10 +6,10 @@ use std::{ }; use gpui::{ - div, prelude::FluentBuilder, px, Animation, AnimationExt, App, AppContext, ClickEvent, Context, - DismissEvent, ElementId, Entity, EventEmitter, InteractiveElement as _, IntoElement, - ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, - Window, + div, prelude::FluentBuilder, px, Animation, AnimationExt, AnyElement, App, AppContext, + ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter, InteractiveElement as _, + IntoElement, ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, + Subscription, Window, }; use smol::Timer; @@ -66,41 +66,42 @@ pub struct Notification { id: NotificationId, type_: Option, title: Option, - message: SharedString, + message: Option, icon: Option, autohide: bool, action_builder: Option) -> Button>>, + content_builder: Option) -> AnyElement>>, on_click: Option>, closing: bool, } impl From for Notification { fn from(s: String) -> Self { - Self::new(s) + Self::new().message(s) } } impl From for Notification { fn from(s: SharedString) -> Self { - Self::new(s) + Self::new().message(s) } } impl From<&'static str> for Notification { fn from(s: &'static str) -> Self { - Self::new(s) + Self::new().message(s) } } impl From<(NotificationType, &'static str)> for Notification { fn from((type_, content): (NotificationType, &'static str)) -> Self { - Self::new(content).with_type(type_) + Self::new().message(content).with_type(type_) } } impl From<(NotificationType, SharedString)> for Notification { fn from((type_, content): (NotificationType, SharedString)) -> Self { - Self::new(content).with_type(type_) + Self::new().message(content).with_type(type_) } } @@ -110,37 +111,51 @@ impl Notification { /// Create a new notification with the given content. /// /// default width is 320px. - pub fn new(message: impl Into) -> Self { + pub fn new() -> Self { let id: SharedString = uuid::Uuid::new_v4().to_string().into(); let id = (TypeId::of::(), id.into()); Self { id: id.into(), title: None, - message: message.into(), + message: None, type_: None, icon: None, autohide: true, action_builder: None, + content_builder: None, on_click: None, closing: false, } } + pub fn message(mut self, message: impl Into) -> Self { + self.message = Some(message.into()); + self + } + pub fn info(message: impl Into) -> Self { - Self::new(message).with_type(NotificationType::Info) + Self::new() + .message(message) + .with_type(NotificationType::Info) } pub fn success(message: impl Into) -> Self { - Self::new(message).with_type(NotificationType::Success) + Self::new() + .message(message) + .with_type(NotificationType::Success) } pub fn warning(message: impl Into) -> Self { - Self::new(message).with_type(NotificationType::Warning) + Self::new() + .message(message) + .with_type(NotificationType::Warning) } pub fn error(message: impl Into) -> Self { - Self::new(message).with_type(NotificationType::Error) + Self::new() + .message(message) + .with_type(NotificationType::Error) } /// Set the type for unique identification of the notification. @@ -225,6 +240,15 @@ impl Notification { }) .detach() } + + /// Set the content of the notification. + pub fn content( + mut self, + content: impl Fn(&mut Window, &mut Context) -> AnyElement + 'static, + ) -> Self { + self.content_builder = Some(Rc::new(content)); + self + } } impl EventEmitter for Notification {} impl FluentBuilder for Notification {} @@ -257,12 +281,17 @@ impl Render for Notification { .child( v_flex() .flex_1() + .overflow_hidden() .when(has_icon, |this| this.pl_6()) .when_some(self.title.clone(), |this, title| { this.child(div().text_sm().font_semibold().child(title)) }) - .overflow_hidden() - .child(div().text_sm().child(self.message.clone())), + .when_some(self.message.clone(), |this, message| { + this.child(div().text_sm().child(message)) + }) + .when_some(self.content_builder.clone(), |this, child_builder| { + this.child(child_builder(window, cx)) + }), ) .when_some(self.action_builder.clone(), |this, action_builder| { this.child(action_builder(window, cx).small().outline().mr_1())