use std::collections::HashSet;
use leptos::callback::Callback;
use leptos::prelude::*;
use crate::engine::BarGeometry;
use crate::ChartItemId;
#[derive(Clone, Copy)]
pub struct ChartInteractionContext {
pub hovered_item: RwSignal<Option<ChartItemId>>,
pub highlighted_item: RwSignal<Option<ChartItemId>>,
pub hidden_series: RwSignal<HashSet<String>>,
pub pointer_plot: RwSignal<Option<(f64, f64)>>,
pub axis_data_index: RwSignal<Option<usize>>,
pub plot_bars: RwSignal<Vec<BarGeometry>>,
pub plot_line_markers: RwSignal<Vec<(f64, f64, String, usize)>>,
pub on_highlight_change: Option<Callback<(Option<ChartItemId>,), ()>>,
}
impl ChartInteractionContext {
pub fn new(on_highlight_change: Option<Callback<(Option<ChartItemId>,), ()>>) -> Self {
Self {
hovered_item: RwSignal::new(None),
highlighted_item: RwSignal::new(None),
hidden_series: RwSignal::new(HashSet::new()),
pointer_plot: RwSignal::new(None),
axis_data_index: RwSignal::new(None),
plot_bars: RwSignal::new(Vec::new()),
plot_line_markers: RwSignal::new(Vec::new()),
on_highlight_change,
}
}
}
impl Default for ChartInteractionContext {
fn default() -> Self {
Self::new(None)
}
}
pub fn use_hovered_item() -> RwSignal<Option<ChartItemId>> {
expect_context::<ChartInteractionContext>().hovered_item
}
pub fn set_hovered_item(item: Option<ChartItemId>) {
let ctx = expect_context::<ChartInteractionContext>();
ctx.hovered_item.set(item);
sync_highlight_from_hover(ctx);
}
pub fn use_highlighted_item() -> RwSignal<Option<ChartItemId>> {
expect_context::<ChartInteractionContext>().highlighted_item
}
pub fn set_highlighted_item(item: Option<ChartItemId>) {
let ctx = expect_context::<ChartInteractionContext>();
if let Some(cb) = ctx.on_highlight_change.as_ref() {
cb.run((item.clone(),));
}
ctx.highlighted_item.set(item);
}
pub fn use_hidden_series() -> RwSignal<HashSet<String>> {
expect_context::<ChartInteractionContext>().hidden_series
}
pub fn toggle_series_visibility(series_id: &str) {
use_hidden_series().update(|set| {
if set.contains(series_id) {
set.remove(series_id);
} else {
set.insert(series_id.to_string());
}
});
}
pub fn is_series_visible(series_id: &str) -> bool {
!use_hidden_series().with(|set| set.contains(series_id))
}
pub fn use_pointer_plot() -> ReadSignal<Option<(f64, f64)>> {
expect_context::<ChartInteractionContext>()
.pointer_plot
.read_only()
}
pub fn use_axis_data_index() -> ReadSignal<Option<usize>> {
expect_context::<ChartInteractionContext>()
.axis_data_index
.read_only()
}
fn sync_highlight_from_hover(ctx: ChartInteractionContext) {
let item = ctx.hovered_item.get();
if let Some(cb) = ctx.on_highlight_change.as_ref() {
cb.run((item.clone(),));
}
ctx.highlighted_item.set(item);
}
#[component]
pub fn ChartInteractionProvider(
#[prop(default = None)]
highlighted_item: Option<RwSignal<Option<ChartItemId>>>,
#[prop(default = None)]
on_highlight_change: Option<Callback<(Option<ChartItemId>,), ()>>,
children: Children,
) -> impl IntoView {
let mut ctx = ChartInteractionContext::new(on_highlight_change);
if let Some(external) = highlighted_item {
ctx.highlighted_item = external;
}
provide_context(ctx);
children()
}