fix: coordinate letters, formatting

This commit is contained in:
Cozma Rares
2023-09-15 18:37:02 +03:00
parent de73210278
commit e7d236ea68
16 changed files with 323 additions and 318 deletions
+1 -1
View File
@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
}
};
+1 -1
View File
@@ -8,7 +8,7 @@ import Modal, { ModalButton } from "./Modal";
import { useState } from "react";
const ChessUI: React.FC<InferProps<[typeof ChessBoard, typeof History]>> = (
props
props,
) => {
const [showModal, setShowModal] = useState(true);
const navigate = useNavigate();
+53 -51
View File
@@ -121,7 +121,7 @@ const ChessBoard: React.FC<{
if (tile == activeTile) return setActiveTile(-1);
setActiveTile(
chess.getMovesForSquare(algebraic(tile)).length == 0 ? -1 : tile
chess.getMovesForSquare(algebraic(tile)).length == 0 ? -1 : tile,
);
};
@@ -198,60 +198,62 @@ const Tile: React.FC<{
blackPerspective,
endGameIcon,
}) => {
const color = squareColor(tileNumber);
const color = squareColor(tileNumber);
const { bg: bgColor, text: textColor } = TILE_COLORS[color];
const activeColor = TILE_COLORS[color].active;
const { bg: bgColor, text: textColor } = TILE_COLORS[color];
const activeColor = TILE_COLORS[color].active;
const tileFile = file(tileNumber);
const tileRank = rank(tileNumber);
const square = algebraic(tileNumber);
const tileFile = file(tileNumber);
const tileRank = rank(tileNumber);
const square = algebraic(tileNumber);
const isFirstColumn = tileFile == (blackPerspective ? FILE.H : FILE.A);
const isLastRow = tileRank == (blackPerspective ? RANK.EIGHTH : RANK.FIRST);
const isFirstColumn = tileFile == (blackPerspective ? FILE.H : FILE.A);
const isLastRow = tileRank == (blackPerspective ? RANK.EIGHTH : RANK.FIRST);
return (
<div
className={[
"relative aspect-square font-bold text-xl group [&>*]:pointer-events-none outline-blue-300 hover:outline outline-2 sm:outline-4 -outline-offset-2 sm:-outline-offset-4",
isActive ? activeColor : bgColor,
piece ? "cursor-pointer" : "",
].join(" ")}
data-tile={tileNumber}
>
{piece && (
<img
src={PIECES[piece.color][piece.type]}
className="max-w-full max-h-full aspect-square"
/>
)}
<Show when={endGameIcon != undefined}>
<div className="absolute top-0 left-full -translate-x-1/2 -translate-y-1/2 bg-white z-10 rounded-full p-1 shadow-lg shadow-black aspect-square flex justify-center items-center">
{endGameIcon}
</div>
if (isFirstColumn || isLastRow) console.log(square);
return (
<div
className={[
"relative isolate aspect-square font-bold text-xl group [&>*]:pointer-events-none outline-blue-300 hover:outline outline-2 sm:outline-4 -outline-offset-2 sm:-outline-offset-4",
isActive ? activeColor : bgColor,
piece ? "cursor-pointer" : "",
].join(" ")}
data-tile={tileNumber}
>
{piece && (
<img
src={PIECES[piece.color][piece.type]}
className="max-w-full max-h-full aspect-square"
/>
)}
<Show when={endGameIcon != undefined}>
<div className="absolute top-0 left-full -translate-x-1/2 -translate-y-1/2 bg-white z-10 rounded-full p-1 shadow-lg shadow-black aspect-square flex justify-center items-center">
{endGameIcon}
</div>
</Show>
<Show when={isAttacked}>
<Show
when={piece == null}
fallback={
<div className="absolute z-10 -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-full aspect-square border-8 border-gray-900/40 rounded-full"></div>
}
>
<div className="absolute z-10 -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-[35%] aspect-square bg-gray-900/40 rounded-full group-hover:w-[45%]"></div>
</Show>
<Show when={isAttacked}>
<Show
when={piece == null}
fallback={
<div className="absolute -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-full aspect-square border-8 border-gray-900/40 rounded-full"></div>
}
>
<div className="absolute -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-[35%] aspect-square bg-gray-900/40 rounded-full group-hover:w-[45%]"></div>
</Show>
</Show>
<Show when={isFirstColumn}>
<div className={`absolute top-1 left-1 -z-10 ${textColor}`}>
{square[1]}
</div>
</Show>
<Show when={isLastRow}>
<div className={`absolute bottom-1 right-1 -z-10 ${textColor}`}>
{square[0]}
</div>
</Show>
</div>
);
};
</Show>
<Show when={isFirstColumn}>
<div className={`absolute top-1 left-1 -z-10 ${textColor}`}>
{square[1]}
</div>
</Show>
<Show when={isLastRow}>
<div className={`absolute bottom-1 right-1 -z-10 ${textColor}`}>
{square[0]}
</div>
</Show>
</div>
);
};
export default ChessBoard;
+1 -1
View File
@@ -16,7 +16,7 @@ const ErrorNorification: React.FC<{
divRef.current.classList.add(
"opacity-0",
"transition-opacity",
"duration-300"
"duration-300",
);
setTimeout(() => {
+73 -73
View File
@@ -2,92 +2,92 @@ import { ReactNode, useEffect, useRef } from "react";
import Chess from "../../../server/src/engine";
const History: React.FC<{
chess: Chess;
buttons?: Array<{ onClick: () => void; title: string; icon: ReactNode }>;
chess: Chess;
buttons?: Array<{ onClick: () => void; title: string; icon: ReactNode }>;
}> = ({ chess, buttons }) => {
const containerRef = useRef<HTMLDivElement>(null);
const historyRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const historyRef = useRef<HTMLDivElement>(null);
const { currentPosition, history: moveHistory } = chess.getHistory();
const { currentPosition, history: moveHistory } = chess.getHistory();
useEffect(() => {
// HACK: couldn't find any other way for ChessBoard and History to have the same height
setTimeout(() => {
const chessboard = document.querySelector(
"#chessboard"
) as HTMLDivElement;
const { height } = chessboard.getBoundingClientRect();
const containerDiv = containerRef.current as HTMLDivElement;
containerDiv.style.setProperty("max-height", `${height}px`);
}, 100);
useEffect(() => {
// HACK: couldn't find any other way for ChessBoard and History to have the same height
setTimeout(() => {
const chessboard = document.querySelector(
"#chessboard",
) as HTMLDivElement;
const { height } = chessboard.getBoundingClientRect();
const containerDiv = containerRef.current as HTMLDivElement;
containerDiv.style.setProperty("max-height", `${height}px`);
}, 100);
const historyDiv = historyRef.current as HTMLDivElement;
historyDiv.scrollTop = historyDiv.scrollHeight;
}, [moveHistory.length]);
const historyDiv = historyRef.current as HTMLDivElement;
historyDiv.scrollTop = historyDiv.scrollHeight;
}, [moveHistory.length]);
const history: ReactNode[] = [];
const history: ReactNode[] = [];
moveHistory.forEach(({ san }, idx) => {
const fullMove = (idx >> 1) + 1;
const current = idx == currentPosition ? "bg-gray-500/80" : "";
moveHistory.forEach(({ san }, idx) => {
const fullMove = (idx >> 1) + 1;
const current = idx == currentPosition ? "bg-gray-500/80" : "";
if (idx % 2 == 0)
history.push(
<span key={idx} className="px-2 py-1">
{fullMove}.
</span>
);
if (idx % 2 == 0)
history.push(
<span key={idx} className="px-2 py-1">
{fullMove}.
</span>,
);
history.push(
<span key={idx + san} className="py-1">
<span className={`inline-block h-full w-2/3 rounded-md ${current}`}>
{san}
</span>
</span>
);
});
if (history.length % 3) history.push(<span key="__history-last__"></span>);
return (
<div
ref={containerRef}
className="bg-zinc-800 rounded-lg text-white hidden md:grid grid-rows-[auto,1fr,auto]"
>
<div className="text-xl text-center my-1 mx-4 border-b">History</div>
<div
ref={historyRef}
className="p-4 min-w-[13rem] h-fit max-h-full overflow-y-scroll overflow-x-hidden grid grid-cols-[auto,1fr,1fr] gap-y-2 text-center [&>:nth-child(3n)]:rounded-r-md [&>:nth-child(3n-2)]:rounded-l-md [&>:not(:nth-child(3n-2))]:font-bold [&>:nth-child(6n)]:bg-zinc-600 [&>:nth-child(6n-1)]:bg-zinc-600 [&>:nth-child(6n-2)]:bg-zinc-600"
>
{history}
</div>
<div className="bg-black grid auto-cols-fr grid-flow-col gap-5 p-4 rounded-b-lg empty:hidden">
{buttons?.map(({ onClick, title, icon }) => (
<Button key={title} title={title} onClick={onClick}>
{icon}
</Button>
))}
</div>
</div>
history.push(
<span key={idx + san} className="py-1">
<span className={`inline-block h-full w-2/3 rounded-md ${current}`}>
{san}
</span>
</span>,
);
});
if (history.length % 3) history.push(<span key="__history-last__"></span>);
return (
<div
ref={containerRef}
className="bg-zinc-800 rounded-lg text-white hidden md:grid grid-rows-[auto,1fr,auto]"
>
<div className="text-xl text-center my-1 mx-4 border-b">History</div>
<div
ref={historyRef}
className="p-4 min-w-[13rem] h-fit max-h-full overflow-y-scroll overflow-x-hidden grid grid-cols-[auto,1fr,1fr] gap-y-2 text-center [&>:nth-child(3n)]:rounded-r-md [&>:nth-child(3n-2)]:rounded-l-md [&>:not(:nth-child(3n-2))]:font-bold [&>:nth-child(6n)]:bg-zinc-600 [&>:nth-child(6n-1)]:bg-zinc-600 [&>:nth-child(6n-2)]:bg-zinc-600"
>
{history}
</div>
<div className="bg-black grid auto-cols-fr grid-flow-col gap-5 p-4 rounded-b-lg empty:hidden">
{buttons?.map(({ onClick, title, icon }) => (
<Button key={title} title={title} onClick={onClick}>
{icon}
</Button>
))}
</div>
</div>
);
};
const Button: React.FC<{
children: React.ReactNode;
title: string;
disabled?: boolean;
onClick: () => void;
children: React.ReactNode;
title: string;
disabled?: boolean;
onClick: () => void;
}> = ({ children, title, disabled, onClick }) => (
<button
className="relative min-w-[1rem] mt-auto bg-zinc-800 flex justify-center items-center p-2 rounded-md hover:bg-zinc-900 transition-colors group capitalize"
onClick={onClick}
disabled={disabled}
>
<span className="absolute -top-full left-1/2 -translate-x-1/2 bg-black rounded-md py-0.5 px-2 whitespace-nowrap opacity-0 pointer-events-none group-hover:opacity-100 transition-opacity delay-100">
{title}
</span>
{children}
</button>
<button
className="relative min-w-[1rem] mt-auto bg-zinc-800 flex justify-center items-center p-2 rounded-md hover:bg-zinc-900 transition-colors group capitalize"
onClick={onClick}
disabled={disabled}
>
<span className="absolute -top-full left-1/2 -translate-x-1/2 bg-black rounded-md py-0.5 px-2 whitespace-nowrap opacity-0 pointer-events-none group-hover:opacity-100 transition-opacity delay-100">
{title}
</span>
{children}
</button>
);
export default History;
+30 -30
View File
@@ -1,43 +1,43 @@
import { useState, useCallback } from "react";
type State = {
error: Error | null;
text: string | null;
error: Error | null;
text: string | null;
};
export default function useCopyToClipboard(): [
State,
(value: any) => Promise<void>
State,
(value: any) => Promise<void>,
] {
const [state, setState] = useState<State>({
error: null,
const [state, setState] = useState<State>({
error: null,
text: null,
});
const copyToClipboard = useCallback(async (value: string) => {
if (!navigator?.clipboard) {
return setState({
error: new Error("Clipboard not supported"),
text: null,
});
});
}
const copyToClipboard = useCallback(async (value: string) => {
if (!navigator?.clipboard) {
return setState({
error: new Error("Clipboard not supported"),
text: null,
});
}
const handleSuccess = () => {
setState({
error: null,
text: value,
});
};
const handleSuccess = () => {
setState({
error: null,
text: value,
});
};
const handleFailure = (e: any) => {
setState({
error: e,
text: null,
});
};
const handleFailure = (e: any) => {
setState({
error: e,
text: null,
});
};
navigator.clipboard.writeText(value).then(handleSuccess, handleFailure);
}, []);
navigator.clipboard.writeText(value).then(handleSuccess, handleFailure);
}, []);
return [state, copyToClipboard];
return [state, copyToClipboard];
}
+1 -1
View File
@@ -45,5 +45,5 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<RouterProvider router={router} />
</div>
</div>
</>
</>,
);
+56 -56
View File
@@ -3,71 +3,71 @@ import { useNavigate } from "react-router-dom";
import Chess, { Move } from "../../../server/src/engine";
import ChessUI from "../components/ChessUI";
import {
ArrowLeft,
CaretLeft,
CaretRight,
Plus,
Repeat,
ArrowLeft,
CaretLeft,
CaretRight,
Plus,
Repeat,
} from "../components/icons";
import InferProps from "../utils/InferProps";
const LocalGame = () => {
const [chess, setChess] = useState(Chess.load());
const [blackPerspective, setBlackPerspective] = useState(false);
const [, aux] = useState(false);
const navigate = useNavigate();
const [chess, setChess] = useState(Chess.load());
const [blackPerspective, setBlackPerspective] = useState(false);
const [, aux] = useState(false);
const navigate = useNavigate();
const rerender = () => aux((prev) => !prev);
const rerender = () => aux((prev) => !prev);
const makeMove = (move: Move) => {
if (chess.didUndo()) chess.undoMoves();
const makeMove = (move: Move) => {
if (chess.didUndo()) chess.undoMoves();
chess.makeMove(move);
chess.makeMove(move);
rerender();
};
const buttons: Pick<InferProps<[typeof ChessUI]>, "buttons">["buttons"] = [
{
onClick: () => navigate("/"),
title: "to main page",
icon: <ArrowLeft />,
},
{
onClick: () => setChess(Chess.load()),
title: "new game",
icon: <Plus />,
},
{
onClick: () => setBlackPerspective((prev) => !prev),
title: "switch sides",
icon: <Repeat />,
},
{
onClick: () => {
chess.undo();
rerender();
};
},
title: "undo",
icon: <CaretLeft />,
},
{
onClick: () => {
chess.redo();
rerender();
},
title: "redo",
icon: <CaretRight />,
},
];
const buttons: Pick<InferProps<[typeof ChessUI]>, "buttons">["buttons"] = [
{
onClick: () => navigate("/"),
title: "to main page",
icon: <ArrowLeft />,
},
{
onClick: () => setChess(Chess.load()),
title: "new game",
icon: <Plus />,
},
{
onClick: () => setBlackPerspective((prev) => !prev),
title: "switch sides",
icon: <Repeat />,
},
{
onClick: () => {
chess.undo();
rerender();
},
title: "undo",
icon: <CaretLeft />,
},
{
onClick: () => {
chess.redo();
rerender();
},
title: "redo",
icon: <CaretRight />,
},
];
return (
<ChessUI
chess={chess}
makeMove={makeMove}
blackPerspective={blackPerspective}
buttons={buttons}
/>
);
return (
<ChessUI
chess={chess}
makeMove={makeMove}
blackPerspective={blackPerspective}
buttons={buttons}
/>
);
};
export default LocalGame;
+2 -2
View File
@@ -4,14 +4,14 @@ type ArgumentTypes<F extends React.FC<any>> = F extends React.FC<infer TT>
type Head<T extends Array<React.FC<any>>> = T extends [
infer TT extends React.FC<any>,
...any
...any,
]
? TT
: never;
type Tail<T extends Array<React.FC<any>>> = T extends [
any,
...infer TT extends Array<React.FC<any>>
...infer TT extends Array<React.FC<any>>,
]
? TT
: never;
+2 -2
View File
@@ -2,8 +2,8 @@ import { io } from "socket.io-client";
// "undefined" means the URL will be computed from the `window.location` object
const URL =
process.env.NODE_ENV === "production" ? undefined : "http://localhost:5000";
process.env.NODE_ENV === "production" ? undefined : "http://localhost:5000";
export const socket = io(URL as string, {
autoConnect: false,
autoConnect: false,
});