dioxus_leaflet/
types.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Represents a geographical position with zoom level
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct MapPosition {
7    pub lat: f64,
8    pub lng: f64,
9    pub zoom: f64,
10}
11
12impl MapPosition {
13    /// Creates a new MapPosition
14    pub fn new(lat: f64, lng: f64, zoom: f64) -> Self {
15        Self { lat, lng, zoom }
16    }
17}
18
19impl Default for MapPosition {
20    fn default() -> Self {
21        Self::new(51.505, -0.09, 13.0)
22    }
23}
24
25/// Represents a marker on the map
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub struct MapMarker {
28    pub lat: f64,
29    pub lng: f64,
30    pub title: String,
31    pub description: Option<String>,
32    pub icon: Option<MarkerIcon>,
33    pub popup_options: Option<PopupOptions>,
34    pub custom_data: Option<HashMap<String, String>>,
35}
36
37impl MapMarker {
38    /// Creates a new MapMarker with basic information
39    pub fn new(lat: f64, lng: f64, title: impl Into<String>) -> Self {
40        Self {
41            lat,
42            lng,
43            title: title.into(),
44            description: None,
45            icon: None,
46            popup_options: None,
47            custom_data: None,
48        }
49    }
50
51    /// Adds a description to the marker
52    pub fn with_description(mut self, description: impl Into<String>) -> Self {
53        self.description = Some(description.into());
54        self
55    }
56
57    /// Adds a custom icon to the marker
58    pub fn with_icon(mut self, icon: MarkerIcon) -> Self {
59        self.icon = Some(icon);
60        self
61    }
62
63    /// Adds custom popup options
64    pub fn with_popup_options(mut self, options: PopupOptions) -> Self {
65        self.popup_options = Some(options);
66        self
67    }
68
69    /// Adds custom data
70    pub fn with_custom_data(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
71        if self.custom_data.is_none() {
72            self.custom_data = Some(HashMap::new());
73        }
74        if let Some(ref mut data) = self.custom_data {
75            data.insert(key.into(), value.into());
76        }
77        self
78    }
79}
80
81impl Default for MapMarker {
82    fn default() -> Self {
83        Self {
84            lat: 0.0,
85            lng: 0.0,
86            title: String::new(),
87            description: None,
88            icon: None,
89            popup_options: None,
90            custom_data: None,
91        }
92    }
93}
94
95/// Custom marker icon configuration
96#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
97pub struct MarkerIcon {
98    pub icon_url: String,
99    pub icon_size: Option<(u32, u32)>,
100    pub icon_anchor: Option<(u32, u32)>,
101    pub popup_anchor: Option<(i32, i32)>,
102    pub shadow_url: Option<String>,
103    pub shadow_size: Option<(u32, u32)>,
104}
105
106impl MarkerIcon {
107    /// Creates a new MarkerIcon with just the URL
108    pub fn new(icon_url: impl Into<String>) -> Self {
109        Self {
110            icon_url: icon_url.into(),
111            icon_size: None,
112            icon_anchor: None,
113            popup_anchor: None,
114            shadow_url: None,
115            shadow_size: None,
116        }
117    }
118}
119
120/// Popup configuration options
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122pub struct PopupOptions {
123    pub max_width: Option<u32>,
124    pub min_width: Option<u32>,
125    pub max_height: Option<u32>,
126    pub auto_pan: Option<bool>,
127    pub keep_in_view: Option<bool>,
128    pub close_button: Option<bool>,
129    pub auto_close: Option<bool>,
130    pub close_on_escape_key: Option<bool>,
131    pub class_name: Option<String>,
132}
133
134impl Default for PopupOptions {
135    fn default() -> Self {
136        Self {
137            max_width: Some(300),
138            min_width: Some(50),
139            max_height: None,
140            auto_pan: Some(true),
141            keep_in_view: Some(false),
142            close_button: Some(true),
143            auto_close: Some(true),
144            close_on_escape_key: Some(true),
145            class_name: None,
146        }
147    }
148}
149
150/// Map configuration options
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub struct MapOptions {
153    pub zoom_control: bool,
154    pub scroll_wheel_zoom: bool,
155    pub double_click_zoom: bool,
156    pub touch_zoom: bool,
157    pub dragging: bool,
158    pub keyboard: bool,
159    pub attribution_control: bool,
160    pub tile_layer: TileLayer,
161    pub leaflet_resources: LeafletResources,
162}
163
164impl Default for MapOptions {
165    fn default() -> Self {
166        Self {
167            zoom_control: true,
168            scroll_wheel_zoom: true,
169            double_click_zoom: true,
170            touch_zoom: true,
171            dragging: true,
172            keyboard: true,
173            attribution_control: true,
174            tile_layer: TileLayer::default(),
175            leaflet_resources: LeafletResources::default(),
176        }
177    }
178}
179
180impl MapOptions {
181    /// Creates a MapOptions with all controls disabled and default tile layer
182    pub fn minimal() -> Self {
183        Self {
184            zoom_control: false,
185            scroll_wheel_zoom: false,
186            double_click_zoom: false,
187            touch_zoom: false,
188            dragging: false,
189            keyboard: false,
190            attribution_control: false,
191            tile_layer: TileLayer::default(),
192            leaflet_resources: LeafletResources::default(),
193        }
194    }
195
196    /// Builder method to enable/disable zoom control
197    pub fn with_zoom_control(mut self, enabled: bool) -> Self {
198        self.zoom_control = enabled;
199        self
200    }
201
202    /// Builder method to enable/disable scroll wheel zoom
203    pub fn with_scroll_wheel_zoom(mut self, enabled: bool) -> Self {
204        self.scroll_wheel_zoom = enabled;
205        self
206    }
207
208    /// Builder method to enable/disable double click zoom
209    pub fn with_double_click_zoom(mut self, enabled: bool) -> Self {
210        self.double_click_zoom = enabled;
211        self
212    }
213
214    /// Builder method to enable/disable touch zoom
215    pub fn with_touch_zoom(mut self, enabled: bool) -> Self {
216        self.touch_zoom = enabled;
217        self
218    }
219
220    /// Builder method to enable/disable dragging
221    pub fn with_dragging(mut self, enabled: bool) -> Self {
222        self.dragging = enabled;
223        self
224    }
225
226    /// Builder method to enable/disable keyboard controls
227    pub fn with_keyboard(mut self, enabled: bool) -> Self {
228        self.keyboard = enabled;
229        self
230    }
231
232    /// Builder method to enable/disable attribution control
233    pub fn with_attribution_control(mut self, enabled: bool) -> Self {
234        self.attribution_control = enabled;
235        self
236    }
237
238    /// Builder method to set tile layer
239    pub fn with_tile_layer(mut self, tile_layer: TileLayer) -> Self {
240        self.tile_layer = tile_layer;
241        self
242    }
243
244    /// Builder method to set Leaflet resources configuration
245    pub fn with_leaflet_resources(mut self, resources: LeafletResources) -> Self {
246        self.leaflet_resources = resources;
247        self
248    }
249}
250
251/// Tile layer configuration
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub struct TileLayer {
254    pub url: String,
255    pub attribution: String,
256    pub max_zoom: u8,
257    pub subdomains: Vec<String>,
258}
259
260impl TileLayer {
261    /// OpenStreetMap tile layer (default)
262    pub fn openstreetmap() -> Self {
263        Self {
264            url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png".to_string(),
265            attribution: "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors".to_string(),
266            max_zoom: 19,
267            subdomains: vec!["a".to_string(), "b".to_string(), "c".to_string()],
268        }
269    }
270
271    /// Satellite imagery tile layer
272    pub fn satellite() -> Self {
273        Self {
274            url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}".to_string(),
275            attribution: "Tiles &copy; Esri".to_string(),
276            max_zoom: 18,
277            subdomains: vec![],
278        }
279    }
280}
281
282impl Default for TileLayer {
283    fn default() -> Self {
284        Self::openstreetmap()
285    }
286}
287
288/// Leaflet resource configuration
289#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
290pub enum LeafletResources {
291    /// Use CDN with specified version
292    Cdn {
293        version: String,
294        base_url: Option<String>, // Allow custom CDN base URL
295    },
296    /// Use local files
297    Local {
298        css_path: String,
299        js_path: String,
300    },
301}
302
303impl LeafletResources {
304    /// Creates a CDN configuration with the specified version
305    pub fn cdn(version: impl Into<String>) -> Self {
306        Self::Cdn {
307            version: version.into(),
308            base_url: None,
309        }
310    }
311
312    /// Creates a CDN configuration with custom base URL
313    pub fn cdn_with_base_url(version: impl Into<String>, base_url: impl Into<String>) -> Self {
314        Self::Cdn {
315            version: version.into(),
316            base_url: Some(base_url.into()),
317        }
318    }
319
320    /// Creates a local files configuration
321    pub fn local(css_path: impl Into<String>, js_path: impl Into<String>) -> Self {
322        Self::Local {
323            css_path: css_path.into(),
324            js_path: js_path.into(),
325        }
326    }
327
328    /// Returns the CSS URL/path
329    pub fn css_url(&self) -> String {
330        match self {
331            Self::Cdn { version, base_url } => {
332                let base = base_url.as_deref().unwrap_or("https://unpkg.com");
333                format!("{}/leaflet@{}/dist/leaflet.css", base, version)
334            }
335            Self::Local { css_path, .. } => css_path.clone(),
336        }
337    }
338
339    /// Returns the JS URL/path
340    pub fn js_url(&self) -> String {
341        match self {
342            Self::Cdn { version, base_url } => {
343                let base = base_url.as_deref().unwrap_or("https://unpkg.com");
344                format!("{}/leaflet@{}/dist/leaflet.js", base, version)
345            }
346            Self::Local { js_path, .. } => js_path.clone(),
347        }
348    }
349
350    /// Returns the integrity hash for CSS (if using CDN with known versions)
351    pub fn css_integrity(&self) -> Option<String> {
352        match self {
353            Self::Cdn { version, base_url } if base_url.is_none() => {
354                // Only provide integrity for unpkg.com with known versions
355                match version.as_str() {
356                    "1.9.4" => Some("sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=".to_string()),
357                    "1.9.3" => Some("sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=".to_string()),
358                    "1.9.2" => Some("sha256-sA+zWATbFveLLNqWO2gtiw3HL/lh1giY/Inf1BJ0z14=".to_string()),
359                    _ => None,
360                }
361            }
362            _ => None,
363        }
364    }
365
366    /// Returns the integrity hash for JS (if using CDN with known versions)
367    pub fn js_integrity(&self) -> Option<String> {
368        match self {
369            Self::Cdn { version, base_url } if base_url.is_none() => {
370                // Only provide integrity for unpkg.com with known versions
371                match version.as_str() {
372                    "1.9.4" => Some("sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=".to_string()),
373                    "1.9.3" => Some("sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=".to_string()),
374                    "1.9.2" => Some("sha256-o9N4PsYA2zOcVD5OHEHviWpTGQ4Q1jEzU7oJiE+zRCE=".to_string()),
375                    _ => None,
376                }
377            }
378            _ => None,
379        }
380    }
381}
382
383impl Default for LeafletResources {
384    fn default() -> Self {
385        Self::cdn("1.9.4")
386    }
387}