site: implement search functionality

This commit is contained in:
Lucas Gabriel Vuotto 2025-04-27 15:20:49 +00:00
parent 3ae6b02672
commit 08be6b17f5
8 changed files with 257 additions and 2 deletions

File diff suppressed because one or more lines are too long

97
public/js/app.js Normal file
View file

@ -0,0 +1,97 @@
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");
}
}));
});