function getCurrentWord(s, cursor) { return s.substring(s.lastIndexOf(' ', cursor - 1) + 1, cursor); } function mySplice(s, idx, del, n) { return s.substring(0, idx) + n + s.substring(idx + del); } function suggestionPosition(el) { // el.parentNode is ul#tags-suggestion, which include a template. That's why // a -1 is needed here. const idx = [...el.parentNode.children].indexOf(el); return idx <= 0 ? null : idx - 1; } function currentSuggestionElement(idx) { if (idx === null) return null; return document.querySelector("#tags-suggestions").children[idx + 1]; } document.addEventListener('alpine:init', () => { Alpine.data('tagsSuggestions', () => ({ suggestions: [], currentSuggestion: null, inputCursor: 0, query: '', reset() { this.suggestions = []; this.currentSuggestion = null; this.inputCursor = 0; this.query = ''; }, async fetchSuggestions(evt) { this.inputCursor = evt.target.selectionStart; this.query = getCurrentWord(evt.target.value, this.inputCursor); if (!this.query) { this.reset(); return; } const params = new URLSearchParams({q: this.query}); const response = await fetch(`${endpoints.search}?${params}`); if (!response.ok) { this.reset(); return; } this.suggestions = await response.json(); }, completeSuggestion(evt) { if (this.currentSuggestion === null) return; const value = this.$refs.tags.value; const pos = this.inputCursor - this.query.length; const tag = this.suggestions[this.currentSuggestion].display; this.$refs.tags.value = mySplice(value, pos, this.query.length, tag + ' '); this.reset(); this.$refs.tags.focus(); // Don't switch focus if we're completing after a Tab press. if (evt.type === "keydown" && evt.keyCode === 9) evt.preventDefault(); }, selectCurrentSuggestion(evt) { this.currentSuggestion = suggestionPosition(evt.currentTarget); this.completeSuggestion(evt); }, setCurrentSuggestion(evt, amount = 0) { currentSuggestionElement(this.currentSuggestion) ?.classList.remove("suggestion-hover"); if (evt === 'mouseenter') this.currentSuggestion = suggestionPosition(evt.currentTarget); else { const len = this.suggestions.length; let cur = this.currentSuggestion; if (cur === null) cur = amount > 0 ? -1 : amount < 0 ? len : 0; this.currentSuggestion = (cur + amount + len) % len; } currentSuggestionElement(this.currentSuggestion) ?.classList.add("suggestion-hover"); } })); });