-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add vanilla address component and Nova address field.
- Loading branch information
Showing
12 changed files
with
559 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
const Address = (function () { | ||
const autocompleteParams = { | ||
input: null, | ||
sessionToken: null, | ||
componentRestrictions: { | ||
country: 'us', | ||
}, | ||
fields: [ | ||
'address_components', | ||
// "place_id", // if needed, Google place ID | ||
// "utc_offset_minutes", // if needed, derive timezone from minutes | ||
], | ||
types: [ | ||
'address', | ||
], | ||
}; | ||
|
||
const placeDetailsParams = { | ||
placeId: null, | ||
sessionToken: null, | ||
fields: [ | ||
'address_components', | ||
// can retrieve more fields if needed for data consistency e.g. timezone | ||
], | ||
}; | ||
|
||
let autocompleteService; | ||
let placesService; | ||
let predictionListElementId; | ||
let placeElementId; | ||
|
||
let resetSessionToken = function () { | ||
let sessionToken = new google.maps.places.AutocompleteSessionToken(); | ||
|
||
autocompleteParams.sessionToken = sessionToken; | ||
placeDetailsParams.sessionToken = sessionToken; | ||
}; | ||
|
||
window.initGoogleMapAutocomplete = function () { | ||
autocompleteService = new google.maps.places.AutocompleteService(); | ||
placesService = new google.maps.places.PlacesService(document.getElementById(placeElementId)); | ||
|
||
resetSessionToken(); | ||
}; | ||
|
||
let getPredictions = function (query) { | ||
return new Promise((resolve, reject) => { | ||
let startsWithStreetNumber = /^\d/; | ||
|
||
if (!startsWithStreetNumber.test(query)) { | ||
hidePredictions(); | ||
reject('Please enter a street number.'); | ||
} else { | ||
showPredictions(); | ||
autocompleteParams.input = query; | ||
autocompleteService.getPlacePredictions(autocompleteParams, (predictions, status) => { | ||
if (status === 'OK') { | ||
resolve(predictions); | ||
} | ||
}); | ||
} | ||
}) | ||
}; | ||
|
||
let getAddressInfo = function (placeId) { | ||
return new Promise((resolve, reject) => { | ||
placeDetailsParams.placeId = placeId; | ||
placesService.getDetails(placeDetailsParams, function (placeDetails, status) { | ||
let addressLine1 = ''; | ||
let city = ''; | ||
let state = ''; | ||
let zip = ''; | ||
|
||
// Get each component of the address from the place details, | ||
// and then fill-in the corresponding field on the form. | ||
// place.address_components are google.maps.GeocoderAddressComponent objects | ||
// which are documented at http://goo.gle/3l5i5Mr | ||
for (const component of placeDetails.address_components) { | ||
// Street number | ||
switch (component.types[0]) { | ||
case 'street_number': | ||
addressLine1 = `${component.long_name} ${addressLine1}`; | ||
break; | ||
|
||
// Street name, e.g. Main Street | ||
case 'route': | ||
addressLine1 += component.long_name; | ||
break; | ||
|
||
// Postal code | ||
case 'postal_code': | ||
zip = `${component.long_name}${zip}`; | ||
break; | ||
|
||
// case "postal_code_suffix": | ||
// postcode = `${postcode}-${component.long_name}`; | ||
// break; | ||
|
||
// City | ||
case 'locality': | ||
city = component.long_name; | ||
break; | ||
|
||
// State | ||
case 'administrative_area_level_1': | ||
state = component.short_name; | ||
break; | ||
|
||
// case "country": | ||
// country = component.long_name; | ||
// break; | ||
} | ||
} | ||
|
||
resolve({ | ||
addressLine1: addressLine1, | ||
city: city, | ||
state: state, | ||
zip: zip | ||
}); | ||
}); | ||
resetSessionToken(); | ||
}); | ||
}; | ||
|
||
let showPredictions = function () { | ||
document.getElementById(predictionListElementId).classList.remove('invisible'); | ||
}; | ||
|
||
let hidePredictions = function () { | ||
document.getElementById(predictionListElementId).classList.add('invisible'); | ||
}; | ||
|
||
let addGoogleMapScript = function (key) { | ||
let documentTag = document, tag = 'script', | ||
object = documentTag.createElement(tag), | ||
scriptTag = documentTag.getElementsByTagName(tag)[0]; | ||
|
||
object.src = `https://maps.googleapis.com/maps/api/js?key=${key}&libraries=places&callback=initGoogleMapAutocomplete`; | ||
|
||
scriptTag.parentNode.insertBefore(object, scriptTag); | ||
}; | ||
|
||
let setup = function (settings) { | ||
// Assign essential element. | ||
predictionListElementId = settings.predictionListElementId; | ||
placeElementId = settings.placeElementId; | ||
|
||
// Add Google Map script. | ||
addGoogleMapScript(settings.googleApiKey); | ||
}; | ||
|
||
// Explicitly reveal public pointers to the private functions | ||
// that we want to reveal publicly | ||
return { | ||
setup: setup, | ||
getPredictions: getPredictions, | ||
showPredictions: showPredictions, | ||
hidePredictions: hidePredictions, | ||
getAddressInfo: getAddressInfo | ||
} | ||
})(); | ||
|
||
export default Address; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<template> | ||
<default-field :field="field" :errors="errors" :show-help-text="showHelpText"> | ||
<template slot="field"> | ||
<input | ||
:ref="field.attribute" | ||
:id="field.attribute" | ||
:dusk="field.attribute" | ||
type="text" | ||
v-model="value" | ||
v-debounce:500ms="getPredictions" | ||
@focus="showPredictions" | ||
v-click-outside="hidePredictions" | ||
class="w-full form-control form-input form-input-bordered" | ||
:class="errorClasses" | ||
:placeholder="field.name" | ||
:disabled="isReadonly" | ||
/> | ||
<div id="results-list" class="absolute top-9 z-10 w-full"> | ||
<div | ||
v-for="prediction in predictions" | ||
:key="prediction.place_id" | ||
@click="getAddressInfo(prediction.place_id)" | ||
class="block w-full px-2 py-1 text-left bg-white cursor-default hover:bg-gray-50" | ||
> | ||
{{ prediction.description }} | ||
</div> | ||
</div> | ||
<div id="attributions"></div> | ||
</template> | ||
</default-field> | ||
</template> | ||
|
||
<script> | ||
import {FormField, HandlesValidationErrors} from 'laravel-nova' | ||
export default { | ||
mixins: [HandlesValidationErrors, FormField], | ||
data() { | ||
return { | ||
placesAutocomplete: null, | ||
predictions: [] | ||
} | ||
}, | ||
/** | ||
* Mount the component. | ||
*/ | ||
mounted() { | ||
this.setInitialValue() | ||
this.field.fill = this.fill | ||
Nova.$on(this.field.attribute + '-value', value => { | ||
this.value = value | ||
}) | ||
this.initializePlaces(); | ||
}, | ||
methods: { | ||
getPredictions() { | ||
this.placesAutocomplete.getPredictions(this.value) | ||
.then(res => { | ||
this.predictions = res; | ||
}) | ||
.catch(error => { | ||
this.placesAutocomplete.hidePredictions(); | ||
Nova.error(error); | ||
}); | ||
}, | ||
showPredictions() { | ||
this.placesAutocomplete.showPredictions(); | ||
}, | ||
hidePredictions() { | ||
this.placesAutocomplete.hidePredictions(); | ||
}, | ||
getAddressInfo(placeId) { | ||
this.placesAutocomplete.getAddressInfo(placeId) | ||
.then(res => { | ||
this.value = res.addressLine1; | ||
Nova.$emit(this.field.secondAddressLine + '-value', ''); | ||
Nova.$emit(this.field.city + '-value', res.city); | ||
Nova.$emit(this.field.postalCode + '-value', res.zip); | ||
Nova.$emit(this.field.state + '-value', res.state); | ||
}); | ||
}, | ||
/** | ||
* Initialize Algolia places library. | ||
*/ | ||
initializePlaces() { | ||
const places = require('./../../address').default; | ||
places.setup({ | ||
googleApiKey: Nova.config.googleMapApiKey, | ||
predictionListElementId: 'results-list', | ||
placeElementId: 'attributions' | ||
}); | ||
this.placesAutocomplete = places; | ||
} | ||
} | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,31 @@ | ||
import VueTelInput from 'vue-tel-input' | ||
import VueTelInput from 'vue-tel-input'; | ||
import vueDebounce from 'vue-debounce'; | ||
|
||
Nova.booting((Vue, router) => { | ||
Vue.use(VueTelInput); | ||
Vue.use(vueDebounce); | ||
|
||
Vue.directive('click-outside', { | ||
bind: function (el, binding, vnode) { | ||
el.clickOutsideEvent = function (event) { | ||
// here I check that click was outside the el and his children | ||
if (!(el == event.target || el.contains(event.target))) { | ||
// and if it did, call method provided in attribute value | ||
vnode.context[binding.expression](event); | ||
} | ||
}; | ||
document.body.addEventListener('click', el.clickOutsideEvent) | ||
}, | ||
unbind: function (el) { | ||
document.body.removeEventListener('click', el.clickOutsideEvent) | ||
}, | ||
}); | ||
|
||
// Phone Number field | ||
Vue.component('index-phone-number', require('./components/PhoneNumber/IndexField')); | ||
Vue.component('detail-phone-number', require('./components/PhoneNumber/DetailField')); | ||
Vue.component('form-phone-number', require('./components/PhoneNumber/FormField')); | ||
|
||
// Address field | ||
Vue.component('form-address-field', require('./components/Address/FormField')); | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.