use dioxus::prelude::*;
use crate::state::{use_overlay_inset, FlowCore, Interaction};
use crate::types::{Point, Rect, Side};
#[component]
pub fn MiniMap(
#[props(default = 200.0)] width: f64,
#[props(default = 140.0)] height: f64,
class: Option<String>,
) -> Element {
let core = use_context::<FlowCore>();
use_overlay_inset(Side::Bottom, height + 26.0);
let content = use_memo(move || {
let geoms = core.geoms.read();
geoms
.iter()
.map(|geom| geom.rect)
.reduce(|acc, rect| acc.union(&rect))
});
let vp = *core.viewport.read();
let container = *core.container.read();
let Some(content) = *content.read() else {
return rsx! {};
};
let visible = Rect::from_points(
vp.screen_to_flow(Point::ZERO),
(container.width / vp.zoom, container.height / vp.zoom).into(),
);
let world = content.union(&visible);
let pad = (world.width.max(world.height) * 0.05).max(10.0);
let world = Rect::new(
world.x - pad,
world.y - pad,
world.width + 2.0 * pad,
world.height + 2.0 * pad,
);
let class = format!(
"df-minimap{}",
class
.as_deref()
.map(|c| format!(" {c}"))
.unwrap_or_default()
);
let on_pointer_down = move |evt: Event<PointerData>| {
evt.stop_propagation();
core.interaction.clone().set(Interaction::Pressed);
let p = evt.element_coordinates();
let scale = (width / world.width).min(height / world.height);
let dx = (width - world.width * scale) / 2.0;
let dy = (height - world.height * scale) / 2.0;
let flow = Point::new(world.x + (p.x - dx) / scale, world.y + (p.y - dy) / scale);
core.center_on(flow, 250);
};
let on_pointer_up = move |_| {
let mut interaction = core.interaction;
if *interaction.peek() == Interaction::Pressed {
interaction.set(Interaction::None);
}
};
rsx! {
svg {
class,
width,
height,
view_box: "{world.x} {world.y} {world.width} {world.height}",
preserve_aspect_ratio: "xMidYMid meet",
"role": "img",
"aria-label": "Graph overview; click to move the view",
onpointerdown: on_pointer_down,
onpointerup: on_pointer_up,
MiniMapNodes {}
rect {
class: "df-minimap-viewport",
x: visible.x,
y: visible.y,
width: visible.width,
height: visible.height,
}
}
}
}
#[component]
fn MiniMapNodes() -> Element {
let core = use_context::<FlowCore>();
let paths = use_memo(move || {
let geoms = core.geoms.read();
let mut plain = String::new();
let mut selected = String::new();
for geom in geoms.iter() {
let r = geom.rect;
if !(r.x.is_finite() && r.y.is_finite() && r.width.is_finite() && r.height.is_finite())
{
continue;
}
let into = if geom.selected {
&mut selected
} else {
&mut plain
};
use std::fmt::Write;
let _ = write!(
into,
"M{} {}h{}v{}h{}z",
r.x, r.y, r.width, r.height, -r.width
);
}
(plain, selected)
});
let (plain, selected) = paths.read().clone();
rsx! {
if !plain.is_empty() {
path { class: "df-minimap-node", d: plain }
}
if !selected.is_empty() {
path { class: "df-minimap-node df-selected", d: selected }
}
}
}