216 lines
6.6 KiB
TypeScript
216 lines
6.6 KiB
TypeScript
"use client";
|
||
|
||
import { motion } from "framer-motion";
|
||
|
||
import { Badge } from "@/components/ui/badge";
|
||
|
||
interface Project6aProps {
|
||
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 ProjectRow = ({ title, category, src, alt, index }: Project6aProps) => (
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
whileInView={{ opacity: 1 }}
|
||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||
viewport={{ once: true }}
|
||
className="group flex cursor-pointer items-center gap-4 rounded-lg p-3 text-start transition-all duration-300 hover:bg-muted/20"
|
||
>
|
||
<div className="relative h-16 w-16 shrink-0 overflow-hidden rounded-lg">
|
||
<motion.img
|
||
src={src}
|
||
alt={alt}
|
||
className="h-full w-full object-cover"
|
||
whileHover={{ scale: 1.1 }}
|
||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||
/>
|
||
<div className="absolute inset-0 bg-muted/40 transition-all duration-300 group-hover:bg-muted/20" />
|
||
</div>
|
||
<div className="flex-1 space-y-1">
|
||
<h4 className="text-lg leading-tight font-semibold text-foreground">
|
||
{title}
|
||
</h4>
|
||
<Badge
|
||
variant="secondary"
|
||
className="h-6 bg-muted text-xs font-medium text-foreground"
|
||
>
|
||
{category}
|
||
</Badge>
|
||
</div>
|
||
</motion.div>
|
||
);
|
||
|
||
export const Project6a = ({
|
||
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<Project6aProps> & {
|
||
images?: Array<{ src?: string; alt: string }>;
|
||
}) => {
|
||
const projectRows = [
|
||
{
|
||
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="mx-auto max-w-4xl text-start">
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 30 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ duration: 0.6 }}
|
||
className="mb-16 space-y-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>
|
||
|
||
<div className="mb-16 space-y-10">
|
||
{images?.map((img, index) => (
|
||
<ImageBlock
|
||
key={index}
|
||
src={img.src}
|
||
alt={img.alt}
|
||
index={index}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
transition={{ duration: 0.6 }}
|
||
viewport={{ once: true }}
|
||
className="mb-16 space-y-6"
|
||
>
|
||
<h3 className="text-sm font-medium tracking-wider text-foreground">
|
||
درباره
|
||
</h3>
|
||
<p className="max-w-2xl text-sm leading-relaxed tracking-wide text-muted-foreground">
|
||
{description}
|
||
</p>
|
||
</motion.div>
|
||
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
transition={{ duration: 0.6 }}
|
||
viewport={{ once: true }}
|
||
className="space-y-6"
|
||
>
|
||
<h3 className="text-sm font-bold tracking-wider text-foreground">
|
||
مجموعههای بیشتر
|
||
</h3>
|
||
<div className="space-y-2">
|
||
{projectRows.map((project, index) => (
|
||
<ProjectRow key={project.title} {...project} index={index} />
|
||
))}
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
};
|