Make player character color and icon optional
Clicking an already-selected color or icon in the create/edit form now deselects it. PCs without a color use the default combatant styling; PCs without an icon show no icon. Domain, application, persistence, and display layers all updated to handle the optional fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -95,9 +95,12 @@ function AddModeSuggestions({
|
||||
</div>
|
||||
<ul>
|
||||
{pcMatches.map((pc) => {
|
||||
const PcIcon = PLAYER_ICON_MAP[pc.icon as PlayerIcon];
|
||||
const pcColor =
|
||||
PLAYER_COLOR_HEX[pc.color as keyof typeof PLAYER_COLOR_HEX];
|
||||
const PcIcon = pc.icon
|
||||
? PLAYER_ICON_MAP[pc.icon as PlayerIcon]
|
||||
: undefined;
|
||||
const pcColor = pc.color
|
||||
? PLAYER_COLOR_HEX[pc.color as keyof typeof PLAYER_COLOR_HEX]
|
||||
: undefined;
|
||||
return (
|
||||
<li key={pc.id}>
|
||||
<button
|
||||
|
||||
@@ -16,7 +16,7 @@ export function ColorPalette({ value, onChange }: ColorPaletteProps) {
|
||||
<button
|
||||
key={color}
|
||||
type="button"
|
||||
onClick={() => onChange(color)}
|
||||
onClick={() => onChange(value === color ? "" : color)}
|
||||
className={cn(
|
||||
"h-8 w-8 rounded-full transition-all",
|
||||
value === color
|
||||
|
||||
@@ -13,8 +13,8 @@ interface CreatePlayerModalProps {
|
||||
name: string,
|
||||
ac: number,
|
||||
maxHp: number,
|
||||
color: string,
|
||||
icon: string,
|
||||
color: string | undefined,
|
||||
icon: string | undefined,
|
||||
) => void;
|
||||
playerCharacter?: PlayerCharacter;
|
||||
}
|
||||
@@ -40,14 +40,14 @@ export function CreatePlayerModal({
|
||||
setName(playerCharacter.name);
|
||||
setAc(String(playerCharacter.ac));
|
||||
setMaxHp(String(playerCharacter.maxHp));
|
||||
setColor(playerCharacter.color);
|
||||
setIcon(playerCharacter.icon);
|
||||
setColor(playerCharacter.color ?? "");
|
||||
setIcon(playerCharacter.icon ?? "");
|
||||
} else {
|
||||
setName("");
|
||||
setAc("10");
|
||||
setMaxHp("10");
|
||||
setColor("blue");
|
||||
setIcon("sword");
|
||||
setColor("");
|
||||
setIcon("");
|
||||
}
|
||||
setError("");
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export function CreatePlayerModal({
|
||||
setError("Max HP must be at least 1");
|
||||
return;
|
||||
}
|
||||
onSave(trimmed, acNum, hpNum, color, icon);
|
||||
onSave(trimmed, acNum, hpNum, color || undefined, icon || undefined);
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export function IconGrid({ value, onChange }: IconGridProps) {
|
||||
<button
|
||||
key={iconId}
|
||||
type="button"
|
||||
onClick={() => onChange(iconId)}
|
||||
onClick={() => onChange(value === iconId ? "" : iconId)}
|
||||
className={cn(
|
||||
"flex h-9 w-9 items-center justify-center rounded-md transition-all",
|
||||
value === iconId
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
PlayerCharacter,
|
||||
PlayerCharacterId,
|
||||
PlayerIcon,
|
||||
} from "@initiative/domain";
|
||||
import type { PlayerCharacter, PlayerCharacterId } from "@initiative/domain";
|
||||
import { Pencil, Plus, Trash2, X } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { PLAYER_COLOR_HEX, PLAYER_ICON_MAP } from "./player-icon-map";
|
||||
@@ -73,9 +69,8 @@ export function PlayerManagement({
|
||||
) : (
|
||||
<div className="flex flex-col gap-1">
|
||||
{characters.map((pc) => {
|
||||
const Icon = PLAYER_ICON_MAP[pc.icon as PlayerIcon];
|
||||
const color =
|
||||
PLAYER_COLOR_HEX[pc.color as keyof typeof PLAYER_COLOR_HEX];
|
||||
const Icon = pc.icon ? PLAYER_ICON_MAP[pc.icon] : undefined;
|
||||
const color = pc.color ? PLAYER_COLOR_HEX[pc.color] : undefined;
|
||||
return (
|
||||
<div
|
||||
key={pc.id}
|
||||
|
||||
Reference in New Issue
Block a user