chore: Fix useButtonType linter errors

This commit is contained in:
2026-06-28 12:39:34 -04:00
parent ce1f0fee41
commit b2bbad9c58

View File

@@ -329,7 +329,12 @@ export default function CharacterSheet() {
b: c.benefits, b: c.benefits,
s: c.skills, s: c.skills,
})), })),
oc: otherClasses.map((c) => ({ n: c.name, lv: c.level, b: c.benefits, s: c.skills })), oc: otherClasses.map((c) => ({
n: c.name,
lv: c.level,
b: c.benefits,
s: c.skills,
})),
sp: spells.map((s) => ({ sp: spells.map((s) => ({
n: s.name, n: s.name,
cl: s.spellClass, cl: s.spellClass,
@@ -612,7 +617,7 @@ export default function CharacterSheet() {
<div className="logo">Fabula Ultima</div> <div className="logo">Fabula Ultima</div>
<div className="tabs"> <div className="tabs">
{["main", "classes", "spells", "manage"].map((tab, i) => ( {["main", "classes", "spells", "manage"].map((tab, i) => (
<button <button type="button"
key={tab} key={tab}
className={`tab${activeTab === tab ? " active" : ""}`} className={`tab${activeTab === tab ? " active" : ""}`}
onClick={() => setActiveTab(tab)} onClick={() => setActiveTab(tab)}
@@ -625,10 +630,10 @@ export default function CharacterSheet() {
<span className={`save-status${saveStatus ? " show" : ""}`}> <span className={`save-status${saveStatus ? " show" : ""}`}>
Saved! Saved!
</span> </span>
<button className="btn-print" onClick={() => window.print()}> <button type="button" className="btn-print" onClick={() => window.print()}>
Print Print
</button> </button>
<button <button type="button"
className="btn-theme" className="btn-theme"
onClick={() => setTheme((t) => (t === "light" ? "dark" : "light"))} onClick={() => setTheme((t) => (t === "light" ? "dark" : "light"))}
> >
@@ -725,7 +730,7 @@ export default function CharacterSheet() {
<div className="level-display"> <div className="level-display">
<span className="level-num">{level}</span> <span className="level-num">{level}</span>
<span className="level-text">Character Level</span> <span className="level-text">Character Level</span>
<button <button type="button"
className="add-btn" className="add-btn"
style={{ style={{
marginTop: 10, marginTop: 10,
@@ -736,7 +741,7 @@ export default function CharacterSheet() {
> >
+ Level Up + Level Up
</button> </button>
<button <button type="button"
className="add-btn" className="add-btn"
style={{ style={{
marginTop: 4, marginTop: 4,
@@ -821,12 +826,14 @@ export default function CharacterSheet() {
<span className="icon"></span> Attributes <span className="icon"></span> Attributes
</div> </div>
<div className="attr-grid"> <div className="attr-grid">
{([ {(
{ label: "Dexterity", base: "dexBase", cur: "dexCur" }, [
{ label: "Insight", base: "insBase", cur: "insCur" }, { label: "Dexterity", base: "dexBase", cur: "dexCur" },
{ label: "Might", base: "migBase", cur: "migCur" }, { label: "Insight", base: "insBase", cur: "insCur" },
{ label: "Willpower", base: "wlpBase", cur: "wlpCur" }, { label: "Might", base: "migBase", cur: "migCur" },
] as const).map(({ label, base, cur }) => ( { label: "Willpower", base: "wlpBase", cur: "wlpCur" },
] as const
).map(({ label, base, cur }) => (
<div key={label} className="attr-block"> <div key={label} className="attr-block">
<div className="attr-name">{label}</div> <div className="attr-name">{label}</div>
<div className="attr-inputs"> <div className="attr-inputs">
@@ -901,7 +908,7 @@ export default function CharacterSheet() {
{inCrisis && <div className="crisis-badge">CRISIS</div>} {inCrisis && <div className="crisis-badge">CRISIS</div>}
</div> </div>
</div> </div>
<button <button type="button"
className="add-btn" className="add-btn"
style={{ padding: "4px 8px" }} style={{ padding: "4px 8px" }}
onClick={calcHP} onClick={calcHP}
@@ -931,7 +938,7 @@ export default function CharacterSheet() {
/> />
</div> </div>
</div> </div>
<button <button type="button"
className="add-btn" className="add-btn"
style={{ padding: "4px 8px" }} style={{ padding: "4px 8px" }}
onClick={calcMP} onClick={calcMP}
@@ -1099,36 +1106,38 @@ export default function CharacterSheet() {
))} ))}
</div> </div>
<div style={{ marginTop: 14 }}> <div style={{ marginTop: 14 }}>
{([ {(
{ [
slot: "Accessory", {
name: "accName", slot: "Accessory",
desc: "accDesc", name: "accName",
namePh: "Item name", desc: "accDesc",
descPh: "Description / effect", namePh: "Item name",
}, descPh: "Description / effect",
{ },
slot: "Armor", {
name: "armName", slot: "Armor",
desc: "armDesc", name: "armName",
namePh: "Item name", desc: "armDesc",
descPh: "Defense bonus / effect", namePh: "Item name",
}, descPh: "Defense bonus / effect",
{ },
slot: "Main Hand", {
name: "mhName", slot: "Main Hand",
desc: "mhDesc", name: "mhName",
namePh: "Weapon name", desc: "mhDesc",
descPh: "Damage / effect", namePh: "Weapon name",
}, descPh: "Damage / effect",
{ },
slot: "Off-Hand", {
name: "ohName", slot: "Off-Hand",
desc: "ohDesc", name: "ohName",
namePh: "Weapon / shield", desc: "ohDesc",
descPh: "Damage / effect", namePh: "Weapon / shield",
}, descPh: "Damage / effect",
] as const).map(({ slot, name, desc, namePh, descPh }) => ( },
] as const
).map(({ slot, name, desc, namePh, descPh }) => (
<div key={slot} className="equip-row"> <div key={slot} className="equip-row">
<div className="equip-slot">{slot}</div> <div className="equip-slot">{slot}</div>
<div className="equip-fields"> <div className="equip-fields">
@@ -1160,75 +1169,128 @@ export default function CharacterSheet() {
placeholder="Items, notes, lore…" placeholder="Items, notes, lore…"
style={{ minHeight: 200 }} style={{ minHeight: 200 }}
/> />
<button <button type="button"
className="add-btn" className="add-btn"
style={{ marginTop: 8 }} style={{ marginTop: 8 }}
onClick={() => { setWeaponCategory("all"); setWeaponPickerOpen(true); }} onClick={() => {
setWeaponCategory("all");
setWeaponPickerOpen(true);
}}
> >
+ Add Equipment + Add Equipment
</button> </button>
{weaponPickerOpen && (() => { {weaponPickerOpen &&
const allWeapons = (weaponsFile as WeaponsFile).weapons; (() => {
const allArmorShields = (armorShieldsFile as ArmorShieldsFile).armor_shields; const allWeapons = (weaponsFile as WeaponsFile).weapons;
type PickerItem = const allArmorShields = (armorShieldsFile as ArmorShieldsFile)
| { kind: "weapon"; data: WeaponTemplate } .armor_shields;
| { kind: "armor"; data: ArmorShieldTemplate }; type PickerItem =
const allItems: PickerItem[] = [ | { kind: "weapon"; data: WeaponTemplate }
...allWeapons.map(w => ({ kind: "weapon" as const, data: w })), | { kind: "armor"; data: ArmorShieldTemplate };
...allArmorShields.map(a => ({ kind: "armor" as const, data: a })), const allItems: PickerItem[] = [
]; ...allWeapons.map((w) => ({
const categories = ["all", ...Array.from(new Set(allItems.map(i => i.data.category))).sort()]; kind: "weapon" as const,
const visible = weaponCategory === "all" ? allItems : allItems.filter(i => i.data.category === weaponCategory); data: w,
const formatLine = (item: PickerItem) => { })),
if (item.kind === "weapon") { ...allArmorShields.map((a) => ({
const w = item.data; kind: "armor" as const,
return `${w.name}: Acc ${w.accuracy}, Dmg ${w.damage}${w.description ? ` | ${w.description}` : ""}${w.cost > 0 ? ` (${w.cost}z)` : ""}`; data: a,
} else { })),
const a = item.data; ];
const init = a.initiative ?? a.initative ?? 0; const categories = [
return `${a.name}: DEF ${a.defense}, MDEF ${a.magic_defense}, Init ${init}${a.description ? ` | ${a.description}` : ""}${a.cost > 0 ? ` (${a.cost}z)` : ""}`; "all",
} ...Array.from(
}; new Set(allItems.map((i) => i.data.category)),
return ( ).sort(),
<div className="spell-picker-overlay" onClick={() => setWeaponPickerOpen(false)}> ];
<div className="spell-picker-modal" onClick={e => e.stopPropagation()}> const visible =
<div className="spell-picker-header"> weaponCategory === "all"
<span>Choose equipment</span> ? allItems
<button className="spell-picker-close" onClick={() => setWeaponPickerOpen(false)}></button> : allItems.filter(
</div> (i) => i.data.category === weaponCategory,
<div style={{ display: "flex", gap: 6, flexWrap: "wrap", padding: "8px 14px", borderBottom: "1px solid var(--border)" }}> );
{categories.map(cat => ( const formatLine = (item: PickerItem) => {
<button if (item.kind === "weapon") {
key={cat} const w = item.data;
className={`add-btn${weaponCategory === cat ? " active-filter" : ""}`} return `${w.name}: Acc ${w.accuracy}, Dmg ${w.damage}${w.description ? ` | ${w.description}` : ""}${w.cost > 0 ? ` (${w.cost}z)` : ""}`;
style={{ padding: "2px 8px", fontSize: "0.65rem", textTransform: "capitalize" }} } else {
onClick={() => setWeaponCategory(cat)} const a = item.data;
const init = a.initiative ?? a.initative ?? 0;
return `${a.name}: DEF ${a.defense}, MDEF ${a.magic_defense}, Init ${init}${a.description ? ` | ${a.description}` : ""}${a.cost > 0 ? ` (${a.cost}z)` : ""}`;
}
};
return (
<div
className="spell-picker-overlay"
onClick={() => setWeaponPickerOpen(false)}
>
<div
className="spell-picker-modal"
onClick={(e) => e.stopPropagation()}
>
<div className="spell-picker-header">
<span>Choose equipment</span>
<button type="button"
className="spell-picker-close"
onClick={() => setWeaponPickerOpen(false)}
> >
{cat}
</button> </button>
))} </div>
<div
style={{
display: "flex",
gap: 6,
flexWrap: "wrap",
padding: "8px 14px",
borderBottom: "1px solid var(--border)",
}}
>
{categories.map((cat) => (
<button type="button"
key={cat}
className={`add-btn${weaponCategory === cat ? " active-filter" : ""}`}
style={{
padding: "2px 8px",
fontSize: "0.65rem",
textTransform: "capitalize",
}}
onClick={() => setWeaponCategory(cat)}
>
{cat}
</button>
))}
</div>
<ul className="spell-picker-list">
{visible.map((item, i) => (
<li
key={i}
className="spell-picker-item"
onClick={() => {
const line = formatLine(item);
f(
"backpack",
fields.backpack
? fields.backpack + "\n" + line
: line,
);
setWeaponPickerOpen(false);
}}
>
<span className="spell-picker-name">
{item.data.name}
</span>
<span className="spell-picker-class">
{item.data.category}
</span>
</li>
))}
</ul>
</div> </div>
<ul className="spell-picker-list">
{visible.map((item, i) => (
<li
key={i}
className="spell-picker-item"
onClick={() => {
const line = formatLine(item);
f("backpack", fields.backpack ? fields.backpack + "\n" + line : line);
setWeaponPickerOpen(false);
}}
>
<span className="spell-picker-name">{item.data.name}</span>
<span className="spell-picker-class">{item.data.category}</span>
</li>
))}
</ul>
</div> </div>
</div> );
); })()}
})()}
</div> </div>
</div> </div>
</div> </div>
@@ -1275,7 +1337,9 @@ export default function CharacterSheet() {
<textarea <textarea
placeholder="Skill information…" placeholder="Skill information…"
value={cls.skills || ""} value={cls.skills || ""}
ref={(el) => { if (el) autoResize(el); }} ref={(el) => {
if (el) autoResize(el);
}}
onInput={(e) => autoResize(e.currentTarget)} onInput={(e) => autoResize(e.currentTarget)}
onChange={(e) => onChange={(e) =>
setPrimaryClasses((prev) => setPrimaryClasses((prev) =>
@@ -1295,7 +1359,14 @@ export default function CharacterSheet() {
justifyContent: "space-between", justifyContent: "space-between",
}} }}
> >
<label style={{ display: "flex", alignItems: "center", gap: "6px", fontSize: "0.85em" }}> <label
style={{
display: "flex",
alignItems: "center",
gap: "6px",
fontSize: "0.85em",
}}
>
Level Level
<input <input
type="number" type="number"
@@ -1305,14 +1376,16 @@ export default function CharacterSheet() {
onChange={(e) => onChange={(e) =>
setPrimaryClasses((prev) => setPrimaryClasses((prev) =>
prev.map((c, i) => prev.map((c, i) =>
i === idx ? { ...c, level: Number(e.target.value) } : c, i === idx
? { ...c, level: Number(e.target.value) }
: c,
), ),
) )
} }
style={{ width: "50px" }} style={{ width: "50px" }}
/> />
</label> </label>
<button <button type="button"
className="spell-del-btn" className="spell-del-btn"
onClick={() => onClick={() =>
setPrimaryClasses((prev) => setPrimaryClasses((prev) =>
@@ -1325,7 +1398,7 @@ export default function CharacterSheet() {
</div> </div>
</div> </div>
))} ))}
<button <button type="button"
className="add-btn" className="add-btn"
disabled={primaryClasses.length >= 3} disabled={primaryClasses.length >= 3}
onClick={() => onClick={() =>
@@ -1393,7 +1466,14 @@ export default function CharacterSheet() {
justifyContent: "space-between", justifyContent: "space-between",
}} }}
> >
<label style={{ display: "flex", alignItems: "center", gap: "6px", fontSize: "0.85em" }}> <label
style={{
display: "flex",
alignItems: "center",
gap: "6px",
fontSize: "0.85em",
}}
>
Level Level
<input <input
type="number" type="number"
@@ -1403,14 +1483,16 @@ export default function CharacterSheet() {
onChange={(e) => onChange={(e) =>
setOtherClasses((prev) => setOtherClasses((prev) =>
prev.map((c, i) => prev.map((c, i) =>
i === idx ? { ...c, level: Number(e.target.value) } : c, i === idx
? { ...c, level: Number(e.target.value) }
: c,
), ),
) )
} }
style={{ width: "50px" }} style={{ width: "50px" }}
/> />
</label> </label>
<button <button type="button"
className="spell-del-btn" className="spell-del-btn"
onClick={() => onClick={() =>
setOtherClasses((prev) => setOtherClasses((prev) =>
@@ -1423,7 +1505,7 @@ export default function CharacterSheet() {
</div> </div>
</div> </div>
))} ))}
<button <button type="button"
className="add-btn" className="add-btn"
onClick={() => onClick={() =>
setOtherClasses((prev) => [ setOtherClasses((prev) => [
@@ -1496,7 +1578,9 @@ export default function CharacterSheet() {
onChange={(e) => onChange={(e) =>
setSpells((prev) => setSpells((prev) =>
prev.map((sp, j) => prev.map((sp, j) =>
j === i ? { ...sp, spellClass: e.target.value } : sp, j === i
? { ...sp, spellClass: e.target.value }
: sp,
), ),
) )
} }
@@ -1538,14 +1622,16 @@ export default function CharacterSheet() {
onChange={(e) => onChange={(e) =>
setSpells((prev) => setSpells((prev) =>
prev.map((sp, j) => prev.map((sp, j) =>
j === i ? { ...sp, duration: e.target.value } : sp, j === i
? { ...sp, duration: e.target.value }
: sp,
), ),
) )
} }
/> />
</td> </td>
<td className="spell-del-col"> <td className="spell-del-col">
<button <button type="button"
className="spell-del-btn" className="spell-del-btn"
onClick={() => onClick={() =>
setSpells((prev) => prev.filter((_, j) => j !== i)) setSpells((prev) => prev.filter((_, j) => j !== i))
@@ -1560,7 +1646,9 @@ export default function CharacterSheet() {
<textarea <textarea
placeholder="Notes / effect description…" placeholder="Notes / effect description…"
value={s.notes || ""} value={s.notes || ""}
ref={(el) => { if (el) autoResize(el); }} ref={(el) => {
if (el) autoResize(el);
}}
onInput={(e) => autoResize(e.currentTarget)} onInput={(e) => autoResize(e.currentTarget)}
onChange={(e) => onChange={(e) =>
setSpells((prev) => setSpells((prev) =>
@@ -1577,18 +1665,25 @@ export default function CharacterSheet() {
</tbody> </tbody>
</table> </table>
<div style={{ display: "flex", gap: 8, marginTop: 10 }}> <div style={{ display: "flex", gap: 8, marginTop: 10 }}>
<button <button type="button"
className="add-btn" className="add-btn"
onClick={() => onClick={() =>
setSpells((prev) => [ setSpells((prev) => [
...prev, ...prev,
{ name: "", spellClass: "", notes: "", mp: "", targets: "", duration: "" }, {
name: "",
spellClass: "",
notes: "",
mp: "",
targets: "",
duration: "",
},
]) ])
} }
> >
+ Add Spell / Arcana + Add Spell / Arcana
</button> </button>
<button <button type="button"
className="add-btn" className="add-btn"
onClick={() => setSpellPickerOpen(true)} onClick={() => setSpellPickerOpen(true)}
> >
@@ -1597,11 +1692,22 @@ export default function CharacterSheet() {
</div> </div>
{spellPickerOpen && ( {spellPickerOpen && (
<div className="spell-picker-overlay" onClick={() => setSpellPickerOpen(false)}> <div
<div className="spell-picker-modal" onClick={(e) => e.stopPropagation()}> className="spell-picker-overlay"
onClick={() => setSpellPickerOpen(false)}
>
<div
className="spell-picker-modal"
onClick={(e) => e.stopPropagation()}
>
<div className="spell-picker-header"> <div className="spell-picker-header">
<span>Choose a spell</span> <span>Choose a spell</span>
<button className="spell-picker-close" onClick={() => setSpellPickerOpen(false)}></button> <button type="button"
className="spell-picker-close"
onClick={() => setSpellPickerOpen(false)}
>
</button>
</div> </div>
<ul className="spell-picker-list"> <ul className="spell-picker-list">
{(spellsFile as SpellsFile).spells.map((t, i) => ( {(spellsFile as SpellsFile).spells.map((t, i) => (
@@ -1673,10 +1779,10 @@ export default function CharacterSheet() {
a previously saved sheet. a previously saved sheet.
</p> </p>
<div className="manage-btn-row"> <div className="manage-btn-row">
<button className="btn-save btn-lg" onClick={saveSheet}> <button type="button" className="btn-save btn-lg" onClick={saveSheet}>
✦ Save to Browser ✦ Save to Browser
</button> </button>
<button className="btn-load btn-lg" onClick={loadSheet}> <button type="button" className="btn-load btn-lg" onClick={loadSheet}>
↑ Load from Browser ↑ Load from Browser
</button> </button>
</div> </div>
@@ -1691,13 +1797,13 @@ export default function CharacterSheet() {
import from a previously exported file. import from a previously exported file.
</p> </p>
<div className="manage-btn-row"> <div className="manage-btn-row">
<button <button type="button"
className="btn-save btn-export btn-lg" className="btn-save btn-export btn-lg"
onClick={exportSheet} onClick={exportSheet}
> >
↓ Export JSON ↓ Export JSON
</button> </button>
<button <button type="button"
className="btn-load btn-import btn-lg" className="btn-load btn-import btn-lg"
onClick={() => { onClick={() => {
if (!importFileRef.current) return; if (!importFileRef.current) return;
@@ -1727,7 +1833,7 @@ export default function CharacterSheet() {
disabled for viewers. disabled for viewers.
</p> </p>
<div className="manage-btn-row"> <div className="manage-btn-row">
<button <button type="button"
className="btn-save btn-export btn-lg" className="btn-save btn-export btn-lg"
onClick={copyShareURL} onClick={copyShareURL}
> >