forked from reya/gpui-native-kit
feat: add status dot component
This commit is contained in:
@@ -72,6 +72,9 @@ pub mod spinner;
|
||||
#[cfg(feature = "field")]
|
||||
pub mod field;
|
||||
|
||||
#[cfg(feature = "status_dot")]
|
||||
pub mod status_dot;
|
||||
|
||||
#[cfg(feature = "toast")]
|
||||
pub mod toast;
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
//! The status dot: a small colored bead for row state.
|
||||
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
App, ElementId, Hsla, InteractiveElement as _, IntoElement, RenderOnce, SharedString,
|
||||
StatefulInteractiveElement as _, StyleRefinement, Styled, Window, div,
|
||||
};
|
||||
use gpui_native_theme::{ActiveTheme as _, Size, Theme};
|
||||
|
||||
use crate::common::{Sizable, StyledExt};
|
||||
|
||||
/// How loud a dot is, the same vocabulary as [`crate::badge::BadgeVariant`].
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum StatusDotVariant {
|
||||
#[default]
|
||||
Neutral,
|
||||
Accent,
|
||||
Success,
|
||||
Warning,
|
||||
Danger,
|
||||
}
|
||||
|
||||
impl StatusDotVariant {
|
||||
fn color(self, theme: &Theme) -> Hsla {
|
||||
match self {
|
||||
Self::Neutral => theme.colors.muted_foreground,
|
||||
Self::Accent => theme.ramps.primary.base,
|
||||
Self::Success => theme.ramps.success.base,
|
||||
Self::Warning => theme.ramps.warning.base,
|
||||
Self::Danger => theme.ramps.destructive.base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A small circular bead marking a row's state.
|
||||
#[derive(IntoElement)]
|
||||
pub struct StatusDot {
|
||||
id: ElementId,
|
||||
variant: StatusDotVariant,
|
||||
size: Size,
|
||||
label: Option<SharedString>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
impl StatusDot {
|
||||
/// Creates an idle dot.
|
||||
pub fn new<I>(id: I) -> Self
|
||||
where
|
||||
I: Into<ElementId>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
variant: StatusDotVariant::default(),
|
||||
size: Size::default(),
|
||||
label: None,
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets how loud the dot is.
|
||||
pub fn variant(mut self, variant: StatusDotVariant) -> Self {
|
||||
self.variant = variant;
|
||||
self
|
||||
}
|
||||
|
||||
/// The state the dot announces to screen readers.
|
||||
pub fn label<L>(mut self, label: L) -> Self
|
||||
where
|
||||
L: Into<SharedString>,
|
||||
{
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for StatusDot {
|
||||
fn apply_size(&mut self, size: Size) {
|
||||
self.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for StatusDot {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for StatusDot {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
let side = metrics.icon_size / 2.;
|
||||
let color = self.variant.color(theme);
|
||||
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.debug_selector(move || self.id.to_string())
|
||||
.flex_none()
|
||||
.size(side)
|
||||
.rounded(theme.radius.full)
|
||||
.bg(color)
|
||||
.when_some(self.label, |dot, label| dot.aria_label(label))
|
||||
.refine_style(&self.style)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user