Add most of components and prepare for V1 Release
This commit is contained in:
48
src/utils/TwoWayBindingWrapper.js
Normal file
48
src/utils/TwoWayBindingWrapper.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* When you have two-way bindings, but the actual bound value will not equal
|
||||
* the value you initially passed in, then to avoid an infinite loop you
|
||||
* need to increment a counter every time you pass in a value, decrement the
|
||||
* same counter every time the bound value changed, but only bubble up
|
||||
* the event when the counter is zero.
|
||||
*
|
||||
Example:
|
||||
|
||||
Let's say DrawingRecognitionCanvas is a deep-learning backed canvas
|
||||
that, when given the name of an object (e.g. 'dog'), draws a dog.
|
||||
But whenever the drawing on it changes, it also sends back its interpretation
|
||||
of the image by way of the @newObjectRecognized event.
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="an object, e.g. Dog, Cat, Frog"
|
||||
v-model="identifiedObject" />
|
||||
<DrawingRecognitionCanvas
|
||||
:object="identifiedObject"
|
||||
@newObjectRecognized="identifiedObject = $event"
|
||||
/>
|
||||
|
||||
new TwoWayBindingWrapper((increment, decrement, shouldUpdate) => {
|
||||
this.$watch('identifiedObject', () => {
|
||||
// new object passed in
|
||||
increment()
|
||||
})
|
||||
this.$deepLearningBackend.on('drawingChanged', () => {
|
||||
recognizeObject(this.$deepLearningBackend)
|
||||
.then((object) => {
|
||||
decrement()
|
||||
if (shouldUpdate()) {
|
||||
this.$emit('newObjectRecognized', object.name)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
*/
|
||||
export default function TwoWayBindingWrapper (fn) {
|
||||
let counter = 0
|
||||
|
||||
fn(
|
||||
() => { counter += 1 },
|
||||
() => { counter = Math.max(0, counter - 1) },
|
||||
() => counter === 0,
|
||||
)
|
||||
}
|
||||
24
src/utils/WatchPrimitiveProperties.js
Normal file
24
src/utils/WatchPrimitiveProperties.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Watch the individual properties of a PoD object, instead of the object
|
||||
* per se. This is different from a deep watch where both the reference
|
||||
* and the individual values are watched.
|
||||
*
|
||||
* In effect, it throttles the multiple $watch to execute at most once per tick.
|
||||
*/
|
||||
export default function WatchPrimitiveProperties (vueInst, propertiesToTrack, handler, immediate = false) {
|
||||
let isHandled = false
|
||||
|
||||
function requestHandle () {
|
||||
if (!isHandled) {
|
||||
isHandled = true
|
||||
vueInst.$nextTick(() => {
|
||||
isHandled = false
|
||||
handler()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (let prop of propertiesToTrack) {
|
||||
vueInst.$watch(prop, requestHandle, {immediate})
|
||||
}
|
||||
}
|
||||
10
src/utils/bindEvents.js
Normal file
10
src/utils/bindEvents.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export default (vueInst, googleMapsInst, events) => {
|
||||
for (let eventName of events) {
|
||||
if (vueInst.$gmapOptions.autobindAllEvents ||
|
||||
vueInst.$attrs[eventName]) {
|
||||
googleMapsInst.addListener(eventName, (ev) => {
|
||||
vueInst.$emit(eventName, ev)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/utils/bindProps.js
Normal file
74
src/utils/bindProps.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import WatchPrimitiveProperties from '../utils/WatchPrimitiveProperties'
|
||||
|
||||
function capitalizeFirstLetter (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
|
||||
export function getPropsValues (vueInst, props) {
|
||||
return Object.keys(props)
|
||||
.reduce(
|
||||
(acc, prop) => {
|
||||
if (vueInst[prop] !== undefined) {
|
||||
acc[prop] = vueInst[prop]
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the properties defined in props to the google maps instance.
|
||||
* If the prop is an Object type, and we wish to track the properties
|
||||
* of the object (e.g. the lat and lng of a LatLng), then we do a deep
|
||||
* watch. For deep watch, we also prevent the _changed event from being
|
||||
* $emitted if the data source was external.
|
||||
*/
|
||||
export function bindProps (vueInst, googleMapsInst, props, options) {
|
||||
for (let attribute in props) {
|
||||
let {twoWay, type, trackProperties, noBind} = props[attribute]
|
||||
|
||||
if (noBind) continue
|
||||
|
||||
const setMethodName = 'set' + capitalizeFirstLetter(attribute)
|
||||
const getMethodName = 'get' + capitalizeFirstLetter(attribute)
|
||||
const eventName = attribute.toLowerCase() + '_changed'
|
||||
const initialValue = vueInst[attribute]
|
||||
|
||||
if (typeof googleMapsInst[setMethodName] === 'undefined') {
|
||||
throw new Error(`${setMethodName} is not a method of (the Maps object corresponding to) ${vueInst.$options._componentTag}`)
|
||||
}
|
||||
|
||||
// We need to avoid an endless
|
||||
// propChanged -> event $emitted -> propChanged -> event $emitted loop
|
||||
// although this may really be the user's responsibility
|
||||
if (type !== Object || !trackProperties) {
|
||||
// Track the object deeply
|
||||
vueInst.$watch(attribute, () => {
|
||||
const attributeValue = vueInst[attribute]
|
||||
|
||||
googleMapsInst[setMethodName](attributeValue)
|
||||
}, {
|
||||
immediate: typeof initialValue !== 'undefined',
|
||||
deep: type === Object
|
||||
})
|
||||
} else {
|
||||
WatchPrimitiveProperties(
|
||||
vueInst,
|
||||
trackProperties.map(prop => `${attribute}.${prop}`),
|
||||
() => {
|
||||
googleMapsInst[setMethodName](vueInst[attribute])
|
||||
},
|
||||
vueInst[attribute] !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
if (twoWay &&
|
||||
(vueInst.$gmapOptions.autobindAllEvents ||
|
||||
vueInst.$attrs[eventName])) {
|
||||
googleMapsInst.addListener(eventName, (ev) => { // eslint-disable-line no-unused-vars
|
||||
vueInst.$emit(eventName, googleMapsInst[getMethodName]())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/utils/lazyValue.js
Normal file
16
src/utils/lazyValue.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// This piece of code was orignally written by sindresorhus and can be seen here
|
||||
// https://github.com/sindresorhus/lazy-value/blob/master/index.js
|
||||
|
||||
export default fn => {
|
||||
let called = false
|
||||
let ret
|
||||
|
||||
return () => {
|
||||
if (!called) {
|
||||
called = true
|
||||
ret = fn()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
55
src/utils/mountableMixin.js
Normal file
55
src/utils/mountableMixin.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Mixin for objects that are mounted by Google Maps
|
||||
Javascript API.
|
||||
|
||||
These are objects that are sensitive to element resize
|
||||
operations so it exposes a property which accepts a bus
|
||||
|
||||
*/
|
||||
|
||||
export default {
|
||||
props: ['resizeBus'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
_actualResizeBus: null,
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (typeof this.resizeBus === 'undefined') {
|
||||
this.$data._actualResizeBus = this.$gmapDefaultResizeBus
|
||||
} else {
|
||||
this.$data._actualResizeBus = this.resizeBus
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
_resizeCallback () {
|
||||
this.resize()
|
||||
},
|
||||
_delayedResizeCallback () {
|
||||
this.$nextTick(() => this._resizeCallback())
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
resizeBus (newVal, oldVal) { // eslint-disable-line no-unused-vars
|
||||
this.$data._actualResizeBus = newVal
|
||||
},
|
||||
'$data._actualResizeBus' (newVal, oldVal) {
|
||||
if (oldVal) {
|
||||
oldVal.$off('resize', this._delayedResizeCallback)
|
||||
}
|
||||
if (newVal) {
|
||||
// newVal.$on('resize', this._delayedResizeCallback)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
unmounted () {
|
||||
if (this.$data._actualResizeBus) {
|
||||
this.$data._actualResizeBus.$off('resize', this._delayedResizeCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/utils/simulateArrowDown.js
Normal file
28
src/utils/simulateArrowDown.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// This piece of code was orignally written by amirnissim and can be seen here
|
||||
// http://stackoverflow.com/a/11703018/2694653
|
||||
// This has been ported to Vanilla.js by GuillaumeLeclerc
|
||||
export default (input) => {
|
||||
var _addEventListener = (input.addEventListener) ? input.addEventListener : input.attachEvent
|
||||
|
||||
function addEventListenerWrapper (type, listener) {
|
||||
// Simulate a 'down arrow' keypress on hitting 'return' when no pac suggestion is selected,
|
||||
// and then trigger the original listener.
|
||||
if (type === 'keydown') {
|
||||
var origListener = listener
|
||||
listener = function (event) {
|
||||
var suggestionSelected = document.getElementsByClassName('pac-item-selected').length > 0
|
||||
if (event.which === 13 && !suggestionSelected) {
|
||||
var simulatedEvent = document.createEvent('Event')
|
||||
simulatedEvent.keyCode = 40
|
||||
simulatedEvent.which = 40
|
||||
origListener.apply(input, [simulatedEvent])
|
||||
}
|
||||
origListener.apply(input, [event])
|
||||
}
|
||||
}
|
||||
_addEventListener.apply(input, [type, listener])
|
||||
}
|
||||
|
||||
input.addEventListener = addEventListenerWrapper
|
||||
input.attachEvent = addEventListenerWrapper
|
||||
}
|
||||
Reference in New Issue
Block a user