generated from nhcarrigan/template
feat: mode bar, goddess tab row, themed resource dropdown (chunk 5)
Add Mortal/Goddess/Vampire mode selector bar, dynamic second tab row that swaps per mode, goddess currencies in the resource bar dropdown (locked pre-apotheosis), full CSS goddess theme with 300ms fade transition, and localStorage persistence of the active mode.
This commit is contained in:
@@ -4,9 +4,10 @@
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
/* eslint-disable max-lines -- Complex layout with many conditional renders */
|
||||
/* eslint-disable max-lines-per-function -- Complex layout with many conditional renders */
|
||||
/* eslint-disable complexity -- Many tab render paths */
|
||||
import { type JSX, useState } from "react";
|
||||
import { type JSX, useEffect, useState } from "react";
|
||||
import { useGame } from "../../context/gameContext.js";
|
||||
import { ResourceBar } from "../ui/resourceBar.js";
|
||||
import { AboutPanel } from "./aboutPanel.js";
|
||||
@@ -41,6 +42,8 @@ import { StoryToast } from "./storyToast.js";
|
||||
import { TranscendencePanel } from "./transcendencePanel.js";
|
||||
import { UpgradePanel } from "./upgradePanel.js";
|
||||
|
||||
type Mode = "mortal" | "goddess" | "vampire";
|
||||
|
||||
type Tab =
|
||||
| "adventurers"
|
||||
| "upgrades"
|
||||
@@ -62,6 +65,19 @@ type Tab =
|
||||
| "story"
|
||||
| "debug";
|
||||
|
||||
type GoddessTab =
|
||||
| "goddess-zones"
|
||||
| "goddess-bosses"
|
||||
| "goddess-quests"
|
||||
| "disciples"
|
||||
| "goddess-equipment"
|
||||
| "goddess-upgrades"
|
||||
| "consecration"
|
||||
| "enlightenment"
|
||||
| "goddess-crafting"
|
||||
| "goddess-exploration"
|
||||
| "goddess-achievements";
|
||||
|
||||
const baseTabs: Array<{ id: Tab; label: string }> = [
|
||||
{ id: "adventurers", label: "⚔️ Adventurers" },
|
||||
{ id: "upgrades", label: "🔧 Upgrades" },
|
||||
@@ -84,6 +100,40 @@ const baseTabs: Array<{ id: Tab; label: string }> = [
|
||||
{ id: "debug", label: "🔧 Debug" },
|
||||
];
|
||||
|
||||
const goddessTabs: Array<{ id: GoddessTab; label: string }> = [
|
||||
{ id: "goddess-zones", label: "🌟 Zones" },
|
||||
{ id: "goddess-bosses", label: "👁️ Bosses" },
|
||||
{ id: "goddess-quests", label: "📿 Quests" },
|
||||
{ id: "disciples", label: "🙏 Disciples" },
|
||||
{ id: "goddess-equipment", label: "🔮 Equipment" },
|
||||
{ id: "goddess-upgrades", label: "✨ Upgrades" },
|
||||
{ id: "consecration", label: "🕯️ Consecration" },
|
||||
{ id: "enlightenment", label: "💫 Enlightenment" },
|
||||
{ id: "goddess-crafting", label: "⚗️ Crafting" },
|
||||
{ id: "goddess-exploration", label: "🌌 Exploration" },
|
||||
{ id: "goddess-achievements", label: "🏆 Achievements" },
|
||||
];
|
||||
|
||||
const modes: Array<Mode> = [ "mortal", "goddess", "vampire" ];
|
||||
|
||||
const modeLabels: Record<Mode, string> = {
|
||||
goddess: "✨ Goddess",
|
||||
mortal: "⚔️ Mortal",
|
||||
vampire: "🧛 Vampire",
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads the saved active mode from localStorage, defaulting to "mortal".
|
||||
* @returns The saved mode or "mortal".
|
||||
*/
|
||||
const readSavedMode = (): Mode => {
|
||||
const saved = localStorage.getItem("elysium-active-mode");
|
||||
if (saved === "goddess" || saved === "vampire") {
|
||||
return saved;
|
||||
}
|
||||
return "mortal";
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the main game layout with tabs and panels.
|
||||
* @returns The JSX element.
|
||||
@@ -104,11 +154,18 @@ const GameLayout = (): JSX.Element => {
|
||||
dismissLoginBonus,
|
||||
schemaOutdated,
|
||||
} = useGame();
|
||||
const [ activeMode, setActiveMode ] = useState<Mode>(readSavedMode);
|
||||
const [ activeTab, setActiveTab ] = useState<Tab>("adventurers");
|
||||
const [ activeGoddessTab, setActiveGoddessTab ]
|
||||
= useState<GoddessTab>("goddess-zones");
|
||||
const [ editingProfile, setEditingProfile ] = useState(false);
|
||||
const [ dismissedOutdatedWarning, setDismissedOutdatedWarning ]
|
||||
= useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.toggle("goddess-mode", activeMode === "goddess");
|
||||
}, [ activeMode ]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="loading-screen">
|
||||
@@ -151,6 +208,11 @@ const GameLayout = (): JSX.Element => {
|
||||
setDismissedOutdatedWarning(true);
|
||||
}
|
||||
|
||||
function handleSetMode(mode: Mode): void {
|
||||
localStorage.setItem("elysium-active-mode", mode);
|
||||
setActiveMode(mode);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="game-layout">
|
||||
<ResourceBar
|
||||
@@ -197,55 +259,138 @@ const GameLayout = (): JSX.Element => {
|
||||
</aside>
|
||||
|
||||
<main className="game-content">
|
||||
<nav className="tab-bar">
|
||||
{baseTabs.map((tab) => {
|
||||
const { id: tabId, label } = tab;
|
||||
function handleTabClick(): void {
|
||||
setActiveTab(tabId);
|
||||
<nav className="mode-bar">
|
||||
{modes.map((mode) => {
|
||||
const apotheosisCount = state.apotheosis?.count ?? 0;
|
||||
const goddessLocked = mode === "goddess" && apotheosisCount === 0;
|
||||
const isLocked = goddessLocked || mode === "vampire";
|
||||
function handleModeClick(): void {
|
||||
if (!isLocked) {
|
||||
handleSetMode(mode);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`tab-button ${
|
||||
activeTab === tabId
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
key={tabId}
|
||||
onClick={handleTabClick}
|
||||
className={`mode-button${activeMode === mode
|
||||
? " active"
|
||||
: ""}${isLocked
|
||||
? " locked"
|
||||
: ""}`}
|
||||
disabled={isLocked}
|
||||
key={mode}
|
||||
onClick={handleModeClick}
|
||||
title={isLocked
|
||||
? "Not yet unlocked"
|
||||
: modeLabels[mode]}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
{tabId === "codex" && codexBadgeCount > 0
|
||||
&& <span className="tab-badge">{codexBadgeCount}</span>
|
||||
}
|
||||
{tabId === "story" && storyBadgeCount > 0
|
||||
&& <span className="tab-badge">{storyBadgeCount}</span>
|
||||
}
|
||||
{modeLabels[mode]}
|
||||
{isLocked
|
||||
? <span className="mode-lock">{"🔒"}</span>
|
||||
: null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{activeMode === "mortal"
|
||||
? <nav className="tab-bar">
|
||||
{baseTabs.map((tab) => {
|
||||
const { id: tabId, label } = tab;
|
||||
function handleTabClick(): void {
|
||||
setActiveTab(tabId);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`tab-button${activeTab === tabId
|
||||
? " active"
|
||||
: ""}`}
|
||||
key={tabId}
|
||||
onClick={handleTabClick}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
{tabId === "codex" && codexBadgeCount > 0
|
||||
&& <span className="tab-badge">{codexBadgeCount}</span>
|
||||
}
|
||||
{tabId === "story" && storyBadgeCount > 0
|
||||
&& <span className="tab-badge">{storyBadgeCount}</span>
|
||||
}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
: <nav className="tab-bar goddess-tab-bar">
|
||||
{goddessTabs.map((tab) => {
|
||||
const { id: tabId, label } = tab;
|
||||
function handleGoddessTabClick(): void {
|
||||
setActiveGoddessTab(tabId);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`tab-button${activeGoddessTab === tabId
|
||||
? " active"
|
||||
: ""}`}
|
||||
key={tabId}
|
||||
onClick={handleGoddessTabClick}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
}
|
||||
|
||||
<div className="tab-content">
|
||||
{activeTab === "adventurers" && <AdventurerPanel />}
|
||||
{activeTab === "upgrades" && <UpgradePanel />}
|
||||
{activeTab === "quests" && <QuestPanel />}
|
||||
{activeTab === "bosses" && <BossPanel />}
|
||||
{activeTab === "equipment" && <EquipmentPanel />}
|
||||
{activeTab === "achievements" && <AchievementPanel />}
|
||||
{activeTab === "prestige" && <PrestigePanel />}
|
||||
{activeTab === "transcendence" && <TranscendencePanel />}
|
||||
{activeTab === "apotheosis" && <ApotheosisPanel />}
|
||||
{activeTab === "exploration" && <ExplorationPanel />}
|
||||
{activeTab === "crafting" && <CraftingPanel />}
|
||||
{activeTab === "statistics" && <StatisticsPanel />}
|
||||
{activeTab === "daily" && <DailyChallengePanel />}
|
||||
{activeTab === "companions" && <CompanionPanel />}
|
||||
{activeTab === "character" && <CharacterSheetPanel />}
|
||||
{activeTab === "story" && <StoryPanel />}
|
||||
{activeTab === "codex" && <CodexPanel />}
|
||||
{activeTab === "about" && <AboutPanel />}
|
||||
{activeTab === "debug" && <DebugPanel />}
|
||||
{activeMode === "mortal" && activeTab === "adventurers"
|
||||
&& <AdventurerPanel />}
|
||||
{activeMode === "mortal" && activeTab === "upgrades"
|
||||
&& <UpgradePanel />}
|
||||
{activeMode === "mortal" && activeTab === "quests"
|
||||
&& <QuestPanel />}
|
||||
{activeMode === "mortal" && activeTab === "bosses"
|
||||
&& <BossPanel />}
|
||||
{activeMode === "mortal" && activeTab === "equipment"
|
||||
&& <EquipmentPanel />}
|
||||
{activeMode === "mortal" && activeTab === "achievements"
|
||||
&& <AchievementPanel />}
|
||||
{activeMode === "mortal" && activeTab === "prestige"
|
||||
&& <PrestigePanel />}
|
||||
{activeMode === "mortal" && activeTab === "transcendence"
|
||||
&& <TranscendencePanel />}
|
||||
{activeMode === "mortal" && activeTab === "apotheosis"
|
||||
&& <ApotheosisPanel />}
|
||||
{activeMode === "mortal" && activeTab === "exploration"
|
||||
&& <ExplorationPanel />}
|
||||
{activeMode === "mortal" && activeTab === "crafting"
|
||||
&& <CraftingPanel />}
|
||||
{activeMode === "mortal" && activeTab === "statistics"
|
||||
&& <StatisticsPanel />}
|
||||
{activeMode === "mortal" && activeTab === "daily"
|
||||
&& <DailyChallengePanel />}
|
||||
{activeMode === "mortal" && activeTab === "companions"
|
||||
&& <CompanionPanel />}
|
||||
{activeMode === "mortal" && activeTab === "character"
|
||||
&& <CharacterSheetPanel />}
|
||||
{activeMode === "mortal" && activeTab === "story"
|
||||
&& <StoryPanel />}
|
||||
{activeMode === "mortal" && activeTab === "codex"
|
||||
&& <CodexPanel />}
|
||||
{activeMode === "mortal" && activeTab === "about"
|
||||
&& <AboutPanel />}
|
||||
{activeMode === "mortal" && activeTab === "debug"
|
||||
&& <DebugPanel />}
|
||||
{activeMode === "goddess"
|
||||
&& <div className="goddess-placeholder">
|
||||
<p>{"✨ Goddess panels coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& <div className="goddess-placeholder">
|
||||
<p>{"🧛 Vampire panels coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user