TurtIeSocks / react-leaflet-geoman

React wrapper for the leaflet-geoman plugin
MIT License
19 stars 7 forks source link

How to create a custom button #11

Closed Kiggo closed 7 months ago

Kiggo commented 7 months ago
// as a React component
import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css'
import { GeomanControls } from 'react-leaflet-geoman-v2'

export default function Drawing() {
  const handleChange = () => {
    console.log('Event fired!')
  }
  return (
    <FeatureGroup>
      <GeomanControls
        options={{
          position: 'topleft',
          drawText: false,
        }}
        globalOptions={{
          continueDrawing: true,
          editable: false,
        }}
        onCreate={handleChange}
        onChange={(e) => console.log('onChange', e)}
      />
    </FeatureGroup>
  )
}

Please tell me how to create a custom button in this code.

TurtIeSocks commented 7 months ago

In order to do it safely, you would need to finish this PR that I lost motivation for.

You can do something like this:

  const map = useMap()
  React.useLayoutEffect(() => {
    map.pm.Toolbar.createCustomControl({
      name: 'Custom Button',
      block: 'custom',
      title: 'Do Some Stuff!',
      className: 'leaflet-button-button',
      toggle: true,
      actions: [
        {
          text: 'Do Stuff',
          onClick: (e) => {
            console.log('Doing Stuff', e)
          },
        },
        {
          text: 'Do More Stuff',
          onClick: (e) => {
            console.log('Doing More Stuff', e)
          },
        },
        'cancel',
      ],
    })
  }, [map])

However, if the component ever gets unmounted and remounted and that useLayoutEffect is called more than once, geoman will error out as there isn't a way to unmount it on cleanup or check if it's already mounted to the DOM, which is what the PR added.

With my fork of geoman (it's outdated and I don't particularly recommend using it, but it does have the aforementioned PR) I have it set up like this in a project:

          if (!map.pm.Toolbar.controlExists('mergeMode')) {
            map.pm.Toolbar.createCustomControl({
              name: 'mergeMode',
              block: 'custom',
              title: 'Merge Shapes',
              className: 'leaflet-button-merge',
              toggle: true,
              actions: [
                {
                  text: 'Merge',
                  onClick() {
                    useShapes.getState().setters.combine()
                  },
                },
                {
                  text: 'Merge All',
                  onClick() {
                    useShapes.getState().setters.combine(true)
                  },
                },
                'cancel',
              ],
            })
          }
TurtIeSocks commented 7 months ago

Ah, actually, you can accomplish this safely @Kiggo with current functionality, you'll just need to augment the TS definitions since it doesn't currently exist:

declare module 'leaflet' {
  namespace PM {
    interface PMMapToolbar {
      getButtons(): Record<string, L.Control>
    }
  }
}

const map = useMap()
React.useLayoutEffect(() => {
  if (!('Custom Button' in map.pm.Toolbar.getButtons())) {
    map.pm.Toolbar.createCustomControl({
      name: 'Custom Button',
      block: 'custom',
      title: 'Do Some Stuff!',
      className: 'leaflet-button-button',
      toggle: true,
      actions: [
        {
          text: 'Do Stuff',
          onClick: (e) => {
            console.log('Doing Stuff', e)
          },
        },
        {
          text: 'Do More Stuff',
          onClick: (e) => {
            console.log('Doing More Stuff', e)
          },
        },
        'cancel',
      ],
    })
  }
}, [map])