orbital_charts/context/
pointer.rs1use leptos::ev::MouseEvent;
4use leptos::prelude::*;
5
6use crate::context::{
7 set_hovered_item, use_chart_context, use_x_scale, use_y_scale, ChartInteractionContext,
8 ChartScale,
9};
10use crate::engine::{category_index_at_x, find_bar_at_pointer, find_nearest_marker};
11use crate::{ChartItemId, ChartOrientation, TooltipTrigger};
12
13#[component]
15pub fn ChartPointerLayer(
16 #[prop(default = TooltipTrigger::Item)] trigger: TooltipTrigger,
17) -> impl IntoView {
18 let ctx = use_chart_context();
19 let plot_w = ctx.drawing_area.plot_width;
20 let plot_h = ctx.drawing_area.plot_height;
21 let inset_left = ctx.drawing_area.left;
22 let inset_top = ctx.drawing_area.top;
23 let orientation = ctx.orientation;
24
25 let category_scale = match orientation {
26 ChartOrientation::Vertical => use_x_scale("x".to_string()),
27 ChartOrientation::Horizontal => use_y_scale("y".to_string()),
28 };
29
30 let interaction = expect_context::<ChartInteractionContext>();
31 let pointer_events = if matches!(trigger, TooltipTrigger::None) {
32 "none"
33 } else {
34 "all"
35 };
36
37 view! {
38 <rect
39 class="orb-pointer-layer"
40 x=0
41 y=0
42 width=plot_w
43 height=plot_h
44 fill="transparent"
45 style=format!("pointer-events: {pointer_events}; cursor: crosshair;")
46 on:mousemove=move |ev: MouseEvent| {
47 let px = ev.offset_x() as f64;
48 let py = ev.offset_y() as f64;
49 interaction
50 .pointer_plot
51 .set(Some((inset_left + px, inset_top + py)));
52
53 let bars = interaction.plot_bars.get_untracked();
54 let markers = interaction.plot_line_markers.get_untracked();
55
56 if matches!(trigger, TooltipTrigger::Axis) {
57 if let ChartScale::Band(band) = category_scale.clone() {
58 interaction
59 .axis_data_index
60 .set(category_index_at_x(&band, px));
61 }
62 if let Some(hit) = find_bar_at_pointer(&bars, px, py) {
63 set_hovered_item(Some(ChartItemId {
64 series_id: hit.series_id,
65 data_index: hit.data_index,
66 }));
67 } else if let Some(hit) = find_nearest_marker(px, py, &markers, 12.0) {
68 set_hovered_item(Some(ChartItemId {
69 series_id: hit.series_id,
70 data_index: hit.data_index,
71 }));
72 } else {
73 set_hovered_item(None);
74 }
75 return;
76 }
77
78 if let Some(hit) = find_bar_at_pointer(&bars, px, py) {
79 set_hovered_item(Some(ChartItemId {
80 series_id: hit.series_id,
81 data_index: hit.data_index,
82 }));
83 } else if let Some(hit) = find_nearest_marker(px, py, &markers, 12.0) {
84 set_hovered_item(Some(ChartItemId {
85 series_id: hit.series_id,
86 data_index: hit.data_index,
87 }));
88 } else {
89 set_hovered_item(None);
90 }
91 interaction.axis_data_index.set(None);
92 }
93 on:mouseleave=move |_| {
94 interaction.pointer_plot.set(None);
95 interaction.axis_data_index.set(None);
96 set_hovered_item(None);
97 }
98 />
99 }
100}
101
102pub fn use_pointer_tracker() -> ReadSignal<Option<(f64, f64)>> {
104 expect_context::<ChartInteractionContext>()
105 .pointer_plot
106 .read_only()
107}