Add most of components and prepare for V1 Release

This commit is contained in:
Fawad Mirzad
2021-02-13 16:09:48 +01:00
parent 1a5cf0b075
commit decca38b67
47 changed files with 2556 additions and 0 deletions

View 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,
)
}

View 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
View 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
View 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
View 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
}
}

View 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)
}
}
}

View 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
}