@react-google-maps/marker-clusterer 2.10.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { Cluster } from './Cluster'
4
4
 
5
5
  import { ClusterIconStyle, ClusterIconInfo } from './types'
6
6
 
7
- export class ClusterIcon {
7
+ export class ClusterIcon extends google.maps.OverlayView {
8
8
  cluster: Cluster
9
9
  className: string
10
10
  clusterClassName: string
@@ -25,146 +25,189 @@ export class ClusterIcon {
25
25
  fontStyle: string
26
26
  fontFamily: string
27
27
  backgroundPosition: string
28
+ cMouseDownInCluster: boolean | null
29
+ cDraggingMapByCluster: boolean | null
30
+ timeOut: number | null
28
31
 
29
32
  boundsChangedListener: google.maps.MapsEventListener | null
30
33
 
31
34
  constructor(cluster: Cluster, styles: ClusterIconStyle[]) {
35
+ super()
32
36
  cluster.getClusterer().extend(ClusterIcon, google.maps.OverlayView)
37
+
33
38
  this.cluster = cluster
39
+
34
40
  this.clusterClassName = this.cluster.getClusterer().getClusterClass()
41
+
35
42
  this.className = this.clusterClassName
43
+
36
44
  this.styles = styles
45
+
37
46
  this.center = undefined
47
+
38
48
  this.div = null
49
+
39
50
  this.sums = null
51
+
40
52
  this.visible = false
53
+
41
54
  this.boundsChangedListener = null
55
+
42
56
  this.url = ''
57
+
43
58
  this.height = 0
44
59
  this.width = 0
60
+
45
61
  this.anchorText = [0, 0]
46
62
  this.anchorIcon = [0, 0]
63
+
47
64
  this.textColor = 'black'
48
65
  this.textSize = 11
49
66
  this.textDecoration = 'none'
50
67
  this.fontWeight = 'bold'
51
68
  this.fontStyle = 'normal'
52
69
  this.fontFamily = 'Arial,sans-serif'
70
+
53
71
  this.backgroundPosition = '0 0'
54
- // @ts-ignore
72
+
73
+ this.cMouseDownInCluster = null
74
+ this.cDraggingMapByCluster = null
75
+ this.timeOut = null
76
+
77
+
55
78
  this.setMap(cluster.getMap()) // Note: this causes onAdd to be called
56
79
  }
57
80
 
58
- onAdd() {
59
- let cMouseDownInCluster: boolean
60
- let cDraggingMapByCluster: boolean
81
+ onBoundsChanged() {
82
+ this.cDraggingMapByCluster = this.cMouseDownInCluster
83
+ }
61
84
 
62
- this.div = document.createElement('div')
63
- this.div.className = this.className
64
- if (this.visible) {
65
- this.show()
66
- }
85
+ onMouseDown() {
86
+ this.cMouseDownInCluster = true
67
87
 
68
- // @ts-ignore
69
- this.getPanes().overlayMouseTarget.appendChild(this.div)
88
+ this.cDraggingMapByCluster = false
89
+ }
70
90
 
71
- // Fix for Issue 157
72
- this.boundsChangedListener = google.maps.event.addListener(
73
- // @ts-ignore
74
- this.getMap(),
75
- 'boundschanged',
76
- function boundsChanged() {
77
- cDraggingMapByCluster = cMouseDownInCluster
78
- }
79
- )
91
+ onClick(event: Event) {
92
+ this.cMouseDownInCluster = false
80
93
 
81
- google.maps.event.addListener(this.div, 'mousedown', function onMouseDown() {
82
- cMouseDownInCluster = true
83
- cDraggingMapByCluster = false
84
- })
85
-
86
- google.maps.event.addListener(
87
- this.div,
88
- 'click',
89
- (event: Event) => {
90
- cMouseDownInCluster = false
91
-
92
- if (!cDraggingMapByCluster) {
93
- const markerClusterer = this.cluster.getClusterer()
94
-
95
- /**
96
- * This event is fired when a cluster marker is clicked.
97
- * @name MarkerClusterer#click
98
- * @param {Cluster} c The cluster that was clicked.
99
- * @event
100
- */
101
- google.maps.event.trigger(markerClusterer, 'click', this.cluster)
102
- google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster) // deprecated name
103
-
104
- // The default click handler follows. Disable it by setting
105
- // the zoomOnClick property to false.
106
- if (markerClusterer.getZoomOnClick()) {
107
- // Zoom into the cluster.
108
- const maxZoom = markerClusterer.getMaxZoom()
109
-
110
- const bounds = this.cluster.getBounds()
111
-
112
- // @ts-ignore
113
- markerClusterer.getMap().fitBounds(bounds)
114
-
115
- // There is a fix for Issue 170 here:
116
- setTimeout(function timeout() {
117
- // @ts-ignore
118
- markerClusterer.getMap().fitBounds(bounds)
119
-
120
- // Don't zoom beyond the max zoom level
121
- // @ts-ignore
122
- if (maxZoom !== null && markerClusterer.getMap().getZoom() > maxZoom) {
123
- // @ts-ignore
124
- markerClusterer.getMap().setZoom(maxZoom + 1)
125
- }
126
- }, 100)
127
- }
94
+ if (!this.cDraggingMapByCluster) {
95
+ const markerClusterer = this.cluster.getClusterer()
128
96
 
129
- // Prevent event propagation to the map:
130
- event.cancelBubble = true
97
+ /**
98
+ * This event is fired when a cluster marker is clicked.
99
+ * @name MarkerClusterer#click
100
+ * @param {Cluster} c The cluster that was clicked.
101
+ * @event
102
+ */
103
+ google.maps.event.trigger(markerClusterer, 'click', this.cluster)
104
+ google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster) // deprecated name
131
105
 
132
- if (event.stopPropagation) {
133
- event.stopPropagation()
134
- }
106
+ // The default click handler follows. Disable it by setting
107
+ // the zoomOnClick property to false.
108
+ if (markerClusterer.getZoomOnClick()) {
109
+ // Zoom into the cluster.
110
+ const maxZoom = markerClusterer.getMaxZoom()
111
+
112
+ const bounds = this.cluster.getBounds()
113
+
114
+ const map = markerClusterer.getMap()
115
+
116
+ if (map !== null && 'fitBounds' in map) {
117
+ map.fitBounds(bounds)
135
118
  }
119
+
120
+
121
+ // There is a fix for Issue 170 here:
122
+ this.timeOut = window.setTimeout(() => {
123
+ const map = markerClusterer.getMap()
124
+
125
+ if (map !== null) {
126
+ if ('fitBounds' in map) {
127
+ map.fitBounds(bounds)
128
+ }
129
+
130
+ const zoom = map.getZoom() || 0
131
+
132
+ // Don't zoom beyond the max zoom level
133
+ if (
134
+ maxZoom !== null &&
135
+ zoom > maxZoom
136
+ ) {
137
+ map.setZoom(maxZoom + 1)
138
+ }
139
+ }
140
+ }, 100)
136
141
  }
137
- )
138
142
 
139
- google.maps.event.addListener(
140
- this.div,
141
- 'mouseover',
142
- () => {
143
- /**
144
- * This event is fired when the mouse moves over a cluster marker.
145
- * @name MarkerClusterer#mouseover
146
- * @param {Cluster} c The cluster that the mouse moved over.
147
- * @event
148
- */
149
- google.maps.event.trigger(this.cluster.getClusterer(), 'mouseover', this.cluster)
143
+ // Prevent event propagation to the map:
144
+ event.cancelBubble = true
145
+
146
+ if (event.stopPropagation) {
147
+ event.stopPropagation()
150
148
  }
149
+ }
150
+ }
151
+
152
+ onMouseOver() {
153
+ /**
154
+ * This event is fired when the mouse moves over a cluster marker.
155
+ * @name MarkerClusterer#mouseover
156
+ * @param {Cluster} c The cluster that the mouse moved over.
157
+ * @event
158
+ */
159
+ google.maps.event.trigger(
160
+ this.cluster.getClusterer(),
161
+ 'mouseover',
162
+ this.cluster
151
163
  )
164
+ }
152
165
 
153
- google.maps.event.addListener(
154
- this.div,
166
+ onMouseOut() {
167
+ /**
168
+ * This event is fired when the mouse moves out of a cluster marker.
169
+ * @name MarkerClusterer#mouseout
170
+ * @param {Cluster} c The cluster that the mouse moved out of.
171
+ * @event
172
+ */
173
+ google.maps.event.trigger(
174
+ this.cluster.getClusterer(),
155
175
  'mouseout',
156
- () => {
157
- /**
158
- * This event is fired when the mouse moves out of a cluster marker.
159
- * @name MarkerClusterer#mouseout
160
- * @param {Cluster} c The cluster that the mouse moved out of.
161
- * @event
162
- */
163
- google.maps.event.trigger(this.cluster.getClusterer(), 'mouseout', this.cluster)
164
- }
176
+ this.cluster
165
177
  )
166
178
  }
167
179
 
180
+ onAdd() {
181
+ this.div = document.createElement('div')
182
+
183
+ this.div.className = this.className
184
+
185
+ if (this.visible) {
186
+ this.show()
187
+ }
188
+
189
+ this.getPanes()?.overlayMouseTarget.appendChild(this.div)
190
+
191
+ const map = this.getMap()
192
+
193
+ if (map !== null) {
194
+ // Fix for Issue 157
195
+ this.boundsChangedListener = google.maps.event.addListener(
196
+ map,
197
+ 'bounds_changed',
198
+ this.onBoundsChanged
199
+ )
200
+
201
+ this.div.addEventListener('mousedown', this.onMouseDown)
202
+
203
+ this.div.addEventListener('click', this.onClick)
204
+
205
+ this.div.addEventListener('mouseover', this.onMouseOver)
206
+
207
+ this.div.addEventListener('mouseout', this.onMouseOut)
208
+ }
209
+ }
210
+
168
211
  onRemove() {
169
212
  if (this.div && this.div.parentNode) {
170
213
  this.hide()
@@ -173,20 +216,32 @@ export class ClusterIcon {
173
216
  google.maps.event.removeListener(this.boundsChangedListener)
174
217
  }
175
218
 
176
- google.maps.event.clearInstanceListeners(this.div)
219
+ this.div.removeEventListener('mousedown', this.onMouseDown)
220
+
221
+ this.div.removeEventListener('click', this.onClick)
222
+
223
+ this.div.removeEventListener('mouseover', this.onMouseOver)
224
+
225
+ this.div.removeEventListener('mouseout', this.onMouseOut)
177
226
 
178
227
  this.div.parentNode.removeChild(this.div)
179
228
 
229
+ if (this.timeOut !== null) {
230
+ window.clearTimeout(this.timeOut)
231
+
232
+ this.timeOut = null
233
+ }
234
+
180
235
  this.div = null
181
236
  }
182
237
  }
183
238
 
184
239
  draw() {
185
240
  if (this.visible && this.div !== null && this.center) {
186
- const { x, y } = this.getPosFromLatLng(this.center)
241
+ const pos = this.getPosFromLatLng(this.center)
187
242
 
188
- this.div.style.top = y + 'px'
189
- this.div.style.left = x + 'px'
243
+ this.div.style.top = pos !== null ? `${pos.y}px` : '0'
244
+ this.div.style.left = pos !== null ? `${pos.x}px` : '0'
190
245
  }
191
246
  }
192
247
 
@@ -210,7 +265,11 @@ export class ClusterIcon {
210
265
 
211
266
  const pos = this.getPosFromLatLng(this.center)
212
267
 
213
- if (this.sums === null || typeof this.sums.title === 'undefined' || this.sums.title === '') {
268
+ if (
269
+ this.sums === null ||
270
+ typeof this.sums.title === 'undefined' ||
271
+ this.sums.title === ''
272
+ ) {
214
273
  divTitle = this.cluster.getClusterer().getTitle()
215
274
  } else {
216
275
  divTitle = this.sums.title
@@ -218,12 +277,15 @@ export class ClusterIcon {
218
277
 
219
278
  this.div.style.cursor = 'pointer'
220
279
  this.div.style.position = 'absolute'
221
- this.div.style.top = `${pos.y}px`
222
- this.div.style.left = `${pos.x}px`
280
+
281
+ this.div.style.top = pos !== null ? `${pos.y}px` : '0'
282
+ this.div.style.left = pos !== null ? `${pos.x}px` : '0'
283
+
223
284
  this.div.style.width = `${this.width}px`
224
285
  this.div.style.height = `${this.height}px`
225
286
 
226
287
  const img = document.createElement('img')
288
+
227
289
  img.alt = divTitle
228
290
  img.src = this.url
229
291
  img.style.position = 'absolute'
@@ -231,10 +293,13 @@ export class ClusterIcon {
231
293
  img.style.left = `${spriteH}px`
232
294
 
233
295
  if (!this.cluster.getClusterer().enableRetinaIcons) {
234
- img.style.clip = `rect(-${spriteV}px, -${spriteH + this.width}px, -${spriteV + this.height}, -${spriteH})`
296
+ img.style.clip = `rect(-${spriteV}px, -${spriteH + this.width}px, -${
297
+ spriteV + this.height
298
+ }, -${spriteH})`
235
299
  }
236
300
 
237
301
  const textElm = document.createElement('div')
302
+
238
303
  textElm.style.position = 'absolute'
239
304
  textElm.style.top = `${this.anchorText[0]}px`
240
305
  textElm.style.left = `${this.anchorText[1]}px`
@@ -250,9 +315,12 @@ export class ClusterIcon {
250
315
  textElm.innerText = `${this.sums?.text}`
251
316
 
252
317
  this.div.innerHTML = ''
318
+
253
319
  this.div.appendChild(img)
254
320
  this.div.appendChild(textElm)
321
+
255
322
  this.div.title = divTitle
323
+
256
324
  this.div.style.display = ''
257
325
  }
258
326
 
@@ -261,14 +329,18 @@ export class ClusterIcon {
261
329
 
262
330
  useStyle(sums: ClusterIconInfo) {
263
331
  this.sums = sums
332
+
264
333
  const styles = this.cluster.getClusterer().getStyles()
265
- const style = styles[Math.min(styles.length - 1, Math.max(0, sums.index - 1))]
334
+
335
+ const style =
336
+ styles[Math.min(styles.length - 1, Math.max(0, sums.index - 1))]
266
337
 
267
338
  this.url = style.url
268
339
  this.height = style.height
269
340
  this.width = style.width
270
341
 
271
- if (style.className) this.className = `${this.clusterClassName} ${style.className}`
342
+ if (style.className)
343
+ this.className = `${this.clusterClassName} ${style.className}`
272
344
 
273
345
  this.anchorText = style.anchorText || [0, 0]
274
346
  this.anchorIcon = style.anchorIcon || [this.height / 2, this.width / 2]
@@ -292,17 +364,14 @@ export class ClusterIcon {
292
364
  this.center = center
293
365
  }
294
366
 
295
- getPosFromLatLng(latlng: google.maps.LatLng): google.maps.Point {
296
- // @ts-ignore
367
+ getPosFromLatLng(latlng: google.maps.LatLng): google.maps.Point | null {
297
368
  const pos = this.getProjection().fromLatLngToDivPixel(latlng)
298
369
 
299
- pos.x -= this.anchorIcon[1]
300
-
301
- pos.y -= this.anchorIcon[0]
370
+ if (pos !== null) {
371
+ pos.x -= this.anchorIcon[1]
302
372
 
303
- // pos.x = pos.x
304
-
305
- // pos.y = pos.y
373
+ pos.y -= this.anchorIcon[0]
374
+ }
306
375
 
307
376
  return pos
308
377
  }