import React, { createContext, useContext, useLayoutEffect, useRef, useState } from 'react' const TabsContext = createContext<{ value: string onChange: (value: string) => void } | null>(null) function useTabsContext() { const ctx = useContext(TabsContext) if (!ctx) throw new Error('Tab components must be used inside ') return ctx } export const Tabs: React.FC<{ defaultValue: string children: React.ReactNode onValueChange?: (value: string) => void }> = ({ defaultValue, children, onValueChange }) => { const [value, setValue] = useState(defaultValue) const handleChange = (newValue: string) => { setValue(newValue) onValueChange?.(newValue) } return ( {children} ) } export const TabList: React.FC<{ children: React.ReactNode; className?: string }> = ({ children, className = '', }) => { const { value } = useTabsContext() const containerRef = useRef(null) const [indicator, setIndicator] = useState<{ left: number; width: number } | null>(null) useLayoutEffect(() => { const el = containerRef.current if (!el) return const updateIndicator = () => { const active = el.querySelector('[data-active="true"]') if (!active) return const cRect = el.getBoundingClientRect() const aRect = active.getBoundingClientRect() if (aRect.width === 0) return setIndicator({ left: aRect.left - cRect.left, width: aRect.width }) } updateIndicator() const ro = new ResizeObserver(updateIndicator) ro.observe(el) return () => ro.disconnect() }, [value]) return (
{indicator && (
)} {children}
) } export const Tab: React.FC<{ value: string children: React.ReactNode className?: string }> = ({ value, children, className = '' }) => { const { value: activeValue, onChange } = useTabsContext() const isActive = activeValue === value return ( ) } export const TabContent: React.FC<{ value: string children: React.ReactNode className?: string }> = ({ value, children, className = '' }) => { const { value: activeValue } = useTabsContext() if (activeValue !== value) return null return
{children}
} export type TabItem = { value: string; label: string }