orbital_charts/shared/layers/
axis_click_layer.rs1use leptos::prelude::*;
4
5use crate::context::{is_series_visible, use_chart_context, use_x_scale, use_y_scale};
6use crate::engine::projected_for_plot_type;
7use crate::{AxisClickData, ChartOrientation, ChartScale, ChartType};
8
9#[component]
11pub fn AxisClickLayer(
12 #[prop(default = None)]
14 axis_id: Option<String>,
15 #[prop(default = None)]
17 orientation: Option<ChartOrientation>,
18) -> impl IntoView {
19 let ctx = use_chart_context();
20 let on_axis = ctx.on_axis_click;
21 let orient = orientation.unwrap_or(ctx.orientation);
22 let projected = ctx.projected.clone();
23 let series_defs = ctx.series.clone();
24 let plot_width = ctx.drawing_area.plot_width;
25 let plot_height = ctx.drawing_area.plot_height;
26
27 let category_axis_id = axis_id.unwrap_or_else(|| match orient {
28 ChartOrientation::Vertical => "x".to_string(),
29 ChartOrientation::Horizontal => "y".to_string(),
30 });
31
32 let category_scale = match orient {
33 ChartOrientation::Vertical => use_x_scale(category_axis_id.clone()),
34 ChartOrientation::Horizontal => use_y_scale(category_axis_id.clone()),
35 };
36
37 view! {
38 {move || {
39 if on_axis.is_none() {
40 return ().into_any();
41 }
42 let Some(data) = projected.as_ref() else {
43 return ().into_any();
44 };
45 let plot_data = projected_for_plot_type(data, &series_defs, ChartType::Bar);
46 if plot_data.categories.is_empty() {
47 return ().into_any();
48 }
49
50 let bands = axis_click_bands(
51 orient,
52 &plot_data.categories,
53 &category_scale,
54 plot_width,
55 plot_height,
56 );
57
58 bands
59 .into_iter()
60 .map(|band| {
61 let axis_id = category_axis_id.clone();
62 let on_axis = on_axis;
63 let projected = projected.clone();
64 let series_defs = series_defs.clone();
65 view! {
66 <rect
67 class="orb-axis-click-band"
68 x=band.x
69 y=band.y
70 width=band.w
71 height=band.h
72 on:click=move |ev| {
73 ev.stop_propagation();
74 if let Some(cb) = &on_axis {
75 let series_values = projected
76 .as_ref()
77 .map(|p| {
78 let plot_data = projected_for_plot_type(
79 p,
80 &series_defs,
81 ChartType::Bar,
82 );
83 plot_data
84 .series
85 .iter()
86 .filter(|s| is_series_visible(&s.id))
87 .map(|s| {
88 (
89 s.id.clone(),
90 s.data.get(band.index).copied().unwrap_or(0.0),
91 )
92 })
93 .collect()
94 })
95 .unwrap_or_default();
96 cb.run((AxisClickData {
97 axis_id: axis_id.clone(),
98 value: band.index as f64,
99 series_values,
100 },));
101 }
102 }
103 />
104 }
105 })
106 .collect_view()
107 .into_any()
108 }}
109 }
110}
111
112struct ClickBand {
113 x: f64,
114 y: f64,
115 w: f64,
116 h: f64,
117 index: usize,
118}
119
120fn axis_click_bands(
121 orient: ChartOrientation,
122 categories: &[String],
123 category_scale: &ChartScale,
124 plot_width: f64,
125 plot_height: f64,
126) -> Vec<ClickBand> {
127 let ChartScale::Band(band) = category_scale else {
128 return Vec::new();
129 };
130 let bw = band.bandwidth();
131 categories
132 .iter()
133 .enumerate()
134 .filter_map(|(i, cat)| {
135 band.scale(cat).map(|center| match orient {
136 ChartOrientation::Vertical => ClickBand {
137 x: center - bw / 2.0,
138 y: 0.0,
139 w: bw,
140 h: plot_height,
141 index: i,
142 },
143 ChartOrientation::Horizontal => ClickBand {
144 x: 0.0,
145 y: center - bw / 2.0,
146 w: plot_width,
147 h: bw,
148 index: i,
149 },
150 })
151 })
152 .collect()
153}