dioxus-leaflet 0.1.2

A general-purpose Leaflet map component for Dioxus applications
Documentation

Dioxus Leaflet

MIT License Apache-2.0 License Crates.io Documentation

A general-purpose Leaflet map component for Dioxus applications. This library provides an easy-to-use, reactive map component that integrates seamlessly with Dioxus applications.

Features

  • πŸ—ΊοΈ Easy-to-use map component with customizable markers
  • 🎯 Interactive markers with popups and custom styling
  • πŸ”§ Extensible marker system with custom icons and data
  • ⚑ TypeScript-like props system for type-safe configuration
  • 🌐 CDN-based Leaflet integration - no additional setup required
  • πŸ“± Responsive design with customizable dimensions
  • 🎨 Customizable tile layers including OpenStreetMap and satellite imagery
  • βš™οΈ Configurable map options for zoom, dragging, and interaction controls
  • πŸ”„ Event handling for map clicks, marker clicks, and map movements

Screenshots

Dioxus Leaflet Example 1

Dioxus Leaflet Example 2

Installation

Add this to your Cargo.toml:

[dependencies]

dioxus-leaflet = "0.1.2"

dioxus = "0.6.3"

Quick Start

Here's a simple example to get you started:

use dioxus::prelude::*;
use dioxus_leaflet::{Map, MapPosition, MapMarker};

fn App() -> Element {
    let markers = vec![
        MapMarker::new(51.505, -0.09, "London")
            .with_description("Capital of England"),
        MapMarker::new(48.8566, 2.3522, "Paris")
            .with_description("Capital of France"),
    ];

    rsx! {
        Map {
            initial_position: MapPosition::new(51.505, -0.09, 5.0),
            markers: markers,
            height: "500px",
            width: "100%"
        }
    }
}

fn main() {
    dioxus::launch(App);
}

Core Components

Map Component

The main Map component provides a full-featured Leaflet map:

rsx! {
    Map {
        initial_position: MapPosition::new(51.505, -0.09, 13.0),
        markers: markers,
        height: "400px",
        width: "100%",
        class: "my-custom-map",
        style: "border: 1px solid #ccc;",
        on_marker_click: move |marker| {
            println!("Marker clicked: {}", marker.title);
        },
        on_map_click: move |position| {
            println!("Map clicked at: {}, {}", position.lat, position.lng);
        }
    }
}

Map Properties

Property Type Default Description
initial_position MapPosition London coordinates Initial map center and zoom
markers Vec<MapMarker> Empty Markers to display on the map
height String "500px" Height of the map container
width String "100%" Width of the map container
options MapOptions Default Map configuration options
class String "" Additional CSS classes
style String "" Additional CSS styles
on_marker_click EventHandler<MapMarker> None Callback when marker is clicked
on_map_click EventHandler<MapPosition> None Callback when map is clicked
on_map_move EventHandler<MapPosition> None Callback when map is moved

Working with Markers

Basic Markers

let marker = MapMarker::new(51.505, -0.09, "London");

Advanced Markers

let marker = MapMarker::new(51.505, -0.09, "London")
    .with_description("The capital city of England")
    .with_custom_data("country", "UK")
    .with_custom_data("population", "8900000")
    .with_popup_options(PopupOptions {
        max_width: Some(250),
        close_button: Some(true),
        auto_close: Some(false),
        ..Default::default()
    });

Custom Marker Icons

use dioxus_leaflet::MarkerIcon;

let custom_icon = MarkerIcon::new("https://example.com/custom-icon.png")
    .with_size(32, 32)
    .with_anchor(16, 32);

let marker = MapMarker::new(51.505, -0.09, "Custom Marker")
    .with_icon(custom_icon);

Map Configuration

Map Options

Customize map behavior with MapOptions:

use dioxus_leaflet::{MapOptions, TileLayer};

let options = MapOptions {
    zoom_control: true,
    scroll_wheel_zoom: true,
    double_click_zoom: false,
    dragging: true,
    keyboard: true,
    attribution_control: true,
    tile_layer: TileLayer::satellite(), // Use satellite imagery
};

