Files
chesskit/src/components/board/squareRenderer.tsx
2025-05-11 18:21:45 +02:00

117 lines
3.5 KiB
TypeScript

import { CurrentPosition } from "@/types/eval";
import { MoveClassification } from "@/types/enums";
import { PrimitiveAtom, atom, useAtomValue } from "jotai";
import Image from "next/image";
import { CSSProperties, forwardRef } from "react";
import {
CustomSquareProps,
Square,
} from "react-chessboard/dist/chessboard/types";
import { CLASSIFICATION_COLORS } from "@/constants";
import { boardHueAtom } from "./states";
export interface Props {
currentPositionAtom: PrimitiveAtom<CurrentPosition>;
clickedSquaresAtom: PrimitiveAtom<Square[]>;
playableSquaresAtom: PrimitiveAtom<Square[]>;
showPlayerMoveIconAtom?: PrimitiveAtom<boolean>;
}
export function getSquareRenderer({
currentPositionAtom,
clickedSquaresAtom,
playableSquaresAtom,
showPlayerMoveIconAtom = atom(false),
}: Props) {
const squareRenderer = forwardRef<HTMLDivElement, CustomSquareProps>(
(props, ref) => {
const { children, square, style } = props;
const showPlayerMoveIcon = useAtomValue(showPlayerMoveIconAtom);
const position = useAtomValue(currentPositionAtom);
const clickedSquares = useAtomValue(clickedSquaresAtom);
const playableSquares = useAtomValue(playableSquaresAtom);
const boardHue = useAtomValue(boardHueAtom);
const fromSquare = position.lastMove?.from;
const toSquare = position.lastMove?.to;
const moveClassification = position?.eval?.moveClassification;
const highlightSquareStyle: CSSProperties | undefined =
clickedSquares.includes(square)
? rightClickSquareStyle
: fromSquare === square || toSquare === square
? previousMoveSquareStyle(moveClassification)
: undefined;
const playableSquareStyle: CSSProperties | undefined =
playableSquares.includes(square) ? playableSquareStyles : undefined;
return (
<div
ref={ref}
style={{
...style,
position: "relative",
filter: `hue-rotate(-${boardHue}deg)`,
}}
>
{children}
{highlightSquareStyle && <div style={highlightSquareStyle} />}
{playableSquareStyle && <div style={playableSquareStyle} />}
{moveClassification && showPlayerMoveIcon && square === toSquare && (
<Image
src={`/icons/${moveClassification}.png`}
alt="move-icon"
width={35}
height={35}
style={{
position: "absolute",
top: "max(-12px, -1.8vw)",
right: "max(-12px, -1.8vw)",
maxWidth: "3.6vw",
maxHeight: "3.6vw",
zIndex: 100,
}}
/>
)}
</div>
);
}
);
squareRenderer.displayName = "SquareRenderer";
return squareRenderer;
}
const rightClickSquareStyle: CSSProperties = {
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: "#eb6150",
opacity: "0.8",
};
const playableSquareStyles: CSSProperties = {
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,.14)",
padding: "35%",
backgroundClip: "content-box",
borderRadius: "50%",
boxSizing: "border-box",
};
const previousMoveSquareStyle = (
moveClassification?: MoveClassification
): CSSProperties => ({
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: moveClassification
? CLASSIFICATION_COLORS[moveClassification]
: "#fad541",
opacity: 0.5,
});