From 29c5ea0a50ed62e905dbe0f750d8a30603674098 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 6 Nov 2024 21:15:35 -0500 Subject: [PATCH] More previews (#20329) Release Notes: - N/A --- crates/ui/src/components/button/button.rs | 102 +++++++++++++++++++++- crates/ui/src/components/facepile.rs | 63 ++++++++++++- crates/ui/src/components/indicator.rs | 51 +++++++++++ crates/workspace/src/theme_preview.rs | 5 +- 4 files changed, 217 insertions(+), 4 deletions(-) diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index f81de83a2bf7c485dcd172d9f92904d6e11ff533..ea4d48de14e68856961f680b366836f9d4b161a2 100644 --- a/crates/ui/src/components/button/button.rs +++ b/crates/ui/src/components/button/button.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use gpui::{AnyView, DefiniteLength}; -use crate::{prelude::*, ElevationIndex, IconPosition, KeyBinding, Spacing}; +use crate::{prelude::*, ElevationIndex, IconPosition, KeyBinding, Spacing, TintColor}; use crate::{ ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label, LineHeightStyle, }; @@ -437,3 +437,103 @@ impl RenderOnce for Button { ) } } + +impl ComponentPreview for Button { + fn description() -> impl Into> { + "A button allows users to take actions, and make choices, with a single tap." + } + + fn examples() -> Vec> { + vec![ + example_group( + "Styles", + vec![ + single_example("Default", Button::new("default", "Default")), + single_example( + "Filled", + Button::new("filled", "Filled").style(ButtonStyle::Filled), + ), + single_example( + "Subtle", + Button::new("outline", "Subtle").style(ButtonStyle::Subtle), + ), + single_example( + "Transparent", + Button::new("transparent", "Transparent").style(ButtonStyle::Transparent), + ), + ], + ), + example_group( + "Tinted", + vec![ + single_example( + "Accent", + Button::new("tinted_accent", "Accent") + .style(ButtonStyle::Tinted(TintColor::Accent)), + ), + single_example( + "Negative", + Button::new("tinted_negative", "Negative") + .style(ButtonStyle::Tinted(TintColor::Negative)), + ), + single_example( + "Warning", + Button::new("tinted_warning", "Warning") + .style(ButtonStyle::Tinted(TintColor::Warning)), + ), + single_example( + "Positive", + Button::new("tinted_positive", "Positive") + .style(ButtonStyle::Tinted(TintColor::Positive)), + ), + ], + ), + example_group( + "States", + vec![ + single_example("Default", Button::new("default_state", "Default")), + single_example( + "Disabled", + Button::new("disabled", "Disabled").disabled(true), + ), + single_example( + "Selected", + Button::new("selected", "Selected").selected(true), + ), + ], + ), + example_group( + "With Icons", + vec![ + single_example( + "Icon Start", + Button::new("icon_start", "Icon Start") + .icon(IconName::Check) + .icon_position(IconPosition::Start), + ), + single_example( + "Icon End", + Button::new("icon_end", "Icon End") + .icon(IconName::Check) + .icon_position(IconPosition::End), + ), + single_example( + "Icon Color", + Button::new("icon_color", "Icon Color") + .icon(IconName::Check) + .icon_color(Color::Accent), + ), + single_example( + "Tinted Icons", + Button::new("icon_color", "Delete") + .style(ButtonStyle::Tinted(TintColor::Negative)) + .color(Color::Error) + .icon_color(Color::Error) + .icon(IconName::Trash) + .icon_position(IconPosition::Start), + ), + ], + ), + ] + } +} diff --git a/crates/ui/src/components/facepile.rs b/crates/ui/src/components/facepile.rs index c3a56d8e20fe8850657cbe51e2a966872100e8b0..99d5232c7926ccdc166e98c738bdf5d253bdc3af 100644 --- a/crates/ui/src/components/facepile.rs +++ b/crates/ui/src/components/facepile.rs @@ -1,5 +1,4 @@ -#![allow(missing_docs)] -use crate::prelude::*; +use crate::{prelude::*, Avatar}; use gpui::{AnyElement, StyleRefinement}; use smallvec::SmallVec; @@ -15,10 +14,12 @@ pub struct Facepile { } impl Facepile { + /// Creates a new empty facepile. pub fn empty() -> Self { Self::new(SmallVec::new()) } + /// Creates a new facepile with the given faces. pub fn new(faces: SmallVec<[AnyElement; 2]>) -> Self { Self { base: div(), faces } } @@ -58,3 +59,61 @@ 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() -> 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", + ]; + + 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( + "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(), + ), + ), + ], + )] + } +} diff --git a/crates/ui/src/components/indicator.rs b/crates/ui/src/components/indicator.rs index 009fe44dfef087c4285ec055fd875523204efc0a..d910542bb314ffe9a2e365f37ca844b289790d11 100644 --- a/crates/ui/src/components/indicator.rs +++ b/crates/ui/src/components/indicator.rs @@ -12,6 +12,7 @@ enum IndicatorKind { #[derive(IntoElement)] pub struct Indicator { kind: IndicatorKind, + border_color: Option, pub color: Color, } @@ -19,6 +20,7 @@ impl Indicator { pub fn dot() -> Self { Self { kind: IndicatorKind::Dot, + border_color: None, color: Color::Default, } } @@ -26,6 +28,8 @@ impl Indicator { pub fn bar() -> Self { Self { kind: IndicatorKind::Bar, + border_color: None, + color: Color::Default, } } @@ -33,6 +37,8 @@ impl Indicator { pub fn icon(icon: impl Into) -> Self { Self { kind: IndicatorKind::Icon(icon.into()), + border_color: None, + color: Color::Default, } } @@ -41,11 +47,25 @@ impl Indicator { self.color = color; self } + + pub fn border_color(mut self, color: Color) -> Self { + self.border_color = Some(color); + self + } } impl RenderOnce for Indicator { fn render(self, cx: &mut WindowContext) -> impl IntoElement { let container = div().flex_none(); + let container = if let Some(border_color) = self.border_color { + if matches!(self.kind, IndicatorKind::Dot | IndicatorKind::Bar) { + container.border_1().border_color(border_color.color(cx)) + } else { + container + } + } else { + container + }; match self.kind { IndicatorKind::Icon(icon) => container @@ -63,3 +83,34 @@ impl RenderOnce for Indicator { } } } + +impl ComponentPreview for Indicator { + fn description() -> impl Into> { + "An indicator visually represents a status or state." + } + + fn examples() -> Vec> { + vec![ + example_group( + "Types", + vec![ + single_example("Dot", Indicator::dot().color(Color::Info)), + single_example("Bar", Indicator::bar().color(Color::Player(2))), + single_example( + "Icon", + Indicator::icon(Icon::new(IconName::Check).color(Color::Success)), + ), + ], + ), + example_group( + "Examples", + vec![ + single_example("Info", Indicator::dot().color(Color::Info)), + single_example("Success", Indicator::dot().color(Color::Success)), + single_example("Warning", Indicator::dot().color(Color::Warning)), + single_example("Error", Indicator::dot().color(Color::Error)), + ], + ), + ] + } +} diff --git a/crates/workspace/src/theme_preview.rs b/crates/workspace/src/theme_preview.rs index f75e07180267106e8face86d1ef8b3d80c7b688c..7361bb3adcf678920346e5e24b9860c8c401184b 100644 --- a/crates/workspace/src/theme_preview.rs +++ b/crates/workspace/src/theme_preview.rs @@ -5,7 +5,7 @@ use theme::all_theme_colors; use ui::{ prelude::*, utils::calculate_contrast_ratio, AudioStatus, Availability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, Checkbox, ElevationIndex, - Facepile, TintColor, Tooltip, + Facepile, Indicator, TintColor, Tooltip, }; use crate::{Item, Workspace}; @@ -510,6 +510,9 @@ impl ThemePreview { .size_full() .gap_2() .child(Checkbox::render_component_previews(cx)) + .child(Facepile::render_component_previews(cx)) + .child(Button::render_component_previews(cx)) + .child(Indicator::render_component_previews(cx)) .child(Icon::render_component_previews(cx)) .child(self.render_avatars(cx)) .child(self.render_buttons(layer, cx))