diff --git a/crates/story/src/alert_story.rs b/crates/story/src/alert_story.rs index cd8dbf0c..c6158cfe 100644 --- a/crates/story/src/alert_story.rs +++ b/crates/story/src/alert_story.rs @@ -6,6 +6,7 @@ use gpui_component::{ alert::Alert, button::{Button, ButtonGroup}, dock::PanelControl, + text::TextView, v_flex, IconName, Selectable as _, Sizable as _, Size, }; @@ -64,20 +65,6 @@ impl Render for AlertStory { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { v_flex() .gap_4() - .child( - Alert::warning( - "banner-1", - "This is a banner alert, it will take the full width of the container.", - ) - .banner() - .on_close(cx.listener(|this, _, _, cx| { - this.banner_visible = !this.banner_visible; - cx.notify(); - })) - .visible(self.banner_visible) - .with_size(self.size) - .icon(IconName::Bell), - ) .child( ButtonGroup::new("toggle-size") .outline() @@ -114,56 +101,135 @@ impl Render for AlertStory { })), ) .child( - section("Info").w_2_3().child( - Alert::info("info1", "This is an info alert.") - .with_size(self.size) - .title("Info message") - .on_close(cx.listener(|_, _, _, _| { - println!("Info alert closed"); - })), - ), - ) - .child( - section("Success with Title").w_2_3().child( - Alert::success( - "success-1", - "You have successfully submitted your form.\n\ - Thank you for your submission!", + section("Default").w_2_3().child( + Alert::new( + "alert-default", + TextView::markdown( + "md", + "This is an alert with icon, title and description (in Markdown).\n\ + - This is a **list** item.\n\ + - This is another list item.", + ), ) .with_size(self.size) - .title("Submit Successful"), + .title("Success! Your changes have been saved"), ), ) .child( - section("Warning").w_2_3().child( - Alert::warning( - "warning-1", - "This is a warning alert with icon and title.\n\ - This is second line of text to test is the line-height is correct.", - ) - .with_size(self.size), + section("With variant").w_2_3().child( + v_flex() + .w_full() + .gap_3() + .child( + Alert::info("info1", "This is an info alert.") + .with_size(self.size) + .title("Info message") + .on_close(cx.listener(|_, _, _, _| { + println!("Info alert closed"); + })), + ) + .child( + Alert::success( + "success-1", + "You have successfully submitted your form.\n\ + Thank you for your submission!", + ) + .with_size(self.size) + .title("Submit Successful"), + ) + .child( + Alert::warning( + "warning-1", + "This is a warning alert with icon and title.\n\ + This is second line of text to test is the line-height is correct.", + ) + .with_size(self.size), + ) + .child( + Alert::error( + "error-1", + TextView::markdown( + "error-message", + "Please verify your billing information and try again.\n\ + - Check your card details\n\ + - Ensure sufficient funds\n\ + - Verify billing address", + ), + ) + .with_size(self.size) + .title("Unable to process your payment."), + ), ), ) .child( - section("Error").w_2_3().child( - Alert::error( - "error-1", - "There was an error submitting your form.\n\ - Please try again later, if you still have issues, please contact support.", - ) - .with_size(self.size) - .title("Error!"), + section("Banner").w_2_3().child( + v_flex() + .w_full() + .gap_2() + .child( + Alert::new( + "banner-1", + "This is a banner alert, it will take \ + the full width of the container.", + ) + .banner() + .on_close(cx.listener(|this, _, _, cx| { + this.banner_visible = !this.banner_visible; + cx.notify(); + })) + .visible(self.banner_visible) + .with_size(self.size), + ) + .child( + Alert::info( + "banner-info", + "This is a banner alert, it will take the full width of the\ + container.", + ) + .banner() + .with_size(self.size), + ) + .child( + Alert::success( + "banner-success", + "This is a banner alert, it will take the full width of the\ + container.", + ) + .banner() + .with_size(self.size), + ) + .child( + Alert::warning( + "banner-warning", + "This is a banner alert, it will take the full width of the\ + container.", + ) + .banner() + .with_size(self.size), + ) + .child( + Alert::error( + "banner-error", + "This is a banner alert, it will take the full width of the\ + container.", + ) + .banner() + .with_size(self.size), + ), ), ) .child( section("Custom Icon").w_2_3().child( - Alert::info( + Alert::new( "other-1", - "Custom icon with info alert with long long long long long long long long long long long long long long long long long long long long messageeeeeeeee.", + "Custom icon with info alert with long \ + long long long long long long long long \ + long long long long long long long long long \ + long long messageeeeeeeee.", ) .title("Custom Icon") .with_size(self.size) - .icon(IconName::Bell), + .icon(IconName::Calendar), ), ) } diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index 222027b9..422be731 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -93,24 +93,17 @@ impl Gallery { }; if let Some(init_story) = init_story { - this.set_active_story(init_story, cx); + this.set_active_story(init_story, window, cx); } this } - fn set_active_story(&mut self, name: &str, cx: &mut App) { - let group_index = 1; - let Some(story_index) = self.stories.get(group_index).and_then(|(_, stories)| { - stories - .iter() - .position(|story| story.read(cx).name.to_lowercase().replace("story", "") == name) - }) else { - return; - }; - - self.active_group_index = Some(group_index); - self.active_index = Some(story_index); + fn set_active_story(&mut self, name: &str, window: &mut Window, cx: &mut App) { + let name = name.to_string(); + self.search_input.update(cx, |this, cx| { + this.set_value(&name, window, cx); + }) } fn view(init_story: Option<&str>, window: &mut Window, cx: &mut App) -> Entity { diff --git a/crates/ui/src/alert.rs b/crates/ui/src/alert.rs index 588c538c..f5d5b179 100644 --- a/crates/ui/src/alert.rs +++ b/crates/ui/src/alert.rs @@ -1,16 +1,21 @@ use std::rc::Rc; use gpui::{ - div, prelude::FluentBuilder as _, px, relative, App, ClickEvent, ElementId, Empty, Hsla, + div, prelude::FluentBuilder as _, px, relative, rems, App, ClickEvent, ElementId, Empty, Hsla, InteractiveElement, IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled, Window, }; -use crate::{h_flex, text::Text, ActiveTheme as _, Icon, IconName, Sizable, Size, StyledExt}; +use crate::{ + h_flex, + text::{Text, TextViewStyle}, + ActiveTheme as _, Icon, IconName, Sizable, Size, StyleSized, StyledExt, +}; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum AlertVariant { #[default] + Secondary, Info, Success, Warning, @@ -20,6 +25,7 @@ pub enum AlertVariant { impl AlertVariant { fn fg(&self, cx: &App) -> Hsla { match self { + AlertVariant::Secondary => cx.theme().secondary_foreground, AlertVariant::Info => cx.theme().info, AlertVariant::Success => cx.theme().success, AlertVariant::Warning => cx.theme().warning, @@ -29,6 +35,17 @@ impl AlertVariant { fn color(&self, cx: &App) -> Hsla { match self { + AlertVariant::Secondary => cx.theme().secondary, + AlertVariant::Info => cx.theme().info, + AlertVariant::Success => cx.theme().success, + AlertVariant::Warning => cx.theme().warning, + AlertVariant::Error => cx.theme().danger, + } + } + + fn border_color(&self, cx: &App) -> Hsla { + match self { + AlertVariant::Secondary => cx.theme().border, AlertVariant::Info => cx.theme().info, AlertVariant::Success => cx.theme().success, AlertVariant::Warning => cx.theme().warning, @@ -43,7 +60,7 @@ pub struct Alert { id: ElementId, style: StyleRefinement, variant: AlertVariant, - icon: Option, + icon: Icon, title: Option, message: Text, size: Size, @@ -54,12 +71,12 @@ pub struct Alert { impl Alert { /// Create a new alert with the given message. - fn new(id: impl Into, message: impl Into) -> Self { + pub fn new(id: impl Into, message: impl Into) -> Self { Self { id: id.into(), style: StyleRefinement::default(), variant: AlertVariant::default(), - icon: None, + icon: Icon::new(IconName::Info), title: None, message: message.into(), size: Size::default(), @@ -105,7 +122,7 @@ impl Alert { /// Set the icon for the alert. pub fn icon(mut self, icon: impl Into) -> Self { - self.icon = Some(icon.into()); + self.icon = icon.into(); self } @@ -159,65 +176,40 @@ impl RenderOnce for Alert { return Empty.into_any_element(); } - let (radius, padding_x, padding_y, gap, line_height, icon_mt) = match self.size { - Size::XSmall => (cx.theme().radius, px(12.), px(6.), px(6.), 1.2, px(2.5)), - Size::Small => (cx.theme().radius, px(12.), px(8.), px(6.), 1.2, px(1.5)), - Size::Large => ( - cx.theme().radius * 3., - px(20.), - px(16.), - px(12.), - 1.4, - px(0.), - ), - _ => ( - cx.theme().radius * 2., - px(16.), - px(12.), - px(8.), - 1.3, - px(1.), - ), + let (radius, padding_x, padding_y, gap) = match self.size { + Size::XSmall => (cx.theme().radius, px(12.), px(6.), px(6.)), + Size::Small => (cx.theme().radius, px(12.), px(8.), px(6.)), + Size::Large => (cx.theme().radius_lg, px(20.), px(16.), px(12.)), + _ => (cx.theme().radius_lg, px(16.), px(12.), px(12.)), }; let color = self.variant.color(cx); + let fg = self.variant.fg(cx); + let border_color = self.variant.border_color(cx); h_flex() .id(self.id) .w_full() - .bg(color.opacity(0.06)) - .text_color(self.variant.fg(cx)) + .text_color(fg) + .bg(color.opacity(0.08)) .px(padding_x) .py(padding_y) .gap(gap) .justify_between() - .line_height(relative(line_height)) - .map(|this| match self.size { - Size::Large => this.text_base(), - _ => this.text_sm(), - }) - .when(!self.banner, |this| { - this.rounded(radius) - .border_1() - .border_color(color) - .items_start() - }) + .input_text_size(self.size.smaller()) + .border_1() + .border_color(border_color) + .when(!self.banner, |this| this.rounded(radius).items_start()) .refine_style(&self.style) .child( div() .flex() .flex_1() .items_start() + .when(self.banner, |this| this.items_center()) .overflow_hidden() .gap(gap) - .child( - div().mt(icon_mt).child( - self.icon - .unwrap_or(IconName::Info.into()) - .with_size(self.size) - .flex_shrink_0(), - ), - ) + .child(self.icon) .child( div() .flex_1() @@ -228,13 +220,17 @@ impl RenderOnce for Alert { div() .w_full() .truncate() - .mb_1() .font_semibold() + .line_height(relative(1.)) + .mb(rems(0.3)) .child(title), ) }) }) - .child(self.message), + .child( + self.message + .style(TextViewStyle::default().paragraph_gap(rems(0.2))), + ), ), ) .when_some(self.on_close, |this, on_close| { @@ -250,7 +246,6 @@ impl RenderOnce for Alert { }) .child( Icon::new(IconName::Close) - .text_color(cx.theme().foreground) .with_size(self.size.max(Size::Medium)) .flex_shrink_0(), ), diff --git a/crates/ui/src/text/text_view.rs b/crates/ui/src/text/text_view.rs index 4a96bc02..24f7e346 100644 --- a/crates/ui/src/text/text_view.rs +++ b/crates/ui/src/text/text_view.rs @@ -59,6 +59,18 @@ impl From for Text { } } +impl Text { + /// Set the style for [`TextView`]. + /// + /// Do nothing if this is `String`. + pub fn style(self, style: TextViewStyle) -> Self { + match self { + Self::String(s) => Self::String(s), + Self::TextView(e) => Self::TextView(e.style(style)), + } + } +} + impl RenderOnce for Text { fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { match self {