UNPKG

9.07 kBTypeScriptView Raw
1import * as React from 'react'
2
3import MapContext from './map-context'
4
5import { unregisterEvents, applyUpdatersToPropsAndRegisterEvents } from './utils/helper'
6
7const eventMap = {
8 onDblClick: 'dblclick',
9 onDragEnd: 'dragend',
10 onDragStart: 'dragstart',
11 onMapTypeIdChanged: 'maptypeid_changed',
12 onMouseMove: 'mousemove',
13 onMouseOut: 'mouseout',
14 onMouseOver: 'mouseover',
15 onMouseDown: 'mousedown',
16 onMouseUp: 'mouseup',
17 onRightClick: 'rightclick',
18 onTilesLoaded: 'tilesloaded',
19 onBoundsChanged: 'bounds_changed',
20 onCenterChanged: 'center_changed',
21 onClick: 'click',
22 onDrag: 'drag',
23 onHeadingChanged: 'heading_changed',
24 onIdle: 'idle',
25 onProjectionChanged: 'projection_changed',
26 onResize: 'resize',
27 onTiltChanged: 'tilt_changed',
28 onZoomChanged: 'zoom_changed',
29}
30
31const updaterMap = {
32 extraMapTypes(map: google.maps.Map, extra: google.maps.MapType[]): void {
33 extra.forEach(function forEachExtra(it, i) {
34 map.mapTypes.set(String(i), it)
35 })
36 },
37 center(map: google.maps.Map, center: google.maps.LatLng | google.maps.LatLngLiteral): void {
38 map.setCenter(center)
39 },
40 clickableIcons(map: google.maps.Map, clickable: boolean): void {
41 map.setClickableIcons(clickable)
42 },
43 heading(map: google.maps.Map, heading: number): void {
44 map.setHeading(heading)
45 },
46 mapTypeId(map: google.maps.Map, mapTypeId: string): void {
47 map.setMapTypeId(mapTypeId)
48 },
49 options(map: google.maps.Map, options: google.maps.MapOptions): void {
50 map.setOptions(options)
51 },
52 streetView(map: google.maps.Map, streetView: google.maps.StreetViewPanorama): void {
53 map.setStreetView(streetView)
54 },
55 tilt(map: google.maps.Map, tilt: number): void {
56 map.setTilt(tilt)
57 },
58 zoom(map: google.maps.Map, zoom: number): void {
59 map.setZoom(zoom)
60 },
61}
62
63interface GoogleMapState {
64 map: google.maps.Map | null
65}
66
67export interface GoogleMapProps {
68 id?: string
69 mapContainerStyle?: React.CSSProperties
70 mapContainerClassName?: string
71 options?: google.maps.MapOptions
72 /** Additional map types to overlay. Overlay map types will display on top of the base map they are attached to, in the order in which they appear in the overlayMapTypes array (overlays with higher index values are displayed in front of overlays with lower index values). */
73 extraMapTypes?: google.maps.MapType[]
74 /** The initial Map center. */
75 center?: google.maps.LatLng | google.maps.LatLngLiteral
76 /** When false, map icons are not clickable. A map icon represents a point of interest, also known as a POI. By default map icons are clickable. */
77 clickableIcons?: boolean
78 /** The heading for aerial imagery in degrees measured clockwise from cardinal direction North. Headings are snapped to the nearest available angle for which imagery is available. */
79 heading?: number
80 /** The initial Map mapTypeId. Defaults to ROADMAP. */
81 mapTypeId?: string
82 /** A StreetViewPanorama to display when the Street View pegman is dropped on the map. If no panorama is specified, a default StreetViewPanorama will be displayed in the map's div when the pegman is dropped. */
83 streetView?: google.maps.StreetViewPanorama
84 /** Controls the automatic switching behavior for the angle of incidence of the map. The only allowed values are 0 and 45. The value 0 causes the map to always use a 0° overhead view regardless of the zoom level and viewport. The value 45 causes the tilt angle to automatically switch to 45 whenever 45° imagery is available for the current zoom level and viewport, and switch back to 0 whenever 45° imagery is not available (this is the default behavior). 45° imagery is only available for satellite and hybrid map types, within some locations, and at some zoom levels. Note: getTilt returns the current tilt angle, not the value specified by this option. Because getTilt and this option refer to different things, do not bind() the tilt property; doing so may yield unpredictable effects. */
85 tilt?: number
86 /** The initial Map zoom level. Required. Valid values: Integers between zero, and up to the supported maximum zoom level. */
87 zoom?: number
88 /** This event is fired when the user clicks on the map. An ApiMouseEvent with properties for the clicked location is returned unless a place icon was clicked, in which case an IconMouseEvent with a placeId is returned. IconMouseEvent and ApiMouseEvent are identical, except that IconMouseEvent has the placeId field. The event can always be treated as an ApiMouseEvent when the placeId is not important. The click event is not fired if a Marker or InfoWindow was clicked. */
89 onClick?: (e: google.maps.MouseEvent) => void
90 /** This event is fired when the user double-clicks on the map. Note that the click event will also fire, right before this one. */
91 onDblClick?: (e: google.maps.MouseEvent) => void
92 /** This event is repeatedly fired while the user drags the map. */
93 onDrag?: () => void
94 /** This event is fired when the user stops dragging the map. */
95 onDragEnd?: () => void
96 /** This event is fired when the user starts dragging the map. */
97 onDragStart?: () => void
98 /** This event is fired when the mapTypeId property changes. */
99 onMapTypeIdChanged?: () => void
100 /** This event is fired whenever the user's mouse moves over the map container. */
101 onMouseMove?: (e: google.maps.MouseEvent) => void
102 /** This event is fired when the user's mouse exits the map container. */
103 onMouseOut?: (e: google.maps.MouseEvent) => void
104 /** This event is fired when the user's mouse enters the map container. */
105 onMouseOver?: (e: google.maps.MouseEvent) => void
106 /** This event is fired when the DOM contextmenu event is fired on the map container. */
107 onRightClick?: (e: google.maps.MouseEvent) => void
108 /** This event is fired when the visible tiles have finished loading. */
109 onTilesLoaded?: () => void
110 /** This event is fired when the viewport bounds have changed. */
111 onBoundsChanged?: () => void
112 /** This event is fired when the map center property changes. */
113 onCenterChanged?: () => void
114 /** This event is fired when the map heading property changes. */
115 onHeadingChanged?: () => void
116 /** This event is fired when the map becomes idle after panning or zooming. */
117 onIdle?: () => void
118 /** This event is fired when the projection has changed. */
119 onProjectionChanged?: () => void
120 /** This event is fired when the map size has changed. */
121 onResize?: () => void
122 /** This event is fired when the map tilt property changes. */
123 onTiltChanged?: () => void
124 /** This event is fired when the map zoom property changes. */
125 onZoomChanged?: () => void
126 /** This callback is called when the map instance has loaded. It is called with the map instance. */
127 onLoad?: (map: google.maps.Map) => void | Promise<void>
128 /** This callback is called when the component unmounts. It is called with the map instance. */
129 onUnmount?: (map: google.maps.Map) => void | Promise<void>
130}
131
132export class GoogleMap extends React.PureComponent<GoogleMapProps, GoogleMapState> {
133 state: GoogleMapState = {
134 map: null,
135 }
136
137 registeredEvents: google.maps.MapsEventListener[] = []
138
139 mapRef: Element | null = null
140
141 getInstance = (): google.maps.Map | null => {
142 if (this.mapRef === null) {
143 return null
144 }
145
146 return new google.maps.Map(this.mapRef, this.props.options)
147 }
148
149 panTo = (latLng: google.maps.LatLng | google.maps.LatLngLiteral): void => {
150 const map = this.getInstance()
151 if (map) {
152 map.panTo(latLng)
153 }
154 }
155
156 setMapCallback = (): void => {
157 if (this.state.map !== null) {
158 if (this.props.onLoad) {
159 this.props.onLoad(this.state.map)
160 }
161 }
162 }
163
164 componentDidMount(): void {
165 const map = this.getInstance()
166
167 this.registeredEvents = applyUpdatersToPropsAndRegisterEvents({
168 updaterMap,
169 eventMap,
170 prevProps: {},
171 nextProps: this.props,
172 instance: map,
173 })
174
175 this.setState(function setMap() {
176 return {
177 map,
178 }
179 }, this.setMapCallback)
180 }
181
182 componentDidUpdate(prevProps: GoogleMapProps): void {
183 if (this.state.map !== null) {
184 unregisterEvents(this.registeredEvents)
185
186 this.registeredEvents = applyUpdatersToPropsAndRegisterEvents({
187 updaterMap,
188 eventMap,
189 prevProps,
190 nextProps: this.props,
191 instance: this.state.map,
192 })
193 }
194 }
195
196 componentWillUnmount(): void {
197 if (this.state.map !== null) {
198 if (this.props.onUnmount) {
199 this.props.onUnmount(this.state.map)
200 }
201
202 unregisterEvents(this.registeredEvents)
203 }
204 }
205
206 getRef = (ref: HTMLDivElement | null): void => {
207 this.mapRef = ref
208 }
209
210 render(): React.ReactNode {
211 return (
212 <div
213 id={this.props.id}
214 ref={this.getRef}
215 style={this.props.mapContainerStyle}
216 className={this.props.mapContainerClassName}
217 >
218 <MapContext.Provider value={this.state.map}>
219 {this.state.map !== null ? this.props.children : <></>}
220 </MapContext.Provider>
221 </div>
222 )
223 }
224}
225
226export default GoogleMap