feat(ui): add staggered entry animations using framer-motion and fix typography nesting
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Badge, Card, Typography } from "poyraz-ui/atoms";
|
import { Badge, Card, Typography } from "poyraz-ui/atoms";
|
||||||
import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "poyraz-ui/molecules";
|
import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "poyraz-ui/molecules";
|
||||||
|
import { StaggerContainer, StaggerItem } from "@/components/motion-wrapper";
|
||||||
import { BOOKMARKS } from "@/data/bookmarks";
|
import { BOOKMARKS } from "@/data/bookmarks";
|
||||||
import { certificates } from "@/data/certificates";
|
import { certificates } from "@/data/certificates";
|
||||||
import { EDUCATION } from "@/data/education";
|
import { EDUCATION } from "@/data/education";
|
||||||
@@ -25,21 +26,23 @@ export function AboutContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="grid gap-2">
|
<StaggerContainer className="grid gap-2">
|
||||||
{EDUCATION.map((item) => (
|
{EDUCATION.map((item) => (
|
||||||
<Card key={item.id} className="rounded-sm border-border p-3">
|
<StaggerItem key={item.id}>
|
||||||
<Typography variant="large" className="text-base leading-tight">
|
<Card className="rounded-sm border-border p-3">
|
||||||
{item.title}
|
<Typography variant="large" className="text-base leading-tight">
|
||||||
</Typography>
|
{item.title}
|
||||||
<Typography variant="small" className="mt-0.5 text-muted-foreground">
|
</Typography>
|
||||||
{item.institution}
|
<Typography variant="small" className="mt-0.5 text-muted-foreground">
|
||||||
</Typography>
|
{item.institution}
|
||||||
<Typography variant="small" className="mt-1 text-red-600">
|
</Typography>
|
||||||
{item.period}
|
<Typography variant="small" className="mt-1 text-red-600">
|
||||||
</Typography>
|
{item.period}
|
||||||
</Card>
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
</StaggerItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</StaggerContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -106,21 +109,23 @@ export function AboutContent() {
|
|||||||
</Sheet>
|
</Sheet>
|
||||||
|
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<div className="grid gap-2">
|
<StaggerContainer className="grid gap-2">
|
||||||
{EXPERIENCE.map((item) => (
|
{EXPERIENCE.map((item) => (
|
||||||
<Card key={item.id} className="rounded-sm border-border p-3">
|
<StaggerItem key={item.id}>
|
||||||
<Typography variant="large" className="text-base leading-tight">
|
<Card className="rounded-sm border-border p-3">
|
||||||
{item.role}
|
<Typography variant="large" className="text-base leading-tight">
|
||||||
</Typography>
|
{item.role}
|
||||||
<Typography variant="small" className="mt-0.5 text-muted-foreground">
|
</Typography>
|
||||||
{item.company}
|
<Typography variant="small" className="mt-0.5 text-muted-foreground">
|
||||||
</Typography>
|
{item.company}
|
||||||
<Typography variant="small" className="mt-1 text-red-600">
|
</Typography>
|
||||||
{item.period}
|
<Typography variant="small" className="mt-1 text-red-600">
|
||||||
</Typography>
|
{item.period}
|
||||||
</Card>
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
</StaggerItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</StaggerContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+18
-14
@@ -1,3 +1,5 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Badge, Card, Typography } from "poyraz-ui/atoms";
|
import { Badge, Card, Typography } from "poyraz-ui/atoms";
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +12,7 @@ import {
|
|||||||
PaginationNext,
|
PaginationNext,
|
||||||
PaginationPrevious,
|
PaginationPrevious,
|
||||||
} from "poyraz-ui/molecules";
|
} from "poyraz-ui/molecules";
|
||||||
|
import { StaggerContainer, StaggerItem } from "@/components/motion-wrapper";
|
||||||
import type { BlogPageData } from "@/data/blog";
|
import type { BlogPageData } from "@/data/blog";
|
||||||
|
|
||||||
type BlogContentProps = {
|
type BlogContentProps = {
|
||||||
@@ -118,22 +121,23 @@ export function BlogContent({ data }: BlogContentProps) {
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{hasArticles ? (
|
{hasArticles ? (
|
||||||
<div className="grid gap-3 md:grid-cols-3">
|
<StaggerContainer className="grid gap-3 md:grid-cols-3">
|
||||||
{data.articles.map((post) => (
|
{data.articles.map((post) => (
|
||||||
<ArticleCard
|
<StaggerItem key={post.id}>
|
||||||
key={post.id}
|
<ArticleCard
|
||||||
image={post.image}
|
image={post.image}
|
||||||
category={post.category}
|
category={post.category}
|
||||||
title={post.title}
|
title={post.title}
|
||||||
excerpt={post.excerpt}
|
excerpt={post.excerpt}
|
||||||
date={post.date}
|
date={post.date}
|
||||||
readTime={post.readTime}
|
readTime={post.readTime}
|
||||||
href={post.href}
|
href={post.href}
|
||||||
className="rounded-sm border-border [&_h3]:line-clamp-2 [&_h3]:min-h-[2.5rem]"
|
className="rounded-sm border-border [&_h3]:line-clamp-2 [&_h3]:min-h-[2.5rem]"
|
||||||
author={{ name: post.author, avatar: "/logo/logo.jpeg" }}
|
author={{ name: post.author, avatar: "/logo/logo.jpeg" }}
|
||||||
/>
|
/>
|
||||||
|
</StaggerItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</StaggerContainer>
|
||||||
) : (
|
) : (
|
||||||
<Card className="rounded-sm border-border p-5">
|
<Card className="rounded-sm border-border p-5">
|
||||||
<Typography variant="p" className="text-muted-foreground">
|
<Typography variant="p" className="text-muted-foreground">
|
||||||
|
|||||||
+43
-43
@@ -4,6 +4,7 @@ import Image from "next/image";
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Card, Typography } from "poyraz-ui/atoms";
|
import { Card, Typography } from "poyraz-ui/atoms";
|
||||||
import { NewsCard } from "poyraz-ui/molecules";
|
import { NewsCard } from "poyraz-ui/molecules";
|
||||||
|
import { StaggerContainer, StaggerItem, FadeIn } from "@/components/motion-wrapper";
|
||||||
|
|
||||||
type HomeHeroProps = {
|
type HomeHeroProps = {
|
||||||
news: {
|
news: {
|
||||||
@@ -38,52 +39,51 @@ export function HomeHero({ news }: HomeHeroProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="grid gap-3 md:h-65 md:grid-cols-2">
|
<section className="grid gap-3 md:h-65 md:grid-cols-2">
|
||||||
<div className="grid gap-2 md:grid-rows-3">
|
<StaggerContainer className="grid gap-2 md:grid-rows-3">
|
||||||
{news.map((item) => (
|
{news.map((item) => (
|
||||||
<NewsCard
|
<StaggerItem key={item.id}>
|
||||||
key={item.id}
|
<NewsCard
|
||||||
className="rounded-sm border-border md:h-full"
|
className="rounded-sm border-border md:h-full"
|
||||||
category={item.category}
|
category={item.category}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
date={item.date}
|
date={item.date}
|
||||||
image={item.image}
|
image={item.image}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card className="grid grid-cols-[112px_1fr] items-stretch gap-2 rounded-sm border-border sm:grid-cols-[168px_1fr] md:h-full">
|
|
||||||
<div className="flex flex-col justify-center gap-1 px-3 py-2">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Typography variant="h2" className="leading-tight">
|
|
||||||
Poyraz
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h2" secondaryFont className="leading-tight text-red-600">
|
|
||||||
{" "}
|
|
||||||
Avsever
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
<Typography variant="small" className="text-muted-foreground">
|
|
||||||
Teknolojiyi merak eden bir genç. Yazılım geliştirme, yapay zeka ve teknoloji dünyasındaki gelişmeleri takip ediyor.
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative h-full overflow-hidden">
|
|
||||||
<div
|
|
||||||
className="relative h-full w-full"
|
|
||||||
onMouseEnter={handleMouseEnter}
|
|
||||||
onMouseLeave={handleMouseLeave}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={frame === 1 ? "/images/hero1.png" : "/images/hero2.png"}
|
|
||||||
alt="Poyraz Avsever"
|
|
||||||
width={240}
|
|
||||||
height={240}
|
|
||||||
className="absolute right-0 bottom-0 h-auto w-20 object-contain md:w-48"
|
|
||||||
/>
|
/>
|
||||||
|
</StaggerItem>
|
||||||
|
))}
|
||||||
|
</StaggerContainer>
|
||||||
|
|
||||||
|
<FadeIn delay={0.15}>
|
||||||
|
<Card className="grid grid-cols-[112px_1fr] items-stretch gap-2 rounded-sm border-border sm:grid-cols-[168px_1fr] md:h-full">
|
||||||
|
<div className="flex flex-col justify-center gap-1 px-3 py-2">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Typography variant="h2" className="leading-tight">
|
||||||
|
Poyraz <span className="font-secondary text-red-600">Avsever</span>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<Typography variant="small" className="text-muted-foreground">
|
||||||
|
Teknolojiyi merak eden bir genç. Yazılım geliştirme, yapay zeka ve teknoloji dünyasındaki gelişmeleri takip ediyor.
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Card>
|
<div className="relative h-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="relative h-full w-full"
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={frame === 1 ? "/images/hero1.png" : "/images/hero2.png"}
|
||||||
|
alt="Poyraz Avsever"
|
||||||
|
width={240}
|
||||||
|
height={240}
|
||||||
|
className="absolute right-0 bottom-0 h-auto w-20 object-contain md:w-48"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
type FadeInProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
delay?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FadeIn({ children, className, delay = 0 }: FadeInProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 12 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.35, delay, ease: "easeOut" }}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type StaggerContainerProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
stagger?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function StaggerContainer({ children, className, stagger = 0.06 }: StaggerContainerProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
variants={{
|
||||||
|
hidden: {},
|
||||||
|
visible: { transition: { staggerChildren: stagger } },
|
||||||
|
}}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type StaggerItemProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function StaggerItem({ children, className }: StaggerItemProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
variants={{
|
||||||
|
hidden: { opacity: 0, y: 12 },
|
||||||
|
visible: { opacity: 1, y: 0, transition: { duration: 0.35, ease: "easeOut" } },
|
||||||
|
}}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import Link from "next/link";
|
|||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
import { Badge, Card, Typography } from "poyraz-ui/atoms";
|
import { Badge, Card, Typography } from "poyraz-ui/atoms";
|
||||||
import { ImageCard } from "poyraz-ui/molecules";
|
import { ImageCard } from "poyraz-ui/molecules";
|
||||||
|
import { StaggerContainer, StaggerItem } from "@/components/motion-wrapper";
|
||||||
import { FIGMA_TEMPLATES, MOBILE_APPS, WEB_APPS, type ProjectItem } from "@/data/projects";
|
import { FIGMA_TEMPLATES, MOBILE_APPS, WEB_APPS, type ProjectItem } from "@/data/projects";
|
||||||
import { getGithubRepos, getNpmPackages } from "@/lib/project-feeds";
|
import { getGithubRepos, getNpmPackages } from "@/lib/project-feeds";
|
||||||
|
|
||||||
@@ -43,19 +44,20 @@ function ProjectSection({ title, items }: { title: string; items: ProjectItem[]
|
|||||||
<Typography variant="large" className="text-base">
|
<Typography variant="large" className="text-base">
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
|
<StaggerContainer className="grid grid-cols-2 gap-2 lg:grid-cols-4">
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<ImageCard
|
<StaggerItem key={item.id}>
|
||||||
key={item.id}
|
<ImageCard
|
||||||
image={item.image}
|
image={item.image}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
description={item.description}
|
description={item.description}
|
||||||
badge={item.badge}
|
badge={item.badge}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
className="aspect-square rounded-sm border-border"
|
className="aspect-square rounded-sm border-border"
|
||||||
/>
|
/>
|
||||||
|
</StaggerItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</StaggerContainer>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user