leptos_leaflet/components/
map_container.rs1use leaflet::Map;
2use leptos::{html::Div, prelude::*};
3use wasm_bindgen::prelude::*;
4use web_sys::HtmlDivElement;
5
6use leaflet::LocateOptions;
7
8use crate::core::JsWriteSignal;
9
10use super::{provide_leaflet_context, MapEvents, PopupEvents, Position, TooltipEvents};
11
12#[component]
17pub fn MapContainer(
18 #[prop(into, optional)] class: Signal<String>,
19 #[prop(into, optional)] style: Signal<String>,
20 #[prop(into, optional)]
22 center: Option<Position>,
23 #[prop(optional, default = 10.0)]
25 zoom: f64,
26 #[prop(optional, default = true)]
28 zoom_control: bool,
29 #[prop(optional, default = true)]
31 scroll_wheel_zoom: bool,
32 #[prop(optional, default = 1.0)]
34 zoom_snap: f64,
35 #[prop(optional, default = 1.0)]
37 zoom_delta: f64,
38 #[prop(optional, default = true)]
40 double_click_zoom: bool,
41 #[prop(optional, default = 0.0)]
43 min_zoom: f64,
44 #[prop(optional)]
46 locate: bool,
47 #[prop(optional)]
49 watch: bool,
50 #[prop(optional)]
52 enable_high_accuracy: bool,
53 #[prop(optional)]
55 set_view: bool,
56 #[prop(optional)] map: Option<JsWriteSignal<Option<Map>>>,
57 #[prop(optional)] events: MapEvents,
58 #[prop(optional)] popup_events: PopupEvents,
59 #[prop(optional)] tooltip_events: TooltipEvents,
60 #[prop(optional)]
62 node_ref: Option<NodeRef<Div>>,
63 #[prop(optional)]
65 prefer_canvas: bool,
66 #[prop(optional)]
68 children: Option<Children>,
69) -> impl IntoView {
70 let map_ref = node_ref.unwrap_or_default();
71 let map_context = provide_leaflet_context();
72
73 let map_load = map_ref;
74 Effect::new(move |_| {
75 if let Some(map_div) = map_load.get() {
76 let html_node = map_div.unchecked_ref::<HtmlDivElement>();
77 if html_node.id().is_empty() {
79 let id = format!("map-{}", (js_sys::Math::random() * 1000.0) as u32);
80 map_div.set_id(&id);
81 }
82 let events = events.clone();
83 let popup_events = popup_events.clone();
84 let tooltip_events = tooltip_events.clone();
85
86 let options = leaflet::MapOptions::new();
87 options.set_prefer_canvas(prefer_canvas);
88 options.set_zoom_control(zoom_control);
89 options.set_scroll_wheel_zoom(scroll_wheel_zoom);
90 options.set_zoom(zoom);
91 options.set_zoom_snap(zoom_snap);
92 options.set_zoom_delta(zoom_delta);
93 options.set_double_click_zoom(JsValue::from_bool(double_click_zoom));
94 options.set_min_zoom(min_zoom);
95 if let Some(center) = center {
96 options.set_center(center.as_lat_lng());
97 }
98 let Ok(leaflet_map) = Map::new(&html_node.id(), &options) else {
99 tracing::error!("Failed to create map");
100 return;
101 };
102
103 events.setup(&leaflet_map);
105 popup_events.setup(&leaflet_map);
106 tooltip_events.setup(&leaflet_map);
107
108 if locate {
109 let mut locate_options = LocateOptions::new();
110 locate_options.enable_high_accuracy(enable_high_accuracy);
111 locate_options.set_view(set_view);
112 locate_options.watch(watch);
113 leaflet_map.locate_with_options(&locate_options);
114 }
115
116 map_context.set_map(&leaflet_map);
117 if let Some(map) = map {
118 map.set(Some(leaflet_map));
119 }
120 };
121 });
122
123 on_cleanup(move || {
124 if let Some(map) = map_context.map_untracked().as_ref() {
125 map.remove();
126 };
127 });
128
129 view! { <div class=move || class.get() node_ref=map_ref style=move || style.get()>{children.map(|child|child())}</div>}
130}
131
132#[derive(Debug, Default, Clone)]
133pub struct LeafletMap {
134 #[cfg(not(feature = "ssr"))]
135 pub map: Option<leaflet::Map>,
136}
137
138impl LeafletMap {
139 pub fn new() -> Self {
140 Self {
141 #[cfg(not(feature = "ssr"))]
142 map: None,
143 }
144 }
145}