Grabbed dndbeyond's source code
``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
import clsx from "clsx";
|
||||
import { FC, HTMLAttributes, useMemo, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import {
|
||||
characterActions,
|
||||
ClassUtils,
|
||||
Constants,
|
||||
DiceUtils,
|
||||
FormatUtils,
|
||||
HelperUtils,
|
||||
RuleDataUtils,
|
||||
} from "@dndbeyond/character-rules-engine";
|
||||
|
||||
import { ConfirmModal, ConfirmModalProps } from "~/components/ConfirmModal";
|
||||
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
|
||||
import {
|
||||
HP_BASE_MAX_VALUE,
|
||||
HP_BONUS_VALUE,
|
||||
HP_OVERRIDE_MAX_VALUE,
|
||||
} from "~/subApps/sheet/constants";
|
||||
import { FormInputField } from "~/tools/js/Shared/components/common/FormInputField";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
export interface HpManageModalProps
|
||||
extends Omit<ConfirmModalProps, "onConfirm"> {}
|
||||
|
||||
export const HpManageModal: FC<HpManageModalProps> = ({
|
||||
onClose,
|
||||
...props
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { hpInfo, preferences, ruleData } = useCharacterEngine();
|
||||
|
||||
const [baseHp, setBaseHp] = useState(hpInfo.baseHp);
|
||||
const [bonusHp, setBonusHp] = useState(hpInfo.bonusHp);
|
||||
const [overrideHp, setOverrideHp] = useState(hpInfo.overrideHp);
|
||||
|
||||
const reset = () => {
|
||||
setBaseHp(hpInfo.baseHp);
|
||||
setBonusHp(hpInfo.bonusHp);
|
||||
setOverrideHp(hpInfo.overrideHp);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
reset();
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
if (baseHp !== hpInfo.baseHp) {
|
||||
dispatch(characterActions.baseHitPointsSet(baseHp));
|
||||
}
|
||||
|
||||
if (bonusHp !== hpInfo.bonusHp) {
|
||||
dispatch(characterActions.bonusHitPointsSet(bonusHp));
|
||||
}
|
||||
|
||||
if (overrideHp !== hpInfo.overrideHp) {
|
||||
dispatch(characterActions.overrideHitPointsSet(overrideHp));
|
||||
}
|
||||
|
||||
onClose();
|
||||
reset();
|
||||
};
|
||||
|
||||
const transformBaseHp = (value: string): number => {
|
||||
let parsedNumber = HelperUtils.parseInputInt(
|
||||
value,
|
||||
RuleDataUtils.getMinimumHpTotal(ruleData)
|
||||
);
|
||||
return HelperUtils.clampInt(
|
||||
parsedNumber,
|
||||
RuleDataUtils.getMinimumHpTotal(ruleData),
|
||||
HP_BASE_MAX_VALUE
|
||||
);
|
||||
};
|
||||
|
||||
const transformBonusHp = (value: string): number | null => {
|
||||
let parsedNumber = HelperUtils.parseInputInt(value);
|
||||
if (parsedNumber === null) {
|
||||
return parsedNumber;
|
||||
}
|
||||
return HelperUtils.clampInt(
|
||||
parsedNumber,
|
||||
HP_BONUS_VALUE.MIN,
|
||||
HP_BONUS_VALUE.MAX
|
||||
);
|
||||
};
|
||||
|
||||
const transformOverrideHp = (value: string): number | null => {
|
||||
let parsedNumber = HelperUtils.parseInputInt(value, null);
|
||||
if (parsedNumber === null) {
|
||||
return parsedNumber;
|
||||
}
|
||||
return HelperUtils.clampInt(
|
||||
parsedNumber,
|
||||
RuleDataUtils.getMinimumHpTotal(ruleData)
|
||||
);
|
||||
};
|
||||
|
||||
const handleOverrideHpUpdate = (value: number | null): void => {
|
||||
const newOverride =
|
||||
value === null
|
||||
? null
|
||||
: HelperUtils.clampInt(
|
||||
value,
|
||||
RuleDataUtils.getMinimumHpTotal(ruleData),
|
||||
HP_OVERRIDE_MAX_VALUE
|
||||
);
|
||||
|
||||
setOverrideHp(newOverride);
|
||||
};
|
||||
|
||||
const totalHp = useMemo(() => {
|
||||
let totalHp: number =
|
||||
baseHp + hpInfo.totalHitPointSources + (bonusHp === null ? 0 : bonusHp);
|
||||
if (overrideHp !== null) {
|
||||
totalHp = overrideHp;
|
||||
}
|
||||
|
||||
return Math.max(RuleDataUtils.getMinimumHpTotal(ruleData), totalHp);
|
||||
}, [baseHp, bonusHp, overrideHp, hpInfo, ruleData]);
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
onClose={handleClose}
|
||||
heading="Manage Hit Points"
|
||||
onConfirm={onConfirm}
|
||||
confirmButtonText="Apply"
|
||||
useMobileFullScreen
|
||||
{...props}
|
||||
>
|
||||
{/* Total Max Hit Points */}
|
||||
<div className={styles.hpManageModal}>
|
||||
<p className={styles.total}>
|
||||
<span className={styles.totalLabel}>Maximum Hit Points</span>
|
||||
<span className={styles.totalValue}>{totalHp}</span>
|
||||
</p>
|
||||
|
||||
{/* Hit Point Controls */}
|
||||
<div className={styles.controls}>
|
||||
{preferences.hitPointType ===
|
||||
Constants.PreferenceHitPointTypeEnum.FIXED ? (
|
||||
<p className={styles.baseHp}>
|
||||
<span className={clsx([styles.baseHpLabel])}>Fixed HP</span>
|
||||
<span className={styles.baseHpValue}>{baseHp}</span>
|
||||
</p>
|
||||
) : (
|
||||
<FormInputField
|
||||
label="Rolled HP"
|
||||
initialValue={baseHp}
|
||||
type="number"
|
||||
onBlur={(value) => setBaseHp(value as number)}
|
||||
transformValueOnBlur={transformBaseHp}
|
||||
/>
|
||||
)}
|
||||
<FormInputField
|
||||
label="HP Modifier"
|
||||
initialValue={bonusHp}
|
||||
type="number"
|
||||
placeholder={"--"}
|
||||
onBlur={(value) => setBonusHp(value as number)}
|
||||
transformValueOnBlur={transformBonusHp}
|
||||
/>
|
||||
<FormInputField
|
||||
inputAttributes={
|
||||
{
|
||||
min: RuleDataUtils.getMinimumHpTotal(ruleData),
|
||||
max: HP_OVERRIDE_MAX_VALUE,
|
||||
} as HTMLAttributes<HTMLInputElement>
|
||||
}
|
||||
label="Override HP"
|
||||
initialValue={overrideHp}
|
||||
type="number"
|
||||
placeholder={"--"}
|
||||
onBlur={handleOverrideHpUpdate}
|
||||
transformValueOnBlur={transformOverrideHp}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Hit Point Bonus Sources */}
|
||||
{hpInfo.hitPointSources.length > 0 && (
|
||||
<div className={styles.hpSources}>
|
||||
<h3>Hit Point Bonuses</h3>
|
||||
{hpInfo.hitPointSources.map((hitPointSource, idx) => (
|
||||
<p key={`${idx}:${hitPointSource.source}`}>
|
||||
{FormatUtils.renderSignedNumber(hitPointSource.amount)} from{" "}
|
||||
{hitPointSource.source}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hit Dice & Potential Values */}
|
||||
<div className={styles.info}>
|
||||
<div className={styles.infoGroup}>
|
||||
<h3>Hit Dice</h3>
|
||||
{hpInfo.classesHitDice.map((classHitDice) => (
|
||||
<div key={ClassUtils.getId(classHitDice.charClass)}>
|
||||
<span className={styles.infoLabel}>
|
||||
{ClassUtils.getName(classHitDice.charClass)}:
|
||||
</span>
|
||||
<span>{DiceUtils.renderDie(classHitDice.dice)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.infoGroup}>
|
||||
<h3>Potential Values</h3>
|
||||
<div>
|
||||
<span className={styles.infoLabel}>Total Fixed Value HP:</span>
|
||||
<span>{hpInfo.totalFixedValueHp}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.infoLabel}>Total Average HP:</span>
|
||||
<span>{hpInfo.totalAverageHp}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.infoLabel}>Total Possible HP:</span>
|
||||
<span>{hpInfo.possibleMaxHitPoints}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* HP Help */}
|
||||
<div className={styles.help}>
|
||||
<h3>Max Hit Points</h3>
|
||||
<p>
|
||||
Your hit point maximum is determined by the number you roll for your
|
||||
hit dice each level or a fixed value determined by your hit dice and
|
||||
your Constitution modifier
|
||||
</p>
|
||||
|
||||
<h3>Bonus Hit Points</h3>
|
||||
<p>
|
||||
Use this field to record any miscellaneous bonus hit points you want
|
||||
to add to your normal hit point maximum. These hit points are
|
||||
different from temporary hit points, which you can add on your
|
||||
character sheet during play.
|
||||
</p>
|
||||
|
||||
<h3>Override Hit Points</h3>
|
||||
<p>
|
||||
Use this field to override your typical hit point maximum. The
|
||||
number you enter here will display as your hit point maximum on your
|
||||
character sheet.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user