document.addEventListener('alpine:init', () => { Alpine.data('tagsSuggestions', (initialSearch = '') => ({ items: [], activeItem: null, search: initialSearch, cursor: 0, currentWord: '', open: false, get sortedItems() { return this.items.toSorted((a, b) => b.count - a.count); }, getCurrentWordFromCursor() { let s = this.$refs.search.value; return s.substring(s.lastIndexOf(' ', this.cursor - 1) + 1, this.cursor); }, async fetchSuggestions() { const currentWord = this.getCurrentWordFromCursor(); if (!currentWord) return; if (currentWord === this.currentWord) { /* Show previous items, if any. */ this.open = this.items.length > 0; return; } this.currentWord = currentWord; const params = new URLSearchParams({q: currentWord}); const response = await fetch(`${endpoints.search}?${params}`); if (!response.ok) { this.items = []; this.open = false; return; } this.activeItem = null; this.items = await response.json(); this.open = this.items.length > 0; }, async fetchSuggestionsOnInput(evt) { this.cursor = evt.target.selectionStart; await this.fetchSuggestions(); }, insertSuggestionAtCursor() { const position = Math.max(this.cursor - this.currentWord.length, 0); const suggestion = this.items[this.activeItem ?? 0].display; const oldValue = this.$refs.search.value; this.$refs.search.value = oldValue.substring(0, position) + suggestion + ' ' + oldValue.substring(position + this.currentWord.length); this.cursor = position + suggestion.length + 1; }, autocompleteSuggestion() { if (this.items.length === 0) return; this.insertSuggestionAtCursor(); this.items = []; this.activeItem = null; this.currentWord = ''; this.open = false; this.$refs.search.selectionStart = this.$refs.search.selectionEnd = this.cursor; this.$refs.search.focus(); }, autocompleteItem(evt) { if (this.currentWord !== '') { this.autocompleteSuggestion(); evt.preventDefault(); } }, getListItemIndexOf(el) { return [...this.$refs.suggestions.querySelectorAll('li')].indexOf(el); }, autocompletePointedItem(evt) { this.activeItem = this.getListItemIndexOf(evt.currentTarget); this.autocompleteSuggestion(); }, setHoveredItem(evt) { this.activeItem = this.getListItemIndexOf(evt.currentTarget); }, moveActiveItem(evt, amount, defaultValue) { const len = this.items.length; if (len === 0) return; let item = this.activeItem === null ? defaultValue : (this.activeItem + amount) % len; if (item < 0) item += len; this.activeItem = item; }, async moveActiveItemBackward(evt) { await this.fetchSuggestionsOnInput(evt); this.moveActiveItem(evt, -1, -1); }, async moveActiveItemForward(evt) { await this.fetchSuggestionsOnInput(evt); this.moveActiveItem(evt, 1, 0); }, })); });