Squashed commit of the following:
commit 4810de3b94b0ec0d7e9b8570de58f85792dffa80 Author: GuillaumeSD <gsd.lfny@gmail.com> Date: Sat Apr 6 01:37:42 2024 +0200 fix : lint commit 59e0b571e6089da6c086ab6340ec6a966b2e9739 Author: GuillaumeSD <gsd.lfny@gmail.com> Date: Sat Apr 6 01:36:17 2024 +0200 feat : UI refacto commit 56806a89dca5c7fb2c229b5a57404f9a856fac09 Author: GuillaumeSD <gsd.lfny@gmail.com> Date: Fri Apr 5 03:56:08 2024 +0200 feat : add moves list commit 9e3d2347882074c38ab183e642ecef8153dbfcde Author: GuillaumeSD <gsd.lfny@gmail.com> Date: Thu Apr 4 02:18:52 2024 +0200 feat : init branch, wip
This commit is contained in:
@@ -17,7 +17,8 @@ export default function BoardContainer() {
|
||||
const screenSize = useScreenSize();
|
||||
const boardOrientation = useAtomValue(boardOrientationAtom);
|
||||
const showBestMoveArrow = useAtomValue(showBestMoveArrowAtom);
|
||||
const { whiteName, blackName } = usePlayersNames(gameAtom);
|
||||
const { whiteName, whiteElo, blackName, blackElo } =
|
||||
usePlayersNames(gameAtom);
|
||||
|
||||
const boardSize = useMemo(() => {
|
||||
const width = screenSize.width;
|
||||
@@ -28,7 +29,7 @@ export default function BoardContainer() {
|
||||
return Math.min(width, height - 150);
|
||||
}
|
||||
|
||||
return Math.min(width - 600, height * 0.95);
|
||||
return Math.min(width - 700, height * 0.95);
|
||||
}, [screenSize]);
|
||||
|
||||
return (
|
||||
@@ -37,8 +38,8 @@ export default function BoardContainer() {
|
||||
boardSize={boardSize}
|
||||
canPlay={true}
|
||||
gameAtom={boardAtom}
|
||||
whitePlayer={whiteName}
|
||||
blackPlayer={blackName}
|
||||
whitePlayer={`${whiteName} (${whiteElo})`}
|
||||
blackPlayer={`${blackName} (${blackElo})`}
|
||||
boardOrientation={boardOrientation ? Color.White : Color.Black}
|
||||
currentPositionAtom={currentPositionAtom}
|
||||
showBestMoveArrow={showBestMoveArrow}
|
||||
|
||||
@@ -26,14 +26,16 @@ export const useCurrentPosition = (engineName?: EngineName) => {
|
||||
lastMove: board.history({ verbose: true }).at(-1),
|
||||
};
|
||||
|
||||
if (gameEval) {
|
||||
const boardHistory = board.history();
|
||||
const gameHistory = game.history();
|
||||
const boardHistory = board.history();
|
||||
const gameHistory = game.history();
|
||||
|
||||
if (
|
||||
boardHistory.length <= gameHistory.length &&
|
||||
gameHistory.slice(0, boardHistory.length).join() === boardHistory.join()
|
||||
) {
|
||||
if (
|
||||
boardHistory.length <= gameHistory.length &&
|
||||
gameHistory.slice(0, boardHistory.length).join() === boardHistory.join()
|
||||
) {
|
||||
position.currentMoveIdx = boardHistory.length;
|
||||
|
||||
if (gameEval) {
|
||||
const evalIndex = boardHistory.length;
|
||||
|
||||
position.eval = gameEval.positions[evalIndex];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Color, MoveClassification } from "@/types/enums";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom, gameAtom, gameEvalAtom } from "../states";
|
||||
import { boardAtom, gameAtom, gameEvalAtom } from "../../states";
|
||||
import { useMemo } from "react";
|
||||
import { moveClassificationColors } from "@/components/board/squareRenderer";
|
||||
import Image from "next/image";
|
||||
@@ -68,8 +68,6 @@ export default function ClassificationRow({ classification }: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
if (!gameEval?.positions.length) return null;
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
@@ -99,6 +97,7 @@ export default function ClassificationRow({ classification }: Props) {
|
||||
alignItems="center"
|
||||
width={"7rem"}
|
||||
gap={1}
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Image
|
||||
src={`/icons/${classification}.png`}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { usePlayersNames } from "@/hooks/usePlayerNames";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { gameAtom, gameEvalAtom } from "../states";
|
||||
import { gameAtom, gameEvalAtom } from "../../states";
|
||||
import { MoveClassification } from "@/types/enums";
|
||||
import ClassificationRow from "./classificationRow";
|
||||
import { useAtomValue } from "jotai";
|
||||
@@ -17,20 +17,10 @@ export default function MovesClassificationsRecap() {
|
||||
item
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
borderRadius={2}
|
||||
border={1}
|
||||
borderColor={"secondary.main"}
|
||||
sx={{
|
||||
backgroundColor: "secondary.main",
|
||||
borderColor: "primary.main",
|
||||
borderWidth: 2,
|
||||
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
|
||||
}}
|
||||
marginTop={{ xs: 0, lg: "2.5em" }}
|
||||
paddingY={3}
|
||||
rowGap={2}
|
||||
xs
|
||||
style={{ maxWidth: "50rem" }}
|
||||
xs={6}
|
||||
sx={{ scrollbarWidth: "thin", overflowY: "auto" }}
|
||||
maxHeight="100%"
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
@@ -40,13 +30,13 @@ export default function MovesClassificationsRecap() {
|
||||
wrap="nowrap"
|
||||
xs={12}
|
||||
>
|
||||
<Typography width="12rem" align="center">
|
||||
<Typography width="12rem" align="center" noWrap>
|
||||
{whiteName}
|
||||
</Typography>
|
||||
|
||||
<Typography width="7rem" />
|
||||
|
||||
<Typography width="12rem" align="center">
|
||||
<Typography width="12rem" align="center" noWrap>
|
||||
{blackName}
|
||||
</Typography>
|
||||
</Grid>
|
||||
62
src/sections/analysis/reviewPanelBody/movesPanel/index.tsx
Normal file
62
src/sections/analysis/reviewPanelBody/movesPanel/index.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Grid } from "@mui/material";
|
||||
import MovesLine from "./movesLine";
|
||||
import { useMemo } from "react";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { gameAtom, gameEvalAtom } from "../../states";
|
||||
import { MoveClassification } from "@/types/enums";
|
||||
|
||||
export default function MovesPanel() {
|
||||
const game = useAtomValue(gameAtom);
|
||||
const gameEval = useAtomValue(gameEvalAtom);
|
||||
|
||||
const gameMoves = useMemo(() => {
|
||||
const history = game.history();
|
||||
if (!history.length) return undefined;
|
||||
|
||||
const moves: { san: string; moveClassification?: MoveClassification }[][] =
|
||||
[];
|
||||
|
||||
for (let i = 0; i < history.length; i += 2) {
|
||||
const items = [
|
||||
{
|
||||
san: history[i],
|
||||
moveClassification: gameEval?.positions[i + 1]?.moveClassification,
|
||||
},
|
||||
];
|
||||
|
||||
if (history[i + 1]) {
|
||||
items.push({
|
||||
san: history[i + 1],
|
||||
moveClassification: gameEval?.positions[i + 2]?.moveClassification,
|
||||
});
|
||||
}
|
||||
|
||||
moves.push(items);
|
||||
}
|
||||
|
||||
return moves;
|
||||
}, [game, gameEval]);
|
||||
|
||||
if (!gameMoves) return null;
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
justifyContent="center"
|
||||
alignItems="start"
|
||||
gap={1}
|
||||
sx={{ scrollbarWidth: "thin", overflowY: "auto" }}
|
||||
maxHeight="100%"
|
||||
xs={6}
|
||||
>
|
||||
{gameMoves?.map((moves, idx) => (
|
||||
<MovesLine
|
||||
key={`${moves.map(({ san }) => san).join()}-${idx}`}
|
||||
moves={moves}
|
||||
moveNb={idx + 1}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { MoveClassification } from "@/types/enums";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { moveClassificationColors } from "@/components/board/squareRenderer";
|
||||
import Image from "next/image";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom, currentPositionAtom, gameAtom } from "../../states";
|
||||
import { useChessActions } from "@/hooks/useChessActions";
|
||||
import { useEffect } from "react";
|
||||
import { isInViewport } from "@/lib/helpers";
|
||||
|
||||
interface Props {
|
||||
san: string;
|
||||
moveClassification?: MoveClassification;
|
||||
moveIdx: number;
|
||||
}
|
||||
|
||||
export default function MoveItem({ san, moveClassification, moveIdx }: Props) {
|
||||
const game = useAtomValue(gameAtom);
|
||||
const { goToMove } = useChessActions(boardAtom);
|
||||
const position = useAtomValue(currentPositionAtom);
|
||||
const color = getMoveColor(moveClassification);
|
||||
|
||||
const isCurrentMove = position?.currentMoveIdx === moveIdx;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCurrentMove) return;
|
||||
const moveItem = document.getElementById(`move-${moveIdx}`);
|
||||
if (!moveItem || !isInViewport(moveItem)) return;
|
||||
moveItem.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}, [isCurrentMove, moveIdx]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (isCurrentMove) return;
|
||||
goToMove(moveIdx, game);
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap={1}
|
||||
width="5rem"
|
||||
wrap="nowrap"
|
||||
onClick={handleClick}
|
||||
paddingY={0.5}
|
||||
sx={{
|
||||
cursor: isCurrentMove ? undefined : "pointer",
|
||||
backgroundColor: isCurrentMove ? "#4f4f4f" : undefined,
|
||||
borderRadius: 1,
|
||||
}}
|
||||
id={`move-${moveIdx}`}
|
||||
>
|
||||
{color && (
|
||||
<Image
|
||||
src={`/icons/${moveClassification}.png`}
|
||||
alt="move-icon"
|
||||
width={15}
|
||||
height={15}
|
||||
style={{
|
||||
maxWidth: "3.6vw",
|
||||
maxHeight: "3.6vw",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Typography color={getMoveColor(moveClassification)}>{san}</Typography>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const getMoveColor = (moveClassification?: MoveClassification) => {
|
||||
if (
|
||||
!moveClassification ||
|
||||
moveClassificationsToIgnore.includes(moveClassification)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return moveClassificationColors[moveClassification];
|
||||
};
|
||||
|
||||
const moveClassificationsToIgnore: MoveClassification[] = [
|
||||
MoveClassification.Good,
|
||||
MoveClassification.Excellent,
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
import { MoveClassification } from "@/types/enums";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import MoveItem from "./moveItem";
|
||||
|
||||
interface Props {
|
||||
moves: { san: string; moveClassification?: MoveClassification }[];
|
||||
moveNb: number;
|
||||
}
|
||||
|
||||
export default function MovesLine({ moves, moveNb }: Props) {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
justifyContent="space-evenly"
|
||||
alignItems="start"
|
||||
xs={12}
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Typography width="2rem">{moveNb}.</Typography>
|
||||
|
||||
<MoveItem {...moves[0]} moveIdx={(moveNb - 1) * 2 + 1} />
|
||||
|
||||
<MoveItem {...moves[1]} moveIdx={(moveNb - 1) * 2 + 2} />
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
44
src/sections/analysis/reviewPanelHeader/gamePanel.tsx
Normal file
44
src/sections/analysis/reviewPanelHeader/gamePanel.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { gameAtom } from "../states";
|
||||
|
||||
export default function GamePanel() {
|
||||
const { gameFromUrl } = useGameDatabase();
|
||||
const game = useAtomValue(gameAtom);
|
||||
|
||||
const hasGameInfo = gameFromUrl !== undefined || !!game.header().White;
|
||||
|
||||
if (!hasGameInfo) return null;
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={11}
|
||||
justifyContent="space-evenly"
|
||||
alignItems="center"
|
||||
rowGap={1}
|
||||
columnGap={3}
|
||||
>
|
||||
<Grid item container xs justifyContent="center" alignItems="center">
|
||||
<Typography noWrap>
|
||||
Site : {gameFromUrl?.site || game.header().Site || "?"}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item container xs justifyContent="center" alignItems="center">
|
||||
<Typography noWrap>
|
||||
Date : {gameFromUrl?.date || game.header().Date || "?"}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item container xs justifyContent="center" alignItems="center">
|
||||
<Typography noWrap>
|
||||
Result :{" "}
|
||||
{gameFromUrl?.termination || game.header().Termination || "?"}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { gameAtom } from "../../states";
|
||||
import PlayerInfo from "./playerInfo";
|
||||
|
||||
export default function GamePanel() {
|
||||
const { gameFromUrl } = useGameDatabase();
|
||||
const game = useAtomValue(gameAtom);
|
||||
|
||||
const hasGameInfo = gameFromUrl !== undefined || !!game.header().White;
|
||||
|
||||
if (!hasGameInfo) return null;
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={12}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap={2}
|
||||
>
|
||||
<Grid item container xs={12} justifyContent="center" alignItems="center">
|
||||
<PlayerInfo color="white" />
|
||||
|
||||
<Typography marginX={1.5}>vs</Typography>
|
||||
|
||||
<PlayerInfo color="black" />
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={11}
|
||||
justifyContent="space-evenly"
|
||||
alignItems="center"
|
||||
rowGap={1}
|
||||
columnGap={3}
|
||||
>
|
||||
<Grid item container xs justifyContent="center" alignItems="center">
|
||||
<Typography noWrap>
|
||||
Site : {gameFromUrl?.site || game.header().Site || "?"}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item container xs justifyContent="center" alignItems="center">
|
||||
<Typography noWrap>
|
||||
Date : {gameFromUrl?.date || game.header().Date || "?"}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item container xs justifyContent="center" alignItems="center">
|
||||
<Typography noWrap>
|
||||
Result :{" "}
|
||||
{gameFromUrl?.termination || game.header().Termination || "?"}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { gameAtom } from "../../states";
|
||||
|
||||
interface Props {
|
||||
color: "white" | "black";
|
||||
}
|
||||
|
||||
export default function PlayerInfo({ color }: Props) {
|
||||
const { gameFromUrl } = useGameDatabase();
|
||||
const game = useAtomValue(gameAtom);
|
||||
|
||||
const rating =
|
||||
gameFromUrl?.[color]?.rating ||
|
||||
game.header()[color === "white" ? "WhiteElo" : "BlackElo"];
|
||||
|
||||
const playerName =
|
||||
gameFromUrl?.[color]?.name ||
|
||||
game.header()[color === "white" ? "White" : "Black"];
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={5}
|
||||
justifyContent={color === "white" ? "flex-end" : "flex-start"}
|
||||
alignItems="center"
|
||||
gap={0.5}
|
||||
>
|
||||
<Typography>
|
||||
{playerName || (color === "white" ? "White" : "Black")}
|
||||
</Typography>
|
||||
|
||||
<Typography>{rating ? `(${rating})` : "(?)"}</Typography>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user