use gpui::AnyElement; use crate::prelude::*; /// A callout component for displaying important information that requires user attention.////// # Usage Example////// ```/// use ui::{Callout};////// Callout::new()/// .icon(Icon::new(IconName::Warning).color(Color::Warning))/// .title(Label::new("Be aware of your subscription!"))/// .description(Label::new("Your subscription is about to expire. Renew now!"))/// .primary_action(Button::new("renew", "Renew Now"))/// .secondary_action(Button::new("remind", "Remind Me Later"))/// ```///#[derive(IntoElement, RegisterComponent)]pub struct Callout { icon: Option<Icon>, title: Option<SharedString>, description: Option<SharedString>, primary_action: Option<AnyElement>, secondary_action: Option<AnyElement>, line_height: Option<Pixels>,} impl Callout { /// Creates a new `Callout` component with default styling. pub fn new() -> Self { Self { icon: None, title: None, description: None, primary_action: None, secondary_action: None, line_height: None, } } /// Sets the icon to display in the callout. pub fn icon(mut self, icon: Icon) -> Self { self.icon = Some(icon); self } /// Sets the title of the callout. pub fn title(mut self, title: impl Into<SharedString>) -> Self { self.title = Some(title.into()); self } /// Sets the description of the callout. /// The description can be single or multi-line text. pub fn description(mut self, description: impl Into<SharedString>) -> Self { self.description = Some(description.into()); self } /// Sets the primary call-to-action button. pub fn primary_action(mut self, action: impl IntoElement) -> Self { self.primary_action = Some(action.into_any_element()); self } /// Sets an optional secondary call-to-action button. pub fn secondary_action(mut self, action: impl IntoElement) -> Self { self.secondary_action = Some(action.into_any_element()); self } /// Sets a custom line height for the callout content. pub fn line_height(mut self, line_height: Pixels) -> Self { self.line_height = Some(line_height); self }} impl RenderOnce for Callout { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let line_height = self.line_height.unwrap_or(window.line_height()); h_flex() .w_full() .p_2() .gap_2() .items_start() .bg(cx.theme().colors().panel_background) .overflow_x_hidden() .when_some(self.icon, |this, icon| { this.child(h_flex().h(line_height).justify_center().child(icon)) }) .child( v_flex() .w_full() .child( h_flex() .h(line_height) .w_full() .gap_1() .flex_wrap() .justify_between() .when_some(self.title, |this, title| { this.child(h_flex().child(Label::new(title).size(LabelSize::Small))) }) .when( self.primary_action.is_some() || self.secondary_action.is_some(), |this| { this.child( h_flex() .gap_0p5() .when_some(self.secondary_action, |this, action| { this.child(action) }) .when_some(self.primary_action, |this, action| { this.child(action) }), ) }, ), ) .when_some(self.description, |this, description| { this.child( div() .w_full() .flex_1() .child(description) .text_ui_sm(cx) .text_color(cx.theme().colors().text_muted), ) }), ) }} impl Component for Callout { fn scope() -> ComponentScope { ComponentScope::Notification } fn description() -> Option<&'static str> { Some( "Used to display a callout for situations where the user needs to know some information, and likely make a decision. This might be a thread running out of tokens, or running out of prompts on a plan and needing to upgrade.", ) } fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> { let callout_examples = vec![ single_example( "Simple with Title Only", Callout::new() .icon( Icon::new(IconName::Info) .color(Color::Accent) .size(IconSize::Small), ) .title("System maintenance scheduled for tonight") .primary_action(Button::new("got-it", "Got it").label_size(LabelSize::Small)) .into_any_element(), ) .width(px(580.)), single_example( "With Title and Description", Callout::new() .icon( Icon::new(IconName::Warning) .color(Color::Warning) .size(IconSize::Small), ) .title("Your settings contain deprecated values") .description( "We'll backup your current settings and update them to the new format.", ) .primary_action( Button::new("update", "Backup & Update").label_size(LabelSize::Small), ) .secondary_action( Button::new("dismiss", "Dismiss").label_size(LabelSize::Small), ) .into_any_element(), ) .width(px(580.)), single_example( "Error with Multiple Actions", Callout::new() .icon( Icon::new(IconName::X) .color(Color::Error) .size(IconSize::Small), ) .title("Thread reached the token limit") .description("Start a new thread from a summary to continue the conversation.") .primary_action( Button::new("new-thread", "Start New Thread").label_size(LabelSize::Small), ) .secondary_action( Button::new("view-summary", "View Summary").label_size(LabelSize::Small), ) .into_any_element(), ) .width(px(580.)), single_example( "Multi-line Description", Callout::new() .icon( Icon::new(IconName::Sparkle) .color(Color::Accent) .size(IconSize::Small), ) .title("Upgrade to Pro") .description("• Unlimited threads\n• Priority support\n• Advanced analytics") .primary_action( Button::new("upgrade", "Upgrade Now").label_size(LabelSize::Small), ) .secondary_action( Button::new("learn-more", "Learn More").label_size(LabelSize::Small), ) .into_any_element(), ) .width(px(580.)), ]; Some( example_group(callout_examples) .vertical() .into_any_element(), ) }}
use gpui::AnyElement;
use crate::prelude::*;
/// A callout component for displaying important information that requires user attention.
///
/// # Usage Example
///
/// ```
/// use ui::{Callout};
///
/// Callout::new()
/// .icon(Icon::new(IconName::Warning).color(Color::Warning))
/// .title(Label::new("Be aware of your subscription!"))
/// .description(Label::new("Your subscription is about to expire. Renew now!"))
/// .primary_action(Button::new("renew", "Renew Now"))
/// .secondary_action(Button::new("remind", "Remind Me Later"))
/// ```
///
#[derive(IntoElement, RegisterComponent)]
pub struct Callout {
icon: Option<Icon>,
title: Option<SharedString>,
description: Option<SharedString>,
primary_action: Option<AnyElement>,
secondary_action: Option<AnyElement>,
line_height: Option<Pixels>,
}
impl Callout {
/// Creates a new `Callout` component with default styling.
pub fn new() -> Self {
Self {
icon: None,
title: None,
description: None,
primary_action: None,
secondary_action: None,
line_height: None,
}
}
/// Sets the icon to display in the callout.
pub fn icon(mut self, icon: Icon) -> Self {
self.icon = Some(icon);
self
}
/// Sets the title of the callout.
pub fn title(mut self, title: impl Into<SharedString>) -> Self {
self.title = Some(title.into());
self
}
/// Sets the description of the callout.
/// The description can be single or multi-line text.
pub fn description(mut self, description: impl Into<SharedString>) -> Self {
self.description = Some(description.into());
self
}
/// Sets the primary call-to-action button.
pub fn primary_action(mut self, action: impl IntoElement) -> Self {
self.primary_action = Some(action.into_any_element());
self
}
/// Sets an optional secondary call-to-action button.
pub fn secondary_action(mut self, action: impl IntoElement) -> Self {
self.secondary_action = Some(action.into_any_element());
self
}
/// Sets a custom line height for the callout content.
pub fn line_height(mut self, line_height: Pixels) -> Self {
self.line_height = Some(line_height);
self
}
}
impl RenderOnce for Callout {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let line_height = self.line_height.unwrap_or(window.line_height());
h_flex()
.w_full()
.p_2()
.gap_2()
.items_start()
.bg(cx.theme().colors().panel_background)
.overflow_x_hidden()
.when_some(self.icon, |this, icon| {
this.child(h_flex().h(line_height).justify_center().child(icon))
})
.child(
v_flex()
.w_full()
.child(
h_flex()
.h(line_height)
.w_full()
.gap_1()
.flex_wrap()
.justify_between()
.when_some(self.title, |this, title| {
this.child(h_flex().child(Label::new(title).size(LabelSize::Small)))
})
.when(
self.primary_action.is_some() || self.secondary_action.is_some(),
|this| {
this.child(
h_flex()
.gap_0p5()
.when_some(self.secondary_action, |this, action| {
this.child(action)
})
.when_some(self.primary_action, |this, action| {
this.child(action)
}),
)
},
),
)
.when_some(self.description, |this, description| {
this.child(
div()
.w_full()
.flex_1()
.child(description)
.text_ui_sm(cx)
.text_color(cx.theme().colors().text_muted),
)
}),
)
}
}
impl Component for Callout {
fn scope() -> ComponentScope {
ComponentScope::Notification
}
fn description() -> Option<&'static str> {
Some(
"Used to display a callout for situations where the user needs to know some information, and likely make a decision. This might be a thread running out of tokens, or running out of prompts on a plan and needing to upgrade.",
)
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
let callout_examples = vec![
single_example(
"Simple with Title Only",
Callout::new()
.icon(
Icon::new(IconName::Info)
.color(Color::Accent)
.size(IconSize::Small),
)
.title("System maintenance scheduled for tonight")
.primary_action(Button::new("got-it", "Got it").label_size(LabelSize::Small))
.into_any_element(),
)
.width(px(580.)),
single_example(
"With Title and Description",
Callout::new()
.icon(
Icon::new(IconName::Warning)
.color(Color::Warning)
.size(IconSize::Small),
)
.title("Your settings contain deprecated values")
.description(
"We'll backup your current settings and update them to the new format.",
)
.primary_action(
Button::new("update", "Backup & Update").label_size(LabelSize::Small),
)
.secondary_action(
Button::new("dismiss", "Dismiss").label_size(LabelSize::Small),
)
.into_any_element(),
)
.width(px(580.)),
single_example(
"Error with Multiple Actions",
Callout::new()
.icon(
Icon::new(IconName::X)
.color(Color::Error)
.size(IconSize::Small),
)
.title("Thread reached the token limit")
.description("Start a new thread from a summary to continue the conversation.")
.primary_action(
Button::new("new-thread", "Start New Thread").label_size(LabelSize::Small),
)
.secondary_action(
Button::new("view-summary", "View Summary").label_size(LabelSize::Small),
)
.into_any_element(),
)
.width(px(580.)),
single_example(
"Multi-line Description",
Callout::new()
.icon(
Icon::new(IconName::Sparkle)
.color(Color::Accent)
.size(IconSize::Small),
)
.title("Upgrade to Pro")
.description("• Unlimited threads\n• Priority support\n• Advanced analytics")
.primary_action(
Button::new("upgrade", "Upgrade Now").label_size(LabelSize::Small),
)
.secondary_action(
Button::new("learn-more", "Learn More").label_size(LabelSize::Small),
)
.into_any_element(),
)
.width(px(580.)),
];
Some(
example_group(callout_examples)
.vertical()
.into_any_element(),
)
}
}