Skip to content

Commit 232ecc2

Browse files
authored
Merge pull request #152 from stophecom/feat/progress-loader
feat: implement global progress loader with delay and transform perfo…
2 parents 61d38f8 + 114b322 commit 232ecc2

2 files changed

Lines changed: 120 additions & 1 deletion

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<script lang="ts" module>
2+
import type { NavigationTarget } from '@sveltejs/kit';
3+
import { twMerge } from 'tailwind-merge';
4+
import { tv } from 'tailwind-variants';
5+
6+
export type NavigationParams = { from: NavigationTarget | null; to: NavigationTarget | null };
7+
8+
export const navbarVariants = tv({
9+
base: 'relative z-50 h-0.5 rounded-r-lg',
10+
variants: {
11+
size: {
12+
sm: 'h-0.5',
13+
md: 'h-1',
14+
lg: 'h-1.5'
15+
},
16+
color: {
17+
theme: 'bg-gradient-to-bl from-[var(--color-primary)] to-[var(--color-primary)]'
18+
}
19+
},
20+
defaultVariants: {
21+
size: 'sm',
22+
color: 'theme'
23+
}
24+
});
25+
26+
type NavType = typeof navbarVariants;
27+
export interface ProgressProps {
28+
class?: string;
29+
size?: keyof NavType['variants']['size'];
30+
color?: keyof NavType['variants']['color'];
31+
initialDuration?: number;
32+
firstPhaseDuration?: number;
33+
secondPhaseDuration?: number;
34+
completionDuration?: number;
35+
resetDelay?: number;
36+
firstPhaseEasing?: (t: number) => number;
37+
secondPhaseEasing?: (t: number) => number;
38+
completionEasing?: (t: number) => number;
39+
}
40+
</script>
41+
42+
<script lang="ts">
43+
import { cubicOut, linear } from 'svelte/easing';
44+
import { Tween } from 'svelte/motion';
45+
46+
import { afterNavigate, beforeNavigate } from '$app/navigation';
47+
48+
let {
49+
size,
50+
color,
51+
class: className,
52+
initialDuration = 500,
53+
firstPhaseDuration = 900,
54+
secondPhaseDuration = 600,
55+
completionDuration = 800,
56+
resetDelay = 500,
57+
firstPhaseEasing = cubicOut,
58+
secondPhaseEasing = cubicOut,
59+
completionEasing = linear
60+
}: ProgressProps = $props();
61+
62+
let progress = new Tween(0, { duration: initialDuration });
63+
let isVisible = $state(false);
64+
let delayTimer: ReturnType<typeof setTimeout> | undefined;
65+
66+
const progressReset = () => {
67+
setTimeout(() => {
68+
progress.set(0, { duration: 0 });
69+
isVisible = false;
70+
}, resetDelay);
71+
};
72+
73+
const progressStyle = $derived(
74+
`transform: scaleX(${progress.current / 100}); transform-origin: left;`
75+
);
76+
77+
function isDiffNavigation(navigation: NavigationParams) {
78+
return navigation.from?.url.href === navigation.to?.url.href;
79+
}
80+
81+
beforeNavigate(async (navigation) => {
82+
if (!isDiffNavigation(navigation)) {
83+
isVisible = false;
84+
if (delayTimer) clearTimeout(delayTimer);
85+
delayTimer = setTimeout(() => {
86+
isVisible = true;
87+
}, 200);
88+
89+
await progress.set(0, { duration: 0 });
90+
await progress.set(35, { duration: firstPhaseDuration, easing: firstPhaseEasing });
91+
await progress.set(75, { duration: secondPhaseDuration, easing: secondPhaseEasing });
92+
} else {
93+
await progress.set(0, { duration: 0 });
94+
}
95+
});
96+
97+
afterNavigate(async (navigation) => {
98+
if (!isDiffNavigation(navigation)) {
99+
if (delayTimer) clearTimeout(delayTimer);
100+
101+
await progress.set(100, { duration: completionDuration, easing: completionEasing });
102+
progressReset();
103+
} else {
104+
if (delayTimer) clearTimeout(delayTimer);
105+
await progress.set(0, { duration: 0 });
106+
isVisible = false;
107+
}
108+
});
109+
</script>
110+
111+
{#if isVisible}
112+
<div class="fixed top-0 left-0 z-110 w-full">
113+
<div
114+
class={twMerge(className, navbarVariants({ size, color }), 'w-full')}
115+
style={progressStyle}
116+
></div>
117+
</div>
118+
{/if}

src/routes/(app)/+layout.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { page } from '$app/state';
55
import { PUBLIC_ENV } from '$env/static/public';
66
import { plausible } from '$lib/client/plausible';
7+
import Progress from '$lib/components/blocks/progress.svelte';
78
import { appName } from '$lib/data/app';
89
import { getLocale } from '$lib/paraglide/runtime';
910
@@ -51,5 +52,5 @@
5152
<meta name="robots" content="noindex" />
5253
{/if}
5354
</svelte:head>
54-
55+
<Progress />
5556
{@render children()}

0 commit comments

Comments
 (0)