style: refine leaderboard component layouts for better visual consistency

This commit is contained in:
FloPinguin
2026-02-22 17:38:21 +01:00
parent 4672720f7d
commit 67e5cd0e51
3 changed files with 124 additions and 127 deletions
@@ -195,9 +195,9 @@ export class LeaderboardClanTable extends LitElement {
const maxGames = Math.max(...clans.map((c) => c.games), 1);
return html`
<div class="h-full px-6 pb-6">
<div class="h-full">
<div
class="h-full overflow-y-auto overflow-x-auto rounded-xl border border-white/5 bg-black/20"
class="h-full overflow-y-auto overflow-x-auto border border-white/5 bg-black/20"
>
<table class="w-full text-sm border-collapse">
<thead>
@@ -214,7 +214,7 @@ export class LeaderboardClanTable extends LitElement {
class="py-4 px-4 text-right font-bold w-32 cursor-pointer hover:text-white/60 transition-colors"
>
<button
class="whitespace-nowrap"
class="whitespace-nowrap uppercase"
@click=${() => this.handleSort("games")}
aria-sort=${this.sortBy === "games"
? this.sortOrder === "asc"
@@ -235,7 +235,7 @@ export class LeaderboardClanTable extends LitElement {
title=${translateText("leaderboard_modal.win_score_tooltip")}
>
<button
class="whitespace-nowrap"
class="whitespace-nowrap uppercase"
@click=${() => this.handleSort("winScore")}
aria-sort=${this.sortBy === "winScore"
? this.sortOrder === "asc"
@@ -256,7 +256,7 @@ export class LeaderboardClanTable extends LitElement {
title=${translateText("leaderboard_modal.loss_score_tooltip")}
>
<button
class="whitespace-nowrap"
class="whitespace-nowrap uppercase"
@click=${() => this.handleSort("lossScore")}
aria-sort=${this.sortBy === "lossScore"
? this.sortOrder === "asc"
@@ -276,7 +276,7 @@ export class LeaderboardClanTable extends LitElement {
class="py-4 px-4 text-right font-bold pr-6 cursor-pointer hover:text-white/60 transition-colors"
>
<button
class="whitespace-nowrap"
class="whitespace-nowrap uppercase"
@click=${() => this.handleSort("ratio")}
aria-sort=${this.sortBy === "ratio"
? this.sortOrder === "asc"
@@ -1,4 +1,3 @@
import { virtualize } from "@lit-labs/virtualizer/virtualize.js";
import { html, LitElement } from "lit";
import { customElement, query, state } from "lit/decorators.js";
import { PlayerLeaderboardEntry } from "../../../core/ApiSchemas";
@@ -228,62 +227,62 @@ export class LeaderboardPlayerList extends LitElement {
}?.[displayRank] ?? String(displayRank);
return html`
<div
<tr
data-current-user=${isCurrentUser ? "true" : "false"}
class="flex items-center border-b border-white/5 py-3 px-4 hover:bg-white/[0.07] transition-colors group w-full ${isCurrentUser
? "bg-blue-500/15 border-l-4 border-l-blue-500 pl-3"
class="border-b border-white/5 hover:bg-white/[0.07] transition-colors group ${isCurrentUser
? "bg-blue-500/15"
: ""}"
>
<div class="w-10 shrink-0 text-center">
<td class="py-3 px-4 text-center">
<div
class="w-10 h-10 mx-auto flex items-center justify-center rounded-lg font-bold font-mono text-lg ${rankColor}"
>
${rankIcon}
</div>
</div>
<div
class="flex-1 min-w-0 flex items-center gap-2 overflow-hidden ml-4"
>
<span class="font-bold text-blue-300 truncate text-base"
>${player.username}</span
>
${player.clanTag
? html`<div
class="px-2 py-0.5 rounded bg-blue-500/10 border border-blue-500/20 text-[10px] font-bold text-blue-300 shrink-0"
>
${player.clanTag}
</div>`
: ""}
</div>
<div class="flex flex-col items-end gap-1 w-20 shrink-0">
<div class="text-right font-mono text-white font-medium">
${player.elo}
<span class="text-[10px] text-white/30 truncate"
>${translateText("leaderboard_modal.elo")}</span
</td>
<td class="py-3 px-4">
<div class="flex items-center gap-2">
${player.clanTag
? html`<div
class="px-2 py-0.5 rounded bg-blue-500/10 border border-blue-500/20 text-[10px] font-bold text-blue-300 shrink-0"
>
${player.clanTag.replace(/^\[|\]$/g, "")}
</div>`
: ""}
<span class="font-bold text-blue-300 truncate text-base"
>${player.clanTag
? player.username.replace(/^\[.*?\]\s*/, "")
: player.username}</span
>
</div>
</div>
<div class="flex flex-col items-end gap-1 w-20 shrink-0">
<div class="text-right font-mono text-white font-medium">
${player.games}
<span class="text-[10px] text-white/30 uppercase"
>${translateText("leaderboard_modal.games")}</span
</td>
<td class="py-3 px-4 text-right">
<span class="font-mono text-white font-medium">${player.elo}</span>
<span class="text-[10px] text-white/30 ml-1"
>${translateText("leaderboard_modal.elo")}</span
>
</td>
<td class="py-3 px-4 text-right">
<span class="font-mono text-white font-medium">${player.games}</span>
<span class="text-[10px] text-white/30 uppercase ml-1"
>${translateText("leaderboard_modal.games")}</span
>
</td>
<td class="py-3 px-4 text-right pr-6">
<div class="inline-flex flex-col items-end">
<span
class="font-mono font-bold ${winRate >= 0.5
? "text-green-400"
: "text-red-400"}"
>${(winRate * 100).toFixed(1)}%</span
>
<span
class="text-[10px] uppercase text-white/30 font-bold tracking-wider"
>${translateText("leaderboard_modal.ratio")}</span
>
</div>
</div>
<div class="inline-flex flex-col items-end pr-4 w-20 shrink-0">
<span
class="font-mono font-bold ${winRate >= 0.5
? "text-green-400"
: "text-red-400"}"
>${(winRate * 100).toFixed(1)}%</span
>
<span
class="text-[10px] uppercase text-white/30 font-bold tracking-wider"
>${translateText("leaderboard_modal.ratio")}</span
>
</div>
</div>
</td>
</tr>
`;
}
@@ -374,87 +373,85 @@ export class LeaderboardPlayerList extends LitElement {
if (this.error) return this.renderError();
return html`
<div class="h-full px-6 pb-6">
<div class="h-full">
<div
class="h-full overflow-y-auto overflow-x-auto rounded-xl border border-white/5 bg-black/20"
class="h-full overflow-y-auto overflow-x-auto border border-white/5 bg-black/20 relative"
>
<div class="min-w-[36rem] flex flex-col h-full">
<div
class="flex items-center text-[10px] uppercase tracking-wider text-white/40 font-bold px-4 py-4 border-b border-white/5 bg-white/2"
>
<div class="w-10 text-center">
${translateText("leaderboard_modal.rank")}
</div>
<div class="flex-1 ml-4">
${translateText("leaderboard_modal.player")}
</div>
<div class="w-20 text-right">
${translateText("leaderboard_modal.elo")}
</div>
<div class="w-20 text-right">
${translateText("leaderboard_modal.games")}
</div>
<div class="w-20 text-right pr-4">
${translateText("leaderboard_modal.win_loss_ratio")}
</div>
</div>
<div class="relative flex-1 min-h-0">
<div
class="virtualizer-container h-full overflow-y-auto scrollbar-thin scrollbar-thumb-white/20 ${this
.showStickyUser
? "pb-20"
: "pb-0"}"
@scroll=${() => this.handleScroll()}
>
${virtualize({
items: this.playerData,
renderItem: (player) => this.renderPlayerRow(player),
scroller: true,
})}
${this.renderPlayerFooter()}
</div>
${this.currentUserEntry
? html`
<div class="absolute inset-x-0 bottom-0">
<div
class="virtualizer-container h-full overflow-y-auto scrollbar-thin scrollbar-thumb-white/20 ${this
.showStickyUser
? "pb-20"
: "pb-0"}"
@scroll=${() => this.handleScroll()}
>
<table class="w-full text-sm border-collapse">
<thead>
<tr
class="text-white/40 text-[10px] uppercase tracking-wider border-b border-white/5 bg-white/2"
>
<th class="py-4 px-4 text-center font-bold w-16">
${translateText("leaderboard_modal.rank")}
</th>
<th class="py-4 px-4 text-left font-bold">
${translateText("leaderboard_modal.player")}
</th>
<th class="py-4 px-4 text-right font-bold">
${translateText("leaderboard_modal.elo")}
</th>
<th class="py-4 px-4 text-right font-bold">
${translateText("leaderboard_modal.games")}
</th>
<th class="py-4 px-4 text-right font-bold pr-6">
${translateText("leaderboard_modal.win_loss_ratio")}
</th>
</tr>
</thead>
<tbody>
${this.playerData.map((player) => this.renderPlayerRow(player))}
</tbody>
</table>
${this.renderPlayerFooter()}
</div>
${this.currentUserEntry
? html`
<div class="absolute inset-x-0 bottom-0 z-20">
<div
class="bg-blue-600/90 backdrop-blur-md border-t border-blue-400/30 py-4 px-6 shadow-2xl flex items-center transition-all duration-200 ${this
.showStickyUser
? "opacity-100 translate-y-0"
: "opacity-0 translate-y-3 pointer-events-none"}"
aria-hidden=${this.showStickyUser ? "false" : "true"}
>
<div class="w-10 text-center">
<div
class="bg-blue-600/90 backdrop-blur-md border-t border-blue-400/30 py-4 px-6 shadow-2xl flex items-center transition-all duration-200 ${this
.showStickyUser
? "opacity-100 translate-y-0"
: "opacity-0 translate-y-3 pointer-events-none"}"
aria-hidden=${this.showStickyUser ? "false" : "true"}
class="w-10 h-10 mx-auto flex items-center justify-center rounded-lg font-bold font-mono text-lg bg-white/20 text-white"
>
<div class="w-10 text-center">
<div
class="w-10 h-10 mx-auto flex items-center justify-center rounded-lg font-bold font-mono text-lg bg-white/20 text-white"
>
${this.currentUserEntry.rank}
</div>
</div>
<div class="flex-1 flex flex-col ml-4">
<span
class="text-[10px] uppercase font-bold text-blue-200/60 leading-tight"
>${translateText(
"leaderboard_modal.your_ranking",
)}</span
>
<span class="font-bold text-white text-base"
>${this.currentUserEntry.username}</span
>
</div>
<div class="flex flex-col items-end w-20">
<div class="font-mono text-white font-bold text-lg">
${this.currentUserEntry.elo}
<span class="text-[10px] text-white/60"
>${translateText("leaderboard_modal.elo")}</span
>
</div>
</div>
${this.currentUserEntry.rank}
</div>
</div>
`
: ""}
</div>
</div>
<div class="flex-1 flex flex-col ml-4">
<span
class="text-[10px] uppercase font-bold text-blue-200/60 leading-tight"
>${translateText(
"leaderboard_modal.your_ranking",
)}</span
>
<span class="font-bold text-white text-base"
>${this.currentUserEntry.username}</span
>
</div>
<div class="flex flex-col items-end w-20">
<div class="font-mono text-white font-bold text-lg">
${this.currentUserEntry.elo}
<span class="text-[10px] text-white/60"
>${translateText("leaderboard_modal.elo")}</span
>
</div>
</div>
</div>
</div>
`
: ""}
</div>
</div>
`;
@@ -47,7 +47,7 @@ export class LeaderboardTabs extends LitElement {
return html`
<div
role="tablist"
class="flex gap-2 p-1 bg-white/5 rounded-full border border-white/10 mb-6 w-fit mx-auto mt-4"
class="flex gap-2 p-1 bg-white/5 rounded-full border border-white/10 mb-4 w-fit mx-auto mt-4"
>
<button
type="button"