Files
cydweb/components/board-selector.tsx
DESKTOP-PUHFO60\dsx2016 a59aaf1c8d Add multi-board selector and scalable firmware catalog UI.
Board chips reserve future models; firmware uses searchable cards with detail pane and optional meta.json media/docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 19:45:24 +08:00

74 lines
2.3 KiB
TypeScript

"use client";
import { useTranslations } from "next-intl";
import { BOARDS } from "@/lib/boards";
import { cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
export function BoardSelector({
value,
onChange,
}: {
value: string;
onChange: (boardId: string) => void;
}) {
const t = useTranslations("boards");
return (
<div>
<div className="mb-3 flex items-baseline justify-between gap-3">
<h2 className="m-0 font-[family-name:var(--font-display)] text-[1.05rem] font-bold">
{t("title")}
</h2>
<p className="m-0 text-xs text-[var(--muted)]">{t("hint")}</p>
</div>
<div
className="grid grid-cols-2 gap-2 sm:grid-cols-4"
role="radiogroup"
aria-label={t("title")}
>
{BOARDS.map((board) => {
const selected = board.id === value;
const disabled = !board.available;
return (
<button
key={board.id}
type="button"
role="radio"
aria-checked={selected}
aria-disabled={disabled}
disabled={disabled}
title={disabled ? t("soon") : board.displayName}
onClick={() => {
if (!disabled) onChange(board.id);
}}
className={cn(
"relative flex flex-col items-start gap-1 rounded-[12px] border px-3 py-3 text-left transition-all",
disabled &&
"cursor-not-allowed opacity-40 grayscale-[0.4]",
!disabled &&
!selected &&
"border-[var(--line)] bg-[rgba(0,0,0,0.2)] hover:border-[rgba(240,197,10,0.35)]",
selected &&
"border-[var(--accent)] bg-[rgba(240,197,10,0.1)] shadow-[inset_0_0_0_1px_rgba(240,197,10,0.25)]"
)}
>
<span className="text-[0.8rem] font-semibold leading-tight">
{t(`items.${board.labelKey}`)}
</span>
<span className="text-[0.7rem] text-[var(--muted)]">
{board.chip}
</span>
{disabled && (
<Badge variant="muted" className="mt-1 absolute top-2 right-2">
{t("soon")}
</Badge>
)}
</button>
);
})}
</div>
</div>
);
}