Skip to content

Commit 3582b2d

Browse files
committed
Animate profile dropdown and submission form
1 parent bbb17bb commit 3582b2d

2 files changed

Lines changed: 41 additions & 22 deletions

File tree

src/components/Header.jsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect, useState } from 'react';
22
import { NavLink } from 'react-router-dom';
3+
import { AnimatePresence, motion } from 'framer-motion';
34
import { signOut } from 'firebase/auth';
45
import { auth } from '../lib/firebase';
56
import { useAuth } from '../context/AuthContext';
@@ -92,25 +93,33 @@ export default function Header() {
9293
{avatar}
9394
<i className={`fa-solid ${profileOpen ? 'fa-chevron-up' : 'fa-chevron-down'} text-xs text-white/70`} />
9495
</button>
95-
{profileOpen && (
96-
<div className="absolute right-0 mt-2 w-56 rounded-2xl border border-white/10 bg-slate-900/95 p-4 text-sm text-white shadow-lg">
97-
<p className="truncate text-xs text-slate-300">{user.email}</p>
98-
<NavLink
99-
to="/apps"
100-
className="mt-3 block rounded-full border border-white/15 px-3 py-2 text-center font-semibold hover:border-sky-400"
101-
onClick={() => setProfileOpen(false)}
96+
<AnimatePresence>
97+
{profileOpen && (
98+
<motion.div
99+
initial={{ opacity: 0, y: -6, scale: 0.98 }}
100+
animate={{ opacity: 1, y: 0, scale: 1 }}
101+
exit={{ opacity: 0, y: -6, scale: 0.98 }}
102+
transition={{ duration: 0.18, ease: [0.4, 0, 0.2, 1] }}
103+
className="absolute right-0 mt-2 w-56 rounded-2xl border border-white/10 bg-slate-900/95 p-4 text-sm text-white shadow-lg"
102104
>
103-
Apps dashboard
104-
</NavLink>
105-
<button
106-
type="button"
107-
className="mt-2 w-full rounded-full bg-slate-800 px-3 py-2 font-semibold hover:bg-slate-700"
108-
onClick={handleSignOut}
109-
>
110-
Sign out
111-
</button>
112-
</div>
113-
)}
105+
<p className="truncate text-xs text-slate-300">{user.email}</p>
106+
<NavLink
107+
to="/apps"
108+
className="mt-3 block rounded-full border border-white/15 px-3 py-2 text-center font-semibold hover:border-sky-400"
109+
onClick={() => setProfileOpen(false)}
110+
>
111+
Apps dashboard
112+
</NavLink>
113+
<button
114+
type="button"
115+
className="mt-2 w-full rounded-full bg-slate-800 px-3 py-2 font-semibold hover:bg-slate-700"
116+
onClick={handleSignOut}
117+
>
118+
Sign out
119+
</button>
120+
</motion.div>
121+
)}
122+
</AnimatePresence>
114123
</div>
115124
</>
116125
)}

src/pages/Apps.jsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect, useMemo, useState } from 'react';
2+
import { AnimatePresence, motion } from 'framer-motion';
23
import { Link } from 'react-router-dom';
34
import { sendEmailVerification, signOut } from 'firebase/auth';
45
import {
@@ -281,8 +282,16 @@ export default function Apps() {
281282
<i className={`fa-solid ${showSubmissionForm ? 'fa-chevron-up' : 'fa-chevron-down'} text-xs`} />
282283
</button>
283284
</div>
284-
{showSubmissionForm && (
285-
<form className="mt-6 space-y-4" onSubmit={handleAppSubmit}>
285+
<AnimatePresence initial={false}>
286+
{showSubmissionForm && (
287+
<motion.form
288+
className="mt-6 space-y-4"
289+
onSubmit={handleAppSubmit}
290+
initial={{ opacity: 0, height: 0 }}
291+
animate={{ opacity: 1, height: 'auto' }}
292+
exit={{ opacity: 0, height: 0 }}
293+
transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
294+
>
286295
<div className="grid gap-4 md:grid-cols-2">
287296
<InputField label="App name" name="name" value={formValues.name} onChange={handleFormChange} required />
288297
<InputField
@@ -346,8 +355,9 @@ export default function Apps() {
346355
>
347356
{submissionStatus.state === 'loading' ? 'Submitting…' : 'Submit for review'}
348357
</button>
349-
</form>
350-
)}
358+
</motion.form>
359+
)}
360+
</AnimatePresence>
351361
</div>
352362
{submissionStatus.message && (
353363
<p className={`text-sm ${submissionStatus.state === 'error' ? 'text-rose-200' : 'text-emerald-300'}`}>

0 commit comments

Comments
 (0)