import type { ReactNode } from 'react'; import { Tabs as BaseTabs } from '@base-ui-components/react/tabs'; import { cn } from '@wener/console'; import { flexRender, type FlexRenderable } from '@wener/reaction'; export namespace Tabs { /* Lifted (with title/action): ┌──────────────────────────────────────────────────────────┐ │ [Title] Tab1 | Tab2 | Tab3 [Action] │ │ (supports wrap) │ ├──────────────────────────────────────────────────────────┤ │ │ │ Tab Content │ │ │ └──────────────────────────────────────────────────────────┘ Lifted (no title - 4px spacer for smooth edge): ┌──────────────────────────────────────────────────────────┐ │ [4px] Tab1 | Tab2 | Tab3 [Action] │ ├──────────────────────────────────────────────────────────┤ │ │ │ Tab Content │ │ │ └──────────────────────────────────────────────────────────┘ Basic (without title/action): ┌─────────────────────────────────────────────┐ │ Tab1 | Tab2 | Tab3 │ ├─────────────────────────────────────────────┤ │ │ │ Tab Content │ │ │ └─────────────────────────────────────────────┘ */ export type TabItem = { key?: string; label?: ReactNode; icon?: FlexRenderable; content?: ReactNode; disabled?: boolean; }; export type CompositeProps = { tabs: TabItem[]; activeTab?: string; onTabChange?: (key: string) => void; defaultTab?: string; variant?: 'box' | 'border' | 'lift'; className?: string; title?: ReactNode; action?: ReactNode; scrollable?: boolean; }; type ListProps = Omit & Pick; const List = ({ tabs, variant = 'lift', children, title, action, className, ...props }: ListProps) => { const tabsWithKeys = tabs.map((tab, index) => ({ ...tab, key: tab.key ?? String(index), })); return ( {variant === 'lift' && (title || action) && (
{title &&

{title}

}
)} {children} {tabsWithKeys.map((tab) => ( {tab.icon && flexRender(tab.icon, {})} {tab.label} ))} {variant === 'lift' && (title || action) && (
)} {variant === 'lift' && action && (
{action}
)} ); }; export const Composite = ({ tabs, activeTab, onTabChange, defaultTab, variant, className, title, action, scrollable, // fixme this is workaround }: CompositeProps) => { const tabsWithKeys = tabs.map((tab, index) => ({ ...tab, key: tab.key ?? String(index), })); const firstTab = tabsWithKeys[0]?.key || ''; const defaultValue = defaultTab || firstTab; let content = ( <> {tabsWithKeys.map((tab) => ( {tab.content} ))} ); if (scrollable) { content = (
{content}
); } return ( { onTabChange?.(value as string); }} defaultValue={defaultValue} > {content} ); }; }