import React, { useState, useEffect, useRef } from 'react'; import { cn } from '@/lib/utils'; export interface VerticalTab { id: string; label: string; icon?: React.ReactNode; } interface VerticalTabFormProps { tabs: VerticalTab[]; children: React.ReactNode; className?: string; } export function VerticalTabForm({ tabs, children, className }: VerticalTabFormProps) { const [activeTab, setActiveTab] = useState(tabs[0]?.id || ''); const contentRef = useRef(null); const sectionRefs = useRef<{ [key: string]: HTMLElement }>({}); // Scroll spy - update active tab based on scroll position useEffect(() => { const handleScroll = () => { if (!contentRef.current) return; const scrollPosition = contentRef.current.scrollTop + 100; // Offset for better UX // Find which section is currently in view for (const tab of tabs) { const section = sectionRefs.current[tab.id]; if (section) { const { offsetTop, offsetHeight } = section; if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) { setActiveTab(tab.id); break; } } } }; const content = contentRef.current; if (content) { content.addEventListener('scroll', handleScroll); return () => content.removeEventListener('scroll', handleScroll); } }, [tabs]); // Register section refs const registerSection = (id: string, element: HTMLElement | null) => { if (element) { sectionRefs.current[id] = element; } }; // Scroll to section const scrollToSection = (id: string) => { const section = sectionRefs.current[id]; if (section && contentRef.current) { const offsetTop = section.offsetTop - 20; // Small offset from top contentRef.current.scrollTo({ top: offsetTop, behavior: 'smooth', }); setActiveTab(id); } }; return (
{/* Vertical Tabs Sidebar */}
{tabs.map((tab) => ( ))}
{/* Content Area */}
{React.Children.map(children, (child) => { if (React.isValidElement(child) && child.props['data-section-id']) { const sectionId = child.props['data-section-id']; const isActive = sectionId === activeTab; return React.cloneElement(child as React.ReactElement, { ref: (el: HTMLElement) => registerSection(sectionId, el), className: isActive ? '' : 'hidden', }); } return child; })}
); } // Section wrapper component for easier usage interface SectionProps { id: string; children: React.ReactNode; className?: string; } export const FormSection = React.forwardRef( ({ id, children, className }, ref) => { return (
{children}
); } ); FormSection.displayName = 'FormSection';