feat: complete website implementation
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
'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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user