diff --git a/projects/website/src/components/leaderboard/leaderboard-pp-chart.tsx b/projects/website/src/components/leaderboard/leaderboard-pp-chart.tsx index bfed290..a48be22 100644 --- a/projects/website/src/components/leaderboard/leaderboard-pp-chart.tsx +++ b/projects/website/src/components/leaderboard/leaderboard-pp-chart.tsx @@ -1,10 +1,12 @@ "use client"; -import React from "react"; +import React, { useState } from "react"; import GenericChart, { DatasetConfig } from "@/components/chart/generic-chart"; import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard"; import { scoresaberService } from "@ssr/common/service/impl/scoresaber"; import Card from "@/components/card"; +import { DualRangeSlider } from "@/components/ui/dual-range-slider"; +import { useDebounce } from "@uidotdev/usehooks"; type Props = { /** @@ -14,10 +16,15 @@ type Props = { }; export default function LeaderboardPpChart({ leaderboard }: Props) { + const [values, setValues] = useState([60, 100]); + const debouncedMin = useDebounce(values[0], 100); + const histories: Record = {}; 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) + "%"; labels.push(label); @@ -45,9 +52,24 @@ export default function LeaderboardPpChart({ leaderboard }: Props) { ]; return ( - -

PP Curve

- + +
+

PP Curve

+ +
+ +
+
+ {value}%} + value={values} + onValueChange={setValues} + min={5} + max={100} + step={1} + /> +
+
); } diff --git a/projects/website/src/components/ui/dual-range-slider.tsx b/projects/website/src/components/ui/dual-range-slider.tsx new file mode 100644 index 0000000..4b9d918 --- /dev/null +++ b/projects/website/src/components/ui/dual-range-slider.tsx @@ -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 { + labelPosition?: "top" | "bottom"; + label?: (value: number | undefined) => React.ReactNode; +} + +const DualRangeSlider = React.forwardRef, DualRangeSliderProps>( + ({ className, label, labelPosition = "top", ...props }, ref) => { + const initialValue = Array.isArray(props.value) ? props.value : [props.min, props.max]; + + return ( + + + + + {initialValue.map((value, index) => ( + + + {label && ( + + {label(value)} + + )} + + + ))} + + ); + } +); +DualRangeSlider.displayName = "DualRangeSlider"; + +export { DualRangeSlider };