// eslint-disable-next-line import React, { useState, useRef, useEffect, Children, cloneElement, ReactElement, } from 'react'; interface TabProps { children?: React.ReactNode; isPro?: boolean; label: string; slug: string; status?: 'ok' | 'empty' | 'error'; } interface TabsProps { urlKey: string; children: React.ReactNode; renderTabHeaders?: ( headers: ReactElement ) => React.ReactNode; renderTabContent?: ( content: ReactElement ) => React.ReactNode; } export default function Tabs( { urlKey, children, renderTabHeaders, renderTabContent, }: TabsProps ) { const params = new URLSearchParams( window.location.search ); const activeSlug = params.get( urlKey ); const childArray = Children.toArray( children ) as ReactElement< TabProps >[]; const indexFromSlug = childArray.findIndex( ( child ) => React.isValidElement( child ) && child.props.slug === activeSlug ); const [ activeIndex, setActiveIndex ] = useState( indexFromSlug >= 0 ? indexFromSlug : 0 ); const tabRefs = useRef< ( HTMLButtonElement | null )[] >( [] ); const underlineRef = useRef< HTMLDivElement >( null ); const isFirstRender = useRef( true ); useEffect( () => { const updateUnderline = () => { const activeTab = tabRefs.current[ activeIndex ]; const underline = underlineRef.current; if ( activeTab && underline ) { const { offsetLeft, offsetWidth } = activeTab; // Disable transition on first render if ( isFirstRender.current ) { underline.style.transition = 'none'; isFirstRender.current = false; } else { underline.style.transition = ''; } underline.style.left = `${ offsetLeft }px`; underline.style.width = `${ offsetWidth }px`; } }; // Small delay to ensure DOM is ready const timer = setTimeout( updateUnderline, 0 ); // Update on window resize window.addEventListener( 'resize', updateUnderline ); return () => { clearTimeout( timer ); window.removeEventListener( 'resize', updateUnderline ); }; }, [ activeIndex ] ); const handleClick = ( index: number, slug: string ) => { setActiveIndex( index ); params.set( urlKey, slug ); window.history.replaceState( null, '', `?${ params.toString() }` ); }; const tabHeaders = ( ); const tabContent = (
{ childArray.map( ( child, idx ) => idx === activeIndex ? cloneElement( child ) : null ) }
); return (
{ renderTabHeaders ? renderTabHeaders( tabHeaders ) : tabHeaders } { renderTabContent ? renderTabContent( tabContent ) : tabContent }
); }