forked from phenom/healer-man
Release Healer Man 0.1.39
This commit is contained in:
1 parent
b417b7c3c3
commit
ed0d2fea3d
51 files changed
+17811
-49
No files matched your search
@@ -0,0 +1,70 @@
|
||||
import { DUNGEON_LEADERBOARD_PAGE_SIZE, type LeaderboardScope } from "../app/dungeonLeaderboardClient";
|
||||
import { formatDungeonDuration, type DungeonClearFilter, type DungeonLeaderboard as LeaderboardData } from "../game/dungeonRunTypes";
|
||||
import { ControllerButton } from "./ControllerButton";
|
||||
import { DungeonRunDetails } from "./DungeonRunDetails";
|
||||
import "./dungeonRuns.css";
|
||||
|
||||
export function DungeonLeaderboard({ dungeonTitle, data, scope, loading, filter, onFilter,
|
||||
expanded, onExpand, offset, onPage, onRefresh, difficultyLabel, onDifficulty,
|
||||
selectedId, select }: {
|
||||
readonly dungeonTitle: string;
|
||||
readonly data: LeaderboardData;
|
||||
readonly scope: LeaderboardScope;
|
||||
readonly loading: boolean;
|
||||
readonly filter: DungeonClearFilter;
|
||||
readonly onFilter: (filter: DungeonClearFilter) => void;
|
||||
readonly expanded: ReadonlySet<string>;
|
||||
readonly onExpand: (id: string) => void;
|
||||
readonly offset: number;
|
||||
readonly onPage: (offset: number) => void;
|
||||
readonly onRefresh: () => void;
|
||||
readonly difficultyLabel: string;
|
||||
readonly onDifficulty?: () => void;
|
||||
readonly selectedId: string;
|
||||
readonly select: (id: string) => void;
|
||||
}) {
|
||||
const controls = { selectedId, select };
|
||||
return <section className="dungeon-leaderboard" aria-label={`${dungeonTitle} leaderboard`} aria-busy={loading}>
|
||||
<header>
|
||||
<p className="front-kicker">{scope === "world" ? "World leaderboard" : "This device · offline leaderboard"}</p>
|
||||
<h3>Fastest expeditions</h3>
|
||||
<div className="dungeon-leaderboard__filters" aria-label="Clear type">
|
||||
<ControllerButton {...controls} controlId="leaderboard-final" aria-pressed={filter === "final"}
|
||||
onClick={() => onFilter("final")}>Final Boss clears <small>All entries</small></ControllerButton>
|
||||
<ControllerButton {...controls} controlId="leaderboard-full" aria-pressed={filter === "full"}
|
||||
onClick={() => onFilter("full")}>Full dungeon clears <small>All bosses</small></ControllerButton>
|
||||
</div>
|
||||
<div className="dungeon-leaderboard__meta">
|
||||
{onDifficulty ? <ControllerButton {...controls} controlId="leaderboard-difficulty" onClick={onDifficulty}
|
||||
aria-label={`Leaderboard difficulty: ${difficultyLabel}. Change difficulty`}>{difficultyLabel} ↻</ControllerButton>
|
||||
: <span>{difficultyLabel}</span>}
|
||||
<span>{data.total} {data.total === 1 ? "run" : "runs"}</span>
|
||||
<ControllerButton {...controls} controlId="leaderboard-refresh" onClick={onRefresh}>Refresh</ControllerButton>
|
||||
</div>
|
||||
</header>
|
||||
<div className="dungeon-leaderboard__entries">
|
||||
{loading ? <p className="dungeon-leaderboard__empty" role="status">Loading run times…</p>
|
||||
: data.entries.length === 0 ? <p className="dungeon-leaderboard__empty">No {filter === "full" ? "full dungeon" : "final boss"} clears yet. Complete a run to set the first time.</p>
|
||||
: <ol>
|
||||
{data.entries.map((entry) => <li key={entry.id}>
|
||||
<ControllerButton {...controls} controlId={`leaderboard-run-${entry.id}`}
|
||||
className="dungeon-leaderboard__run" aria-expanded={expanded.has(entry.id)}
|
||||
aria-controls={`run-details-${entry.id}`} onClick={() => onExpand(entry.id)}>
|
||||
<b>#{entry.rank}</b><span><strong>{entry.party.find((member) => !member.npc)?.name ?? entry.party[0]?.name ?? "Party"}</strong>
|
||||
<small>{entry.fullClear ? "All bosses" : `${entry.killedBossIds.length} bosses`} · {new Date(entry.finishedAt).toLocaleDateString()}</small></span>
|
||||
<time>{formatDungeonDuration(entry.durationMs)}</time><em aria-hidden="true">{expanded.has(entry.id) ? "−" : "+"}</em>
|
||||
</ControllerButton>
|
||||
{expanded.has(entry.id) ? <div id={`run-details-${entry.id}`}><DungeonRunDetails dungeonId={entry.dungeonId}
|
||||
difficultyId={entry.difficultyId} party={entry.party} killedBossIds={entry.killedBossIds} /></div> : null}
|
||||
</li>)}
|
||||
</ol>}
|
||||
</div>
|
||||
{data.total > DUNGEON_LEADERBOARD_PAGE_SIZE ? <nav className="dungeon-leaderboard__pages" aria-label="Leaderboard pages">
|
||||
<ControllerButton {...controls} controlId="leaderboard-prev" disabled={offset === 0 || loading}
|
||||
onClick={() => onPage(Math.max(0, offset - DUNGEON_LEADERBOARD_PAGE_SIZE))}>Previous</ControllerButton>
|
||||
<span>{offset + 1}–{Math.min(offset + DUNGEON_LEADERBOARD_PAGE_SIZE, data.total)} of {data.total}</span>
|
||||
<ControllerButton {...controls} controlId="leaderboard-next" disabled={offset + DUNGEON_LEADERBOARD_PAGE_SIZE >= data.total || loading}
|
||||
onClick={() => onPage(offset + DUNGEON_LEADERBOARD_PAGE_SIZE)}>Next</ControllerButton>
|
||||
</nav> : null}
|
||||
</section>;
|
||||
}
|
||||
Reference in new issue
Block a user