dioxus_element_plug/components/
progress.rs1use dioxus::prelude::*;
2
3#[derive(Clone, PartialEq)]
5pub enum ProgressType {
6 Line,
7 Circle,
8 Dashboard,
9}
10
11impl ProgressType {
12 pub fn as_str(&self) -> &'static str {
13 match self {
14 ProgressType::Line => "line",
15 ProgressType::Circle => "circle",
16 ProgressType::Dashboard => "dashboard",
17 }
18 }
19}
20
21#[derive(Clone, PartialEq)]
23pub enum ProgressStatus {
24 Default,
25 Success,
26 Exception,
27 Warning,
28}
29
30impl ProgressStatus {
31 pub fn as_class(&self) -> &'static str {
32 match self {
33 ProgressStatus::Default => "",
34 ProgressStatus::Success => "is-success",
35 ProgressStatus::Exception => "is-exception",
36 ProgressStatus::Warning => "is-warning",
37 }
38 }
39}
40
41#[derive(Props, Clone, PartialEq)]
43pub struct ProgressProps {
44 #[props(default = ProgressType::Line)]
46 pub progress_type: ProgressType,
47
48 #[props(default = 0)]
50 pub percentage: u32,
51
52 #[props(default = ProgressStatus::Default)]
54 pub status: ProgressStatus,
55
56 #[props(default = false)]
58 pub indeterminate: bool,
59
60 #[props(default = 6)]
62 pub stroke_width: u32,
63
64 #[props(default = false)]
66 pub text_inside: bool,
67
68 #[props(default = 126)]
70 pub width: u32,
71
72 #[props(default = true)]
74 pub show_text: bool,
75
76 #[props(default)]
78 pub color: Option<String>,
79
80 #[props(default = false)]
82 pub striped: bool,
83
84 #[props(default = false)]
86 pub striped_flow: bool,
87
88 #[props(default)]
90 pub class: Option<String>,
91
92 #[props(default)]
94 pub style: Option<String>,
95}
96
97#[component]
107pub fn Progress(props: ProgressProps) -> Element {
108 let percentage = props.percentage.min(100);
109
110 let mut class_names = vec!["el-progress".to_string()];
111 class_names.push(format!("el-progress--{}", props.progress_type.as_str()));
112
113 let status_class = props.status.as_class();
114 if !status_class.is_empty() {
115 class_names.push(status_class.to_string());
116 }
117
118 if props.indeterminate {
119 class_names.push("el-progress--indeterminate".to_string());
120 }
121
122 if props.striped {
123 class_names.push("el-progress--striped".to_string());
124 }
125
126 if props.striped_flow {
127 class_names.push("el-progress--striped-flow".to_string());
128 }
129
130 if props.text_inside && props.progress_type == ProgressType::Line {
131 class_names.push("el-progress--text-inside".to_string());
132 }
133
134 if let Some(ref custom_class) = props.class {
135 class_names.push(custom_class.clone());
136 }
137
138 let class_string = class_names.join(" ");
139 let style_string = props.style.clone().unwrap_or_default();
140
141 let bar_color = props.color.clone().unwrap_or_else(|| {
142 match props.status {
143 ProgressStatus::Success => "#67C23A".to_string(),
144 ProgressStatus::Exception => "#F56C6C".to_string(),
145 ProgressStatus::Warning => "#E6A23C".to_string(),
146 ProgressStatus::Default => "#409EFF".to_string(),
147 }
148 });
149
150 let display_text = format!("{}%", percentage);
151
152 if props.progress_type == ProgressType::Line {
153 let bar_style = format!("width: {}%; background-color: {};", percentage, bar_color);
154 let height_style = format!("height: {}px;", props.stroke_width);
155
156 rsx! {
157 div {
158 class: "{class_string}",
159 style: "{style_string}",
160 div {
161 class: "el-progress__outer",
162 style: "{height_style}",
163 div {
164 class: "el-progress__inner",
165 div {
166 class: "el-progress__bar",
167 style: "{bar_style}",
168 if props.text_inside && props.show_text {
169 div {
170 class: "el-progress__text el-progress__text--inside",
171 "{display_text}"
172 }
173 }
174 }
175 }
176 }
177 if props.show_text && !props.text_inside {
178 div {
179 class: "el-progress__text",
180 if props.status == ProgressStatus::Success {
181 i { class: "el-icon-circle-check" }
182 } else if props.status == ProgressStatus::Exception {
183 i { class: "el-icon-circle-close" }
184 } else if props.status == ProgressStatus::Warning {
185 i { class: "el-icon-warning" }
186 } else {
187 "{display_text}"
188 }
189 }
190 }
191 }
192 }
193 } else {
194 let size = props.width;
196 let deg = (percentage as f64 / 100.0) * 360.0;
197 let bg_gradient = if props.progress_type == ProgressType::Dashboard {
198 format!("conic-gradient(from 135deg, {} 0deg, {} {}deg, transparent {}deg)",
199 bar_color, bar_color, deg * 0.75, deg * 0.75)
200 } else {
201 format!("conic-gradient({} {}deg, var(--el-fill-color-light) {}deg)",
202 bar_color, deg, deg)
203 };
204
205 let inner_size = size - props.stroke_width * 2;
206
207 rsx! {
208 div {
209 class: "{class_string}",
210 style: "{style_string}",
211 div {
212 class: "el-progress-circle",
213 style: "width: {size}px; height: {size}px; border-radius: 50%; background: {bg_gradient}; display: flex; align-items: center; justify-content: center;",
214 div {
215 style: "width: {inner_size}px; height: {inner_size}px; border-radius: 50%; background: var(--el-bg-color); display: flex; align-items: center; justify-content: center;",
216 if props.show_text {
217 div {
218 class: "el-progress__text",
219 if props.status == ProgressStatus::Success {
220 i { class: "el-icon-circle-check" }
221 } else if props.status == ProgressStatus::Exception {
222 i { class: "el-icon-circle-close" }
223 } else {
224 "{display_text}"
225 }
226 }
227 }
228 }
229 }
230 }
231 }
232 }
233}