Implement a basic autocomplete style search
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import { main } from "$wails/models";
|
||||
import { CreateFood, GetLastPer100 } from "$wails/main/App";
|
||||
import { foodStore } from "$lib/store/Energy/foodStore";
|
||||
import FoodSearchEntry from "./FoodSearchEntry.svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let item: main.Food = {
|
||||
food: "",
|
||||
@@ -20,6 +22,16 @@
|
||||
let per100Edited: boolean = false;
|
||||
let per100Element: 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 }) {
|
||||
name = name.trim();
|
||||
@@ -27,6 +39,11 @@
|
||||
description = description.trim();
|
||||
per100 = per100.trim();
|
||||
|
||||
if (!name) {
|
||||
foodSearch = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!per100Edited && event.currentTarget == per100Element) per100Edited = true;
|
||||
|
||||
if (event.key == "Enter") {
|
||||
@@ -50,27 +67,53 @@
|
||||
|
||||
foodStore.update((value) => [res.data, ...value]);
|
||||
nameElement.focus();
|
||||
foodSearch = [];
|
||||
}
|
||||
|
||||
if (!per100Edited)
|
||||
GetLastPer100(name.trim()).then((res) => {
|
||||
console.log(res.data);
|
||||
if (res.success && res.data) {
|
||||
console.log(res.data[0].food);
|
||||
per100 = res.data[0].per100.toString();
|
||||
foodSearch = res.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<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
|
||||
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap bg-gray-50 dark:text-white dark:bg-gray-800"
|
||||
scope="row"
|
||||
>
|
||||
</th>
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
></th>
|
||||
<td
|
||||
bind:innerText={name}
|
||||
class:border-[3px]={!name}
|
||||
@@ -80,15 +123,13 @@
|
||||
autofocus
|
||||
on:keydown={update}
|
||||
bind:this={nameElement}
|
||||
>
|
||||
</td>
|
||||
/>
|
||||
<td
|
||||
bind:innerText={description}
|
||||
class="px-6 py-4 bg-gray-50 dark:bg-gray-800 overflow-hidden"
|
||||
contenteditable="true"
|
||||
on:keydown={update}
|
||||
>
|
||||
</td>
|
||||
/>
|
||||
<td
|
||||
bind:innerText={amount}
|
||||
class:border-[3px]={!amount}
|
||||
@@ -96,8 +137,7 @@
|
||||
class="px-6 py-4 overflow-hidden"
|
||||
contenteditable="true"
|
||||
on:keydown={update}
|
||||
>
|
||||
</td>
|
||||
/>
|
||||
<td
|
||||
bind:this={per100Element}
|
||||
bind:innerText={per100}
|
||||
@@ -106,7 +146,13 @@
|
||||
class:border-orange-600={!per100}
|
||||
contenteditable="true"
|
||||
on:keydown={update}
|
||||
>
|
||||
</td>
|
||||
/>
|
||||
</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>
|
||||
|
13
frontend/src/lib/components/Energy/FoodSearchEntry.svelte
Normal file
13
frontend/src/lib/components/Energy/FoodSearchEntry.svelte
Normal 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>
|
Reference in New Issue
Block a user