use gpui::{
App, Hsla, IntoElement, ParentElement, RenderOnce, SharedString, Styled, Window, div, px,
};
use gpui_kit_semantics::{NodeSpec, Role, Semantic};
use gpui_kit_theme::{ActiveTheme, Theme, TypeScale};
use crate::foundation::{Ident, StyledExt};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Tone {
#[default]
Neutral,
Accent,
Success,
Warning,
Danger,
Info,
}
impl Tone {
pub fn name(self) -> &'static str {
match self {
Self::Neutral => "neutral",
Self::Accent => "accent",
Self::Success => "success",
Self::Warning => "warning",
Self::Danger => "danger",
Self::Info => "info",
}
}
pub(crate) fn color(self, theme: &Theme) -> Hsla {
match self {
Self::Neutral => theme.colors.text_faint,
Self::Accent => theme.colors.accent,
Self::Success => theme.colors.success,
Self::Warning => theme.colors.warning,
Self::Danger => theme.colors.danger,
Self::Info => theme.colors.info,
}
}
}
#[derive(Debug, IntoElement)]
pub struct Badge {
ident: Option<Ident>,
label: SharedString,
tone: Tone,
}
impl Badge {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
ident: None,
label: label.into(),
tone: Tone::default(),
}
}
pub fn id(mut self, ident: impl Into<Ident>) -> Self {
self.ident = Some(ident.into());
self
}
pub fn tone(mut self, tone: Tone) -> Self {
self.tone = tone;
self
}
pub fn neutral(self) -> Self {
self.tone(Tone::Neutral)
}
pub fn accent(self) -> Self {
self.tone(Tone::Accent)
}
pub fn success(self) -> Self {
self.tone(Tone::Success)
}
pub fn warning(self) -> Self {
self.tone(Tone::Warning)
}
pub fn danger(self) -> Self {
self.tone(Tone::Danger)
}
pub fn info(self) -> Self {
self.tone(Tone::Info)
}
}
impl RenderOnce for Badge {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme().clone();
let (foreground, background) = match self.tone {
Tone::Neutral => (theme.colors.text_muted, theme.colors.raised),
tone => {
let color = tone.color(&theme);
(color, color.opacity(0.18))
}
};
let element = div()
.flex_none()
.px_token(&theme, gpui_kit_theme::Space::Sm)
.py(px(2.0))
.rounded_full()
.bg(background)
.type_scale(&theme, TypeScale::Caption)
.text_color(foreground)
.child(self.label.clone());
match self.ident {
Some(ident) => element
.semantic_in(
cx,
NodeSpec::new(ident.semantic_id(), Role::Status).text(self.label.clone()),
)
.into_any_element(),
None => element.into_any_element(),
}
}
}