use crate::theme::{Theme, ThemeExt};
use gpui::prelude::*;
use gpui::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BadgeVariant {
#[default]
Default,
Primary,
Success,
Warning,
Error,
Info,
}
impl BadgeVariant {
fn colors(&self, theme: &Theme) -> (Rgba, Rgba) {
match self {
BadgeVariant::Default => (theme.surface, theme.text_secondary),
BadgeVariant::Primary => (theme.badge_primary_bg, theme.badge_primary_text),
BadgeVariant::Success => (theme.badge_success_bg, theme.badge_success_text),
BadgeVariant::Warning => (theme.badge_warning_bg, theme.badge_warning_text),
BadgeVariant::Error => (theme.badge_error_bg, theme.badge_error_text),
BadgeVariant::Info => (theme.badge_info_bg, theme.badge_info_text),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BadgeSize {
Sm,
#[default]
Md,
Lg,
}
impl From<crate::ComponentSize> for BadgeSize {
fn from(size: crate::ComponentSize) -> Self {
match size {
crate::ComponentSize::Xs | crate::ComponentSize::Sm => Self::Sm,
crate::ComponentSize::Md => Self::Md,
crate::ComponentSize::Lg | crate::ComponentSize::Xl => Self::Lg,
}
}
}
#[derive(IntoElement)]
pub struct Badge {
label: SharedString,
variant: BadgeVariant,
size: BadgeSize,
rounded: bool,
icon: Option<SharedString>,
}
impl Badge {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
label: label.into(),
variant: BadgeVariant::default(),
size: BadgeSize::default(),
rounded: false,
icon: None,
}
}
pub fn variant(mut self, variant: BadgeVariant) -> Self {
self.variant = variant;
self
}
pub fn size(mut self, size: BadgeSize) -> Self {
self.size = size;
self
}
pub fn rounded(mut self, rounded: bool) -> Self {
self.rounded = rounded;
self
}
pub fn icon(mut self, icon: impl Into<SharedString>) -> Self {
self.icon = Some(icon.into());
self
}
pub fn build_with_theme(self, theme: &Theme) -> Div {
let (bg, text_color) = self.variant.colors(theme);
let (px_val, py_val) = match self.size {
BadgeSize::Sm => (px(6.0), px(2.0)),
BadgeSize::Md => (px(8.0), px(3.0)),
BadgeSize::Lg => (px(12.0), px(4.0)),
};
let mut badge = div()
.flex()
.items_center()
.gap_1()
.px(px_val)
.py(py_val)
.bg(bg)
.text_color(text_color);
badge = match self.size {
BadgeSize::Sm => badge.text_xs(),
BadgeSize::Md => badge.text_xs(),
BadgeSize::Lg => badge.text_sm(),
};
if self.rounded {
badge = badge.rounded_full();
} else {
badge = badge.rounded(px(3.0));
}
if let Some(icon) = self.icon {
badge = badge.child(div().child(icon));
}
badge = badge.child(self.label);
badge
}
}
impl RenderOnce for Badge {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
self.build_with_theme(&theme)
}
}
#[derive(IntoElement)]
pub struct BadgeDot {
variant: BadgeVariant,
size: Pixels,
}
impl BadgeDot {
pub fn new() -> Self {
Self {
variant: BadgeVariant::default(),
size: px(8.0),
}
}
pub fn variant(mut self, variant: BadgeVariant) -> Self {
self.variant = variant;
self
}
pub fn size(mut self, size: Pixels) -> Self {
self.size = size;
self
}
pub fn build_with_theme(self, theme: &Theme) -> Div {
let (bg, _) = self.variant.colors(theme);
div().w(self.size).h(self.size).rounded_full().bg(bg)
}
}
impl Default for BadgeDot {
fn default() -> Self {
Self::new()
}
}
impl RenderOnce for BadgeDot {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
self.build_with_theme(&theme)
}
}