leptos_leaflet/components/
image_overlay.rs

1use super::{use_pane_context, Bounds, LeafletMapContext};
2use crate::core::IntoThreadSafeJsValue;
3use leptos::logging::log;
4use leptos::prelude::*;
5
6/// An image overlay component.
7#[component(transparent)]
8pub fn ImageOverlay(
9    #[prop(into)] url: String,
10    #[prop(into)] bounds: Bounds,
11    #[prop(into, optional)] opacity: Option<Signal<f64>>,
12    #[prop(into, optional)] alt: Option<Signal<String>>,
13    #[prop(into, optional)] interactive: Option<Signal<bool>>,
14    #[prop(into, optional)] cross_origin: Option<Signal<String>>,
15    #[prop(into, optional)] cross_origin_toggle: Option<Signal<bool>>,
16    #[prop(into, optional)] error_overlay_url: Option<Signal<String>>,
17    #[prop(into, optional)] z_index: Option<Signal<f64>>,
18    #[prop(into, optional)] class_name: Option<Signal<String>>,
19    #[prop(into, optional)] bubbling_mouse_events: Option<Signal<bool>>,
20    #[prop(into, optional)] pane: Option<Signal<String>>,
21    #[prop(into, optional)] attribution: Option<Signal<String>>,
22) -> impl IntoView {
23    let map_context = use_context::<LeafletMapContext>().expect("map context not found");
24    Effect::new(move |_| {
25        if let Some(map) = map_context.map() {
26            log!("Adding image layer: {}", url);
27            let options = leaflet::ImageOverlayOptions::new();
28            if let Some(opacity) = opacity {
29                options.set_opacity(opacity.get_untracked());
30            }
31            if let Some(alt) = &alt {
32                options.set_alt(alt.get_untracked());
33            }
34            if let Some(interactive) = interactive {
35                options.set_interactive(interactive.get_untracked());
36            }
37            if let Some(cross_origin) = &cross_origin {
38                options.set_cross_origin(cross_origin.get_untracked());
39            }
40            if let Some(cross_origin_toggle) = cross_origin_toggle {
41                options.set_cross_origin_toggle(cross_origin_toggle.get_untracked());
42            }
43            if let Some(error_overlay_url) = &error_overlay_url {
44                options.set_error_overlay_url(error_overlay_url.get_untracked());
45            }
46            if let Some(z_index) = z_index {
47                options.set_z_index(z_index.get_untracked());
48            }
49            if let Some(class_name) = &class_name {
50                options.set_class_name(class_name.get_untracked());
51            }
52            if let Some(bubbling_mouse_events) = &bubbling_mouse_events {
53                options.set_bubbling_mouse_events(bubbling_mouse_events.get_untracked());
54            }
55
56            // Use explicit pane if provided, otherwise use pane context if available
57            if let Some(pane) = &pane {
58                let pane_value = pane.get_untracked();
59                if !pane_value.is_empty() {
60                    options.set_pane(pane_value);
61                }
62            } else if let Some(pane_context) = use_pane_context() {
63                options.set_pane(pane_context.name().to_string());
64            }
65            if let Some(attribution) = &attribution {
66                options.set_attribution(attribution.get_untracked());
67            }
68
69            let map_layer = leaflet::ImageOverlay::new_with_options(
70                &url,
71                &bounds.as_lat_lng_bounds(),
72                &options,
73            )
74            .into_thread_safe_js_value();
75            map_layer.add_to(&map);
76            on_cleanup(move || {
77                map_layer.remove();
78            });
79        }
80    });
81}