use leptos::callback::Callback;
use leptos::prelude::*;
use crate::context::use_hovered_item;
use crate::engine::{arc_label_position, PieSliceLayout};
use crate::shared::motion::ArcSweepSlice;
use crate::{
ArcLabelMode, ChartItemId, FadeMode, HighlightMode, HighlightScope, PieArcLabelConfig,
};
#[component]
pub fn PieSlice(
layout: PieSliceLayout,
#[prop(into)]
fill: String,
#[prop(into)]
series_id: String,
cx: f64,
cy: f64,
inner_r: f64,
outer_r: f64,
#[prop(default = None)]
arc_label: Option<PieArcLabelConfig>,
#[prop(default = None)]
highlight_scope: Option<HighlightScope>,
#[prop(default = false)]
skip_animation: bool,
#[prop(default = orbital_motion::MotionDuration::Normal)]
sweep_duration: orbital_motion::MotionDuration,
#[prop(into)]
draw_key: Signal<String>,
#[prop(default = None)]
on_item_click: Option<Callback<(ChartItemId,), ()>>,
) -> impl IntoView {
let hovered = use_hovered_item();
let item_id = ChartItemId {
series_id: series_id.clone(),
data_index: layout.index,
};
let scope = highlight_scope.unwrap_or(HighlightScope {
highlight: HighlightMode::Item,
fade: FadeMode::Global,
});
let item_id_for_hover = item_id.clone();
let slice_class = move || {
let mut classes = vec![
"orb-pie-slice-group".to_string(),
"orb-pie-slice".to_string(),
];
let h = hovered.get();
let is_hovered = h.as_ref() == Some(&item_id_for_hover);
match scope.highlight {
HighlightMode::Item if is_hovered => classes.push("orb-pie-slice-highlighted".into()),
HighlightMode::Item if h.is_some() && scope.fade == FadeMode::Global => {
classes.push("orb-pie-slice-faded".into());
}
_ => {}
}
classes.join(" ")
};
let label_text = arc_label_text(&layout, arc_label.as_ref());
let show_label = label_text.is_some()
&& layout.angle_deg >= arc_label.as_ref().and_then(|c| c.min_angle).unwrap_or(0.0);
let label_radius = arc_label
.as_ref()
.and_then(|c| c.radius.as_ref())
.map(|r| r.resolve(outer_r * 2.0, outer_r * 2.0, true))
.unwrap_or((inner_r + outer_r) / 2.0);
let (lx, ly) = arc_label_position(cx, cy, layout.mid_rad, label_radius);
let fill_signal: Signal<String> = Signal::from(fill);
let on_click = on_item_click;
let series_for_click = series_id.clone();
let index = layout.index;
view! {
<g
class=slice_class
style="pointer-events: all; cursor: pointer;"
on:click=move |ev| {
if let Some(cb) = on_click.as_ref() {
cb.run((ChartItemId {
series_id: series_for_click.clone(),
data_index: index,
},));
}
let _ = ev;
}
on:mouseenter=move |_| hovered.set(Some(item_id.clone()))
on:mouseleave=move |_| hovered.set(None)
>
<ArcSweepSlice
cx=cx
cy=cy
inner_r=inner_r
outer_r=outer_r
start_rad=layout.start_rad
end_rad=layout.end_rad
full_path=layout.path_d.clone()
fill=fill_signal
skip_animation=skip_animation
duration=sweep_duration
draw_key=draw_key
/>
{show_label.then(|| {
let text = label_text.clone().unwrap_or_default();
view! {
<text
class="orb-pie-label"
x=lx
y=ly
text-anchor="middle"
dominant-baseline="central"
>
{text}
</text>
}
})}
</g>
}
}
fn arc_label_text(layout: &PieSliceLayout, config: Option<&PieArcLabelConfig>) -> Option<String> {
let mode = config
.and_then(|c| c.mode.as_ref())
.unwrap_or(&ArcLabelMode::FormattedValue);
match mode {
ArcLabelMode::Value => Some(format!("{:.0}", layout.value)),
ArcLabelMode::FormattedValue => Some(format!("{:.1}", layout.value)),
ArcLabelMode::Label => Some(layout.label.clone()),
}
}