refactor: Split CharacterSheet.tsx into separate components

This commit is contained in:
2026-06-28 13:10:15 -04:00
parent c9252cfe66
commit dbf3126f29
9 changed files with 1475 additions and 1344 deletions

View File

@@ -0,0 +1,232 @@
import React from "react";
import { ClassEntry } from "../types";
import { autoResize } from "../utils";
interface ClassesPageProps {
isActive: boolean;
primaryClasses: ClassEntry[];
setPrimaryClasses: React.Dispatch<React.SetStateAction<ClassEntry[]>>;
otherClasses: ClassEntry[];
setOtherClasses: React.Dispatch<React.SetStateAction<ClassEntry[]>>;
heroicSkills: string;
onHeroicSkillsChange: (v: string) => void;
}
export default function ClassesPage({
isActive,
primaryClasses,
setPrimaryClasses,
otherClasses,
setOtherClasses,
heroicSkills,
onHeroicSkillsChange,
}: ClassesPageProps) {
return (
<div className={`page${isActive ? " active" : ""}`} id="page-classes">
<div className="grid-2" style={{ marginBottom: 20 }}>
<div className="section">
<div className="section-title">
<span className="icon"></span> Primary Classes (up to 3)
</div>
{primaryClasses.map((cls, idx) => (
<div key={idx} className="class-block">
<div className="class-header">
<input
type="text"
placeholder="Class name…"
value={cls.name || ""}
onChange={(e) =>
setPrimaryClasses((prev) =>
prev.map((c, i) => (i === idx ? { ...c, name: e.target.value } : c))
)
}
/>
<input
type="text"
placeholder="Free benefits…"
value={cls.benefits || ""}
onChange={(e) =>
setPrimaryClasses((prev) =>
prev.map((c, i) => (i === idx ? { ...c, benefits: e.target.value } : c))
)
}
/>
</div>
<div className="class-skills">
<textarea
placeholder="Skill information…"
value={cls.skills || ""}
ref={(el) => {
if (el) autoResize(el);
}}
onInput={(e) => autoResize(e.currentTarget)}
onChange={(e) =>
setPrimaryClasses((prev) =>
prev.map((c, i) => (i === idx ? { ...c, skills: e.target.value } : c))
)
}
/>
</div>
<div
style={{
padding: "6px 10px",
borderTop: "1px solid var(--border)",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<label
style={{ display: "flex", alignItems: "center", gap: "6px", fontSize: "0.85em" }}
>
Level
<input
type="number"
min={1}
max={10}
value={cls.level ?? 1}
onChange={(e) =>
setPrimaryClasses((prev) =>
prev.map((c, i) =>
i === idx ? { ...c, level: Number(e.target.value) } : c
)
)
}
style={{ width: "50px" }}
/>
</label>
<button
type="button"
className="spell-del-btn"
onClick={() =>
setPrimaryClasses((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
</div>
</div>
))}
<button
type="button"
className="add-btn"
disabled={primaryClasses.length >= 3}
onClick={() =>
setPrimaryClasses((prev) => [
...prev,
{ name: "", level: 1, benefits: "", skills: "" },
])
}
>
+ Add Primary Class
</button>
</div>
<div className="section">
<div className="section-title">
<span className="icon"></span> Other Classes (max 3 non-mastered)
</div>
{otherClasses.map((cls, idx) => (
<div key={idx} className="class-block">
<div className="class-header">
<input
type="text"
placeholder="Class name…"
value={cls.name || ""}
onChange={(e) =>
setOtherClasses((prev) =>
prev.map((c, i) => (i === idx ? { ...c, name: e.target.value } : c))
)
}
/>
<input
type="text"
placeholder="Free benefits…"
value={cls.benefits || ""}
onChange={(e) =>
setOtherClasses((prev) =>
prev.map((c, i) => (i === idx ? { ...c, benefits: e.target.value } : c))
)
}
/>
</div>
<div className="class-skills">
<textarea
placeholder="Skill information…"
value={cls.skills || ""}
onChange={(e) =>
setOtherClasses((prev) =>
prev.map((c, i) => (i === idx ? { ...c, skills: e.target.value } : c))
)
}
/>
</div>
<div
style={{
padding: "6px 10px",
borderTop: "1px solid var(--border)",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<label
style={{ display: "flex", alignItems: "center", gap: "6px", fontSize: "0.85em" }}
>
Level
<input
type="number"
min={1}
max={10}
value={cls.level ?? 1}
onChange={(e) =>
setOtherClasses((prev) =>
prev.map((c, i) =>
i === idx ? { ...c, level: Number(e.target.value) } : c
)
)
}
style={{ width: "50px" }}
/>
</label>
<button
type="button"
className="spell-del-btn"
onClick={() =>
setOtherClasses((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
</div>
</div>
))}
<button
type="button"
className="add-btn"
onClick={() =>
setOtherClasses((prev) => [
...prev,
{ name: "", level: 1, benefits: "", skills: "" },
])
}
>
+ Add Class
</button>
</div>
</div>
<div className="section">
<div className="section-title">
<span className="icon"></span> Heroic Skills
</div>
<textarea
value={heroicSkills}
onChange={(e) => onHeroicSkillsChange(e.target.value)}
placeholder="Record your heroic skill abilities here…"
style={{ minHeight: 100 }}
/>
</div>
</div>
);
}