Tracker: unfold a combatant's stat block inline

- extract the 5e stat-block markup into enemy/_statblock.html.twig
  (sb.block(enemy) macro); the enemy detail page now uses it too
- each tracker combatant gets a "stat block" toggle that expands the
  block in place (Bootstrap collapse), so you don't have to leave the
  encounter to check AC / attacks
- open blocks are remembered in localStorage and re-opened after each
  damage/heal reload, and the caret rotates when expanded

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Frank
2026-09-06 19:29:52 +02:00
co-authored by Claude Sonnet 5
parent cbe4b0a0e1
commit 2ed169fc78
5 changed files with 137 additions and 59 deletions
+40
View File
@@ -105,3 +105,43 @@ document.addEventListener('DOMContentLoaded', () => initDataTables());
document.addEventListener('turbo:load', () => initDataTables());
// Restore the plain <table> before Turbo snapshots the page for its cache.
document.addEventListener('turbo:before-cache', destroyDataTables);
// --- Remember unfolded stat blocks on the tracker -----------------------
// Every damage/heal is a POST + redirect, so without this a stat block the
// DM has open snaps shut on each hit. Keyed by the collapse element id.
const SB_KEY = 'dmtools.statblocks';
function readOpenSet() {
try {
return new Set(JSON.parse(localStorage.getItem(SB_KEY) || '[]'));
} catch (e) {
return new Set();
}
}
function restoreStatblocks() {
const open = readOpenSet();
document.querySelectorAll('.statblock-collapse').forEach((el) => {
const on = open.has(el.id);
el.classList.toggle('show', on);
document
.querySelectorAll(`[data-bs-toggle="collapse"][data-bs-target="#${el.id}"]`)
.forEach((btn) => btn.setAttribute('aria-expanded', on ? 'true' : 'false'));
});
}
function rememberStatblock(el, isOpen) {
if (!el.classList.contains('statblock-collapse')) return;
const open = readOpenSet();
isOpen ? open.add(el.id) : open.delete(el.id);
try {
localStorage.setItem(SB_KEY, JSON.stringify([...open]));
} catch (e) {
/* private mode - ignore */
}
}
document.addEventListener('shown.bs.collapse', (e) => rememberStatblock(e.target, true));
document.addEventListener('hidden.bs.collapse', (e) => rememberStatblock(e.target, false));
document.addEventListener('DOMContentLoaded', restoreStatblocks);
document.addEventListener('turbo:load', restoreStatblocks);