Skip to content

Commit b63f039

Browse files
committed
feat: ✨ add transition animation
1 parent 4cd64ed commit b63f039

File tree

7 files changed

+306
-16
lines changed

7 files changed

+306
-16
lines changed

apps/web/package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
"format": "prettier --write ."
1414
},
1515
"dependencies": {
16-
"@svelte-dev/i18n": "*",
17-
"@svelte-dev/session": "*",
1816
"@svelte-dev/auth": "*",
17+
"@svelte-dev/auth-alipay": "*",
1918
"@svelte-dev/auth-github": "*",
2019
"@svelte-dev/auth-sso": "*",
21-
"@svelte-dev/auth-alipay": "*"
20+
"@svelte-dev/i18n": "*",
21+
"@svelte-dev/session": "*",
22+
"theme-change": "^2.5.0"
2223
},
2324
"devDependencies": {
2425
"@melt-ui/pp": "^0.1.4",
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script>
2+
// @ts-nocheck
3+
import { page } from '$app/stores';
4+
5+
const { className, ...rest } = $props();
6+
</script>
7+
8+
<ul
9+
class={`${className ?? 'animate-menu hidden lg:inline-flex menu menu-horizontal round'}`}
10+
{...rest}>
11+
<li aria-current={$page.url.pathname === '/' ? 'repo' : undefined}><a href="/">Item 1</a></li>
12+
<li aria-current={$page.url.pathname === '/en/' ? 'repo' : undefined}>
13+
<a href="/en">Item 2</a>
14+
</li>
15+
<li><a>Item 3</a></li>
16+
</ul>
17+
18+
<style>
19+
.animate-menu li[aria-current='repo']::before {
20+
--size: 6px;
21+
content: '';
22+
width: 0;
23+
height: 0;
24+
position: absolute;
25+
top: 0;
26+
left: calc(50% - var(--size));
27+
border: var(--size) solid transparent;
28+
border-top: var(--size) solid oklch(var(--p));
29+
view-transition-name: active-page;
30+
}
31+
</style>
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script>
2+
import { themes } from '$lib/themes';
3+
import { themeChange } from 'theme-change';
4+
5+
$effect(() => {
6+
themeChange(false);
7+
});
8+
</script>
9+
10+
<div class="dropdown dropdown-end">
11+
<div tabindex="0" class="btn gap-1 normal-case btn-ghost">
12+
<svg
13+
width="20"
14+
height="20"
15+
xmlns="http://www.w3.org/2000/svg"
16+
fill="none"
17+
viewBox="0 0 24 24"
18+
class="inline-block h-5 w-5 stroke-current md:h-6 md:w-6">
19+
<path
20+
stroke-linecap="round"
21+
stroke-linejoin="round"
22+
stroke-width="2"
23+
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
24+
</svg>
25+
<svg
26+
width="12px"
27+
height="12px"
28+
class="ml-1 hidden h-3 w-3 fill-current opacity-60 sm:inline-block"
29+
xmlns="http://www.w3.org/2000/svg"
30+
viewBox="0 0 2048 2048">
31+
<path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z" />
32+
</svg>
33+
</div>
34+
<div
35+
tabindex="0"
36+
class="dropdown-content bg-base-200 text-base-content rounded-t-box rounded-b-box top-px max-h-96 h-[70vh] w-60 overflow-y-auto shadow-2xl mt-16">
37+
<div class="grid grid-cols-1 gap-3 p-3">
38+
{#each themes as theme}
39+
<div
40+
class="outline-base-content overflow-hidden rounded-lg outline-2 outline-offset-2 hover:outline"
41+
data-set-theme={theme.id}
42+
data-act-class="[&_svg]:visible">
43+
<div
44+
data-theme={theme.id}
45+
class="bg-base-100 text-base-content w-full cursor-pointer font-sans">
46+
<div class="grid grid-cols-5 grid-rows-3">
47+
<div class="col-span-5 row-span-3 row-start-1 flex gap-1 py-3 px-4">
48+
<svg
49+
xmlns="http://www.w3.org/2000/svg"
50+
width="16"
51+
height="16"
52+
viewBox="0 0 24 24"
53+
fill="currentColor"
54+
class="invisible h-3 w-3 shrink-0 mt-1">
55+
<path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z" />
56+
</svg>
57+
<div class="flex-grow text-sm font-bold">{theme.name}</div>
58+
<div class="flex flex-shrink-0 flex-wrap gap-1">
59+
<div class="bg-primary w-2 rounded" />
60+
<div class="bg-secondary w-2 rounded" />
61+
<div class="bg-accent w-2 rounded" />
62+
<div class="bg-neutral w-2 rounded" />
63+
</div>
64+
</div>
65+
</div>
66+
</div>
67+
</div>
68+
{/each}
69+
</div>
70+
</div>
71+
</div>

apps/web/src/components/Navbar.svelte

+31-12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { fallbackLng } from '$lib/i8n';
44
import { getRealPath } from '$lib/utils';
55
import { locale, locales } from '@svelte-dev/i18n';
6+
import ChangeRepo from './ChangeRepo.svelte';
67
78
const pathname = $derived(
89
`${$locale === fallbackLng ? '' : `/${$locale}`}${getRealPath($page.url.pathname, $locales)}`
@@ -25,24 +26,42 @@
2526
stroke-width="2"
2627
d="M4 6h16M4 12h8m-8 6h16" /></svg>
2728
</div>
28-
<ul
29+
<ChangeRepo
2930
tabindex="0"
30-
class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
31-
<!-- <li><a>Item 1</a></li>
32-
<li>
33-
<a>Parent</a>
34-
<ul class="p-2">
35-
<li><a>Submenu 1</a></li>
36-
<li><a>Submenu 2</a></li>
37-
</ul>
38-
</li>
39-
<li><a>Item 3</a></li> -->
40-
</ul>
31+
className="menu dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52" />
4132
</div>
4233
<a href={pathname} class="btn btn-ghost text-xl">Svelte</a>
34+
<ChangeRepo />
4335
</div>
4436

4537
<div class="navbar-end">
38+
{#await import('./ChangeTheme.svelte')}
39+
<div class="btn btn-ghost gap-1 normal-case cursor-wait">
40+
<svg
41+
width="20"
42+
height="20"
43+
xmlns="http://www.w3.org/2000/svg"
44+
fill="none"
45+
viewBox="0 0 24 24"
46+
class="inline-block h-5 w-5 stroke-current md:h-6 md:w-6">
47+
<path
48+
stroke-linecap="round"
49+
stroke-linejoin="round"
50+
stroke-width="2"
51+
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
52+
</svg>
53+
<svg
54+
width="12px"
55+
height="12px"
56+
class="ml-1 hidden h-3 w-3 fill-current opacity-60 sm:inline-block"
57+
xmlns="http://www.w3.org/2000/svg"
58+
viewBox="0 0 2048 2048">
59+
<path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z" />
60+
</svg>
61+
</div>
62+
{:then Module}
63+
<Module.default />
64+
{/await}
4665
{#await import('./ChangeLanguage.svelte')}
4766
<div class="btn btn-ghost gap-1 normal-case cursor-wait">
4867
<svg

apps/web/src/lib/themes.ts

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
export const defaultDarkTheme = 'sunset';
2+
export const defaultLightTheme = 'retro';
3+
4+
export const themes = [
5+
{
6+
name: '🌝  light',
7+
id: 'light'
8+
},
9+
{
10+
name: '🌚  dark',
11+
id: 'dark'
12+
},
13+
{
14+
name: '🧁  cupcake',
15+
id: 'cupcake'
16+
},
17+
{
18+
name: '🐝  bumblebee',
19+
id: 'bumblebee'
20+
},
21+
{
22+
name: '✳️  Emerald',
23+
id: 'emerald'
24+
},
25+
{
26+
name: '🏢  Corporate',
27+
id: 'corporate'
28+
},
29+
{
30+
name: '🌃  synthwave',
31+
id: 'synthwave'
32+
},
33+
{
34+
name: '👴  retro',
35+
id: 'retro'
36+
},
37+
{
38+
name: '🤖  cyberpunk',
39+
id: 'cyberpunk'
40+
},
41+
{
42+
name: '🌸  valentine',
43+
id: 'valentine'
44+
},
45+
{
46+
name: '🎃  halloween',
47+
id: 'halloween'
48+
},
49+
{
50+
name: '🌷  garden',
51+
id: 'garden'
52+
},
53+
{
54+
name: '🌲  forest',
55+
id: 'forest'
56+
},
57+
{
58+
name: '🐟  aqua',
59+
id: 'aqua'
60+
},
61+
{
62+
name: '👓  lofi',
63+
id: 'lofi'
64+
},
65+
{
66+
name: '🖍  pastel',
67+
id: 'pastel'
68+
},
69+
{
70+
name: '🧚‍♀️  fantasy',
71+
id: 'fantasy'
72+
},
73+
{
74+
name: '📝  Wireframe',
75+
id: 'wireframe'
76+
},
77+
{
78+
name: '🏴  black',
79+
id: 'black'
80+
},
81+
{
82+
name: '💎  luxury',
83+
id: 'luxury'
84+
},
85+
{
86+
name: '🧛‍♂️  dracula',
87+
id: 'dracula'
88+
},
89+
{
90+
name: '🖨  CMYK',
91+
id: 'cmyk'
92+
},
93+
{
94+
name: '🍁  Autumn',
95+
id: 'autumn'
96+
},
97+
{
98+
name: '💼  Business',
99+
id: 'business'
100+
},
101+
{
102+
name: '💊  Acid',
103+
id: 'acid'
104+
},
105+
{
106+
name: '🍋  Lemonade',
107+
id: 'lemonade'
108+
},
109+
{
110+
name: '🌙  Night',
111+
id: 'night'
112+
},
113+
{
114+
name: '☕️  Coffee',
115+
id: 'coffee'
116+
},
117+
{
118+
name: '❄️  Winter',
119+
id: 'winter'
120+
},
121+
{
122+
name: '🕶️ Dim',
123+
id: 'dim'
124+
},
125+
{
126+
name: '🤓 Nord',
127+
id: 'nord'
128+
},
129+
{
130+
name: '🌇 Sunset',
131+
id: 'sunset'
132+
}
133+
] as const;
134+
135+
export const darkThemes = [
136+
'dark',
137+
'synthwave',
138+
'halloween',
139+
'forest',
140+
'black',
141+
'luxury',
142+
'dracula',
143+
'business',
144+
'night',
145+
'coffee',
146+
'dim',
147+
'sunset'
148+
] as const;

apps/web/src/routes/[[lang=locale]]/+layout.svelte

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
<script>
22
import '../../app.css';
3-
import { t } from '@svelte-dev/i18n';
3+
import { locale, t } from '@svelte-dev/i18n';
44
import Navbar from '$components/Navbar.svelte';
5+
import { onNavigate } from '$app/navigation';
6+
import { navigating } from '$app/stores';
7+
import { fallbackLng } from '$lib/i8n';
8+
9+
navigating.subscribe((params) => {
10+
if (params?.to) locale.set(params.to?.params?.lang ?? fallbackLng);
11+
});
12+
13+
onNavigate((navigation) => {
14+
// @ts-ignore
15+
if (!document.startViewTransition) return;
16+
17+
return new Promise((resolve) => {
18+
// @ts-ignore
19+
document.startViewTransition(async () => {
20+
resolve();
21+
await navigation.complete;
22+
});
23+
});
24+
});
525
</script>
626
727
<Navbar />

bun.lockb

360 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)