Files
dndbeyond_src/ddb_main/tools/js/smartComponents/AttackTable/AttackTable.tsx
T

163 lines
4.4 KiB
TypeScript

import clsx from "clsx";
import { useMemo } from "react";
import {
AbilityLookup,
Action,
Attack,
CharacterTheme,
Constants,
DataOriginRefData,
Item,
ItemPlan,
RuleData,
Spell,
SpellCasterInfo,
WeaponSpellDamageGroup,
} from "@dndbeyond/character-rules-engine/es";
import { RollContext } from "@dndbeyond/pocket-dimension-dice/types";
import CombatActionAttack from "../CombatAttack/CombatActionAttack";
import CombatItemAttack from "../CombatAttack/CombatItemAttack";
import CombatSpellAttack from "../CombatAttack/CombatSpellAttack";
import styles from "./styles.module.css";
interface AttackTableProps extends React.HTMLAttributes<HTMLElement> {
theme: CharacterTheme;
showNotes?: boolean;
diceEnabled?: boolean;
attacks: Array<Attack>;
weaponSpellDamageGroups: Array<WeaponSpellDamageGroup>;
abilityLookup: AbilityLookup;
ruleData: RuleData;
spellCasterInfo: SpellCasterInfo;
onAttackClick?: (attack: Attack) => void;
rollContext: RollContext;
dataOriginRefData: DataOriginRefData;
proficiencyBonus: number;
itemPlans: Array<ItemPlan>;
}
export const AttackTable = ({
theme,
attacks,
weaponSpellDamageGroups,
ruleData,
abilityLookup,
spellCasterInfo,
onAttackClick,
dataOriginRefData,
proficiencyBonus,
rollContext,
showNotes = true,
diceEnabled = false,
itemPlans,
...props
}: AttackTableProps) => {
const columnClassNameBase = useMemo(
() => [styles.col, theme?.isDarkMode && styles.darkMode],
[theme]
);
const getColumnClass = (addtionalClass: string) =>
clsx([...columnClassNameBase, addtionalClass]);
const renderedAttacks = useMemo(
(): React.ReactNode =>
!!attacks.length ? (
attacks.map((attack): React.ReactNode => {
const commonCombatAttackProps = {
key: attack.key,
attack,
ruleData,
abilityLookup,
onClick: onAttackClick,
showNotes,
diceEnabled,
theme,
rollContext,
proficiencyBonus,
className: styles.attack,
};
switch (attack.type) {
case Constants.AttackSourceTypeEnum.ACTION:
case Constants.AttackSourceTypeEnum.CUSTOM:
return (
<CombatActionAttack
action={attack.data as Action}
{...commonCombatAttackProps}
key={commonCombatAttackProps.key}
/>
);
case Constants.AttackSourceTypeEnum.ITEM:
return (
<CombatItemAttack
item={attack.data as Item}
weaponSpellDamageGroups={weaponSpellDamageGroups}
itemPlans={itemPlans}
{...commonCombatAttackProps}
key={commonCombatAttackProps.key}
/>
);
case Constants.AttackSourceTypeEnum.SPELL:
return (
<CombatSpellAttack
spell={attack.data as Spell}
spellCasterInfo={spellCasterInfo}
dataOriginRefData={dataOriginRefData}
{...commonCombatAttackProps}
key={commonCombatAttackProps.key}
/>
);
default:
return null;
}
})
) : (
<div
className={clsx([
styles.default,
theme?.isDarkMode && styles.darkMode,
])}
>
Equip weapons or add spells to see your attacks here.
</div>
),
[
attacks,
ruleData,
abilityLookup,
onAttackClick,
showNotes,
diceEnabled,
theme,
rollContext,
proficiencyBonus,
weaponSpellDamageGroups,
spellCasterInfo,
dataOriginRefData,
itemPlans,
]
);
return (
<div {...props}>
<div className={styles.tableHeader}>
<div className={getColumnClass(styles.icon)} />
<div className={getColumnClass(styles.name)}>Attack</div>
<div className={getColumnClass(styles.range)}>Range</div>
<div className={getColumnClass(styles.tohit)}>Hit / DC</div>
<div className={getColumnClass(styles.damage)}>Damage</div>
{showNotes && <div className={getColumnClass(styles.notes)}>Notes</div>}
</div>
<div data-testid="attack-table-content">{renderedAttacks}</div>
</div>
);
};