made the player page look much nicer
All checks were successful
Deploy Website / docker (ubuntu-latest) (push) Successful in 2m23s
All checks were successful
Deploy Website / docker (ubuntu-latest) (push) Successful in 2m23s
This commit is contained in:
parent
f156b7f582
commit
1c2214a659
@ -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 & {
|
||||||
/**
|
/**
|
||||||
@ -34,69 +32,82 @@ export default function ScoreMissesAndPausesBadge({ score, hideXMark }: ScoreMis
|
|||||||
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">
|
||||||
<ScoreMissesTooltip
|
<div className="flex items-center gap-1">
|
||||||
missedNotes={score.missedNotes}
|
<ScoreMissesTooltip
|
||||||
badCuts={score.badCuts}
|
missedNotes={score.missedNotes}
|
||||||
bombCuts={misses?.bombCuts}
|
badCuts={score.badCuts}
|
||||||
wallsHit={misses?.wallsHit}
|
bombCuts={misses?.bombCuts}
|
||||||
fullCombo={score.fullCombo}
|
wallsHit={misses?.wallsHit}
|
||||||
>
|
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 && previousScoreMisses && scoreImprovement && misses && isMissImprovement && (
|
||||||
{additionalData && !!pauses && pauses > 0 && (
|
<ScoreMissesTooltip
|
||||||
<>
|
missedNotes={previousScoreMisses.missedNotes}
|
||||||
<p>|</p>
|
badCuts={previousScoreMisses.badCuts}
|
||||||
<Tooltip
|
bombCuts={previousScoreMisses.bombCuts}
|
||||||
display={
|
wallsHit={previousScoreMisses.wallsHit}
|
||||||
<p>
|
fullCombo={previousScoreFc}
|
||||||
{pauses}x Pause{pauses > 1 ? "s" : ""}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div className="flex gap-1 items-center">
|
<div className="text-xs flex flex-row gap-1">
|
||||||
<p>{pauses && pauses}</p>
|
<p>(vs {previousScoreFc ? "FC" : formatNumberWithCommas(previousScoreMisses.misses)}x)</p>
|
||||||
<PauseIcon className="w-4 h-4" />
|
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</ScoreMissesTooltip>
|
||||||
</>
|
)}
|
||||||
)}
|
|
||||||
</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>-></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>
|
||||||
)}
|
{/*{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>-></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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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">
|
||||||
<ScoreBadges badges={badges} score={score} leaderboard={leaderboard} />
|
<div className={`grid grid-cols-3 gap-1 ml-0 lg:ml-2 justify-center`}>
|
||||||
|
<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}
|
||||||
|
Reference in New Issue
Block a user