2025-05-28 15:36:51 -07:00

81 lines
2.3 KiB
TypeScript

import { orderBy } from "lodash";
import { FC, HTMLAttributes, useMemo, useState } from "react";
import { DiceContract, DiceUtils } from "@dndbeyond/character-rules-engine";
import { Button } from "~/components/Button";
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
import { HpManageModal } from "../HpManageModal";
import styles from "./styles.module.css";
export interface HpSummaryProps extends HTMLAttributes<HTMLDivElement> {}
export const HpSummary: FC<HpSummaryProps> = ({ className, ...props }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { hpInfo } = useCharacterEngine();
const openModal = () => {
setIsModalOpen(true);
};
const closeModal = () => {
setIsModalOpen(false);
};
const hitDiceStrings = useMemo(() => {
const hitDice = hpInfo.classesHitDice.map(
(classHitDice) => classHitDice.dice
);
const combinedHitDice: Array<DiceContract> = [];
hitDice.forEach((hitDie) => {
const existingDieIdx = combinedHitDice.findIndex(
(hitDice) => hitDice.diceValue === hitDie.diceValue
);
if (existingDieIdx > -1) {
let existingDie = combinedHitDice[existingDieIdx];
combinedHitDice[existingDieIdx] = {
...combinedHitDice[existingDieIdx],
diceCount:
(existingDie.diceCount !== null ? existingDie.diceCount : 0) +
(hitDie.diceCount !== null ? hitDie.diceCount : 0),
};
} else {
combinedHitDice.push(hitDie);
}
});
return orderBy(combinedHitDice, ["diceValue"]).map((hitDie) =>
DiceUtils.renderDie(hitDie)
);
}, [hpInfo]);
return (
<div {...props}>
<div className={styles.hpSummaryContent}>
<div>
<p>
<span className={styles.label}>Max Hit Points:</span>
<span data-testid="hit-point-max">{hpInfo.totalHp}</span>
</p>
<p>
<span className={styles.label}>Hit Dice:</span>
<span data-testid="hit-point-dice">
{hitDiceStrings.join(" + ")}
</span>
</p>
</div>
<Button
className={styles.button}
variant="builder"
size="x-small"
onClick={openModal}
>
Manage HP
</Button>
</div>
<HpManageModal open={isModalOpen} onClose={closeModal} />
</div>
);
};