mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-01 15:25:05 -05:00
234 lines
6.5 KiB
TypeScript
234 lines
6.5 KiB
TypeScript
import {
|
|
Box,
|
|
Flex,
|
|
IconButton,
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
Progress,
|
|
useColorMode,
|
|
} from "@chakra-ui/react";
|
|
import { Ability } from "@prisma/client";
|
|
import { ViewSlotsAbilities } from "components/builds/ViewSlots";
|
|
import AbilityIcon from "components/common/AbilityIcon";
|
|
import { useMyTheme } from "hooks/common";
|
|
import { Explanation } from "hooks/useAbilityEffects";
|
|
import { useState } from "react";
|
|
import { FaChartLine, FaQuestion } from "react-icons/fa";
|
|
import { isMainAbility } from "utils/lists/abilities";
|
|
import StatChart from "./StatChart";
|
|
|
|
interface BuildStatsProps {
|
|
explanations: Explanation[];
|
|
otherExplanations?: Explanation[];
|
|
build: ViewSlotsAbilities;
|
|
hideExtra?: boolean;
|
|
showNotActualProgress?: boolean;
|
|
startChartsAtZero?: boolean;
|
|
}
|
|
|
|
const BuildStats: React.FC<BuildStatsProps> = ({
|
|
explanations,
|
|
otherExplanations,
|
|
build,
|
|
hideExtra = true,
|
|
showNotActualProgress = false,
|
|
startChartsAtZero = true,
|
|
}) => {
|
|
const [expandedCharts, setExpandedCharts] = useState<Set<string>>(new Set());
|
|
|
|
const abilityArrays: (Ability | "UNKNOWN")[][] = [
|
|
build.headAbilities ?? [],
|
|
build.clothingAbilities ?? [],
|
|
build.shoesAbilities ?? [],
|
|
];
|
|
|
|
const abilityToPoints: Partial<Record<Ability, number>> = {};
|
|
abilityArrays.forEach((arr) =>
|
|
arr.forEach((ability, index) => {
|
|
if (ability !== "UNKNOWN") {
|
|
let abilityPoints = index === 0 ? 10 : 3;
|
|
if (isMainAbility(ability)) abilityPoints = 999;
|
|
abilityToPoints[ability] = abilityToPoints.hasOwnProperty(ability)
|
|
? (abilityToPoints[ability] as any) + abilityPoints
|
|
: abilityPoints;
|
|
}
|
|
})
|
|
);
|
|
|
|
const BuildStat: React.FC<{
|
|
title: string;
|
|
effect: string;
|
|
otherEffect?: string;
|
|
ability: Ability;
|
|
info?: string;
|
|
progressBarValue: number;
|
|
otherProgressBarValue?: number;
|
|
getEffect?: (ap: number) => number;
|
|
ap: number;
|
|
otherAp?: number;
|
|
chartExpanded: boolean;
|
|
toggleChart: () => void;
|
|
}> = ({
|
|
title,
|
|
effect,
|
|
ability,
|
|
otherEffect,
|
|
otherProgressBarValue,
|
|
getEffect,
|
|
info,
|
|
ap,
|
|
otherAp,
|
|
chartExpanded,
|
|
toggleChart,
|
|
progressBarValue = 0,
|
|
}) => {
|
|
const { themeColorShade, secondaryBgColor } = useMyTheme();
|
|
const { colorMode } = useColorMode();
|
|
|
|
return (
|
|
<>
|
|
<Flex justifyContent="space-between">
|
|
<Flex fontWeight="bold" mr="1em" mb="0.5em" alignItems="center">
|
|
<Box minW="30px">
|
|
<AbilityIcon ability={ability} size="TINY" loading="eager" />
|
|
</Box>
|
|
<IconButton
|
|
aria-label="Show chart for the stat"
|
|
mx="0.5rem"
|
|
icon={<FaChartLine />}
|
|
onClick={() => toggleChart()}
|
|
isRound
|
|
variant="ghost"
|
|
/>
|
|
{title}
|
|
{info && (
|
|
<Popover trigger="hover" placement="top-start">
|
|
<PopoverTrigger>
|
|
<Box>
|
|
<Box
|
|
color={themeColorShade}
|
|
ml="0.2em"
|
|
as={FaQuestion}
|
|
mb="0.2em"
|
|
/>
|
|
</Box>
|
|
</PopoverTrigger>
|
|
<PopoverContent
|
|
zIndex={4}
|
|
p="0.5em"
|
|
bg={secondaryBgColor}
|
|
border="0"
|
|
>
|
|
{info}
|
|
</PopoverContent>
|
|
</Popover>
|
|
)}
|
|
</Flex>
|
|
<Box
|
|
fontWeight="bold"
|
|
color={`orange.${colorMode === "dark" ? "200" : "500"}`}
|
|
alignSelf="flex-end"
|
|
>
|
|
{effect}
|
|
</Box>
|
|
</Flex>
|
|
<Progress
|
|
colorScheme="orange"
|
|
height={otherEffect ? "16px" : "32px"}
|
|
value={progressBarValue}
|
|
bg={colorMode === "dark" ? "#464b64" : `orange.100`}
|
|
/>
|
|
{otherEffect && (
|
|
<>
|
|
<Progress
|
|
colorScheme="blue"
|
|
height="16px"
|
|
value={otherProgressBarValue}
|
|
bg={colorMode === "dark" ? "#464b64" : `blue.100`}
|
|
/>
|
|
<Flex justifyContent="space-between">
|
|
<Box />
|
|
<Box
|
|
fontWeight="bold"
|
|
color={`blue.${colorMode === "dark" ? "200" : "500"}`}
|
|
alignSelf="flex-end"
|
|
>
|
|
{otherEffect}
|
|
</Box>
|
|
</Flex>
|
|
</>
|
|
)}
|
|
{getEffect && chartExpanded && (
|
|
<Box my="1em" ml="-26px">
|
|
<StatChart
|
|
title={title}
|
|
ap={ap}
|
|
otherAp={otherAp}
|
|
getEffect={getEffect}
|
|
startChartsAtZero={startChartsAtZero}
|
|
/>
|
|
</Box>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{explanations.map((_explanation, index) => {
|
|
const explanation = explanations[index];
|
|
const otherExplanation = otherExplanations
|
|
? otherExplanations[index]
|
|
: undefined;
|
|
|
|
if (
|
|
explanation.effectFromMax === 0 &&
|
|
(!otherExplanation || otherExplanation.effectFromMax === 0) &&
|
|
hideExtra
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<Box my="1em" key={explanation.title}>
|
|
<BuildStat
|
|
title={explanation.title}
|
|
ability={explanation.ability}
|
|
effect={explanation.effect}
|
|
progressBarValue={
|
|
showNotActualProgress
|
|
? explanation.effectFromMax
|
|
: explanation.effectFromMaxActual
|
|
}
|
|
otherEffect={otherExplanation?.effect}
|
|
otherProgressBarValue={
|
|
showNotActualProgress
|
|
? otherExplanation?.effectFromMax
|
|
: otherExplanation?.effectFromMaxActual
|
|
}
|
|
getEffect={explanation.getEffect}
|
|
info={explanation.info}
|
|
ap={explanation.ap}
|
|
otherAp={otherExplanation?.ap}
|
|
chartExpanded={expandedCharts.has(explanation.title)}
|
|
toggleChart={() => {
|
|
const newSet = new Set(expandedCharts);
|
|
if (newSet.has(explanation.title)) {
|
|
newSet.delete(explanation.title);
|
|
} else {
|
|
newSet.add(explanation.title);
|
|
}
|
|
|
|
setExpandedCharts(newSet);
|
|
}}
|
|
/>
|
|
</Box>
|
|
);
|
|
})}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default BuildStats;
|