HR-ATS-Portal/frontend/src/ui/Tabs.jsx

49 lines
1.6 KiB
JavaScript

/* ============================================================
Tabs.jsx — new primitive.
js/ui.js had no tab component, so settings (10 tabs), candidates (8), inbox
(7) and rbac each hand-rolled one by pre-rendering every pane and toggling
`.active`. One component replaces four ad-hoc implementations, and only the
active pane is mounted — which also means a chart in a hidden pane no longer
draws into a zero-width canvas.
============================================================ */
import { useId, useState } from 'react'
export function Tabs({ tabs, value, onChange, className = 'tabs' }) {
const id = useId()
return (
<div className={className} role="tablist">
{tabs.map((t) => {
const key = t.key ?? t
const label = t.label ?? t
const active = key === value
return (
<button
key={key}
id={`${id}-${key}`}
role="tab"
aria-selected={active}
className={`tab${active ? ' active' : ''}`}
onClick={() => onChange(key)}
>
{label}{t.count != null && <span className="tab-count">{t.count}</span>}
</button>
)
})}
</div>
)
}
/** Uncontrolled convenience wrapper: <TabPanel tabs={[{key,label,render}]} /> */
export default function TabPanel({ tabs, initial, className }) {
const [value, setValue] = useState(initial ?? tabs[0]?.key)
const active = tabs.find((t) => t.key === value) ?? tabs[0]
return (
<>
<Tabs tabs={tabs} value={value} onChange={setValue} className={className} />
<div role="tabpanel">{active?.render?.()}</div>
</>
)
}