mirror of
https://github.com/Xevion/smart-rgb.git
synced 2025-12-15 12:13:20 -06:00
74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
import type { AttackEntry, LeaderboardEntry } from "@/shared/api/types";
|
|
|
|
// Format troop count for human-readable display
|
|
// 100 => "100", 12,493 => "12.4k", 980,455 => "980k"
|
|
export function formatTroopCount(count: number): string {
|
|
if (count < 1000) return count.toString();
|
|
if (count < 1000000) {
|
|
const k = count / 1000;
|
|
return k % 1 === 0 ? `${k}k` : `${k.toFixed(1)}k`;
|
|
}
|
|
const m = count / 1000000;
|
|
return m % 1 === 0 ? `${m}M` : `${m.toFixed(1)}M`;
|
|
}
|
|
|
|
// Calculate the background width percentage based on troop count
|
|
// Uses power scale (x^0.4): 100 troops = 5%, 1k = 7.9%, 10k = 15.3%, 100k = 33.7%, 1M = 80%
|
|
function calculateBackgroundWidth(troops: number): number {
|
|
const minTroops = 100;
|
|
const maxTroops = 1000000;
|
|
const minWidth = 5;
|
|
const maxWidth = 80;
|
|
|
|
// Clamp troops to range
|
|
const clampedTroops = Math.max(minTroops, Math.min(maxTroops, troops));
|
|
|
|
// Power scale with exponent 0.4 provides gentler progression than logarithmic
|
|
const powerMin = Math.pow(minTroops, 0.4);
|
|
const powerMax = Math.pow(maxTroops, 0.4);
|
|
const powerTroops = Math.pow(clampedTroops, 0.4);
|
|
|
|
// Map to 5-80% range
|
|
const normalized = (powerTroops - powerMin) / (powerMax - powerMin);
|
|
return minWidth + normalized * (maxWidth - minWidth);
|
|
}
|
|
|
|
interface AttackRowProps {
|
|
attack: AttackEntry;
|
|
playerMap: Map<number, LeaderboardEntry>;
|
|
onNationHover?: (nationId: number | null) => void;
|
|
}
|
|
|
|
export function AttackRow({ attack, playerMap, onNationHover }: AttackRowProps) {
|
|
// For outgoing attacks, show target's name (who we're attacking)
|
|
// For incoming attacks, show attacker's name (who is attacking us)
|
|
const displayPlayerId = attack.is_outgoing ? attack.target_id : attack.attacker_id;
|
|
|
|
const displayPlayer = displayPlayerId !== null ? playerMap.get(displayPlayerId) : null;
|
|
const displayName = displayPlayer?.name || "Unclaimed Territory";
|
|
|
|
const backgroundWidth = calculateBackgroundWidth(attack.troops);
|
|
const backgroundColor = attack.is_outgoing
|
|
? "rgba(59, 130, 246, 0.3)" // Blue with 30% opacity
|
|
: "rgba(239, 68, 68, 0.3)"; // Red with 30% opacity
|
|
|
|
return (
|
|
<button
|
|
className="attacks__row"
|
|
onClick={() => console.log("Attack clicked:", attack)}
|
|
onMouseEnter={() => displayPlayerId !== null && onNationHover?.(displayPlayerId)}
|
|
onMouseLeave={() => onNationHover?.(null)}
|
|
>
|
|
<div
|
|
className="attacks__background"
|
|
style={{
|
|
width: `${backgroundWidth}%`,
|
|
backgroundColor,
|
|
}}
|
|
/>
|
|
<div className="attacks__nation">{displayName}</div>
|
|
<div className="attacks__troops">{formatTroopCount(attack.troops)}</div>
|
|
</button>
|
|
);
|
|
}
|