import React, { useLayoutEffect, useRef, useState, type CSSProperties } from 'react' export interface SlidingTab { id: T label: React.ReactNode title?: string icon?: React.ComponentType<{ size?: number; className?: string }> count?: number } interface SlidingTabsProps { tabs: readonly SlidingTab[] activeTab: T onChange: (id: T) => void size?: 'sm' | 'md' fullWidth?: boolean className?: string indicatorColor?: string indicatorTextColor?: string } // Stripe-style sliding indicator — der aktive Pill gleitet zwischen Tabs. // Nutzt gemessene Offsets der Buttons + CSS transform. export function SlidingTabs({ tabs, activeTab, onChange, size = 'md', fullWidth, className, indicatorColor = 'var(--accent)', indicatorTextColor = 'var(--accent-text)', }: SlidingTabsProps): React.ReactElement { const containerRef = useRef(null) const tabRefs = useRef>(new Map()) const [indicator, setIndicator] = useState<{ left: number; width: number; ready: boolean }>({ left: 0, width: 0, ready: false }) useLayoutEffect(() => { const active = tabRefs.current.get(activeTab) const container = containerRef.current if (!active || !container) return const containerRect = container.getBoundingClientRect() const activeRect = active.getBoundingClientRect() setIndicator({ left: activeRect.left - containerRect.left + container.scrollLeft, width: activeRect.width, ready: true, }) }, [activeTab, tabs.length]) const padding = size === 'sm' ? '5px 12px' : '6px 14px' const fontSize = size === 'sm' ? 12 : 13 const borderRadius = size === 'sm' ? 18 : 20 return (
{/* Sliding indicator */} {indicator.ready && (
)} {tabs.map(tab => { const isActive = tab.id === activeTab const Icon = tab.icon const btnStyle: CSSProperties = { position: 'relative', zIndex: 1, flexShrink: 0, padding, borderRadius, border: 'none', cursor: 'pointer', fontSize, fontWeight: isActive ? 600 : 500, background: 'transparent', color: isActive ? indicatorTextColor : 'var(--text-muted)', fontFamily: 'inherit', transition: 'color 220ms cubic-bezier(0.23, 1, 0.32, 1)', display: 'flex', alignItems: 'center', gap: 6, flex: fullWidth ? 1 : undefined, justifyContent: 'center', whiteSpace: 'nowrap', } return ( ) })}
) } export default SlidingTabs