use crate::prelude::*;
use gpui::{AnyElement, AnyView, ClickEvent, Hsla, IntoElement, ParentElement, Styled};
#[derive(IntoElement, RegisterComponent)]
pub struct Chip {
label: SharedString,
label_color: Color,
label_size: LabelSize,
icon: Option<IconName>,
icon_color: Color,
bg_color: Option<Hsla>,
border_color: Option<Hsla>,
height: Option<Pixels>,
truncate: bool,
pill: bool,
on_dismiss: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
}
impl Chip {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
label: label.into(),
label_color: Color::Default,
label_size: LabelSize::XSmall,
icon: None,
icon_color: Color::Default,
bg_color: None,
border_color: None,
height: None,
truncate: false,
pill: false,
on_dismiss: None,
tooltip: None,
}
}
pub fn label_color(mut self, color: Color) -> Self {
self.label_color = color;
self
}
pub fn label_size(mut self, size: LabelSize) -> Self {
self.label_size = size;
self
}
pub fn icon(mut self, icon: IconName) -> Self {
self.icon = Some(icon);
self
}
pub fn icon_color(mut self, color: Color) -> Self {
self.icon_color = color;
self
}
pub fn bg_color(mut self, color: Hsla) -> Self {
self.bg_color = Some(color);
self
}
pub fn border_color(mut self, color: Hsla) -> Self {
self.border_color = Some(color);
self
}
pub fn height(mut self, height: Pixels) -> Self {
self.height = Some(height);
self
}
pub fn truncate(mut self) -> Self {
self.truncate = true;
self
}
pub fn pill(mut self, pill: bool) -> Self {
self.pill = pill;
self
}
pub fn dismissible(
mut self,
on_dismiss: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
) -> Self {
self.on_dismiss = Some(Box::new(on_dismiss));
self
}
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 Chip {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let bg_color = self.bg_color.unwrap_or_else(|| semantic::surface(cx));
let border_color = self.border_color.unwrap_or_else(|| semantic::border(cx));
let pill = self.pill;
h_flex()
.when_some(self.height, |this, h| this.h(h))
.when(self.truncate, |this| this.min_w_0())
.when(!self.truncate, |this| this.flex_none())
.gap_1()
.px_2()
.py_0p5()
.border_1()
.when(pill, |this| this.rounded_full())
.when(!pill, |this| this.rounded_md())
.border_color(border_color)
.bg(bg_color)
.overflow_hidden()
.when_some(self.icon, |this, icon| {
this.child(
Icon::new(icon)
.size(IconSize::XSmall)
.color(self.icon_color),
)
})
.child(
Label::new(self.label.clone())
.size(self.label_size)
.color(self.label_color)
.buffer_font(cx)
.truncate(),
)
.id(self.label.clone())
.when_some(self.on_dismiss, |this, on_dismiss| {
this.child(
div()
.id("dismiss")
.cursor_pointer()
.child(
Icon::new(IconName::XMark)
.size(IconSize::XSmall)
.color(Color::Muted),
)
.on_click(move |event, window, cx| {
cx.stop_propagation();
on_dismiss(event, window, cx)
}),
)
})
.when_some(self.tooltip, |this, tooltip| this.tooltip(tooltip))
}
}
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(
"Pill",
Chip::new("Chip Example").pill(true).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(
"With Icon",
Chip::new("Chip Example")
.icon(IconName::Check)
.into_any_element(),
),
single_example(
"Dismissible",
Chip::new("Chip Example")
.dismissible(|_, _, _| {})
.into_any_element(),
),
];
Some(example_group(chip_examples).vertical().into_any_element())
}
}