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 LoadingSidebar from "./sidebar/LoadingSidebar.svelte";
|
||||||
import { Tween } from "svelte/motion";
|
import { Tween } from "svelte/motion";
|
||||||
import { quintOut } from "svelte/easing";
|
import { quintOut } from "svelte/easing";
|
||||||
|
import Progressbar from "../Progressbar.svelte";
|
||||||
|
|
||||||
const views: Record<string, string> = {
|
const views: Record<string, string> = {
|
||||||
main: "MainSidebar",
|
main: "MainSidebar",
|
||||||
@ -134,10 +135,6 @@
|
|||||||
return () => value;
|
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 searchText = $derived.by(debounce(() => searchbar.text, 300));
|
||||||
let searchResults: Feature[] = $state([]);
|
let searchResults: Feature[] = $state([]);
|
||||||
let mobileView = $derived(window.innerWidth < 768 || routing.currentTrip);
|
let mobileView = $derived(window.innerWidth < 768 || routing.currentTrip);
|
||||||
@ -149,10 +146,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (searchText.length > 0) {
|
if (searchText.length > 0) {
|
||||||
loading = true;
|
view.loading = true;
|
||||||
search(searchText, 0, 0).then((results) => {
|
search(searchText, 0, 0).then((results) => {
|
||||||
searchResults = results;
|
searchResults = results;
|
||||||
loading = false;
|
view.loading = false;
|
||||||
view.switch("search", {
|
view.switch("search", {
|
||||||
results: searchResults,
|
results: searchResults,
|
||||||
query: searchText,
|
query: searchText,
|
||||||
@ -211,6 +208,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if view.loading}
|
||||||
|
<Progressbar />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if routing.currentTrip}
|
{#if routing.currentTrip}
|
||||||
<InRouteSidebar />
|
<InRouteSidebar />
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
import InternetAccess from "../info/InternetAccess.svelte";
|
import InternetAccess from "../info/InternetAccess.svelte";
|
||||||
import RestaurantInfo from "../info/RestaurantInfo.svelte";
|
import RestaurantInfo from "../info/RestaurantInfo.svelte";
|
||||||
import { m } from "$lang/messages";
|
import { m } from "$lang/messages";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
// let { feature }: { feature: Feature } = $props();
|
// let { feature }: { feature: Feature } = $props();
|
||||||
|
|
||||||
@ -81,9 +82,16 @@
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
view.loading = true;
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#await fetchPOI(lat, lng, 20)}
|
{#await fetchPOI(lat, lng, 20).then(r => {
|
||||||
|
view.loading = false;
|
||||||
|
return r;
|
||||||
|
})}
|
||||||
<SidebarHeader
|
<SidebarHeader
|
||||||
onback={() => {
|
onback={() => {
|
||||||
pin.liftPin();
|
pin.liftPin();
|
||||||
|
|||||||
@ -4,19 +4,20 @@
|
|||||||
import { fetchNearbyPOI, type OverpassElement } from "$lib/services/Overpass";
|
import { fetchNearbyPOI, type OverpassElement } from "$lib/services/Overpass";
|
||||||
import { location } from "../location.svelte";
|
import { location } from "../location.svelte";
|
||||||
import { map, pin } from "../map.svelte";
|
import { map, pin } from "../map.svelte";
|
||||||
|
import { view } from "../view.svelte";
|
||||||
import SidebarHeader from "./SidebarHeader.svelte";
|
import SidebarHeader from "./SidebarHeader.svelte";
|
||||||
|
|
||||||
let { tags }: { tags?: string } = $props();
|
let { tags }: { tags?: string } = $props();
|
||||||
|
|
||||||
let pois: OverpassElement[] = $state([]);
|
let pois: OverpassElement[] = $state([]);
|
||||||
let loading = $state(true);
|
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!tags) {
|
if (!tags) {
|
||||||
loading = false;
|
view.loading = false;
|
||||||
pois = [];
|
pois = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
view.loading = true;
|
||||||
fetchNearbyPOI(location.lat, location.lng, tags.split(","), 500).then(
|
fetchNearbyPOI(location.lat, location.lng, tags.split(","), 500).then(
|
||||||
(results) => {
|
(results) => {
|
||||||
pois = results.elements.sort((a, b) => {
|
pois = results.elements.sort((a, b) => {
|
||||||
@ -30,7 +31,7 @@
|
|||||||
);
|
);
|
||||||
return distA - distB;
|
return distA - distB;
|
||||||
});
|
});
|
||||||
loading = false;
|
view.loading = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -60,7 +61,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
|
|
||||||
{#if loading}
|
{#if view.loading}
|
||||||
<div class="text-sm text-muted-foreground">{m.loading()}</div>
|
<div class="text-sm text-muted-foreground">{m.loading()}</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-col gap-2 p-2">
|
<div class="flex flex-col gap-2 p-2">
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
|
view.loading = true;
|
||||||
console.log(fromLocation, toLocation);
|
console.log(fromLocation, toLocation);
|
||||||
const FROM: WorldLocation =
|
const FROM: WorldLocation =
|
||||||
fromLocation == "current"
|
fromLocation == "current"
|
||||||
@ -103,6 +104,7 @@
|
|||||||
}
|
}
|
||||||
drawAllRoutes(routes);
|
drawAllRoutes(routes);
|
||||||
zoomToPoints(FROM, TO, map.value!);
|
zoomToPoints(FROM, TO, map.value!);
|
||||||
|
view.loading = false;
|
||||||
}}>{m["sidebar.route.calculate"]()}</Button
|
}}>{m["sidebar.route.calculate"]()}</Button
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import SidebarHeader from "../SidebarHeader.svelte";
|
import SidebarHeader from "../SidebarHeader.svelte";
|
||||||
import { m } from "$lang/messages";
|
import { m } from "$lang/messages";
|
||||||
|
import { view } from "../../view.svelte";
|
||||||
|
|
||||||
let licenses: License[] = $state([]);
|
let licenses: License[] = $state([]);
|
||||||
let loading = $state(true);
|
|
||||||
|
|
||||||
interface License {
|
interface License {
|
||||||
name: string;
|
name: string;
|
||||||
@ -20,15 +20,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
view.loading = true;
|
||||||
const res = await fetch("/licenses.json");
|
const res = await fetch("/licenses.json");
|
||||||
licenses = await res.json();
|
licenses = await res.json();
|
||||||
loading = false;
|
view.loading = false;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SidebarHeader>{m["sidebar.about.licenses"]()}</SidebarHeader>
|
<SidebarHeader>{m["sidebar.about.licenses"]()}</SidebarHeader>
|
||||||
|
|
||||||
{#if loading}
|
{#if view.loading}
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
{:else if licenses.length == 0}
|
{:else if licenses.length == 0}
|
||||||
<p>No licenses found.</p>
|
<p>No licenses found.</p>
|
||||||
|
|||||||
@ -6,9 +6,11 @@ export interface View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const view = $state({
|
export const view = $state({
|
||||||
|
loading: false,
|
||||||
current: { type: getOnboardingView("main") } as View,
|
current: { type: getOnboardingView("main") } as View,
|
||||||
history: [] as View[],
|
history: [] as View[],
|
||||||
back: () => {
|
back: () => {
|
||||||
|
view.loading = false;
|
||||||
if (view.history.length > 0) {
|
if (view.history.length > 0) {
|
||||||
view.current = view.history.pop()!;
|
view.current = view.history.pop()!;
|
||||||
} else {
|
} else {
|
||||||
@ -19,6 +21,7 @@ export const view = $state({
|
|||||||
if (view.current.type !== to) {
|
if (view.current.type !== to) {
|
||||||
view.history.push(view.current);
|
view.history.push(view.current);
|
||||||
}
|
}
|
||||||
|
view.loading = false;
|
||||||
view.current = { type: to, props } as View;
|
view.current = { type: to, props } as View;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user