smeijer / leaflet-geosearch

A geocoding/address-lookup library supporting various api providers.
https://smeijer.github.io/leaflet-geosearch/
MIT License
1.02k stars 270 forks source link

Add custom marker and eventhandler #310

Closed DrBlackBird closed 2 years ago

DrBlackBird commented 2 years ago

Hi all!

First, thanks for your great implementation. It just works and also in a very beautiful way!

I have to need to provide a custom popup. I dont want to use the integrated popup and use a modal or a drawer on the side instead. With react-leaflet I´m able to create an onClick eventhandler on the marker and open a modal on click on of the marker like this:

<MapContainer
    center={[0, 0]}
    zoom={14}
    scrollWheelZoom={true}
    dragging={true}
    zoomControl={true}
    style={{ height: '100%', width: '100%' }}>
        <TileLayer
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            />
            <Marker
                position={[0, 0]}
                draggable={true}
                eventHandlers={{ click: handleOnClick }}>
                <Modal open={open} setOpen={handleOnClick} />
            </Marker>
        <LeafletSearch />    
</MapContainer>

Is this somehow possible with the implementation or rather not? In the documentation I found this:

map.on('geosearch/showlocation', yourEventHandler);

this could be somehow what I´m looking for but I dont know how to use it with react-leaflet :(. Maybe somebody could give me a poke into the right direction?

Thanks a lot folks!

DrBlackBird commented 2 years ago

Ok, I found a solution for that.

In my component which provides the search field I hooked up the above mentioned event listener in my useEffect and set the result value to a state in my parent component.

// Searchfield component

import { useEffect } from 'react';
import { GeoSearchControl, OpenStreetMapProvider } from 'leaflet-geosearch';
import { useMap } from 'react-leaflet';
import 'leaflet-geosearch/dist/geosearch.css';
import Modal from '../Modal';
import { on } from 'events';

const LeafletSearch = ({ setSelectedSearchItem }) => {
    const open = true;
    const provider = new OpenStreetMapProvider({});

    // @ts-ignore
    const searchControl = new GeoSearchControl({
        provider: provider,
        notFoundMessage: 'Could not find location.',
        style: 'bar',
        showPopUp: false,
        showMarker: false,
    });

    const handleResult = result => {
        setSelectedSearchItem(result.location);
    };

    const map = useMap();

    // @ts-ignore
    useEffect(() => {
        map.addControl(searchControl);
        map.on('geosearch/showlocation', handleResult);

        return () => map.removeControl(searchControl);
    }, []);

    return null;
};

export default LeafletSearch;

The parent looks like this

import 'leaflet-defaulticon-compatibility';
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css';
import 'leaflet/dist/leaflet.css';
import React, { useEffect } from 'react';
import { MapContainer, Marker, TileLayer } from 'react-leaflet';
import LeafletSearch from '../LeafletSearch';
import Modal from '../Modal';
import { useMap } from 'react-leaflet';

const Map = () => {
    const [open, setOpen] = React.useState(false);
    const [map, setMap] = React.useState(undefined);
    const [selectedSearchItem, setSelectedSearchItem] = React.useState(undefined);

    const handleOnClick = () => {
        let prevOpen = open;
        setOpen(!prevOpen);
    };

    if (selectedSearchItem) console.log(selectedSearchItem);

    return (
        <MapContainer
            center={[0, 0]}
            zoom={14}
            scrollWheelZoom={true}
            dragging={true}
            zoomControl={true}
            style={{ height: '100%', width: '100%' }}
            whenCreated={map => setMap(map)}>
            <TileLayer
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            />
            {selectedSearchItem && (
                <Marker
                    position={[selectedSearchItem.y, selectedSearchItem.x]}
                    draggable={true}
                    eventHandlers={{ click: handleOnClick }}>
                    <Modal open={open} setOpen={handleOnClick} />
                </Marker>
            )}

            <LeafletSearch setSelectedSearchItem={setSelectedSearchItem} />
        </MapContainer>
    );
};
export default Map;

Maybe this could be useful to someone who is looking for the same :)

iamtekson commented 1 year ago

Perfect! Thank you very much for adding the solution. Really appreciated!