import React, { useState, useEffect, forwardRef, useImperativeHandle, Ref, ReactNode, ReactPortal, } from 'react'; import { createPortal } from 'react-dom'; import { Control, ControlOptions, DomUtil, DomEvent, Map } from 'leaflet'; import { ElementHook, LeafletProvider, LeafletElement, createElementHook, LeafletContextInterface, createControlHook, } from '@react-leaflet/core'; import { useMap } from 'react-leaflet'; interface IDumbControl extends Control {} interface PropsWithChildren { children?: ReactNode; } interface ControlOptionsWithChildren extends ControlOptions { children?: ReactNode; } const DumbControl = Control.extend({ options: { className: '', onOff: '', handleOff: function noop() {}, }, onAdd(/* map */) { const _controlDiv = DomUtil.create('div', this.options.className); DomEvent.on(_controlDiv, 'click', (event) => { DomEvent.stopPropagation(event); }); DomEvent.disableScrollPropagation(_controlDiv); DomEvent.disableClickPropagation(_controlDiv); return _controlDiv; }, onRemove(map: Map) { if (this.options.onOff !== '') { map.off(this.options.onOff, this.options.handleOff, this); } return this; }, }); const useForceUpdate: () => () => void = () => { const [, setValue] = useState(0); // integer state return () => setValue((value) => value + 1); // update the state to force render }; export function createContainerComponent( useElement: ElementHook, ): React.ForwardRefExoticComponent & React.RefAttributes> { function ContainerComponent(props: P, ref: Ref): ReactPortal | null { const forceUpdate = useForceUpdate(); const map = useMap(); const ctx = { __version: 0, map }; const { instance, context } = useElement(props, ctx).current; const children = props.children; const contentNode = (instance as any).getContainer(); useImperativeHandle(ref, () => instance); useEffect(() => { forceUpdate(); }, [contentNode]); if (children === undefined || contentNode === undefined) return null; return createPortal({children}, contentNode); } return forwardRef(ContainerComponent); } export function createControlComponent( createInstance: (props: P) => E, ): React.ForwardRefExoticComponent & React.RefAttributes> { function createElement(props: P, context: LeafletContextInterface): LeafletElement { return { instance: createInstance(props), context }; } const useElement = createElementHook(createElement); const useControl = createControlHook(useElement); return createContainerComponent(useControl); } const ReactLeafletControl = createControlComponent( function createControlWithChildren(props) { return new DumbControl(props); }, ); export default ReactLeafletControl;