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:
david raistrick
2026-07-07 14:51:45 -04:00
parent a5b6d61e83
commit 95db6fdfdc
2 changed files with 14 additions and 21 deletions
+14 -8
View File
@@ -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>