import React, { useEffect, useState } from 'react'; import { ipcChannels } from '../../shared/ipc/contracts'; import { DICE_COUNT_MAX, DICE_COUNT_MIN, DICE_SIDES, clampDiceCount, type DiceSides, } from '../../shared/types/diceRoll'; import { useEditorI18n } from '../editor/i18n/EditorI18nContext'; import { getDndApi } from '../shared/dndApi'; import { useDiceRollState } from '../shared/dice/useDiceRollState'; import { Button } from '../shared/ui/controls'; import styles from './DiceApp.module.css'; export function DiceApp() { const { t } = useEditorI18n(); const api = getDndApi(); const [state, diceApi] = useDiceRollState(); const [sides, setSides] = useState(20); const [count, setCount] = useState(1); const [countDraft, setCountDraft] = useState('1'); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { void api.invoke(ipcChannels.windows.closeDice, {}); } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [api]); const commitCountDraft = () => { const trimmed = countDraft.trim(); if (trimmed === '') { setCount(DICE_COUNT_MIN); setCountDraft(String(DICE_COUNT_MIN)); return; } const parsed = Number(trimmed); if (!Number.isFinite(parsed) || parsed < DICE_COUNT_MIN) { setCount(DICE_COUNT_MIN); setCountDraft(String(DICE_COUNT_MIN)); return; } const next = clampDiceCount(parsed); setCount(next); setCountDraft(String(next)); }; const setCountClamped = (n: number) => { const next = clampDiceCount(n); setCount(next); setCountDraft(String(next)); }; const showToPlayers = state?.visibleToPlayers ?? true; return (
{t('dice.sides')}
{DICE_SIDES.map((s) => ( ))}
{t('dice.count')}
{ const digits = e.target.value.replace(/\D/g, ''); if (digits === '') { setCountDraft(''); return; } const n = Number(digits); if (!Number.isFinite(n)) { setCountDraft(String(DICE_COUNT_MIN)); setCount(DICE_COUNT_MIN); return; } if (n > DICE_COUNT_MAX) { setCountDraft(String(DICE_COUNT_MAX)); setCount(DICE_COUNT_MAX); return; } setCountDraft(digits); }} onBlur={() => commitCountDraft()} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); commitCountDraft(); (e.currentTarget as HTMLInputElement).blur(); } }} aria-label={t('dice.count')} data-testid="dice-count" />
{state && state.values.length > 0 ? (
{t('dice.result')}
{state.notation}
{state.values.map((v, i) => ( {v} ))}
{t('dice.total')}: {state.total}
{state.active ?
{t('dice.rolling')}
: null}
) : (
{t('dice.noResult')}
)}
); }