Mark component props as Readonly<> across 15 component files and simplify edit-player-character field access with optional chaining and nullish coalescing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
39 lines
1.0 KiB
TypeScript
39 lines
1.0 KiB
TypeScript
import type { PlayerIcon } from "@initiative/domain";
|
|
import { VALID_PLAYER_ICONS } from "@initiative/domain";
|
|
import { cn } from "../lib/utils";
|
|
import { PLAYER_ICON_MAP } from "./player-icon-map";
|
|
|
|
interface IconGridProps {
|
|
value: string;
|
|
onChange: (icon: string) => void;
|
|
}
|
|
|
|
const ICONS = [...VALID_PLAYER_ICONS] as PlayerIcon[];
|
|
|
|
export function IconGrid({ value, onChange }: Readonly<IconGridProps>) {
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
{ICONS.map((iconId) => {
|
|
const Icon = PLAYER_ICON_MAP[iconId];
|
|
return (
|
|
<button
|
|
key={iconId}
|
|
type="button"
|
|
onClick={() => onChange(value === iconId ? "" : iconId)}
|
|
className={cn(
|
|
"flex h-9 w-9 items-center justify-center rounded-md transition-all",
|
|
value === iconId
|
|
? "bg-primary/20 text-foreground ring-2 ring-primary"
|
|
: "text-muted-foreground hover:bg-card hover:text-foreground",
|
|
)}
|
|
aria-label={iconId}
|
|
title={iconId}
|
|
>
|
|
<Icon size={20} />
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|