Character section rollup: clickable title, count, persisted collapse
Match campaigns rollup pattern. Title button toggles collapse (chevron left, like campaigns). Character count in header. Collapse state persisted to localStorage key ttrpg.charactersCollapsed.
This commit is contained in:
@@ -50,19 +50,6 @@ lots of updates
|
||||
- `isActive = id => updatedParticipants.find(p => p.id === id && p.isActive)`
|
||||
- Returns participant obj (truthy) not boolean. Works by accident, fragile.
|
||||
|
||||
### BUG: select campaign during active combat = screen flicker, no action
|
||||
- In active encounter, click different campaign → flicker, no nav, no end.
|
||||
- Either block (toast: end encounter first) or auto-end current + switch.
|
||||
- Decide UX before fix.
|
||||
|
||||
|
||||
### FEAT: generic/non-5e rules mode
|
||||
- Campaign/encounter ruleset toggle: `5e` vs `generic`.
|
||||
- Generic mode turns off death saves/state machine.
|
||||
- Generic mode allows negative HP.
|
||||
- 5e mode rejects negative damage/heal and clamps HP at 0.
|
||||
|
||||
|
||||
|
||||
### FEAT: clarify "Is NPC" in add-participant
|
||||
- Ambiguous label. May expand work based on what NPC means here (ally? monster?
|
||||
|
||||
+14
-8
@@ -5,7 +5,7 @@ import {
|
||||
PlusCircle, Users, Swords, Trash2, Eye, Edit3, Save, XCircle, ChevronsUpDown,
|
||||
UserCheck, UserX, HeartCrack, HeartPulse, Zap, EyeOff, ExternalLink, AlertTriangle,
|
||||
Play as PlayIcon, Pause as PauseIcon, SkipForward as SkipForwardIcon,
|
||||
StopCircle as StopCircleIcon, Users2, Dices, ChevronUp, ChevronDown, ScrollText,
|
||||
StopCircle as StopCircleIcon, Users2, Dices, ChevronDown, ScrollText,
|
||||
Maximize2, Minimize2, Moon, Coffee, Clock, ChevronRight, X,
|
||||
Undo2, Redo2, Crosshair
|
||||
} from 'lucide-react';
|
||||
@@ -682,7 +682,14 @@ function CharacterManager({ campaignId, campaignCharacters }) {
|
||||
const [editingCharacter, setEditingCharacter] = useState(null);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [itemToDelete, setItemToDelete] = useState(null);
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const [isOpen, setIsOpen] = useState(() => {
|
||||
try { return localStorage.getItem('ttrpg.charactersCollapsed') !== 'true'; }
|
||||
catch { return true; }
|
||||
});
|
||||
useEffect(() => {
|
||||
try { localStorage.setItem('ttrpg.charactersCollapsed', String(!isOpen)); }
|
||||
catch {}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleAddCharacter = async () => {
|
||||
if (!db || !characterName.trim() || !campaignId) return;
|
||||
@@ -769,15 +776,14 @@ function CharacterManager({ campaignId, campaignCharacters }) {
|
||||
<>
|
||||
<div className="p-4 bg-stone-900 rounded-lg shadow">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<h3 className="text-xl font-semibold text-amber-300 font-cinzel tracking-wide flex items-center">
|
||||
<Users size={24} className="mr-2" /> Campaign Characters
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="p-1 text-stone-400 hover:text-stone-200"
|
||||
aria-label={isOpen ? "Collapse" : "Expand"}
|
||||
className="flex items-center text-xl font-semibold text-amber-300 font-cinzel tracking-wide hover:text-amber-200 transition-colors"
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
{isOpen ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
|
||||
{isOpen ? <ChevronDown size={20} className="mr-1" /> : <ChevronRight size={20} className="mr-1" />}
|
||||
<Users size={24} className="mr-2" /> Campaign Characters
|
||||
<span className="text-sm font-normal text-stone-400 ml-1">({campaignCharacters.length})</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user