Add NearestStations Card & Location monitor
This commit is contained in:
@@ -27,7 +27,6 @@
|
||||
return LOCATIONS.data
|
||||
.filter((r) => tokens.every((t) => r.s.includes(t)))
|
||||
.sort((a, b) => {
|
||||
|
||||
// Priority One - Exact CRS Match
|
||||
const aExactCrs = a.c?.toLowerCase() === lowerQuery;
|
||||
const bExactCrs = b.c?.toLowerCase() === lowerQuery;
|
||||
@@ -36,7 +35,7 @@
|
||||
|
||||
// Priority Two - 'Stations' with CRS
|
||||
if (!!a.c && !b.c) return -1;
|
||||
if (!a.c & !! b.c) return 1;
|
||||
if (!a.c & !!b.c) return 1;
|
||||
|
||||
// Alphabetical Sort
|
||||
return a.n.localeCompare(b.n);
|
||||
|
||||
@@ -1,53 +1,56 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
toc: string;
|
||||
}
|
||||
interface Props {
|
||||
toc: string;
|
||||
}
|
||||
|
||||
let {
|
||||
toc
|
||||
}: Props = $props();
|
||||
let { toc }: Props = $props();
|
||||
|
||||
let code = $derived(toc.toUpperCase());
|
||||
let code = $derived(toc.toUpperCase());
|
||||
</script>
|
||||
|
||||
<div class="toc-container {code}">
|
||||
{code}
|
||||
{code}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.toc-container {
|
||||
border-radius: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2px 8px;
|
||||
font-weight: 800;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
.toc-container {
|
||||
border-radius: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2px 8px;
|
||||
font-weight: 800;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.GW { /* Great Western Railway */
|
||||
background: #004225;
|
||||
color: #E2E2E2;
|
||||
}
|
||||
.GW {
|
||||
/* Great Western Railway */
|
||||
background: #004225;
|
||||
color: #e2e2e2;
|
||||
}
|
||||
|
||||
.GR { /* LNER */
|
||||
background-color: #C00000;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.GR {
|
||||
/* LNER */
|
||||
background-color: #c00000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.VT { /* Avanti West Coast */
|
||||
background-color: #004354;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.VT {
|
||||
/* Avanti West Coast */
|
||||
background-color: #004354;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.SW { /* South Western Railway */
|
||||
background-color: #2A3389;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.SW {
|
||||
/* South Western Railway */
|
||||
background-color: #2a3389;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.XC { /* CrossCountry */
|
||||
background-color: #660000;
|
||||
color: #E4D5B1;
|
||||
}
|
||||
</style>
|
||||
.XC {
|
||||
/* CrossCountry */
|
||||
background-color: #660000;
|
||||
color: #e4d5b1;
|
||||
}
|
||||
</style>
|
||||
|
||||
69
src/lib/components/ui/cards/NearbyStationsCard.svelte
Normal file
69
src/lib/components/ui/cards/NearbyStationsCard.svelte
Normal file
@@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import BaseCard from '$lib/components/ui/cards/BaseCard.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
|
||||
import { fade } from 'svelte/transition';
|
||||
import { flip } from 'svelte/animate';
|
||||
|
||||
import { nearestStationsState } from '$lib/geohash.svelte';
|
||||
|
||||
const flipDuration = 300;
|
||||
</script>
|
||||
|
||||
<BaseCard header={'Nearby Stations'}>
|
||||
<div class="card-content">
|
||||
{#if nearestStationsState.error && nearestStationsState.list.length === 0}
|
||||
<p class="msg">{nearestStationsState.error}</p>
|
||||
{:else if nearestStationsState.loading && nearestStationsState.list.length === 0}
|
||||
<p class="msg">Locating stations...</p>
|
||||
{:else}
|
||||
<div class="stations-flex">
|
||||
{#each nearestStationsState.list as station (station.c)}
|
||||
<div
|
||||
class="btn-container"
|
||||
animate:flip={{ duration: flipDuration }}
|
||||
in:fade={{ duration: 200, delay: 100 }}
|
||||
out:fade={{ duration: 150 }}
|
||||
>
|
||||
<Button href={`/board?loc=${station.c}`}
|
||||
><span class="stn-name">{station.n}</span></Button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</BaseCard>
|
||||
|
||||
<style>
|
||||
.card-content {
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
padding: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.stations-flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.msg {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-title);
|
||||
}
|
||||
|
||||
.stn-name {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
</style>
|
||||
@@ -1,28 +1,34 @@
|
||||
<script lang="ts">
|
||||
import BaseCard from '$lib/components/ui/cards/BaseCard.svelte';
|
||||
import Textbox from '$lib/components/ui/Textbox.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import BaseCard from '$lib/components/ui/cards/BaseCard.svelte';
|
||||
import Textbox from '$lib/components/ui/Textbox.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
|
||||
let { onsearch }: { onsearch: (c: string) => void } = $props();
|
||||
let { onsearch }: { onsearch: (c: string) => void } = $props();
|
||||
|
||||
let codeValue = $state('');
|
||||
let codeValue = $state('');
|
||||
|
||||
function resetValues(): void {
|
||||
codeValue = '';
|
||||
}
|
||||
function resetValues(): void {
|
||||
codeValue = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<BaseCard header={'Find by Code'}>
|
||||
<div class="card-content">
|
||||
<div class="textbox-container">
|
||||
<div class="textbox-item-wrapper">
|
||||
<Textbox placeholder={"Code"} uppercase={true} type={'number'} max={9999} bind:value={codeValue} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-wrapper">
|
||||
<Button onclick={() => onsearch(codeValue.toString())}>Search</Button>
|
||||
<Button onclick={resetValues}>Reset</Button>
|
||||
</div>
|
||||
<div class="textbox-container">
|
||||
<div class="textbox-item-wrapper">
|
||||
<Textbox
|
||||
placeholder={'Code'}
|
||||
uppercase={true}
|
||||
type={'number'}
|
||||
max={9999}
|
||||
bind:value={codeValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-wrapper">
|
||||
<Button onclick={() => onsearch(codeValue.toString())}>Search</Button>
|
||||
<Button onclick={resetValues}>Reset</Button>
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
|
||||
@@ -34,21 +40,21 @@ function resetValues(): void {
|
||||
padding: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.textbox-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
.textbox-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.textbox-item-wrapper {
|
||||
width: 30%;
|
||||
}
|
||||
.textbox-item-wrapper {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
<script lang="ts">
|
||||
import BaseCard from '$lib/components/ui/cards/BaseCard.svelte';
|
||||
import Textbox from '$lib/components/ui/Textbox.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import BaseCard from '$lib/components/ui/cards/BaseCard.svelte';
|
||||
import Textbox from '$lib/components/ui/Textbox.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
|
||||
let { onsearch }: { onsearch: (s: string, e: string) => void } = $props();
|
||||
let { onsearch }: { onsearch: (s: string, e: string) => void } = $props();
|
||||
|
||||
let startValue = $state('');
|
||||
let endValue = $state('');
|
||||
|
||||
let startValue = $state('');
|
||||
let endValue = $state('');
|
||||
|
||||
function resetValues(): void {
|
||||
startValue = '';
|
||||
endValue = '';
|
||||
}
|
||||
function resetValues(): void {
|
||||
startValue = '';
|
||||
endValue = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<BaseCard header={'Find by Start/End CRS'}>
|
||||
<div class="card-content">
|
||||
<div class="textbox-container">
|
||||
<div class="textbox-item-wrapper">
|
||||
<Textbox placeholder={"Start"} uppercase={true} maxLength={3} bind:value={startValue} />
|
||||
</div>
|
||||
<div class="textbox-item-wrapper">
|
||||
<Textbox placeholder={"End"} uppercase={true} maxLength={3} bind:value={endValue} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-wrapper">
|
||||
<Button onclick={() => onsearch(startValue, endValue)}>Search</Button>
|
||||
<Button onclick={resetValues}>Reset</Button>
|
||||
</div>
|
||||
<div class="textbox-container">
|
||||
<div class="textbox-item-wrapper">
|
||||
<Textbox placeholder={'Start'} uppercase={true} maxLength={3} bind:value={startValue} />
|
||||
</div>
|
||||
<div class="textbox-item-wrapper">
|
||||
<Textbox placeholder={'End'} uppercase={true} maxLength={3} bind:value={endValue} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-wrapper">
|
||||
<Button onclick={() => onsearch(startValue, endValue)}>Search</Button>
|
||||
<Button onclick={resetValues}>Reset</Button>
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
|
||||
@@ -40,21 +39,21 @@ function resetValues(): void {
|
||||
padding: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.textbox-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
.textbox-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.textbox-item-wrapper {
|
||||
width: 30%;
|
||||
}
|
||||
.textbox-item-wrapper {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user