Add initial location search box

This commit is contained in:
2026-03-16 14:08:00 +00:00
parent 66ed33343c
commit 1b0b93b34b
2 changed files with 105 additions and 1 deletions

View File

@@ -0,0 +1,104 @@
<script lang="ts">
import Textbox from '$lib/components/Textbox.svelte';
import { onMount } from 'svelte';
interface LocationRecord {
n: string; // name
t: string; // tiploc
c?: string; // crs
s: string; // search string
}
let value = $state("");
let results = $state<LocationRecord[]>([]);
let locations: LocationRecord[] = [];
let showResults = $state(false);
let selectedIndex = $state(-1);
const MAX_RESULTS = 10;
async function loadLocations() {
const res = await fetch("/api/tiplocs");
locations = await res.json();
}
onMount(loadLocations);
function tokenize(query: string) {
return query
.toLowerCase()
.trim()
.split(/\s+/)
.filter(Boolean);
}
function search(query: string) {
if (query.length < 2) {
results = [];
return;
}
const tokens = tokenize(query);
results = locations
.filter(r => tokens.every(t => r.s.includes(t)))
.slice(0, MAX_RESULTS);
}
$effect(() => {
search(value);
});
function choose(loc: LocationRecord) {
value = loc.n;
showResults = false;
selectedIndex = -1;
// emit event later if needed
}
function handleKey(e: KeyboardEvent) {
if (!results.length) return;
if (e.key === "ArrowDown") {
e.preventDefault();
selectedIndex = Math.min(selectedIndex + 1, results.length - 1);
}
if (e.key === "ArrowUp") {
e.preventDefault();
selectedIndex = Math.max(selectedIndex - 1, 0);
}
if (e.key === "Enter" && selectedIndex >= 0) {
choose(results[selectedIndex]);
}
}
</script>
<div class="location-search">
<Textbox
bind:value
placeholder="Search for a station or TIPLOC"
oninput={() => showResults = true}
onkeydown={handleKey}
/>
{#if showResults && results.length}
<ul class="suggestions">
{#each results as loc, i}
<li
class:selected={i === selectedIndex}
onclick={() => choose(loc)}
>
<span class="name">{loc.n}</span>
{#if loc.c}
<span class="crs">{loc.c}</span>
{/if}
</li>
{/each}
</ul>
{/if}
</div>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import type { HTLMInputAttributes } from 'svelte/elements';
import type { HTMLInputAttributes } from 'svelte/elements';
interface Props extends HTMLInputAttributes {
value?: string;