Skip to main content

fret_ui_kit/primitives/
progress.rs

1//! Progress primitives (Radix-aligned outcomes).
2//!
3//! Upstream reference:
4//! - `repo-ref/primitives/packages/react/progress/src/progress.tsx`
5//!
6//! Radix progress is conceptually a value clamped into a range, where the value can be absent
7//! (indeterminate). Fret's shadcn recipe uses these helpers to compute the visual fill fraction.
8
9/// Normalizes a progress value into the `[0, 1]` range.
10pub fn normalize_progress(value: f32, min: f32, max: f32) -> f32 {
11    if !value.is_finite() || !min.is_finite() || !max.is_finite() {
12        return 0.0;
13    }
14    let span = max - min;
15    if !span.is_finite() || span.abs() <= f32::EPSILON {
16        return 0.0;
17    }
18    ((value - min) / span).clamp(0.0, 1.0)
19}
20
21/// Normalizes an optional progress value; `None` represents indeterminate.
22pub fn normalize_progress_opt(value: Option<f32>, min: f32, max: f32) -> Option<f32> {
23    value.map(|v| normalize_progress(v, min, max))
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn normalize_progress_clamps_to_unit_interval() {
32        assert_eq!(normalize_progress(-1.0, 0.0, 10.0), 0.0);
33        assert_eq!(normalize_progress(0.0, 0.0, 10.0), 0.0);
34        assert_eq!(normalize_progress(5.0, 0.0, 10.0), 0.5);
35        assert_eq!(normalize_progress(10.0, 0.0, 10.0), 1.0);
36        assert_eq!(normalize_progress(999.0, 0.0, 10.0), 1.0);
37    }
38
39    #[test]
40    fn normalize_progress_handles_degenerate_ranges() {
41        assert_eq!(normalize_progress(5.0, 0.0, 0.0), 0.0);
42        assert_eq!(normalize_progress(f32::NAN, 0.0, 1.0), 0.0);
43    }
44
45    #[test]
46    fn normalize_progress_opt_preserves_none() {
47        assert_eq!(normalize_progress_opt(None, 0.0, 10.0), None);
48        assert_eq!(normalize_progress_opt(Some(5.0), 0.0, 10.0), Some(0.5));
49    }
50}