UNPKG

react-vite-themes

Version:

A test/experimental React theme system created for learning purposes. Features atomic design components, SCSS variables, and dark/light theme support. Not intended for production use.

90 lines (89 loc) 4.7 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React, { useState, createContext, useContext, useEffect, useRef } from 'react'; import { Icon } from '../../atoms/Icon'; import { cn } from '../../../utils/classNames'; import './Tabs.scss'; const TabsContext = createContext(undefined); const useTabsContext = () => { const context = useContext(TabsContext); if (!context) { throw new Error('Tabs components must be used within a Tabs component'); } return context; }; export const TabList = ({ children, className }) => { const { variant, size, isVertical, isFullWidth } = useTabsContext(); const tabListClasses = cn('tabs-list', `tabs-list--${variant}`, `tabs-list--${size}`, isVertical && 'tabs-list--vertical', isFullWidth && 'tabs-list--full-width', className); return (_jsx("div", { className: tabListClasses, role: "tablist", children: children })); }; export const Tab = ({ tabId, children, className }) => { const { activeTab, setActiveTab, variant, size } = useTabsContext(); const isActive = activeTab === tabId; const tabClasses = cn('tab', `tab--${variant}`, `tab--${size}`, isActive && 'tab--active', className); return (_jsx("button", { className: tabClasses, onClick: () => setActiveTab(tabId), role: "tab", "aria-selected": isActive, "aria-controls": `panel-${tabId}`, id: `tab-${tabId}`, children: children })); }; export const TabPanel = ({ tabId, children, className }) => { const { activeTab, useTargetId } = useTabsContext(); const isActive = activeTab === tabId; // If using targetId approach, don't render this panel if (useTargetId) { return null; } const panelClasses = cn('tab-panel', isActive && 'tab-panel--active', className); return (_jsx("div", { className: panelClasses, role: "tabpanel", id: `panel-${tabId}`, "aria-labelledby": `tab-${tabId}`, hidden: !isActive, children: children })); }; export const TargetContent = ({ targetId }) => { const { activeTab, useTargetId } = useTabsContext(); const targetRef = useRef(null); useEffect(() => { if (useTargetId) { targetRef.current = document.getElementById(targetId); } }, [targetId, useTargetId]); useEffect(() => { if (useTargetId && targetRef.current) { const isActive = activeTab === targetId.replace('-content', ''); if (isActive) { targetRef.current.style.display = 'block'; targetRef.current.setAttribute('aria-hidden', 'false'); } else { targetRef.current.style.display = 'none'; targetRef.current.setAttribute('aria-hidden', 'true'); } } }, [activeTab, targetId, useTargetId]); // This component doesn't render anything visible // It just manages the target element's visibility return null; }; // Main Tabs Component export const Tabs = ({ tabs, defaultActiveTab, variant = 'default', size = 'md', isFullWidth = false, isVertical = false, showIcons = true, className, onTabChange }) => { const [activeTab, setActiveTab] = useState(defaultActiveTab || tabs[0]?.id || ''); // Auto-detect which approach is being used const useTargetId = tabs.some(tab => tab.targetId) && !tabs.some(tab => tab.content); const handleTabChange = (tabId) => { setActiveTab(tabId); onTabChange?.(tabId); }; const contextValue = { activeTab, setActiveTab: handleTabChange, variant, size, isVertical, isFullWidth, showIcons, useTargetId }; const tabsClasses = cn('tabs', `tabs--${variant}`, `tabs--${size}`, isVertical && 'tabs--vertical', className); return (_jsx(TabsContext.Provider, { value: contextValue, children: _jsxs("div", { className: tabsClasses, children: [_jsx(TabList, { children: tabs.map((tab) => (_jsxs(Tab, { tabId: tab.id, children: [showIcons && tab.icon && (_jsx(Icon, { name: tab.icon, size: "sm" })), _jsx("span", { className: "tab-label", children: tab.label })] }, tab.id))) }), useTargetId ? ( // Target ID approach - manage external content _jsx("div", { className: "tabs-content", children: tabs.map((tab) => (_jsx(TargetContent, { targetId: tab.targetId }, tab.id))) })) : ( // Content prop approach - render content inline _jsx("div", { className: "tabs-content", children: tabs.map((tab) => (_jsx(TabPanel, { tabId: tab.id, children: tab.content }, tab.id))) }))] }) })); }; // Simple Tabs Component (for backward compatibility) export const SimpleTabs = (props) => { return _jsx(Tabs, { ...props }); };