Skip to content

Commit b641136

Browse files
committed
fix: Current path cards, things i love cards
1 parent b80c01e commit b641136

3 files changed

Lines changed: 173 additions & 37 deletions

File tree

components/GridItems/Languages.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ const Languages = ({ item }: { item: CombinedInterfaces }) => {
6464
animate={{ opacity: 1 }}
6565
transition={{ duration: 0.5, ease: "easeInOut" }}
6666
>
67-
<div className="flex flex-col w-full h-full overflow-hidden rounded-3xl">
67+
<div className="relative flex h-full w-full overflow-hidden rounded-3xl">
6868
{displayText.length > 0 && (
69-
<div className="flex items-start w-full h-full overflow-hidden p-8 justify-start align-center">
69+
<div className="pointer-events-none absolute inset-x-0 top-0 z-30 p-4 md:p-8">
7070
<motion.div
71-
className="z-10 bg-white dark:bg-neutral-900 flex items-start p-3 text-sm font-medium text-black dark:text-white md:h-16 rounded-lg w-full"
71+
className="flex max-h-28 w-full items-start overflow-hidden rounded-lg bg-white p-3 text-sm font-medium text-black dark:bg-neutral-900 dark:text-white"
7272
initial={{ opacity: 0.5 }}
7373
animate={{ opacity: 0.9 }}
7474
style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
@@ -77,7 +77,7 @@ const Languages = ({ item }: { item: CombinedInterfaces }) => {
7777
</motion.div>
7878
</div>
7979
)}
80-
<div className="relative flex flex-col items-end justify-end w-full h-full overflow-hidden rounded-3xl p-1">
80+
<div className="relative flex h-full w-full flex-col items-end justify-end overflow-hidden rounded-3xl p-1">
8181
{/* Overlay */}
8282
<div className="absolute inset-0 z-10 bg-gradient-to-b from-transparent via-neutral-950/30 to-neutral-950/30" />
8383
{/* Image */}

components/LeftPanel/index.tsx

Lines changed: 147 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,98 @@ import { IconTypes, LeftPanelOptions } from "@/components/Types/enum";
66
import { LeftPanelProps } from "@/components/Types";
77
import { useState, useEffect, useRef } from "react";
88
import { motion, AnimatePresence, PanInfo } from "framer-motion";
9-
import { FiMenu, FiX, FiChevronLeft } from "react-icons/fi";
9+
import { FiMenu, FiX, FiChevronLeft, FiChevronRight } from "react-icons/fi";
1010
import { siteConfig } from "@/config/site-config";
1111

12+
const GoalCardStack = ({
13+
controls = false,
14+
swipe = true,
15+
swipeAxis = "x",
16+
}: {
17+
controls?: boolean;
18+
swipe?: boolean;
19+
swipeAxis?: "x" | "y";
20+
}) => {
21+
const [activeIndex, setActiveIndex] = useState(0);
22+
const goals = siteConfig.currentGoalItems;
23+
const total = goals.length;
24+
const nextGoal = () => setActiveIndex((index) => (index + 1) % total);
25+
const prevGoal = () => setActiveIndex((index) => (index - 1 + total) % total);
26+
27+
const handleSwipe = (
28+
_: MouseEvent | TouchEvent | PointerEvent,
29+
info: PanInfo,
30+
) => {
31+
const offset = swipeAxis === "y" ? info.offset.y : info.offset.x;
32+
if (offset < -50) nextGoal();
33+
if (offset > 50) prevGoal();
34+
};
35+
36+
return (
37+
<div
38+
className="relative mt-6 h-40"
39+
onPointerDown={(event) => event.stopPropagation()}
40+
>
41+
{goals.map((goal, index) => {
42+
const stackIndex = (index - activeIndex + total) % total;
43+
if (stackIndex > 2) return null;
44+
45+
return (
46+
<motion.div
47+
key={goal.title}
48+
className="absolute inset-x-0 top-0 h-32 rounded-2xl border border-white/10 p-5 text-white shadow-md"
49+
style={{
50+
background: goal.color,
51+
zIndex: 30 - stackIndex,
52+
}}
53+
animate={{
54+
y: stackIndex * 12,
55+
scale: 1 - stackIndex * 0.04,
56+
opacity: 1 - stackIndex * 0.18,
57+
}}
58+
drag={swipe && stackIndex === 0 ? swipeAxis : false}
59+
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
60+
dragElastic={0.25}
61+
onDragEnd={handleSwipe}
62+
whileTap={
63+
swipe && stackIndex === 0 ? { cursor: "grabbing" } : undefined
64+
}
65+
>
66+
<p className="text-sm font-medium text-white/75">{goal.label}</p>
67+
<p className="mt-2 text-lg font-semibold leading-snug">
68+
{goal.title}
69+
</p>
70+
<p className="mt-2 text-xs leading-relaxed text-white/75">
71+
{goal.description}
72+
</p>
73+
</motion.div>
74+
);
75+
})}
76+
77+
{controls && (
78+
<div className="absolute bottom-0 right-0 z-40 flex gap-2">
79+
<button
80+
type="button"
81+
onClick={prevGoal}
82+
className="grid h-8 w-8 place-items-center rounded-lg border border-neutral-200 bg-white text-neutral-700 shadow-sm dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
83+
aria-label="Previous goal"
84+
>
85+
<FiChevronLeft size={16} />
86+
</button>
87+
<button
88+
type="button"
89+
onClick={nextGoal}
90+
className="grid h-8 w-8 place-items-center rounded-lg border border-neutral-200 bg-white text-neutral-700 shadow-sm dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
91+
aria-label="Next goal"
92+
>
93+
<FiChevronRight size={16} />
94+
</button>
95+
</div>
96+
)}
97+
</div>
98+
);
99+
};
100+
12101
const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
13102
const [clicked, setClicked] = useState(LeftPanelOptions.ABOUT);
14103
const [isOpen, setIsOpen] = useState(true);
@@ -37,27 +126,33 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
37126

38127
useEffect(() => {
39128
const handleEscape = (e: KeyboardEvent) => {
40-
if (e.key === 'Escape') closeMenu();
129+
if (e.key === "Escape") closeMenu();
41130
};
42-
131+
43132
if (isOpen) {
44-
document.addEventListener('keydown', handleEscape);
45-
document.body.style.overflow = 'hidden';
133+
document.addEventListener("keydown", handleEscape);
134+
document.body.style.overflow = "hidden";
46135
}
47-
136+
48137
return () => {
49-
document.removeEventListener('keydown', handleEscape);
50-
document.body.style.overflow = 'unset';
138+
document.removeEventListener("keydown", handleEscape);
139+
document.body.style.overflow = "unset";
51140
};
52141
}, [isOpen]);
53142

54143
// Handle drag to close
55-
const handleDrag = (event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
144+
const handleDrag = (
145+
event: MouseEvent | TouchEvent | PointerEvent,
146+
info: PanInfo,
147+
) => {
56148
const progress = Math.max(0, Math.min(1, -info.offset.x / 250));
57149
setDragProgress(progress);
58150
};
59151

60-
const handleDragEnd = (event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
152+
const handleDragEnd = (
153+
event: MouseEvent | TouchEvent | PointerEvent,
154+
info: PanInfo,
155+
) => {
61156
if (info.offset.x < -100 || info.velocity.x < -500) {
62157
closeMenu();
63158
}
@@ -67,7 +162,7 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
67162
return (
68163
<>
69164
{/* Mobile Header with Hamburger */}
70-
<motion.div
165+
<motion.div
71166
className="xl:hidden flex items-center justify-between p-4 bg-white/80 dark:bg-neutral-900/80 backdrop-blur-md border-b border-neutral-200 dark:border-neutral-700 sticky top-0 z-40"
72167
initial={{ y: -100 }}
73168
animate={{ y: 0 }}
@@ -83,18 +178,18 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
83178
<FiMenu size={20} />
84179
<span className="text-sm font-medium">Menu</span>
85180
</motion.button>
86-
87-
<motion.h1
181+
182+
<motion.h1
88183
className="text-lg font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"
89184
initial={{ opacity: 0, x: 20 }}
90185
animate={{ opacity: 1, x: 0 }}
91186
transition={{ delay: 0.1 }}
92187
>
93-
{clicked === LeftPanelOptions.ABOUT ? "Portfolio": clicked}
188+
{clicked === LeftPanelOptions.ABOUT ? "Portfolio" : clicked}
94189
</motion.h1>
95190
</motion.div>
96191

97-
<motion.div
192+
<motion.div
98193
className="hidden xl:flex flex-col justify-between py-6 xl:max-w-sm xl:py-8 xl:h-full"
99194
initial={{ x: -50, opacity: 0 }}
100195
animate={{ x: 0, opacity: 1 }}
@@ -121,6 +216,13 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
121216
</motion.div>
122217
))}
123218
</div>
219+
<motion.div
220+
initial={{ y: 20, opacity: 0 }}
221+
animate={{ y: 0, opacity: 1 }}
222+
transition={{ delay: 0.75 }}
223+
>
224+
<GoalCardStack controls swipe={false} />
225+
</motion.div>
124226
<motion.div
125227
className="hidden mt-6 xl:flex cursor-pointer relative group"
126228
onClick={handleInteraction}
@@ -150,27 +252,27 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
150252
onClick={closeMenu}
151253
transition={{ duration: 0.2 }}
152254
/>
153-
255+
154256
{/* Slide-out Panel */}
155257
<motion.div
156258
ref={constraintsRef}
157259
className="fixed inset-0 z-50 w-screen xl:hidden"
158260
initial={{ x: "-100%" }}
159261
animate={{ x: 0 }}
160262
exit={{ x: "-100%" }}
161-
transition={{
162-
type: "spring",
163-
stiffness: 400,
263+
transition={{
264+
type: "spring",
265+
stiffness: 400,
164266
damping: 30,
165-
mass: 0.8
267+
mass: 0.8,
166268
}}
167269
drag="x"
168270
dragConstraints={{ left: -250, right: 0 }}
169271
dragElastic={0.1}
170272
onDrag={handleDrag}
171273
onDragEnd={handleDragEnd}
172274
style={{
173-
x: dragProgress ? -dragProgress * 250 : 0
275+
x: dragProgress ? -dragProgress * 250 : 0,
174276
}}
175277
>
176278
<div className="h-full bg-white dark:bg-neutral-900 shadow-2xl flex flex-col">
@@ -199,9 +301,9 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
199301
>
200302
<Bio compact />
201303
</motion.div>
202-
304+
203305
<div className="border-b my-6 border-neutral-200 dark:border-neutral-700" />
204-
306+
205307
<div className="flex flex-col gap-2">
206308
{menuItems.map((item, index) => (
207309
<motion.div
@@ -215,13 +317,25 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
215317
category={item.icon}
216318
href={`#`}
217319
clickedCategory={clickedCategory}
218-
setClicked={() => handleMenuClick(item.text as LeftPanelOptions)}
320+
setClicked={() =>
321+
handleMenuClick(item.text as LeftPanelOptions)
322+
}
219323
clicked={clicked}
220324
/>
221325
</motion.div>
222326
))}
223327
</div>
224328

329+
<div className="flex-1 overflow-y-auto pt-6">
330+
<motion.div
331+
initial={{ y: 20, opacity: 0 }}
332+
animate={{ y: 0, opacity: 1 }}
333+
transition={{ delay: 0.35 }}
334+
>
335+
<GoalCardStack swipeAxis="y" />
336+
</motion.div>
337+
</div>
338+
225339
<motion.div
226340
className="mt-8 flex cursor-pointer relative group"
227341
onClick={handleHiddenClick}
@@ -235,26 +349,26 @@ const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => {
235349
</div>
236350

237351
{/* Drag Hint */}
238-
<motion.div
352+
<motion.div
239353
className="absolute right-4 top-1/2 transform -translate-y-1/2 opacity-30 pointer-events-none"
240-
animate={{
354+
animate={{
241355
x: [0, -10, 0],
242-
opacity: [0.3, 0.6, 0.3]
356+
opacity: [0.3, 0.6, 0.3],
243357
}}
244-
transition={{
245-
duration: 2,
358+
transition={{
359+
duration: 2,
246360
repeat: Infinity,
247-
ease: "easeInOut"
361+
ease: "easeInOut",
248362
}}
249363
>
250364
<FiChevronLeft size={24} />
251365
</motion.div>
252366

253-
<div
367+
<div
254368
className="absolute right-0 top-0 bottom-0 w-1 bg-gradient-to-b from-blue-500 to-purple-500 rounded-l-full transition-all duration-200"
255-
style={{
369+
style={{
256370
opacity: dragProgress,
257-
transform: `scaleY(${0.3 + dragProgress * 0.7})`
371+
transform: `scaleY(${0.3 + dragProgress * 0.7})`,
258372
}}
259373
/>
260374
</div>

config/site-config.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,27 @@ const GridItems: GridItemInterface[] = [
642642
},
643643
];
644644

645+
const CurrentGoalItems = [
646+
{
647+
label: "My current goal",
648+
title: "Neuro AI RSE career path",
649+
description: "Build toward research software roles in neuro AI.",
650+
color: "linear-gradient(135deg, #2563eb, #9333ea)",
651+
},
652+
{
653+
label: "Now learning",
654+
title: "Neuroscience tooling",
655+
description: "MNE, signal workflows, reproducible analysis.",
656+
color: "linear-gradient(135deg, #0891b2, #16a34a)",
657+
},
658+
{
659+
label: "Next proof",
660+
title: "Open-source research work",
661+
description: "More useful commits, notebooks, and small tools.",
662+
color: "linear-gradient(135deg, #ea580c, #db2777)",
663+
},
664+
];
665+
645666
export const currentYear = new Date().getFullYear();
646667

647668
export const siteConfig = {
@@ -661,4 +682,5 @@ export const siteConfig = {
661682
achievementItems: AchItems,
662683
experienceItems: ExpItems,
663684
projectItems: ProjItems,
685+
currentGoalItems: CurrentGoalItems,
664686
} as const;

0 commit comments

Comments
 (0)