gpui_kit/display/
badge.rs1use gpui::{
2 App, Hsla, IntoElement, ParentElement, RenderOnce, SharedString, Styled, Window, div, px,
3};
4use gpui_kit_semantics::{NodeSpec, Role, Semantic};
5use gpui_kit_theme::{ActiveTheme, Theme, TypeScale};
6
7use crate::foundation::{Ident, StyledExt};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub enum Tone {
15 #[default]
16 Neutral,
17 Accent,
18 Success,
19 Warning,
20 Danger,
21 Info,
22}
23
24impl Tone {
25 pub fn name(self) -> &'static str {
28 match self {
29 Self::Neutral => "neutral",
30 Self::Accent => "accent",
31 Self::Success => "success",
32 Self::Warning => "warning",
33 Self::Danger => "danger",
34 Self::Info => "info",
35 }
36 }
37
38 pub(crate) fn color(self, theme: &Theme) -> Hsla {
39 match self {
40 Self::Neutral => theme.colors.text_faint,
41 Self::Accent => theme.colors.accent,
42 Self::Success => theme.colors.success,
43 Self::Warning => theme.colors.warning,
44 Self::Danger => theme.colors.danger,
45 Self::Info => theme.colors.info,
46 }
47 }
48}
49
50#[derive(Debug, IntoElement)]
55pub struct Badge {
56 ident: Option<Ident>,
57 label: SharedString,
58 tone: Tone,
59}
60
61impl Badge {
62 pub fn new(label: impl Into<SharedString>) -> Self {
63 Self {
64 ident: None,
65 label: label.into(),
66 tone: Tone::default(),
67 }
68 }
69
70 pub fn id(mut self, ident: impl Into<Ident>) -> Self {
71 self.ident = Some(ident.into());
72 self
73 }
74
75 pub fn tone(mut self, tone: Tone) -> Self {
76 self.tone = tone;
77 self
78 }
79
80 pub fn neutral(self) -> Self {
81 self.tone(Tone::Neutral)
82 }
83
84 pub fn accent(self) -> Self {
85 self.tone(Tone::Accent)
86 }
87
88 pub fn success(self) -> Self {
89 self.tone(Tone::Success)
90 }
91
92 pub fn warning(self) -> Self {
93 self.tone(Tone::Warning)
94 }
95
96 pub fn danger(self) -> Self {
97 self.tone(Tone::Danger)
98 }
99
100 pub fn info(self) -> Self {
101 self.tone(Tone::Info)
102 }
103}
104
105impl RenderOnce for Badge {
106 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
107 let theme = cx.theme().clone();
108 let (foreground, background) = match self.tone {
114 Tone::Neutral => (theme.colors.text_muted, theme.colors.raised),
115 tone => {
116 let color = tone.color(&theme);
117 (color, color.opacity(0.18))
118 }
119 };
120
121 let element = div()
122 .flex_none()
123 .px_token(&theme, gpui_kit_theme::Space::Sm)
124 .py(px(2.0))
125 .rounded_full()
126 .bg(background)
127 .type_scale(&theme, TypeScale::Caption)
128 .text_color(foreground)
129 .child(self.label.clone());
130 match self.ident {
131 Some(ident) => element
132 .semantic_in(
133 cx,
134 NodeSpec::new(ident.semantic_id(), Role::Status).text(self.label.clone()),
135 )
136 .into_any_element(),
137 None => element.into_any_element(),
138 }
139 }
140}