1use dioxus::prelude::*;
4
5use crate::state::{use_overlay_inset, FlowCore, Interaction};
6use crate::types::{Point, Rect, Side};
7
8#[component]
11pub fn MiniMap(
12 #[props(default = 200.0)] width: f64,
13 #[props(default = 140.0)] height: f64,
14 class: Option<String>,
15) -> Element {
16 let core = use_context::<FlowCore>();
17 use_overlay_inset(Side::Bottom, height + 26.0);
20 let geoms = core.geoms.read();
21 let vp = *core.viewport.read();
22 let container = *core.container.read();
23
24 if geoms.is_empty() {
26 return rsx! {};
27 }
28
29 let visible = Rect::from_points(
31 vp.screen_to_flow(Point::ZERO),
32 (container.width / vp.zoom, container.height / vp.zoom).into(),
33 );
34 let mut world = visible;
35 for geom in geoms.iter() {
36 world = world.union(&geom.rect);
37 }
38 let pad = (world.width.max(world.height) * 0.05).max(10.0);
40 let world = Rect::new(
41 world.x - pad,
42 world.y - pad,
43 world.width + 2.0 * pad,
44 world.height + 2.0 * pad,
45 );
46
47 let class = format!(
48 "df-minimap{}",
49 class
50 .as_deref()
51 .map(|c| format!(" {c}"))
52 .unwrap_or_default()
53 );
54
55 let on_pointer_down = move |evt: Event<PointerData>| {
56 evt.stop_propagation();
57 core.interaction.clone().set(Interaction::Pressed);
58 let p = evt.element_coordinates();
61 let scale = (width / world.width).min(height / world.height);
62 let dx = (width - world.width * scale) / 2.0;
63 let dy = (height - world.height * scale) / 2.0;
64 let flow = Point::new(world.x + (p.x - dx) / scale, world.y + (p.y - dy) / scale);
65 core.center_on(flow, 250);
66 };
67 let on_pointer_up = move |_| {
68 let mut interaction = core.interaction;
69 if *interaction.peek() == Interaction::Pressed {
70 interaction.set(Interaction::None);
71 }
72 };
73
74 rsx! {
75 svg {
76 class,
77 width,
78 height,
79 view_box: "{world.x} {world.y} {world.width} {world.height}",
80 preserve_aspect_ratio: "xMidYMid meet",
81 "role": "img",
82 "aria-label": "Graph overview; click to move the view",
83 onpointerdown: on_pointer_down,
84 onpointerup: on_pointer_up,
85 MiniMapNodes {}
86 rect {
87 class: "df-minimap-viewport",
88 x: visible.x,
89 y: visible.y,
90 width: visible.width,
91 height: visible.height,
92 }
93 }
94 }
95}
96
97#[component]
101fn MiniMapNodes() -> Element {
102 let core = use_context::<FlowCore>();
103 let geoms = core.geoms.read();
104 rsx! {
105 for geom in geoms.iter() {
106 rect {
107 class: if geom.selected { "df-minimap-node df-selected" } else { "df-minimap-node" },
108 x: geom.rect.x,
109 y: geom.rect.y,
110 width: geom.rect.width,
111 height: geom.rect.height,
112 rx: 2.0,
113 }
114 }
115 }
116}