use leptos::callback::Callback;
use leptos::prelude::*;
use orbital_macros::component_doc;
use super::container::GaugeContainer;
use crate::engine::parse_gauge_radius;
use crate::{ChartMotion, GaugeGeometry, PieRadius};
#[derive(Clone)]
pub enum GaugeLabel {
Text(String),
Formatter(Callback<(Option<f64>, f64, f64), String>),
}
impl Default for GaugeLabel {
fn default() -> Self {
Self::Formatter(Callback::new(|(value, _, _): (Option<f64>, f64, f64)| {
value
.map(|v| {
if (v - v.round()).abs() < f64::EPSILON {
format!("{}", v.round() as i64)
} else {
format!("{v:.1}")
}
})
.unwrap_or_default()
}))
}
}
#[component_doc(
category = "Charts",
preview_slug = "gauge",
preview_label = "Gauge",
preview_icon = icondata::AiDashboardOutlined,
)]
#[component]
pub fn Gauge(
#[prop(default = None)]
value: Option<f64>,
#[prop(default = 0.0)]
value_min: f64,
#[prop(default = 100.0)]
value_max: f64,
#[prop(default = 0.0)]
start_angle: f64,
#[prop(default = 360.0)]
end_angle: f64,
#[prop(optional, into, default = String::new())]
inner_radius: String,
#[prop(optional, into, default = String::new())]
outer_radius: String,
#[prop(default = GaugeLabel::default())]
label: GaugeLabel,
#[prop(optional, into)]
aria_value_text: Option<String>,
#[prop(default = 200.0)]
width: f64,
#[prop(default = 200.0)]
height: f64,
#[prop(optional)]
skip_animation: Option<bool>,
#[prop(optional)]
motion: Option<ChartMotion>,
#[prop(optional, into)]
value_color: Option<String>,
#[prop(optional)]
children: Option<Children>,
#[prop(optional, into)]
class: MaybeProp<String>,
) -> impl IntoView {
let geometry = GaugeGeometry {
inner_radius: parse_gauge_radius(&inner_radius, PieRadius::Percent(80.0)),
outer_radius: parse_gauge_radius(&outer_radius, PieRadius::Percent(100.0)),
start_angle,
end_angle,
..Default::default()
};
let label_text = Memo::new(move |_| match &label {
GaugeLabel::Text(s) => s.clone(),
GaugeLabel::Formatter(cb) => cb.run((value, value_min, value_max)),
});
let show_label = value.is_some();
view! {
<GaugeContainer
value=value
value_min=value_min
value_max=value_max
geometry=geometry
width=width
height=height
skip_animation=skip_animation
motion=motion
value_color=value_color
aria_value_text=aria_value_text
class=class
>
{show_label.then(|| {
let text = label_text;
view! {
<foreignObject
x="0"
y="0"
width=width
height=height
style="pointer-events: none;"
>
<div
class="orb-gauge-value-host"
style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;"
>
<orbital_core_components::Text
font=orbital_core_components::TextFont::Numeric
>
{move || text.get()}
</orbital_core_components::Text>
</div>
</foreignObject>
}
})}
{children.map(|c| c())}
</GaugeContainer>
}
}