Dropdown and Templating Support for AutoComplete

pull/12/head
cagataycivici 2019-01-28 15:20:01 +03:00
parent 6aa9680df9
commit 8cdb74834a
2 changed files with 82 additions and 17 deletions

View File

@ -2,6 +2,7 @@
<span :class="containerClass"> <span :class="containerClass">
<input ref="input" :class="inputClass" v-bind="$attrs" v-on="listeners" :value="inputValue" type="text" autoComplete="off"> <input ref="input" :class="inputClass" v-bind="$attrs" v-on="listeners" :value="inputValue" type="text" autoComplete="off">
<i className="p-autocomplete-loader pi pi-spinner pi-spin" v-show="searching"></i> <i className="p-autocomplete-loader pi pi-spinner pi-spin" v-show="searching"></i>
<Button ref="dropdownButton" type="button" icon="pi pi-fw pi-chevron-down" class="p-autocomplete-dropdown" :disabled="disabled" @click="onDropdownClick" v-if="dropdown"/>
<transition name="p-input-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave"> <transition name="p-input-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
<div ref="overlay" class="p-autocomplete-panel" :style="{'max-height': scrollHeight}" v-if="overlayVisible"> <div ref="overlay" class="p-autocomplete-panel" :style="{'max-height': scrollHeight}" v-if="overlayVisible">
<ul class="p-autocomplete-items p-autocomplete-list p-component"> <ul class="p-autocomplete-items p-autocomplete-list p-component">
@ -19,6 +20,7 @@
<script> <script>
import ObjectUtils from '../utils/ObjectUtils'; import ObjectUtils from '../utils/ObjectUtils';
import DomHandler from '../utils/DomHandler'; import DomHandler from '../utils/DomHandler';
import Button from '../button/Button';
export default { export default {
inheritAttrs: false, inheritAttrs: false,
@ -26,6 +28,10 @@ export default {
value: null, value: null,
suggestions: Array, suggestions: Array,
dropdown: Boolean, dropdown: Boolean,
dropdownMode: {
type: String,
default: 'blank'
},
multiple: Boolean, multiple: Boolean,
disabled: Boolean, disabled: Boolean,
field: String, field: String,
@ -67,6 +73,7 @@ export default {
}, },
methods: { methods: {
onOverlayEnter() { onOverlayEnter() {
this.$refs.overlay.style.zIndex = String(DomHandler.generateZIndex());
this.alignOverlay(); this.alignOverlay();
this.bindOutsideClickListener(); this.bindOutsideClickListener();
}, },
@ -79,20 +86,18 @@ export default {
bindOutsideClickListener() { bindOutsideClickListener() {
if (!this.outsideClickListener) { if (!this.outsideClickListener) {
this.outsideClickListener = (event) => { this.outsideClickListener = (event) => {
if (this.overlayVisible && this.$refs.overlay && this.isOutsideClicked()) { if (this.overlayVisible && this.$refs.overlay && this.isOutsideClicked(event)) {
this.hideOverlay(); this.hideOverlay();
} }
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
isOutsideClicked() { isOutsideClicked(event) {
if (this.multiple) { return !this.$refs.overlay.contains(event.target) && event.target !== this.getInputElement() && !this.isDropdownClicked(event);
return !this.$refs.overlay.contains(event.target) && event.target !== this.$refs.input; },
} isDropdownClicked(event) {
else { return this.$refs.dropdownButton ? (event.target === this.$refs.dropdownButton || this.$refs.dropdownButton.$el.contains(event.target)) : false;
return !this.$refs.overlay.contains(event.target) && event.target !== this.$refs.input;
}
}, },
unbindOutsideClickListener() { unbindOutsideClickListener() {
if (this.outsideClickListener) { if (this.outsideClickListener) {
@ -120,6 +125,20 @@ export default {
this.focus(); this.focus();
this.hideOverlay(); this.hideOverlay();
}, },
onDropdownClick(event) {
this.focus();
const query = this.getInputElement().value;
if (this.dropdownMode === 'blank')
this.search(event, '', 'dropdown');
else if (this.dropdownMode === 'current')
this.search(event, query, 'dropdown');
this.$emit('click-dropdown', {
originalEvent: event,
query: query
});
},
getItemContent(item) { getItemContent(item) {
return this.field ? ObjectUtils.resolveFieldData(item, this.field) : item; return this.field ? ObjectUtils.resolveFieldData(item, this.field) : item;
}, },
@ -130,10 +149,7 @@ export default {
this.overlayVisible = false; this.overlayVisible = false;
}, },
focus() { focus() {
if (this.multiple) this.getInputElement().focus();
this.$refs.multiInput.focus();
else
this.$refs.input.focus();
}, },
search(event, query, source) { search(event, query, source) {
//allow empty string but not undefined or null //allow empty string but not undefined or null
@ -151,6 +167,9 @@ export default {
originalEvent: event, originalEvent: event,
query: query query: query
}); });
},
getInputElement() {
return this.multiple ? this.$refs.inputMultiple : this.$refs.input;
} }
}, },
computed: { computed: {
@ -213,12 +232,15 @@ export default {
return resolvedFieldData != null ? resolvedFieldData : this.value; return resolvedFieldData != null ? resolvedFieldData : this.value;
} }
else else
return value; return this.value;
} }
else { else {
return ''; return '';
} }
} }
},
components: {
'Button': Button
} }
} }
</script> </script>

View File

@ -9,8 +9,19 @@
<div class="content-section implementation"> <div class="content-section implementation">
<h3 class="first">Basic</h3> <h3 class="first">Basic</h3>
<AutoComplete v-model="country" :suggestions="countriesBasic" @complete="searchCountryBasic($event)" field="name" /> <AutoComplete v-model="country" :suggestions="filteredCountriesBasic" @complete="searchCountryBasic($event)" field="name" />
<span style="marginLeft: .5em">Country: {{country || 'none'}}</span> <span style="marginLeft: .5em">Country: {{country || 'none'}}</span>
<h3>Dropdown and Templating</h3>
<AutoComplete v-model="brand" :suggestions="filteredBrands" @complete="searchBrand($event)" placeholder="Hint: type 'v' or 'f'" :dropdown="true">
<template slot="item" slot-scope="{item}">
<div class="p-clearfix p-autocomplete-brand-item">
<img :alt="item" :src="'/demo/images/car/' + item + '.png'" />
<div>{{item}}</div>
</div>
</template>
</AutoComplete>
<span style="marginLeft: .5em">Brand: {{brand || 'none'}}</span>
</div> </div>
</div> </div>
</template> </template>
@ -23,7 +34,10 @@ export default {
return { return {
countries: null, countries: null,
country: null, country: null,
countriesBasic: null filteredCountriesBasic: null,
brands: null,
brand: null,
filteredBrands: null
} }
}, },
countryService: null, countryService: null,
@ -32,6 +46,7 @@ export default {
}, },
mounted() { mounted() {
this.countryService.getCountries().then(data => this.countries = data); this.countryService.getCountries().then(data => this.countries = data);
this.brands = ['Audi', 'BMW', 'Fiat', 'Ford', 'Honda', 'Jaguar', 'Mercedes', 'Renault', 'Volvo'];
}, },
methods: { methods: {
searchCountryBasic(event) { searchCountryBasic(event) {
@ -39,13 +54,41 @@ export default {
let results = this.countries.filter((country) => { let results = this.countries.filter((country) => {
return country.name.toLowerCase().startsWith(event.query.toLowerCase()); return country.name.toLowerCase().startsWith(event.query.toLowerCase());
}); });
this.countriesBasic = results; this.filteredCountriesBasic = results;
}, 250);
},
searchBrand(event) {
setTimeout(() => {
let results;
if (event.query.length === 0) {
results = [...this.brands];
}
else {
results = this.brands.filter((brand) => {
return brand.toLowerCase().startsWith(event.query.toLowerCase());
});
}
this.filteredBrands = results;
}, 250); }, 250);
} }
} }
} }
</script> </script>
<style> <style lang="scss">
.p-autocomplete-brand-item {
img {
width: 32px;
display: inline-block;
margin: 5px 0 2px 5px;
}
div {
font-size: 16px;
float: right;
margin: 10px 10px 0 0;
}
}
</style> </style>