use leptos::prelude::*;
use orbital_motion::{MotionCurve, MotionDuration};
use crate::engine::{arc_path_d, arc_path_d_sweep};
use crate::GaugeLayout;
#[component]
pub fn GaugeValueArcMotion(
layout: GaugeLayout,
#[prop(into)]
fill: String,
#[prop(default = false)]
skip_animation: bool,
#[prop(default = MotionDuration::Normal)]
duration: MotionDuration,
#[prop(default = MotionCurve::DecelerateMax)]
curve: MotionCurve,
#[prop(into)]
draw_key: Signal<String>,
) -> impl IntoView {
let sweep_fraction = RwSignal::new(if skip_animation { 1.0 } else { 0.0 });
let layout_stored = StoredValue::new(layout.clone());
Effect::new(move |_| {
let _ = draw_key.get();
if skip_animation {
sweep_fraction.set(1.0);
return;
}
sweep_fraction.set(0.0);
animate_sweep(sweep_fraction, duration);
});
let path_d = move || {
let layout = layout_stored.get_value();
let Some(value_rad) = layout.value_rad else {
return String::new();
};
let frac = sweep_fraction.get();
if frac >= 1.0 {
arc_path_d(
layout.cx,
layout.cy,
layout.inner_radius,
layout.outer_radius,
layout.start_rad,
value_rad,
)
} else {
arc_path_d_sweep(
layout.cx,
layout.cy,
layout.inner_radius,
layout.outer_radius,
layout.start_rad,
value_rad,
frac,
)
}
};
let style = move || {
format!(
"fill: {}; transition: opacity {} {};",
fill,
duration.ms(),
curve.css_var(),
)
};
view! {
<path class="orb-gauge-fill" d=path_d style=style />
}
}
#[component]
pub fn GaugeReferenceArc(
layout: GaugeLayout,
#[prop(into, default = "var(--orb-color-border-subtle, #e5e7eb)".into())]
fill: String,
) -> impl IntoView {
let d = arc_path_d(
layout.cx,
layout.cy,
layout.inner_radius,
layout.outer_radius,
layout.start_rad,
layout.end_rad,
);
view! {
<path class="orb-gauge-track" d=d fill=fill />
}
}
fn animate_sweep(signal: RwSignal<f64>, duration: MotionDuration) {
#[cfg(feature = "hydrate")]
{
let ms = duration
.ms()
.trim_end_matches("ms")
.parse::<u32>()
.unwrap_or(300);
let steps = 20u32;
let step_ms = (ms / steps).max(16);
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
let mut step = 0u32;
fn tick(signal: RwSignal<f64>, step: &mut u32, steps: u32, step_ms: u32) {
*step += 1;
let frac = (*step as f64 / steps as f64).min(1.0);
signal.set(frac);
if *step < steps {
if let Some(win) = web_sys::window() {
let s = signal;
let mut st = *step;
let cb = Closure::once(move || {
tick(s, &mut st, steps, step_ms);
});
let _ = win.set_timeout_with_callback_and_timeout_and_arguments_0(
cb.as_ref().unchecked_ref(),
step_ms as i32,
);
cb.forget();
}
}
}
tick(signal, &mut step, steps, step_ms);
}
#[cfg(not(feature = "hydrate"))]
{
let _ = duration;
signal.set(1.0);
}
}