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 { fade } from 'svelte/transition';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
interface LocationRecord {
|
import { LOCATIONS } from '$lib/locations-object.svelte.ts';
|
||||||
n: string; // name
|
|
||||||
t: string; // tiploc
|
|
||||||
c?: string; // crs
|
|
||||||
s: string; // search string
|
|
||||||
}
|
|
||||||
|
|
||||||
let { value = $bindable() } = $props();
|
let { value = $bindable() } = $props();
|
||||||
|
|
||||||
let results = $state<LocationRecord[]>([]);
|
|
||||||
let locations: LocationRecord[] = [];
|
|
||||||
|
|
||||||
let showResults = $state(false);
|
let showResults = $state(false);
|
||||||
let selectedIndex = $state(-1);
|
let selectedIndex = $state(-1);
|
||||||
|
|
||||||
const MAX_RESULTS = 5;
|
const MAX_RESULTS = 5;
|
||||||
|
|
||||||
async function loadLocations() {
|
|
||||||
const res = await fetch('/api/tiplocs');
|
|
||||||
locations = await res.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(loadLocations);
|
|
||||||
|
|
||||||
function tokenize(query: string) {
|
function tokenize(query: string) {
|
||||||
return query.toLowerCase().trim().split(/\s+/).filter(Boolean);
|
return query.toLowerCase().trim().split(/\s+/).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
function search(query: string) {
|
let results = $derived.by(() => {
|
||||||
if (query.length < 3) {
|
if (value.length < 3) return [];
|
||||||
results = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = tokenize(query);
|
const tokens = tokenize(value);
|
||||||
const lowerQuery = query.toLowerCase().trim();
|
const lowerQuery = value.toLowerCase().trim();
|
||||||
|
|
||||||
results = locations
|
return LOCATIONS.data
|
||||||
.filter((r) => tokens.every((t) => r.s.includes(t)))
|
.filter((r) => tokens.every((t) => r.s.includes(t)))
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
// Check if query matches CRS
|
// Check if query matches CRS
|
||||||
@@ -56,11 +41,12 @@
|
|||||||
return a.n.localeCompare(b.n);
|
return a.n.localeCompare(b.n);
|
||||||
})
|
})
|
||||||
.slice(0, MAX_RESULTS);
|
.slice(0, MAX_RESULTS);
|
||||||
}
|
})
|
||||||
|
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
search(value);
|
if (results) selectedIndex = -1;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hide results when click outside of container
|
// Hide results when click outside of container
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
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">
|
<script lang="ts">
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { slide, fade } from 'svelte/transition';
|
import { slide, fade } from 'svelte/transition';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
import { LOCATIONS } from '$lib/locations-object.svelte.ts';
|
||||||
|
|
||||||
import '$lib/global.css';
|
import '$lib/global.css';
|
||||||
|
|
||||||
@@ -10,6 +13,8 @@
|
|||||||
|
|
||||||
import { IconHome, IconDialpad, IconSettings, IconHelp, IconDots } from '@tabler/icons-svelte';
|
import { IconHome, IconDialpad, IconSettings, IconHelp, IconDots } from '@tabler/icons-svelte';
|
||||||
|
|
||||||
|
onMount(() => LOCATIONS.init());
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
|
|
||||||
// Navigation State
|
// Navigation State
|
||||||
|
|||||||
Reference in New Issue
Block a user