import invariant from 'invariant' 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 = {} const updaterMap = { data( instance: google.maps.visualization.HeatmapLayer, data: | google.maps.MVCArray< google.maps.LatLng | google.maps.visualization.WeightedLocation > | google.maps.LatLng[] | google.maps.visualization.WeightedLocation[] ): void { instance.setData(data) }, map( instance: google.maps.visualization.HeatmapLayer, map: google.maps.Map ): void { instance.setMap(map) }, options( instance: google.maps.visualization.HeatmapLayer, options: google.maps.visualization.HeatmapLayerOptions ): void { instance.setOptions(options) }, } type HeatmapLayerState = { heatmapLayer: google.maps.visualization.HeatmapLayer | null } export type HeatmapLayerProps = { // required /** The data points to display. Required. */ data: | google.maps.MVCArray< google.maps.LatLng | google.maps.visualization.WeightedLocation > | google.maps.LatLng[] | google.maps.visualization.WeightedLocation[] options?: google.maps.visualization.HeatmapLayerOptions | undefined /** This callback is called when the heatmapLayer instance has loaded. It is called with the heatmapLayer instance. */ onLoad?: | ((heatmapLayer: google.maps.visualization.HeatmapLayer) => void) | undefined /** This callback is called when the component unmounts. It is called with the heatmapLayer instance. */ onUnmount?: | ((heatmapLayer: google.maps.visualization.HeatmapLayer) => void) | undefined } function HeatmapLayerFunctional({ data, onLoad, onUnmount, options, }: HeatmapLayerProps) { const map = useContext(MapContext) const [instance, setInstance] = useState(null) useEffect(() => { if (!google.maps.visualization) { invariant( !!google.maps.visualization, 'Did you include prop libraries={["visualization"]} in useJsApiScript? %s', google.maps.visualization ) } }, []) useEffect(() => { invariant(!!data, 'data property is required in HeatmapLayer %s', data) }, [data]) // Order does matter useEffect(() => { if (instance !== null) { instance.setMap(map) } }, [map]) useEffect(() => { if (options && instance !== null) { instance.setOptions(options) } }, [instance, options]) useEffect(() => { const heatmapLayer = new google.maps.visualization.HeatmapLayer({ ...options, data, map, }) setInstance(heatmapLayer) if (onLoad) { onLoad(heatmapLayer) } return () => { if (instance !== null) { if (onUnmount) { onUnmount(instance) } instance.setMap(null) } } }, []) return null } export const HeatmapLayerF = memo(HeatmapLayerFunctional) export class HeatmapLayer extends PureComponent< HeatmapLayerProps, HeatmapLayerState > { static override contextType = MapContext declare context: ContextType registeredEvents: google.maps.MapsEventListener[] = [] override state: HeatmapLayerState = { heatmapLayer: null, } setHeatmapLayerCallback = (): void => { if (this.state.heatmapLayer !== null && this.props.onLoad) { this.props.onLoad(this.state.heatmapLayer) } } override componentDidMount(): void { invariant( !!google.maps.visualization, 'Did you include prop libraries={["visualization"]} to ? %s', google.maps.visualization ) invariant( !!this.props.data, 'data property is required in HeatmapLayer %s', this.props.data ) const heatmapLayer = new google.maps.visualization.HeatmapLayer({ ...this.props.options, data: this.props.data, map: this.context, }) this.registeredEvents = applyUpdatersToPropsAndRegisterEvents({ updaterMap, eventMap, prevProps: {}, nextProps: this.props, instance: heatmapLayer, }) this.setState(function setHeatmapLayer() { return { heatmapLayer, } }, this.setHeatmapLayerCallback) } override componentDidUpdate(prevProps: HeatmapLayerProps): void { unregisterEvents(this.registeredEvents) this.registeredEvents = applyUpdatersToPropsAndRegisterEvents({ updaterMap, eventMap, prevProps, nextProps: this.props, instance: this.state.heatmapLayer, }) } override componentWillUnmount(): void { if (this.state.heatmapLayer !== null) { if (this.props.onUnmount) { this.props.onUnmount(this.state.heatmapLayer) } unregisterEvents(this.registeredEvents) this.state.heatmapLayer.setMap(null) } } override render(): null { return null } } export default HeatmapLayer