Generic (non-5e) ruleset mode + UI polish + combat switch fix #7
@@ -50,19 +50,6 @@ lots of updates
|
|||||||
- `isActive = id => updatedParticipants.find(p => p.id === id && p.isActive)`
|
- `isActive = id => updatedParticipants.find(p => p.id === id && p.isActive)`
|
||||||
- Returns participant obj (truthy) not boolean. Works by accident, fragile.
|
- 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
|
### FEAT: clarify "Is NPC" in add-participant
|
||||||
- Ambiguous label. May expand work based on what NPC means here (ally? monster?
|
- 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,
|
PlusCircle, Users, Swords, Trash2, Eye, Edit3, Save, XCircle, ChevronsUpDown,
|
||||||
UserCheck, UserX, HeartCrack, HeartPulse, Zap, EyeOff, ExternalLink, AlertTriangle,
|
UserCheck, UserX, HeartCrack, HeartPulse, Zap, EyeOff, ExternalLink, AlertTriangle,
|
||||||
Play as PlayIcon, Pause as PauseIcon, SkipForward as SkipForwardIcon,
|
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,
|
Maximize2, Minimize2, Moon, Coffee, Clock, ChevronRight, X,
|
||||||
Undo2, Redo2, Crosshair
|
Undo2, Redo2, Crosshair
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
@@ -682,7 +682,14 @@ function CharacterManager({ campaignId, campaignCharacters }) {
|
|||||||
const [editingCharacter, setEditingCharacter] = useState(null);
|
const [editingCharacter, setEditingCharacter] = useState(null);
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
const [itemToDelete, setItemToDelete] = useState(null);
|
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 () => {
|
const handleAddCharacter = async () => {
|
||||||
if (!db || !characterName.trim() || !campaignId) return;
|
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="p-4 bg-stone-900 rounded-lg shadow">
|
||||||
<div className="flex justify-between items-center mb-3">
|
<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
|
<button
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
className="p-1 text-stone-400 hover:text-stone-200"
|
className="flex items-center text-xl font-semibold text-amber-300 font-cinzel tracking-wide hover:text-amber-200 transition-colors"
|
||||||
aria-label={isOpen ? "Collapse" : "Expand"}
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user