chore: community feedback fixes and UI improvements #102

Merged
naomi merged 12 commits from chore/more into main 2026-03-23 16:07:26 -07:00
2 changed files with 161 additions and 55 deletions
Showing only changes of commit d4bb140ad6 - Show all commits
+104 -55
View File
@@ -4,6 +4,7 @@
* @license Naomi's Public License
* @author Naomi Carrigan
*/
/* eslint-disable max-lines -- Resource bar has many resource and action elements */
/* eslint-disable max-lines-per-function -- Large header with many resource and action elements */
/* eslint-disable max-statements -- Resource bar requires many local computations and handlers */
/* eslint-disable complexity -- Many conditional resource and badge render paths */
@@ -77,6 +78,7 @@ const ResourceBar = ({
}: ResourceBarProperties): JSX.Element => {
const { formatNumber, syncError, state } = useGame();
const [ isProfileOpen, setIsProfileOpen ] = useState(false);
const [ isResourcesOpen, setIsResourcesOpen ] = useState(false);
const { gold, essence, crystals } = resources;
let partyCombatPower = 0;
@@ -99,18 +101,28 @@ const ResourceBar = ({
? "#"
: `/profile/${state.player.discordId}`;
const resourceValues = [ gold, essence, crystals ];
const anyFull = resourceValues.some((v) => {
return v >= RESOURCE_CAP;
});
const goldFull = gold >= RESOURCE_CAP;
const essenceFull = essence >= RESOURCE_CAP;
const crystalsFull = crystals >= RESOURCE_CAP;
const anyFull = goldFull || essenceFull || crystalsFull;
const hiddenResourcesFull = essenceFull || crystalsFull;
function handleForceSync(): void {
void onForceSync();
}
function handleToggleResources(): void {
setIsResourcesOpen((previous) => {
return !previous;
});
}
function handleResourceBlur(event: FocusEvent<HTMLDivElement>): void {
if (!event.currentTarget.contains(event.relatedTarget)) {
setIsResourcesOpen(false);
}
}
function handleToggleProfile(): void {
setIsProfileOpen((previous) => {
return !previous;
@@ -131,59 +143,96 @@ const ResourceBar = ({
return (
<>
<header className="resource-bar">
<div className={`resource${goldFull
? " resource-full"
: ""}`}>
<span className="resource-icon">{"🪙"}</span>
<span className="resource-value">{formatNumber(gold)}</span>
<span className="resource-label">{"Gold"}</span>
{goldFull
? <span className="resource-cap-badge" title={resourceFullTooltip}>
{"FULL"}
</span>
<div
className="resource-menu"
onBlur={handleResourceBlur}
>
<button
className={`resource resource-toggle${goldFull
? " resource-full"
: ""}`}
onClick={handleToggleResources}
title="Click to see all resources"
type="button"
>
<span className="resource-icon">{"🪙"}</span>
<span className="resource-value">{formatNumber(gold)}</span>
<span className="resource-label">{"Gold"}</span>
{goldFull
? <span
className="resource-cap-badge"
title={resourceFullTooltip}
>
{"FULL"}
</span>
: null}
{hiddenResourcesFull
? <span
className="resource-alert-dot"
title={"One or more resources are full!"}
/>
: null}
</button>
{isResourcesOpen
? <div className="resources-dropdown">
<div className="resource">
<span className="resource-icon">{"📈"}</span>
<span className="resource-value">
{formatNumber(goldPerSecond)}
</span>
<span className="resource-label">{"Gold/s"}</span>
</div>
<div className={`resource${essenceFull
? " resource-full"
: ""}`}>
<span className="resource-icon">{"✨"}</span>
<span className="resource-value">
{formatNumber(essence)}
</span>
<span className="resource-label">{"Essence"}</span>
{essenceFull
? <span
className="resource-cap-badge"
title={resourceFullTooltip}
>
{"FULL"}
</span>
: null}
</div>
<div className={`resource${crystalsFull
? " resource-full"
: ""}`}>
<span className="resource-icon">{"💎"}</span>
<span className="resource-value">
{formatNumber(crystals)}
</span>
<span className="resource-label">{"Crystals"}</span>
{crystalsFull
? <span
className="resource-cap-badge"
title={resourceFullTooltip}
>
{"FULL"}
</span>
: null}
</div>
<div className="resource">
<span className="resource-icon">{"🔮"}</span>
<span className="resource-value">
{formatNumber(runestones)}
</span>
<span className="resource-label">{"Runestones"}</span>
</div>
<div className="resource">
<span className="resource-icon">{"⚔️"}</span>
<span className="resource-value">
{formatNumber(partyCombatPower)}
</span>
<span className="resource-label">{"Combat Power"}</span>
</div>
</div>
: null}
</div>
<div className="resource">
<span className="resource-icon">{"📈"}</span>
<span className="resource-value">{formatNumber(goldPerSecond)}</span>
<span className="resource-label">{"Gold/s"}</span>
</div>
<div className={`resource${essenceFull
? " resource-full"
: ""}`}>
<span className="resource-icon">{"✨"}</span>
<span className="resource-value">{formatNumber(essence)}</span>
<span className="resource-label">{"Essence"}</span>
{essenceFull
? <span className="resource-cap-badge" title={resourceFullTooltip}>
{"FULL"}
</span>
: null}
</div>
<div className={`resource${crystalsFull
? " resource-full"
: ""}`}>
<span className="resource-icon">{"💎"}</span>
<span className="resource-value">{formatNumber(crystals)}</span>
<span className="resource-label">{"Crystals"}</span>
{crystalsFull
? <span className="resource-cap-badge" title={resourceFullTooltip}>
{"FULL"}
</span>
: null}
</div>
<div className="resource">
<span className="resource-icon">{"🔮"}</span>
<span className="resource-value">{formatNumber(runestones)}</span>
<span className="resource-label">{"Runestones"}</span>
</div>
<div className="resource">
<span className="resource-icon">{"⚔️"}</span>
<span className="resource-value">
{formatNumber(partyCombatPower)}
</span>
<span className="resource-label">{"Combat Power"}</span>
</div>
{apotheosisCount > 0
&& <div className="apotheosis-badge">
{"✨ Apotheosis "}
+57
View File
@@ -116,6 +116,63 @@ body::before {
text-align: center;
}
/* ── Resource toggle + dropdown ─────────────────────────────────────────── */
.resource-menu {
position: relative;
}
.resource-toggle {
background: none;
border: none;
border-radius: 0.4rem;
cursor: pointer;
font-family: inherit;
padding: 0.2rem 0.4rem;
position: relative;
transition: background 0.15s;
}
.resource-toggle:hover {
background: rgba(255, 255, 255, 0.07);
}
.resource-alert-dot {
background: var(--colour-warning, #f59e0b);
border-radius: 50%;
height: 0.45rem;
position: absolute;
right: 0;
top: 0;
width: 0.45rem;
}
.resources-dropdown {
background: var(--colour-surface);
border: 1px solid rgba(147, 51, 234, 0.4);
border-radius: 0.5rem;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
display: flex;
flex-direction: column;
gap: 0.1rem;
left: 0;
padding: 0.4rem;
position: absolute;
top: calc(100% + 0.4rem);
z-index: 100;
}
.resources-dropdown .resource {
border-radius: 0.35rem;
gap: 0.5rem;
padding: 0.3rem 0.5rem;
white-space: nowrap;
}
.resources-dropdown .resource:hover {
background: rgba(255, 255, 255, 0.04);
}
/* ===================== GAME LAYOUT ===================== */
.game-layout {
display: flex;