made the player page look much nicer
All checks were successful
Deploy Website / docker (ubuntu-latest) (push) Successful in 2m23s

This commit is contained in:
Lee 2024-10-28 16:22:33 +00:00
parent f156b7f582
commit 1c2214a659
8 changed files with 112 additions and 81 deletions

@ -21,7 +21,7 @@ export function HandAccuracyBadge({ score, hand }: HandAccuracyProps) {
const formattedHand = capitalizeFirstLetter(hand); const formattedHand = capitalizeFirstLetter(hand);
return ( return (
<div className="flex flex-col items-center justify-center"> <div className="flex gap-1 items-center justify-center">
<Tooltip <Tooltip
display={ display={
<> <>

@ -1,9 +1,7 @@
import { formatNumberWithCommas } from "@ssr/common/utils/number-utils"; import { formatNumberWithCommas } from "@ssr/common/utils/number-utils";
import { PauseIcon } from "@heroicons/react/24/solid";
import { ScoreBadgeProps } from "@/components/score/badges/badge-props"; import { ScoreBadgeProps } from "@/components/score/badges/badge-props";
import { ScoreMissesTooltip } from "@/components/score/score-misses-tooltip"; import { ScoreMissesTooltip } from "@/components/score/score-misses-tooltip";
import { Misses } from "@ssr/common/model/additional-score-data/misses"; import { Misses } from "@ssr/common/model/additional-score-data/misses";
import Tooltip from "@/components/tooltip";
type ScoreMissesBadgeProps = ScoreBadgeProps & { type ScoreMissesBadgeProps = ScoreBadgeProps & {
/** /**
@ -33,6 +31,7 @@ export default function ScoreMissesAndPausesBadge({ score, hideXMark }: ScoreMis
const pauses = additionalData?.pauses; const pauses = additionalData?.pauses;
return ( return (
<div className="flex flex-col justify-center items-center w-full"> <div className="flex flex-col justify-center items-center w-full">
<div className="flex items-center gap-1">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<ScoreMissesTooltip <ScoreMissesTooltip
missedNotes={score.missedNotes} missedNotes={score.missedNotes}
@ -41,33 +40,12 @@ export default function ScoreMissesAndPausesBadge({ score, hideXMark }: ScoreMis
wallsHit={misses?.wallsHit} wallsHit={misses?.wallsHit}
fullCombo={score.fullCombo} fullCombo={score.fullCombo}
> >
<div className="flex items-center">
<p> <p>
{score.fullCombo ? <span className="text-green-400">FC</span> : formatNumberWithCommas(score.misses)} {score.fullCombo ? <span className="text-green-400">FC</span> : formatNumberWithCommas(score.misses)}
{!hideXMark && !score.fullCombo && <span>x</span>} {!hideXMark && !score.fullCombo && <span>x</span>}
</p> </p>
</div>
</ScoreMissesTooltip> </ScoreMissesTooltip>
{additionalData && !!pauses && pauses > 0 && (
<>
<p>|</p>
<Tooltip
display={
<p>
{pauses}x Pause{pauses > 1 ? "s" : ""}
</p>
}
>
<div className="flex gap-1 items-center">
<p>{pauses && pauses}</p>
<PauseIcon className="w-4 h-4" />
</div>
</Tooltip>
</>
)}
</div>
{additionalData && previousScoreMisses && scoreImprovement && misses && isMissImprovement && ( {additionalData && previousScoreMisses && scoreImprovement && misses && isMissImprovement && (
<div className="flex gap-2 items-center justify-center">
<ScoreMissesTooltip <ScoreMissesTooltip
missedNotes={previousScoreMisses.missedNotes} missedNotes={previousScoreMisses.missedNotes}
badCuts={previousScoreMisses.badCuts} badCuts={previousScoreMisses.badCuts}
@ -75,28 +53,61 @@ export default function ScoreMissesAndPausesBadge({ score, hideXMark }: ScoreMis
wallsHit={previousScoreMisses.wallsHit} wallsHit={previousScoreMisses.wallsHit}
fullCombo={previousScoreFc} fullCombo={previousScoreFc}
> >
<div className="flex gap-1 items-center text-xs"> <div className="text-xs flex flex-row gap-1">
{previousScoreFc ? ( <p>(vs {previousScoreFc ? "FC" : formatNumberWithCommas(previousScoreMisses.misses)}x)</p>
<p className="text-green-400">FC</p>
) : (
formatNumberWithCommas(previousScoreMisses.misses)
)}
</div> </div>
</ScoreMissesTooltip> </ScoreMissesTooltip>
<p>-&gt;</p>
<ScoreMissesTooltip
missedNotes={misses.missedNotes}
badCuts={misses.badCuts}
bombCuts={misses.bombCuts}
wallsHit={misses.wallsHit}
fullCombo={additionalData.fullCombo}
>
<div className="flex gap-1 items-center text-xs">
{additionalData.fullCombo ? <p className="text-green-400">FC</p> : formatNumberWithCommas(misses.misses)}
</div>
</ScoreMissesTooltip>
</div>
)} )}
</div> </div>
{/*{additionalData && !!pauses && pauses > 0 && (*/}
{/* <>*/}
{/* <p>|</p>*/}
{/* <Tooltip*/}
{/* display={*/}
{/* <p>*/}
{/* {pauses}x Pause{pauses > 1 ? "s" : ""}*/}
{/* </p>*/}
{/* }*/}
{/* >*/}
{/* <div className="flex gap-1 items-center">*/}
{/* <p>{pauses && pauses}</p>*/}
{/* <PauseIcon className="w-4 h-4" />*/}
{/* </div>*/}
{/* </Tooltip>*/}
{/* </>*/}
{/*)}*/}
</div>
{/*{additionalData && previousScoreMisses && scoreImprovement && misses && isMissImprovement && (*/}
{/* <div className="flex gap-2 items-center justify-center">*/}
{/* <ScoreMissesTooltip*/}
{/* missedNotes={previousScoreMisses.missedNotes}*/}
{/* badCuts={previousScoreMisses.badCuts}*/}
{/* bombCuts={previousScoreMisses.bombCuts}*/}
{/* wallsHit={previousScoreMisses.wallsHit}*/}
{/* fullCombo={previousScoreFc}*/}
{/* >*/}
{/* <div className="flex gap-1 items-center text-xs">*/}
{/* {previousScoreFc ? (*/}
{/* <p className="text-green-400">FC</p>*/}
{/* ) : (*/}
{/* formatNumberWithCommas(previousScoreMisses.misses)*/}
{/* )}*/}
{/* </div>*/}
{/* </ScoreMissesTooltip>*/}
{/* <p>-&gt;</p>*/}
{/* <ScoreMissesTooltip*/}
{/* missedNotes={misses.missedNotes}*/}
{/* badCuts={misses.badCuts}*/}
{/* bombCuts={misses.bombCuts}*/}
{/* wallsHit={misses.wallsHit}*/}
{/* fullCombo={additionalData.fullCombo}*/}
{/* >*/}
{/* <div className="flex gap-1 items-center text-xs">*/}
{/* {additionalData.fullCombo ? <p className="text-green-400">FC</p> : formatNumberWithCommas(misses.misses)}*/}
{/* </div>*/}
{/* </ScoreMissesTooltip>*/}
{/* </div>*/}
{/*)}*/}
</div>
); );
} }

@ -1,6 +1,6 @@
import StatValue from "@/components/stat-value";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score"; import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/model/leaderboard/impl/scoresaber-leaderboard"; import ScoreSaberLeaderboard from "@ssr/common/model/leaderboard/impl/scoresaber-leaderboard";
import clsx from "clsx";
/** /**
* A badge to display in the score stats. * A badge to display in the score stats.
@ -27,6 +27,19 @@ export function ScoreBadges({ badges, score, leaderboard }: ScoreBadgeProps) {
if (toRender === undefined) { if (toRender === undefined) {
return <div key={index} />; return <div key={index} />;
} }
return <StatValue key={index} color={color} value={toRender} />; return (
<div
key={index}
className={clsx(
"flex h-fit p-1 items-center justify-center rounded-md text-sm cursor-default",
color ? color : "bg-accent"
)}
style={{
backgroundColor: (!color?.includes("bg") && color) || undefined,
}}
>
{toRender}
</div>
);
}); });
} }

@ -63,7 +63,7 @@ export default function ScoreSongInfo({ leaderboard, beatSaverMap }: Props) {
> >
{leaderboard.songName} {leaderboard.songSubName} {leaderboard.songName} {leaderboard.songSubName}
</Link> </Link>
<div className="flex flex-col text-sm"> <div className="flex flex-row text-sm gap-2">
<p className="text-gray-400">{leaderboard.songAuthorName}</p> <p className="text-gray-400">{leaderboard.songAuthorName}</p>
<FallbackLink <FallbackLink
href={mappersProfile} href={mappersProfile}

@ -72,8 +72,10 @@ type Props = {
export default function ScoreStats({ score, leaderboard }: Props) { export default function ScoreStats({ score, leaderboard }: Props) {
return ( return (
<div className={`grid grid-cols-3 grid-rows-2 gap-1 ml-0 lg:ml-2 `}> <div className="flex flex-col justify-center h-full">
<div className={`grid grid-cols-3 gap-1 ml-0 lg:ml-2 justify-center`}>
<ScoreBadges badges={badges} score={score} leaderboard={leaderboard} /> <ScoreBadges badges={badges} score={score} leaderboard={leaderboard} />
</div> </div>
</div>
); );
} }

@ -120,12 +120,12 @@ export default function Score({ leaderboard, beatSaverMap, score, settings, high
}; };
const gridColsClass = settings?.noScoreButtons const gridColsClass = settings?.noScoreButtons
? "grid-cols-[20px 1fr_1fr] lg:grid-cols-[0.5fr_4fr_300px]" // Fewer columns if no buttons ? "grid-cols-[20px 1fr_1fr] lg:grid-cols-[0.5fr_4fr_350px]" // Fewer columns if no buttons
: "grid-cols-[20px 1fr_1fr] lg:grid-cols-[0.5fr_4fr_1fr_300px]"; // Original with buttons : "grid-cols-[20px 1fr_1fr] lg:grid-cols-[0.5fr_4fr_1fr_350px]"; // Original with buttons
return ( return (
<div className="pb-2 pt-2"> <div className="pb-2 pt-2 w-full h-full">
<div className={`grid w-full gap-2 lg:gap-0 ${gridColsClass}`}> <div className={`grid w-full h-full gap-2 lg:gap-0 ${gridColsClass}`}>
<ScoreRankInfo score={score} leaderboard={leaderboard} /> <ScoreRankInfo score={score} leaderboard={leaderboard} />
<ScoreSongInfo leaderboard={leaderboard} beatSaverMap={beatSaverMap} /> <ScoreSongInfo leaderboard={leaderboard} beatSaverMap={beatSaverMap} />
{!settings?.noScoreButtons && ( {!settings?.noScoreButtons && (

@ -16,18 +16,24 @@ type Props = {
*/ */
color?: string; color?: string;
/**
* The additional classes for the stat.
*/
className?: string;
/** /**
* The value of the stat. * The value of the stat.
*/ */
value: React.ReactNode; value: React.ReactNode;
}; };
export default function StatValue({ name, icon, color, value }: Props) { export default function StatValue({ name, icon, color, className, value }: Props) {
return ( return (
<div <div
className={clsx( className={clsx(
"flex min-w-16 gap-2 h-full p-1 items-center justify-center rounded-md text-sm cursor-default", "flex min-w-16 gap-2 h-full p-1 items-center justify-center rounded-md text-sm cursor-default",
color ? color : "bg-accent" color ? color : "bg-accent",
className
)} )}
style={{ style={{
backgroundColor: (!color?.includes("bg") && color) || undefined, backgroundColor: (!color?.includes("bg") && color) || undefined,

@ -2,7 +2,7 @@
import { Tooltip as ShadCnTooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Tooltip as ShadCnTooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { useState } from "react"; import { useState } from "react";
import { cn } from "@/common/utils"; import { clsx } from "clsx";
type Props = { type Props = {
/** /**
@ -36,16 +36,15 @@ export default function Tooltip({ children, display, asChild = true, side = "top
return ( return (
<ShadCnTooltip open={open}> <ShadCnTooltip open={open}>
<TooltipTrigger className={className} asChild={asChild}> <TooltipTrigger
<div className={clsx("cursor-default", className)}
className={cn("cursor-default", className)} asChild={asChild}
onMouseEnter={() => setOpen(true)} onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)} onMouseLeave={() => setOpen(false)}
onTouchStart={() => setOpen(!open)} onTouchStart={() => setOpen(!open)}
onTouchEnd={() => setOpen(!open)} onTouchEnd={() => setOpen(!open)}
> >
{children} {children}
</div>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent className="max-w-[350px]" side={side}> <TooltipContent className="max-w-[350px]" side={side}>
{display} {display}