feat: sidebar navigation to dedicated detail views
- Sidebar icons now navigate to full-screen views for each tool - Dashboard bento cards have 'Expand' buttons that open the detail view - Back arrow (←) in title bar returns to dashboard - Detail views wrapped in a glass-card with max-w-2xl layout - Settings page gets its own full layout with heading - World Map, Quest Designer, Soundboard show placeholder screens with icons - All nav items in sidebar: World, NPCs, Encounter, Dice, Session, Sound, Initiative, Random Tables, Calendar, Settings
This commit is contained in:
+162
-20
@@ -7,48 +7,129 @@ import {
|
||||
Volume2,
|
||||
Settings,
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
Sparkles,
|
||||
Timer,
|
||||
Calendar,
|
||||
} from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { Dashboard } from "./components/Dashboard";
|
||||
import { SettingsPanel } from "./components/SettingsPanel";
|
||||
import { NpcGenerator } from "./components/NpcGenerator";
|
||||
import { DiceRoller } from "./components/DiceRoller";
|
||||
import { SessionLogger } from "./components/SessionLogger";
|
||||
import { EncounterBuilder } from "./components/EncounterBuilder";
|
||||
import { InitiativeTracker } from "./components/InitiativeTracker";
|
||||
import { RandomTables } from "./components/RandomTables";
|
||||
import { CalendarWidget } from "./components/CalendarWidget";
|
||||
|
||||
type View = "dashboard" | "settings";
|
||||
export type View =
|
||||
| "dashboard"
|
||||
| "world"
|
||||
| "npcs"
|
||||
| "encounter"
|
||||
| "dice"
|
||||
| "session"
|
||||
| "sound"
|
||||
| "initiative"
|
||||
| "tables"
|
||||
| "calendar"
|
||||
| "quest"
|
||||
| "settings";
|
||||
|
||||
const navItems = [
|
||||
{ icon: Map, label: "World" },
|
||||
{ icon: User, label: "NPCs" },
|
||||
{ icon: Swords, label: "Encounter" },
|
||||
{ icon: Dice5, label: "Dice" },
|
||||
{ icon: ScrollText, label: "Session" },
|
||||
{ icon: Volume2, label: "Sound" },
|
||||
const navItems: { icon: typeof Map; label: string; view: View }[] = [
|
||||
{ icon: Map, label: "World", view: "world" },
|
||||
{ icon: User, label: "NPCs", view: "npcs" },
|
||||
{ icon: Swords, label: "Encounter", view: "encounter" },
|
||||
{ icon: Dice5, label: "Dice", view: "dice" },
|
||||
{ icon: ScrollText, label: "Session", view: "session" },
|
||||
{ icon: Volume2, label: "Sound", view: "sound" },
|
||||
];
|
||||
|
||||
function renderView(view: View): React.ReactNode {
|
||||
switch (view) {
|
||||
case "npcs":
|
||||
return <NpcGenerator />;
|
||||
case "dice":
|
||||
return <DiceRoller />;
|
||||
case "session":
|
||||
return <SessionLogger />;
|
||||
case "encounter":
|
||||
return <EncounterBuilder />;
|
||||
case "initiative":
|
||||
return <InitiativeTracker />;
|
||||
case "tables":
|
||||
return <RandomTables />;
|
||||
case "calendar":
|
||||
return <CalendarWidget />;
|
||||
case "settings":
|
||||
return <SettingsPanel />;
|
||||
case "world":
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)]">
|
||||
<div className="text-center">
|
||||
<Map size={64} className="mx-auto mb-4 opacity-30" />
|
||||
<p className="text-lg font-heading">World Map</p>
|
||||
<p className="text-sm mt-2">Interactive map canvas coming soon</p>
|
||||
<p className="text-xs mt-1 opacity-60">react-konva integration pending</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "quest":
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)]">
|
||||
<div className="text-center">
|
||||
<Sparkles size={64} className="mx-auto mb-4 opacity-30" />
|
||||
<p className="text-lg font-heading">Quest Designer</p>
|
||||
<p className="text-sm mt-2">Branching flowchart coming soon</p>
|
||||
<p className="text-xs mt-1 opacity-60">react-flow integration pending</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "sound":
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)]">
|
||||
<div className="text-center">
|
||||
<Volume2 size={64} className="mx-auto mb-4 opacity-30" />
|
||||
<p className="text-lg font-heading">Soundboard</p>
|
||||
<p className="text-sm mt-2">Local audio clips for ambience</p>
|
||||
<p className="text-xs mt-1 opacity-60">Coming soon</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [view, setView] = useState<View>("dashboard");
|
||||
const [activeNav, setActiveNav] = useState(0);
|
||||
|
||||
const isDetailView = view !== "dashboard" && view !== "settings";
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen overflow-hidden bg-[var(--color-bg-deep)]">
|
||||
{/* Left Rail */}
|
||||
<nav className="flex flex-col items-center w-14 bg-[var(--color-bg-surface)] border-r border-[var(--color-border-subtle)] py-3 gap-1 shrink-0">
|
||||
{/* App Logo */}
|
||||
{/* App Logo — goes to dashboard */}
|
||||
<button
|
||||
onClick={() => setView("dashboard")}
|
||||
className="w-8 h-8 rounded-lg bg-[var(--color-gold-bright)] flex items-center justify-center mb-4 text-[var(--color-bg-deep)] font-heading font-bold text-lg cursor-pointer"
|
||||
title="DM-Pal"
|
||||
className={`w-8 h-8 rounded-lg flex items-center justify-center mb-4 font-heading font-bold text-lg cursor-pointer transition-colors ${
|
||||
view === "dashboard"
|
||||
? "bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)]"
|
||||
: "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)] hover:bg-[var(--color-gold-bright)] hover:text-[var(--color-bg-deep)]"
|
||||
}`}
|
||||
title="Dashboard"
|
||||
>
|
||||
⚔
|
||||
</button>
|
||||
|
||||
{navItems.map((item, i) => (
|
||||
{navItems.map((item) => (
|
||||
<button
|
||||
key={item.label}
|
||||
onClick={() => {
|
||||
setActiveNav(i);
|
||||
setView("dashboard");
|
||||
}}
|
||||
onClick={() => setView(item.view)}
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||
i === activeNav && view === "dashboard"
|
||||
view === item.view
|
||||
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||
}`}
|
||||
@@ -61,7 +142,44 @@ export default function App() {
|
||||
{/* Spacer */}
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* Settings at bottom */}
|
||||
{/* Extra nav items */}
|
||||
<button
|
||||
onClick={() => setView("initiative")}
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||
view === "initiative"
|
||||
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||
}`}
|
||||
title="Initiative"
|
||||
>
|
||||
<Timer size={18} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setView("tables")}
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||
view === "tables"
|
||||
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||
}`}
|
||||
title="Random Tables"
|
||||
>
|
||||
<Sparkles size={18} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setView("calendar")}
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||
view === "calendar"
|
||||
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||
}`}
|
||||
title="Calendar"
|
||||
>
|
||||
<Calendar size={18} />
|
||||
</button>
|
||||
|
||||
<div className="w-6 h-px bg-[var(--color-border-subtle)] my-1" />
|
||||
|
||||
{/* Settings */}
|
||||
<button
|
||||
onClick={() => setView(view === "settings" ? "dashboard" : "settings")}
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||
@@ -82,6 +200,15 @@ export default function App() {
|
||||
className="flex items-center h-10 px-4 bg-[var(--color-bg-surface)] border-b border-[var(--color-border-subtle)] shrink-0"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
{isDetailView && (
|
||||
<button
|
||||
onClick={() => setView("dashboard")}
|
||||
className="mr-3 text-[var(--color-text-secondary)] hover:text-[var(--color-gold-bright)] transition-colors cursor-pointer"
|
||||
title="Back to dashboard"
|
||||
>
|
||||
<ChevronLeft size={18} />
|
||||
</button>
|
||||
)}
|
||||
<span className="font-heading text-[var(--color-gold-bright)] text-base font-bold tracking-wider">
|
||||
DM-Pal
|
||||
</span>
|
||||
@@ -93,7 +220,22 @@ export default function App() {
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 overflow-auto">
|
||||
{view === "dashboard" ? <Dashboard /> : <SettingsPanel />}
|
||||
{view === "dashboard" ? (
|
||||
<Dashboard onNavigate={setView} />
|
||||
) : view === "settings" ? (
|
||||
<div className="max-w-lg mx-auto p-6">
|
||||
<h2 className="font-heading text-[var(--color-gold-bright)] text-lg font-semibold mb-4">
|
||||
Settings
|
||||
</h2>
|
||||
<SettingsPanel />
|
||||
</div>
|
||||
) : isDetailView ? (
|
||||
<div className="max-w-2xl mx-auto p-6">
|
||||
<div className="glass-card p-6">
|
||||
{renderView(view)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user