rsx! {
    Map {
        options: options,
        // ... other props
    }
}

Tile Layers

Choose from different tile layer providers:

use dioxus_leaflet::TileLayer;

// OpenStreetMap (default)
let osm_tiles = TileLayer::openstreetmap();

// Satellite imagery
let satellite_tiles = TileLayer::satellite();

// Custom tile layer
let custom_tiles = TileLayer {
    url: "https://{s}.tile.custom-provider.com/{z}/{x}/{y}.png".to_string(),
    attribution: "&copy; Custom Provider".to_string(),
    max_zoom: 18,
    subdomains: vec!["a".to_string(), "b".to_string()],
};

Event Handling

Handle various map and marker events:

fn App() -> Element {
    let mut selected_marker = use_signal(|| None::<MapMarker>);
    let mut map_center = use_signal(|| MapPosition::default());

    rsx! {
        Map {
            on_marker_click: move |marker| {
                selected_marker.set(Some(marker));
            },
            on_map_click: move |position| {
                println!("Clicked at: {}, {}", position.lat, position.lng);
            },
            on_map_move: move |position| {
                map_center.set(position);
            },
            // ... other props
        }
        
        // Display selected marker info
        if let Some(marker) = selected_marker.read().as_ref() {
            div {
                "Selected: {marker.title}"
                if let Some(desc) = &marker.description {
                    p { "{desc}" }
                }
            }
        }
    }
}

Styling

CSS Classes

The component uses these CSS classes that you can style:

  • .dioxus-leaflet-container - Main container
  • .dioxus-leaflet-map - Map element

Custom Styling

.dioxus-leaflet-container {
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.my-custom-map {
    border: 2px solid #007cba;
}

Examples

Tourist Map

use dioxus::prelude::*;
use dioxus_leaflet::{Map, MapPosition, MapMarker, PopupOptions};

fn TouristMap() -> Element {
    let attractions = vec![
        MapMarker::new(51.5074, -0.1278, "Big Ben")
            .with_description("Famous clock tower in London")
            .with_popup_options(PopupOptions {
                max_width: Some(200),
                ..Default::default()
            }),
        MapMarker::new(51.5033, -0.1195, "London Eye")
            .with_description("Giant Ferris wheel on the Thames"),
        MapMarker::new(51.5194, -0.1270, "British Museum")
            .with_description("World-famous museum with artifacts from around the globe"),
    ];

    rsx! {
        div { class: "tourist-map-container",
            h1 { "London Attractions" }
            Map {
                initial_position: MapPosition::new(51.5074, -0.1278, 12.0),
                markers: attractions,
                height: "100vh",
                width: "100vw",
                class: "attraction-map"
            }
        }
    }
}

Real-time Location Tracking

fn LocationTracker() -> Element {
    let mut current_position = use_signal(|| MapPosition::default());
    let mut path_markers = use_signal(|| Vec::<MapMarker>::new());

    // Simulate location updates
    use_future(move || async move {
        loop {
            tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
            let new_pos = MapPosition::new(
                current_position.read().lat + (fastrand::f64() - 0.5) * 0.01,
                current_position.read().lng + (fastrand::f64() - 0.5) * 0.01,
                current_position.read().zoom,
            );
            current_position.set(new_pos.clone());
            
            let marker = MapMarker::new(new_pos.lat, new_pos.lng, "Current Location");
            path_markers.with_mut(|markers| markers.push(marker));
        }
    });

    rsx! {
        Map {
            initial_position: current_position.read().clone(),
            markers: path_markers.read().clone(),
            height: "500px",
            on_map_click: move |pos| {
                current_position.set(pos);
            }
        }
    }
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under either of:

at your option.

Acknowledgments

  • Leaflet - The amazing mapping library this component wraps
  • Dioxus - The reactive UI library for Rust
  • OpenStreetMap - Free geographic data used in examples

Note: This library requires an internet connection to load Leaflet CSS and JavaScript from CDN. If you need offline support, consider hosting the Leaflet files locally and modifying the component accordingly.