122 lines
3.5 KiB
TypeScript
122 lines
3.5 KiB
TypeScript
import clsx from "clsx";
|
|
import { FC, HTMLAttributes } from "react";
|
|
import { useSelector } from "react-redux";
|
|
|
|
import {
|
|
AdvantageIcon,
|
|
BoxBackground,
|
|
DigitalDiceWrapper,
|
|
InitiativeBoxSvg,
|
|
} from "@dndbeyond/character-components/es";
|
|
import {
|
|
RollKinds,
|
|
RollTypes,
|
|
} from "@dndbeyond/pocket-dimension-dice/constants";
|
|
|
|
import { NumberDisplay } from "~/components/NumberDisplay";
|
|
import { useCharacterTheme } from "~/contexts/CharacterTheme";
|
|
import { useSidebar } from "~/contexts/Sidebar";
|
|
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
|
|
import a11yStyles from "~/styles/accessibility.module.css";
|
|
import {
|
|
appEnvSelectors,
|
|
characterRollContextSelectors,
|
|
} from "~/tools/js/Shared/selectors";
|
|
|
|
import { PaneComponentEnum } from "../Sidebar/types";
|
|
import styles from "./styles.module.css";
|
|
|
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
isMobile?: boolean;
|
|
isTablet?: boolean;
|
|
}
|
|
export const InitiativeBox: FC<Props> = ({ isMobile, isTablet, ...props }) => {
|
|
const {
|
|
pane: { paneHistoryStart },
|
|
} = useSidebar();
|
|
const handleClick = (evt: React.MouseEvent): void => {
|
|
evt.stopPropagation();
|
|
evt.nativeEvent.stopImmediatePropagation();
|
|
paneHistoryStart(PaneComponentEnum.INITIATIVE);
|
|
};
|
|
|
|
const {
|
|
hasInitiativeAdvantage,
|
|
processedInitiative,
|
|
characterTheme: theme,
|
|
} = useCharacterEngine();
|
|
const initiative = Number(processedInitiative);
|
|
const { isDarkMode } = useCharacterTheme();
|
|
|
|
const diceEnabled = useSelector(appEnvSelectors.getDiceEnabled);
|
|
const rollContext = useSelector(
|
|
characterRollContextSelectors.getCharacterRollContext
|
|
);
|
|
|
|
return (
|
|
<section
|
|
className={clsx([isMobile ? styles.boxMobile : styles.box])}
|
|
onClick={handleClick}
|
|
{...props}
|
|
>
|
|
{!isMobile && (
|
|
<div
|
|
className={clsx([
|
|
styles.label,
|
|
isDarkMode && !isTablet && styles.dark,
|
|
])}
|
|
data-testid="combat-initiative-label"
|
|
>
|
|
Initiative
|
|
</div>
|
|
)}
|
|
<div className={isMobile ? styles.valueMobile : styles.value}>
|
|
{!isMobile && (
|
|
<>
|
|
<BoxBackground StyleComponent={InitiativeBoxSvg} theme={theme} />
|
|
<h2 className={a11yStyles.screenreaderOnly}>Initiative</h2>
|
|
</>
|
|
)}
|
|
<DigitalDiceWrapper
|
|
diceNotation={`1d20${
|
|
initiative > 0
|
|
? `+${initiative}`
|
|
: initiative !== 0
|
|
? initiative
|
|
: ""
|
|
}`}
|
|
rollType={RollTypes.Roll}
|
|
action="Initiative"
|
|
rollKind={hasInitiativeAdvantage ? RollKinds.Advantage : undefined}
|
|
isDiceEnabled={diceEnabled}
|
|
entityId={String(rollContext.entityId)}
|
|
entityType={String(rollContext.entityType)}
|
|
name={String(rollContext.name)}
|
|
>
|
|
<NumberDisplay
|
|
number={initiative}
|
|
type="signed"
|
|
size="large"
|
|
className={clsx([
|
|
isMobile && !theme.isDarkMode && styles.numberColorOverride,
|
|
])}
|
|
/>
|
|
</DigitalDiceWrapper>
|
|
</div>
|
|
{hasInitiativeAdvantage && (
|
|
<div
|
|
className={clsx([styles.advantage, isMobile && styles.mobile])}
|
|
aria-label="Has advantage on initiative"
|
|
>
|
|
<AdvantageIcon
|
|
theme={theme}
|
|
className={styles.advantageIcon}
|
|
title={"Advantage on Initiative"}
|
|
/>
|
|
</div>
|
|
)}
|
|
{isMobile && <div className={styles.labelMobile}>Initiative</div>}
|
|
</section>
|
|
);
|
|
};
|