mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
Feat/progress circle (#124)
* feat: Adds ProgressCircle component * feat: ProgressCircle adds children prop * feat: ProgressCircle adds class and size prop
This commit is contained in:
parent
0f499a67a9
commit
a44d0a9c38
4 changed files with 176 additions and 7 deletions
|
@ -19,11 +19,40 @@ view! {
|
|||
}
|
||||
```
|
||||
|
||||
### Circle
|
||||
|
||||
```rust demo
|
||||
let percentage = create_rw_signal(0.0f32);
|
||||
|
||||
view! {
|
||||
<Space>
|
||||
<ProgressCircle percentage/>
|
||||
<ProgressCircle percentage color=ProgressColor::Success/>
|
||||
<ProgressCircle percentage color=ProgressColor::Warning/>
|
||||
<ProgressCircle percentage color=ProgressColor::Error/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button on_click=move |_| percentage.update(|v| *v -= 10.0)>"-10%"</Button>
|
||||
<Button on_click=move |_| percentage.update(|v| *v += 10.0)>"+10%"</Button>
|
||||
</Space>
|
||||
}
|
||||
```
|
||||
|
||||
### Progress Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ------------------- | ----------------------------------------- | ------------------------------------- | ------------------------------ |
|
||||
| --- | --- | --- | --- |
|
||||
| percentage | `MaybeSignal<f32>` | `Default::default()` | Percentage value. |
|
||||
| color | `MaybeSignal<ProgressColor>` | `ProgressColor::Primary` | Progress color. |
|
||||
| show_indicator | `MaybeSignal<bool>` | `true` | Whether to display indicators. |
|
||||
| indicator_placement | `MaybeSignal<ProgressIndicatorPlacement>` | `ProgressIndicatorPlacement::Outside` | Indicator placement. |
|
||||
|
||||
### ProgressCircle Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| class | `OptionalProp<MaybeSignal<String>>` | `Default::default()` | Addtional classes for the progress element. |
|
||||
| percentage | `MaybeSignal<f32>` | `Default::default()` | Percentage value. |
|
||||
| color | `MaybeSignal<ProgressColor>` | `ProgressColor::Primary` | ProgressCircle color. |
|
||||
| size | `MaybeSignal<Stringr>` | `120px` | ProgressCircle size. |
|
||||
| children | `Option<Children>` | `None` | ProgressCircle's content. |
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
mod progress_circle;
|
||||
mod theme;
|
||||
|
||||
pub use progress_circle::ProgressCircle;
|
||||
pub use theme::ProgressTheme;
|
||||
|
||||
use crate::{use_theme, utils::mount_style, Theme};
|
||||
use leptos::*;
|
||||
pub use theme::ProgressTheme;
|
||||
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub enum ProgressIndicatorPlacement {
|
||||
|
|
30
thaw/src/progress/progress-circle.css
Normal file
30
thaw/src/progress/progress-circle.css
Normal file
|
@ -0,0 +1,30 @@
|
|||
.thaw-progress-circle {
|
||||
width: var(--thaw-size);
|
||||
height: var(--thaw-size);
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.thaw-progress-circle__fill {
|
||||
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
stroke 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
stroke-dasharray 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.thaw-progress-circle__fill--empty {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.thaw-progress-circle__content {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.thaw-progress-circle__content--text {
|
||||
font-size: 28px;
|
||||
}
|
107
thaw/src/progress/progress_circle.rs
Normal file
107
thaw/src/progress/progress_circle.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use super::ProgressColor;
|
||||
use crate::{
|
||||
use_theme,
|
||||
utils::{class_list::class_list, mount_style, OptionalProp},
|
||||
Theme,
|
||||
};
|
||||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
pub fn ProgressCircle(
|
||||
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
||||
#[prop(into, optional)] percentage: MaybeSignal<f32>,
|
||||
#[prop(into, optional)] color: MaybeSignal<ProgressColor>,
|
||||
#[prop(into, default = "120px".into())] size: MaybeSignal<String>,
|
||||
#[prop(optional)] children: Option<Children>,
|
||||
) -> impl IntoView {
|
||||
mount_style("progress-circle", include_str!("./progress-circle.css"));
|
||||
let theme = use_theme(Theme::light);
|
||||
|
||||
let stroke_width = 7;
|
||||
let view_box_width = 100;
|
||||
|
||||
let radius = 50;
|
||||
let begin_position_x = 0;
|
||||
let begin_position_y = radius;
|
||||
let end_position_x = 0;
|
||||
let end_position_y = 2 * radius;
|
||||
let center_x = 50 + stroke_width / 2;
|
||||
let rail_path = format!("M {center_x},{center_x} m {begin_position_x},{begin_position_y} a {radius},{radius} 0 1 1 {end_position_x},{} a {radius},{radius} 0 1 1 {},{end_position_y}", -end_position_y, -end_position_x);
|
||||
|
||||
let len = std::f64::consts::PI * 2.0 * f64::from(radius);
|
||||
let rail_stroke_dasharray = format!("{len}px {}px", view_box_width * 8);
|
||||
let rail_stroke_color =
|
||||
Memo::new(move |_| theme.with(|theme| theme.progress.background_color.clone()));
|
||||
|
||||
let fill_path = rail_path.clone();
|
||||
let fill_stroke_dasharray = Memo::new(move |_| {
|
||||
let percentage = percentage.get();
|
||||
let percentage = if percentage < 0.0 {
|
||||
0.0
|
||||
} else if percentage > 100.0 {
|
||||
100.0
|
||||
} else {
|
||||
percentage
|
||||
};
|
||||
format!(
|
||||
"{}px {}px",
|
||||
f64::from(percentage / 100.0) * len,
|
||||
view_box_width * 8
|
||||
)
|
||||
});
|
||||
let fill_stroke_color =
|
||||
Memo::new(move |_| theme.with(|theme| color.get().theme_background_color(theme)));
|
||||
|
||||
view! {
|
||||
<div
|
||||
class=class_list!["thaw-progress-circle", class.map(|c| move || c.get())]
|
||||
role="progressbar"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow=move || percentage.get()
|
||||
style=("--thaw-fill-color", move || fill_stroke_color.get())
|
||||
style=("--thaw-size", move || size.get())
|
||||
|
||||
>
|
||||
<svg viewBox="0 0 107 107">
|
||||
<g>
|
||||
<path
|
||||
d=rail_path
|
||||
stroke-width=stroke_width
|
||||
stroke-linecap="round"
|
||||
fill="none"
|
||||
style:stroke=move || rail_stroke_color.get()
|
||||
style:stroke-dasharray=rail_stroke_dasharray
|
||||
></path>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
class=("thaw-progress-circle__fill", true)
|
||||
class=("thaw-progress-circle__fill--empty", move || percentage.get() <= 0.0)
|
||||
d=fill_path
|
||||
stroke-width=stroke_width
|
||||
stroke-linecap="round"
|
||||
fill="none"
|
||||
style:stroke="var(--thaw-fill-color)"
|
||||
style:stroke-dasharray=move || fill_stroke_dasharray.get()
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
{
|
||||
if let Some(children) = children {
|
||||
view! {
|
||||
<div class="thaw-progress-circle__content">
|
||||
{children()}
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
view! {
|
||||
<div class="thaw-progress-circle__content thaw-progress-circle__content--text">
|
||||
{move || percentage.get()}"%"
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue