import React, { FC, HTMLAttributes } from "react"; import { Tooltip } from "@dndbeyond/character-common-components/es"; import { NoteComponents } from "@dndbeyond/character-components/es"; import { CharacterTheme, Constants, Creature, ExtraManager, } from "@dndbeyond/character-rules-engine/es"; import { NumberDisplay } from "~/components/NumberDisplay"; import { useCharacterEngine } from "~/hooks/useCharacterEngine"; import ExtraName from "../ExtraName"; interface Props extends HTMLAttributes { extra: ExtraManager; onShow?: (extra: ExtraManager) => void; onStatusChange?: (extra: ExtraManager, newStatus: boolean) => void; showNotes: boolean; isReadonly: boolean; theme: CharacterTheme; } export const ExtraRow: FC = ({ extra, onShow, onStatusChange, showNotes, isReadonly = false, theme, ...props }) => { const { hpInfo } = useCharacterEngine(); let avatarUrl = extra.getAvatarUrl(); if (!avatarUrl) { avatarUrl = ""; } const name = extra.getName(); const handleClick = (evt: React.MouseEvent): void => { if (onShow) { evt.stopPropagation(); evt.nativeEvent.stopImmediatePropagation(); onShow(extra); } }; const handleActivate = (): void => { if (onStatusChange) { onStatusChange(extra, true); } }; const handleDeactivate = (): void => { if (onStatusChange) { onStatusChange(extra, false); } }; const renderMetaItems = (): React.ReactNode => { let metaItems: Array = []; const sizeInfo = extra.getSizeInfo(); let size: string = ""; if (sizeInfo !== null && sizeInfo.name !== null) { size = sizeInfo.name; } let type = extra.getType(); let typeName: string = ""; if (type) { typeName = type; } let description: string = `${size} ${typeName.toLowerCase()}`.trim(); if (description) { metaItems.push(description); } return (
{metaItems.map((metaItem, idx) => ( {metaItem} ))}
); }; const renderHitPoints = (): React.ReactNode => { let useOwnerHp = false; if (extra.isCreature()) { const extraData = extra.getExtraData() as Creature; if (extraData) { useOwnerHp = extraData.useOwnerHp; } } const hitPointInfo = useOwnerHp ? hpInfo : extra.getHitPointInfo().data; const showTooltip = useOwnerHp ? false : extra.getHitPointInfo().showTooltip; let contentNode: React.ReactNode = null; if (hitPointInfo !== null) { let totalHp: number = hitPointInfo.totalHp ? hitPointInfo.totalHp : 0; let remainingHp: number = hitPointInfo.remainingHp ? hitPointInfo.remainingHp : 0; let tempHp: number = hitPointInfo.tempHp ? hitPointInfo.tempHp : 0; const current: number = remainingHp + tempHp; const total: number = totalHp + tempHp; contentNode = ( {current} / {total} ); } else { contentNode = ( -- ); } let classNames: Array = ["ct-extra-row__hp"]; if ( hitPointInfo !== null && hitPointInfo.tempHp !== null && hitPointInfo.tempHp > 0 ) { classNames.push("ct-extra-row__hp--has-temp"); } if (theme.isDarkMode) { classNames.push("ct-extra-row__hp--dark-mode"); } return (
{contentNode} {showTooltip && renderAdditionalInfoTooltip()}
); }; const renderSpeed = (): React.ReactNode => { const movementInfo = extra.getMovementInfo(); const { data, label, showTooltip } = movementInfo; let contentNode: React.ReactNode = null; if (data !== null) { contentNode = (
{showTooltip && renderAdditionalInfoTooltip()}
{data.movementId !== Constants.MovementTypeEnum.WALK && (
{label}
)}
); } else { contentNode = (
-- {showTooltip && renderAdditionalInfoTooltip()}
); } return (
{contentNode}
); }; const renderArmorClass = (): React.ReactNode => { let armorClassInfo = extra.getArmorClassInfo(); let armorClassText: string | number = "--"; if (armorClassInfo.value !== null) { armorClassText = armorClassInfo.value; } return ( {armorClassText} {armorClassInfo.showTooltip && renderAdditionalInfoTooltip()} ); }; const renderAdditionalInfoTooltip = (): React.ReactNode => { return ( * ); }; return (
{avatarUrl && ( {`${name} )}
{renderMetaItems()}
{renderArmorClass()}
{renderHitPoints()} {renderSpeed()} {showNotes && (
)} {/*
*/}
); };