Skip to main content

dioxus_leaflet/components/
map.rs

1use crate::{LatLng, MapOptions, MapPosition, interop, types::Id};
2use dioxus::{
3    core::{spawn_forever, use_drop},
4    prelude::*,
5};
6use std::rc::Rc;
7
8const MAP_CSS: Asset = asset!("/assets/dioxus_leaflet.scss");
9
10/// Main map component using Leaflet
11#[component]
12pub fn Map(
13    /// Initial position of the map
14    #[props(default = MapPosition::default())]
15    initial_position: MapPosition,
16
17    /// Height of the map container
18    #[props(into)]
19    height: Option<String>,
20
21    /// Width of the map container
22    #[props(into)]
23    width: Option<String>,
24
25    /// Map configuration options
26    options: Option<MapOptions>,
27
28    /// Custom CSS class for the map container
29    #[props(into)]
30    class: Option<String>,
31
32    /// Custom CSS styles for the map container
33    #[props(into)]
34    style: Option<String>,
35
36    /// Callback when map is clicked
37    on_click: Option<EventHandler<LatLng>>,
38
39    /// Callback when map is moved
40    on_move: Option<EventHandler<MapPosition>>,
41
42    children: Element,
43) -> Element {
44    let id = use_context_provider(|| Rc::new(Id::map(dioxus_core::current_scope_id().0)));
45    let options = options.unwrap_or(MapOptions::default());
46    let leaflet_css = options.leaflet_resources.css_url();
47    let leaflet_js = options.leaflet_resources.js_url();
48    let height = height.unwrap_or_else(|| "400px".to_string());
49    let width = width.unwrap_or_else(|| "100%".to_string());
50    let style = style.unwrap_or_default();
51    let class = class.unwrap_or_default();
52
53    let id2 = id.clone();
54    let load_error = use_resource(move || {
55        let id = id2.clone();
56        let pos = initial_position;
57        let opts = options.clone();
58        async move {
59            interop::update_map(&id, &pos, &opts)
60                .await
61                .map_err(|e| e.to_string())
62        }
63    });
64
65    let id2 = id.clone();
66    let _click_handle = use_resource(move || {
67        let id = id2.clone();
68        async move {
69            if let Some(on_click) = on_click {
70                interop::on_map_click(&id, on_click).await
71            } else {
72                Ok(())
73            }
74        }
75    });
76
77    let id2 = id.clone();
78    let _move_handle = use_resource(move || {
79        let id = id2.clone();
80        async move {
81            if let Some(on_move) = on_move {
82                interop::on_map_move(&id, on_move).await
83            } else {
84                Ok(())
85            }
86        }
87    });
88
89    let id2 = id.clone();
90    use_drop(move || {
91        let id = id2.clone();
92        spawn_forever(async move {
93            _ = interop::delete_map(&id).await;
94        });
95    });
96
97    rsx! {
98        // Leaflet CSS
99        document::Style { href: leaflet_css }
100
101        document::Style { href: MAP_CSS }
102
103        // Leaflet JavaScript
104        document::Script { src: leaflet_js }
105
106        // boot logic
107        document::Script { src: interop::DL_JS, r#type: "module" }
108
109        if let Some(Err(err)) = load_error() {
110            p { "{err}" }
111        } else {
112            // Keep size/style on the wrapping container so Leaflet can mount reliably.
113            div {
114                class: "dioxus-leaflet-container {class}",
115                style: "height: {height}; width: {width}; {style}",
116
117                // Element taken over by leaflet
118                div {
119                    id: "dioxus-leaflet-map-{id.id()}",
120                    class: "dioxus-leaflet-map",
121                    {children}
122                }
123            }
124        }
125    }
126}