56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { type KeyboardEvent } from "react";
|
|
|
|
export interface TabItem<T extends string> {
|
|
id: T;
|
|
label: string;
|
|
}
|
|
|
|
interface TabsProps<T extends string> {
|
|
items: TabItem<T>[];
|
|
activeId: T;
|
|
onChange: (id: T) => void;
|
|
}
|
|
|
|
export function Tabs<T extends string>({ items, activeId, onChange }: TabsProps<T>) {
|
|
function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {
|
|
const currentIndex = items.findIndex((item) => item.id === activeId);
|
|
if (event.key === "ArrowRight") {
|
|
event.preventDefault();
|
|
const next = items.at((currentIndex + 1) % items.length);
|
|
if (next) onChange(next.id);
|
|
} else if (event.key === "ArrowLeft") {
|
|
event.preventDefault();
|
|
const prev = items.at((currentIndex - 1 + items.length) % items.length);
|
|
if (prev) onChange(prev.id);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div role="tablist" aria-label="Sections du CV" className="flex flex-wrap gap-1">
|
|
{items.map((item) => {
|
|
const selected = item.id === activeId;
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
role="tab"
|
|
id={`tab-${item.id}`}
|
|
aria-selected={selected}
|
|
aria-controls={`panel-${item.id}`}
|
|
tabIndex={selected ? 0 : -1}
|
|
onClick={() => onChange(item.id)}
|
|
onKeyDown={handleKeyDown}
|
|
className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent ${
|
|
selected
|
|
? "bg-accent text-white"
|
|
: "border border-border bg-white text-muted hover:text-ink"
|
|
}`}
|
|
>
|
|
{item.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
} |