Files
mimic/frontend/src/components/Tabs.tsx

73 lines
2.0 KiB
TypeScript
Raw Normal View History

import type { KeyboardEvent } from 'react';
interface TabItem {
id: string;
label: string;
count?: number;
disabled?: boolean;
}
interface TabsProps {
items: TabItem[];
activeId: string;
onChange: (id: string) => void;
}
export function Tabs({ items, activeId, onChange }: TabsProps): JSX.Element {
function nextEnabled(from: number, direction: 1 | -1): string {
const len = items.length;
let i = (from + direction + len) % len;
while (i !== from) {
if (!items[i].disabled) return items[i].id;
i = (i + direction + len) % len;
}
return items[from].id;
}
function handleKeyDown(e: KeyboardEvent<HTMLButtonElement>, index: number) {
if (e.key === 'ArrowRight') {
e.preventDefault();
onChange(nextEnabled(index, 1));
} else if (e.key === 'ArrowLeft') {
e.preventDefault();
onChange(nextEnabled(index, -1));
}
}
return (
<div
role="tablist"
className="flex items-end border-b border-hairline gap-xs"
>
{items.map((item, index) => {
const isActive = item.id === activeId;
const isDisabled = item.disabled ?? false;
return (
<button
key={item.id}
id={`tab-${item.id}`}
role="tab"
aria-selected={isActive}
aria-controls={`tabpanel-${item.id}`}
aria-disabled={isDisabled || undefined}
disabled={isDisabled}
type="button"
onClick={() => { if (!isDisabled) onChange(item.id); }}
onKeyDown={(e) => handleKeyDown(e, index)}
className={`tab-underline${isActive ? ' tab-underline-active' : ''}${isDisabled ? ' tab-underline-disabled' : ''} pb-xs`}
>
{item.label}
{item.count !== undefined ? (
<span
className={`ml-xxs tab-count-pill${isActive ? ' tab-count-pill-active' : ''}`}
>
{item.count}
</span>
) : null}
</button>
);
})}
</div>
);
}