Files
reui-blocks/blocks/project6.tsx
T

231 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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>
);
};