Skip to main content

leptix_primitives/components/
progress.rs

1use 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}