Skip to content

Commit

Permalink
rename directory to lowercase (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanSmiths committed Dec 1, 2024
1 parent 8772bfb commit b77535a
Show file tree
Hide file tree
Showing 23 changed files with 1,104 additions and 0 deletions.
153 changes: 153 additions & 0 deletions app/components/home/about/About.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"use client";

import { useGSAP } from "@gsap/react";
import { gsap } from "gsap";
import { useRef } from "react";
import { bebas_neue } from "../../../../utils/fonts";

type RowElement = {
label: string;
id: string;
};

const About = () => {
const containerRef = useRef<HTMLDivElement | null>(null);
const triggerRef = useRef<HTMLDivElement | null>(null);
const dotRef = useRef<HTMLDivElement | null>(null);

const firstRow: RowElement[] = [
{ label: "I", id: "I" },
{ label: "Expertly", id: "Expertly" },
{ label: "Blend", id: "Blend" },
];

const secondRow: RowElement[] = [
{ label: "My", id: "My" },
{ label: "Design", id: "Design" },
{ label: "Background", id: "Background" },
{ label: "With", id: "With" },
];

const thirdRow: RowElement[] = [
{ label: "My", id: "My-2" },
{ label: "Development", id: "Development" },
{ label: "Skills.", id: "Skills" },
];

gsap.registerPlugin(useGSAP);

useGSAP(
(): void => {
const secondTimeline = gsap.timeline({
scrollTrigger: {
trigger: triggerRef.current,
start: "top bottom",
end: "3500px",
scrub: true,
},
});

const timeline = gsap.timeline({
scrollTrigger: {
trigger: triggerRef.current,
start: "top top",
end: "2500px",
scrub: true,
pin: true,
},
});

secondTimeline.fromTo(
dotRef.current,
{
scale: 0,
translateY: -30,
},
{
scale: 350,
duration: 1,
translateY: 800,
},
);

const elementIDs: string[] = [
"#I",
"#Expertly",
"#Blend",
"#My",
"#Design",
"#Background",
"#With",
"#My-2",
"#Development",
"#Skills",
];

elementIDs.forEach((id: string): void => {
timeline.to(id, {
display: "block",
opacity: 1,
filter: "blur(0px)",
});
});

timeline.to(["#Development", "#Skills"], {
color: "#FF4D4D",
textShadow: "0px 0px 10px #FF4D4D",
});
},
{ scope: containerRef },
);

return (
<div
data-testid="homeAboutSection"
className="overflow-hidden pt-section"
ref={containerRef}
>
<div ref={triggerRef} className="relative grid h-screen w-full">
<div
ref={dotRef}
className="absolute left-1/2 h-2 w-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-dark dark:bg-light"
></div>
<div className="col-start-1 col-end-7 flex flex-col items-center justify-center gap-x-small gap-y-0 md:col-start-2 md:col-end-12">
<div className="flex flex-wrap items-center justify-center gap-x-small">
{firstRow.map(({ label, id }: RowElement, index: number) => (
<span
key={index}
id={id}
className={`${bebas_neue.className} hidden text-7xl text-light opacity-0 blur-2xl dark:text-dark md:text-8xl lg:text-[9rem]`}
>
{label}
</span>
))}
</div>
<div className="flex flex-wrap items-center justify-center gap-x-small">
{secondRow.map(({ label, id }: RowElement, index: number) => (
<span
key={index}
id={id}
className={`${bebas_neue.className} hidden text-7xl text-light opacity-0 blur-2xl dark:text-dark md:text-8xl lg:text-[9rem]`}
>
{label}
</span>
))}
</div>
<div className="flex flex-wrap items-center justify-center gap-x-small">
{thirdRow.map(({ label, id }: RowElement, index: number) => (
<span
key={index}
id={id}
className={`${bebas_neue.className} hidden text-7xl text-light opacity-0 blur-2xl dark:text-dark md:text-8xl lg:text-[9rem]`}
>
{label}
</span>
))}
</div>
</div>
</div>
</div>
);
};

export default About;
72 changes: 72 additions & 0 deletions app/components/home/blog/Blog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Link from "next/link";
import { FC } from "react";
import { getBlogPosts, Posts } from "../../../../utils/getPosts";

const Blog: FC = () => {
const posts: Posts[] = getBlogPosts();
return (
<div data-testid="homeBlogSection" className="my-medium grid">
<div className="col-span-full flex flex-col md:col-start-4 md:col-end-13">
{posts.map((post: Posts, index: number) => (
<div key={index}>
<div className="h-[1px] w-full bg-dark dark:bg-light"></div>
<Link
data-testid={`blogPostLink${index}`}
data-cy="blogPost"
className="group flex min-h-32 flex-col items-start justify-center gap-small transition hover:bg-dark dark:hover:bg-light md:flex-row md:items-center md:justify-between md:px-small"
href={`blog/${post?.slug}`}
>
<div className="flex flex-col items-start gap-small md:flex-row md:items-center md:gap-medium">
<span
data-cy="blogPostIndex"
className="hidden transition group-hover:text-light dark:group-hover:text-dark md:block"
>
0{index + 1}
</span>
<h3
data-cy="blogPostTitle"
className="text-left text-xl font-semibold transition group-hover:text-light dark:group-hover:text-dark md:text-3xl"
>
{post?.metadata.title}
</h3>
</div>
<div className="flex min-w-fit flex-row gap-smallest md:ml-regular md:flex-col md:text-right">
<span
data-cy="blogPostCategory"
className="transition group-hover:text-light dark:group-hover:text-dark"
>
{post?.metadata.category}
</span>
<time
data-cy="blogPostDate"
className="transition group-hover:text-light dark:group-hover:text-dark"
dateTime={post?.metadata.date}
>
{post?.metadata.date
? new Date(post?.metadata.date).toLocaleDateString(
"en-us",
{
year: "numeric",
month: "short",
day: "numeric",
},
)
: "Date not available"}
</time>
<span
data-cy="blogPostTime"
className="transition group-hover:text-light dark:group-hover:text-dark"
>
{post?.metadata.time} min to read
</span>
</div>
</Link>
<div className="h-[1px] w-full bg-dark dark:bg-light"></div>
</div>
))}
</div>
</div>
);
};

export default Blog;
75 changes: 75 additions & 0 deletions app/components/home/expertise/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use client";

import React, { useEffect, useRef, useState } from "react";
import { gsap } from "gsap";

interface AnimatedAccordionProps {
title: string;
content: string;
rotation: number;
left: number;
top: number;
}

export default function AnimatedAccordion({
title,
content,
top,
left,
rotation,
}: AnimatedAccordionProps) {
const [isOpen, setIsOpen] = useState<boolean>(false);
const accordionRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const accordionTimeline = useRef<GSAPTimeline | null>(null);

useEffect((): void => {
if (accordionRef.current && contentRef.current) {
accordionTimeline.current = gsap.timeline({ paused: true });
accordionTimeline.current
.to(accordionRef.current, { rotationZ: 0, duration: 0.5 })
.to(accordionRef.current, { width: "400px", duration: 0.5 }, "-=0.25")
.to(contentRef.current, {
height: "auto",
duration: 0.5,
});
}
}, []);

const toggleAccordion = (): void => {
if (!accordionTimeline.current) return;
if (isOpen) {
accordionTimeline.current.reverse();
} else {
accordionTimeline.current.play();
}
setIsOpen(!isOpen);
};

return (
<div
style={{
transform: `rotate(${rotation}deg)`,
left: `${left}%`,
top: `${top}%`,
}}
onClick={toggleAccordion}
ref={accordionRef}
className="group absolute flex w-[200px] origin-top cursor-pointer flex-col rounded-lg border border-dark bg-light p-4 shadow-lg transition hover:border-light hover:bg-dark dark:border-light dark:bg-dark dark:hover:border-dark dark:hover:bg-light"
>
<div className="flex origin-top items-center justify-between">
<h2 className="text-xl font-bold transition group-hover:text-light dark:group-hover:text-dark">
{title}
</h2>
<span className="text-2xl transition group-hover:text-light dark:group-hover:text-dark">
{isOpen ? "-" : "+"}
</span>
</div>
<div className="h-0 overflow-hidden" ref={contentRef}>
<p className="transition group-hover:text-light dark:group-hover:text-dark">
{content}
</p>
</div>
</div>
);
}
42 changes: 42 additions & 0 deletions app/components/home/expertise/AccordionItems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export type AccordionItem = {
title: string;
rotation: number;
left: number;
top: number;
content: string;
};

export const accordionItems: AccordionItem[] = [
{
title: "Frontend",
rotation: 10,
left: 20,
top: 30,
content:
"Mission type safety. I’ve crafted 20+ data-driven features using Angular, RxJS, and TypeScript, for one of the biggest banks in the world. Mentored colleagues on Next.js helped cut development time by 30%. I’m all about creating dynamic, fast, and user-centric experiences.",
},
{
title: "Backend",
rotation: -6,
left: 40,
top: 60,
content:
"Using PostgreSQL, Prisma, and Nest.js, I build solid backend systems that deliver. My work ensures data reliability, scalability, and the backend power to drive front-end engagement.",
},
{
title: "Design",
rotation: 8,
left: 55,
top: 35,
content:
"Armed with Figma and a knack for UX, I’ve designed 20+ websites that slaps. Interviewing users, my insights led to a checkout flow overhaul, slashing cart abandonment by 15%. Plus, I secured major contracts with innovative, user-centered designs that deliver both style and substance.",
},
{
title: "Testing",
rotation: -8,
left: 70,
top: 55,
content:
"Fear no more deploying on a friday. With Cypress, Playwright, and solid GitHub CI/CD setup knowledge, I ensure flawless and stable releases. My 40+ automated tests across the stack for TD Cowen cut post-launch issues by 30%, making every deployment solid, reliable, and user-ready.",
},
];
37 changes: 37 additions & 0 deletions app/components/home/expertise/Expertise.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import { useRef } from "react";
import AnimatedAccordion from "./Accordion";
import { AccordionItem, accordionItems } from "./AccordionItems";
import useHorizontalScroll from "../../../../utils/hooks/useHorizontalScroll";
import { bebas_neue } from "../../../../utils/fonts";

const Expertise = () => {
const containerRef = useRef<HTMLDivElement | null>(null);
const triggerRef = useRef<HTMLDivElement | null>(null);

useHorizontalScroll(containerRef, triggerRef);

return (
<div>
<div ref={triggerRef} className="h-screen w-full overflow-hidden">
<div
ref={containerRef}
className="relative flex h-full w-fit items-center"
>
{accordionItems.map((item: AccordionItem, index: number) => (
<AnimatedAccordion key={index} {...item} />
))}
<h2
aria-label="Expertises section"
className={`${bebas_neue.className} cursor-default pt-14 text-[40rem] leading-[28rem] md:pt-32 md:text-[60rem] md:leading-[43rem]`}
>
Expertises
</h2>
</div>
</div>
</div>
);
};

export default Expertise;
15 changes: 15 additions & 0 deletions app/components/home/hero/CityInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FC } from "react";
import Time from "./Time";
import FetchWeather from "./Weather/FetchWeather";

const CityInfo: FC = () => {
return (
<div className="col-span-full ml-small mt-small flex h-fit flex-wrap items-center gap-x-4 pb-small md:mb-small md:gap-x-10 md:pb-0">
<span className="text-sm sm:text-lg">Wiesbaden (DE)</span>
<FetchWeather />
<Time />
</div>
);
};

export default CityInfo;
Loading

0 comments on commit b77535a

Please sign in to comment.