diff --git a/components/LeftPanel/index.tsx b/components/LeftPanel/index.tsx index 0174509..e324a42 100644 --- a/components/LeftPanel/index.tsx +++ b/components/LeftPanel/index.tsx @@ -4,67 +4,259 @@ import Bio from "./Bio"; import MiniButton from "@/components/common/MiniButton"; import { IconTypes, LeftPanelOptions } from "@/components/Types/enum"; import { LeftPanelProps } from "@/components/Types"; -import { useState } from "react"; +import { useState, useEffect, useRef } from "react"; +import { motion, AnimatePresence, PanInfo } from "framer-motion"; +import { FiMenu, FiX, FiChevronLeft } from "react-icons/fi"; const LeftPanel = ({ clickedCategory, handleInteraction }: LeftPanelProps) => { const [clicked, setClicked] = useState(LeftPanelOptions.ABOUT); + const [isOpen, setIsOpen] = useState(false); + const [dragProgress, setDragProgress] = useState(0); + const constraintsRef = useRef(null); - return ( -
-
- -
+ const menuItems = [ + { text: LeftPanelOptions.ABOUT, icon: IconTypes.SEARCH }, + { text: LeftPanelOptions.EXPERIENCE, icon: IconTypes.ALBUM }, + { text: LeftPanelOptions.ACHIEVEMENTS, icon: IconTypes.MEDAL }, + { text: LeftPanelOptions.PROJECTS, icon: IconTypes.PROJECTS }, + ]; -
- - - - -
+ const handleMenuClick = (item: LeftPanelOptions) => { + setClicked(item); + setIsOpen(false); + }; + + const closeMenu = () => setIsOpen(false); + + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') closeMenu(); + }; + + if (isOpen) { + document.addEventListener('keydown', handleEscape); + document.body.style.overflow = 'hidden'; + } + + return () => { + document.removeEventListener('keydown', handleEscape); + document.body.style.overflow = 'unset'; + }; + }, [isOpen]); -
{ + const progress = Math.max(0, Math.min(1, -info.offset.x / 250)); + setDragProgress(progress); + }; + + const handleDragEnd = (event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => { + if (info.offset.x < -100 || info.velocity.x < -500) { + closeMenu(); + } + setDragProgress(0); + }; + + return ( + <> + {/* Mobile Header with Hamburger */} + + setIsOpen(true)} + className="flex items-center gap-3 px-3 py-2 rounded-xl bg-neutral-100 dark:bg-neutral-800 hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors duration-200" + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + aria-label="Open Menu" > -
- - Hidden element... SSSHHHHH...🤫 - + + Menu + + + + {clicked === LeftPanelOptions.ABOUT ? "Portfolio": clicked} + + + + +
+ +
+
+ {menuItems.map((item, index) => ( + + + + ))} +
+ +
+ + Hidden element... SSSHHHHH...🤫 + +
-
-
+ + + {/* Mobile Overlay Menu - Now from the LEFT */} + + {isOpen && ( + <> + {/* Backdrop */} + + + {/* Slide-out Panel */} + +
+ {/* Header */} +
+

+ Profile +

+ + + +
+ + {/* Content */} +
+ + + + +
+ +
+ {menuItems.map((item, index) => ( + + handleMenuClick(item.text as LeftPanelOptions)} + clicked={clicked} + /> + + ))} +
+ + +
+ +
+ + {/* Drag Hint */} + + + + +
+
+ + + )} + + ); }; diff --git a/config/site-config.ts b/config/site-config.ts index 67dee7c..08b9062 100644 --- a/config/site-config.ts +++ b/config/site-config.ts @@ -529,13 +529,13 @@ export const siteConfig = { description: "Portfolio website powered by Next.js, Tailwind CSS", }, creator: "Balasubramanian T K", - title: "Software Developer", + title: "Senior Software Engineer", bio: "Web Developer & ELT Enthusiast, Competent with JavaScript and Python.", location: "Kerala, India", locationLink: "https://maps.app.goo.gl/HTiGU32uhqhmVuBZ8", email: "btk.codedev@gmail.com", - footerText: `Built by @btkcodedev | ©${currentYear} btkcodedev. All Rights Reserved`, + footerText: `I am not Apple, so you could 'steal my work' (Hackintosh guys know this reference :P ), but karma always says to give credits`, aboutItems: GridItems, achievementItems: AchItems, experienceItems: ExpItems, diff --git a/package-lock.json b/package-lock.json index 031a194..dc21d04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "postcss": "8.4.24", "react": "^18", "react-dom": "^18", + "react-icons": "^5.5.0", "tailwind-merge": "^1.13.2", "tailwindcss": "3.3.2", "typescript": "5.1.3" @@ -3788,6 +3789,15 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 215a8f6..91fa445 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "btkcodedev-portfolio", - "version": "1.0.0", + "version": "2.0.0", "private": true, - "license": "UNLICENSED", + "license": "Proprietary", "scripts": { "dev": "next dev", "build": "next build", @@ -26,8 +26,9 @@ "postcss": "8.4.24", "react": "^18", "react-dom": "^18", + "react-icons": "^5.5.0", "tailwind-merge": "^1.13.2", "tailwindcss": "3.3.2", "typescript": "5.1.3" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 3cabd1a..9359f6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2241,6 +2241,11 @@ react-dom@*, react-dom@^18, react-dom@^18.0.0, react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-icons@^5.5.0: + version "5.5.0" + resolved "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz" + integrity sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw== + react-is@^16.13.1: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"