159 lines
5.6 KiB
JavaScript
159 lines
5.6 KiB
JavaScript
Vue.component('tt-autocomplete', {
|
|
template: `
|
|
<div class="form-group">
|
|
<slot name="prepend"></slot>
|
|
<label v-if="label" :for="label">{{ label }}</label>
|
|
<div class="autocomplete position-relative">
|
|
<input
|
|
type="text"
|
|
class="form-control form-control-sm"
|
|
v-model="displayValue"
|
|
@input="onInput"
|
|
@focus="onFocus"
|
|
@blur="onBlur"
|
|
:style="{'padding-right': $slots.append ? '30px' : '0'}"
|
|
/>
|
|
<slot name="append"></slot>
|
|
|
|
<ul v-show="showSuggestions && displayValue.length > 0 || isLoading"
|
|
class="dropdown-menu show dropdown-shadow">
|
|
|
|
<div v-show="isLoading" class="loader"></div>
|
|
|
|
<template v-show="showSuggestions && displayingItems.length > 0 && isLoading !== true">
|
|
<li
|
|
v-for="(item) in displayingItems.slice(0, 10)"
|
|
:key="item.value"
|
|
:class="{'active': value === item.value}"
|
|
class="dropdown-item"
|
|
@click.prevent="selectSuggestion(item)"
|
|
style="cursor: pointer;"
|
|
>
|
|
{{ item.text }}
|
|
</li>
|
|
<!-- display more search results available define it more precisely in German -->
|
|
<li v-show="displayingItems.length > 10" class="dropdown-item disabled">
|
|
Mehr Suchergebnisse vorhanden. Bitte genauer eingeben
|
|
</li>
|
|
</template>
|
|
<li v-show="displayingItems.length === 0 && isLoading === false && displayValue.length > 3"
|
|
class="dropdown-item disabled">
|
|
Keine Suchergebnisse vorhanden.
|
|
</li>
|
|
<li v-show="displayValue.length < 3" class="dropdown-item disabled">
|
|
Bitte mindestens 3 Zeichen eingeben
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
`,
|
|
// TODO: Implement giving the option without the need of an API || need to use computed property to filter the items
|
|
// TODO: Fix the weirdness with timeout and selecting the suggestion
|
|
props: {
|
|
value: {
|
|
type: [String, Number]
|
|
},
|
|
label: {
|
|
type: String,
|
|
required: false,
|
|
},
|
|
apiUrl: String,
|
|
items: {
|
|
type: Array,
|
|
default: () => [],
|
|
}
|
|
},
|
|
async mounted() {
|
|
if (this.value && this.apiUrl) {
|
|
const response = await axios.get(`${this.apiUrl}&autocomplete=1&searchedID=${this.value}`);
|
|
this.displayValue = response.data[0].text;
|
|
} else if (this.value) {
|
|
const selectedItem = this.items.find(item => item.value === this.value);
|
|
this.displayValue = selectedItem ? selectedItem.text : '';
|
|
}
|
|
|
|
},
|
|
data() {
|
|
return {
|
|
displayingItems: [],
|
|
displayValue: '',
|
|
isLoading: false,
|
|
showSuggestions: false,
|
|
cursor: -1,
|
|
fetchSuggestionsDebounceTimer: null,
|
|
};
|
|
},
|
|
watch: {
|
|
value(newValue) {
|
|
const selectedItem = this.displayingItems.find(item => item.value === newValue);
|
|
this.displayValue = selectedItem ? selectedItem.text : '';
|
|
},
|
|
apiUrl() {
|
|
this.fetchSuggestions();
|
|
},
|
|
},
|
|
methods: {
|
|
onInput(event) {
|
|
this.displayValue = event.target.value;
|
|
this.$emit('input', undefined);
|
|
this.fetchSuggestions();
|
|
},
|
|
onFocus() {
|
|
this.showSuggestions = true;
|
|
},
|
|
onBlur() {
|
|
setTimeout(() => {
|
|
this.showSuggestions = false;
|
|
}, 200);
|
|
},
|
|
fetchSuggestions() {
|
|
if (this.displayValue.length < 3) {
|
|
this.$set(this, 'displayingItems', []);
|
|
return this.displayingItems = [];
|
|
}
|
|
|
|
if (!this.apiUrl) {
|
|
|
|
const isNegated = this.displayValue.startsWith('!');
|
|
const substrings = (isNegated ? this.displayValue.slice(1) : this.displayValue).split(' ').map(s => s.toLowerCase());
|
|
this.displayingItems = this.items.filter(item => {
|
|
let substringMatch = true;
|
|
for (var k = 0, klen = substrings.length; k < klen; ++k) {
|
|
if (!item.text.toLowerCase().includes(substrings[k])) {
|
|
substringMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
return (isNegated && !substringMatch) || (!isNegated && substringMatch);
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
this.isLoading = true;
|
|
clearTimeout(this.fetchSuggestionsDebounceTimer);
|
|
|
|
this.fetchSuggestionsDebounceTimer = setTimeout(() => {
|
|
// Simulate the API call
|
|
setTimeout(async () => {
|
|
const response = await axios.get(`${this.apiUrl}&autocomplete=1&q=${this.displayValue}`);
|
|
if (response.data?.status === 'error') {
|
|
this.displayingItems = [];
|
|
} else {
|
|
this.displayingItems = response.data
|
|
}
|
|
this.isLoading = false;
|
|
|
|
this.fetchSuggestionsDebounceTimer = null;
|
|
}, 100);
|
|
}, 300); // Adjust the 300ms debounce time as needed
|
|
}
|
|
,
|
|
selectSuggestion(item) {
|
|
this.$emit('input', item.value);
|
|
this.displayValue = item.text;
|
|
this.showSuggestions = false;
|
|
},
|
|
},
|
|
});
|