Files
vue-google-maps-community-fork/src/components/map.vue
2022-12-16 11:14:19 -08:00

217 lines
5.0 KiB
Vue

<template>
<div class="vue-map-container" :class="$attrs.class">
<div ref="vue-map" class="vue-map" :style="$attrs.style ? $attrs.style : ''"></div>
<div class="vue-map-hidden">
<slot></slot>
</div>
<slot name="visible"></slot>
</div>
</template>
<script>
import bindEvents from '../utils/bindEvents.js'
import { bindProps, getPropsValues } from '../utils/bindProps.js'
import mountableMixin from '../utils/mountableMixin.js'
import TwoWayBindingWrapper from '../utils/TwoWayBindingWrapper.js'
import WatchPrimitiveProperties from '../utils/WatchPrimitiveProperties.js'
import { mappedPropsToVueProps } from './build-component.js'
const props = {
center: {
required: true,
twoWay: true,
type: Object,
noBind: true,
},
zoom: {
required: false,
twoWay: true,
type: Number,
noBind: true,
},
heading: {
type: Number,
twoWay: true,
},
mapTypeId: {
twoWay: true,
type: String,
},
tilt: {
twoWay: true,
type: Number,
},
options: {
type: Object,
default() {
return {}
},
},
}
const events = [
'bounds_changed',
'click',
'dblclick',
'drag',
'dragend',
'dragstart',
'idle',
'mousemove',
'mouseout',
'mouseover',
'resize',
'rightclick',
'tilesloaded',
]
// Plain Google Maps methods exposed here for convenience
const linkedMethods = ['panBy', 'panTo', 'panToBounds', 'fitBounds'].reduce((all, methodName) => {
all[methodName] = function () {
if (this.$mapObject) {
this.$mapObject[methodName].apply(this.$mapObject, arguments)
}
}
return all
}, {})
// Other convenience methods exposed by Vue Google Maps
const customMethods = {
resize() {
if (this.$mapObject) {
google.maps.event.trigger(this.$mapObject, 'resize')
}
},
resizePreserveCenter() {
if (!this.$mapObject) {
return
}
const oldCenter = this.$mapObject.getCenter()
google.maps.event.trigger(this.$mapObject, 'resize')
this.$mapObject.setCenter(oldCenter)
},
/// Override mountableMixin::_resizeCallback
/// because resizePreserveCenter is usually the
/// expected behaviour
_resizeCallback() {
this.resizePreserveCenter()
},
}
export default {
mixins: [mountableMixin],
props: mappedPropsToVueProps({
...props,
...events.reduce(
(obj, eventName) => ({
...obj,
[`on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`.replace(
/[-_]+(.)?/g,
(_, c) => (c ? c.toUpperCase() : '')
)]: Function,
}),
{}
),
}),
inheritAttrs: false,
provide() {
this.$mapPromise = new Promise((resolve, reject) => {
this.$mapPromiseDeferred = { resolve, reject }
})
return {
$mapPromise: this.$mapPromise,
}
},
emits: ['center_changed', 'zoom_changed', 'bounds_changed'],
computed: {
finalLat() {
return this.center && typeof this.center.lat === 'function'
? this.center.lat()
: this.center.lat
},
finalLng() {
return this.center && typeof this.center.lng === 'function'
? this.center.lng()
: this.center.lng
},
finalLatLng() {
return { lat: this.finalLat, lng: this.finalLng }
},
},
watch: {
zoom(zoom) {
if (this.$mapObject) {
this.$mapObject.setZoom(zoom)
}
},
},
mounted() {
return this.$gmapApiPromiseLazy()
.then(() => {
// getting the DOM element where to create the map
const element = this.$refs['vue-map']
// creating the map
const options = {
...this.options,
...getPropsValues(this, props),
}
delete options.options
this.$mapObject = new google.maps.Map(element, options)
// binding properties (two and one way)
bindProps(this, this.$mapObject, props)
// binding events
bindEvents(this, this.$mapObject, events)
// manually trigger center and zoom
TwoWayBindingWrapper((increment, decrement, shouldUpdate) => {
this.$mapObject.addListener('center_changed', () => {
if (shouldUpdate()) {
this.$emit('center_changed', this.$mapObject.getCenter())
}
decrement()
})
const updateCenter = () => {
increment()
this.$mapObject.setCenter(this.finalLatLng)
}
WatchPrimitiveProperties(this, ['finalLat', 'finalLng'], updateCenter)
})
this.$mapObject.addListener('zoom_changed', () => {
this.$emit('zoom_changed', this.$mapObject.getZoom())
})
this.$mapObject.addListener('bounds_changed', () => {
this.$emit('bounds_changed', this.$mapObject.getBounds())
})
this.$mapPromiseDeferred.resolve(this.$mapObject)
return this.$mapObject
})
.catch((error) => {
throw error
})
},
methods: {
...customMethods,
...linkedMethods,
},
}
</script>
<style>
.vue-map {
width: 100%;
height: 100%;
min-height: 2rem;
}
</style>