@react-chess-tools/react-chess-game 0.5.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +399 -0
- package/dist/index.cjs +785 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +278 -0
- package/dist/index.d.ts +278 -0
- package/dist/{index.mjs → index.js} +339 -196
- package/dist/index.js.map +1 -0
- package/package.json +19 -9
- package/src/components/ChessGame/Theme.stories.tsx +242 -0
- package/src/components/ChessGame/ThemePresets.stories.tsx +144 -0
- package/src/components/ChessGame/parts/Board.tsx +215 -204
- package/src/components/ChessGame/parts/KeyboardControls.tsx +9 -0
- package/src/components/ChessGame/parts/Root.tsx +13 -1
- package/src/components/ChessGame/parts/Sounds.tsx +9 -0
- package/src/components/ChessGame/parts/__tests__/Board.test.tsx +122 -0
- package/src/components/ChessGame/parts/__tests__/KeyboardControls.test.tsx +34 -0
- package/src/components/ChessGame/parts/__tests__/Root.test.tsx +50 -0
- package/src/components/ChessGame/parts/__tests__/Sounds.test.tsx +22 -0
- package/src/docs/Theming.mdx +281 -0
- package/src/hooks/useChessGame.ts +23 -7
- package/src/index.ts +19 -0
- package/src/theme/__tests__/context.test.tsx +60 -0
- package/src/theme/__tests__/defaults.test.ts +61 -0
- package/src/theme/__tests__/utils.test.ts +106 -0
- package/src/theme/context.tsx +37 -0
- package/src/theme/defaults.ts +22 -0
- package/src/theme/index.ts +36 -0
- package/src/theme/presets.ts +41 -0
- package/src/theme/types.ts +56 -0
- package/src/theme/utils.ts +47 -0
- package/src/utils/__tests__/board.test.ts +118 -0
- package/src/utils/board.ts +18 -9
- package/src/utils/chess.ts +25 -5
- package/README.MD +0 -190
- package/coverage/clover.xml +0 -6
- package/coverage/coverage-final.json +0 -1
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -101
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov.info +0 -0
- package/dist/index.d.mts +0 -158
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ChessGameTheme } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default theme for ChessGame component.
|
|
5
|
+
* These values match the original hardcoded colors for backward compatibility.
|
|
6
|
+
*/
|
|
7
|
+
export const defaultGameTheme: ChessGameTheme = {
|
|
8
|
+
board: {
|
|
9
|
+
lightSquare: { backgroundColor: "#f0d9b5" },
|
|
10
|
+
darkSquare: { backgroundColor: "#b58863" },
|
|
11
|
+
},
|
|
12
|
+
state: {
|
|
13
|
+
lastMove: "rgba(255, 255, 0, 0.5)",
|
|
14
|
+
check: "rgba(255, 0, 0, 0.5)",
|
|
15
|
+
activeSquare: "rgba(255, 255, 0, 0.5)",
|
|
16
|
+
dropSquare: { backgroundColor: "rgba(255, 255, 0, 0.4)" },
|
|
17
|
+
},
|
|
18
|
+
indicators: {
|
|
19
|
+
move: "rgba(0, 0, 0, 0.1)",
|
|
20
|
+
capture: "rgba(1, 0, 0, 0.1)",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export type {
|
|
3
|
+
ChessGameTheme,
|
|
4
|
+
BoardTheme,
|
|
5
|
+
StateTheme,
|
|
6
|
+
IndicatorTheme,
|
|
7
|
+
PartialChessGameTheme,
|
|
8
|
+
DeepPartial,
|
|
9
|
+
} from "./types";
|
|
10
|
+
|
|
11
|
+
// Default theme
|
|
12
|
+
export { defaultGameTheme } from "./defaults";
|
|
13
|
+
|
|
14
|
+
// Preset themes
|
|
15
|
+
export { lichessTheme, chessComTheme } from "./presets";
|
|
16
|
+
|
|
17
|
+
// All themes as a single object
|
|
18
|
+
import { defaultGameTheme } from "./defaults";
|
|
19
|
+
import { lichessTheme, chessComTheme } from "./presets";
|
|
20
|
+
|
|
21
|
+
export const themes = {
|
|
22
|
+
default: defaultGameTheme,
|
|
23
|
+
lichess: lichessTheme,
|
|
24
|
+
chessCom: chessComTheme,
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
// Utilities
|
|
28
|
+
export { mergeTheme, mergeThemeWith } from "./utils";
|
|
29
|
+
|
|
30
|
+
// Context and hook
|
|
31
|
+
export {
|
|
32
|
+
ChessGameThemeContext,
|
|
33
|
+
useChessGameTheme,
|
|
34
|
+
ThemeProvider,
|
|
35
|
+
} from "./context";
|
|
36
|
+
export type { ThemeProviderProps } from "./context";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ChessGameTheme } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lichess-inspired theme with green highlights
|
|
5
|
+
*/
|
|
6
|
+
export const lichessTheme: ChessGameTheme = {
|
|
7
|
+
board: {
|
|
8
|
+
lightSquare: { backgroundColor: "#f0d9b5" },
|
|
9
|
+
darkSquare: { backgroundColor: "#b58863" },
|
|
10
|
+
},
|
|
11
|
+
state: {
|
|
12
|
+
lastMove: "rgba(155, 199, 0, 0.41)",
|
|
13
|
+
check: "rgba(255, 0, 0, 0.5)",
|
|
14
|
+
activeSquare: "rgba(20, 85, 30, 0.5)",
|
|
15
|
+
dropSquare: { backgroundColor: "rgba(20, 85, 30, 0.3)" },
|
|
16
|
+
},
|
|
17
|
+
indicators: {
|
|
18
|
+
move: "rgba(20, 85, 30, 0.3)",
|
|
19
|
+
capture: "rgba(20, 85, 30, 0.3)",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Chess.com-inspired theme with green board and yellow highlights
|
|
25
|
+
*/
|
|
26
|
+
export const chessComTheme: ChessGameTheme = {
|
|
27
|
+
board: {
|
|
28
|
+
lightSquare: { backgroundColor: "#ebecd0" },
|
|
29
|
+
darkSquare: { backgroundColor: "#779556" },
|
|
30
|
+
},
|
|
31
|
+
state: {
|
|
32
|
+
lastMove: "rgba(255, 255, 0, 0.5)",
|
|
33
|
+
check: "rgba(255, 0, 0, 0.7)",
|
|
34
|
+
activeSquare: "rgba(255, 255, 0, 0.5)",
|
|
35
|
+
dropSquare: { backgroundColor: "rgba(255, 255, 0, 0.4)" },
|
|
36
|
+
},
|
|
37
|
+
indicators: {
|
|
38
|
+
move: "rgba(0, 0, 0, 0.1)",
|
|
39
|
+
capture: "rgba(0, 0, 0, 0.1)",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Board appearance configuration - colors for light and dark squares
|
|
5
|
+
*/
|
|
6
|
+
export interface BoardTheme {
|
|
7
|
+
/** Style for light squares */
|
|
8
|
+
lightSquare: CSSProperties;
|
|
9
|
+
/** Style for dark squares */
|
|
10
|
+
darkSquare: CSSProperties;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Game state highlight colors (RGBA color strings)
|
|
15
|
+
*/
|
|
16
|
+
export interface StateTheme {
|
|
17
|
+
/** Background color for last move from/to squares */
|
|
18
|
+
lastMove: string;
|
|
19
|
+
/** Background color for king when in check */
|
|
20
|
+
check: string;
|
|
21
|
+
/** Background color for currently selected piece's square */
|
|
22
|
+
activeSquare: string;
|
|
23
|
+
/** Full CSSProperties for valid drop target squares */
|
|
24
|
+
dropSquare: CSSProperties;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Move indicator styling - colors for move dots and capture rings
|
|
29
|
+
*/
|
|
30
|
+
export interface IndicatorTheme {
|
|
31
|
+
/** Color for move dots on empty destination squares (used in radial-gradient) */
|
|
32
|
+
move: string;
|
|
33
|
+
/** Color for capture rings on capturable pieces (used in radial-gradient) */
|
|
34
|
+
capture: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Complete theme configuration for ChessGame component
|
|
39
|
+
*/
|
|
40
|
+
export interface ChessGameTheme {
|
|
41
|
+
board: BoardTheme;
|
|
42
|
+
state: StateTheme;
|
|
43
|
+
indicators: IndicatorTheme;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Utility type for creating partial versions of nested objects
|
|
48
|
+
*/
|
|
49
|
+
export type DeepPartial<T> = {
|
|
50
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Partial theme for user customization - allows overriding only specific properties
|
|
55
|
+
*/
|
|
56
|
+
export type PartialChessGameTheme = DeepPartial<ChessGameTheme>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { merge } from "lodash";
|
|
2
|
+
import type { ChessGameTheme, PartialChessGameTheme } from "./types";
|
|
3
|
+
import { defaultGameTheme } from "./defaults";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Deep merges a partial theme with the default theme.
|
|
7
|
+
* Allows users to override only specific theme properties while keeping defaults for the rest.
|
|
8
|
+
*
|
|
9
|
+
* @param partialTheme - Partial theme with only the properties to override
|
|
10
|
+
* @returns Complete theme with overridden properties merged with defaults
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const customTheme = mergeTheme({
|
|
15
|
+
* state: { lastMove: "rgba(100, 200, 100, 0.6)" }
|
|
16
|
+
* });
|
|
17
|
+
* // Returns full theme with only lastMove color changed
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export const mergeTheme = (
|
|
21
|
+
partialTheme?: PartialChessGameTheme,
|
|
22
|
+
): ChessGameTheme => {
|
|
23
|
+
if (!partialTheme) {
|
|
24
|
+
return { ...defaultGameTheme };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return merge({}, defaultGameTheme, partialTheme);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Deep merges a partial theme with a base theme.
|
|
32
|
+
* Useful when extending an existing theme.
|
|
33
|
+
*
|
|
34
|
+
* @param baseTheme - The base theme to extend
|
|
35
|
+
* @param partialTheme - Partial theme with properties to override
|
|
36
|
+
* @returns Complete theme with overridden properties
|
|
37
|
+
*/
|
|
38
|
+
export const mergeThemeWith = (
|
|
39
|
+
baseTheme: ChessGameTheme,
|
|
40
|
+
partialTheme?: PartialChessGameTheme,
|
|
41
|
+
): ChessGameTheme => {
|
|
42
|
+
if (!partialTheme) {
|
|
43
|
+
return { ...baseTheme };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return merge({}, baseTheme, partialTheme);
|
|
47
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Chess } from "chess.js";
|
|
2
2
|
import { getCustomSquareStyles, deepMergeChessboardOptions } from "../board";
|
|
3
3
|
import { getGameInfo } from "../chess";
|
|
4
|
+
import { defaultGameTheme } from "../../theme/defaults";
|
|
5
|
+
import type { ChessGameTheme } from "../../theme/types";
|
|
4
6
|
|
|
5
7
|
describe("Board Utilities", () => {
|
|
6
8
|
describe("getCustomSquareStyles", () => {
|
|
@@ -139,6 +141,122 @@ describe("Board Utilities", () => {
|
|
|
139
141
|
expect(styles.a3.background).toContain("25%");
|
|
140
142
|
expect(styles.c3.background).toContain("25%");
|
|
141
143
|
});
|
|
144
|
+
|
|
145
|
+
describe("with custom theme", () => {
|
|
146
|
+
const customTheme: ChessGameTheme = {
|
|
147
|
+
...defaultGameTheme,
|
|
148
|
+
state: {
|
|
149
|
+
...defaultGameTheme.state,
|
|
150
|
+
lastMove: "rgba(100, 200, 100, 0.6)",
|
|
151
|
+
check: "rgba(200, 50, 50, 0.7)",
|
|
152
|
+
activeSquare: "rgba(100, 100, 255, 0.5)",
|
|
153
|
+
},
|
|
154
|
+
indicators: {
|
|
155
|
+
move: "rgba(50, 50, 50, 0.2)",
|
|
156
|
+
capture: "rgba(200, 50, 50, 0.3)",
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
it("should use custom theme colors for last move", () => {
|
|
161
|
+
const game = new Chess();
|
|
162
|
+
game.move("e4");
|
|
163
|
+
const orientation = "w";
|
|
164
|
+
const info = getGameInfo(game, orientation);
|
|
165
|
+
const activeSquare = null;
|
|
166
|
+
|
|
167
|
+
const styles = getCustomSquareStyles(
|
|
168
|
+
game,
|
|
169
|
+
info,
|
|
170
|
+
activeSquare,
|
|
171
|
+
customTheme,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
expect(styles.e2).toHaveProperty(
|
|
175
|
+
"backgroundColor",
|
|
176
|
+
"rgba(100, 200, 100, 0.6)",
|
|
177
|
+
);
|
|
178
|
+
expect(styles.e4).toHaveProperty(
|
|
179
|
+
"backgroundColor",
|
|
180
|
+
"rgba(100, 200, 100, 0.6)",
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should use custom theme colors for active square", () => {
|
|
185
|
+
const game = new Chess();
|
|
186
|
+
const orientation = "w";
|
|
187
|
+
const info = getGameInfo(game, orientation);
|
|
188
|
+
const activeSquare = "e2";
|
|
189
|
+
|
|
190
|
+
const styles = getCustomSquareStyles(
|
|
191
|
+
game,
|
|
192
|
+
info,
|
|
193
|
+
activeSquare,
|
|
194
|
+
customTheme,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(styles.e2).toHaveProperty(
|
|
198
|
+
"backgroundColor",
|
|
199
|
+
"rgba(100, 100, 255, 0.5)",
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("should use custom theme colors for check", () => {
|
|
204
|
+
const game = new Chess(
|
|
205
|
+
"rnbqkbnr/ppp2ppp/8/3pp3/4P3/5Q2/PPPP1PPP/RNB1KBNR b KQkq - 1 3",
|
|
206
|
+
);
|
|
207
|
+
const orientation = "b";
|
|
208
|
+
const info = getGameInfo(game, orientation);
|
|
209
|
+
const modifiedInfo = { ...info, isCheck: true };
|
|
210
|
+
const activeSquare = null;
|
|
211
|
+
|
|
212
|
+
const styles = getCustomSquareStyles(
|
|
213
|
+
game,
|
|
214
|
+
modifiedInfo,
|
|
215
|
+
activeSquare,
|
|
216
|
+
customTheme,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(styles.e8).toHaveProperty(
|
|
220
|
+
"backgroundColor",
|
|
221
|
+
"rgba(200, 50, 50, 0.7)",
|
|
222
|
+
);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should use custom theme colors for move indicators", () => {
|
|
226
|
+
const game = new Chess();
|
|
227
|
+
const orientation = "w";
|
|
228
|
+
const info = getGameInfo(game, orientation);
|
|
229
|
+
const activeSquare = "e2";
|
|
230
|
+
|
|
231
|
+
const styles = getCustomSquareStyles(
|
|
232
|
+
game,
|
|
233
|
+
info,
|
|
234
|
+
activeSquare,
|
|
235
|
+
customTheme,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(styles.e3.background).toContain("rgba(50, 50, 50, 0.2)");
|
|
239
|
+
expect(styles.e4.background).toContain("rgba(50, 50, 50, 0.2)");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("should use custom theme colors for capture indicators", () => {
|
|
243
|
+
const game = new Chess(
|
|
244
|
+
"rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2",
|
|
245
|
+
);
|
|
246
|
+
const orientation = "w";
|
|
247
|
+
const info = getGameInfo(game, orientation);
|
|
248
|
+
const activeSquare = "e4";
|
|
249
|
+
|
|
250
|
+
const styles = getCustomSquareStyles(
|
|
251
|
+
game,
|
|
252
|
+
info,
|
|
253
|
+
activeSquare,
|
|
254
|
+
customTheme,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
expect(styles.d5.background).toContain("rgba(200, 50, 50, 0.3)");
|
|
258
|
+
});
|
|
259
|
+
});
|
|
142
260
|
});
|
|
143
261
|
|
|
144
262
|
describe("deepMergeChessboardOptions", () => {
|
package/src/utils/board.ts
CHANGED
|
@@ -3,14 +3,23 @@ import { type CSSProperties } from "react";
|
|
|
3
3
|
import { merge } from "lodash";
|
|
4
4
|
import type { ChessboardOptions } from "react-chessboard";
|
|
5
5
|
import { getDestinationSquares, type GameInfo } from "./chess";
|
|
6
|
+
import type { ChessGameTheme } from "../theme/types";
|
|
7
|
+
import { defaultGameTheme } from "../theme/defaults";
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Generates custom square styles based on game state and theme.
|
|
11
|
+
*
|
|
12
|
+
* @param game - Chess.js game instance
|
|
13
|
+
* @param info - Game info containing lastMove, isCheck, turn
|
|
14
|
+
* @param activeSquare - Currently selected square (if any)
|
|
15
|
+
* @param theme - Theme configuration (defaults to defaultGameTheme)
|
|
16
|
+
* @returns Record of square names to CSS properties
|
|
17
|
+
*/
|
|
10
18
|
export const getCustomSquareStyles = (
|
|
11
19
|
game: Chess,
|
|
12
20
|
info: GameInfo,
|
|
13
21
|
activeSquare: Square | null,
|
|
22
|
+
theme: ChessGameTheme = defaultGameTheme,
|
|
14
23
|
) => {
|
|
15
24
|
const customSquareStyles: Record<string, CSSProperties> = {};
|
|
16
25
|
|
|
@@ -18,16 +27,16 @@ export const getCustomSquareStyles = (
|
|
|
18
27
|
|
|
19
28
|
if (lastMove) {
|
|
20
29
|
customSquareStyles[lastMove.from] = {
|
|
21
|
-
backgroundColor:
|
|
30
|
+
backgroundColor: theme.state.lastMove,
|
|
22
31
|
};
|
|
23
32
|
customSquareStyles[lastMove.to] = {
|
|
24
|
-
backgroundColor:
|
|
33
|
+
backgroundColor: theme.state.lastMove,
|
|
25
34
|
};
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
if (activeSquare) {
|
|
29
38
|
customSquareStyles[activeSquare] = {
|
|
30
|
-
backgroundColor:
|
|
39
|
+
backgroundColor: theme.state.activeSquare,
|
|
31
40
|
};
|
|
32
41
|
}
|
|
33
42
|
|
|
@@ -37,8 +46,8 @@ export const getCustomSquareStyles = (
|
|
|
37
46
|
customSquareStyles[square] = {
|
|
38
47
|
background:
|
|
39
48
|
game.get(square) && game.get(square)?.color !== turn
|
|
40
|
-
?
|
|
41
|
-
:
|
|
49
|
+
? `radial-gradient(circle, ${theme.indicators.capture} 85%, transparent 85%)`
|
|
50
|
+
: `radial-gradient(circle, ${theme.indicators.move} 25%, transparent 25%)`,
|
|
42
51
|
};
|
|
43
52
|
});
|
|
44
53
|
}
|
|
@@ -48,7 +57,7 @@ export const getCustomSquareStyles = (
|
|
|
48
57
|
return row.forEach((square) => {
|
|
49
58
|
if (square?.type === "k" && square?.color === info.turn) {
|
|
50
59
|
customSquareStyles[square.square] = {
|
|
51
|
-
backgroundColor:
|
|
60
|
+
backgroundColor: theme.state.check,
|
|
52
61
|
};
|
|
53
62
|
}
|
|
54
63
|
});
|
package/src/utils/chess.ts
CHANGED
|
@@ -8,9 +8,17 @@ import _ from "lodash";
|
|
|
8
8
|
* @returns A new Chess.js instance with the same state as the original.
|
|
9
9
|
*/
|
|
10
10
|
export const cloneGame = (game: Chess) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
try {
|
|
12
|
+
const copy = new Chess();
|
|
13
|
+
const pgn = game?.pgn();
|
|
14
|
+
if (pgn) {
|
|
15
|
+
copy.loadPgn(pgn);
|
|
16
|
+
}
|
|
17
|
+
return copy;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error("Failed to clone game:", e);
|
|
20
|
+
return new Chess();
|
|
21
|
+
}
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
/**
|
|
@@ -74,6 +82,10 @@ export const requiresPromotion = (
|
|
|
74
82
|
game: Chess,
|
|
75
83
|
move: Parameters<Chess["move"]>[0],
|
|
76
84
|
) => {
|
|
85
|
+
if (!game) {
|
|
86
|
+
throw new Error("Game is required");
|
|
87
|
+
}
|
|
88
|
+
|
|
77
89
|
try {
|
|
78
90
|
const copy = cloneGame(game);
|
|
79
91
|
const result = copy.move(move);
|
|
@@ -100,13 +112,21 @@ export const getCurrentFen = (
|
|
|
100
112
|
const tempGame = new Chess();
|
|
101
113
|
if (currentMoveIndex === -1) {
|
|
102
114
|
if (fen) {
|
|
103
|
-
|
|
115
|
+
try {
|
|
116
|
+
tempGame.load(fen);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error("Failed to load FEN in getCurrentFen:", fen, e);
|
|
119
|
+
}
|
|
104
120
|
}
|
|
105
121
|
} else {
|
|
106
122
|
const moves = game.history().slice(0, currentMoveIndex + 1);
|
|
107
123
|
|
|
108
124
|
if (fen) {
|
|
109
|
-
|
|
125
|
+
try {
|
|
126
|
+
tempGame.load(fen);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error("Failed to load FEN in getCurrentFen:", fen, e);
|
|
129
|
+
}
|
|
110
130
|
}
|
|
111
131
|
moves.forEach((move) => tempGame.move(move));
|
|
112
132
|
}
|
package/README.MD
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
<p>
|
|
3
|
-
|
|
4
|
-
</p>
|
|
5
|
-
<p>
|
|
6
|
-
<a href="https://github.com/Clariity/react-chessboard" target="blank">react-chessboard</a> + <a href="https://github.com/jhlywa/chess.js" target="blank">chess.js</a> + nice defaults
|
|
7
|
-
</p>
|
|
8
|
-
<p>
|
|
9
|
-
An easy-customizable, ready-to-use chess game component for React.
|
|
10
|
-
</div>
|
|
11
|
-
|
|
12
|
-
## Project Description
|
|
13
|
-
|
|
14
|
-
This project is a React-based chess game that allows users to play chess online. It is part of the `react-chess-tools` package and is designed to be easy to use and customizable. It is built on top of the `react-chessboard` and `chess.js` packages, and provides a nice default configuration for the chess game, including:
|
|
15
|
-
|
|
16
|
-
- Sounds
|
|
17
|
-
- Move-by-click
|
|
18
|
-
- Square highlighting
|
|
19
|
-
- Keyboard controls
|
|
20
|
-
|
|
21
|
-
It is built using an approach similar to the one used in the `radix-ui`, where the `ChessGame` component is built using a `ChessGameContext` that you can use to customize and enhance the component game. It also provides a set of default components that you can use to build your next chess app.
|
|
22
|
-
|
|
23
|
-
## Preview
|
|
24
|
-
|
|
25
|
-
Visit the [demo](https://react-chess-tools.vercel.app/) to see the `react-chess-game` component in action.
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
|
|
29
|
-
To install the `react-chess-game` package, run the following command:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
$ npm install @react-chess-tools/react-chess-game
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Usage
|
|
36
|
-
|
|
37
|
-
To use the `react-chess-game` package, you can import the `ChessGame` component and use it as follows:
|
|
38
|
-
|
|
39
|
-
```tsx
|
|
40
|
-
import { ChessGame } from "@react-chess-tools/react-chess-game";
|
|
41
|
-
|
|
42
|
-
const App = () => (
|
|
43
|
-
<ChessGame.Root>
|
|
44
|
-
<ChessGame.Board />
|
|
45
|
-
</ChessGame.Root>
|
|
46
|
-
);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Documentation
|
|
50
|
-
|
|
51
|
-
The `react-chess-game` package provides a set of components that you can use to build your chess app. The following sections describe the components and their usage.
|
|
52
|
-
|
|
53
|
-
### ChessGame.Root
|
|
54
|
-
|
|
55
|
-
The `ChessGame.Root` component is the root component of the `react-chess-game` package. It is used to provide the `ChessGameContext` to the rest of the components. It also provides a set of default values for the `ChessGameContext` that you can customize. It instantiates a `Chess` instance using the `fen` prop and provides it to the `ChessGameContext`.
|
|
56
|
-
|
|
57
|
-
#### Props
|
|
58
|
-
|
|
59
|
-
The `ChessGame.Root` component accepts the following props:
|
|
60
|
-
|
|
61
|
-
| Name | Type | Default | Description |
|
|
62
|
-
| ----------- | ---------- | ------- | ----------------------------------------------- |
|
|
63
|
-
| children | React.FC | | The children of the `ChessGame.Root` component. |
|
|
64
|
-
| fen | string | | The initial FEN of the chess game. |
|
|
65
|
-
| orientation | "w" \| "b" | "w" | The orientation of the chess game. |
|
|
66
|
-
|
|
67
|
-
### ChessGame.Board
|
|
68
|
-
|
|
69
|
-
The `ChessGame.Board` component is the main component of the `react-chess-game` package. It is used to render the chess board and the pieces. It uses the `ChessGameContext` to get the `Chess` instance and the `orientation` of the game.
|
|
70
|
-
|
|
71
|
-
This version targets `react-chessboard` v5 and exposes a single `options` prop instead of spreading all board props.
|
|
72
|
-
|
|
73
|
-
#### Props
|
|
74
|
-
|
|
75
|
-
Accepts a single prop:
|
|
76
|
-
|
|
77
|
-
| Name | Type | Description |
|
|
78
|
-
| -------- | ------------------- | -------------------------------------------------------------------------------------------------- |
|
|
79
|
-
| options? | `ChessboardOptions` | Forwarded to `react-chessboard` v5 `Chessboard({ options })`. Your values merge with the defaults. |
|
|
80
|
-
|
|
81
|
-
Quick example (custom styles and event handlers):
|
|
82
|
-
|
|
83
|
-
```tsx
|
|
84
|
-
<ChessGame.Root>
|
|
85
|
-
<ChessGame.Board
|
|
86
|
-
options={{
|
|
87
|
-
squareStyles: { e4: { boxShadow: "inset 0 0 0 2px #4f46e5" } },
|
|
88
|
-
onPieceDrop: ({ sourceSquare, targetSquare }) => {
|
|
89
|
-
// return boolean to accept/reject drop
|
|
90
|
-
return true;
|
|
91
|
-
},
|
|
92
|
-
showNotation: true,
|
|
93
|
-
animationDurationInMs: 300,
|
|
94
|
-
}}
|
|
95
|
-
/>
|
|
96
|
-
|
|
97
|
-
{/* Other parts like <ChessGame.Sounds /> or <ChessGame.KeyboardControls /> */}
|
|
98
|
-
</ChessGame.Root>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### ChessGame.Sounds
|
|
102
|
-
|
|
103
|
-
The `ChessGame.Sounds` component is used to provide the sounds for the chess game. By default, it uses built-in sounds, but you can provide your own sounds. Sounds must be provided as base 64 encoded strings.
|
|
104
|
-
|
|
105
|
-
#### Props
|
|
106
|
-
|
|
107
|
-
The `ChessGame.Sounds` component accepts the following props:
|
|
108
|
-
|
|
109
|
-
| Name | Type | Default | Description |
|
|
110
|
-
| -------- | ------ | ------- | ------------------------------------------------- |
|
|
111
|
-
| check | string | | The sound to play when a player is in check. |
|
|
112
|
-
| move | string | | The sound to play when a player makes a move. |
|
|
113
|
-
| capture | string | | The sound to play when a player captures a piece. |
|
|
114
|
-
| gameOver | string | | The sound to play when the game is over. |
|
|
115
|
-
|
|
116
|
-
### ChessGame.KeyboardControls
|
|
117
|
-
|
|
118
|
-
The `ChessGame.KeyboardControls` component is used to provide keyboard controls for navigating through the chess game. By default, it enables arrow key controls for moving through game history, but you can customize the key bindings.
|
|
119
|
-
|
|
120
|
-
#### Props
|
|
121
|
-
|
|
122
|
-
The `ChessGame.KeyboardControls` component accepts the following props:
|
|
123
|
-
|
|
124
|
-
| Name | Type | Default | Description |
|
|
125
|
-
| -------- | ---------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
126
|
-
| controls | KeyboardControls | defaultKeyboardControls | Object mapping key names to handler functions. The default controls are: ArrowLeft (previous move), ArrowRight (next move), ArrowUp (go to start), ArrowDown (go to end) |
|
|
127
|
-
|
|
128
|
-
### useChessGameContext
|
|
129
|
-
|
|
130
|
-
The `useChessGameContext` hook is used to get the `ChessGameContext` from the `ChessGame.Root` component. It can be used to customize the chess game.
|
|
131
|
-
|
|
132
|
-
#### Returns
|
|
133
|
-
|
|
134
|
-
The `useChessGameContext` hook returns the following values:
|
|
135
|
-
|
|
136
|
-
| Name | Type | Description |
|
|
137
|
-
| ---------------- | ---------- | -------------------------------------------------------------------------------------- |
|
|
138
|
-
| game | Chess | The underlying `Chess.js` instance. |
|
|
139
|
-
| orientation | "w" \| "b" | The orientation of the chess game. |
|
|
140
|
-
| currentFen | string | The current FEN string representing the board position. |
|
|
141
|
-
| currentPosition | string | The current move in the game history. |
|
|
142
|
-
| currentMoveIndex | number | The index of the current move in the game history. |
|
|
143
|
-
| isLatestMove | boolean | Whether the current position is the latest move in the game. |
|
|
144
|
-
| methods | Methods | The methods you can use to interact with the chess game. |
|
|
145
|
-
| info | Info | The info of the chess game, calculated using the chess instance using chess.js methods |
|
|
146
|
-
|
|
147
|
-
#### Methods
|
|
148
|
-
|
|
149
|
-
The `useChessGameContext` hook also exposes these methods:
|
|
150
|
-
|
|
151
|
-
| Name | Type | Description |
|
|
152
|
-
| ---------------- | ------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------- | --- | ------------------ | ------------------------------------------------------------------ |
|
|
153
|
-
| makeMove | `(move: string | { from: Square; to: Square; promotion?: "q" | "r" | "b" | "n" }) => boolean` | Attempts a move at the latest position; returns `true` if applied. |
|
|
154
|
-
| setPosition | `(fen: string, orientation: "w" | "b") => void` | Sets a new FEN and orientation, resets history navigation to latest. |
|
|
155
|
-
| flipBoard | `() => void` | Flips the board orientation. |
|
|
156
|
-
| goToMove | `(moveIndex: number) => void` | Jumps to a specific move index (`-1` is the starting position). |
|
|
157
|
-
| goToStart | `() => void` | Goes to the starting position. |
|
|
158
|
-
| goToEnd | `() => void` | Goes to the latest move. |
|
|
159
|
-
| goToPreviousMove | `() => void` | Goes to the previous move. |
|
|
160
|
-
| goToNextMove | `() => void` | Goes to the next move. |
|
|
161
|
-
|
|
162
|
-
#### Info
|
|
163
|
-
|
|
164
|
-
The `useChessGameContext` hook returns the following info:
|
|
165
|
-
|
|
166
|
-
| Name | Type | Description |
|
|
167
|
-
| ---------------------- | ---------- | -------------------------------------------------------------------------- |
|
|
168
|
-
| turn | "w" \| "b" | The turn of the chess game |
|
|
169
|
-
| isPlayerTurn | boolean | Whether it is the player's turn |
|
|
170
|
-
| isOpponentTurn | boolean | Whether it is the opponent's turn |
|
|
171
|
-
| moveNumber | number | The number of the current move |
|
|
172
|
-
| lastMove | Move | The last move made in the chess game |
|
|
173
|
-
| isCheck | boolean | Whether the player is in check |
|
|
174
|
-
| isCheckmate | boolean | Whether the player is in checkmate |
|
|
175
|
-
| isDraw | boolean | Whether the game is a draw |
|
|
176
|
-
| isStalemate | boolean | Whether the game is a stalemate |
|
|
177
|
-
| isThreefoldRepetition | boolean | Whether the game is a threefold repetition |
|
|
178
|
-
| isInsufficientMaterial | boolean | Whether the game is a insufficient material |
|
|
179
|
-
| isGameOver | boolean | Whether the game is over |
|
|
180
|
-
| isDrawn | boolean | Whether the game is drawn |
|
|
181
|
-
| hasPlayerWon | boolean | Whether the player (the side specified in the `orientation` prop) has won |
|
|
182
|
-
| hasPlayerLost | boolean | Whether the player (the side specified in the `orientation` prop) has lost |
|
|
183
|
-
|
|
184
|
-
## 📝 License
|
|
185
|
-
|
|
186
|
-
This project is [MIT](https://opensource.org/licenses/MIT) licensed.
|
|
187
|
-
|
|
188
|
-
## Show your support
|
|
189
|
-
|
|
190
|
-
Give a ⭐️ if this project helped you!
|
package/coverage/clover.xml
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<coverage generated="1746218683707" clover="3.2.0">
|
|
3
|
-
<project timestamp="1746218683707" name="All files">
|
|
4
|
-
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0" elements="0" coveredelements="0" complexity="0" loc="0" ncloc="0" packages="0" files="0" classes="0"/>
|
|
5
|
-
</project>
|
|
6
|
-
</coverage>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|