New source found from dndbeyond.com

This commit is contained in:
2026-07-08 01:00:09 -07:00
parent 9a983a6d7b
commit dcefa0d2f2
2865 changed files with 222467 additions and 49053 deletions
@@ -1,4 +1,5 @@
import React, { useCallback, useContext, useEffect, useMemo } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Tooltip } from "@dndbeyond/character-common-components/es";
import {
DiceComponentUtils,
@@ -19,19 +20,16 @@ import {
Spell,
LeveledSpellManager,
} from "@dndbeyond/character-rules-engine/es";
import { RollTypes } from "@dndbeyond/pocket-dimension-dice/constants";
import {
Dice,
DiceEvent,
DiceTools,
IRollContext,
RollRequest,
RollType,
} from "@dndbeyond/dice";
import { GameLogContext } from "@dndbeyond/game-log-components";
RollContext,
RollDicePayload,
} from "@dndbeyond/pocket-dimension-dice/types";
import { ItemName } from "~/components/ItemName";
import { NumberDisplay } from "~/components/NumberDisplay";
import { SpellName } from "~/components/SpellName";
import { TypeScriptUtils } from "../../utils";
import SpellSlotChooser from "../SpellSlotChooser";
import { ThemeButton } from "../common/Button";
@@ -61,7 +59,7 @@ interface Props {
diceEnabled: boolean;
theme: CharacterTheme;
proficiencyBonus: number;
rollContext: IRollContext;
rollContext: RollContext;
}
function SpellsSpell({
@@ -89,9 +87,9 @@ function SpellsSpell({
() => new LeveledSpellManager({ spell: spellData }),
[spellData]
);
const [isSlotChooserOpen, setIsSlotChooserOpen] = React.useState(false);
const [isCriticalHit, setIsCriticalHit] = React.useState(false);
const spellsSpellNode = React.useRef<HTMLDivElement>(null);
const [isSlotChooserOpen, setIsSlotChooserOpen] = useState(false);
const [isCriticalHit, setIsCriticalHit] = useState(false);
const spellsSpellNode = useRef<HTMLDivElement>(null);
const handleSpellClick = (evt: React.MouseEvent): void => {
evt.nativeEvent.stopImmediatePropagation();
@@ -127,22 +125,11 @@ function SpellsSpell({
setIsSlotChooserOpen(false);
};
const handleRollResults = (result: RollRequest): void => {
const handleRollResults = (result: RollDicePayload["data"]): void => {
let wasCrit = DiceComponentUtils.isCriticalRoll(result);
setIsCriticalHit(wasCrit);
};
const diceEventHandler = useMemo(() => {
if (!spellsSpellNode.current) {
return () => {
/* NOOP */
};
}
return DiceComponentUtils.setupResetCritStateOnRoll(
spell.getName(),
spellsSpellNode.current
);
}, [spell]);
// TODO: use mui click away listener
const handleDocumentClick = useCallback((): void => {
handleSpellSlotChooserClose();
@@ -154,15 +141,11 @@ function SpellsSpell({
if (isSlotChooserOpen) {
document.removeEventListener("click", handleDocumentClick);
}
Dice.removeEventListener(DiceEvent.ROLL, diceEventHandler);
},
[isSlotChooserOpen, handleDocumentClick, diceEventHandler]
[isSlotChooserOpen, handleDocumentClick]
);
// Todo: consider making a separate component for this
const [{ messageTargetOptions, defaultMessageTargetOption, userId }] =
useContext(GameLogContext);
const renderAttackInfo = (): React.ReactNode => {
let toHit: number | null = null;
let saveDcValue: number | null = null;
@@ -182,27 +165,18 @@ function SpellsSpell({
return (
<div className="ct-spells-spell__tohit">
<DigitalDiceWrapper
diceNotation={DiceTools.CustomD20(toHit)}
rollType={RollType.ToHit}
rollAction={spell.getName()}
diceEnabled={diceEnabled}
themeColor={theme.themeColor}
onRollResults={handleRollResults}
rollContext={rollContext}
rollTargetOptions={
messageTargetOptions
? Object.values(messageTargetOptions.entities).filter(
TypeScriptUtils.isNotNullOrUndefined
)
: undefined
}
rollTargetDefault={defaultMessageTargetOption}
userId={Number(userId) || 0}
diceNotation={`1d20${
toHit > 0 ? `+${toHit}` : toHit !== 0 ? toHit : ""
}`}
rollType={RollTypes.ToHit}
action={spell.getName()}
isDiceEnabled={diceEnabled}
rollCallback={handleRollResults}
entityId={String(rollContext.entityId)}
entityType={String(rollContext.entityType)}
name={String(rollContext.name)}
>
<NumberDisplay
number={toHit}
type="signed"
/>
<NumberDisplay number={toHit} type="signed" />
</DigitalDiceWrapper>
</div>
);
@@ -312,7 +286,7 @@ function SpellsSpell({
actionNode = <span className="ct-spells-spell__at-will">At Will</span>;
} else {
actionNode = (
<React.Fragment>
<>
<ThemeButton
size={"small"}
onClick={
@@ -337,7 +311,7 @@ function SpellsSpell({
doesPactSlotExist={doesPactSlotExist}
/>
)}
</React.Fragment>
</>
);
}
let classNames: Array<string> = ["ct-spells-spell", className];
@@ -431,6 +405,11 @@ function SpellsSpell({
theme={theme}
isCriticalHit={isCriticalHit}
rollContext={rollContext}
rollCallback={() => {
if (isCriticalHit) {
setIsCriticalHit(false);
}
}}
/>
</div>
{showNotes && (