progress.rs
1 use dioxus::html::GlobalAttributesExtension; 2 use dioxus::prelude::*; 3 use dioxus_primitives::progress::Progress as ProgressPrimitive; 4 5 /// Progress size variants 6 #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] 7 pub enum ProgressSize { 8 Small, 9 #[default] 10 Medium, 11 Large, 12 } 13 14 /// Progress color variants 15 #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] 16 pub enum ProgressVariant { 17 #[default] 18 Default, 19 Destructive, 20 Success, 21 Warning, 22 } 23 24 /// Props for the Progress component 25 #[derive(Props, Clone, PartialEq)] 26 pub struct ProgressProps { 27 /// The current progress value, between 0 and max 28 value: ReadSignal<f64>, 29 30 /// The maximum value. Defaults to 100 31 #[props(default = 100.0)] 32 max: f64, 33 34 /// Size variant of the progress bar 35 #[props(default)] 36 pub size: ProgressSize, 37 38 /// Color variant of the progress bar 39 #[props(default)] 40 pub variant: ProgressVariant, 41 42 /// Optional ID for the progress element 43 #[props(default)] 44 pub id: Option<String>, 45 46 /// Accessible label for the progress 47 #[props(default)] 48 pub aria_label: Option<String>, 49 50 /// Whether to show the percentage text 51 #[props(default = false)] 52 pub show_percentage: bool, 53 54 /// Custom class names for the progress container 55 #[props(default)] 56 pub class: Option<String>, 57 } 58 59 /// A styled progress component for showing completion progress 60 #[component] 61 pub fn Progress(props: ProgressProps) -> Element { 62 // Calculate percentage 63 let current: f64 = (props.value)(); 64 let max_value = props.max; 65 let percentage = (current / max_value * 100.0).clamp(0.0, 100.0); 66 // An adapter to convert signal type from `f64` to `Option<f64>` 67 let value_optional = use_memo(move || Some((props.value)())); 68 69 // Determine size-specific classes 70 let height_class = match props.size { 71 ProgressSize::Small => "h-2", 72 ProgressSize::Medium => "h-3", 73 ProgressSize::Large => "h-4", 74 }; 75 76 // Determine color variant classes 77 let indicator_color = match props.variant { 78 ProgressVariant::Default => "bg-primary", 79 ProgressVariant::Destructive => "bg-destructive", 80 ProgressVariant::Success => "bg-green-500", 81 ProgressVariant::Warning => "bg-yellow-500", 82 }; 83 84 // Build container classes 85 let container_class = format!( 86 "relative w-full overflow-hidden rounded-full bg-secondary {}", 87 height_class 88 ); 89 90 let combined_class = if let Some(custom_class) = &props.class { 91 format!("{} {}", container_class, custom_class) 92 } else { 93 container_class 94 }; 95 96 // Build indicator classes 97 let indicator_class = format!( 98 "h-full transition-all duration-300 ease-in-out {}", 99 indicator_color 100 ); 101 102 rsx! { 103 div { class: "w-full space-y-2", 104 if props.show_percentage { 105 div { class: "flex justify-between text-sm text-muted-foreground", 106 span { 107 if let Some(label) = &props.aria_label { 108 "{label}" 109 } else { 110 "Progress" 111 } 112 } 113 span { "{percentage:.0}%" } 114 } 115 } 116 117 ProgressPrimitive { 118 value: value_optional, 119 max: props.max, 120 class: combined_class, 121 id: props.id.clone(), 122 123 div { 124 class: indicator_class, 125 style: format!("width: {}%", percentage), 126 } 127 } 128 } 129 } 130 }