122 lines
4.6 KiB
TypeScript
122 lines
4.6 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useRef, useEffect, MouseEvent, TouchEvent } from 'react';
|
|
import Image from 'next/image';
|
|
import { projectsData } from '@/content/projects';
|
|
import { GripVertical } from 'lucide-react';
|
|
|
|
export const BeforeAfterSlider = () => {
|
|
const [sliderPosition, setSliderPosition] = useState(50);
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Pobierz pierwszy projekt z kategorii remonty, który ma beforeAfter
|
|
const project = projectsData.find(p => p.category === 'remonty' && p.beforeAfter);
|
|
|
|
if (!project || !project.beforeAfter) return null;
|
|
|
|
const handleMove = (clientX: number) => {
|
|
if (!containerRef.current) return;
|
|
|
|
const rect = containerRef.current.getBoundingClientRect();
|
|
const x = Math.max(0, Math.min(clientX - rect.left, rect.width));
|
|
const percentage = (x / rect.width) * 100;
|
|
setSliderPosition(percentage);
|
|
};
|
|
|
|
const onMouseMove = (e: globalThis.MouseEvent) => {
|
|
if (!isDragging) return;
|
|
handleMove(e.clientX);
|
|
};
|
|
|
|
const onTouchMove = (e: globalThis.TouchEvent) => {
|
|
if (!isDragging) return;
|
|
handleMove(e.touches[0].clientX);
|
|
};
|
|
|
|
const stopDragging = () => {
|
|
setIsDragging(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (isDragging) {
|
|
window.addEventListener('mousemove', onMouseMove);
|
|
window.addEventListener('mouseup', stopDragging);
|
|
window.addEventListener('touchmove', onTouchMove);
|
|
window.addEventListener('touchend', stopDragging);
|
|
} else {
|
|
window.removeEventListener('mousemove', onMouseMove);
|
|
window.removeEventListener('mouseup', stopDragging);
|
|
window.removeEventListener('touchmove', onTouchMove);
|
|
window.removeEventListener('touchend', stopDragging);
|
|
}
|
|
|
|
return () => {
|
|
window.removeEventListener('mousemove', onMouseMove);
|
|
window.removeEventListener('mouseup', stopDragging);
|
|
window.removeEventListener('touchmove', onTouchMove);
|
|
window.removeEventListener('touchend', stopDragging);
|
|
};
|
|
}, [isDragging]);
|
|
|
|
return (
|
|
<section className="py-16 bg-white">
|
|
<div className="container mx-auto px-4 md:px-8 max-w-5xl">
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-3xl font-bold text-primary mb-4">Przed i Po</h2>
|
|
<p className="text-gray-600">Zobacz metamorfozę na jednym z naszych ostatnich projektów.</p>
|
|
</div>
|
|
|
|
<div
|
|
ref={containerRef}
|
|
className="relative w-full aspect-[4/3] md:aspect-[16/9] rounded-2xl overflow-hidden select-none cursor-ew-resize bg-gray-100"
|
|
onMouseDown={() => setIsDragging(true)}
|
|
onTouchStart={() => setIsDragging(true)}
|
|
>
|
|
{/* Obrazek PO (na spodzie) */}
|
|
<div className="absolute inset-0">
|
|
<Image
|
|
src={project.beforeAfter.afterUrl}
|
|
alt="Wygląd po remoncie"
|
|
fill
|
|
className="object-cover pointer-events-none"
|
|
/>
|
|
<div className="absolute top-4 right-4 bg-white/80 backdrop-blur text-primary font-semibold px-4 py-1.5 rounded-full text-sm">
|
|
Po
|
|
</div>
|
|
</div>
|
|
|
|
{/* Obrazek PRZED (na wierzchu, obcięty) */}
|
|
<div
|
|
className="absolute inset-0 overflow-hidden"
|
|
style={{ width: `${sliderPosition}%` }}
|
|
>
|
|
<div className="absolute inset-0 w-[100vw] min-w-full">
|
|
{/* Używamy native img dla prostszego manipulowania wymiarami wewnątrz overflow */}
|
|
<img
|
|
src={project.beforeAfter.beforeUrl}
|
|
alt="Wygląd przed remontem"
|
|
className="absolute inset-0 w-full h-full object-cover pointer-events-none"
|
|
style={{ width: containerRef.current?.getBoundingClientRect().width || '100%' }}
|
|
/>
|
|
</div>
|
|
<div className="absolute top-4 left-4 bg-white/80 backdrop-blur text-gray-700 font-semibold px-4 py-1.5 rounded-full text-sm">
|
|
Przed
|
|
</div>
|
|
</div>
|
|
|
|
{/* Suwak (linia i uchwyt) */}
|
|
<div
|
|
className="absolute top-0 bottom-0 w-1 bg-white cursor-ew-resize flex items-center justify-center shadow-[0_0_10px_rgba(0,0,0,0.3)]"
|
|
style={{ left: `calc(${sliderPosition}% - 2px)` }}
|
|
>
|
|
<div className="w-10 h-10 bg-white rounded-full flex items-center justify-center shadow-lg border border-gray-100 text-primary hover:scale-110 transition-transform">
|
|
<GripVertical className="w-5 h-5 text-gray-400" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|