Skip to content

Commit

Permalink
feat: Add skills page and enhance useObserver hook (#14)
Browse files Browse the repository at this point in the history
- Created a new skills page with additional skills.
- Refactored `useObserver` hook to observe elements only once on initial page load.
- Added visibility property for education and experience sections.
  • Loading branch information
Fingertips18 authored Nov 2, 2024
1 parent a1b7095 commit daef59e
Show file tree
Hide file tree
Showing 18 changed files with 268 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/components/common/hint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../shadcn/tooltip";
} from "@/components/shadcn/tooltip";

interface HintProps {
children: React.ReactNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { cn } from "@/lib/utils";
interface SkillIconProps {
Icon: IconType;
hexColor: string;
ariaHidden?: React.AriaAttributes["aria-hidden"];
}

const SkillIcon = ({ Icon, hexColor }: SkillIconProps) => {
const SkillIcon = ({ Icon, hexColor, ariaHidden }: SkillIconProps) => {
const [hovered, setHovered] = useState(false);

return (
<li
aria-hidden={ariaHidden}
className={cn(
"rounded-full p-4 border",
"rounded-full p-4 border bg-foreground/5",
hovered ? "border-foreground/15" : "border-border"
)}
onMouseEnter={() => setHovered(true)}
Expand Down
61 changes: 45 additions & 16 deletions src/components/header/sheet-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { LucideMenu } from "lucide-react";
import { Link, useLocation } from "react-router-dom";
import { LucideMenu, MoveLeft } from "lucide-react";
import { useEffect, useState } from "react";
import { useLenis } from "lenis/react";

import {
Expand All @@ -12,6 +14,8 @@ import {
} from "@/components/shadcn/sheet";
import { Button } from "@/components/shadcn/button";
import { ROOTMENU } from "@/constants/collections";
import { useResize } from "@/lib/hooks/useResize";
import { AppRoutes } from "@/routes/app-routes";
import { Hint } from "@/components/common/hint";
import { cn } from "@/lib/utils";

Expand All @@ -23,6 +27,15 @@ interface SheetMenuProps {

const SheetMenu = ({ active }: SheetMenuProps) => {
const lenis = useLenis();
const location = useLocation();
const [open, setOpen] = useState(false);
const { width } = useResize();

useEffect(() => {
if (width > 1024) {
setOpen(false);
}
}, [width]);

const onOpenChange = (open: boolean) => {
if (open) {
Expand All @@ -44,12 +57,19 @@ const SheetMenu = ({ active }: SheetMenuProps) => {
};

return (
<Sheet onOpenChange={onOpenChange}>
<Sheet
open={open}
onOpenChange={(open) => {
onOpenChange(open);
setOpen(open);
}}
>
<Hint asChild label="Menu">
<SheetTrigger asChild>
<Button
variant={"ghost"}
size={"icon"}
aria-label="menu-toggle"
className="lg:hidden hover:drop-shadow-primary-glow"
>
<LucideMenu className="w-5 h-5" />
Expand All @@ -65,20 +85,29 @@ const SheetMenu = ({ active }: SheetMenuProps) => {
</SheetHeader>

<nav className="w-full flex justify-end mt-10 flex-1">
<ul className="space-y-6 text-end">
{ROOTMENU.map((m, i) => (
<li
key={`${m.label}-${i}`}
className={cn(
"capitalize font-semibold leading-none hover:scale-95 transition-all cursor-pointer hover:drop-shadow-primary-glow lg:hover:text-accent",
active === m.label && "text-accent"
)}
onClick={() => onClick(m.label)}
>
{m.label}
</li>
))}
</ul>
{location.pathname === "/" ? (
<ul className="space-y-6 text-end">
{ROOTMENU.map((m, i) => (
<li
key={`${m.label}-${i}`}
className={cn(
"capitalize font-semibold leading-none hover:scale-95 transition-all cursor-pointer hover:drop-shadow-primary-glow lg:hover:text-accent",
active === m.label && "text-accent"
)}
onClick={() => onClick(m.label)}
>
{m.label}
</li>
))}
</ul>
) : (
<Link
to={AppRoutes.root}
className="flex items-center gap-x-2 hover:text-accent"
>
<MoveLeft className="size-4" /> Go home
</Link>
)}
</nav>

<SheetFooter className="fixed bottom-4 right-4">
Expand Down
11 changes: 10 additions & 1 deletion src/components/header/spread-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useLocation } from "react-router-dom";
import { useLenis } from "lenis/react";

import { ROOTMENU } from "@/constants/collections";
Expand All @@ -10,6 +11,7 @@ interface SpreadMenuProps {

const SpreadMenu = ({ active, isMounted }: SpreadMenuProps) => {
const lenis = useLenis();
const location = useLocation();

const onClick = (id: string) => {
const section = document.getElementById(id);
Expand All @@ -19,7 +21,14 @@ const SpreadMenu = ({ active, isMounted }: SpreadMenuProps) => {
};

return (
<nav className="hidden lg:flex-center px-4 flex-grow">
<nav
className={cn(
"hidden lg:flex-center px-4 flex-grow transition-opacity duration-500 ease-in-out",
location.pathname === "/"
? "opacity-100"
: "opacity-0 pointer-events-none"
)}
>
<ul
className={cn(
"flex-center gap-x-10 transition-opacity duration-1000 ease-in-out",
Expand Down
42 changes: 42 additions & 0 deletions src/constants/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
SiCss3Hex,
SiDart,
SiDartHex,
SiDocker,
SiDockerHex,
SiDotnet,
SiDotnetHex,
SiExpress,
SiExpressHex,
SiFigma,
Expand All @@ -37,6 +41,8 @@ import {
SiJavascriptHex,
SiJupyter,
SiJupyterHex,
SiLaravel,
SiLaravelHex,
SiMongodb,
SiMongodbHex,
SiMysql,
Expand All @@ -57,14 +63,20 @@ import {
SiPrismaHex,
SiPython,
SiPythonHex,
SiRailway,
SiRailwayHex,
SiReact,
SiReactHex,
SiReacthookform,
SiReacthookformHex,
SiRedux,
SiReduxHex,
SiRender,
SiRenrenHex,
SiRive,
SiRiveHex,
SiSentry,
SiSentryHex,
SiShadcnui,
SiShadcnuiHex,
SiSupabase,
Expand Down Expand Up @@ -188,6 +200,16 @@ export const BACKEND = [
hexColor: SiGoHex,
label: "Go",
},
{
icon: SiLaravel,
hexColor: SiLaravelHex,
label: "Laravel",
},
{
icon: SiDotnet,
hexColor: SiDotnetHex,
label: ".NET",
},
{
icon: SiMongodb,
hexColor: SiMongodbHex,
Expand Down Expand Up @@ -251,6 +273,16 @@ export const OTHERS = [
hexColor: SiNetlifyHex,
label: "Netlify",
},
{
icon: SiRender,
hexColor: SiRenrenHex,
label: "Render",
},
{
icon: SiRailway,
hexColor: SiRailwayHex,
label: "Railway",
},
{
icon: SiGithubpages,
hexColor: SiGithubpagesHex,
Expand Down Expand Up @@ -279,6 +311,11 @@ export const OTHERS = [
];

export const TOOLS = [
{
icon: SiDocker,
hexColor: SiDockerHex,
label: "Docker",
},
{
icon: SiGooglecolab,
hexColor: SiGooglecolabHex,
Expand Down Expand Up @@ -319,4 +356,9 @@ export const TOOLS = [
hexColor: SiPostmanHex,
label: "Postman",
},
{
icon: SiSentry,
hexColor: SiSentryHex,
label: "Sentry",
},
];
20 changes: 14 additions & 6 deletions src/lib/hooks/useObserver.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,27 @@ import { RefObject, useEffect, useState } from "react";
interface useObserverProps {
elementRef: RefObject<HTMLElement>;
threshold?: number;
root?: Element | Document | null;
rootMargin?: string;
}

const useObserver = ({ elementRef, threshold = 0.1 }: useObserverProps) => {
const useObserver = ({
elementRef,
threshold = 0.1,
root = null,
rootMargin = "0px",
}: useObserverProps) => {
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setIsVisible(entry.isIntersecting);
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect(); // Stop observing after it becomes visible
}
},
{ threshold: threshold }
{ threshold: threshold, root: root, rootMargin: rootMargin }
);

const currentRef = elementRef.current;
Expand All @@ -26,11 +36,9 @@ const useObserver = ({ elementRef, threshold = 0.1 }: useObserverProps) => {
if (currentRef) {
observer.unobserve(currentRef);
}
observer.disconnect();
};
}, [elementRef, threshold]);
}, [elementRef, threshold, root, rootMargin]);

return { isVisible };
};

export { useObserver };
16 changes: 8 additions & 8 deletions src/lib/hooks/useVisibility.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { useEffect, useState } from "react";
const useVisibility = () => {
const [isVisible, setIsVisible] = useState(true);

useEffect(() => {
const handleVisibilityChange = () => {
if (document.visibilityState === "hidden") {
setIsVisible(false);
} else {
setIsVisible(true);
}
};
const handleVisibilityChange = () => {
if (document.visibilityState === "hidden") {
setIsVisible(false);
} else {
setIsVisible(true);
}
};

useEffect(() => {
document.addEventListener("visibilitychange", handleVisibilityChange);

return () => {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/root/_components/contact/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ const Contact = () => {

<div
className={cn(
`flex-center flex-col gap-y-6 lg:gap-y-12 flex-1 w-full h-full
transition-opacity duration-500 ease-in-out`,
`flex-center flex-col gap-y-6 lg:gap-y-12 flex-1 w-full
h-full transition-opacity duration-500 ease-in-out`,
isVisible ? "opacity-100" : "opacity-0"
)}
>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/root/_components/education/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const Education = () => {
lineColor="hsl(var(--foreground) / 0.6)"
className={cn(
"mt-4 lg:mt-20 transition-opacity duration-500 ease-in-out",
isVisible ? "opacity-100" : "opacity-0"
isVisible ? "opacity-100 visible" : "opacity-0 invisible"
)}
>
{EDUCATIONS.map((e, i) => (
Expand Down
2 changes: 1 addition & 1 deletion src/pages/root/_components/experience/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const Experience = () => {
lineColor="hsl(var(--foreground) / 0.6)"
className={cn(
"mt-4 lg:mt-20 transition-opacity duration-500 ease-in-out",
isVisible ? "opacity-100" : "opacity-0"
isVisible ? "opacity-100 visible" : "opacity-0 invisible"
)}
>
{EXPERIENCES.map((e) => (
Expand Down
12 changes: 10 additions & 2 deletions src/pages/root/_components/skills/backend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useVisibility } from "@/lib/hooks/useVisibility";
import { BACKEND } from "@/constants/skills";
import { cn } from "@/lib/utils";

import { SkillIcon } from "./skill-icon";
import { SkillIcon } from "../../../../components/common/skill-icon";

const Backend = () => {
const { isVisible } = useVisibility();
Expand All @@ -15,13 +15,21 @@ const Backend = () => {
!isVisible && "paused"
)}
>
{BACKEND.concat(BACKEND).map((b, i) => (
{BACKEND.map((b, i) => (
<SkillIcon
key={`backend-${b.label}-${i}`}
Icon={b.icon}
hexColor={b.hexColor}
/>
))}
{BACKEND.map((b, i) => (
<SkillIcon
key={`backend-${b.label}-${i}`}
Icon={b.icon}
hexColor={b.hexColor}
ariaHidden="true"
/>
))}
</ul>
</div>
);
Expand Down
Loading

0 comments on commit daef59e

Please sign in to comment.