ui: Add Chip component (#34521)

Possibly the simplest component in our set, but a nice one to have so we
can standardize how it looks across the app.

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-07-16 01:15:45 -03:00 committed by GitHub
parent ee4b9a27a2
commit 59d524427e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 124 additions and 40 deletions

View file

@ -27,7 +27,7 @@ use project::{
use proto::Plan; use proto::Plan;
use settings::{Settings, update_settings_file}; use settings::{Settings, update_settings_file};
use ui::{ use ui::{
ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex, Indicator, PopoverMenu, Chip, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex, Indicator, PopoverMenu,
Scrollbar, ScrollbarState, Switch, SwitchColor, Tooltip, prelude::*, Scrollbar, ScrollbarState, Switch, SwitchColor, Tooltip, prelude::*,
}; };
use util::ResultExt as _; use util::ResultExt as _;
@ -227,7 +227,7 @@ impl AgentConfiguration {
) )
.map(|this| { .map(|this| {
if is_zed_provider { if is_zed_provider {
this.child( this.gap_2().child(
self.render_zed_plan_info(current_plan, cx), self.render_zed_plan_info(current_plan, cx),
) )
} else { } else {
@ -474,26 +474,15 @@ impl AgentConfiguration {
.opacity(0.5) .opacity(0.5)
.blend(cx.theme().colors().text_accent.opacity(0.2)); .blend(cx.theme().colors().text_accent.opacity(0.2));
let (plan_name, plan_color, bg_color) = match plan { let (plan_name, label_color, bg_color) = match plan {
Plan::Free => ("Free", Color::Default, free_chip_bg), Plan::Free => ("Free", Color::Default, free_chip_bg),
Plan::ZedProTrial => ("Pro Trial", Color::Accent, pro_chip_bg), Plan::ZedProTrial => ("Pro Trial", Color::Accent, pro_chip_bg),
Plan::ZedPro => ("Pro", Color::Accent, pro_chip_bg), Plan::ZedPro => ("Pro", Color::Accent, pro_chip_bg),
}; };
h_flex() Chip::new(plan_name.to_string())
.ml_1() .bg_color(bg_color)
.px_1() .label_color(label_color)
.rounded_sm()
.border_1()
.border_color(cx.theme().colors().border)
.bg(bg_color)
.overflow_hidden()
.child(
Label::new(plan_name.to_string())
.color(plan_color)
.size(LabelSize::XSmall)
.buffer_font(cx),
)
.into_any_element() .into_any_element()
} else { } else {
div().into_any_element() div().into_any_element()

View file

@ -48,20 +48,20 @@ impl RenderOnce for ComponentExample {
) )
.child( .child(
div() div()
.flex()
.w_full()
.rounded_xl()
.min_h(px(100.)) .min_h(px(100.))
.justify_center() .w_full()
.p_8() .p_8()
.flex()
.items_center()
.justify_center()
.rounded_xl()
.border_1() .border_1()
.border_color(cx.theme().colors().border.opacity(0.5)) .border_color(cx.theme().colors().border.opacity(0.5))
.bg(pattern_slash( .bg(pattern_slash(
cx.theme().colors().surface_background.opacity(0.5), cx.theme().colors().surface_background.opacity(0.25),
12.0, 12.0,
12.0, 12.0,
)) ))
.shadow_xs()
.child(self.element), .child(self.element),
) )
.into_any_element() .into_any_element()

View file

@ -24,7 +24,7 @@ use settings::Settings;
use strum::IntoEnumIterator as _; use strum::IntoEnumIterator as _;
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{ use ui::{
CheckboxWithLabel, ContextMenu, PopoverMenu, ScrollableHandle, Scrollbar, ScrollbarState, CheckboxWithLabel, Chip, ContextMenu, PopoverMenu, ScrollableHandle, Scrollbar, ScrollbarState,
ToggleButton, Tooltip, prelude::*, ToggleButton, Tooltip, prelude::*,
}; };
use vim_mode_setting::VimModeSetting; use vim_mode_setting::VimModeSetting;
@ -759,20 +759,7 @@ impl ExtensionsPage {
_ => {} _ => {}
} }
Some( Some(Chip::new(extension_provides_label(*provides)))
div()
.px_1()
.border_1()
.rounded_sm()
.border_color(cx.theme().colors().border)
.bg(cx.theme().colors().element_background)
.child(
Label::new(extension_provides_label(
*provides,
))
.size(LabelSize::XSmall),
),
)
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
), ),

View file

@ -2,6 +2,7 @@ mod avatar;
mod banner; mod banner;
mod button; mod button;
mod callout; mod callout;
mod chip;
mod content_group; mod content_group;
mod context_menu; mod context_menu;
mod disclosure; mod disclosure;
@ -43,6 +44,7 @@ pub use avatar::*;
pub use banner::*; pub use banner::*;
pub use button::*; pub use button::*;
pub use callout::*; pub use callout::*;
pub use chip::*;
pub use content_group::*; pub use content_group::*;
pub use context_menu::*; pub use context_menu::*;
pub use disclosure::*; pub use disclosure::*;

View file

@ -0,0 +1,106 @@
use crate::prelude::*;
use gpui::{AnyElement, Hsla, IntoElement, ParentElement, Styled};
/// Chips provide a container for an informative label.
///
/// # Usage Example
///
/// ```
/// use ui::{Chip};
///
/// Chip::new("This Chip")
/// ```
#[derive(IntoElement, RegisterComponent)]
pub struct Chip {
label: SharedString,
label_color: Color,
label_size: LabelSize,
bg_color: Option<Hsla>,
}
impl Chip {
/// Creates a new `Chip` component with the specified label.
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
label: label.into(),
label_color: Color::Default,
label_size: LabelSize::XSmall,
bg_color: None,
}
}
/// Sets the color of the label.
pub fn label_color(mut self, color: Color) -> Self {
self.label_color = color;
self
}
/// Sets the size of the label.
pub fn label_size(mut self, size: LabelSize) -> Self {
self.label_size = size;
self
}
/// Sets a custom background color for the callout content.
pub fn bg_color(mut self, color: Hsla) -> Self {
self.bg_color = Some(color);
self
}
}
impl RenderOnce for Chip {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let bg_color = self
.bg_color
.unwrap_or(cx.theme().colors().element_background);
h_flex()
.min_w_0()
.flex_initial()
.px_1()
.border_1()
.rounded_sm()
.border_color(cx.theme().colors().border)
.bg(bg_color)
.overflow_hidden()
.child(
Label::new(self.label)
.size(self.label_size)
.color(self.label_color)
.buffer_font(cx),
)
}
}
impl Component for Chip {
fn scope() -> ComponentScope {
ComponentScope::DataDisplay
}
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let chip_examples = vec![
single_example("Default", Chip::new("Chip Example").into_any_element()),
single_example(
"Customized Label Color",
Chip::new("Chip Example")
.label_color(Color::Accent)
.into_any_element(),
),
single_example(
"Customized Label Size",
Chip::new("Chip Example")
.label_size(LabelSize::Large)
.label_color(Color::Accent)
.into_any_element(),
),
single_example(
"Customized Background Color",
Chip::new("Chip Example")
.bg_color(cx.theme().colors().text_accent.opacity(0.1))
.into_any_element(),
),
];
Some(example_group(chip_examples).vertical().into_any_element())
}
}

View file

@ -206,7 +206,7 @@ impl RenderOnce for KeybindingHint {
impl Component for KeybindingHint { impl Component for KeybindingHint {
fn scope() -> ComponentScope { fn scope() -> ComponentScope {
ComponentScope::None ComponentScope::DataDisplay
} }
fn description() -> Option<&'static str> { fn description() -> Option<&'static str> {

View file

@ -274,7 +274,7 @@ impl Render for LinkPreview {
impl Component for Tooltip { impl Component for Tooltip {
fn scope() -> ComponentScope { fn scope() -> ComponentScope {
ComponentScope::None ComponentScope::DataDisplay
} }
fn description() -> Option<&'static str> { fn description() -> Option<&'static str> {