@react-chess-tools/react-chess-game 0.5.1 → 1.0.0
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/dist/index.cjs +775 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +264 -0
- package/dist/index.d.ts +264 -0
- package/dist/{index.mjs → index.js} +172 -38
- package/dist/index.js.map +1 -0
- package/package.json +18 -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 +6 -4
- package/src/components/ChessGame/parts/Root.tsx +11 -1
- package/src/docs/Theming.mdx +281 -0
- package/src/hooks/useChessGame.ts +23 -7
- package/src/index.ts +20 -0
- package/src/theme/__tests__/context.test.tsx +75 -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/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 -143
- package/dist/index.mjs.map +0 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { ChessboardOptions } from 'react-chessboard';
|
|
2
|
+
import * as React$1 from 'react';
|
|
3
|
+
import React__default, { CSSProperties } from 'react';
|
|
4
|
+
import * as chess_js from 'chess.js';
|
|
5
|
+
import { Color, Chess } from 'chess.js';
|
|
6
|
+
|
|
7
|
+
type Sound = "check" | "move" | "capture" | "gameOver";
|
|
8
|
+
type Sounds = Record<Sound, HTMLAudioElement>;
|
|
9
|
+
|
|
10
|
+
type SoundsProps = {
|
|
11
|
+
sounds?: Partial<Record<Sound, string>>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
interface ChessGameProps {
|
|
15
|
+
options?: ChessboardOptions;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Board appearance configuration - colors for light and dark squares
|
|
20
|
+
*/
|
|
21
|
+
interface BoardTheme {
|
|
22
|
+
/** Style for light squares */
|
|
23
|
+
lightSquare: CSSProperties;
|
|
24
|
+
/** Style for dark squares */
|
|
25
|
+
darkSquare: CSSProperties;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Game state highlight colors (RGBA color strings)
|
|
29
|
+
*/
|
|
30
|
+
interface StateTheme {
|
|
31
|
+
/** Background color for last move from/to squares */
|
|
32
|
+
lastMove: string;
|
|
33
|
+
/** Background color for king when in check */
|
|
34
|
+
check: string;
|
|
35
|
+
/** Background color for currently selected piece's square */
|
|
36
|
+
activeSquare: string;
|
|
37
|
+
/** Full CSSProperties for valid drop target squares */
|
|
38
|
+
dropSquare: CSSProperties;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Move indicator styling - colors for move dots and capture rings
|
|
42
|
+
*/
|
|
43
|
+
interface IndicatorTheme {
|
|
44
|
+
/** Color for move dots on empty destination squares (used in radial-gradient) */
|
|
45
|
+
move: string;
|
|
46
|
+
/** Color for capture rings on capturable pieces (used in radial-gradient) */
|
|
47
|
+
capture: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Complete theme configuration for ChessGame component
|
|
51
|
+
*/
|
|
52
|
+
interface ChessGameTheme {
|
|
53
|
+
board: BoardTheme;
|
|
54
|
+
state: StateTheme;
|
|
55
|
+
indicators: IndicatorTheme;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Utility type for creating partial versions of nested objects
|
|
59
|
+
*/
|
|
60
|
+
type DeepPartial<T> = {
|
|
61
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Partial theme for user customization - allows overriding only specific properties
|
|
65
|
+
*/
|
|
66
|
+
type PartialChessGameTheme = DeepPartial<ChessGameTheme>;
|
|
67
|
+
|
|
68
|
+
interface RootProps {
|
|
69
|
+
fen?: string;
|
|
70
|
+
orientation?: Color;
|
|
71
|
+
/** Optional theme configuration. Supports partial themes - only override the colors you need. */
|
|
72
|
+
theme?: PartialChessGameTheme;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type useChessGameProps = {
|
|
76
|
+
fen?: string;
|
|
77
|
+
orientation?: Color;
|
|
78
|
+
};
|
|
79
|
+
declare const useChessGame: ({ fen, orientation: initialOrientation, }?: useChessGameProps) => {
|
|
80
|
+
game: Chess;
|
|
81
|
+
currentFen: string;
|
|
82
|
+
currentPosition: string;
|
|
83
|
+
orientation: Color;
|
|
84
|
+
currentMoveIndex: number;
|
|
85
|
+
isLatestMove: boolean;
|
|
86
|
+
info: {
|
|
87
|
+
turn: Color;
|
|
88
|
+
isPlayerTurn: boolean;
|
|
89
|
+
isOpponentTurn: boolean;
|
|
90
|
+
moveNumber: number;
|
|
91
|
+
lastMove: chess_js.Move | undefined;
|
|
92
|
+
isCheck: boolean;
|
|
93
|
+
isCheckmate: boolean;
|
|
94
|
+
isDraw: boolean;
|
|
95
|
+
isStalemate: boolean;
|
|
96
|
+
isThreefoldRepetition: boolean;
|
|
97
|
+
isInsufficientMaterial: boolean;
|
|
98
|
+
isGameOver: boolean;
|
|
99
|
+
isDrawn: boolean;
|
|
100
|
+
hasPlayerWon: boolean;
|
|
101
|
+
hasPlayerLost: boolean;
|
|
102
|
+
};
|
|
103
|
+
methods: {
|
|
104
|
+
makeMove: (move: Parameters<Chess["move"]>[0]) => boolean;
|
|
105
|
+
setPosition: (fen: string, orientation: Color) => void;
|
|
106
|
+
flipBoard: () => void;
|
|
107
|
+
goToMove: (moveIndex: number) => void;
|
|
108
|
+
goToStart: () => void;
|
|
109
|
+
goToEnd: () => void;
|
|
110
|
+
goToPreviousMove: () => void;
|
|
111
|
+
goToNextMove: () => void;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
declare const useChessGameContext: () => {
|
|
116
|
+
game: chess_js.Chess;
|
|
117
|
+
currentFen: string;
|
|
118
|
+
currentPosition: string;
|
|
119
|
+
orientation: chess_js.Color;
|
|
120
|
+
currentMoveIndex: number;
|
|
121
|
+
isLatestMove: boolean;
|
|
122
|
+
info: {
|
|
123
|
+
turn: chess_js.Color;
|
|
124
|
+
isPlayerTurn: boolean;
|
|
125
|
+
isOpponentTurn: boolean;
|
|
126
|
+
moveNumber: number;
|
|
127
|
+
lastMove: chess_js.Move | undefined;
|
|
128
|
+
isCheck: boolean;
|
|
129
|
+
isCheckmate: boolean;
|
|
130
|
+
isDraw: boolean;
|
|
131
|
+
isStalemate: boolean;
|
|
132
|
+
isThreefoldRepetition: boolean;
|
|
133
|
+
isInsufficientMaterial: boolean;
|
|
134
|
+
isGameOver: boolean;
|
|
135
|
+
isDrawn: boolean;
|
|
136
|
+
hasPlayerWon: boolean;
|
|
137
|
+
hasPlayerLost: boolean;
|
|
138
|
+
};
|
|
139
|
+
methods: {
|
|
140
|
+
makeMove: (move: Parameters<chess_js.Chess["move"]>[0]) => boolean;
|
|
141
|
+
setPosition: (fen: string, orientation: chess_js.Color) => void;
|
|
142
|
+
flipBoard: () => void;
|
|
143
|
+
goToMove: (moveIndex: number) => void;
|
|
144
|
+
goToStart: () => void;
|
|
145
|
+
goToEnd: () => void;
|
|
146
|
+
goToPreviousMove: () => void;
|
|
147
|
+
goToNextMove: () => void;
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
type ChessGameContextType = ReturnType<typeof useChessGame>;
|
|
151
|
+
|
|
152
|
+
type KeyboardControlsProps = {
|
|
153
|
+
controls?: KeyboardControls;
|
|
154
|
+
};
|
|
155
|
+
type KeyboardControls = Record<string, (context: ChessGameContextType) => void>;
|
|
156
|
+
declare const KeyboardControls: React.FC<KeyboardControlsProps>;
|
|
157
|
+
|
|
158
|
+
declare const ChessGame: {
|
|
159
|
+
Root: React$1.FC<React$1.PropsWithChildren<RootProps>>;
|
|
160
|
+
Board: React$1.FC<ChessGameProps>;
|
|
161
|
+
Sounds: React$1.FC<SoundsProps>;
|
|
162
|
+
KeyboardControls: React$1.FC<{
|
|
163
|
+
controls?: KeyboardControls;
|
|
164
|
+
}>;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Returns an object with information about the current state of the game. This can be determined
|
|
169
|
+
* using chess.js instance, but this function is provided for convenience.
|
|
170
|
+
* @param game - The Chess.js instance representing the game.
|
|
171
|
+
* @returns An object with information about the current state of the game.
|
|
172
|
+
*/
|
|
173
|
+
declare const getGameInfo: (game: Chess, orientation: Color) => {
|
|
174
|
+
turn: Color;
|
|
175
|
+
isPlayerTurn: boolean;
|
|
176
|
+
isOpponentTurn: boolean;
|
|
177
|
+
moveNumber: number;
|
|
178
|
+
lastMove: chess_js.Move | undefined;
|
|
179
|
+
isCheck: boolean;
|
|
180
|
+
isCheckmate: boolean;
|
|
181
|
+
isDraw: boolean;
|
|
182
|
+
isStalemate: boolean;
|
|
183
|
+
isThreefoldRepetition: boolean;
|
|
184
|
+
isInsufficientMaterial: boolean;
|
|
185
|
+
isGameOver: boolean;
|
|
186
|
+
isDrawn: boolean;
|
|
187
|
+
hasPlayerWon: boolean;
|
|
188
|
+
hasPlayerLost: boolean;
|
|
189
|
+
};
|
|
190
|
+
type GameInfo = ReturnType<typeof getGameInfo>;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Smart deep merge for ChessboardOptions that handles different property types appropriately:
|
|
194
|
+
* - Functions: Overwrite (custom functions replace base functions)
|
|
195
|
+
* - Objects: Deep merge (nested objects merge recursively)
|
|
196
|
+
* - Primitives: Overwrite (custom values replace base values)
|
|
197
|
+
*
|
|
198
|
+
* This ensures that computed options (like squareStyles with move highlighting) are preserved
|
|
199
|
+
* while allowing custom options to extend or override them intelligently.
|
|
200
|
+
*
|
|
201
|
+
* @param baseOptions - The computed base options (e.g., computed squareStyles, event handlers)
|
|
202
|
+
* @param customOptions - Custom options provided by the user
|
|
203
|
+
* @returns Intelligently merged ChessboardOptions
|
|
204
|
+
*/
|
|
205
|
+
declare const deepMergeChessboardOptions: (baseOptions: ChessboardOptions, customOptions?: Partial<ChessboardOptions>) => ChessboardOptions;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Default theme for ChessGame component.
|
|
209
|
+
* These values match the original hardcoded colors for backward compatibility.
|
|
210
|
+
*/
|
|
211
|
+
declare const defaultGameTheme: ChessGameTheme;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Lichess-inspired theme with green highlights
|
|
215
|
+
*/
|
|
216
|
+
declare const lichessTheme: ChessGameTheme;
|
|
217
|
+
/**
|
|
218
|
+
* Chess.com-inspired theme with green board and yellow highlights
|
|
219
|
+
*/
|
|
220
|
+
declare const chessComTheme: ChessGameTheme;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Deep merges a partial theme with the default theme.
|
|
224
|
+
* Allows users to override only specific theme properties while keeping defaults for the rest.
|
|
225
|
+
*
|
|
226
|
+
* @param partialTheme - Partial theme with only the properties to override
|
|
227
|
+
* @returns Complete theme with overridden properties merged with defaults
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const customTheme = mergeTheme({
|
|
232
|
+
* state: { lastMove: "rgba(100, 200, 100, 0.6)" }
|
|
233
|
+
* });
|
|
234
|
+
* // Returns full theme with only lastMove color changed
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
declare const mergeTheme: (partialTheme?: PartialChessGameTheme) => ChessGameTheme;
|
|
238
|
+
/**
|
|
239
|
+
* Deep merges a partial theme with a base theme.
|
|
240
|
+
* Useful when extending an existing theme.
|
|
241
|
+
*
|
|
242
|
+
* @param baseTheme - The base theme to extend
|
|
243
|
+
* @param partialTheme - Partial theme with properties to override
|
|
244
|
+
* @returns Complete theme with overridden properties
|
|
245
|
+
*/
|
|
246
|
+
declare const mergeThemeWith: (baseTheme: ChessGameTheme, partialTheme?: PartialChessGameTheme) => ChessGameTheme;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Context for ChessGame theme
|
|
250
|
+
*/
|
|
251
|
+
declare const ChessGameThemeContext: React__default.Context<ChessGameTheme>;
|
|
252
|
+
/**
|
|
253
|
+
* Hook to access the current ChessGame theme.
|
|
254
|
+
* Returns the default theme if no ThemeProvider is present.
|
|
255
|
+
*/
|
|
256
|
+
declare const useChessGameTheme: () => ChessGameTheme;
|
|
257
|
+
|
|
258
|
+
declare const themes: {
|
|
259
|
+
readonly default: ChessGameTheme;
|
|
260
|
+
readonly lichess: ChessGameTheme;
|
|
261
|
+
readonly chessCom: ChessGameTheme;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export { type BoardTheme, ChessGame, type ChessGameContextType, type ChessGameProps, type ChessGameTheme, ChessGameThemeContext, type DeepPartial, type GameInfo, type IndicatorTheme, KeyboardControls, type PartialChessGameTheme, type RootProps, type Sound, type Sounds, type SoundsProps, type StateTheme, chessComTheme, deepMergeChessboardOptions, defaultGameTheme, lichessTheme, mergeTheme, mergeThemeWith, themes, useChessGame, useChessGameContext, type useChessGameProps, useChessGameTheme };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/ChessGame/parts/Root.tsx
|
|
2
|
-
import
|
|
2
|
+
import React4 from "react";
|
|
3
3
|
|
|
4
4
|
// src/hooks/useChessGame.ts
|
|
5
5
|
import React, { useEffect } from "react";
|
|
@@ -9,9 +9,17 @@ import { Chess as Chess2 } from "chess.js";
|
|
|
9
9
|
import { Chess } from "chess.js";
|
|
10
10
|
import _ from "lodash";
|
|
11
11
|
var cloneGame = (game) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
try {
|
|
13
|
+
const copy = new Chess();
|
|
14
|
+
const pgn = game == null ? void 0 : game.pgn();
|
|
15
|
+
if (pgn) {
|
|
16
|
+
copy.loadPgn(pgn);
|
|
17
|
+
}
|
|
18
|
+
return copy;
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.error("Failed to clone game:", e);
|
|
21
|
+
return new Chess();
|
|
22
|
+
}
|
|
15
23
|
};
|
|
16
24
|
var getGameInfo = (game, orientation) => {
|
|
17
25
|
const turn = game.turn();
|
|
@@ -57,6 +65,9 @@ var isLegalMove = (game, move) => {
|
|
|
57
65
|
}
|
|
58
66
|
};
|
|
59
67
|
var requiresPromotion = (game, move) => {
|
|
68
|
+
if (!game) {
|
|
69
|
+
throw new Error("Game is required");
|
|
70
|
+
}
|
|
60
71
|
try {
|
|
61
72
|
const copy = cloneGame(game);
|
|
62
73
|
const result = copy.move(move);
|
|
@@ -76,12 +87,20 @@ var getCurrentFen = (fen, game, currentMoveIndex) => {
|
|
|
76
87
|
const tempGame = new Chess();
|
|
77
88
|
if (currentMoveIndex === -1) {
|
|
78
89
|
if (fen) {
|
|
79
|
-
|
|
90
|
+
try {
|
|
91
|
+
tempGame.load(fen);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error("Failed to load FEN in getCurrentFen:", fen, e);
|
|
94
|
+
}
|
|
80
95
|
}
|
|
81
96
|
} else {
|
|
82
97
|
const moves = game.history().slice(0, currentMoveIndex + 1);
|
|
83
98
|
if (fen) {
|
|
84
|
-
|
|
99
|
+
try {
|
|
100
|
+
tempGame.load(fen);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.error("Failed to load FEN in getCurrentFen:", fen, e);
|
|
103
|
+
}
|
|
85
104
|
}
|
|
86
105
|
moves.forEach((move) => tempGame.move(move));
|
|
87
106
|
}
|
|
@@ -93,9 +112,21 @@ var useChessGame = ({
|
|
|
93
112
|
fen,
|
|
94
113
|
orientation: initialOrientation
|
|
95
114
|
} = {}) => {
|
|
96
|
-
const [game, setGame] = React.useState(
|
|
115
|
+
const [game, setGame] = React.useState(() => {
|
|
116
|
+
try {
|
|
117
|
+
return new Chess2(fen);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.error("Invalid FEN:", fen, e);
|
|
120
|
+
return new Chess2();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
97
123
|
useEffect(() => {
|
|
98
|
-
|
|
124
|
+
try {
|
|
125
|
+
setGame(new Chess2(fen));
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error("Invalid FEN:", fen, e);
|
|
128
|
+
setGame(new Chess2());
|
|
129
|
+
}
|
|
99
130
|
}, [fen]);
|
|
100
131
|
const [orientation, setOrientation] = React.useState(
|
|
101
132
|
initialOrientation ?? "w"
|
|
@@ -119,11 +150,15 @@ var useChessGame = ({
|
|
|
119
150
|
[game, currentMoveIndex]
|
|
120
151
|
);
|
|
121
152
|
const setPosition = React.useCallback((fen2, orientation2) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
153
|
+
try {
|
|
154
|
+
const newGame = new Chess2();
|
|
155
|
+
newGame.load(fen2);
|
|
156
|
+
setOrientation(orientation2);
|
|
157
|
+
setGame(newGame);
|
|
158
|
+
setCurrentMoveIndex(-1);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
console.error("Failed to load FEN:", fen2, e);
|
|
161
|
+
}
|
|
127
162
|
}, []);
|
|
128
163
|
const makeMove = React.useCallback(
|
|
129
164
|
(move) => {
|
|
@@ -212,18 +247,68 @@ var useChessGameContext = () => {
|
|
|
212
247
|
return context;
|
|
213
248
|
};
|
|
214
249
|
|
|
250
|
+
// src/theme/context.tsx
|
|
251
|
+
import React3, { createContext, useContext } from "react";
|
|
252
|
+
|
|
253
|
+
// src/theme/defaults.ts
|
|
254
|
+
var defaultGameTheme = {
|
|
255
|
+
board: {
|
|
256
|
+
lightSquare: { backgroundColor: "#f0d9b5" },
|
|
257
|
+
darkSquare: { backgroundColor: "#b58863" }
|
|
258
|
+
},
|
|
259
|
+
state: {
|
|
260
|
+
lastMove: "rgba(255, 255, 0, 0.5)",
|
|
261
|
+
check: "rgba(255, 0, 0, 0.5)",
|
|
262
|
+
activeSquare: "rgba(255, 255, 0, 0.5)",
|
|
263
|
+
dropSquare: { backgroundColor: "rgba(255, 255, 0, 0.4)" }
|
|
264
|
+
},
|
|
265
|
+
indicators: {
|
|
266
|
+
move: "rgba(0, 0, 0, 0.1)",
|
|
267
|
+
capture: "rgba(1, 0, 0, 0.1)"
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/theme/context.tsx
|
|
272
|
+
var ChessGameThemeContext = createContext(defaultGameTheme);
|
|
273
|
+
var useChessGameTheme = () => {
|
|
274
|
+
return useContext(ChessGameThemeContext);
|
|
275
|
+
};
|
|
276
|
+
var ThemeProvider = ({
|
|
277
|
+
theme,
|
|
278
|
+
children
|
|
279
|
+
}) => {
|
|
280
|
+
return /* @__PURE__ */ React3.createElement(ChessGameThemeContext.Provider, { value: theme }, children);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/theme/utils.ts
|
|
284
|
+
import { merge } from "lodash";
|
|
285
|
+
var mergeTheme = (partialTheme) => {
|
|
286
|
+
if (!partialTheme) {
|
|
287
|
+
return { ...defaultGameTheme };
|
|
288
|
+
}
|
|
289
|
+
return merge({}, defaultGameTheme, partialTheme);
|
|
290
|
+
};
|
|
291
|
+
var mergeThemeWith = (baseTheme, partialTheme) => {
|
|
292
|
+
if (!partialTheme) {
|
|
293
|
+
return { ...baseTheme };
|
|
294
|
+
}
|
|
295
|
+
return merge({}, baseTheme, partialTheme);
|
|
296
|
+
};
|
|
297
|
+
|
|
215
298
|
// src/components/ChessGame/parts/Root.tsx
|
|
216
299
|
var Root = ({
|
|
217
300
|
fen,
|
|
218
301
|
orientation,
|
|
302
|
+
theme,
|
|
219
303
|
children
|
|
220
304
|
}) => {
|
|
221
305
|
const context = useChessGame({ fen, orientation });
|
|
222
|
-
|
|
306
|
+
const mergedTheme = React4.useMemo(() => mergeTheme(theme), [theme]);
|
|
307
|
+
return /* @__PURE__ */ React4.createElement(ChessGameContext.Provider, { value: context }, /* @__PURE__ */ React4.createElement(ThemeProvider, { theme: mergedTheme }, children));
|
|
223
308
|
};
|
|
224
309
|
|
|
225
310
|
// src/components/ChessGame/parts/Board.tsx
|
|
226
|
-
import
|
|
311
|
+
import React5 from "react";
|
|
227
312
|
import {
|
|
228
313
|
Chessboard,
|
|
229
314
|
defaultPieces,
|
|
@@ -231,23 +316,21 @@ import {
|
|
|
231
316
|
} from "react-chessboard";
|
|
232
317
|
|
|
233
318
|
// src/utils/board.ts
|
|
234
|
-
import { merge } from "lodash";
|
|
235
|
-
var
|
|
236
|
-
var CHECK_COLOR = "rgba(255, 0, 0, 0.5)";
|
|
237
|
-
var getCustomSquareStyles = (game, info, activeSquare) => {
|
|
319
|
+
import { merge as merge2 } from "lodash";
|
|
320
|
+
var getCustomSquareStyles = (game, info, activeSquare, theme = defaultGameTheme) => {
|
|
238
321
|
const customSquareStyles = {};
|
|
239
322
|
const { lastMove, isCheck, turn } = info;
|
|
240
323
|
if (lastMove) {
|
|
241
324
|
customSquareStyles[lastMove.from] = {
|
|
242
|
-
backgroundColor:
|
|
325
|
+
backgroundColor: theme.state.lastMove
|
|
243
326
|
};
|
|
244
327
|
customSquareStyles[lastMove.to] = {
|
|
245
|
-
backgroundColor:
|
|
328
|
+
backgroundColor: theme.state.lastMove
|
|
246
329
|
};
|
|
247
330
|
}
|
|
248
331
|
if (activeSquare) {
|
|
249
332
|
customSquareStyles[activeSquare] = {
|
|
250
|
-
backgroundColor:
|
|
333
|
+
backgroundColor: theme.state.activeSquare
|
|
251
334
|
};
|
|
252
335
|
}
|
|
253
336
|
if (activeSquare) {
|
|
@@ -255,7 +338,7 @@ var getCustomSquareStyles = (game, info, activeSquare) => {
|
|
|
255
338
|
destinationSquares.forEach((square) => {
|
|
256
339
|
var _a;
|
|
257
340
|
customSquareStyles[square] = {
|
|
258
|
-
background: game.get(square) && ((_a = game.get(square)) == null ? void 0 : _a.color) !== turn ?
|
|
341
|
+
background: game.get(square) && ((_a = game.get(square)) == null ? void 0 : _a.color) !== turn ? `radial-gradient(circle, ${theme.indicators.capture} 85%, transparent 85%)` : `radial-gradient(circle, ${theme.indicators.move} 25%, transparent 25%)`
|
|
259
342
|
};
|
|
260
343
|
});
|
|
261
344
|
}
|
|
@@ -264,7 +347,7 @@ var getCustomSquareStyles = (game, info, activeSquare) => {
|
|
|
264
347
|
return row.forEach((square) => {
|
|
265
348
|
if ((square == null ? void 0 : square.type) === "k" && (square == null ? void 0 : square.color) === info.turn) {
|
|
266
349
|
customSquareStyles[square.square] = {
|
|
267
|
-
backgroundColor:
|
|
350
|
+
backgroundColor: theme.state.check
|
|
268
351
|
};
|
|
269
352
|
}
|
|
270
353
|
});
|
|
@@ -276,7 +359,7 @@ var deepMergeChessboardOptions = (baseOptions, customOptions) => {
|
|
|
276
359
|
if (!customOptions) {
|
|
277
360
|
return { ...baseOptions };
|
|
278
361
|
}
|
|
279
|
-
const result =
|
|
362
|
+
const result = merge2({}, baseOptions, customOptions, {
|
|
280
363
|
customizer: (_objValue, srcValue) => {
|
|
281
364
|
if (typeof srcValue === "function") {
|
|
282
365
|
return srcValue;
|
|
@@ -295,6 +378,7 @@ var deepMergeChessboardOptions = (baseOptions, customOptions) => {
|
|
|
295
378
|
var Board = ({ options = {} }) => {
|
|
296
379
|
var _a, _b, _c;
|
|
297
380
|
const gameContext = useChessGameContext();
|
|
381
|
+
const theme = useChessGameTheme();
|
|
298
382
|
if (!gameContext) {
|
|
299
383
|
throw new Error("ChessGameContext not found");
|
|
300
384
|
}
|
|
@@ -307,8 +391,8 @@ var Board = ({ options = {} }) => {
|
|
|
307
391
|
methods: { makeMove }
|
|
308
392
|
} = gameContext;
|
|
309
393
|
const { turn, isGameOver } = info;
|
|
310
|
-
const [activeSquare, setActiveSquare] =
|
|
311
|
-
const [promotionMove, setPromotionMove] =
|
|
394
|
+
const [activeSquare, setActiveSquare] = React5.useState(null);
|
|
395
|
+
const [promotionMove, setPromotionMove] = React5.useState(null);
|
|
312
396
|
const onSquareClick = (square) => {
|
|
313
397
|
if (isGameOver) {
|
|
314
398
|
return;
|
|
@@ -357,13 +441,13 @@ var Board = ({ options = {} }) => {
|
|
|
357
441
|
setActiveSquare(null);
|
|
358
442
|
setPromotionMove(null);
|
|
359
443
|
};
|
|
360
|
-
const squareWidth =
|
|
444
|
+
const squareWidth = React5.useMemo(() => {
|
|
361
445
|
var _a2;
|
|
362
446
|
if (typeof document === "undefined") return 80;
|
|
363
447
|
const squareElement = document.querySelector(`[data-square]`);
|
|
364
448
|
return ((_a2 = squareElement == null ? void 0 : squareElement.getBoundingClientRect()) == null ? void 0 : _a2.width) ?? 80;
|
|
365
449
|
}, [promotionMove]);
|
|
366
|
-
const promotionSquareLeft =
|
|
450
|
+
const promotionSquareLeft = React5.useMemo(() => {
|
|
367
451
|
var _a2;
|
|
368
452
|
if (!(promotionMove == null ? void 0 : promotionMove.to)) return 0;
|
|
369
453
|
const column = ((_a2 = promotionMove.to.match(/^[a-h]/)) == null ? void 0 : _a2[0]) ?? "a";
|
|
@@ -374,18 +458,18 @@ var Board = ({ options = {} }) => {
|
|
|
374
458
|
);
|
|
375
459
|
}, [promotionMove, squareWidth, orientation]);
|
|
376
460
|
const baseOptions = {
|
|
377
|
-
squareStyles: getCustomSquareStyles(game, info, activeSquare),
|
|
461
|
+
squareStyles: getCustomSquareStyles(game, info, activeSquare, theme),
|
|
378
462
|
boardOrientation: orientation === "b" ? "black" : "white",
|
|
379
463
|
position: currentFen,
|
|
380
464
|
showNotation: true,
|
|
381
465
|
showAnimations: isLatestMove,
|
|
466
|
+
lightSquareStyle: theme.board.lightSquare,
|
|
467
|
+
darkSquareStyle: theme.board.darkSquare,
|
|
382
468
|
canDragPiece: ({ piece }) => {
|
|
383
469
|
if (isGameOver) return false;
|
|
384
470
|
return piece.pieceType[0] === turn;
|
|
385
471
|
},
|
|
386
|
-
dropSquareStyle:
|
|
387
|
-
backgroundColor: "rgba(255, 255, 0, 0.4)"
|
|
388
|
-
},
|
|
472
|
+
dropSquareStyle: theme.state.dropSquare,
|
|
389
473
|
onPieceDrag: ({ piece, square }) => {
|
|
390
474
|
if (piece.pieceType[0] === turn) {
|
|
391
475
|
setActiveSquare(square);
|
|
@@ -413,7 +497,7 @@ var Board = ({ options = {} }) => {
|
|
|
413
497
|
animationDurationInMs: game.history().length === 0 ? 0 : 300
|
|
414
498
|
};
|
|
415
499
|
const mergedOptions = deepMergeChessboardOptions(baseOptions, options);
|
|
416
|
-
return /* @__PURE__ */
|
|
500
|
+
return /* @__PURE__ */ React5.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React5.createElement(Chessboard, { options: mergedOptions }), promotionMove && /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(
|
|
417
501
|
"div",
|
|
418
502
|
{
|
|
419
503
|
onClick: () => setPromotionMove(null),
|
|
@@ -431,7 +515,7 @@ var Board = ({ options = {} }) => {
|
|
|
431
515
|
zIndex: 1e3
|
|
432
516
|
}
|
|
433
517
|
}
|
|
434
|
-
), /* @__PURE__ */
|
|
518
|
+
), /* @__PURE__ */ React5.createElement(
|
|
435
519
|
"div",
|
|
436
520
|
{
|
|
437
521
|
style: {
|
|
@@ -447,7 +531,7 @@ var Board = ({ options = {} }) => {
|
|
|
447
531
|
boxShadow: "0 0 10px 0 rgba(0, 0, 0, 0.5)"
|
|
448
532
|
}
|
|
449
533
|
},
|
|
450
|
-
["q", "r", "n", "b"].map((piece) => /* @__PURE__ */
|
|
534
|
+
["q", "r", "n", "b"].map((piece) => /* @__PURE__ */ React5.createElement(
|
|
451
535
|
"button",
|
|
452
536
|
{
|
|
453
537
|
key: piece,
|
|
@@ -589,9 +673,59 @@ var ChessGame = {
|
|
|
589
673
|
Sounds,
|
|
590
674
|
KeyboardControls: KeyboardControls2
|
|
591
675
|
};
|
|
676
|
+
|
|
677
|
+
// src/theme/presets.ts
|
|
678
|
+
var lichessTheme = {
|
|
679
|
+
board: {
|
|
680
|
+
lightSquare: { backgroundColor: "#f0d9b5" },
|
|
681
|
+
darkSquare: { backgroundColor: "#b58863" }
|
|
682
|
+
},
|
|
683
|
+
state: {
|
|
684
|
+
lastMove: "rgba(155, 199, 0, 0.41)",
|
|
685
|
+
check: "rgba(255, 0, 0, 0.5)",
|
|
686
|
+
activeSquare: "rgba(20, 85, 30, 0.5)",
|
|
687
|
+
dropSquare: { backgroundColor: "rgba(20, 85, 30, 0.3)" }
|
|
688
|
+
},
|
|
689
|
+
indicators: {
|
|
690
|
+
move: "rgba(20, 85, 30, 0.3)",
|
|
691
|
+
capture: "rgba(20, 85, 30, 0.3)"
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
var chessComTheme = {
|
|
695
|
+
board: {
|
|
696
|
+
lightSquare: { backgroundColor: "#ebecd0" },
|
|
697
|
+
darkSquare: { backgroundColor: "#779556" }
|
|
698
|
+
},
|
|
699
|
+
state: {
|
|
700
|
+
lastMove: "rgba(255, 255, 0, 0.5)",
|
|
701
|
+
check: "rgba(255, 0, 0, 0.7)",
|
|
702
|
+
activeSquare: "rgba(255, 255, 0, 0.5)",
|
|
703
|
+
dropSquare: { backgroundColor: "rgba(255, 255, 0, 0.4)" }
|
|
704
|
+
},
|
|
705
|
+
indicators: {
|
|
706
|
+
move: "rgba(0, 0, 0, 0.1)",
|
|
707
|
+
capture: "rgba(0, 0, 0, 0.1)"
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
// src/theme/index.ts
|
|
712
|
+
var themes = {
|
|
713
|
+
default: defaultGameTheme,
|
|
714
|
+
lichess: lichessTheme,
|
|
715
|
+
chessCom: chessComTheme
|
|
716
|
+
};
|
|
592
717
|
export {
|
|
593
718
|
ChessGame,
|
|
719
|
+
ChessGameThemeContext,
|
|
720
|
+
chessComTheme,
|
|
721
|
+
deepMergeChessboardOptions,
|
|
722
|
+
defaultGameTheme,
|
|
723
|
+
lichessTheme,
|
|
724
|
+
mergeTheme,
|
|
725
|
+
mergeThemeWith,
|
|
726
|
+
themes,
|
|
594
727
|
useChessGame,
|
|
595
|
-
useChessGameContext
|
|
728
|
+
useChessGameContext,
|
|
729
|
+
useChessGameTheme
|
|
596
730
|
};
|
|
597
|
-
//# sourceMappingURL=index.
|
|
731
|
+
//# sourceMappingURL=index.js.map
|