darkwatch/client/src/components/InfoPanel.tsx

196 lines
6.5 KiB
TypeScript

import { useRef, useEffect } from "react";
import type { Character } from "../types";
import TalentList from "./TalentList";
import styles from "./InfoPanel.module.css";
const CLASSES = ["Fighter", "Priest", "Thief", "Wizard"];
const ANCESTRIES = ["Human", "Elf", "Dwarf", "Halfling", "Goblin", "Half-Orc"];
const ALIGNMENTS = ["Lawful", "Neutral", "Chaotic"];
interface InfoPanelProps {
character: Character;
mode: "view" | "edit";
onUpdate: (id: number, data: Partial<Character>) => void;
onAddTalent: (
characterId: number,
data: {
name: string;
description: string;
effect?: Record<string, unknown>;
game_talent_id?: number | null;
},
) => void;
onRemoveTalent: (characterId: number, talentId: number) => void;
}
export default function InfoPanel({
character,
mode,
onUpdate,
onAddTalent,
onRemoveTalent,
}: InfoPanelProps) {
const debounceRef = useRef<ReturnType<typeof setTimeout>>();
useEffect(() => {
return () => {
if (debounceRef.current) clearTimeout(debounceRef.current);
};
}, []);
function handleField(field: string, value: string | number) {
if (typeof value === "string") {
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
onUpdate(character.id, { [field]: value });
}, 400);
} else {
onUpdate(character.id, { [field]: value });
}
}
return (
<div className={styles.panel}>
<TalentList
talents={character.talents}
onAdd={(data) => onAddTalent(character.id, data)}
onRemove={(id) => onRemoveTalent(character.id, id)}
mode={mode}
/>
<div className={styles.sectionTitle} style={{ marginTop: "0.75rem" }}>
Info
</div>
{mode === "view" ? (
<div className={styles.infoGrid}>
{character.background && (
<div className={styles.infoRow}>
<span className={styles.infoLabel}>Background</span>
<span className={styles.infoValue}>{character.background}</span>
</div>
)}
{character.deity && (
<div className={styles.infoRow}>
<span className={styles.infoLabel}>Deity</span>
<span className={styles.infoValue}>{character.deity}</span>
</div>
)}
{character.languages && (
<div className={styles.infoRow}>
<span className={styles.infoLabel}>Languages</span>
<span className={styles.infoValue}>{character.languages}</span>
</div>
)}
<div className={styles.infoRow}>
<span className={styles.infoLabel}>Alignment</span>
<span className={styles.infoValue}>{character.alignment}</span>
</div>
{character.notes && (
<>
<div className={styles.infoRow}>
<span className={styles.infoLabel}>Notes</span>
</div>
<div className={styles.notes}>{character.notes}</div>
</>
)}
</div>
) : (
<div className={styles.infoGrid}>
<div className={styles.editRow}>
<div className={styles.field}>
<label className={styles.fieldLabel}>Class</label>
<select
className={styles.editSelect}
value={character.class}
onChange={(e) => handleField("class", e.target.value)}
>
{CLASSES.map((c) => (
<option key={c} value={c}>
{c}
</option>
))}
</select>
</div>
<div className={styles.field}>
<label className={styles.fieldLabel}>Ancestry</label>
<select
className={styles.editSelect}
value={character.ancestry}
onChange={(e) => handleField("ancestry", e.target.value)}
>
{ANCESTRIES.map((a) => (
<option key={a} value={a}>
{a}
</option>
))}
</select>
</div>
</div>
<div className={styles.editRow}>
<div className={styles.field}>
<label className={styles.fieldLabel}>Level</label>
<input
className={styles.editField}
type="number"
min={0}
value={character.level}
onChange={(e) => handleField("level", Number(e.target.value))}
/>
</div>
<div className={styles.field}>
<label className={styles.fieldLabel}>Alignment</label>
<select
className={styles.editSelect}
value={character.alignment}
onChange={(e) => handleField("alignment", e.target.value)}
>
{ALIGNMENTS.map((a) => (
<option key={a} value={a}>
{a}
</option>
))}
</select>
</div>
</div>
<div className={styles.field}>
<label className={styles.fieldLabel}>Background</label>
<input
className={styles.editField}
value={character.background}
placeholder="Urchin..."
onChange={(e) => handleField("background", e.target.value)}
/>
</div>
<div className={styles.field}>
<label className={styles.fieldLabel}>Deity</label>
<input
className={styles.editField}
value={character.deity}
placeholder="None..."
onChange={(e) => handleField("deity", e.target.value)}
/>
</div>
<div className={styles.field}>
<label className={styles.fieldLabel}>Languages</label>
<input
className={styles.editField}
value={character.languages}
placeholder="Common, Elvish..."
onChange={(e) => handleField("languages", e.target.value)}
/>
</div>
<div className={styles.field}>
<label className={styles.fieldLabel}>Notes</label>
<textarea
className={styles.notesEdit}
value={character.notes}
onChange={(e) => handleField("notes", e.target.value)}
placeholder="Freeform notes..."
/>
</div>
</div>
)}
</div>
);
}