orbital_charts/context/
interaction.rs1use std::collections::HashSet;
4
5use leptos::callback::Callback;
6use leptos::prelude::*;
7
8use crate::engine::BarGeometry;
9use crate::ChartItemId;
10
11#[derive(Clone, Copy)]
13pub struct ChartInteractionContext {
14 pub hovered_item: RwSignal<Option<ChartItemId>>,
16 pub highlighted_item: RwSignal<Option<ChartItemId>>,
18 pub hidden_series: RwSignal<HashSet<String>>,
20 pub pointer_plot: RwSignal<Option<(f64, f64)>>,
22 pub axis_data_index: RwSignal<Option<usize>>,
24 pub plot_bars: RwSignal<Vec<BarGeometry>>,
26 pub plot_line_markers: RwSignal<Vec<(f64, f64, String, usize)>>,
28 pub on_highlight_change: Option<Callback<(Option<ChartItemId>,), ()>>,
30}
31
32impl ChartInteractionContext {
33 pub fn new(on_highlight_change: Option<Callback<(Option<ChartItemId>,), ()>>) -> Self {
35 Self {
36 hovered_item: RwSignal::new(None),
37 highlighted_item: RwSignal::new(None),
38 hidden_series: RwSignal::new(HashSet::new()),
39 pointer_plot: RwSignal::new(None),
40 axis_data_index: RwSignal::new(None),
41 plot_bars: RwSignal::new(Vec::new()),
42 plot_line_markers: RwSignal::new(Vec::new()),
43 on_highlight_change,
44 }
45 }
46}
47
48impl Default for ChartInteractionContext {
49 fn default() -> Self {
50 Self::new(None)
51 }
52}
53
54pub fn use_hovered_item() -> RwSignal<Option<ChartItemId>> {
56 expect_context::<ChartInteractionContext>().hovered_item
57}
58
59pub fn set_hovered_item(item: Option<ChartItemId>) {
61 let ctx = expect_context::<ChartInteractionContext>();
62 ctx.hovered_item.set(item);
63 sync_highlight_from_hover(ctx);
64}
65
66pub fn use_highlighted_item() -> RwSignal<Option<ChartItemId>> {
68 expect_context::<ChartInteractionContext>().highlighted_item
69}
70
71pub fn set_highlighted_item(item: Option<ChartItemId>) {
73 let ctx = expect_context::<ChartInteractionContext>();
74 if let Some(cb) = ctx.on_highlight_change.as_ref() {
75 cb.run((item.clone(),));
76 }
77 ctx.highlighted_item.set(item);
78}
79
80pub fn use_hidden_series() -> RwSignal<HashSet<String>> {
82 expect_context::<ChartInteractionContext>().hidden_series
83}
84
85pub fn toggle_series_visibility(series_id: &str) {
87 use_hidden_series().update(|set| {
88 if set.contains(series_id) {
89 set.remove(series_id);
90 } else {
91 set.insert(series_id.to_string());
92 }
93 });
94}
95
96pub fn is_series_visible(series_id: &str) -> bool {
98 !use_hidden_series().with(|set| set.contains(series_id))
99}
100
101pub fn use_pointer_plot() -> ReadSignal<Option<(f64, f64)>> {
103 expect_context::<ChartInteractionContext>()
104 .pointer_plot
105 .read_only()
106}
107
108pub fn use_axis_data_index() -> ReadSignal<Option<usize>> {
110 expect_context::<ChartInteractionContext>()
111 .axis_data_index
112 .read_only()
113}
114
115fn sync_highlight_from_hover(ctx: ChartInteractionContext) {
116 let item = ctx.hovered_item.get();
117 if let Some(cb) = ctx.on_highlight_change.as_ref() {
118 cb.run((item.clone(),));
119 }
120 ctx.highlighted_item.set(item);
121}
122
123#[component]
125pub fn ChartInteractionProvider(
126 #[prop(default = None)]
128 highlighted_item: Option<RwSignal<Option<ChartItemId>>>,
129 #[prop(default = None)]
131 on_highlight_change: Option<Callback<(Option<ChartItemId>,), ()>>,
132 children: Children,
133) -> impl IntoView {
134 let mut ctx = ChartInteractionContext::new(on_highlight_change);
135 if let Some(external) = highlighted_item {
136 ctx.highlighted_item = external;
137 }
138 provide_context(ctx);
139 children()
140}