217 lines
5.0 KiB
Vue
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>
|