1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// Tooltip component for heatmap cells
use leptos::prelude::*;
/// Floating tooltip that displays the row/column label and value for a hovered heatmap cell.
#[component]
pub fn HeatmapTooltip(
/// Hovered row label (None = hidden)
row_label: Signal<Option<String>>,
/// Hovered column label (None = hidden)
col_label: Signal<Option<String>>,
/// Hovered cell value (None = hidden)
value: Signal<Option<f64>>,
/// Mouse X position in chart-inner coordinates
x: Signal<f64>,
/// Mouse Y position in chart-inner coordinates
y: Signal<f64>,
/// Inner width of the chart area (used for edge-flipping)
inner_width: Signal<f64>,
/// Inner height of the chart area (used for edge-flipping)
inner_height: Signal<f64>,
/// Text color
#[prop(into, optional)]
tooltip_text: Option<Signal<String>>,
/// Tooltip background color
#[prop(into, optional)]
tooltip_bg: Option<Signal<String>>,
) -> impl IntoView {
let tooltip_bg = move || {
tooltip_bg
.map(|s| s.get())
.unwrap_or_else(|| "rgba(0,0,0,0.85)".to_string())
};
let tooltip_text = move || {
tooltip_text
.map(|s| s.get())
.unwrap_or_else(|| "#ffffff".to_string())
};
view! {
{move || {
let row = row_label.get()?;
let col = col_label.get()?;
let val = value.get()?;
let mx = x.get();
let my = y.get();
let w = inner_width.get();
let h = inner_height.get();
let tc = tooltip_text();
let label = format!("({row}, {col}): {val:.3}");
let box_w = 8.0 + (label.len() as f64) * 6.5;
let box_h = 28.0;
let bx = if mx + box_w + 12.0 > w { mx - box_w - 12.0 } else { mx + 12.0 };
let by = if my + box_h + 8.0 > h { my - box_h - 8.0 } else { my + 8.0 };
Some(
view! {
<g class="heatmap-tooltip" style="pointer-events: none;">
<rect
x=format!("{bx:.2}")
y=format!("{by:.2}")
width=format!("{box_w:.2}")
height=box_h
rx="4"
fill=tooltip_bg()
/>
<text
x=format!("{:.2}", bx + 8.0)
y=format!("{:.2}", by + 18.0)
font-size="11"
fill=tc.clone()
font-family="'JetBrains Mono', monospace"
>
{label}
</text>
</g>
},
)
}}
}
}