import { Children, createContext, isValidElement, useContext, type ReactElement, type ReactNode } from 'react'; import { Tab, Tabs as BaseTabs } from 'baseui/tabs/index.js'; import { cn } from '../../utils.js'; type TabsContextValue = { activeKey: string; }; const TabsContext = createContext(null); type TabsProps = { className?: string; value: string; onValueChange: (value: string) => void; children: ReactNode; }; type TabsListProps = { className?: string; children: ReactNode; 'aria-label'?: string; }; type TabsTriggerProps = { value: string; disabled?: boolean; children: ReactNode; }; type TabsContentProps = { value: string; className?: string; children: ReactNode; }; type ElementWithChildren = ReactElement<{ children?: ReactNode }>; type TabTriggerElement = ReactElement; type TabsContentElement = ReactElement; const collectElements = ( children: ReactNode, matcher: (element: ReactElement) => element is ReactElement, ): Array> => { const matches: Array> = []; Children.forEach(children, (child) => { if (!isValidElement(child)) { return; } if (matcher(child)) { matches.push(child); return; } const childWithChildren = child as ElementWithChildren; if (childWithChildren.props.children !== undefined) { matches.push(...collectElements(childWithChildren.props.children, matcher)); } }); return matches; }; const tabsOverrides = { Root: { style: { width: '100%', minWidth: 0, }, }, TabBar: { style: { display: 'inline-flex', minHeight: '26px', maxWidth: '100%', alignItems: 'center', gap: '2px', overflowX: 'auto', overflowY: 'hidden', border: '1px solid rgba(255, 255, 255, 0.08)', borderRadius: '4px', background: 'rgba(255, 255, 255, 0.04)', paddingTop: '2px', paddingRight: '2px', paddingBottom: '2px', paddingLeft: '2px', scrollbarWidth: 'none', }, }, Tab: { style: ({ $active, $disabled, $isFocusVisible }: { $active?: boolean; $disabled?: boolean; $isFocusVisible?: boolean }) => ({ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'nowrap', borderRadius: '2px', backgroundColor: $active ? '#ffffff' : 'transparent', minHeight: '22px', paddingTop: '3px', paddingRight: '9px', paddingBottom: '3px', paddingLeft: '9px', color: $active ? '#000000' : 'rgba(255, 255, 255, 0.6)', fontSize: '12px', fontWeight: 500, lineHeight: 1.2, transitionProperty: 'background-color, color', transitionDuration: '120ms', outline: $isFocusVisible ? '2px solid rgba(130, 50, 255, 0.95)' : 'none', outlineOffset: '-2px', cursor: $disabled ? 'default' : 'pointer', opacity: $disabled ? 0.4 : 1, ':hover': $disabled || $active ? undefined : { color: '#ffffff', }, }), }, }; export const Tabs = ({ className, value, onValueChange, children }: TabsProps) => { const lists = collectElements(children, (child): child is ReactElement => child.type === TabsList); const content = collectElements(children, (child): child is TabsContentElement => child.type === TabsContent); const list = lists[0]; if (!list) { return null; } const triggers = Children.toArray(list.props.children).filter( (child): child is TabTriggerElement => isValidElement(child) && child.type === TabsTrigger, ); return (
onValueChange(String(activeKey))} overrides={tabsOverrides}> {triggers.map((trigger) => ( ))} {content}
); }; export const TabsList = ({ children }: TabsListProps) => { return <>{children}; }; export const TabsTrigger: (_props: TabsTriggerProps) => null = () => { return null; }; export const TabsContent = ({ value, className, children }: TabsContentProps) => { const context = useContext(TabsContext); const isActive = context?.activeKey === value; if (!isActive) { return null; } return
{children}
; };