import { memo, useState, useEffect, useContext, PureComponent, type ContextType, } from 'react' import { unregisterEvents, applyUpdatersToPropsAndRegisterEvents, } from '../../utils/helper.js' import MapContext from '../../map-context.js' const eventMap = { onClick: 'click', onDblClick: 'dblclick', onMouseDown: 'mousedown', onMouseOut: 'mouseout', onMouseOver: 'mouseover', onMouseUp: 'mouseup', onRightClick: 'rightclick', onAddFeature: 'addfeature', onRemoveFeature: 'removefeature', onRemoveProperty: 'removeproperty', onSetGeometry: 'setgeometry', onSetProperty: 'setproperty', } const updaterMap = { add( instance: google.maps.Data, feature: google.maps.Data.Feature | google.maps.Data.FeatureOptions ): void { instance.add(feature) }, addgeojson( instance: google.maps.Data, geojson: object, options?: google.maps.Data.GeoJsonOptions | undefined ): void { instance.addGeoJson(geojson, options) }, contains( instance: google.maps.Data, feature: google.maps.Data.Feature ): void { instance.contains(feature) }, foreach( instance: google.maps.Data, callback: (feature: google.maps.Data.Feature) => void ): void { instance.forEach(callback) }, loadgeojson( instance: google.maps.Data, url: string, options: google.maps.Data.GeoJsonOptions, callback: (features: google.maps.Data.Feature[]) => void ): void { instance.loadGeoJson(url, options, callback) }, overridestyle( instance: google.maps.Data, feature: google.maps.Data.Feature, style: google.maps.Data.StyleOptions ): void { instance.overrideStyle(feature, style) }, remove(instance: google.maps.Data, feature: google.maps.Data.Feature): void { instance.remove(feature) }, revertstyle( instance: google.maps.Data, feature: google.maps.Data.Feature ): void { instance.revertStyle(feature) }, controlposition( instance: google.maps.Data, controlPosition: google.maps.ControlPosition ): void { instance.setControlPosition(controlPosition) }, controls(instance: google.maps.Data, controls: string[] | null): void { instance.setControls(controls) }, drawingmode(instance: google.maps.Data, mode: string | null): void { instance.setDrawingMode(mode) }, map(instance: google.maps.Data, map: google.maps.Map): void { instance.setMap(map) }, style( instance: google.maps.Data, style: google.maps.Data.StylingFunction | google.maps.Data.StyleOptions ): void { instance.setStyle(style) }, togeojson( instance: google.maps.Data, callback: (feature: object) => void ): void { instance.toGeoJson(callback) }, } type DataState = { data: google.maps.Data | null } export type DataProps = { options?: google.maps.Data.DataOptions | undefined /** This event is fired for a click on the geometry. */ onClick?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired for a double click on the geometry. */ onDblClick?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired for a mousedown on the geometry. */ onMouseDown?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired when the DOM mousemove event is fired on the rectangle. */ onMouseMove?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired when the mouse leaves the area of the geometry. */ onMouseOut?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired when the mouse enters the area of the geometry. */ onMouseOver?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired for a mouseup on the geometry. */ onMouseUp?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired for a rightclick on the geometry. */ onRightClick?: ((e: google.maps.Data.MouseEvent) => void) | undefined /** This event is fired when a feature is added to the collection. */ onAddFeature?: ((e: google.maps.Data.AddFeatureEvent) => void) | undefined /** This event is fired when a feature is removed from the collection. */ onRemoveFeature?: | ((e: google.maps.Data.RemoveFeatureEvent) => void) | undefined /** This event is fired when a feature's property is removed. */ onRemoveProperty?: | ((e: google.maps.Data.RemovePropertyEvent) => void) | undefined /** This event is fired when a feature's geometry is set. */ onSetGeometry?: ((e: google.maps.Data.SetGeometryEvent) => void) | undefined /** This event is fired when a feature's property is set. */ onSetProperty?: ((e: google.maps.Data.SetPropertyEvent) => void) | undefined /** This callback is called when the data instance has loaded. It is called with the data instance. */ onLoad?: ((data: google.maps.Data) => void) | undefined /** This callback is called when the component unmounts. It is called with the data instance. */ onUnmount?: ((data: google.maps.Data) => void) | undefined } function DataFunctional({ options, onClick, onDblClick, onMouseDown, onMouseMove, onMouseOut, onMouseOver, onMouseUp, onRightClick, onAddFeature, onRemoveFeature, onRemoveProperty, onSetGeometry, onSetProperty, onLoad, onUnmount, }: DataProps): null { const map = useContext(MapContext) const [instance, setInstance] = useState(null) const [dblclickListener, setDblclickListener] = useState(null) const [mousedownListener, setMousedownListener] = useState(null) const [mousemoveListener, setMousemoveListener] = useState(null) const [mouseoutListener, setMouseoutListener] = useState(null) const [mouseoverListener, setMouseoverListener] = useState(null) const [mouseupListener, setMouseupListener] = useState(null) const [rightclickListener, setRightclickListener] = useState(null) const [clickListener, setClickListener] = useState(null) const [addFeatureListener, setAddFeatureListener] = useState(null) const [removeFeatureListener, setRemoveFeatureListener] = useState(null) const [removePropertyListener, setRemovePropertyListener] = useState(null) const [setGeometryListener, setSetGeometryListener] = useState(null) const [setPropertyListener, setSetPropertyListener] = useState(null) // Order does matter useEffect(() => { if (instance !== null) { instance.setMap(map) } }, [map]) useEffect(() => { if (instance && onDblClick) { if (dblclickListener !== null) { google.maps.event.removeListener(dblclickListener) } setDblclickListener( google.maps.event.addListener(instance, 'dblclick', onDblClick) ) } }, [onDblClick]) useEffect(() => { if (instance && onMouseDown) { if (mousedownListener !== null) { google.maps.event.removeListener(mousedownListener) } setMousedownListener( google.maps.event.addListener(instance, 'mousedown', onMouseDown) ) } }, [onMouseDown]) useEffect(() => { if (instance && onMouseMove) { if (mousemoveListener !== null) { google.maps.event.removeListener(mousemoveListener) } setMousemoveListener( google.maps.event.addListener(instance, 'mousemove', onMouseMove) ) } }, [onMouseMove]) useEffect(() => { if (instance && onMouseOut) { if (mouseoutListener !== null) { google.maps.event.removeListener(mouseoutListener) } setMouseoutListener( google.maps.event.addListener(instance, 'mouseout', onMouseOut) ) } }, [onMouseOut]) useEffect(() => { if (instance && onMouseOver) { if (mouseoverListener !== null) { google.maps.event.removeListener(mouseoverListener) } setMouseoverListener( google.maps.event.addListener(instance, 'mouseover', onMouseOver) ) } }, [onMouseOver]) useEffect(() => { if (instance && onMouseUp) { if (mouseupListener !== null) { google.maps.event.removeListener(mouseupListener) } setMouseupListener( google.maps.event.addListener(instance, 'mouseup', onMouseUp) ) } }, [onMouseUp]) useEffect(() => { if (instance && onRightClick) { if (rightclickListener !== null) { google.maps.event.removeListener(rightclickListener) } setRightclickListener( google.maps.event.addListener(instance, 'rightclick', onRightClick) ) } }, [onRightClick]) useEffect(() => { if (instance && onClick) { if (clickListener !== null) { google.maps.event.removeListener(clickListener) } setClickListener( google.maps.event.addListener(instance, 'click', onClick) ) } }, [onClick]) useEffect(() => { if (instance && onAddFeature) { if (addFeatureListener !== null) { google.maps.event.removeListener(addFeatureListener) } setAddFeatureListener( google.maps.event.addListener(instance, 'addfeature', onAddFeature) ) } }, [onAddFeature]) useEffect(() => { if (instance && onRemoveFeature) { if (removeFeatureListener !== null) { google.maps.event.removeListener(removeFeatureListener) } setRemoveFeatureListener( google.maps.event.addListener( instance, 'removefeature', onRemoveFeature ) ) } }, [onRemoveFeature]) useEffect(() => { if (instance && onRemoveProperty) { if (removePropertyListener !== null) { google.maps.event.removeListener(removePropertyListener) } setRemovePropertyListener( google.maps.event.addListener( instance, 'removeproperty', onRemoveProperty ) ) } }, [onRemoveProperty]) useEffect(() => { if (instance && onSetGeometry) { if (setGeometryListener !== null) { google.maps.event.removeListener(setGeometryListener) } setSetGeometryListener( google.maps.event.addListener(instance, 'setgeometry', onSetGeometry) ) } }, [onSetGeometry]) useEffect(() => { if (instance && onSetProperty) { if (setPropertyListener !== null) { google.maps.event.removeListener(setPropertyListener) } setSetPropertyListener( google.maps.event.addListener(instance, 'setproperty', onSetProperty) ) } }, [onSetProperty]) useEffect(() => { if (map !== null) { const data = new google.maps.Data({ ...options, map, }) if (onDblClick) { setDblclickListener( google.maps.event.addListener(data, 'dblclick', onDblClick) ) } if (onMouseDown) { setMousedownListener( google.maps.event.addListener(data, 'mousedown', onMouseDown) ) } if (onMouseMove) { setMousemoveListener( google.maps.event.addListener(data, 'mousemove', onMouseMove) ) } if (onMouseOut) { setMouseoutListener( google.maps.event.addListener(data, 'mouseout', onMouseOut) ) } if (onMouseOver) { setMouseoverListener( google.maps.event.addListener(data, 'mouseover', onMouseOver) ) } if (onMouseUp) { setMouseupListener( google.maps.event.addListener(data, 'mouseup', onMouseUp) ) } if (onRightClick) { setRightclickListener( google.maps.event.addListener(data, 'rightclick', onRightClick) ) } if (onClick) { setClickListener(google.maps.event.addListener(data, 'click', onClick)) } if (onAddFeature) { setAddFeatureListener( google.maps.event.addListener(data, 'addfeature', onAddFeature) ) } if (onRemoveFeature) { setRemoveFeatureListener( google.maps.event.addListener(data, 'removefeature', onRemoveFeature) ) } if (onRemoveProperty) { setRemovePropertyListener( google.maps.event.addListener( data, 'removeproperty', onRemoveProperty ) ) } if (onSetGeometry) { setSetGeometryListener( google.maps.event.addListener(data, 'setgeometry', onSetGeometry) ) } if (onSetProperty) { setSetPropertyListener( google.maps.event.addListener(data, 'setproperty', onSetProperty) ) } setInstance(data) if (onLoad) { onLoad(data) } } return () => { if (instance) { if (dblclickListener !== null) { google.maps.event.removeListener(dblclickListener) } if (mousedownListener !== null) { google.maps.event.removeListener(mousedownListener) } if (mousemoveListener !== null) { google.maps.event.removeListener(mousemoveListener) } if (mouseoutListener !== null) { google.maps.event.removeListener(mouseoutListener) } if (mouseoverListener !== null) { google.maps.event.removeListener(mouseoverListener) } if (mouseupListener !== null) { google.maps.event.removeListener(mouseupListener) } if (rightclickListener !== null) { google.maps.event.removeListener(rightclickListener) } if (clickListener !== null) { google.maps.event.removeListener(clickListener) } if (addFeatureListener !== null) { google.maps.event.removeListener(addFeatureListener) } if (removeFeatureListener !== null) { google.maps.event.removeListener(removeFeatureListener) } if (removePropertyListener !== null) { google.maps.event.removeListener(removePropertyListener) } if (setGeometryListener !== null) { google.maps.event.removeListener(setGeometryListener) } if (setPropertyListener !== null) { google.maps.event.removeListener(setPropertyListener) } if (onUnmount) { onUnmount(instance) } instance.setMap(null) } } }, []) return null } export const DataF = memo(DataFunctional) export class Data extends PureComponent { static override contextType = MapContext declare context: ContextType registeredEvents: google.maps.MapsEventListener[] = [] override state: DataState = { data: null, } setDataCallback = (): void => { if (this.state.data !== null && this.props.onLoad) { this.props.onLoad(this.state.data) } } override componentDidMount(): void { if (this.context !== null) { const data = new google.maps.Data({ ...this.props.options, map: this.context, }) this.registeredEvents = applyUpdatersToPropsAndRegisterEvents({ updaterMap, eventMap, prevProps: {}, nextProps: this.props, instance: data, }) this.setState(() => { return { data, } }, this.setDataCallback) } } override componentDidUpdate(prevProps: DataProps): void { if (this.state.data !== null) { unregisterEvents(this.registeredEvents) this.registeredEvents = applyUpdatersToPropsAndRegisterEvents({ updaterMap, eventMap, prevProps, nextProps: this.props, instance: this.state.data, }) } } override componentWillUnmount(): void { if (this.state.data !== null) { if (this.props.onUnmount) { this.props.onUnmount(this.state.data) } unregisterEvents(this.registeredEvents) if (this.state.data) { this.state.data.setMap(null) } } } override render(): null { return null } } export default Data