32 lines
1.5 KiB
TypeScript
32 lines
1.5 KiB
TypeScript
import { useCombatStore } from "../game/combatStore";
|
|
|
|
export function SummonFrames({ now }: { readonly now: number }) {
|
|
const summons = useCombatStore((state) => state.summons);
|
|
const selectedFriendlyActorId = useCombatStore((state) => state.selectedFriendlyActorId);
|
|
const selectFriendlyActor = useCombatStore((state) => state.selectFriendlyActor);
|
|
if (!summons.length) return null;
|
|
return (
|
|
<section className="summon-frames" aria-label="Pets, totems, and summons">
|
|
{summons.map((summon) => {
|
|
const healthPercent = summon.maxHealth > 0 ? Math.max(0, Math.min(100, summon.health / summon.maxHealth * 100)) : 0;
|
|
const remaining = summon.expiresAt >= Number.MAX_SAFE_INTEGER
|
|
? "∞"
|
|
: `${Math.max(0, Math.ceil((summon.expiresAt - now) / 1_000))}s`;
|
|
return (
|
|
<button
|
|
type="button"
|
|
className={`summon-frame summon-frame--${summon.kind}${selectedFriendlyActorId === summon.id ? " is-selected" : ""}`}
|
|
key={summon.id}
|
|
aria-pressed={selectedFriendlyActorId === summon.id}
|
|
onClick={() => selectFriendlyActor(selectedFriendlyActorId === summon.id ? null : summon.id)}
|
|
>
|
|
<header><strong>{summon.name}</strong><span>{summon.kind} · {remaining}</span></header>
|
|
<div><i style={{ width: `${healthPercent}%` }} /></div>
|
|
<small>{Math.ceil(summon.health)} / {summon.maxHealth}</small>
|
|
</button>
|
|
);
|
|
})}
|
|
</section>
|
|
);
|
|
}
|