Refactor location object into separate module so that it can be used in multiple locations
This commit is contained in:
@@ -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<LocationRecord[]>([]);
|
||||
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,10 +41,11 @@
|
||||
return a.n.localeCompare(b.n);
|
||||
})
|
||||
.slice(0, MAX_RESULTS);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
$effect(() => {
|
||||
search(value);
|
||||
if (results) selectedIndex = -1;
|
||||
});
|
||||
|
||||
// Hide results when click outside of container
|
||||
|
||||
26
src/lib/locations-object.svelte.ts
Normal file
26
src/lib/locations-object.svelte.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
interface LocationRecord {
|
||||
n: string; // name
|
||||
t: string; // tiploc
|
||||
c?: string; // crs
|
||||
s: string; // search string
|
||||
}
|
||||
|
||||
class LocationStore {
|
||||
data = $state<LocationRecord[]>([]);
|
||||
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();
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { slide, fade } from 'svelte/transition';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { LOCATIONS } from '$lib/locations-object.svelte.ts';
|
||||
|
||||
import '$lib/global.css';
|
||||
|
||||
@@ -10,6 +13,8 @@
|
||||
|
||||
import { IconHome, IconDialpad, IconSettings, IconHelp, IconDots } from '@tabler/icons-svelte';
|
||||
|
||||
onMount(() => LOCATIONS.init());
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
// Navigation State
|
||||
|
||||
Reference in New Issue
Block a user