import { type FC } from 'react'; import * as Tabs from '@radix-ui/react-tabs'; import type { FoldrRow } from '../Foldr.ts'; import { SheetFrame } from './SheetFrame.tsx'; import styles from '../styles.module.css'; export interface TabBarProps { readonly rows: readonly FoldrRow[]; readonly activeIndex: number; /** Bumped whenever `rows` mutates in-place — keeps iframe postMessage in sync. */ readonly rowsRev: number; readonly basePath: string; readonly suffix: string; readonly index: string; readonly onChange: (idx: number) => void; readonly onRename?: ((idx: number) => void) | undefined; readonly firstFocusUsed: boolean; readonly onFirstFocus: () => void; } /** * Renders a Radix Tabs.Root with one tab per TOC row, plus one TabsContent * (iframe) per row. `activeIndex` drives `value`; clicking a trigger bubbles * back through `onChange(nextIndex)`. * * The iframe-per-tab rendering mirrors the legacy `Nav`/`Frame` component * split: every tab's frame is kept mounted (Radix TabsContent has * `forceMount` for this so the iframe state survives tab switches), and * `visibility: hidden` in CSS hides non-active ones. */ export const TabBar: FC = ({ rows, activeIndex, rowsRev, basePath, suffix, index, onChange, onRename, firstFocusUsed, onFirstFocus, }) => { const clampedIndex = Math.max(0, Math.min(activeIndex, rows.length - 1)); const activeValue = `tab-${clampedIndex}`; return ( onChange(parseInt(v.replace(/^tab-/, ''), 10))} orientation="horizontal" activationMode="manual" > {rows.map((row, i) => { const title = row.title; const value = `tab-${i}`; return ( { onChange(i); onRename?.(i); }} > {title} ); })} {rows.map((row, i) => { const title = row.title; const value = `tab-${i}`; const link = row.link || `/${encodeURIComponent(title)}`; const src = `${basePath}${link}${suffix}`; return ( ); })} ); };