diff --git a/TODO.md b/TODO.md index ee69e02..5fa5bae 100644 --- a/TODO.md +++ b/TODO.md @@ -11,7 +11,7 @@ also better vert tab layout - labelt friendly x needs AC for players dude -and quick entry hp +x and quick entry hp hp do not carry from encounter to ecnounter!!! ^feature to add back to campaign character after encounter? @@ -20,6 +20,7 @@ maybe campaign toggle in charc section (choosing each end is DM overload will be hp wont go over max and no temp hp support +refresh/reload/code update causes UI to update to top unselected cambpaign have to drill all the way back down to active encounter....every time. siemtmes? diff --git a/src/App.js b/src/App.js index 844689e..576fe10 100644 --- a/src/App.js +++ b/src/App.js @@ -1085,6 +1085,17 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp ]; const participants = encounter.participants || []; + const [editingField, setEditingField] = useState(null); + const [editingId, setEditingId] = useState(null); + const blurTimeoutRef = useRef(null); + const handleFieldFocus = (id, label) => { + if (blurTimeoutRef.current) { clearTimeout(blurTimeoutRef.current); blurTimeoutRef.current = null; } + setEditingId(id); + setEditingField(label); + }; + const handleFieldBlur = () => { + blurTimeoutRef.current = setTimeout(() => { setEditingField(null); setEditingId(null); }, 150); + }; useEffect(() => { if (participantType === 'character' && selectedCharacterId) { @@ -1283,6 +1294,53 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp } }; + const handleInlineCurrentHp = async (participantId, value) => { + let n = parseInt(value, 10); + if (isNaN(n)) return; + const p = participants.find(pp => pp.id === participantId); + if (!p || n === p.currentHp) return; + try { + const isGeneric = (encounter.ruleset === 'generic'); + let patch; + if (!isGeneric && n < 0) n = 0; + if (isGeneric) { + patch = { currentHp: n, status: n > 0 ? 'conscious' : 'down' }; + } else { + patch = { currentHp: n, deathSaveSuccesses: 0, deathSaveFailures: 0 }; + if (n > 0) { patch.status = 'conscious'; } + else if (p.type === 'monster') { patch.status = 'dead'; patch.isActive = false; } + else { patch.status = 'dying'; } + } + await updateParticipant(encounter, participantId, patch, buildCtx(encounterPath)); + } catch (err) { + showToast('Failed to update HP.'); + } + }; + + const handleInlineMaxHp = async (participantId, value) => { + const n = parseInt(value, 10); + if (isNaN(n)) return; + const p = participants.find(pp => pp.id === participantId); + if (!p || n === p.maxHp || n <= 0) return; + try { + await updateParticipant(encounter, participantId, { maxHp: n }, buildCtx(encounterPath)); + } catch (err) { + showToast('Failed to update Max HP.'); + } + }; + + const handleInlineAc = async (participantId, value) => { + const n = parseInt(value, 10); + if (isNaN(n)) return; + const p = participants.find(pp => pp.id === participantId); + if (!p || n === p.ac) return; + try { + await updateParticipant(encounter, participantId, { ac: n }, buildCtx(encounterPath)); + } catch (err) { + showToast('Failed to update AC.'); + } + }; + const handleDeathSaveChange = async (participantId, outcome) => { if (!db) return; try { @@ -1602,6 +1660,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp {participants.length === 0 &&

No participants added yet.

} +