95 lines
3.9 KiB
TypeScript
95 lines
3.9 KiB
TypeScript
import React, { useRef } from 'react'
|
|
import { motion, useScroll, useTransform, useInView } from 'framer-motion'
|
|
import { useSite, type TimelineItem } from '../../api'
|
|
import { SectionHeading } from '../SectionHeading'
|
|
import { AddItemButton, EditableItem } from '../../admin/Editable'
|
|
|
|
const ease = [0.23, 1, 0.32, 1] as const
|
|
|
|
type Milestone = TimelineItem
|
|
|
|
function Item({ item, index }: { item: Milestone; index: number }) {
|
|
const ref = useRef<HTMLLIElement>(null)
|
|
const active = useInView(ref, { once: true, amount: 0.6 })
|
|
const onRight = index % 2 === 0
|
|
|
|
return (
|
|
<li ref={ref} className="relative md:grid md:grid-cols-2 md:gap-16">
|
|
<span
|
|
aria-hidden="true"
|
|
className={`absolute right-[-5px] top-8 z-10 h-3 w-3 rounded-full transition-all duration-500 ease-lux md:right-1/2 md:translate-x-1/2 ${
|
|
active
|
|
? 'bg-copper shadow-[0_0_15px_rgba(197,143,92,0.6),0_0_0_6px_rgba(213,135,76,0.15)]'
|
|
: 'bg-ivory/20'
|
|
}`}
|
|
/>
|
|
|
|
<motion.div
|
|
className={`pr-8 md:pr-0 ${
|
|
onRight ? 'md:col-start-1 md:pl-16 md:text-left' : 'md:col-start-2 md:pr-16'
|
|
}`}
|
|
initial={{ opacity: 0, y: 24 }}
|
|
animate={active ? { opacity: 1, y: 0 } : { opacity: 0, y: 24 }}
|
|
transition={{ duration: 0.55, ease }}
|
|
>
|
|
<EditableItem resource="timeline" item={item}>
|
|
<div className="group relative overflow-hidden rounded-2xl border border-ivory/10 bg-ink/40 p-6 backdrop-blur-md transition-all duration-500 ease-lux hover:-translate-y-1.5 hover:border-copper/50 hover:bg-ink/60 hover:shadow-[0_12px_40px_-10px_rgba(197,143,92,0.35)] md:p-8">
|
|
{/* لایه نوری ظریف که در هاور روشن میشود */}
|
|
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.03] to-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
|
|
|
|
<span className="relative z-10 text-sm font-medium tracking-widest text-copper">{item.year}</span>
|
|
<h3 className="relative z-10 mt-3 text-xl font-bold text-ivory transition-colors duration-300 group-hover:text-white">{item.title}</h3>
|
|
<p className="relative z-10 mt-3 text-sm font-light leading-relaxed text-ivory/60 transition-colors duration-300 group-hover:text-ivory/80">{item.desc}</p>
|
|
</div>
|
|
</EditableItem>
|
|
</motion.div>
|
|
</li>
|
|
)
|
|
}
|
|
|
|
export function Timeline() {
|
|
const { timeline } = useSite()
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const { scrollYProgress } = useScroll({
|
|
target: ref,
|
|
offset: ['start 70%', 'end 60%'],
|
|
})
|
|
const scaleY = useTransform(scrollYProgress, [0, 1], [0, 1])
|
|
|
|
return (
|
|
<section className="bg-char py-24 md:py-32" aria-labelledby="timeline-title">
|
|
<div className="mx-auto w-full max-w-site px-6 lg:px-10">
|
|
|
|
<div className="mb-16">
|
|
<SectionHeading
|
|
title="مسیر ما"
|
|
lead="از یک دفتر چهارنفره تا ۴۷ پروژه. خط زمان پروژههای ما را ببینید."
|
|
headingKey="timeline"
|
|
align="center"
|
|
/>
|
|
</div>
|
|
|
|
<div ref={ref} className="relative mx-auto mt-20 max-w-4xl">
|
|
<span
|
|
aria-hidden="true"
|
|
className="absolute inset-y-0 right-0 w-px bg-ivory/10 md:right-1/2"
|
|
/>
|
|
<motion.span
|
|
aria-hidden="true"
|
|
className="absolute inset-y-0 right-0 w-px origin-top bg-gradient-to-b from-copper/80 via-copper to-copper/20 shadow-[0_0_10px_rgba(197,143,92,0.5)] md:right-1/2"
|
|
style={{ scaleY }}
|
|
/>
|
|
|
|
<ol className="flex flex-col gap-12 md:gap-16">
|
|
{timeline.map((item, i) => (
|
|
<Item key={item.id} item={item} index={i} />
|
|
))}
|
|
</ol>
|
|
<div className="mt-12">
|
|
<AddItemButton resource="timeline" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
} |