add progress

This commit is contained in:
2026-10-28 19:58:01 +07:00
parent 5f441518eb
commit 9626391403
4 changed files with 157 additions and 0 deletions
+3
View File
@@ -22,3 +22,6 @@ pub mod link;
#[cfg(feature = "slider")]
pub mod slider;
#[cfg(feature = "progress")]
pub mod progress;
+101
View File
@@ -0,0 +1,101 @@
//! The progress bar: determinate fill or an indeterminate sweep.
use std::time::Duration;
use gpui::prelude::FluentBuilder as _;
use gpui::{
Animation, AnimationExt as _, App, ElementId, InteractiveElement as _, IntoElement,
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
Styled as _, Window, div, relative,
};
use gpui_native_theme::ActiveTheme as _;
/// A progress bar. With a `value` in `0..=1` it paints a determinate fill.
#[derive(IntoElement)]
pub struct Progress {
id: ElementId,
value: f32,
indeterminate: bool,
a11y_label: Option<SharedString>,
}
impl Progress {
/// Creates a determinate progress bar at `value` (`0..=1`).
pub fn new<I>(id: I, value: f32) -> Self
where
I: Into<ElementId>,
{
Self {
id: id.into(),
value,
indeterminate: false,
a11y_label: None,
}
}
/// Sweeps instead of painting a fill, for work of unknown length.
pub fn indeterminate(mut self, indeterminate: bool) -> Self {
self.indeterminate = indeterminate;
self
}
/// Sets the accessibility label.
pub fn accessibility_label<L>(mut self, label: L) -> Self
where
L: Into<SharedString>,
{
self.a11y_label = Some(label.into());
self
}
}
impl RenderOnce for Progress {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
let colors = theme.colors;
let ramps = theme.ramps;
let radius = theme.radius;
let value = self.value.clamp(0.0, 1.0);
div()
.id(self.id)
.w_full()
.role(Role::ProgressIndicator)
.when_some(self.a11y_label, |progress, label| {
progress.aria_label(label)
})
.when(!self.indeterminate, |progress| {
progress
.aria_numeric_value(f64::from(value))
.aria_min_numeric_value(0.0)
.aria_max_numeric_value(1.0)
})
.child(
div()
.w_full()
.h_1p5()
.rounded(radius.full)
.bg(colors.border)
.child(if self.indeterminate {
div()
.h_full()
.w(relative(0.3))
.rounded(radius.full)
.bg(ramps.primary.base)
.with_animation(
"indeterminate",
Animation::new(Duration::from_millis(1500)).repeat(),
|pill, delta| pill.ml(relative(delta * 0.7)),
)
.into_any_element()
} else {
div()
.h_full()
.w(relative(value))
.rounded(radius.full)
.bg(ramps.primary.base)
.into_any_element()
}),
)
}
}