'use client'; import { forwardRef, HTMLAttributes, ReactNode, useRef, useState, useEffect } from 'react'; export interface HorizontalScrollProps extends HTMLAttributes { children: ReactNode; variant?: 'cyan' | 'green' | 'amber' | 'blood'; panelSize?: 'full' | 'large' | 'medium' | 'small'; fadeEdges?: boolean; showIndicators?: boolean; gap?: number; } const variantStyles = { cyan: { scrollbar: 'scrollbar-thumb-cyan-400', indicator: 'border-cyan-400 hover:bg-cyan-400 hover:shadow-[0_0_10px_#00f0ff]' }, green: { scrollbar: 'scrollbar-thumb-emerald-400', indicator: 'border-emerald-400 hover:bg-emerald-400 hover:shadow-[0_0_10px_#00ff88]' }, amber: { scrollbar: 'scrollbar-thumb-amber-400', indicator: 'border-amber-400 hover:bg-amber-400 hover:shadow-[0_0_10px_#ffaa00]' }, blood: { scrollbar: 'scrollbar-thumb-red-800', indicator: 'border-red-800 hover:bg-red-800 hover:shadow-[0_0_10px_#8b1a1a]' }, }; const sizeClasses = { full: 'w-screen', large: 'w-[80vw]', medium: 'w-[60vw]', small: 'w-[40vw]', }; export const HorizontalScroll = forwardRef( ({ children, variant = 'cyan', panelSize = 'large', fadeEdges = false, showIndicators = false, gap = 2, className = '', ...props }, ref) => { const containerRef = useRef(null); const [activeIndex, setActiveIndex] = useState(0); const [panelCount, setPanelCount] = useState(0); useEffect(() => { const container = containerRef.current; if (!container) return; const panels = container.querySelectorAll('[data-panel]'); setPanelCount(panels.length); const handleScroll = () => { if (!container || panels.length === 0) return; const panelWidth = container.scrollWidth / panels.length; setActiveIndex(Math.round(container.scrollLeft / panelWidth)); }; container.addEventListener('scroll', handleScroll, { passive: true }); return () => container.removeEventListener('scroll', handleScroll); }, [children]); const scrollToPanel = (index: number) => { const container = containerRef.current; if (!container || panelCount === 0) return; const panelWidth = container.scrollWidth / panelCount; container.scrollTo({ left: panelWidth * index, behavior: 'smooth' }); }; const colors = variantStyles[variant]; return (
{ (containerRef as React.MutableRefObject).current = node; if (typeof ref === 'function') ref(node); else if (ref) ref.current = node; }} className={` w-full overflow-x-auto overflow-y-hidden snap-x snap-mandatory scroll-smooth scrollbar-thin scrollbar-track-black/30 ${colors.scrollbar} ${fadeEdges ? '[mask-image:linear-gradient(90deg,transparent_0%,black_5%,black_95%,transparent_100%)]' : ''} `} {...props} >
{Array.isArray(children) ? children.map((child, i) => (
{child}
)) :
{children}
}
{showIndicators && panelCount > 1 && (
{Array.from({ length: panelCount }).map((_, i) => (
)}
); } ); HorizontalScroll.displayName = 'HorizontalScroll'; export default HorizontalScroll;