feat: add loading state to view
This commit is contained in:
75
src/lib/components/Progressbar.svelte
Normal file
75
src/lib/components/Progressbar.svelte
Normal file
@ -0,0 +1,75 @@
|
||||
<div class="progress-bar indeterminate bg-card/50 backdrop-blur-md">
|
||||
<div class="short-bar"></div>
|
||||
<div class="long-bar"></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@keyframes progressbarindeterminateshort {
|
||||
0% {
|
||||
left: -40%;
|
||||
}
|
||||
|
||||
75% {
|
||||
left: 120%;
|
||||
}
|
||||
|
||||
100% {
|
||||
left: 120%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes progressbarindeterminatelong {
|
||||
0% {
|
||||
left: -90%;
|
||||
}
|
||||
|
||||
37.5% {
|
||||
left: -90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 5px;
|
||||
position: absolute;
|
||||
border-radius: 1.5px;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.progress-bar > .short-bar {
|
||||
background-color: var(--accent);
|
||||
width: 40%;
|
||||
height: 100%;
|
||||
left: -40%;
|
||||
border-radius: 2.5px;
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
-webkit-animation: progressbarindeterminateshort 2s ease-in-out infinite;
|
||||
-moz-animation: progressbarindeterminateshort 2s ease-in-out infinite;
|
||||
-o-animation: progressbarindeterminateshort 2s ease-in-out infinite;
|
||||
animation: progressbarindeterminateshort 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.progress-bar > .long-bar {
|
||||
background-color: var(--accent);
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
left: -90%;
|
||||
border-radius: 2.5px;
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
-webkit-animation: progressbarindeterminatelong 2s ease-in-out infinite;
|
||||
-moz-animation: progressbarindeterminatelong 2s ease-in-out infinite;
|
||||
-o-animation: progressbarindeterminatelong 2s ease-in-out infinite;
|
||||
animation: progressbarindeterminatelong 2s ease-in-out infinite;
|
||||
}
|
||||
</style>
|
||||
@ -24,6 +24,7 @@
|
||||
import LoadingSidebar from "./sidebar/LoadingSidebar.svelte";
|
||||
import { Tween } from "svelte/motion";
|
||||
import { quintOut } from "svelte/easing";
|
||||
import Progressbar from "../Progressbar.svelte";
|
||||
|
||||
const views: Record<string, string> = {
|
||||
main: "MainSidebar",
|
||||
@ -134,10 +135,6 @@
|
||||
return () => value;
|
||||
}
|
||||
|
||||
// TODO: implement loading state
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let loading = $state(false);
|
||||
|
||||
let searchText = $derived.by(debounce(() => searchbar.text, 300));
|
||||
let searchResults: Feature[] = $state([]);
|
||||
let mobileView = $derived(window.innerWidth < 768 || routing.currentTrip);
|
||||
@ -149,10 +146,10 @@
|
||||
return;
|
||||
}
|
||||
if (searchText.length > 0) {
|
||||
loading = true;
|
||||
view.loading = true;
|
||||
search(searchText, 0, 0).then((results) => {
|
||||
searchResults = results;
|
||||
loading = false;
|
||||
view.loading = false;
|
||||
view.switch("search", {
|
||||
results: searchResults,
|
||||
query: searchText,
|
||||
@ -211,6 +208,10 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if view.loading}
|
||||
<Progressbar />
|
||||
{/if}
|
||||
|
||||
{#if routing.currentTrip}
|
||||
<InRouteSidebar />
|
||||
{:else}
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
import InternetAccess from "../info/InternetAccess.svelte";
|
||||
import RestaurantInfo from "../info/RestaurantInfo.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
// let { feature }: { feature: Feature } = $props();
|
||||
|
||||
@ -81,9 +82,16 @@
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
view.loading = true;
|
||||
})
|
||||
</script>
|
||||
|
||||
{#await fetchPOI(lat, lng, 20)}
|
||||
{#await fetchPOI(lat, lng, 20).then(r => {
|
||||
view.loading = false;
|
||||
return r;
|
||||
})}
|
||||
<SidebarHeader
|
||||
onback={() => {
|
||||
pin.liftPin();
|
||||
|
||||
@ -4,19 +4,20 @@
|
||||
import { fetchNearbyPOI, type OverpassElement } from "$lib/services/Overpass";
|
||||
import { location } from "../location.svelte";
|
||||
import { map, pin } from "../map.svelte";
|
||||
import { view } from "../view.svelte";
|
||||
import SidebarHeader from "./SidebarHeader.svelte";
|
||||
|
||||
let { tags }: { tags?: string } = $props();
|
||||
|
||||
let pois: OverpassElement[] = $state([]);
|
||||
let loading = $state(true);
|
||||
|
||||
$effect(() => {
|
||||
if (!tags) {
|
||||
loading = false;
|
||||
view.loading = false;
|
||||
pois = [];
|
||||
return;
|
||||
}
|
||||
view.loading = true;
|
||||
fetchNearbyPOI(location.lat, location.lng, tags.split(","), 500).then(
|
||||
(results) => {
|
||||
pois = results.elements.sort((a, b) => {
|
||||
@ -30,7 +31,7 @@
|
||||
);
|
||||
return distA - distB;
|
||||
});
|
||||
loading = false;
|
||||
view.loading = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
@ -60,7 +61,7 @@
|
||||
{/if}
|
||||
</SidebarHeader>
|
||||
|
||||
{#if loading}
|
||||
{#if view.loading}
|
||||
<div class="text-sm text-muted-foreground">{m.loading()}</div>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-2 p-2">
|
||||
|
||||
@ -69,6 +69,7 @@
|
||||
</div>
|
||||
<Button
|
||||
onclick={async () => {
|
||||
view.loading = true;
|
||||
console.log(fromLocation, toLocation);
|
||||
const FROM: WorldLocation =
|
||||
fromLocation == "current"
|
||||
@ -103,6 +104,7 @@
|
||||
}
|
||||
drawAllRoutes(routes);
|
||||
zoomToPoints(FROM, TO, map.value!);
|
||||
view.loading = false;
|
||||
}}>{m["sidebar.route.calculate"]()}</Button
|
||||
>
|
||||
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
import { onMount } from "svelte";
|
||||
import SidebarHeader from "../SidebarHeader.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import { view } from "../../view.svelte";
|
||||
|
||||
let licenses: License[] = $state([]);
|
||||
let loading = $state(true);
|
||||
|
||||
interface License {
|
||||
name: string;
|
||||
@ -20,15 +20,16 @@
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
view.loading = true;
|
||||
const res = await fetch("/licenses.json");
|
||||
licenses = await res.json();
|
||||
loading = false;
|
||||
view.loading = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<SidebarHeader>{m["sidebar.about.licenses"]()}</SidebarHeader>
|
||||
|
||||
{#if loading}
|
||||
{#if view.loading}
|
||||
<p>Loading...</p>
|
||||
{:else if licenses.length == 0}
|
||||
<p>No licenses found.</p>
|
||||
|
||||
@ -6,9 +6,11 @@ export interface View {
|
||||
}
|
||||
|
||||
export const view = $state({
|
||||
loading: false,
|
||||
current: { type: getOnboardingView("main") } as View,
|
||||
history: [] as View[],
|
||||
back: () => {
|
||||
view.loading = false;
|
||||
if (view.history.length > 0) {
|
||||
view.current = view.history.pop()!;
|
||||
} else {
|
||||
@ -19,6 +21,7 @@ export const view = $state({
|
||||
if (view.current.type !== to) {
|
||||
view.history.push(view.current);
|
||||
}
|
||||
view.loading = false;
|
||||
view.current = { type: to, props } as View;
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user