leptix_primitives/components/
progress.rs1use leptos::{html::AnyElement, *};
2
3use crate::{primitive::Primitive, util::Attributes};
4
5const DEFAULT_MAX: f64 = 100.0;
6
7#[derive(Clone)]
8struct ProgressContextValue {
9 value: Signal<Option<f64>>,
10 max: Signal<f64>,
11}
12
13#[component]
14pub fn ProgressRoot(
15 #[prop(optional, into)] value: MaybeProp<f64>,
16 #[prop(default=100.0f64.into(), into)] max: MaybeSignal<f64>,
17
18 #[prop(optional)] get_value_label: Option<Callback<(f64, f64), String>>,
19
20 #[prop(optional)] node_ref: NodeRef<AnyElement>,
21 #[prop(attrs)] attrs: Attributes,
22 children: ChildrenFn,
23
24 #[prop(optional, into)] as_child: MaybeProp<bool>,
25) -> impl IntoView {
26 let max = Signal::derive(move || {
27 let max = max.get();
28
29 if !max.is_nan() && max > 0.0 {
30 max
31 } else {
32 DEFAULT_MAX
33 }
34 });
35
36 let value = Signal::derive(move || {
37 let max = max.get();
38
39 value
40 .get()
41 .and_then(|value| (!value.is_nan() && value <= max && value >= 0.0).then_some(value))
42 });
43
44 let get_value_label = get_value_label.unwrap_or(Callback::new(|(value, max): (f64, f64)| {
45 format!("{}%", (value / max).round() * 100.0)
46 }));
47
48 let value_label = Signal::derive(move || {
49 value
50 .get()
51 .map(|value| get_value_label.call((value, max.get())))
52 });
53
54 provide_context(ProgressContextValue {
55 value,
56 max: Signal::derive(move || max.get()),
57 });
58
59 view! {
60 <Primitive
61 {..attrs}
62 attr:role="progressbar"
63 attr:aria-valuemax=max
64 attr:aria-valuemin=0
65 attr:aria-valuenow=value
66 attr:aria-valuetext=value_label
67 attr:data-state=move || {
68 value
69 .get()
70 .map(|value| {
71 if value == max.get() {
72 "complete"
73 } else {
74 "loading"
75 }
76 })
77 .unwrap_or("indeterminate")
78 }
79 attr:data-value=value
80 attr:data-max=max
81 element=html::div
82 node_ref=node_ref
83 as_child=as_child
84 >
85 {children()}
86 </Primitive>
87 }
88}
89
90#[component]
91pub fn ProgressIndicator(
92 #[prop(optional)] node_ref: NodeRef<AnyElement>,
93 #[prop(attrs)] attrs: Attributes,
94 #[prop(optional)] children: Option<ChildrenFn>,
95
96 #[prop(optional, into)] as_child: MaybeProp<bool>,
97) -> impl IntoView {
98 let ProgressContextValue { max, value } =
99 use_context().expect("ProgressIndicator needs to be in a Progress component");
100
101 let children = StoredValue::new(children);
102
103 view! {
104 <Primitive
105 {..attrs}
106 attr:data-state=move || {
107 value
108 .get()
109 .map(|value| {
110 if value == max.get() {
111 "complete"
112 } else {
113 "loading"
114 }
115 })
116 .unwrap_or("indeterminate")
117 }
118 attr:data-value=value
119 attr:data-max=max
120 element=html::div
121 node_ref=node_ref
122 as_child=as_child
123 >
124 {children.with_value(|children| children.as_ref().map(|children| children()))}
125 </Primitive>
126 }
127}