Implement a basic autocomplete style search

This commit is contained in:
2024-08-13 18:32:01 +02:00
parent 0051ae71d9
commit 12b00e5147
2 changed files with 74 additions and 15 deletions

View File

@@ -3,6 +3,8 @@
import { main } from "$wails/models"; import { main } from "$wails/models";
import { CreateFood, GetLastPer100 } from "$wails/main/App"; import { CreateFood, GetLastPer100 } from "$wails/main/App";
import { foodStore } from "$lib/store/Energy/foodStore"; import { foodStore } from "$lib/store/Energy/foodStore";
import FoodSearchEntry from "./FoodSearchEntry.svelte";
import { onMount } from "svelte";
let item: main.Food = { let item: main.Food = {
food: "", food: "",
@@ -20,6 +22,16 @@
let per100Edited: boolean = false; let per100Edited: boolean = false;
let per100Element: HTMLTableCellElement; let per100Element: HTMLTableCellElement;
let nameElement: HTMLTableCellElement; let nameElement: HTMLTableCellElement;
let autocompleteList: HTMLUListElement;
let foodSearch: main.Food[] = [];
// Maybe it would be a good idea to use $ instead of update down there...
// Maybe it's a topic for another day
$: {
if (!name) {
foodSearch = [];
}
}
async function update(event: KeyboardEvent & { currentTarget: EventTarget & HTMLTableCellElement }) { async function update(event: KeyboardEvent & { currentTarget: EventTarget & HTMLTableCellElement }) {
name = name.trim(); name = name.trim();
@@ -27,6 +39,11 @@
description = description.trim(); description = description.trim();
per100 = per100.trim(); per100 = per100.trim();
if (!name) {
foodSearch = [];
return;
}
if (!per100Edited && event.currentTarget == per100Element) per100Edited = true; if (!per100Edited && event.currentTarget == per100Element) per100Edited = true;
if (event.key == "Enter") { if (event.key == "Enter") {
@@ -50,27 +67,53 @@
foodStore.update((value) => [res.data, ...value]); foodStore.update((value) => [res.data, ...value]);
nameElement.focus(); nameElement.focus();
foodSearch = [];
} }
if (!per100Edited) if (!per100Edited)
GetLastPer100(name.trim()).then((res) => { GetLastPer100(name.trim()).then((res) => {
console.log(res.data);
if (res.success && res.data) { if (res.success && res.data) {
console.log(res.data[0].food); foodSearch = res.data;
per100 = res.data[0].per100.toString();
} }
}); });
} }
let hiLiteIndex: number | null = null;
function navigateList(e: KeyboardEvent) {
if (e.key === "ArrowDown" && hiLiteIndex && hiLiteIndex <= foodSearch.length - 1) {
hiLiteIndex === null ? (hiLiteIndex = 0) : (hiLiteIndex += 1);
} else if (e.key === "ArrowUp" && hiLiteIndex !== null) {
hiLiteIndex === 0 ? (hiLiteIndex = foodSearch.length - 1) : (hiLiteIndex -= 1);
} else if (e.key === "Enter") {
// @ts-ignore ITS NOT NULL YOU ASSHAT
// WE CHECKED
// ITS NOT
setInputVal(foodSearch[hiLiteIndex]);
} else {
return;
}
}
function setInputVal(val: string) {
name = val;
hiLiteIndex = null;
foodSearch = [];
}
$: {
if (nameElement && autocompleteList) {
const { top, left, height } = nameElement.getBoundingClientRect();
autocompleteList.style.top = `${top + height}px`;
autocompleteList.style.left = `${left}px`;
}
}
</script> </script>
<template> <template>
<tr class="border-b border-gray-200 dark:border-gray-700 text-lg font-bold"> <tr class="border-b border-gray-200 dark:border-gray-700 text-lg font-bold relative">
<th <th
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap bg-gray-50 dark:text-white dark:bg-gray-800" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap bg-gray-50 dark:text-white dark:bg-gray-800"
scope="row" scope="row"
> ></th>
</th>
<!-- svelte-ignore a11y-autofocus -->
<td <td
bind:innerText={name} bind:innerText={name}
class:border-[3px]={!name} class:border-[3px]={!name}
@@ -80,15 +123,13 @@
autofocus autofocus
on:keydown={update} on:keydown={update}
bind:this={nameElement} bind:this={nameElement}
> />
</td>
<td <td
bind:innerText={description} bind:innerText={description}
class="px-6 py-4 bg-gray-50 dark:bg-gray-800 overflow-hidden" class="px-6 py-4 bg-gray-50 dark:bg-gray-800 overflow-hidden"
contenteditable="true" contenteditable="true"
on:keydown={update} on:keydown={update}
> />
</td>
<td <td
bind:innerText={amount} bind:innerText={amount}
class:border-[3px]={!amount} class:border-[3px]={!amount}
@@ -96,8 +137,7 @@
class="px-6 py-4 overflow-hidden" class="px-6 py-4 overflow-hidden"
contenteditable="true" contenteditable="true"
on:keydown={update} on:keydown={update}
> />
</td>
<td <td
bind:this={per100Element} bind:this={per100Element}
bind:innerText={per100} bind:innerText={per100}
@@ -106,7 +146,13 @@
class:border-orange-600={!per100} class:border-orange-600={!per100}
contenteditable="true" contenteditable="true"
on:keydown={update} on:keydown={update}
> />
</td>
</tr> </tr>
{#if foodSearch.length > 0}
<ul bind:this={autocompleteList} class="z-50 fixed top-0 left-0 w-3/12 border border-x-gray-800">
{#each foodSearch as f, i}
<FoodSearchEntry itemLabel={f.food} highlighted={i === hiLiteIndex} on:click={() => setInputVal(f)} />
{/each}
</ul>
{/if}
</template> </template>

View File

@@ -0,0 +1,13 @@
<script>
export let itemLabel;
export let highlighted;
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<li
class="list-none border-b border-gray-800 z-50 px-2 py-2 cursor-pointer bg-[#1b2636] hover:bg-[#81921f] hover:text-white active:bg-DodgerBlue active:text-white text-lg"
class:bg-DodgerBlue={highlighted}
class:text-white={highlighted}
on:click>
{itemLabel}
</li>