From 3240560a0bde04bca996b1241553224dd60f07ee Mon Sep 17 00:00:00 2001 From: Fred Boniface Date: Tue, 17 Mar 2026 19:28:02 +0000 Subject: [PATCH] Refactor location object into separate module so that it can be used in multiple locations --- .../components/ui/LocationSearchBox.svelte | 38 ++++++------------- src/lib/locations-object.svelte.ts | 26 +++++++++++++ src/routes/+layout.svelte | 5 +++ 3 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 src/lib/locations-object.svelte.ts diff --git a/src/lib/components/ui/LocationSearchBox.svelte b/src/lib/components/ui/LocationSearchBox.svelte index 5156771..d4853e8 100644 --- a/src/lib/components/ui/LocationSearchBox.svelte +++ b/src/lib/components/ui/LocationSearchBox.svelte @@ -4,44 +4,29 @@ import { fade } from 'svelte/transition'; import { goto } from '$app/navigation'; - interface LocationRecord { - n: string; // name - t: string; // tiploc - c?: string; // crs - s: string; // search string - } + import { LOCATIONS } from '$lib/locations-object.svelte.ts'; + + let { value = $bindable() } = $props(); - let results = $state([]); - let locations: LocationRecord[] = []; - let showResults = $state(false); let selectedIndex = $state(-1); const MAX_RESULTS = 5; - 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 < 3) { - results = []; - return; - } + let results = $derived.by(() => { + if (value.length < 3) return []; - const tokens = tokenize(query); - const lowerQuery = query.toLowerCase().trim(); + const tokens = tokenize(value); + const lowerQuery = value.toLowerCase().trim(); - results = locations + return LOCATIONS.data .filter((r) => tokens.every((t) => r.s.includes(t))) .sort((a, b) => { // Check if query matches CRS @@ -56,11 +41,12 @@ return a.n.localeCompare(b.n); }) .slice(0, MAX_RESULTS); - } + }) + $effect(() => { - search(value); - }); + if (results) selectedIndex = -1; + }); // Hide results when click outside of container $effect(() => { diff --git a/src/lib/locations-object.svelte.ts b/src/lib/locations-object.svelte.ts new file mode 100644 index 0000000..61d8046 --- /dev/null +++ b/src/lib/locations-object.svelte.ts @@ -0,0 +1,26 @@ + interface LocationRecord { + n: string; // name + t: string; // tiploc + c?: string; // crs + s: string; // search string + } + +class LocationStore { + data = $state([]); + loaded = $state(false); + + async init() { + if (this.loaded) return; + + try { + const res = await fetch('/api/tiplocs'); + this.data = await res.json(); + this.loaded = true; + } catch (err) { + console.error('Failed to load locations', err); + } + } + +} + +export const LOCATIONS = new LocationStore(); \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9d531c5..6a81584 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,6 +1,9 @@