Files
reui-blocks/blocks/project6.tsx
T

231 lines
6.7 KiB
TypeScript
Raw Normal View History

"use client";
import { motion } from "framer-motion";
import { Badge } from "@/components/ui/badge";
interface Project6Props {
title?: string;
subtitle?: string;
category?: string;
year?: string;
description?: string;
images?: Array<{ src?: string; alt: string }>;
src: string;
alt: string;
index: number;
}
const ImageBlock = ({
src,
alt,
index,
}: {
src?: string;
alt: string;
index: number;
}) => (
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }}
viewport={{ once: true, margin: "-100px" }}
className="group relative mb-8 last:mb-0"
>
<div className="relative aspect-[4/5] overflow-hidden rounded-2xl bg-muted">
{src ? (
<motion.img
src={src}
alt={alt}
className="h-full w-full object-cover"
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.3, ease: "easeOut" }}
/>
) : (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
<span className="text-sm">جایگاه تصویر</span>
</div>
)}
<div className="absolute inset-0 bg-gradient-to-t from-muted/50 via-transparent to-transparent" />
<motion.div
className="absolute inset-0 bg-muted/20"
initial={{ opacity: 0 }}
whileHover={{ opacity: 1 }}
transition={{ duration: 0.3 }}
/>
</div>
</motion.div>
);
const StickySection = ({
title,
subtitle,
category,
year,
description,
}: {
title: string;
subtitle: string;
category: string;
year: string;
description: string;
}) => (
<div className="top-20 flex flex-col self-start text-start lg:sticky lg:min-h-screen lg:justify-between">
<motion.div
initial={{ opacity: 0, x: 30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
>
<div className="space-y-3">
<h1 className="text-5xl font-bold tracking-tight text-foreground md:text-6xl">
{title}
</h1>
<p className="text-xl text-muted-foreground">{subtitle}</p>
<div className="flex items-center gap-4">
<Badge
variant="secondary"
className="rounded-3xl bg-muted px-4 py-1 text-xs font-medium text-foreground"
>
{category}
</Badge>
<span className="rounded-3xl border border-border px-3 text-sm text-muted-foreground">
{year}
</span>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="mt-5 space-y-3 md:sticky md:bottom-9"
>
<h3 className="text-sm font-medium tracking-wider text-foreground">
درباره
</h3>
<p className="max-w-sm text-sm leading-relaxed tracking-wide text-muted-foreground">
{description}
</p>
</motion.div>
</div>
);
const ProjectCard = ({ title, category, src, alt, index }: Project6Props) => (
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
viewport={{ once: true }}
className="group relative cursor-pointer"
>
<div className="relative aspect-[4/3] overflow-hidden rounded-2xl">
<motion.img
src={src}
alt={alt}
className="h-full w-full object-cover"
whileHover={{ scale: 1.1 }}
transition={{ duration: 0.4, ease: "easeOut" }}
/>
<div className="absolute inset-0 bg-muted/40 transition-all duration-300 group-hover:bg-muted/20" />
<div className="absolute inset-0 flex flex-col justify-between p-6 text-start">
<Badge
variant="secondary"
className="self-start border border-muted bg-background text-xs font-medium text-foreground"
>
{category}
</Badge>
<h3 className="text-2xl font-semibold text-foreground opacity-70">
{title}
</h3>
</div>
</div>
</motion.div>
);
export const Project6 = ({
title = "نمای شهری",
subtitle = "مجموعه نماهای موبایل",
category = "طراحی",
year = "۲۲۵",
description = "مجموعه‌ای جذاب از نماهای رابط که جوهره تجربه کاربری مدرن را از دریچه طراحان معاصر با تم تک‌رنگ به تصویر می‌کشد.",
images = [
{
src: "/block-media/shadcnblocks/block/lummi/bw1.jpeg",
alt: "نمای رابط ۱",
},
{
src: "/block-media/shadcnblocks/block/lummi/bw2.jpeg",
alt: "نمای رابط ۲",
},
{
src: "/block-media/shadcnblocks/block/lummi/bw3.jpeg",
alt: "نمای رابط ۳",
},
{
src: "/block-media/shadcnblocks/block/lummi/bw4.jpeg",
alt: "نمای رابط ۴",
},
],
}: Partial<ReturnType<typeof StickySection>["props"]> & {
images?: Array<{ src?: string; alt: string }>;
}) => {
const projectCards = [
{
title: "محتوایی",
category: "موبایل",
src: "/block-media/shadcnblocks/block/lummi/bw12.jpeg",
alt: "نمای موبایل",
},
{
title: "معماری وب",
category: "وب",
src: "/block-media/shadcnblocks/block/lummi/bw8.jpeg",
alt: "نمای محصول",
},
{
title: "معماری فرانت‌اند",
category: "سبک زندگی",
src: "/block-media/shadcnblocks/block/lummi/bw9.jpeg",
alt: "محتوای لایف‌استایل",
},
];
return (
<section dir="rtl" lang="fa" className="py-32">
<div className="container">
<div className="grid gap-12 lg:grid-cols-2 lg:gap-20">
<StickySection
title={title!}
subtitle={subtitle!}
category={category!}
year={year!}
description={description!}
/>
<div className="space-y-10">
{images?.map((img, index) => (
<ImageBlock
key={index}
src={img.src}
alt={img.alt}
index={index}
/>
))}
</div>
</div>
<div className="mt-32 text-start">
<h3 className="mb-6 text-sm font-bold font-medium tracking-wider text-foreground">
مجموعه‌های بیشتر
</h3>
<div className="grid gap-6 pb-16 md:grid-cols-3">
{projectCards.map((project, index) => (
<ProjectCard key={project.title} {...project} index={index} />
))}
</div>
</div>
</div>
</section>
);
};