Files
thetool/public/plugins/vue/tt-components/tt-autocomplete.js
2024-07-16 06:55:46 +00:00

159 lines
6.0 KiB
JavaScript

Vue.component('tt-autocomplete', {
template: `
<div class="form-group" :class="{'row': row}">
<slot name="prepend"></slot>
<label :class="{'col-form-label': row, 'col-sm-4': row, 'col-form-label-sm': sm && row}"
v-if="label" :for="label">{{ label }}</label>
<div class="autocomplete position-relative" :class="{'col-sm-8 p-0': row}">
<input
type="text"
:id="label"
class="form-control"
:class="{'form-control-sm': 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: () => [] },
sm: { type: Boolean, default: true },
row: { type: Boolean, default: false },
},
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 : '';
} else {
this.displayValue = '';
}
},
data() {
return {
displayingItems: [],
displayValue: '',
isLoading: false,
showSuggestions: false,
cursor: -1,
fetchSuggestionsDebounceTimer: null,
};
},
watch: {
value(newValue) {
const selectedItem = this.displayingItems.find(item => item.value === newValue);
console.log(selectedItem);
this.displayValue = selectedItem ? selectedItem.text : '';
},
apiUrl() {
this.fetchSuggestions();
},
},
methods: {
onInput(event) {
this.displayValue = event.target.value;
this.$emit('input', '');
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;
},
},
});