aboutsummaryrefslogtreecommitdiff
path: root/src/routes
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-21 16:13:12 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-21 16:13:12 +0200
commit07a83b2e3db40f4a380092d6566500799b340dcd (patch)
tree25bbe37ec3c459b48bcc14bf10eb8ad75836ef20 /src/routes
parent8f95ffb13be709036916d604849f8fde7e777a8f (diff)
feat: fuzzy autocomplete for add-item input
Replace the native <datalist> with a custom fuzzysort-driven component (FuzzyAutocomplete) with keyboard nav and highlighted matches, preserving exact-match auto-add. Extract a unit-tested fuzzy-search helper, and gitignore .juggler/. Fix the dev DB boot failure caused by a sibling project's ambient DATABASE_URL leaking in by overriding it (file:./data.db) in mise.toml.
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/+page.svelte51
1 files changed, 6 insertions, 45 deletions
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 9900bb3..66380a4 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -2,10 +2,10 @@
import { deserialize } from '$app/forms';
import { goto } from '$app/navigation';
import { enhance } from '$app/forms';
+ import FuzzyAutocomplete from '$lib/components/FuzzyAutocomplete.svelte';
let { data, form } = $props();
let name = $state('');
- let inputRef: HTMLInputElement;
// eslint-disable-next-line svelte/prefer-writable-derived
let localItems = $state([]);
let addError = $state('');
@@ -41,7 +41,6 @@
const action = redirectToEdit ? '?/addAndEdit' : '?/add';
name = '';
- inputRef.focus();
try {
const response = await fetch(action, {
@@ -79,48 +78,15 @@
</script>
<div class="add-bar">
- <input
- bind:this={inputRef}
- class="form-input"
- type="text"
+ <FuzzyAutocomplete
+ items={data.catalog}
bind:value={name}
placeholder="Ajouter un article..."
- autocomplete="off"
- list="item-suggestions"
- onkeydown={(e) => {
- if (e.key === 'Enter') {
- e.preventDefault();
- handleAdd(false);
- }
- }}
- oninput={(e) => {
- if ((e as InputEvent).inputType === 'insertReplacementText') {
- const val = e.currentTarget.value.trim().toLowerCase();
- const match = data.catalog.find(
- (item) => item.name.toLowerCase() === val,
- );
- if (match) {
- name = match.name;
- handleAdd(false);
- }
- }
- }}
- onchange={(e) => {
- const val = e.currentTarget.value.trim().toLowerCase();
- const match = data.catalog.find(
- (item) => item.name.toLowerCase() === val,
- );
- if (match) {
- name = match.name;
- handleAdd(false);
- }
+ onselect={(item) => {
+ name = item.name;
+ handleAdd(false);
}}
/>
- <datalist id="item-suggestions">
- {#each data.catalog as item (item.id)}
- <option value={item.name}></option>
- {/each}
- </datalist>
<button type="button" aria-label="Ajouter" onclick={() => handleAdd(false)}
>+</button
>
@@ -187,11 +153,6 @@
z-index: 10;
}
- .add-bar input {
- flex: 1;
- background: var(--surface);
- }
-
.add-bar button {
width: 3rem;
height: 3rem;