154 lines
4.8 KiB
JavaScript
154 lines
4.8 KiB
JavaScript
import bindEvents from '../utils/bindEvents.js'
|
|
import { bindProps, getPropsValues } from '../utils/bindProps.js'
|
|
import MapElementMixin from './mapElementMixin'
|
|
|
|
/**
|
|
*
|
|
* @param {Object} options
|
|
* @param {Object} options.mappedProps - Definitions of props
|
|
* @param {Object} options.mappedProps.PROP.type - Value type
|
|
* @param {Boolean} options.mappedProps.PROP.twoWay
|
|
* - Whether the prop has a corresponding PROP_changed
|
|
* event
|
|
* @param {Boolean} options.mappedProps.PROP.noBind
|
|
* - If true, do not apply the default bindProps / bindEvents.
|
|
* However it will still be added to the list of component props
|
|
* @param {Object} options.props - Regular Vue-style props.
|
|
* Note: must be in the Object form because it will be
|
|
* merged with the `mappedProps`
|
|
*
|
|
* @param {Object} options.events - Google Maps API events
|
|
* that are not bound to a corresponding prop
|
|
* @param {String} options.name - e.g. `polyline`
|
|
* @param {=> String} options.ctr - constructor, e.g.
|
|
* `google.maps.Polyline`. However, since this is not
|
|
* generally available during library load, this becomes
|
|
* a function instead, e.g. () => google.maps.Polyline
|
|
* which will be called only after the API has been loaded
|
|
* @param {(MappedProps, OtherVueProps) => Array} options.ctrArgs -
|
|
* If the constructor in `ctr` needs to be called with
|
|
* arguments other than a single `options` object, e.g. for
|
|
* GroundOverlay, we call `new GroundOverlay(url, bounds, options)`
|
|
* then pass in a function that returns the argument list as an array
|
|
*
|
|
* Otherwise, the constructor will be called with an `options` object,
|
|
* with property and values merged from:
|
|
*
|
|
* 1. the `options` property, if any
|
|
* 2. a `map` property with the Google Maps
|
|
* 3. all the properties passed to the component in `mappedProps`
|
|
* @param {Object => Any} options.beforeCreate -
|
|
* Hook to modify the options passed to the initializer
|
|
* @param {(options.ctr, Object) => Any} options.afterCreate -
|
|
* Hook called when
|
|
*
|
|
*/
|
|
export default function (options) {
|
|
const {
|
|
mappedProps,
|
|
name,
|
|
ctr,
|
|
ctrArgs,
|
|
events,
|
|
beforeCreate,
|
|
afterCreate,
|
|
props,
|
|
...rest
|
|
} = options
|
|
|
|
const promiseName = `$${name}Promise`
|
|
const instanceName = `$${name}Object`
|
|
|
|
assert(!(rest.props instanceof Array), '`props` should be an object, not Array')
|
|
|
|
return {
|
|
...(typeof GENERATE_DOC !== 'undefined' ? { $vgmOptions: options } : {}),
|
|
mixins: [MapElementMixin],
|
|
props: {
|
|
...props,
|
|
...mappedPropsToVueProps(mappedProps),
|
|
},
|
|
render() {
|
|
return ''
|
|
},
|
|
provide() {
|
|
const promise = this.$mapPromise
|
|
.then((map) => {
|
|
// Infowindow needs this to be immediately available
|
|
this.$map = map
|
|
|
|
// Initialize the maps with the given options
|
|
const options = {
|
|
...this.options,
|
|
map,
|
|
...getPropsValues(this, mappedProps),
|
|
}
|
|
delete options.options // delete the extra options
|
|
|
|
if (beforeCreate) {
|
|
const result = beforeCreate.bind(this)(options)
|
|
|
|
if (result instanceof Promise) {
|
|
return result.then(() => ({ options }))
|
|
}
|
|
}
|
|
return { options }
|
|
})
|
|
.then(({ options }) => {
|
|
const ConstructorObject = ctr()
|
|
// https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
|
this[instanceName] = ctrArgs
|
|
? new (Function.prototype.bind.call(
|
|
ConstructorObject,
|
|
null,
|
|
...ctrArgs(options, getPropsValues(this, props || {}))
|
|
))()
|
|
: new ConstructorObject(options)
|
|
|
|
bindProps(this, this[instanceName], mappedProps)
|
|
bindEvents(this, this[instanceName], events)
|
|
|
|
if (afterCreate) {
|
|
afterCreate.bind(this)(this[instanceName])
|
|
}
|
|
return this[instanceName]
|
|
})
|
|
this[promiseName] = promise
|
|
return { [promiseName]: promise }
|
|
},
|
|
unmounted() {
|
|
// Note: not all Google Maps components support maps
|
|
if (this[instanceName] && this[instanceName].setMap) {
|
|
this[instanceName].setMap(null)
|
|
}
|
|
},
|
|
...rest,
|
|
}
|
|
}
|
|
|
|
function assert(v, message) {
|
|
if (!v) throw new Error(message)
|
|
}
|
|
|
|
/**
|
|
* Strips out the extraneous properties we have in our
|
|
* props definitions
|
|
* @param {Object} props
|
|
*/
|
|
export function mappedPropsToVueProps(mappedProps) {
|
|
return Object.entries(mappedProps)
|
|
.map(([key, prop]) => {
|
|
const value = {}
|
|
|
|
if ('type' in prop) value.type = prop.type
|
|
if ('default' in prop) value.default = prop.default
|
|
if ('required' in prop) value.required = prop.required
|
|
|
|
return [key, value]
|
|
})
|
|
.reduce((acc, [key, val]) => {
|
|
acc[key] = val
|
|
return acc
|
|
}, {})
|
|
}
|