fix(topbar): coin balance was clipped — compact large numbers + shrink bar
The Home top bar overflowed on narrow screens; in RTL the coins pill is the far-left item so its leading digits got clipped (showed "04,240" for 104,240). - CoinsPill: compact big balances (104,240 → 104K, 1.2M), shrink-0 + whitespace-nowrap; exact value in the tooltip. - TopBar: tighter gaps, profile pill min-w-0 (shrinks/truncates first), icon+coins group shrink-0 so it never gets squeezed. Verified: tsc + next build clean; web rebuilt on :1500. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,13 @@ import { useUIStore } from "@/lib/ui-store";
|
|||||||
import { useI18n } from "@/lib/i18n";
|
import { useI18n } from "@/lib/i18n";
|
||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
|
|
||||||
|
/** Compact balance so big numbers don't overflow tight headers (104,240 → 104K). */
|
||||||
|
function fmtCoins(n: number): string {
|
||||||
|
if (n < 10_000) return n.toLocaleString();
|
||||||
|
if (n < 1_000_000) return `${(n / 1000).toFixed(n < 100_000 ? 1 : 0)}K`.replace(".0K", "K");
|
||||||
|
return `${(n / 1_000_000).toFixed(1)}M`.replace(".0M", "M");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The coin balance, as a button that opens the buy-coins store. Use it anywhere
|
* The coin balance, as a button that opens the buy-coins store. Use it anywhere
|
||||||
* coins are shown so tapping the balance always leads to topping up.
|
* coins are shown so tapping the balance always leads to topping up.
|
||||||
@@ -17,15 +24,15 @@ export function CoinsPill({ className }: { className?: string }) {
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => go("buycoins")}
|
onClick={() => go("buycoins")}
|
||||||
title={t("buy.title")}
|
title={`${coins.toLocaleString()} — ${t("buy.title")}`}
|
||||||
aria-label={t("buy.title")}
|
aria-label={t("buy.title")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"glass rounded-full ltr:pl-3 rtl:pr-3 ltr:pr-1 rtl:pl-1 py-1 flex items-center gap-1.5 hover:bg-navy-800/80 active:scale-95 transition",
|
"glass rounded-full ltr:pl-3 rtl:pr-3 ltr:pr-1 rtl:pl-1 py-1 flex items-center gap-1.5 shrink-0 hover:bg-navy-800/80 active:scale-95 transition",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Coins className="size-3.5 text-gold-400 shrink-0" />
|
<Coins className="size-3.5 text-gold-400 shrink-0" />
|
||||||
<span className="text-xs font-bold text-gold-300 tabular-nums">{coins.toLocaleString()}</span>
|
<span className="text-xs font-bold text-gold-300 tabular-nums whitespace-nowrap">{fmtCoins(coins)}</span>
|
||||||
<span className="grid size-5 place-items-center rounded-full btn-gold shrink-0">
|
<span className="grid size-5 place-items-center rounded-full btn-gold shrink-0">
|
||||||
<Plus className="size-3" strokeWidth={3.5} />
|
<Plus className="size-3" strokeWidth={3.5} />
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ export function TopBar() {
|
|||||||
const xpPct = maxed ? 100 : Math.min(100, Math.max(0, Math.round((profile.xp / xpNeed) * 100)));
|
const xpPct = maxed ? 100 : Math.min(100, Math.max(0, Math.round((profile.xp / xpNeed) * 100)));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => go("profile")}
|
onClick={() => go("profile")}
|
||||||
className="glass rounded-full ltr:pr-4 rtl:pl-4 ltr:pl-1.5 rtl:pr-1.5 py-1.5 flex items-center gap-2 hover:bg-navy-800/80 transition"
|
className="glass rounded-full ltr:pr-4 rtl:pl-4 ltr:pl-1.5 rtl:pr-1.5 py-1.5 flex items-center gap-2 min-w-0 hover:bg-navy-800/80 transition"
|
||||||
>
|
>
|
||||||
<span className="relative size-9 rounded-full bg-navy-900 gold-border flex items-center justify-center overflow-hidden">
|
<span className="relative size-9 shrink-0 rounded-full bg-navy-900 gold-border flex items-center justify-center overflow-hidden">
|
||||||
<Avatar id={profile.avatar} image={profile.avatarImage} size={profile.avatarImage ? 36 : 26} />
|
<Avatar id={profile.avatar} image={profile.avatarImage} size={profile.avatarImage ? 36 : 26} />
|
||||||
</span>
|
</span>
|
||||||
<span className="text-start leading-tight min-w-[96px]">
|
<span className="text-start leading-tight min-w-0 w-[88px] sm:w-24">
|
||||||
<span className="flex items-center gap-1 text-sm font-bold text-cream max-w-28 truncate">
|
<span className="flex items-center gap-1 text-sm font-bold text-cream truncate">
|
||||||
{profile.displayName}
|
{profile.displayName}
|
||||||
{profile.plan === "pro" && <Crown className="size-3 text-gold-400 fill-gold-500 shrink-0" />}
|
{profile.plan === "pro" && <Crown className="size-3 text-gold-400 fill-gold-500 shrink-0" />}
|
||||||
</span>
|
</span>
|
||||||
@@ -48,7 +48,7 @@ export function TopBar() {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1.5 shrink-0">
|
||||||
<button
|
<button
|
||||||
onClick={() => go("notifications")}
|
onClick={() => go("notifications")}
|
||||||
className="glass rounded-full p-2 hover:bg-navy-800/80 transition relative"
|
className="glass rounded-full p-2 hover:bg-navy-800/80 transition relative"
|
||||||
|
|||||||
Reference in New Issue
Block a user