use dioxus::prelude::*;
use super::context::MapHandleSignal;
#[cfg(target_arch = "wasm32")]
use super::event_dispatch::MapEventHandlers;
#[cfg(target_arch = "wasm32")]
use crate::events::MapEvent;
use crate::events::{
LayerClickEvent, LayerHoverEvent, MapClickEvent, MapContextMenuEvent, MapDblClickEvent,
MapErrorEvent, MapMoveEvent, MapPitchEvent, MapRotateEvent, MapZoomEvent, MarkerClickEvent,
MarkerDragEndEvent, MarkerDragStartEvent, MarkerHoverEvent,
};
use crate::handle::MapHandle;
use crate::interop::generate_map_id;
use crate::types::{Bounds, LatLng};
#[derive(Props, Clone, PartialEq)]
pub struct MapProps {
#[props(default = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json".to_string())]
pub style: String,
#[props(default = LatLng::helsinki())]
pub center: LatLng,
#[props(default = 10.0)]
pub zoom: f64,
#[props(default = 0.0)]
pub bearing: f64,
#[props(default = 0.0)]
pub pitch: f64,
#[props(optional)]
pub min_zoom: Option<f64>,
#[props(optional)]
pub max_zoom: Option<f64>,
#[props(optional)]
pub max_bounds: Option<Bounds>,
#[props(optional)]
pub cooperative_gestures: Option<bool>,
#[props(default = "100%".to_string())]
pub height: String,
#[props(default = "100%".to_string())]
pub width: String,
#[props(default = 80)]
pub move_event_throttle_ms: u32,
#[props(optional)]
pub on_ready: Option<EventHandler<MapHandle>>,
#[props(optional)]
pub on_error: Option<EventHandler<MapErrorEvent>>,
#[props(optional)]
pub on_click: Option<EventHandler<MapClickEvent>>,
#[props(optional)]
pub on_dblclick: Option<EventHandler<MapDblClickEvent>>,
#[props(optional)]
pub on_contextmenu: Option<EventHandler<MapContextMenuEvent>>,
#[props(optional)]
pub on_marker_click: Option<EventHandler<MarkerClickEvent>>,
#[props(optional)]
pub on_marker_hover: Option<EventHandler<MarkerHoverEvent>>,
#[props(optional)]
pub on_marker_dragstart: Option<EventHandler<MarkerDragStartEvent>>,
#[props(optional)]
pub on_marker_dragend: Option<EventHandler<MarkerDragEndEvent>>,
#[props(optional)]
pub on_move: Option<EventHandler<MapMoveEvent>>,
#[props(optional)]
pub on_zoom: Option<EventHandler<MapZoomEvent>>,
#[props(optional)]
pub on_rotate: Option<EventHandler<MapRotateEvent>>,
#[props(optional)]
pub on_pitch: Option<EventHandler<MapPitchEvent>>,
#[props(optional)]
pub on_layer_click: Option<EventHandler<LayerClickEvent>>,
#[props(optional)]
pub on_layer_hover: Option<EventHandler<LayerHoverEvent>>,
pub children: Element,
}
#[component]
pub fn Map(props: MapProps) -> Element {
let map_id = use_hook(generate_map_id);
let container_id = format!("{map_id}_container");
#[allow(unused_variables, unused_mut)]
let mut init_started = use_signal(|| false);
#[allow(unused_mut)]
let mut map_handle_signal: MapHandleSignal = use_signal(|| None::<MapHandle>);
use_context_provider(|| map_handle_signal);
#[cfg(target_arch = "wasm32")]
{
use crate::interop::{destroy_map_js, init_map_js, set_move_event_throttle_js};
let handlers = MapEventHandlers {
on_ready: props.on_ready,
on_error: props.on_error,
on_click: props.on_click,
on_dblclick: props.on_dblclick,
on_contextmenu: props.on_contextmenu,
on_marker_click: props.on_marker_click,
on_marker_hover: props.on_marker_hover,
on_marker_dragstart: props.on_marker_dragstart,
on_marker_dragend: props.on_marker_dragend,
on_move: props.on_move,
on_zoom: props.on_zoom,
on_rotate: props.on_rotate,
on_pitch: props.on_pitch,
on_layer_click: props.on_layer_click,
on_layer_hover: props.on_layer_hover,
};
let style = props.style.clone();
let center = props.center;
let zoom = props.zoom;
let bearing = props.bearing;
let pitch = props.pitch;
let min_zoom = props.min_zoom;
let max_zoom = props.max_zoom;
let max_bounds = props.max_bounds;
let cooperative_gestures = props.cooperative_gestures;
let move_event_throttle_ms = props.move_event_throttle_ms;
{
let map_id = map_id.clone();
let container_id = container_id.clone();
let handlers = handlers.clone();
use_effect(move || {
if init_started() {
return;
}
init_started.set(true);
let container_id = container_id.clone();
let map_id = map_id.clone();
let style = style.clone();
let handlers = handlers.clone();
let map_handle_signal = map_handle_signal;
let max_bounds_str = max_bounds.map(|b| {
format!(
"[[{}, {}], [{}, {}]]",
b.sw.lng, b.sw.lat, b.ne.lng, b.ne.lat
)
});
spawn(async move {
let init_js = init_map_js(
&container_id,
&map_id,
&style,
center.lng,
center.lat,
zoom,
bearing,
pitch,
min_zoom,
max_zoom,
max_bounds_str.as_deref(),
cooperative_gestures,
move_event_throttle_ms,
);
let mut eval = document::eval(&init_js);
while let Ok(json) = eval.recv::<String>().await {
if let Ok(event) = serde_json::from_str::<MapEvent>(&json) {
handlers.dispatch(&map_id, event, map_handle_signal);
}
}
});
});
}
{
let mut tracked_style = use_signal(|| props.style.clone());
if tracked_style() != props.style && init_started() {
let map_id = map_id.clone();
let new_style = props.style.clone();
tracked_style.set(new_style.clone());
spawn(async move {
let js = crate::interop::set_style_js(&map_id, &new_style);
let _ = document::eval(&js).await;
});
}
}
{
let mut tracked_move_throttle = use_signal(|| props.move_event_throttle_ms);
if tracked_move_throttle() != props.move_event_throttle_ms && init_started() {
let map_id = map_id.clone();
let new_throttle = props.move_event_throttle_ms;
tracked_move_throttle.set(new_throttle);
spawn(async move {
let js = set_move_event_throttle_js(&map_id, new_throttle);
let _ = document::eval(&js).await;
});
}
}
{
let map_id = map_id.clone();
use_drop(move || {
map_handle_signal.set(None);
let cleanup_js = destroy_map_js(&map_id);
spawn(async move {
let _ = document::eval(&cleanup_js).await;
});
});
}
}
rsx! {
div {
class: "map-container",
id: "{container_id}",
style: "width: {props.width}; height: {props.height};",
{props.children}
}
}
}