'use client'; import { forwardRef, HTMLAttributes, ReactNode, useRef, useState, useEffect } from 'react'; import styles from './horizontal-scroll.module.css'; export interface HorizontalScrollProps extends HTMLAttributes { /** Panel contents */ children: ReactNode; /** Color variant */ variant?: 'cyan' | 'green' | 'amber' | 'blood'; /** Panel size */ panelSize?: 'full' | 'large' | 'medium' | 'small'; /** Show fade on edges */ fadeEdges?: boolean; /** Show navigation indicators */ showIndicators?: boolean; /** Gap between panels in rem */ gap?: number; } export const HorizontalScroll = forwardRef( ( { children, variant = 'cyan', panelSize = 'large', fadeEdges = false, showIndicators = false, gap = 2, className, style, ...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(`.${styles.panel}`); setPanelCount(panels.length); const handleScroll = () => { if (!container) return; const scrollLeft = container.scrollLeft; const panelWidth = container.scrollWidth / panels.length; setActiveIndex(Math.round(scrollLeft / panelWidth)); }; container.addEventListener('scroll', handleScroll, { passive: true }); return () => container.removeEventListener('scroll', handleScroll); }, [children]); const scrollToPanel = (index: number) => { const container = containerRef.current; if (!container) return; const panelWidth = container.scrollWidth / panelCount; container.scrollTo({ left: panelWidth * index, behavior: 'smooth' }); }; const panelSizeClass = { full: styles.panelFull, large: styles.panelLarge, medium: styles.panelMedium, small: styles.panelSmall, }[panelSize]; return (
{ (containerRef as React.MutableRefObject).current = node; if (typeof ref === 'function') ref(node); else if (ref) ref.current = node; }} className={`${styles.container} ${styles[variant]} ${fadeEdges ? styles.fadeEdges : ''}`} style={{ '--panel-gap': `${gap}rem`, ...style } as React.CSSProperties} {...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;