2025-04-26 03:43:59 +02:00

104 lines
2.9 KiB
TypeScript

import React, { useState, useEffect, useRef } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
import './TabView.css';
interface TabBarProps {
tabs: {
id: string;
label: string;
icon?: React.ReactNode | IconDefinition;
component: React.ComponentType<any>;
}[];
activeTab?: string;
onTabChange?: (tabId: string) => void;
}
export const TabView: React.FC<TabBarProps> = ({
tabs,
activeTab,
onTabChange
}) => {
const [active, setActive] = useState(activeTab || tabs[0]?.id || '');
const [ActiveComponent, setActiveComponent] = useState<React.ComponentType<any> | null>(null);
const [isTransitioning, setIsTransitioning] = useState(false);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (activeTab && activeTab !== active) {
setActive(activeTab);
}
}, [activeTab, active]);
useEffect(() => {
const activeTabData = tabs.find(tab => tab.id === active);
if (activeTabData) {
// Add transitioning effect
setIsTransitioning(true);
// Fade out current content
if (contentRef.current) {
contentRef.current.style.opacity = '0';
contentRef.current.style.transform = 'translateX(-10px)';
}
// Set new component after brief transition
setTimeout(() => {
setActiveComponent(() => activeTabData.component);
// Fade in new content
if (contentRef.current) {
contentRef.current.style.opacity = '1';
contentRef.current.style.transform = 'translateX(0)';
}
setIsTransitioning(false);
}, 100);
}
}, [active, tabs]);
const handleTabClick = (tabId: string) => {
if (tabId !== active && !isTransitioning) {
setActive(tabId);
onTabChange?.(tabId);
}
};
return (
<div className="tab-container">
<div
className="tab-content"
ref={contentRef}
style={{
transition: 'opacity 0.3s ease, transform 0.3s ease',
}}
>
{ActiveComponent && <ActiveComponent />}
</div>
<div className="tab-bar">
{tabs.map(tab => (
<button
key={tab.id}
className={`tab-bar-item ${active === tab.id ? 'active' : ''}`}
onClick={() => handleTabClick(tab.id)}
aria-selected={active === tab.id}
role="tab"
>
{tab.icon && (
<span className="tab-icon">
{React.isValidElement(tab.icon) ? (
tab.icon
) : (
<FontAwesomeIcon icon={tab.icon as IconDefinition} />
)}
</span>
)}
<span className="tab-label">{tab.label}</span>
</button>
))}
</div>
</div>
);
};
export default TabView;