add acc slider to the pp graph
Some checks failed
Deploy Website / docker (ubuntu-latest) (push) Has been cancelled
Some checks failed
Deploy Website / docker (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
parent
b7349f0226
commit
077bb6d73b
@ -1,10 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import GenericChart, { DatasetConfig } from "@/components/chart/generic-chart";
|
import GenericChart, { DatasetConfig } from "@/components/chart/generic-chart";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
||||||
import Card from "@/components/card";
|
import Card from "@/components/card";
|
||||||
|
import { DualRangeSlider } from "@/components/ui/dual-range-slider";
|
||||||
|
import { useDebounce } from "@uidotdev/usehooks";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/**
|
/**
|
||||||
@ -14,10 +16,15 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function LeaderboardPpChart({ leaderboard }: Props) {
|
export default function LeaderboardPpChart({ leaderboard }: Props) {
|
||||||
|
const [values, setValues] = useState([60, 100]);
|
||||||
|
const debouncedMin = useDebounce(values[0], 100);
|
||||||
|
|
||||||
const histories: Record<string, (number | null)[]> = {};
|
const histories: Record<string, (number | null)[]> = {};
|
||||||
const labels: string[] = [];
|
const labels: string[] = [];
|
||||||
|
const min = debouncedMin;
|
||||||
|
const precision = min >= 60 ? 0.1 : 0.2;
|
||||||
|
|
||||||
for (let accuracy = 60; accuracy <= 100; accuracy += 0.2) {
|
for (let accuracy = min; accuracy <= 100; accuracy += precision) {
|
||||||
const label = accuracy.toFixed(2) + "%";
|
const label = accuracy.toFixed(2) + "%";
|
||||||
labels.push(label);
|
labels.push(label);
|
||||||
|
|
||||||
@ -45,9 +52,24 @@ export default function LeaderboardPpChart({ leaderboard }: Props) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="h-64 w-full">
|
<Card className="w-full gap-7">
|
||||||
|
<div className="flex flex-col h-64">
|
||||||
<p className="font-semibold">PP Curve</p>
|
<p className="font-semibold">PP Curve</p>
|
||||||
<GenericChart labels={labels} datasetConfig={datasetConfig} histories={histories} />
|
<GenericChart labels={labels} datasetConfig={datasetConfig} histories={histories} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<div className="w-[95%]">
|
||||||
|
<DualRangeSlider
|
||||||
|
label={value => <span>{value}%</span>}
|
||||||
|
value={values}
|
||||||
|
onValueChange={setValues}
|
||||||
|
min={5}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
48
projects/website/src/components/ui/dual-range-slider.tsx
Normal file
48
projects/website/src/components/ui/dual-range-slider.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||||
|
import { cn } from "@/common/utils";
|
||||||
|
|
||||||
|
interface DualRangeSliderProps extends React.ComponentProps<typeof SliderPrimitive.Root> {
|
||||||
|
labelPosition?: "top" | "bottom";
|
||||||
|
label?: (value: number | undefined) => React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DualRangeSlider = React.forwardRef<React.ElementRef<typeof SliderPrimitive.Root>, DualRangeSliderProps>(
|
||||||
|
({ className, label, labelPosition = "top", ...props }, ref) => {
|
||||||
|
const initialValue = Array.isArray(props.value) ? props.value : [props.min, props.max];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SliderPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn("relative flex w-full touch-none select-none items-center text-[15px]", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
|
||||||
|
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||||
|
</SliderPrimitive.Track>
|
||||||
|
{initialValue.map((value, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50">
|
||||||
|
{label && (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"absolute flex w-full justify-center",
|
||||||
|
labelPosition === "top" && "-top-7",
|
||||||
|
labelPosition === "bottom" && "top-4"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{label(value)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</SliderPrimitive.Thumb>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</SliderPrimitive.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
DualRangeSlider.displayName = "DualRangeSlider";
|
||||||
|
|
||||||
|
export { DualRangeSlider };
|
Reference in New Issue
Block a user