Files
aramesh-backend/frontend/src/components/home/BeforeAfter.tsx
T

126 lines
4.9 KiB
TypeScript

import React, { useCallback, useEffect, useRef, useState } from 'react'
import { MoveHorizontalIcon } from 'lucide-react'
import { Reveal } from '../Reveal'
import { SectionHeading } from '../SectionHeading'
import { useSite } from '../../api'
import { SingletonPill } from '../../admin/Editable'
export function BeforeAfter() {
const { before_after } = useSite()
const containerRef = useRef<HTMLDivElement>(null)
const [position, setPosition] = useState(52)
const dragging = useRef(false)
const setFromClientX = useCallback((clientX: number) => {
const el = containerRef.current
if (!el) return
const rect = el.getBoundingClientRect()
const ratio = ((clientX - rect.left) / rect.width) * 100
setPosition(Math.min(98, Math.max(2, ratio)))
}, [])
useEffect(() => {
const move = (e: MouseEvent | TouchEvent) => {
if (!dragging.current) return
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
setFromClientX(clientX)
}
const stop = () => {
dragging.current = false
}
window.addEventListener('mousemove', move)
window.addEventListener('touchmove', move)
window.addEventListener('mouseup', stop)
window.addEventListener('touchend', stop)
return () => {
window.removeEventListener('mousemove', move)
window.removeEventListener('touchmove', move)
window.removeEventListener('mouseup', stop)
window.removeEventListener('touchend', stop)
}
}, [setFromClientX])
const onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'ArrowLeft') setPosition((p) => Math.max(2, p - 4))
if (e.key === 'ArrowRight') setPosition((p) => Math.min(98, p + 4))
}
return (
<section className="bg-char py-24 lg:py-32" aria-labelledby="ba-title">
<div className="mx-auto max-w-site px-6 lg:px-10">
<SectionHeading
title={before_after.title}
lead={before_after.lead}
align="center"
/>
<div className="flex justify-start">
<SingletonPill resource="before_after" label="ویرایش تصاویر و برچسب‌های قبل/بعد" />
</div>
<Reveal delay={0.1}>
<div
ref={containerRef}
className="relative mx-auto mt-14 aspect-[4/3] w-full max-w-4xl select-none overflow-hidden rounded-xl border border-ivory/10 shadow-2xl md:aspect-[16/10]"
onMouseDown={(e) => {
dragging.current = true
setFromClientX(e.clientX)
}}
onTouchStart={(e) => {
dragging.current = true
setFromClientX(e.touches[0].clientX)
}}
>
<img
src={before_after.before}
alt={before_after.before_label}
className="absolute inset-0 h-full w-full object-cover object-center"
draggable={false}
/>
<div className="absolute inset-0 overflow-hidden" style={{ clipPath: `inset(0 0 0 ${position}%)` }} aria-hidden="true">
<img
src={before_after.after}
alt=""
className="absolute inset-0 h-full w-full object-cover object-center"
draggable={false}
/>
</div>
<span className="pointer-events-none absolute left-6 top-6 z-30 rounded-md bg-ink/85 px-4 py-2 text-xs font-light text-ivory backdrop-blur-md border border-ivory/10 shadow-lg">
{before_after.before_label}
</span>
<span className="pointer-events-none absolute right-6 top-6 z-30 rounded-md bg-ink/85 px-4 py-2 text-xs font-light text-copper backdrop-blur-md border border-copper/30 shadow-lg">
{before_after.after_label}
</span>
<div className="pointer-events-none absolute inset-y-0 z-30" style={{ left: `${position}%` }}>
<div className="h-full w-[2px] bg-copper shadow-[0_0_12px_rgba(213,135,76,0.9)]" />
</div>
<button
type="button"
role="slider"
aria-label="مقایسه کانسپت و پروژه ساخته‌شده"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={Math.round(position)}
onKeyDown={onKeyDown}
onMouseDown={() => {
dragging.current = true
}}
onTouchStart={() => {
dragging.current = true
}}
className="absolute top-1/2 z-40 flex h-14 w-14 -translate-x-1/2 -translate-y-1/2 cursor-ew-resize items-center justify-center rounded-full bg-copper text-char shadow-2xl transition-transform duration-200 ease-lux hover:scale-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-ivory"
style={{ left: `${position}%` }}
>
<MoveHorizontalIcon className="h-5 w-5" />
</button>
</div>
</Reveal>
</div>
</section>
)
}