Files
e-tib.com/components/blocks/ReferencesSlider.tsx
Marc Mintel 9ed690c53f chore: completely remove all decorative animations as requested
Former-commit-id: 1efd9a9ae1d934a64375765f984740af874dd1a9
2026-05-11 22:30:01 +02:00

158 lines
6.3 KiB
TypeScript

'use client';
import * as React from 'react';
import { motion } from 'framer-motion';
import Link from 'next/link';
import Image from 'next/image';
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
export interface Reference {
id: string;
title: string;
slug: string;
category: string;
image?: {
url: string;
alt?: string;
} | string | null;
}
interface ReferencesSliderProps {
references?: Reference[];
badge?: string;
title?: string;
description?: string;
ctaLabel?: string;
ctaHref?: string;
data?: {
badge?: string;
title?: string;
description?: string;
ctaLabel?: string;
ctaHref?: string;
}
}
export function ReferencesSlider(props: ReferencesSliderProps) {
const { data } = props;
const references = props.references || [];
const containerRef = React.useRef<HTMLDivElement>(null);
const [isDragging, setIsDragging] = React.useState(false);
const [startX, setStartX] = React.useState(0);
const [scrollLeft, setScrollLeft] = React.useState(0);
const badge = props.badge || data?.badge || 'Ausgewählte Projekte';
const title = props.title || data?.title || 'Referenzen & Erfolge';
const description = props.description || data?.description || 'Ein Auszug unserer erfolgreich abgeschlossenen Projekte im Bereich Kabeltiefbau, Bohrtechnik und Netzinfrastruktur.';
const ctaLabel = props.ctaLabel || data?.ctaLabel || 'Alle Referenzen ansehen';
const ctaHref = props.ctaHref || data?.ctaHref || '/referenzen';
if (!references || references.length === 0) return null;
// Fallback images pool if CMS doesn't provide one
const fallbacks = [
'/assets/photos/DSC01123.JPG',
'/assets/photos/DSC00850.JPG',
'/assets/photos/DSC01129.JPG',
'/assets/photos/DSC00010.JPG',
];
const onMouseDown = (e: React.MouseEvent) => {
if (!containerRef.current) return;
setIsDragging(true);
setStartX(e.pageX - containerRef.current.offsetLeft);
setScrollLeft(containerRef.current.scrollLeft);
};
const onMouseLeave = () => {
setIsDragging(false);
};
const onMouseUp = () => {
setIsDragging(false);
};
const onMouseMove = (e: React.MouseEvent) => {
if (!isDragging || !containerRef.current) return;
e.preventDefault();
const x = e.pageX - containerRef.current.offsetLeft;
const walk = (x - startX) * 2; // Scroll-fast
containerRef.current.scrollLeft = scrollLeft - walk;
};
return (
<section className="py-24 bg-neutral-dark text-white relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-neutral-dark via-neutral-900 to-neutral-dark z-0" />
<div className="container relative z-10 mb-12 flex flex-col md:flex-row justify-between items-end gap-6">
<div>
<h2 className="text-primary-light font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
<h3 className="font-heading text-4xl md:text-5xl font-extrabold">{title}</h3>
</div>
<p className="text-white/60 max-w-md">
{description}
</p>
</div>
<div className="relative z-10">
<div
ref={containerRef}
onMouseDown={onMouseDown}
onMouseLeave={onMouseLeave}
onMouseUp={onMouseUp}
onMouseMove={onMouseMove}
className={`flex gap-6 overflow-x-auto pb-12 pt-4 px-[calc((100vw-min(100%,1280px))/2)] [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
>
{references.map((ref, i) => {
const imgSrc = (ref.image && typeof ref.image === 'object' && ref.image.url)
? ref.image.url
: fallbacks[i % fallbacks.length];
return (
<motion.div
key={ref.id}
initial={{ opacity: 0, x: 50 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: i * 0.1, duration: 0.6, ease: "easeOut" }}
className="flex-shrink-0 w-[320px] md:w-[480px] snap-center group pointer-events-auto"
>
<div className="relative aspect-[16/10] bg-neutral-800 rounded-2xl overflow-hidden mb-5 border border-white/5 shadow-2xl">
<Image
src={imgSrc}
alt={ref.title}
fill
sizes="(max-width: 768px) 320px, 480px"
className="object-cover saturate-50 opacity-70 group-hover:scale-105 group-hover:opacity-100 group-hover:saturate-100 transition-all duration-700 ease-in-out pointer-events-none"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/30 to-transparent opacity-80 group-hover:opacity-90 transition-opacity duration-300 pointer-events-none" />
<HoverShineOverlay />
<div className="absolute bottom-6 left-8 right-8 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-500 pointer-events-none">
<span className="inline-block py-1.5 px-4 rounded-full bg-primary/30 backdrop-blur-md text-white text-xs font-bold uppercase tracking-widest mb-3 border border-primary/50">
{ref.category}
</span>
<h4 className="font-heading text-2xl md:text-3xl font-bold line-clamp-2">{ref.title}</h4>
</div>
</div>
</motion.div>
);
})}
</div>
</div>
<div className="container relative z-10 mt-4 flex justify-center">
<Link
href={ctaHref}
className="px-8 py-4 rounded-xl bg-white/5 border border-white/20 hover:bg-white hover:text-neutral-dark hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] transition-all font-bold flex items-center gap-3 backdrop-blur-sm"
>
{ctaLabel}
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
</Link>
</div>
</section>
);
}