From 4835cd26ae26247b016fbb9d7afd19ea8dedff82 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 18 Feb 2025 13:55:42 -0500 Subject: [PATCH 1/8] Update component_preview.rs --- .../src/component_preview.rs | 167 ++++++++++++------ 1 file changed, 114 insertions(+), 53 deletions(-) diff --git a/crates/component_preview/src/component_preview.rs b/crates/component_preview/src/component_preview.rs index e8edd392fdc551..24cbfed386cde1 100644 --- a/crates/component_preview/src/component_preview.rs +++ b/crates/component_preview/src/component_preview.rs @@ -3,7 +3,8 @@ //! A view for exploring Zed components. use component::{components, ComponentMetadata}; -use gpui::{prelude::*, App, EventEmitter, FocusHandle, Focusable, Window}; +use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window}; +use gpui::{ListState, ScrollHandle, UniformListScrollHandle}; use ui::prelude::*; use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId}; @@ -12,7 +13,7 @@ pub fn init(cx: &mut App) { cx.observe_new(|workspace: &mut Workspace, _, _cx| { workspace.register_action( |workspace, _: &workspace::OpenComponentPreview, window, cx| { - let component_preview = cx.new(ComponentPreview::new); + let component_preview = cx.new(|cx| ComponentPreview::new(window, cx)); workspace.add_item_to_active_pane( Box::new(component_preview), None, @@ -28,55 +29,92 @@ pub fn init(cx: &mut App) { struct ComponentPreview { focus_handle: FocusHandle, + view_scroll_handle: ScrollHandle, + nav_scroll_handle: UniformListScrollHandle, + components: Vec, + component_list: ListState, } impl ComponentPreview { - pub fn new(cx: &mut Context) -> Self { + pub fn new(_window: &mut Window, cx: &mut Context) -> Self { + let components = components().all_sorted(); + let initial_length = components.len(); + println!("Initial length: {}", initial_length); + Self { focus_handle: cx.focus_handle(), + view_scroll_handle: ScrollHandle::new(), + nav_scroll_handle: UniformListScrollHandle::new(), + components, + component_list: ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), { + let this = cx.entity().downgrade(); + move |ix, window: &mut Window, cx: &mut App| { + this.update(cx, |this, cx| { + // div() + // .child(this.components[ix].name().clone()) + // .into_any_element() + this.render_preview(ix, window, cx).into_any_element() + }) + .unwrap() + } + }), } } - fn render_sidebar(&self, _window: &Window, _cx: &Context) -> impl IntoElement { - let components = components().all_sorted(); - let sorted_components = components.clone(); - - v_flex() - .max_w_48() - .gap_px() - .p_1() - .children( - sorted_components - .into_iter() - .map(|component| self.render_sidebar_entry(&component, _cx)), - ) - .child( - Label::new("These will be clickable once the layout is moved to a gpui::List.") - .color(Color::Muted) - .size(LabelSize::XSmall) - .italic(), - ) + // fn update_list(self, _window: &Window, _cx: &mut App) -> ListState { + // let length = components().all_sorted().len(); + + // ListState::new( + // length, + // gpui::ListAlignment::Top, + // px(500.0), + // move |ix, _, _| self.render_sidebar_entry(ix).into_any_element(), + // ) + // } + + // fn render_sidebar(&self, window: &Window, cx: &Context) -> impl IntoElement { + // let components = components().all_sorted(); + + // List::new("component-list") + // .track_scroll() + // .on_scroll( + // cx.listener(|this, event, cx| { + // this.component_list.scroll_to(event.top_offset_y, cx) + // }), + // ) + // .on_scroll_to_item( + // cx.listener(|this, index, cx| this.component_list.scroll_to_item(index, true, cx)), + // ) + // .state(&self.component_list) + // .item_size(Pixels(24.0)) + // .child(ListHeader::new("").child(Label::new("Components").size(LabelSize::Small))) + // .items(components.iter().enumerate().map(|(index, component)| { + // ListItem::new(index).child(self.render_sidebar_entry(component, cx)) + // })) + // } + + fn get_component(&self, ix: usize) -> ComponentMetadata { + println!("Getting component at index {}", ix); + println!("Got component name: {}", self.components[ix].name()); + self.components[ix].clone() } - fn render_sidebar_entry( - &self, - component: &ComponentMetadata, - _cx: &Context, - ) -> impl IntoElement { - h_flex() - .w_40() - .px_1p5() - .py_0p5() - .text_sm() - .child(component.name().clone()) + fn render_sidebar_entry(&self, ix: usize, selected: bool) -> impl IntoElement { + let component = self.get_component(ix); + + Label::new(component.name().clone()) + .size(LabelSize::Small) + .color(Color::Default) } fn render_preview( &self, - component: &ComponentMetadata, + ix: usize, window: &mut Window, cx: &Context, ) -> impl IntoElement { + let component = self.get_component(ix); + let name = component.name(); let scope = component.scope(); @@ -88,6 +126,7 @@ impl ComponentPreview { .w_full() .gap_3() .py_6() + .flex_none() .child( v_flex() .gap_1() @@ -116,20 +155,20 @@ impl ComponentPreview { .into_any_element() } - fn render_previews(&self, window: &mut Window, cx: &Context) -> impl IntoElement { - v_flex() - .id("component-previews") - .size_full() - .overflow_y_scroll() - .p_4() - .gap_4() - .children( - components() - .all_previews_sorted() - .iter() - .map(|component| self.render_preview(component, window, cx)), - ) - } + // fn render_previews(&self, window: &mut Window, cx: &Context) -> impl IntoElement { + // v_flex() + // .id("component-previews") + // .size_full() + // .overflow_y_scroll() + // .p_4() + // .gap_4() + // .children( + // components() + // .all_previews_sorted() + // .iter() + // .map(|component| self.render_preview(component, window, cx)), + // ) + // } } impl Render for ComponentPreview { @@ -140,12 +179,34 @@ impl Render for ComponentPreview { .items_start() .overflow_hidden() .size_full() - .max_h_full() .track_focus(&self.focus_handle) .px_2() .bg(cx.theme().colors().editor_background) - .child(self.render_sidebar(window, cx)) - .child(self.render_previews(window, cx)) + .debug_below() + .child( + uniform_list( + cx.entity().clone(), + "component-nav", + self.components.len(), + move |this, range, _window, _cx| { + range + .map(|ix| { + div().w_full().h_8().child(this.get_component(ix).name()) + // this.render_sidebar_entry(ix, false) + }) + .collect() + }, + ) + .track_scroll(self.nav_scroll_handle.clone()) + .flex_grow(), + ) + .child( + v_flex().id("component-list").size_full().child( + list(self.component_list.clone()) + .flex_grow() + .with_sizing_behavior(gpui::ListSizingBehavior::Auto), + ), + ) } } @@ -175,13 +236,13 @@ impl Item for ComponentPreview { fn clone_on_split( &self, _workspace_id: Option, - _window: &mut Window, + window: &mut Window, cx: &mut Context, ) -> Option> where Self: Sized, { - Some(cx.new(Self::new)) + Some(cx.new(|cx| Self::new(window, cx))) } fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) { From febd70488b34b59efea6e45c20fea1a76009fa6f Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 20 Feb 2025 19:22:11 -0500 Subject: [PATCH 2/8] use lists --- crates/component/src/component.rs | 8 +- .../src/component_preview.rs | 169 +++++++----------- 2 files changed, 73 insertions(+), 104 deletions(-) diff --git a/crates/component/src/component.rs b/crates/component/src/component.rs index 35ab8751e5c196..0c3b7db3fc27f0 100644 --- a/crates/component/src/component.rs +++ b/crates/component/src/component.rs @@ -173,9 +173,9 @@ pub enum ExampleLabelSide { Left, /// Right side Right, - #[default] /// Top side Top, + #[default] /// Bottom side Bottom, } @@ -202,8 +202,8 @@ impl RenderOnce for ComponentExample { base.gap_1() .p_2() - .text_sm() - .text_color(cx.theme().colors().text) + .text_size(px(10.)) + .text_color(cx.theme().colors().text_muted) .when(self.grow, |this| this.flex_1()) .child(self.element) .child(self.variant_name) @@ -250,7 +250,7 @@ impl RenderOnce for ComponentExampleGroup { .flex() .items_center() .gap_3() - .child(div().h_px().w_4().bg(cx.theme().colors().border_variant)) + .child(div().h_px().w_4().bg(cx.theme().colors().border)) .child( div() .flex_none() diff --git a/crates/component_preview/src/component_preview.rs b/crates/component_preview/src/component_preview.rs index 24cbfed386cde1..8b51935a33a0fe 100644 --- a/crates/component_preview/src/component_preview.rs +++ b/crates/component_preview/src/component_preview.rs @@ -5,7 +5,7 @@ use component::{components, ComponentMetadata}; use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window}; use gpui::{ListState, ScrollHandle, UniformListScrollHandle}; -use ui::prelude::*; +use ui::{prelude::*, ListItem}; use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId}; @@ -29,82 +29,64 @@ pub fn init(cx: &mut App) { struct ComponentPreview { focus_handle: FocusHandle, - view_scroll_handle: ScrollHandle, + _view_scroll_handle: ScrollHandle, nav_scroll_handle: UniformListScrollHandle, components: Vec, component_list: ListState, + selected_index: usize, } impl ComponentPreview { pub fn new(_window: &mut Window, cx: &mut Context) -> Self { let components = components().all_sorted(); let initial_length = components.len(); - println!("Initial length: {}", initial_length); + + let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), { + let this = cx.entity().downgrade(); + move |ix, window: &mut Window, cx: &mut App| { + this.update(cx, |this, cx| { + this.render_preview(ix, window, cx).into_any_element() + }) + .unwrap() + } + }); Self { focus_handle: cx.focus_handle(), - view_scroll_handle: ScrollHandle::new(), + _view_scroll_handle: ScrollHandle::new(), nav_scroll_handle: UniformListScrollHandle::new(), components, - component_list: ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), { - let this = cx.entity().downgrade(); - move |ix, window: &mut Window, cx: &mut App| { - this.update(cx, |this, cx| { - // div() - // .child(this.components[ix].name().clone()) - // .into_any_element() - this.render_preview(ix, window, cx).into_any_element() - }) - .unwrap() - } - }), + component_list, + selected_index: 0, } } - // fn update_list(self, _window: &Window, _cx: &mut App) -> ListState { - // let length = components().all_sorted().len(); - - // ListState::new( - // length, - // gpui::ListAlignment::Top, - // px(500.0), - // move |ix, _, _| self.render_sidebar_entry(ix).into_any_element(), - // ) - // } - - // fn render_sidebar(&self, window: &Window, cx: &Context) -> impl IntoElement { - // let components = components().all_sorted(); - - // List::new("component-list") - // .track_scroll() - // .on_scroll( - // cx.listener(|this, event, cx| { - // this.component_list.scroll_to(event.top_offset_y, cx) - // }), - // ) - // .on_scroll_to_item( - // cx.listener(|this, index, cx| this.component_list.scroll_to_item(index, true, cx)), - // ) - // .state(&self.component_list) - // .item_size(Pixels(24.0)) - // .child(ListHeader::new("").child(Label::new("Components").size(LabelSize::Small))) - // .items(components.iter().enumerate().map(|(index, component)| { - // ListItem::new(index).child(self.render_sidebar_entry(component, cx)) - // })) - // } + fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context) { + self.component_list.scroll_to_reveal_item(ix); + self.selected_index = ix; + cx.notify(); + } fn get_component(&self, ix: usize) -> ComponentMetadata { - println!("Getting component at index {}", ix); - println!("Got component name: {}", self.components[ix].name()); self.components[ix].clone() } - fn render_sidebar_entry(&self, ix: usize, selected: bool) -> impl IntoElement { + fn render_sidebar_entry( + &self, + ix: usize, + selected: bool, + cx: &Context, + ) -> impl IntoElement { let component = self.get_component(ix); - Label::new(component.name().clone()) - .size(LabelSize::Small) - .color(Color::Default) + ListItem::new(ix) + .child(Label::new(component.name().clone()).color(Color::Default)) + .selectable(true) + .toggle_state(selected) + .inset(true) + .on_click(cx.listener(move |this, _, _, cx| { + this.scroll_to_preview(ix, cx); + })) } fn render_preview( @@ -121,58 +103,46 @@ impl ComponentPreview { let description = component.description(); v_flex() - .border_b_1() - .border_color(cx.theme().colors().border) - .w_full() - .gap_3() - .py_6() - .flex_none() + .py_2() .child( - v_flex() - .gap_1() + v_group() + .w_full() + .gap_3() + .py_6() + .px_8() + .flex_none() .child( - h_flex() + v_flex() .gap_1() - .text_2xl() - .child(div().child(name)) - .when_some(scope, |this, scope| { - this.child(div().opacity(0.5).child(format!("({})", scope))) + .child( + h_flex() + .gap_1() + .text_2xl() + .child(div().child(name)) + .when_some(scope, |this, scope| { + this.child(div().opacity(0.5).child(format!("({})", scope))) + }), + ) + .when_some(description, |this, description| { + this.child( + div() + .text_ui_sm(cx) + .text_color(cx.theme().colors().text_muted) + .max_w(px(600.0)) + .child(description), + ) }), ) - .when_some(description, |this, description| { - this.child( - div() - .text_ui_sm(cx) - .text_color(cx.theme().colors().text_muted) - .max_w(px(600.0)) - .child(description), - ) + .when_some(component.preview(), |this, preview| { + this.child(preview(window, cx)) }), ) - .when_some(component.preview(), |this, preview| { - this.child(preview(window, cx)) - }) .into_any_element() } - - // fn render_previews(&self, window: &mut Window, cx: &Context) -> impl IntoElement { - // v_flex() - // .id("component-previews") - // .size_full() - // .overflow_y_scroll() - // .p_4() - // .gap_4() - // .children( - // components() - // .all_previews_sorted() - // .iter() - // .map(|component| self.render_preview(component, window, cx)), - // ) - // } } impl Render for ComponentPreview { - fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement { + fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement { h_flex() .id("component-preview") .key_context("ComponentPreview") @@ -182,26 +152,25 @@ impl Render for ComponentPreview { .track_focus(&self.focus_handle) .px_2() .bg(cx.theme().colors().editor_background) - .debug_below() .child( uniform_list( cx.entity().clone(), "component-nav", self.components.len(), - move |this, range, _window, _cx| { + move |this, range, _window, cx| { range - .map(|ix| { - div().w_full().h_8().child(this.get_component(ix).name()) - // this.render_sidebar_entry(ix, false) - }) + .map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx)) .collect() }, ) .track_scroll(self.nav_scroll_handle.clone()) + .pt_4() + .w(px(240.)) + .h_full() .flex_grow(), ) .child( - v_flex().id("component-list").size_full().child( + v_flex().id("component-list").px_8().size_full().child( list(self.component_list.clone()) .flex_grow() .with_sizing_behavior(gpui::ListSizingBehavior::Auto), From 4cbb6d35b3d4fa166481058388cdc181109d2215 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 20 Feb 2025 20:52:21 -0500 Subject: [PATCH 3/8] Remove outdated docs --- crates/ui/docs/building-ui.md | 49 ----------- crates/ui/docs/hello-world.md | 160 ---------------------------------- 2 files changed, 209 deletions(-) delete mode 100644 crates/ui/docs/building-ui.md delete mode 100644 crates/ui/docs/hello-world.md diff --git a/crates/ui/docs/building-ui.md b/crates/ui/docs/building-ui.md deleted file mode 100644 index e0160e336ed36d..00000000000000 --- a/crates/ui/docs/building-ui.md +++ /dev/null @@ -1,49 +0,0 @@ -# Building UI with GPUI - -## Common patterns - -### Method ordering - -- id -- Flex properties -- Position properties -- Size properties -- Style properties -- Handlers -- State properties - -### Using the Label Component to Create UI Text - -The `Label` component helps in displaying text on user interfaces. It creates an interface where specific parameters such as label color, line height style, and strikethrough can be set. - -Firstly, to create a `Label` instance, use the `Label::new()` function. This function takes a string that will be displayed as text in the interface. - -```rust -Label::new("Hello, world!"); -``` - -Now let's dive a bit deeper into how to customize `Label` instances: - -- **Setting Color:** To set the color of the label using various predefined color options such as `Default`, `Muted`, `Created`, `Modified`, `Deleted`, etc, the `color()` function is called on the `Label` instance: - - ```rust - Label::new("Hello, world!").color(LabelColor::Default); - ``` - -- **Setting Line Height Style:** To set the line height style, the `line_height_style()` function is utilized: - - ```rust - Label::new("Hello, world!").line_height_style(LineHeightStyle::TextLabel); - ``` - -- **Adding a Strikethrough:** To add a strikethrough in a `Label`, the `set_strikethrough()` function is used: - - ```rust - Label::new("Hello, world!").set_strikethrough(true); - ``` - -That's it! Now you can use the `Label` component to create and customize text on your application's interface. - -## Building a new component - -TODO diff --git a/crates/ui/docs/hello-world.md b/crates/ui/docs/hello-world.md deleted file mode 100644 index 8ff2fe4db7f1da..00000000000000 --- a/crates/ui/docs/hello-world.md +++ /dev/null @@ -1,160 +0,0 @@ -# Hello World - -Let's work through the prototypical "Build a todo app" example to showcase how we might build a simple component from scratch. - -## Setup - -We'll create a headline, a list of todo items, and a form to add new items. - -~~~rust -struct TodoList { - headline: SharedString, - items: Vec, - submit_form: ClickHandler -} - -struct TodoItem { - text: SharedString, - completed: bool, - delete: ClickHandler -} - -impl TodoList { - pub fn new( - // Here we impl Into - headline: impl Into, - items: Vec, - submit_form: ClickHandler - ) -> Self { - Self { - // and here we call .into() so we can simply pass a string - // when creating the headline. This pattern is used throughout - // outr components - headline: headline.into(), - items: Vec::new(), - submit_form, - } - } -} -~~~ - -All of this is relatively straightforward. - -We use [gpui::SharedString] in components instead of [std::string::String]. This allows us to efficiently handle shared string data across multiple components and threads without the performance overhead of copying strings. - -When we want to pass an action we pass a `ClickHandler`. Whenever we want to add an action, the struct it belongs to needs to be generic over the view type `V`. - -~~~rust -use gpui::hsla - -impl TodoList { - // ... - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Element { - div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0)) - } -} -~~~ - -Every component needs a render method, and it should return `impl Element`. This basic component will render a 16x16px yellow square on the screen. - -A couple of questions might come to mind: - -**Why is `size_4()` 16px, not 4px?** - -gpui's style system is based on conventions created by [Tailwind CSS](https://tailwindcss.com/). Here is an example of the list of sizes for `width`: [Width - TailwindCSS Docs](https://tailwindcss.com/docs/width). - -I'll quote from the Tailwind [Core Concepts](https://tailwindcss.com/docs/utility-first) docs here: - -> Now I know what you’re thinking, “this is an atrocity, what a horrible mess!” -> and you’re right, it’s kind of ugly. In fact it’s just about impossible to -> think this is a good idea the first time you see it — -> you have to actually try it. - -As you start using the Tailwind-style conventions you will be surprised how quick it makes it to build out UIs. - -**Why `50.0/360.0` in `hsla()`?** - -gpui [gpui::Hsla] use `0.0-1.0` for all its values, but it is common for tools to use `0-360` for hue. - -This may change in the future, but this is a little trick that let's you use familiar looking values. - -## Building out the container - -Let's grab our [theme::colors::ThemeColors] from the theme and start building out a basic container. - -We can access the current theme's colors like this: - -~~~rust -impl TodoList { - // ... - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Element { - let color = cx.theme().colors() - - div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0) - } -} -~~~ - -Now we have access to the complete set of colors defined in the theme. - -~~~rust -use gpui::hsla - -impl TodoList { - // ... - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Element { - let color = cx.theme().colors() - - div().size_4().bg(color.surface) - } -} -~~~ - -Let's finish up some basic styles for the container then move on to adding the other elements. - -~~~rust -use gpui::hsla - -impl TodoList { - // ... - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Element { - let color = cx.theme().colors() - - div() - // Flex properties - .flex() - .flex_col() // Stack elements vertically - .gap_2() // Add 8px of space between elements - // Size properties - .w_96() // Set width to 384px - .p_4() // Add 16px of padding on all sides - // Color properties - .bg(color.surface) // Set background color - .text_color(color.text) // Set text color - // Border properties - .rounded_md() // Add 4px of border radius - .border_1() // Add a 1px border - .border_color(color.border) - .child( - "Hello, world!" - ) - } -} -~~~ - -### Headline - -TODO - -### List of todo items - -TODO - -### Input - -TODO - - -### End result - -TODO From 1ccbdbb1242dd13e375efa2f41cb2cbbc5337a72 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 20 Feb 2025 21:19:11 -0500 Subject: [PATCH 4/8] Update avatar & preview --- crates/ui/src/components/avatar.rs | 307 +++++++++++++++++- crates/ui/src/components/avatar/avatar.rs | 156 --------- .../avatar/avatar_audio_status_indicator.rs | 72 ---- .../avatar/avatar_availability_indicator.rs | 49 --- crates/ui/src/components/stories/avatar.rs | 6 +- 5 files changed, 304 insertions(+), 286 deletions(-) delete mode 100644 crates/ui/src/components/avatar/avatar.rs delete mode 100644 crates/ui/src/components/avatar/avatar_audio_status_indicator.rs delete mode 100644 crates/ui/src/components/avatar/avatar_availability_indicator.rs diff --git a/crates/ui/src/components/avatar.rs b/crates/ui/src/components/avatar.rs index 6c2d88916e7fe4..8105ce4c9663b5 100644 --- a/crates/ui/src/components/avatar.rs +++ b/crates/ui/src/components/avatar.rs @@ -1,7 +1,302 @@ -mod avatar; -mod avatar_audio_status_indicator; -mod avatar_availability_indicator; +use crate::{prelude::*, Indicator}; -pub use avatar::*; -pub use avatar_audio_status_indicator::*; -pub use avatar_availability_indicator::*; +use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled}; + +/// An element that renders a user avatar with customizable appearance options. +/// +/// # Examples +/// +/// ``` +/// use ui::{Avatar, AvatarShape}; +/// +/// Avatar::new("path/to/image.png") +/// .shape(AvatarShape::Circle) +/// .grayscale(true) +/// .border_color(gpui::red()); +/// ``` +#[derive(IntoElement, IntoComponent)] +pub struct Avatar { + image: Img, + size: Option, + border_color: Option, + indicator: Option, +} + +impl Avatar { + /// Creates a new avatar element with the specified image source. + pub fn new(src: impl Into) -> Self { + Avatar { + image: img(src), + size: None, + border_color: None, + indicator: None, + } + } + + /// Applies a grayscale filter to the avatar image. + /// + /// # Examples + /// + /// ``` + /// use ui::{Avatar, AvatarShape}; + /// + /// let avatar = Avatar::new("path/to/image.png").grayscale(true); + /// ``` + pub fn grayscale(mut self, grayscale: bool) -> Self { + self.image = self.image.grayscale(grayscale); + self + } + + /// Sets the border color of the avatar. + /// + /// This might be used to match the border to the background color of + /// the parent element to create the illusion of cropping another + /// shape underneath (for example in face piles.) + pub fn border_color(mut self, color: impl Into) -> Self { + self.border_color = Some(color.into()); + self + } + + /// Size overrides the avatar size. By default they are 1rem. + pub fn size>(mut self, size: impl Into>) -> Self { + self.size = size.into().map(Into::into); + self + } + + /// Sets the current indicator to be displayed on the avatar, if any. + pub fn indicator(mut self, indicator: impl Into>) -> Self { + self.indicator = indicator.into().map(IntoElement::into_any_element); + self + } +} + +impl RenderOnce for Avatar { + fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { + let border_width = if self.border_color.is_some() { + px(2.) + } else { + px(0.) + }; + + let image_size = self.size.unwrap_or_else(|| rems(1.).into()); + let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.; + + div() + .size(container_size) + .rounded_full() + .when_some(self.border_color, |this, color| { + this.border(border_width).border_color(color) + }) + .child( + self.image + .size(image_size) + .rounded_full() + .bg(cx.theme().colors().ghost_element_background), + ) + .children(self.indicator.map(|indicator| div().child(indicator))) + } +} + +use gpui::AnyView; + +/// The audio status of an player, for use in representing +/// their status visually on their avatar. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub enum AudioStatus { + /// The player's microphone is muted. + Muted, + /// The player's microphone is muted, and collaboration audio is disabled. + Deafened, +} + +/// An indicator that shows the audio status of a player. +#[derive(IntoElement)] +pub struct AvatarAudioStatusIndicator { + audio_status: AudioStatus, + tooltip: Option AnyView>>, +} + +impl AvatarAudioStatusIndicator { + /// Creates a new `AvatarAudioStatusIndicator` + pub fn new(audio_status: AudioStatus) -> Self { + Self { + audio_status, + tooltip: None, + } + } + + /// Sets the tooltip for the indicator. + pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self { + self.tooltip = Some(Box::new(tooltip)); + self + } +} + +impl RenderOnce for AvatarAudioStatusIndicator { + fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { + let icon_size = IconSize::Indicator; + + let width_in_px = icon_size.rems() * window.rem_size(); + let padding_x = px(4.); + + div() + .absolute() + .bottom(rems_from_px(-3.)) + .right(rems_from_px(-6.)) + .w(width_in_px + padding_x) + .h(icon_size.rems()) + .child( + h_flex() + .id("muted-indicator") + .justify_center() + .px(padding_x) + .py(px(2.)) + .bg(cx.theme().status().error_background) + .rounded_md() + .child( + Icon::new(match self.audio_status { + AudioStatus::Muted => IconName::MicMute, + AudioStatus::Deafened => IconName::AudioOff, + }) + .size(icon_size) + .color(Color::Error), + ) + .when_some(self.tooltip, |this, tooltip| { + this.tooltip(move |window, cx| tooltip(window, cx)) + }), + ) + } +} + +/// Represents the availability status of a collaborator. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub enum CollaboratorAvailability { + Free, + Busy, +} + +/// Represents the availability and presence status of a collaborator. +#[derive(IntoElement)] +pub struct AvatarAvailabilityIndicator { + availability: CollaboratorAvailability, + avatar_size: Option, +} + +impl AvatarAvailabilityIndicator { + /// Creates a new indicator + pub fn new(availability: CollaboratorAvailability) -> Self { + Self { + availability, + avatar_size: None, + } + } + + /// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on. + pub fn avatar_size(mut self, size: impl Into>) -> Self { + self.avatar_size = size.into(); + self + } +} + +impl RenderOnce for AvatarAvailabilityIndicator { + fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { + let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size()); + + // HACK: non-integer sizes result in oval indicators. + let indicator_size = (avatar_size * 0.4).round(); + + div() + .absolute() + .bottom_0() + .right_0() + .size(indicator_size) + .rounded(indicator_size) + .bg(match self.availability { + CollaboratorAvailability::Free => cx.theme().status().created, + CollaboratorAvailability::Busy => cx.theme().status().deleted, + }) + } +} + +// View this component preview using `workspace: open component-preview` +impl ComponentPreview for Avatar { + fn preview(_window: &mut Window, cx: &App) -> AnyElement { + let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4"; + + v_flex() + .gap_6() + .children(vec![ + example_group_with_title( + "Sizes", + vec![ + single_example("Default", Avatar::new(example_avatar).into_any_element()), + single_example( + "Small", + Avatar::new(example_avatar).size(px(24.)).into_any_element(), + ), + single_example( + "Large", + Avatar::new(example_avatar).size(px(48.)).into_any_element(), + ), + ], + ), + example_group_with_title( + "Styles", + vec![ + single_example("Default", Avatar::new(example_avatar).into_any_element()), + single_example( + "Grayscale", + Avatar::new(example_avatar) + .grayscale(true) + .into_any_element(), + ), + single_example( + "With Border", + Avatar::new(example_avatar) + .border_color(cx.theme().colors().border) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Audio Status", + vec![ + single_example( + "Muted", + Avatar::new(example_avatar) + .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)) + .into_any_element(), + ), + single_example( + "Deafened", + Avatar::new(example_avatar) + .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Availability", + vec![ + single_example( + "Free", + Avatar::new(example_avatar) + .indicator(AvatarAvailabilityIndicator::new( + CollaboratorAvailability::Free, + )) + .into_any_element(), + ), + single_example( + "Busy", + Avatar::new(example_avatar) + .indicator(AvatarAvailabilityIndicator::new( + CollaboratorAvailability::Busy, + )) + .into_any_element(), + ), + ], + ), + ]) + .into_any_element() + } +} diff --git a/crates/ui/src/components/avatar/avatar.rs b/crates/ui/src/components/avatar/avatar.rs deleted file mode 100644 index 65622f8c3f2ffd..00000000000000 --- a/crates/ui/src/components/avatar/avatar.rs +++ /dev/null @@ -1,156 +0,0 @@ -use crate::{prelude::*, Indicator}; - -use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled}; - -/// An element that renders a user avatar with customizable appearance options. -/// -/// # Examples -/// -/// ``` -/// use ui::{Avatar, AvatarShape}; -/// -/// Avatar::new("path/to/image.png") -/// .shape(AvatarShape::Circle) -/// .grayscale(true) -/// .border_color(gpui::red()); -/// ``` -#[derive(IntoElement, IntoComponent)] -pub struct Avatar { - image: Img, - size: Option, - border_color: Option, - indicator: Option, -} - -impl Avatar { - /// Creates a new avatar element with the specified image source. - pub fn new(src: impl Into) -> Self { - Avatar { - image: img(src), - size: None, - border_color: None, - indicator: None, - } - } - - /// Applies a grayscale filter to the avatar image. - /// - /// # Examples - /// - /// ``` - /// use ui::{Avatar, AvatarShape}; - /// - /// let avatar = Avatar::new("path/to/image.png").grayscale(true); - /// ``` - pub fn grayscale(mut self, grayscale: bool) -> Self { - self.image = self.image.grayscale(grayscale); - self - } - - /// Sets the border color of the avatar. - /// - /// This might be used to match the border to the background color of - /// the parent element to create the illusion of cropping another - /// shape underneath (for example in face piles.) - pub fn border_color(mut self, color: impl Into) -> Self { - self.border_color = Some(color.into()); - self - } - - /// Size overrides the avatar size. By default they are 1rem. - pub fn size>(mut self, size: impl Into>) -> Self { - self.size = size.into().map(Into::into); - self - } - - /// Sets the current indicator to be displayed on the avatar, if any. - pub fn indicator(mut self, indicator: impl Into>) -> Self { - self.indicator = indicator.into().map(IntoElement::into_any_element); - self - } -} - -impl RenderOnce for Avatar { - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - let border_width = if self.border_color.is_some() { - px(2.) - } else { - px(0.) - }; - - let image_size = self.size.unwrap_or_else(|| rems(1.).into()); - let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.; - - div() - .size(container_size) - .rounded_full() - .when_some(self.border_color, |this, color| { - this.border(border_width).border_color(color) - }) - .child( - self.image - .size(image_size) - .rounded_full() - .bg(cx.theme().colors().ghost_element_background), - ) - .children(self.indicator.map(|indicator| div().child(indicator))) - } -} - -// View this component preview using `workspace: open component-preview` -impl ComponentPreview for Avatar { - fn preview(_window: &mut Window, _cx: &App) -> AnyElement { - let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4"; - - v_flex() - .gap_6() - .children(vec![ - example_group_with_title( - "Sizes", - vec![ - single_example( - "Default", - Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4") - .into_any_element(), - ), - single_example( - "Small", - Avatar::new(example_avatar).size(px(24.)).into_any_element(), - ), - single_example( - "Large", - Avatar::new(example_avatar).size(px(48.)).into_any_element(), - ), - ], - ), - example_group_with_title( - "Styles", - vec![ - single_example("Default", Avatar::new(example_avatar).into_any_element()), - single_example( - "Grayscale", - Avatar::new(example_avatar) - .grayscale(true) - .into_any_element(), - ), - single_example( - "With Border", - Avatar::new(example_avatar) - .border_color(gpui::red()) - .into_any_element(), - ), - ], - ), - example_group_with_title( - "With Indicator", - vec![single_example( - "Dot", - Avatar::new(example_avatar) - .indicator(Indicator::dot().color(Color::Success)) - .into_any_element(), - )], - ), - ]) - .into_any_element() - } -} diff --git a/crates/ui/src/components/avatar/avatar_audio_status_indicator.rs b/crates/ui/src/components/avatar/avatar_audio_status_indicator.rs deleted file mode 100644 index 23e093880b232b..00000000000000 --- a/crates/ui/src/components/avatar/avatar_audio_status_indicator.rs +++ /dev/null @@ -1,72 +0,0 @@ -use gpui::AnyView; - -use crate::prelude::*; - -/// The audio status of an player, for use in representing -/// their status visually on their avatar. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum AudioStatus { - /// The player's microphone is muted. - Muted, - /// The player's microphone is muted, and collaboration audio is disabled. - Deafened, -} - -/// An indicator that shows the audio status of a player. -#[derive(IntoElement)] -pub struct AvatarAudioStatusIndicator { - audio_status: AudioStatus, - tooltip: Option AnyView>>, -} - -impl AvatarAudioStatusIndicator { - /// Creates a new `AvatarAudioStatusIndicator` - pub fn new(audio_status: AudioStatus) -> Self { - Self { - audio_status, - tooltip: None, - } - } - - /// Sets the tooltip for the indicator. - pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self { - self.tooltip = Some(Box::new(tooltip)); - self - } -} - -impl RenderOnce for AvatarAudioStatusIndicator { - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - let icon_size = IconSize::Indicator; - - let width_in_px = icon_size.rems() * window.rem_size(); - let padding_x = px(4.); - - div() - .absolute() - .bottom(rems_from_px(-3.)) - .right(rems_from_px(-6.)) - .w(width_in_px + padding_x) - .h(icon_size.rems()) - .child( - h_flex() - .id("muted-indicator") - .justify_center() - .px(padding_x) - .py(px(2.)) - .bg(cx.theme().status().error_background) - .rounded_md() - .child( - Icon::new(match self.audio_status { - AudioStatus::Muted => IconName::MicMute, - AudioStatus::Deafened => IconName::AudioOff, - }) - .size(icon_size) - .color(Color::Error), - ) - .when_some(self.tooltip, |this, tooltip| { - this.tooltip(move |window, cx| tooltip(window, cx)) - }), - ) - } -} diff --git a/crates/ui/src/components/avatar/avatar_availability_indicator.rs b/crates/ui/src/components/avatar/avatar_availability_indicator.rs deleted file mode 100644 index 126e12c91ab78d..00000000000000 --- a/crates/ui/src/components/avatar/avatar_availability_indicator.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![allow(missing_docs)] -use crate::prelude::*; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum Availability { - Free, - Busy, -} - -#[derive(IntoElement)] -pub struct AvatarAvailabilityIndicator { - availability: Availability, - avatar_size: Option, -} - -impl AvatarAvailabilityIndicator { - pub fn new(availability: Availability) -> Self { - Self { - availability, - avatar_size: None, - } - } - - /// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on. - pub fn avatar_size(mut self, size: impl Into>) -> Self { - self.avatar_size = size.into(); - self - } -} - -impl RenderOnce for AvatarAvailabilityIndicator { - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size()); - - // HACK: non-integer sizes result in oval indicators. - let indicator_size = (avatar_size * 0.4).round(); - - div() - .absolute() - .bottom_0() - .right_0() - .size(indicator_size) - .rounded(indicator_size) - .bg(match self.availability { - Availability::Free => cx.theme().status().created, - Availability::Busy => cx.theme().status().deleted, - }) - } -} diff --git a/crates/ui/src/components/stories/avatar.rs b/crates/ui/src/components/stories/avatar.rs index 8ad2aca8aff170..6b268d8bd6bbe3 100644 --- a/crates/ui/src/components/stories/avatar.rs +++ b/crates/ui/src/components/stories/avatar.rs @@ -1,7 +1,7 @@ use gpui::Render; use story::{Story, StoryItem, StorySection}; -use crate::{prelude::*, AudioStatus, Availability, AvatarAvailabilityIndicator}; +use crate::{prelude::*, AudioStatus, CollaboratorAvailability, AvatarAvailabilityIndicator}; use crate::{Avatar, AvatarAudioStatusIndicator}; pub struct AvatarStory; @@ -26,12 +26,12 @@ impl Render for AvatarStory { .child(StoryItem::new( "With free availability indicator", Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .indicator(AvatarAvailabilityIndicator::new(Availability::Free)), + .indicator(AvatarAvailabilityIndicator::new(CollaboratorAvailability::Free)), )) .child(StoryItem::new( "With busy availability indicator", Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .indicator(AvatarAvailabilityIndicator::new(Availability::Busy)), + .indicator(AvatarAvailabilityIndicator::new(CollaboratorAvailability::Busy)), )), ) .child( From 58dd0f09d5e0da699146f514bb42bc795b291504 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 20 Feb 2025 21:20:47 -0500 Subject: [PATCH 5/8] update ui/components --- crates/collab_ui/src/collab_panel.rs | 4 +-- crates/component/src/component.rs | 7 +++-- .../src/component_preview.rs | 29 ++++++++++++------- crates/ui/src/ui.rs | 2 -- crates/workspace/src/theme_preview.rs | 2 +- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 62a37166edb9e0..0e9da50f3192a8 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2458,8 +2458,8 @@ impl CollabPanel { Avatar::new(contact.user.avatar_uri.clone()) .indicator::(if online { Some(AvatarAvailabilityIndicator::new(match busy { - true => ui::Availability::Busy, - false => ui::Availability::Free, + true => ui::CollaboratorAvailability::Busy, + false => ui::CollaboratorAvailability::Free, })) } else { None diff --git a/crates/component/src/component.rs b/crates/component/src/component.rs index 0c3b7db3fc27f0..00c1be661ad8e1 100644 --- a/crates/component/src/component.rs +++ b/crates/component/src/component.rs @@ -200,7 +200,7 @@ impl RenderOnce for ComponentExample { ExampleLabelSide::Top => base.flex_col_reverse(), }; - base.gap_1() + base.gap_2() .p_2() .text_size(px(10.)) .text_color(cx.theme().colors().text_muted) @@ -245,11 +245,12 @@ impl RenderOnce for ComponentExampleGroup { .text_color(cx.theme().colors().text_muted) .when(self.grow, |this| this.w_full().flex_1()) .when_some(self.title, |this, title| { - this.gap_4().pb_5().child( + this.gap_4().child( div() .flex() .items_center() .gap_3() + .pb_1() .child(div().h_px().w_4().bg(cx.theme().colors().border)) .child( div() @@ -271,7 +272,7 @@ impl RenderOnce for ComponentExampleGroup { .flex() .items_start() .w_full() - .gap_8() + .gap_6() .children(self.examples) .into_any_element(), ) diff --git a/crates/component_preview/src/component_preview.rs b/crates/component_preview/src/component_preview.rs index 8b51935a33a0fe..e462e9021bb06d 100644 --- a/crates/component_preview/src/component_preview.rs +++ b/crates/component_preview/src/component_preview.rs @@ -105,11 +105,15 @@ impl ComponentPreview { v_flex() .py_2() .child( - v_group() + v_flex() + .bg(cx.theme().colors().text.opacity(0.05)) + .border_1() + .border_color(cx.theme().colors().border) + .rounded_md() .w_full() - .gap_3() - .py_6() - .px_8() + .gap_4() + .py_4() + .px_6() .flex_none() .child( v_flex() @@ -117,7 +121,7 @@ impl ComponentPreview { .child( h_flex() .gap_1() - .text_2xl() + .text_xl() .child(div().child(name)) .when_some(scope, |this, scope| { this.child(div().opacity(0.5).child(format!("({})", scope))) @@ -170,11 +174,16 @@ impl Render for ComponentPreview { .flex_grow(), ) .child( - v_flex().id("component-list").px_8().size_full().child( - list(self.component_list.clone()) - .flex_grow() - .with_sizing_behavior(gpui::ListSizingBehavior::Auto), - ), + v_flex() + .id("component-list") + .px_8() + .pt_4() + .size_full() + .child( + list(self.component_list.clone()) + .flex_grow() + .with_sizing_behavior(gpui::ListSizingBehavior::Auto), + ), ) } } diff --git a/crates/ui/src/ui.rs b/crates/ui/src/ui.rs index 8c1064211541f4..148ebda6eafa6f 100644 --- a/crates/ui/src/ui.rs +++ b/crates/ui/src/ui.rs @@ -1,5 +1,3 @@ -#![deny(missing_docs)] - //! # UI – Zed UI Primitives & Components //! //! This crate provides a set of UI primitives and components that are used to build all of the elements in Zed's UI. diff --git a/crates/workspace/src/theme_preview.rs b/crates/workspace/src/theme_preview.rs index da2d6b3ff19634..49b70cb307d58d 100644 --- a/crates/workspace/src/theme_preview.rs +++ b/crates/workspace/src/theme_preview.rs @@ -4,7 +4,7 @@ use strum::IntoEnumIterator; use theme::all_theme_colors; use ui::{ element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus, - Availability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, + CollaboratorAvailability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, Checkbox, CheckboxWithLabel, ContentGroup, DecoratedIcon, ElevationIndex, Facepile, IconDecoration, Indicator, KeybindingHint, Switch, Table, TintColor, Tooltip, }; From b73440873f2ef16fde2ca32fac8e40a37d38b568 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 20 Feb 2025 22:13:45 -0500 Subject: [PATCH 6/8] Continue on component preview --- .../src/component_preview.rs | 1 - crates/ui/src/components/avatar.rs | 2 +- crates/ui/src/components/button/button.rs | 26 ++- .../ui/src/components/button/button_icon.rs | 1 - .../ui/src/components/button/button_like.rs | 1 - .../ui/src/components/button/icon_button.rs | 163 +++++++++++++++++- .../ui/src/components/button/toggle_button.rs | 131 +++++++++++++- crates/ui/src/components/context_menu.rs | 1 - crates/ui/src/components/disclosure.rs | 1 - crates/ui/src/components/divider.rs | 1 - crates/ui/src/components/dropdown_menu.rs | 1 - crates/ui/src/components/facepile.rs | 113 ++++++------ crates/ui/src/components/icon.rs | 2 - crates/ui/src/components/image.rs | 1 - crates/ui/src/components/indent_guides.rs | 1 - crates/ui/src/components/indicator.rs | 1 - crates/ui/src/components/keybinding.rs | 1 - .../src/components/label/highlighted_label.rs | 2 - crates/ui/src/components/list/list.rs | 2 - crates/ui/src/components/list/list_header.rs | 2 - crates/ui/src/components/list/list_item.rs | 2 - .../ui/src/components/list/list_separator.rs | 2 - .../ui/src/components/list/list_sub_header.rs | 2 - crates/ui/src/components/modal.rs | 2 - crates/ui/src/components/numeric_stepper.rs | 2 - crates/ui/src/components/popover.rs | 2 - crates/ui/src/components/popover_menu.rs | 2 - crates/ui/src/components/radio.rs | 2 - crates/ui/src/components/right_click_menu.rs | 2 - crates/ui/src/components/scrollbar.rs | 1 - .../ui/src/components/settings_container.rs | 2 - crates/ui/src/components/settings_group.rs | 2 - crates/ui/src/components/stack.rs | 2 - crates/ui/src/components/stories.rs | 1 - crates/ui/src/components/tab.rs | 1 - crates/ui/src/components/tab_bar.rs | 1 - crates/ui/src/components/tooltip.rs | 2 - crates/ui/src/styles/typography.rs | 65 ++++--- crates/ui/src/utils/format_distance.rs | 1 - crates/ui/src/utils/search_input.rs | 2 - 40 files changed, 387 insertions(+), 165 deletions(-) diff --git a/crates/component_preview/src/component_preview.rs b/crates/component_preview/src/component_preview.rs index e462e9021bb06d..6a3dcfc406aa17 100644 --- a/crates/component_preview/src/component_preview.rs +++ b/crates/component_preview/src/component_preview.rs @@ -106,7 +106,6 @@ impl ComponentPreview { .py_2() .child( v_flex() - .bg(cx.theme().colors().text.opacity(0.05)) .border_1() .border_color(cx.theme().colors().border) .rounded_md() diff --git a/crates/ui/src/components/avatar.rs b/crates/ui/src/components/avatar.rs index 8105ce4c9663b5..b0670d04c056de 100644 --- a/crates/ui/src/components/avatar.rs +++ b/crates/ui/src/components/avatar.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, Indicator}; +use crate::prelude::*; use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled}; diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index 58a3d5ae9175ee..e7112aa8aec509 100644 --- a/crates/ui/src/components/button/button.rs +++ b/crates/ui/src/components/button/button.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use component::{example_group_with_title, single_example, ComponentPreview}; use gpui::{AnyElement, AnyView, DefiniteLength}; use ui_macros::IntoComponent; @@ -81,6 +80,7 @@ use super::button_icon::ButtonIcon; /// ``` /// #[derive(IntoElement, IntoComponent)] +#[component(scope = "input")] pub struct Button { base: ButtonLike, label: SharedString, @@ -463,7 +463,7 @@ impl ComponentPreview for Button { .gap_6() .children(vec![ example_group_with_title( - "Styles", + "Button Styles", vec![ single_example( "Default", @@ -481,6 +481,12 @@ impl ComponentPreview for Button { .style(ButtonStyle::Subtle) .into_any_element(), ), + single_example( + "Tinted", + Button::new("tinted_accent_style", "Accent") + .style(ButtonStyle::Tinted(TintColor::Accent)) + .into_any_element(), + ), single_example( "Transparent", Button::new("transparent", "Transparent") @@ -490,7 +496,7 @@ impl ComponentPreview for Button { ], ), example_group_with_title( - "Tinted", + "Tint Styles", vec![ single_example( "Accent", @@ -519,7 +525,7 @@ impl ComponentPreview for Button { ], ), example_group_with_title( - "States", + "Special States", vec![ single_example( "Default", @@ -540,7 +546,7 @@ impl ComponentPreview for Button { ], ), example_group_with_title( - "With Icons", + "Buttons with Icons", vec![ single_example( "Icon Start", @@ -563,16 +569,6 @@ impl ComponentPreview for Button { .icon_color(Color::Accent) .into_any_element(), ), - single_example( - "Tinted Icons", - Button::new("tinted_icons", "Error") - .style(ButtonStyle::Tinted(TintColor::Error)) - .color(Color::Error) - .icon_color(Color::Error) - .icon(IconName::Trash) - .icon_position(IconPosition::Start) - .into_any_element(), - ), ], ), ]) diff --git a/crates/ui/src/components/button/button_icon.rs b/crates/ui/src/components/button/button_icon.rs index adacd12f27039f..337f10700a1098 100644 --- a/crates/ui/src/components/button/button_icon.rs +++ b/crates/ui/src/components/button/button_icon.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use crate::{prelude::*, Icon, IconName, IconSize, IconWithIndicator, Indicator}; use gpui::Hsla; diff --git a/crates/ui/src/components/button/button_like.rs b/crates/ui/src/components/button/button_like.rs index 96d093c249ab4a..5503e6f865afa8 100644 --- a/crates/ui/src/components/button/button_like.rs +++ b/crates/ui/src/components/button/button_like.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use gpui::{relative, CursorStyle, DefiniteLength, MouseButton}; use gpui::{transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems}; use smallvec::SmallVec; diff --git a/crates/ui/src/components/button/icon_button.rs b/crates/ui/src/components/button/icon_button.rs index 204ea8e564c888..97084116057e5e 100644 --- a/crates/ui/src/components/button/icon_button.rs +++ b/crates/ui/src/components/button/icon_button.rs @@ -1,8 +1,7 @@ -#![allow(missing_docs)] use gpui::{AnyView, DefiniteLength, Hsla}; use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle}; -use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton}; +use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton, TintColor}; use crate::{IconName, IconSize}; use super::button_icon::ButtonIcon; @@ -14,7 +13,8 @@ pub enum IconButtonShape { Wide, } -#[derive(IntoElement)] +#[derive(IntoElement, IntoComponent)] +#[component(scope = "input")] pub struct IconButton { base: ButtonLike, shape: IconButtonShape, @@ -200,3 +200,160 @@ impl RenderOnce for IconButton { ) } } + +impl ComponentPreview for IconButton { + fn preview(_window: &mut Window, _cx: &App) -> AnyElement { + v_flex() + .gap_6() + .children(vec![ + example_group_with_title( + "Icon Button Styles", + vec![ + single_example( + "Default", + IconButton::new("default", IconName::Check) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "Filled", + IconButton::new("filled", IconName::Check) + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .into_any_element(), + ), + single_example( + "Subtle", + IconButton::new("subtle", IconName::Check) + .layer(ElevationIndex::Background) + .style(ButtonStyle::Subtle) + .into_any_element(), + ), + single_example( + "Tinted", + IconButton::new("tinted", IconName::Check) + .layer(ElevationIndex::Background) + .style(ButtonStyle::Tinted(TintColor::Accent)) + .into_any_element(), + ), + single_example( + "Transparent", + IconButton::new("transparent", IconName::Check) + .layer(ElevationIndex::Background) + .style(ButtonStyle::Transparent) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Icon Button Shapes", + vec![ + single_example( + "Square", + IconButton::new("square", IconName::Check) + .shape(IconButtonShape::Square) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "Wide", + IconButton::new("wide", IconName::Check) + .shape(IconButtonShape::Wide) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Icon Button Sizes", + vec![ + single_example( + "Small", + IconButton::new("small", IconName::Check) + .icon_size(IconSize::XSmall) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "Small", + IconButton::new("small", IconName::Check) + .icon_size(IconSize::Small) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "Medium", + IconButton::new("medium", IconName::Check) + .icon_size(IconSize::Medium) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "XLarge", + IconButton::new("xlarge", IconName::Check) + .icon_size(IconSize::XLarge) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Special States", + vec![ + single_example( + "Disabled", + IconButton::new("disabled", IconName::Check) + .disabled(true) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "Selected", + IconButton::new("selected", IconName::Check) + .toggle_state(true) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "With Indicator", + IconButton::new("indicator", IconName::Check) + .indicator(Indicator::dot().color(Color::Success)) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Custom Colors", + vec![ + single_example( + "Custom Icon Color", + IconButton::new("custom_color", IconName::Check) + .icon_color(Color::Accent) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + single_example( + "With Alpha", + IconButton::new("alpha", IconName::Check) + .alpha(0.5) + .style(ButtonStyle::Filled) + .layer(ElevationIndex::Background) + .into_any_element(), + ), + ], + ), + ]) + .into_any_element() + } +} diff --git a/crates/ui/src/components/button/toggle_button.rs b/crates/ui/src/components/button/toggle_button.rs index 9681e8031bd70b..1fb8a2c01633ff 100644 --- a/crates/ui/src/components/button/toggle_button.rs +++ b/crates/ui/src/components/button/toggle_button.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use gpui::{AnyView, ClickEvent}; use crate::{prelude::*, ButtonLike, ButtonLikeRounding, ElevationIndex}; @@ -16,7 +15,8 @@ pub enum ToggleButtonPosition { Last, } -#[derive(IntoElement)] +#[derive(IntoElement, IntoComponent)] +#[component(scope = "input")] pub struct ToggleButton { base: ButtonLike, position_in_group: Option, @@ -142,3 +142,130 @@ impl RenderOnce for ToggleButton { ) } } + +impl ComponentPreview for ToggleButton { + fn preview(_window: &mut Window, _cx: &App) -> AnyElement { + v_flex() + .gap_6() + .children(vec![ + example_group_with_title( + "Button Styles", + vec![ + single_example( + "Off", + ToggleButton::new("off", "Off") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .into_any_element(), + ), + single_example( + "On", + ToggleButton::new("on", "On") + .layer(ElevationIndex::Background) + .toggle_state(true) + .style(ButtonStyle::Filled) + .into_any_element(), + ), + single_example( + "Off – Disabled", + ToggleButton::new("disabled_off", "Disabled Off") + .layer(ElevationIndex::Background) + .disabled(true) + .style(ButtonStyle::Filled) + .into_any_element(), + ), + single_example( + "On – Disabled", + ToggleButton::new("disabled_on", "Disabled On") + .layer(ElevationIndex::Background) + .disabled(true) + .toggle_state(true) + .style(ButtonStyle::Filled) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Button Group", + vec![ + single_example( + "Three Buttons", + h_flex() + .child( + ToggleButton::new("three_btn_first", "First") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .first() + .into_any_element(), + ) + .child( + ToggleButton::new("three_btn_middle", "Middle") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .middle() + .toggle_state(true) + .into_any_element(), + ) + .child( + ToggleButton::new("three_btn_last", "Last") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .last() + .into_any_element(), + ) + .into_any_element(), + ), + single_example( + "Two Buttons", + h_flex() + .child( + ToggleButton::new("two_btn_first", "First") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .first() + .into_any_element(), + ) + .child( + ToggleButton::new("two_btn_last", "Last") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .last() + .into_any_element(), + ) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Alternate Sizes", + vec![ + single_example( + "None", + ToggleButton::new("none", "None") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .size(ButtonSize::None) + .into_any_element(), + ), + single_example( + "Compact", + ToggleButton::new("compact", "Compact") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .size(ButtonSize::Compact) + .into_any_element(), + ), + single_example( + "Large", + ToggleButton::new("large", "Large") + .layer(ElevationIndex::Background) + .style(ButtonStyle::Filled) + .size(ButtonSize::Large) + .into_any_element(), + ), + ], + ), + ]) + .into_any_element() + } +} diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index 03e5fa407db791..6b87bdbcd72b5c 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use crate::{ h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, IconSize, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader, diff --git a/crates/ui/src/components/disclosure.rs b/crates/ui/src/components/disclosure.rs index 036a26090e0721..c39bcf47d50b97 100644 --- a/crates/ui/src/components/disclosure.rs +++ b/crates/ui/src/components/disclosure.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use std::sync::Arc; use gpui::{ClickEvent, CursorStyle}; diff --git a/crates/ui/src/components/divider.rs b/crates/ui/src/components/divider.rs index 2a0f87a610c73d..21463bad21fb10 100644 --- a/crates/ui/src/components/divider.rs +++ b/crates/ui/src/components/divider.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use gpui::{Hsla, IntoElement}; use crate::prelude::*; diff --git a/crates/ui/src/components/dropdown_menu.rs b/crates/ui/src/components/dropdown_menu.rs index 1f2e0473af4fe1..7d2072d6c9daae 100644 --- a/crates/ui/src/components/dropdown_menu.rs +++ b/crates/ui/src/components/dropdown_menu.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton}; use crate::{prelude::*, ContextMenu, PopoverMenu}; diff --git a/crates/ui/src/components/facepile.rs b/crates/ui/src/components/facepile.rs index d965bc598a457b..59df3f4c005c9a 100644 --- a/crates/ui/src/components/facepile.rs +++ b/crates/ui/src/components/facepile.rs @@ -1,4 +1,4 @@ -use crate::prelude::*; +use crate::{prelude::*, Avatar}; use gpui::{AnyElement, StyleRefinement}; use smallvec::SmallVec; @@ -7,7 +7,7 @@ use smallvec::SmallVec; /// /// Facepiles are used to display a group of people or things, /// such as a list of participants in a collaboration session. -#[derive(IntoElement)] +#[derive(IntoElement, IntoComponent)] pub struct Facepile { base: Div, faces: SmallVec<[AnyElement; 2]>, @@ -60,60 +60,57 @@ impl RenderOnce for Facepile { } } -// impl ComponentPreview for Facepile { -// fn description() -> impl Into> { -// "A facepile is a collection of faces stacked horizontally–\ -// always with the leftmost face on top and descending in z-index.\ -// \n\nFacepiles are used to display a group of people or things,\ -// such as a list of participants in a collaboration session." -// } -// fn examples(_window: &mut Window, _: &mut App) -> Vec> { -// let few_faces: [&'static str; 3] = [ -// "https://avatars.githubusercontent.com/u/1714999?s=60&v=4", -// "https://avatars.githubusercontent.com/u/67129314?s=60&v=4", -// "https://avatars.githubusercontent.com/u/482957?s=60&v=4", -// ]; +impl ComponentPreview for Facepile { + fn preview(_window: &mut Window, _cx: &App) -> AnyElement { + let faces: [&'static str; 6] = [ + "https://avatars.githubusercontent.com/u/326587?s=60&v=4", + "https://avatars.githubusercontent.com/u/2280405?s=60&v=4", + "https://avatars.githubusercontent.com/u/1789?s=60&v=4", + "https://avatars.githubusercontent.com/u/67129314?s=60&v=4", + "https://avatars.githubusercontent.com/u/482957?s=60&v=4", + "https://avatars.githubusercontent.com/u/1714999?s=60&v=4", + ]; -// let many_faces: [&'static str; 6] = [ -// "https://avatars.githubusercontent.com/u/326587?s=60&v=4", -// "https://avatars.githubusercontent.com/u/2280405?s=60&v=4", -// "https://avatars.githubusercontent.com/u/1789?s=60&v=4", -// "https://avatars.githubusercontent.com/u/67129314?s=60&v=4", -// "https://avatars.githubusercontent.com/u/482957?s=60&v=4", -// "https://avatars.githubusercontent.com/u/1714999?s=60&v=4", -// ]; - -// vec![example_group_with_title( -// "Examples", -// vec![ -// single_example( -// "Few Faces", -// Facepile::new( -// few_faces -// .iter() -// .map(|&url| Avatar::new(url).into_any_element()) -// .collect(), -// ), -// ), -// single_example( -// "Many Faces", -// Facepile::new( -// many_faces -// .iter() -// .map(|&url| Avatar::new(url).into_any_element()) -// .collect(), -// ), -// ), -// single_example( -// "Custom Size", -// Facepile::new( -// few_faces -// .iter() -// .map(|&url| Avatar::new(url).size(px(24.)).into_any_element()) -// .collect(), -// ), -// ), -// ], -// )] -// } -// } + v_flex() + .gap_6() + .children(vec![ + example_group_with_title( + "Facepile Examples", + vec![ + single_example( + "Default", + Facepile::new( + faces + .iter() + .map(|&url| Avatar::new(url).into_any_element()) + .collect(), + ) + .into_any_element(), + ), + single_example( + "Custom Size", + Facepile::new( + faces + .iter() + .map(|&url| Avatar::new(url).size(px(24.)).into_any_element()) + .collect(), + ) + .into_any_element(), + ), + ], + ), + example_group_with_title( + "Special Cases", + vec![ + single_example("Empty Facepile", Facepile::empty().into_any_element()), + single_example( + "Single Face", + Facepile::new(vec![Avatar::new(faces[0]).into_any_element()].into()) + .into_any_element(), + ), + ], + ), + ]) + .into_any_element() + } +} diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index ca90a16ea7029c..ddb03071262458 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - mod decorated_icon; mod icon_decoration; diff --git a/crates/ui/src/components/image.rs b/crates/ui/src/components/image.rs index cb90b9a1e388fe..cee5cb3538ec7e 100644 --- a/crates/ui/src/components/image.rs +++ b/crates/ui/src/components/image.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use gpui::{svg, App, IntoElement, Rems, RenderOnce, Size, Styled, Window}; use serde::{Deserialize, Serialize}; use strum::{EnumIter, EnumString, IntoStaticStr}; diff --git a/crates/ui/src/components/indent_guides.rs b/crates/ui/src/components/indent_guides.rs index 87c390ef77d2e7..0d9c3f5571c3a9 100644 --- a/crates/ui/src/components/indent_guides.rs +++ b/crates/ui/src/components/indent_guides.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use std::{cmp::Ordering, ops::Range, rc::Rc}; use gpui::{ diff --git a/crates/ui/src/components/indicator.rs b/crates/ui/src/components/indicator.rs index 0cf4cab72eba99..86d1f31e409382 100644 --- a/crates/ui/src/components/indicator.rs +++ b/crates/ui/src/components/indicator.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use crate::{prelude::*, AnyIcon}; #[derive(Default)] diff --git a/crates/ui/src/components/keybinding.rs b/crates/ui/src/components/keybinding.rs index c3a3e6579d4961..13fb412e3b8f77 100644 --- a/crates/ui/src/components/keybinding.rs +++ b/crates/ui/src/components/keybinding.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use crate::PlatformStyle; use crate::{h_flex, prelude::*, Icon, IconName, IconSize}; use gpui::{ diff --git a/crates/ui/src/components/label/highlighted_label.rs b/crates/ui/src/components/label/highlighted_label.rs index a8be4624039eed..df6866aee8fad0 100644 --- a/crates/ui/src/components/label/highlighted_label.rs +++ b/crates/ui/src/components/label/highlighted_label.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::ops::Range; use gpui::{FontWeight, HighlightStyle, StyledText}; diff --git a/crates/ui/src/components/list/list.rs b/crates/ui/src/components/list/list.rs index 88f9c54246b19e..7cd81a51648180 100644 --- a/crates/ui/src/components/list/list.rs +++ b/crates/ui/src/components/list/list.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::AnyElement; use smallvec::SmallVec; diff --git a/crates/ui/src/components/list/list_header.rs b/crates/ui/src/components/list/list_header.rs index d7a5c7bbaf4f58..91bb0ca76a2a23 100644 --- a/crates/ui/src/components/list/list_header.rs +++ b/crates/ui/src/components/list/list_header.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::sync::Arc; use crate::{h_flex, prelude::*, Disclosure, Label}; diff --git a/crates/ui/src/components/list/list_item.rs b/crates/ui/src/components/list/list_item.rs index 7eec79cfd74c46..2d2e506e62d4e3 100644 --- a/crates/ui/src/components/list/list_item.rs +++ b/crates/ui/src/components/list/list_item.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::sync::Arc; use gpui::{px, AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent, Pixels}; diff --git a/crates/ui/src/components/list/list_separator.rs b/crates/ui/src/components/list/list_separator.rs index f3a900404f44b3..92a7c987c76f7d 100644 --- a/crates/ui/src/components/list/list_separator.rs +++ b/crates/ui/src/components/list/list_separator.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use crate::prelude::*; #[derive(IntoElement)] diff --git a/crates/ui/src/components/list/list_sub_header.rs b/crates/ui/src/components/list/list_sub_header.rs index 297068fc85b371..3cf2fd51d7d71c 100644 --- a/crates/ui/src/components/list/list_sub_header.rs +++ b/crates/ui/src/components/list/list_sub_header.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use crate::prelude::*; use crate::{h_flex, Icon, IconName, IconSize, Label}; diff --git a/crates/ui/src/components/modal.rs b/crates/ui/src/components/modal.rs index 601c4228c6ddac..b148e2902b2984 100644 --- a/crates/ui/src/components/modal.rs +++ b/crates/ui/src/components/modal.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use crate::{ h_flex, v_flex, Clickable, Color, DynamicSpacing, Headline, HeadlineSize, IconButton, IconButtonShape, IconName, Label, LabelCommon, LabelSize, diff --git a/crates/ui/src/components/numeric_stepper.rs b/crates/ui/src/components/numeric_stepper.rs index 87cdfbee645092..d491732b85a0f8 100644 --- a/crates/ui/src/components/numeric_stepper.rs +++ b/crates/ui/src/components/numeric_stepper.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::ClickEvent; use crate::{prelude::*, IconButtonShape}; diff --git a/crates/ui/src/components/popover.rs b/crates/ui/src/components/popover.rs index 85033eb32f10cc..2ac0b8f5fc9f6b 100644 --- a/crates/ui/src/components/popover.rs +++ b/crates/ui/src/components/popover.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use crate::prelude::*; use crate::v_flex; use gpui::{ diff --git a/crates/ui/src/components/popover_menu.rs b/crates/ui/src/components/popover_menu.rs index e3ce37b43e0b74..6be332b6932e1b 100644 --- a/crates/ui/src/components/popover_menu.rs +++ b/crates/ui/src/components/popover_menu.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::{cell::RefCell, rc::Rc}; use gpui::{ diff --git a/crates/ui/src/components/radio.rs b/crates/ui/src/components/radio.rs index d7ee106d2d5bb0..c7e19f5c34701e 100644 --- a/crates/ui/src/components/radio.rs +++ b/crates/ui/src/components/radio.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::sync::Arc; use crate::prelude::*; diff --git a/crates/ui/src/components/right_click_menu.rs b/crates/ui/src/components/right_click_menu.rs index 1bbf5aeedd292b..9d171e6daa26ee 100644 --- a/crates/ui/src/components/right_click_menu.rs +++ b/crates/ui/src/components/right_click_menu.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::{cell::RefCell, rc::Rc}; use gpui::{ diff --git a/crates/ui/src/components/scrollbar.rs b/crates/ui/src/components/scrollbar.rs index a9793adea81e23..5cb0b8a43dff1d 100644 --- a/crates/ui/src/components/scrollbar.rs +++ b/crates/ui/src/components/scrollbar.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use std::{any::Any, cell::Cell, fmt::Debug, ops::Range, rc::Rc, sync::Arc}; use crate::{prelude::*, px, relative, IntoElement}; diff --git a/crates/ui/src/components/settings_container.rs b/crates/ui/src/components/settings_container.rs index 538a5b91f849e3..b8a4a021c623fb 100644 --- a/crates/ui/src/components/settings_container.rs +++ b/crates/ui/src/components/settings_container.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::AnyElement; use smallvec::SmallVec; diff --git a/crates/ui/src/components/settings_group.rs b/crates/ui/src/components/settings_group.rs index 9ead450179096f..90cd1b16276ee1 100644 --- a/crates/ui/src/components/settings_group.rs +++ b/crates/ui/src/components/settings_group.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::AnyElement; use smallvec::SmallVec; diff --git a/crates/ui/src/components/stack.rs b/crates/ui/src/components/stack.rs index 2af0a5d3f98b08..74a5e80575bfe0 100644 --- a/crates/ui/src/components/stack.rs +++ b/crates/ui/src/components/stack.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::{div, Div}; use crate::StyledExt; diff --git a/crates/ui/src/components/stories.rs b/crates/ui/src/components/stories.rs index 9fa380c70319a8..e9e0649026c996 100644 --- a/crates/ui/src/components/stories.rs +++ b/crates/ui/src/components/stories.rs @@ -1,6 +1,5 @@ // We allow missing docs for stories as the docs will more or less be // "This is the ___ story", which is not very useful. -#![allow(missing_docs)] mod avatar; mod button; mod context_menu; diff --git a/crates/ui/src/components/tab.rs b/crates/ui/src/components/tab.rs index bf8637737753a2..3d16bdc84b3461 100644 --- a/crates/ui/src/components/tab.rs +++ b/crates/ui/src/components/tab.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use std::cmp::Ordering; use gpui::{AnyElement, IntoElement, Stateful}; diff --git a/crates/ui/src/components/tab_bar.rs b/crates/ui/src/components/tab_bar.rs index ee7b2a66446c88..cd6781be593cfc 100644 --- a/crates/ui/src/components/tab_bar.rs +++ b/crates/ui/src/components/tab_bar.rs @@ -1,4 +1,3 @@ -#![allow(missing_docs)] use gpui::{AnyElement, ScrollHandle}; use smallvec::SmallVec; diff --git a/crates/ui/src/components/tooltip.rs b/crates/ui/src/components/tooltip.rs index cd391bb7f1a0ab..246b74cd2ee93e 100644 --- a/crates/ui/src/components/tooltip.rs +++ b/crates/ui/src/components/tooltip.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::{Action, AnyElement, AnyView, AppContext as _, FocusHandle, IntoElement, Render}; use settings::Settings; use theme::ThemeSettings; diff --git a/crates/ui/src/styles/typography.rs b/crates/ui/src/styles/typography.rs index c575e734756ac6..9848137441aab2 100644 --- a/crates/ui/src/styles/typography.rs +++ b/crates/ui/src/styles/typography.rs @@ -237,40 +237,37 @@ impl Headline { impl ComponentPreview for Headline { fn preview(_window: &mut Window, _cx: &App) -> AnyElement { v_flex() - .gap_6() - .children(vec![example_group_with_title( - "Headline Sizes", - vec![ - single_example( - "XLarge", - Headline::new("XLarge Headline") - .size(HeadlineSize::XLarge) - .into_any_element(), - ), - single_example( - "Large", - Headline::new("Large Headline") - .size(HeadlineSize::Large) - .into_any_element(), - ), - single_example( - "Medium (Default)", - Headline::new("Medium Headline").into_any_element(), - ), - single_example( - "Small", - Headline::new("Small Headline") - .size(HeadlineSize::Small) - .into_any_element(), - ), - single_example( - "XSmall", - Headline::new("XSmall Headline") - .size(HeadlineSize::XSmall) - .into_any_element(), - ), - ], - )]) + .gap_1() + .children(vec![ + single_example( + "XLarge", + Headline::new("XLarge Headline") + .size(HeadlineSize::XLarge) + .into_any_element(), + ), + single_example( + "Large", + Headline::new("Large Headline") + .size(HeadlineSize::Large) + .into_any_element(), + ), + single_example( + "Medium (Default)", + Headline::new("Medium Headline").into_any_element(), + ), + single_example( + "Small", + Headline::new("Small Headline") + .size(HeadlineSize::Small) + .into_any_element(), + ), + single_example( + "XSmall", + Headline::new("XSmall Headline") + .size(HeadlineSize::XSmall) + .into_any_element(), + ), + ]) .into_any_element() } } diff --git a/crates/ui/src/utils/format_distance.rs b/crates/ui/src/utils/format_distance.rs index af4f0d95052b28..213d9c8b4c2ef2 100644 --- a/crates/ui/src/utils/format_distance.rs +++ b/crates/ui/src/utils/format_distance.rs @@ -1,5 +1,4 @@ // This won't be documented further as it is intended to be removed, or merged with the `time_format` crate. -#![allow(missing_docs)] use chrono::{DateTime, Local, NaiveDateTime}; diff --git a/crates/ui/src/utils/search_input.rs b/crates/ui/src/utils/search_input.rs index 3a507f9a5ae145..c541a3b21fbf5b 100644 --- a/crates/ui/src/utils/search_input.rs +++ b/crates/ui/src/utils/search_input.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use gpui::Pixels; pub struct SearchInputWidth; From dd9a0e116a44282efcb95f70e58d7eba7fc43922 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Fri, 21 Feb 2025 08:22:41 -0500 Subject: [PATCH 7/8] Remove avatar/button stories --- crates/storybook/src/story_selector.rs | 4 -- crates/ui/src/components/stories.rs | 6 -- crates/ui/src/components/stories/avatar.rs | 64 ---------------------- crates/ui/src/components/stories/button.rs | 38 ------------- 4 files changed, 112 deletions(-) diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs index 9bae13f7bf1e14..a4814404135389 100644 --- a/crates/storybook/src/story_selector.rs +++ b/crates/storybook/src/story_selector.rs @@ -14,8 +14,6 @@ use ui::prelude::*; pub enum ComponentStory { ApplicationMenu, AutoHeightEditor, - Avatar, - Button, CollabNotification, ContextMenu, Cursor, @@ -47,8 +45,6 @@ impl ComponentStory { .new(|cx| title_bar::ApplicationMenuStory::new(window, cx)) .into(), Self::AutoHeightEditor => AutoHeightEditorStory::new(window, cx).into(), - Self::Avatar => cx.new(|_| ui::AvatarStory).into(), - Self::Button => cx.new(|_| ui::ButtonStory).into(), Self::CollabNotification => cx .new(|_| collab_ui::notifications::CollabNotificationStory) .into(), diff --git a/crates/ui/src/components/stories.rs b/crates/ui/src/components/stories.rs index e9e0649026c996..69bd5715861846 100644 --- a/crates/ui/src/components/stories.rs +++ b/crates/ui/src/components/stories.rs @@ -1,7 +1,3 @@ -// We allow missing docs for stories as the docs will more or less be -// "This is the ___ story", which is not very useful. -mod avatar; -mod button; mod context_menu; mod disclosure; mod icon; @@ -14,8 +10,6 @@ mod tab; mod tab_bar; mod toggle_button; -pub use avatar::*; -pub use button::*; pub use context_menu::*; pub use disclosure::*; pub use icon::*; diff --git a/crates/ui/src/components/stories/avatar.rs b/crates/ui/src/components/stories/avatar.rs index 6b268d8bd6bbe3..e69de29bb2d1d6 100644 --- a/crates/ui/src/components/stories/avatar.rs +++ b/crates/ui/src/components/stories/avatar.rs @@ -1,64 +0,0 @@ -use gpui::Render; -use story::{Story, StoryItem, StorySection}; - -use crate::{prelude::*, AudioStatus, CollaboratorAvailability, AvatarAvailabilityIndicator}; -use crate::{Avatar, AvatarAudioStatusIndicator}; - -pub struct AvatarStory; - -impl Render for AvatarStory { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - Story::container() - .child(Story::title_for::()) - .child( - StorySection::new() - .child(StoryItem::new( - "Default", - Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4"), - )) - .child(StoryItem::new( - "Default", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4"), - )), - ) - .child( - StorySection::new() - .child(StoryItem::new( - "With free availability indicator", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .indicator(AvatarAvailabilityIndicator::new(CollaboratorAvailability::Free)), - )) - .child(StoryItem::new( - "With busy availability indicator", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .indicator(AvatarAvailabilityIndicator::new(CollaboratorAvailability::Busy)), - )), - ) - .child( - StorySection::new() - .child(StoryItem::new( - "With info border", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .border_color(cx.theme().status().info_border), - )) - .child(StoryItem::new( - "With error border", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .border_color(cx.theme().status().error_border), - )), - ) - .child( - StorySection::new() - .child(StoryItem::new( - "With muted audio indicator", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)), - )) - .child(StoryItem::new( - "With deafened audio indicator", - Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4") - .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)), - )), - ) - } -} diff --git a/crates/ui/src/components/stories/button.rs b/crates/ui/src/components/stories/button.rs index 730a15471b5092..e69de29bb2d1d6 100644 --- a/crates/ui/src/components/stories/button.rs +++ b/crates/ui/src/components/stories/button.rs @@ -1,38 +0,0 @@ -use gpui::Render; -use story::Story; - -use crate::{prelude::*, IconName}; -use crate::{Button, ButtonStyle}; - -pub struct ButtonStory; - -impl Render for ButtonStory { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - Story::container() - .child(Story::title_for::