leptos_leaflet/components/
context.rs1use leaflet::Map;
2use leptos::prelude::*;
3use wasm_bindgen::JsCast;
4
5use crate::core::{JsReadSignal, JsRwSignal, JsWriteSignal};
6
7
8#[derive(Debug, Clone, Copy)]
12pub struct LeafletMapContext {
13 map: JsRwSignal<Option<leaflet::Map>>,
14 thread_id: std::thread::ThreadId,
15}
16
17impl LeafletMapContext {
18 pub fn new() -> Self {
20 Self {
21 map: JsRwSignal::new_local(None),
22 thread_id: std::thread::current().id(),
23 }
24 }
25
26 pub fn set_map(&self, map: &leaflet::Map) {
32 if !self.is_valid() {
33 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
34 return;
35 }
36 self.map.set(Some(map.clone()));
37 }
38
39 pub fn map(&self) -> Option<leaflet::Map> {
41 if self.is_valid() {
42 self.map.get()
43 } else {
44 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
45 None
46 }
47 }
48
49 pub fn map_untracked(&self) -> Option<leaflet::Map> {
50 if self.is_valid() {
51 self.map.get_untracked()
52 } else {
53 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
54 None
55 }
56 }
57
58 pub fn map_signal(&self) -> JsReadSignal<Option<leaflet::Map>> {
60 if self.is_valid() {
61 self.map.read_only()
62 } else {
63 panic!("Accessing map from a different thread. Probably running on the server.");
64 }
65 }
66
67 pub fn add_layer<L: Into<leaflet::Layer> + Clone>(&self, layer: &L) {
74 if !self.is_valid() {
75 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
76 return;
77 }
78 let map = self.map.get_untracked().expect("Map to be available");
79 let layer: leaflet::Layer = layer.to_owned().into();
80 layer.add_to(&map);
81 }
82
83 pub fn remove_layer<L: Into<leaflet::Layer> + Clone>(&self, layer: &L) {
90 if !self.is_valid() {
91 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
92 return;
93 }
94 let map = self.map.get_untracked().expect("Map to be available");
95 let layer: leaflet::Layer = layer.to_owned().into();
96 layer.remove_from(&map);
97 }
98
99 fn is_valid(&self) -> bool {
100 std::thread::current().id() == self.thread_id && !self.map.is_disposed()
101 }
102}
103
104impl Default for LeafletMapContext {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110pub fn provide_leaflet_context() -> LeafletMapContext {
112 let context = LeafletMapContext::new();
113 provide_context(context);
114 context
115}
116
117pub fn use_leaflet_context() -> Option<LeafletMapContext> {
119 use_context::<LeafletMapContext>()
120}
121
122pub fn extend_context_with_overlay() -> LeafletOverlayContainerContext {
124 let overlay_context = LeafletOverlayContainerContext::new();
125 provide_context(overlay_context);
126 overlay_context
127}
128
129pub fn use_overlay_context() -> Option<LeafletOverlayContainerContext> {
131 use_context::<LeafletOverlayContainerContext>()
132}
133
134pub fn use_overlay_context_layer<T>() -> Option<T>
136where
137 T: Into<leaflet::Layer> + Clone + JsCast,
138{
139 expect_context::<LeafletOverlayContainerContext>().container::<T>()
140}
141
142pub fn update_overlay_context<C: Into<leaflet::Layer> + Clone>(layer: &C) {
148 let overlay_context = use_context::<LeafletOverlayContainerContext>().expect("overlay context");
149 overlay_context.set_container(layer);
150}
151
152#[derive(Debug, Clone, Copy)]
154pub struct LeafletOverlayContainerContext {
155 container: JsRwSignal<Option<leaflet::Layer>>,
156 thread_id: std::thread::ThreadId,
157}
158
159impl LeafletOverlayContainerContext {
160 pub fn new() -> Self {
161 Self {
162 container: JsRwSignal::new_local(None),
163 thread_id: std::thread::current().id(),
164 }
165 }
166
167 pub fn set_container<C: Into<leaflet::Layer> + Clone>(&self, layer: &C) {
169 if !self.is_valid() {
170 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
171 return;
172 }
173 self.container.set(Some(layer.clone().into()));
174 }
175
176 pub fn container<T: JsCast>(&self) -> Option<T> {
178 if !self.is_valid() {
179 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
180 return None;
181 }
182 self.container.get().map(|layer| layer.unchecked_into())
183 }
184
185 pub fn untrack_container<C: JsCast>(&self) -> Option<C> {
187 if !self.is_valid() {
188 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
189 return None;
190 }
191 self.container
192 .get_untracked()
193 .map(|layer| layer.unchecked_into())
194 }
195
196 fn is_valid(&self) -> bool {
197 std::thread::current().id() == self.thread_id && !self.container.is_disposed()
198 }
199}
200
201impl Default for LeafletOverlayContainerContext {
202 fn default() -> Self {
203 Self::new()
204 }
205}
206
207#[derive(Debug, Clone, Copy)]
208pub struct TileLayerWmsContext {
209 wms: JsRwSignal<Option<leaflet::TileLayerWms>>,
210 thread_id: std::thread::ThreadId,
211}
212
213impl Default for TileLayerWmsContext {
214 fn default() -> Self {
215 Self::new()
216 }
217}
218
219impl TileLayerWmsContext {
220 pub fn new() -> Self {
221 Self {
222 wms: JsRwSignal::new_local(None),
223 thread_id: std::thread::current().id(),
224 }
225 }
226
227 pub fn set_wms(&self, wms: &leaflet::TileLayerWms) {
228 if !self.is_valid() {
229 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
230 return;
231 }
232 self.wms.set(Some(wms.clone()));
233 }
234
235 pub fn wms(&self) -> Option<leaflet::TileLayerWms> {
236 if self.is_valid() {
237 self.wms.get()
238 } else {
239 leptos::logging::error!("Accessing map from a different thread. Probably running on the server.");
240 None
241 }
242 }
243
244 fn is_valid(&self) -> bool {
245 std::thread::current().id() == self.thread_id && !self.wms.is_disposed()
246 }
247}
248
249pub type MapReadSignal = JsReadSignal<Option<Map>>;
250pub type MapWriteSignal = JsWriteSignal<Option<Map>>;
251
252pub fn create_map_signal() -> (MapReadSignal, MapWriteSignal) {
254 JsRwSignal::new_local(None).split()
255}