react-native-chess-kit 0.1.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/LICENSE +21 -0
- package/README.md +168 -0
- package/lib/commonjs/board-background.js +49 -0
- package/lib/commonjs/board-background.js.map +1 -0
- package/lib/commonjs/board-coordinates.js +78 -0
- package/lib/commonjs/board-coordinates.js.map +1 -0
- package/lib/commonjs/board-drag-ghost.js +110 -0
- package/lib/commonjs/board-drag-ghost.js.map +1 -0
- package/lib/commonjs/board-legal-dots.js +67 -0
- package/lib/commonjs/board-legal-dots.js.map +1 -0
- package/lib/commonjs/board-piece.js +74 -0
- package/lib/commonjs/board-piece.js.map +1 -0
- package/lib/commonjs/board-pieces.js +47 -0
- package/lib/commonjs/board-pieces.js.map +1 -0
- package/lib/commonjs/board.js +188 -0
- package/lib/commonjs/board.js.map +1 -0
- package/lib/commonjs/index.js +26 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +6 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/use-board-gesture.js +158 -0
- package/lib/commonjs/use-board-gesture.js.map +1 -0
- package/lib/commonjs/use-board-pieces.js +195 -0
- package/lib/commonjs/use-board-pieces.js.map +1 -0
- package/lib/commonjs/use-board-state.js +78 -0
- package/lib/commonjs/use-board-state.js.map +1 -0
- package/lib/module/board-background.js +44 -0
- package/lib/module/board-background.js.map +1 -0
- package/lib/module/board-coordinates.js +73 -0
- package/lib/module/board-coordinates.js.map +1 -0
- package/lib/module/board-drag-ghost.js +104 -0
- package/lib/module/board-drag-ghost.js.map +1 -0
- package/lib/module/board-legal-dots.js +62 -0
- package/lib/module/board-legal-dots.js.map +1 -0
- package/lib/module/board-piece.js +69 -0
- package/lib/module/board-piece.js.map +1 -0
- package/lib/module/board-pieces.js +42 -0
- package/lib/module/board-pieces.js.map +1 -0
- package/lib/module/board.js +184 -0
- package/lib/module/board.js.map +1 -0
- package/lib/module/index.js +21 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/use-board-gesture.js +154 -0
- package/lib/module/use-board-gesture.js.map +1 -0
- package/lib/module/use-board-pieces.js +189 -0
- package/lib/module/use-board-pieces.js.map +1 -0
- package/lib/module/use-board-state.js +74 -0
- package/lib/module/use-board-state.js.map +1 -0
- package/lib/typescript/board-background.d.ts +15 -0
- package/lib/typescript/board-background.d.ts.map +1 -0
- package/lib/typescript/board-coordinates.d.ts +20 -0
- package/lib/typescript/board-coordinates.d.ts.map +1 -0
- package/lib/typescript/board-drag-ghost.d.ts +21 -0
- package/lib/typescript/board-drag-ghost.d.ts.map +1 -0
- package/lib/typescript/board-legal-dots.d.ts +16 -0
- package/lib/typescript/board-legal-dots.d.ts.map +1 -0
- package/lib/typescript/board-piece.d.ts +36 -0
- package/lib/typescript/board-piece.d.ts.map +1 -0
- package/lib/typescript/board-pieces.d.ts +22 -0
- package/lib/typescript/board-pieces.d.ts.map +1 -0
- package/lib/typescript/board.d.ts +17 -0
- package/lib/typescript/board.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +88 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/use-board-gesture.d.ts +46 -0
- package/lib/typescript/use-board-gesture.d.ts.map +1 -0
- package/lib/typescript/use-board-pieces.d.ts +23 -0
- package/lib/typescript/use-board-pieces.d.ts.map +1 -0
- package/lib/typescript/use-board-state.d.ts +35 -0
- package/lib/typescript/use-board-state.d.ts.map +1 -0
- package/package.json +73 -0
- package/src/board-background.tsx +46 -0
- package/src/board-coordinates.tsx +98 -0
- package/src/board-drag-ghost.tsx +132 -0
- package/src/board-legal-dots.tsx +73 -0
- package/src/board-piece.tsx +104 -0
- package/src/board-pieces.tsx +56 -0
- package/src/board.tsx +203 -0
- package/src/index.ts +39 -0
- package/src/types.ts +114 -0
- package/src/use-board-gesture.ts +201 -0
- package/src/use-board-pieces.ts +158 -0
- package/src/use-board-state.ts +104 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { forwardRef, useState, useCallback, useImperativeHandle, useEffect } from 'react';
|
|
4
|
+
import { View } from 'react-native';
|
|
5
|
+
import { GestureDetector } from 'react-native-gesture-handler';
|
|
6
|
+
import { useBoardPieces } from './use-board-pieces';
|
|
7
|
+
import { useBoardState } from './use-board-state';
|
|
8
|
+
import { useBoardGesture } from './use-board-gesture';
|
|
9
|
+
import { BoardBackground } from './board-background';
|
|
10
|
+
import { BoardCoordinates } from './board-coordinates';
|
|
11
|
+
import { BoardLegalDots } from './board-legal-dots';
|
|
12
|
+
import { BoardPiecesLayer } from './board-pieces';
|
|
13
|
+
import { BoardDragGhost } from './board-drag-ghost';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* High-performance custom chess board built on Reanimated + Gesture Handler.
|
|
17
|
+
*
|
|
18
|
+
* Architecture:
|
|
19
|
+
* - 1 gesture handler (vs 32 in typical implementations)
|
|
20
|
+
* - ~40 components mounted (vs ~281)
|
|
21
|
+
* - ~75 native views (vs ~470)
|
|
22
|
+
* - 0 React Context providers
|
|
23
|
+
* - 0 re-renders during drag (pure worklet — only 2 shared value writes per frame)
|
|
24
|
+
*
|
|
25
|
+
* Follows chess.com/lichess pattern: single gesture receiver on the board,
|
|
26
|
+
* coordinate math to determine touched piece, shared values for drag state.
|
|
27
|
+
*/
|
|
28
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
29
|
+
export const Board = /*#__PURE__*/forwardRef(function Board({
|
|
30
|
+
fen,
|
|
31
|
+
orientation,
|
|
32
|
+
boardSize,
|
|
33
|
+
gestureEnabled,
|
|
34
|
+
player,
|
|
35
|
+
onMove,
|
|
36
|
+
colors,
|
|
37
|
+
moveDuration,
|
|
38
|
+
withLetters,
|
|
39
|
+
withNumbers,
|
|
40
|
+
renderPiece,
|
|
41
|
+
showLegalMoves,
|
|
42
|
+
moveMethod
|
|
43
|
+
}, ref) {
|
|
44
|
+
const squareSize = boardSize / 8;
|
|
45
|
+
|
|
46
|
+
// --- Piece data from FEN ---
|
|
47
|
+
const pieces = useBoardPieces(fen);
|
|
48
|
+
|
|
49
|
+
// --- Chess.js for legal move validation ---
|
|
50
|
+
const boardState = useBoardState(fen);
|
|
51
|
+
|
|
52
|
+
// Sync internal chess.js when parent changes FEN (puzzle reset, opponent move, etc.)
|
|
53
|
+
// Must be in useEffect — side effects during render violate React's rules
|
|
54
|
+
// and can fire multiple times in concurrent mode.
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
boardState.loadFen(fen);
|
|
57
|
+
}, [fen, boardState]);
|
|
58
|
+
|
|
59
|
+
// --- Selection state (triggers legal dots display) ---
|
|
60
|
+
const [selectedSquare, setSelectedSquare] = useState(null);
|
|
61
|
+
const [legalMoves, setLegalMoves] = useState([]);
|
|
62
|
+
|
|
63
|
+
// Default piece renderer (no-op if parent provides renderPiece)
|
|
64
|
+
const defaultRenderPiece = useCallback((code, size) => /*#__PURE__*/_jsx(View, {
|
|
65
|
+
style: {
|
|
66
|
+
width: size,
|
|
67
|
+
height: size,
|
|
68
|
+
backgroundColor: 'rgba(0,0,0,0.3)'
|
|
69
|
+
}
|
|
70
|
+
}), []);
|
|
71
|
+
const pieceRenderer = renderPiece ?? defaultRenderPiece;
|
|
72
|
+
|
|
73
|
+
// --- Gesture callbacks ---
|
|
74
|
+
const handlePieceSelected = useCallback(square => {
|
|
75
|
+
setSelectedSquare(square);
|
|
76
|
+
if (showLegalMoves) {
|
|
77
|
+
setLegalMoves(boardState.getLegalMoves(square));
|
|
78
|
+
}
|
|
79
|
+
}, [showLegalMoves, boardState]);
|
|
80
|
+
const handleSelectionCleared = useCallback(() => {
|
|
81
|
+
setSelectedSquare(null);
|
|
82
|
+
setLegalMoves([]);
|
|
83
|
+
}, []);
|
|
84
|
+
const handlePieceMoved = useCallback((from, to) => {
|
|
85
|
+
// Clear selection and legal dots
|
|
86
|
+
setSelectedSquare(null);
|
|
87
|
+
setLegalMoves([]);
|
|
88
|
+
|
|
89
|
+
// Notify parent — parent decides whether to accept/reject
|
|
90
|
+
onMove?.({
|
|
91
|
+
from,
|
|
92
|
+
to
|
|
93
|
+
});
|
|
94
|
+
}, [onMove]);
|
|
95
|
+
|
|
96
|
+
// --- Single centralized gesture ---
|
|
97
|
+
const {
|
|
98
|
+
gesture,
|
|
99
|
+
gestureState
|
|
100
|
+
} = useBoardGesture({
|
|
101
|
+
squareSize,
|
|
102
|
+
orientation,
|
|
103
|
+
gestureEnabled,
|
|
104
|
+
player,
|
|
105
|
+
moveMethod,
|
|
106
|
+
pieces,
|
|
107
|
+
callbacks: {
|
|
108
|
+
onPieceSelected: handlePieceSelected,
|
|
109
|
+
onPieceMoved: handlePieceMoved,
|
|
110
|
+
onSelectionCleared: handleSelectionCleared
|
|
111
|
+
},
|
|
112
|
+
selectedSquare,
|
|
113
|
+
legalMoves
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// --- Imperative ref for parent (move, highlight, reset, undo) ---
|
|
117
|
+
useImperativeHandle(ref, () => ({
|
|
118
|
+
move: move => {
|
|
119
|
+
// Pre-apply to internal chess.js so subsequent getLegalMoves calls
|
|
120
|
+
// reflect the new position. The parent will also update the FEN prop,
|
|
121
|
+
// which triggers useBoardPieces -> piece position animates via shared values.
|
|
122
|
+
boardState.applyMove(move.from, move.to);
|
|
123
|
+
},
|
|
124
|
+
highlight: (_square, _color) => {
|
|
125
|
+
// Highlights are handled by overlay layers in the consuming app,
|
|
126
|
+
// not internally — this is a no-op stub for API compatibility.
|
|
127
|
+
// Use the Board's overlay API or render your own highlight layer.
|
|
128
|
+
},
|
|
129
|
+
clearHighlights: () => {
|
|
130
|
+
// Same as highlight — handled by overlay layer
|
|
131
|
+
},
|
|
132
|
+
resetBoard: newFen => {
|
|
133
|
+
boardState.loadFen(newFen);
|
|
134
|
+
setSelectedSquare(null);
|
|
135
|
+
setLegalMoves([]);
|
|
136
|
+
},
|
|
137
|
+
undo: () => {
|
|
138
|
+
boardState.undoMove();
|
|
139
|
+
setSelectedSquare(null);
|
|
140
|
+
setLegalMoves([]);
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
return /*#__PURE__*/_jsx(GestureDetector, {
|
|
144
|
+
gesture: gesture,
|
|
145
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
146
|
+
style: {
|
|
147
|
+
width: boardSize,
|
|
148
|
+
height: boardSize
|
|
149
|
+
},
|
|
150
|
+
children: [/*#__PURE__*/_jsx(BoardBackground, {
|
|
151
|
+
boardSize: boardSize,
|
|
152
|
+
lightColor: colors.light,
|
|
153
|
+
darkColor: colors.dark
|
|
154
|
+
}), /*#__PURE__*/_jsx(BoardCoordinates, {
|
|
155
|
+
boardSize: boardSize,
|
|
156
|
+
orientation: orientation,
|
|
157
|
+
lightColor: colors.light,
|
|
158
|
+
darkColor: colors.dark,
|
|
159
|
+
withLetters: withLetters,
|
|
160
|
+
withNumbers: withNumbers
|
|
161
|
+
}), showLegalMoves && /*#__PURE__*/_jsx(BoardLegalDots, {
|
|
162
|
+
legalMoves: legalMoves,
|
|
163
|
+
squareSize: squareSize,
|
|
164
|
+
orientation: orientation
|
|
165
|
+
}), /*#__PURE__*/_jsx(BoardPiecesLayer, {
|
|
166
|
+
pieces: pieces,
|
|
167
|
+
squareSize: squareSize,
|
|
168
|
+
orientation: orientation,
|
|
169
|
+
moveDuration: moveDuration,
|
|
170
|
+
renderPiece: pieceRenderer,
|
|
171
|
+
activeSquare: gestureState.activeSquare,
|
|
172
|
+
isDragging: gestureState.isDragging
|
|
173
|
+
}), /*#__PURE__*/_jsx(BoardDragGhost, {
|
|
174
|
+
squareSize: squareSize,
|
|
175
|
+
isDragging: gestureState.isDragging,
|
|
176
|
+
dragX: gestureState.dragX,
|
|
177
|
+
dragY: gestureState.dragY,
|
|
178
|
+
dragPieceCode: gestureState.dragPieceCode,
|
|
179
|
+
renderPiece: pieceRenderer
|
|
180
|
+
})]
|
|
181
|
+
})
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","forwardRef","useState","useCallback","useImperativeHandle","useEffect","View","GestureDetector","useBoardPieces","useBoardState","useBoardGesture","BoardBackground","BoardCoordinates","BoardLegalDots","BoardPiecesLayer","BoardDragGhost","jsx","_jsx","jsxs","_jsxs","Board","fen","orientation","boardSize","gestureEnabled","player","onMove","colors","moveDuration","withLetters","withNumbers","renderPiece","showLegalMoves","moveMethod","ref","squareSize","pieces","boardState","loadFen","selectedSquare","setSelectedSquare","legalMoves","setLegalMoves","defaultRenderPiece","code","size","style","width","height","backgroundColor","pieceRenderer","handlePieceSelected","square","getLegalMoves","handleSelectionCleared","handlePieceMoved","from","to","gesture","gestureState","callbacks","onPieceSelected","onPieceMoved","onSelectionCleared","move","applyMove","highlight","_square","_color","clearHighlights","resetBoard","newFen","undo","undoMove","children","lightColor","light","darkColor","dark","activeSquare","isDragging","dragX","dragY","dragPieceCode"],"sourceRoot":"..\\..\\src","sources":["board.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// react-native-chess-kit
|
|
4
|
+
// High-performance chess board for React Native
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Main component
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export { Board } from './board';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Utility functions (useful for overlay positioning)
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export { squareToXY, xyToSquare } from './use-board-pieces';
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Board","squareToXY","xyToSquare"],"sourceRoot":"..\\..\\src","sources":["index.ts"],"mappings":";;AAAA;AACA;;AAEA;AACA;AACA;;AAEA,SAASA,KAAK,QAAQ,SAAS;;AAE/B;AACA;AACA;;AAuBA;AACA;AACA;;AAEA,SAASC,UAAU,EAAEC,UAAU,QAAQ,oBAAoB","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"..\\..\\src","sources":["types.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useMemo, useCallback, useRef } from 'react';
|
|
4
|
+
import { Gesture } from 'react-native-gesture-handler';
|
|
5
|
+
import { useSharedValue, runOnJS } from 'react-native-reanimated';
|
|
6
|
+
import { xyToSquare } from './use-board-pieces';
|
|
7
|
+
/**
|
|
8
|
+
* Single centralized gesture handler for the entire board.
|
|
9
|
+
*
|
|
10
|
+
* Instead of 32 separate Gesture.Pan() handlers (one per piece), we use ONE
|
|
11
|
+
* handler on the board container. Touch -> coordinate math -> which piece.
|
|
12
|
+
*
|
|
13
|
+
* Supports three modes:
|
|
14
|
+
* - 'drag': drag piece to target square
|
|
15
|
+
* - 'click': tap source piece, then tap target square
|
|
16
|
+
* - 'both': drag or click (default)
|
|
17
|
+
*
|
|
18
|
+
* All drag position tracking uses shared values — zero JS bridge calls,
|
|
19
|
+
* zero re-renders during drag. Only the final move triggers JS via runOnJS.
|
|
20
|
+
*
|
|
21
|
+
* The gesture object is STABLE (only recreated when squareSize, orientation,
|
|
22
|
+
* gestureEnabled, player, or moveMethod change). Frequently-changing data
|
|
23
|
+
* (pieces, selectedSquare, legalMoves) is read from refs via runOnJS bridge
|
|
24
|
+
* functions, avoiding costly gesture teardown/rebuild on every move.
|
|
25
|
+
*/
|
|
26
|
+
export function useBoardGesture({
|
|
27
|
+
squareSize,
|
|
28
|
+
orientation,
|
|
29
|
+
gestureEnabled,
|
|
30
|
+
player,
|
|
31
|
+
moveMethod,
|
|
32
|
+
pieces,
|
|
33
|
+
callbacks,
|
|
34
|
+
selectedSquare,
|
|
35
|
+
legalMoves
|
|
36
|
+
}) {
|
|
37
|
+
// Shared values for drag tracking — updated on UI thread only
|
|
38
|
+
const activeSquare = useSharedValue(null);
|
|
39
|
+
const dragX = useSharedValue(0);
|
|
40
|
+
const dragY = useSharedValue(0);
|
|
41
|
+
const isDragging = useSharedValue(false);
|
|
42
|
+
const dragPieceCode = useSharedValue(null);
|
|
43
|
+
const gestureState = {
|
|
44
|
+
activeSquare,
|
|
45
|
+
dragX,
|
|
46
|
+
dragY,
|
|
47
|
+
isDragging,
|
|
48
|
+
dragPieceCode
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// --- Refs for frequently-changing data (read from JS thread via runOnJS) ---
|
|
52
|
+
// These update every move but do NOT cause gesture object recreation.
|
|
53
|
+
const piecesRef = useRef(pieces);
|
|
54
|
+
piecesRef.current = pieces;
|
|
55
|
+
const selectedSquareRef = useRef(selectedSquare);
|
|
56
|
+
selectedSquareRef.current = selectedSquare;
|
|
57
|
+
const legalMovesRef = useRef(legalMoves);
|
|
58
|
+
legalMovesRef.current = legalMoves;
|
|
59
|
+
const callbacksRef = useRef(callbacks);
|
|
60
|
+
callbacksRef.current = callbacks;
|
|
61
|
+
|
|
62
|
+
// --- JS-thread bridge functions called from worklets via runOnJS ---
|
|
63
|
+
// These read current values from refs, so they always have fresh data.
|
|
64
|
+
|
|
65
|
+
const handleBegin = useCallback((touchX, touchY) => {
|
|
66
|
+
const square = xyToSquare(touchX, touchY, squareSize, orientation);
|
|
67
|
+
const currentPieces = piecesRef.current;
|
|
68
|
+
const currentSelected = selectedSquareRef.current;
|
|
69
|
+
const currentLegalMoves = legalMovesRef.current;
|
|
70
|
+
const cbs = callbacksRef.current;
|
|
71
|
+
const canClick = moveMethod !== 'drag';
|
|
72
|
+
|
|
73
|
+
// Build lookup for the current touch
|
|
74
|
+
const piece = currentPieces.find(p => p.square === square);
|
|
75
|
+
const isPlayerPiece = piece ? player === 'both' || (piece.color === 'w' ? 'white' : 'black') === player : false;
|
|
76
|
+
|
|
77
|
+
// Click-to-move: second tap on a legal target square
|
|
78
|
+
const legalSquares = new Set(currentLegalMoves.map(m => m.square));
|
|
79
|
+
if (canClick && currentSelected && legalSquares.has(square)) {
|
|
80
|
+
cbs.onPieceMoved(currentSelected, square);
|
|
81
|
+
activeSquare.value = null;
|
|
82
|
+
isDragging.value = false;
|
|
83
|
+
dragPieceCode.value = null;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (isPlayerPiece && piece) {
|
|
87
|
+
// Tapped/started dragging a player piece
|
|
88
|
+
activeSquare.value = square;
|
|
89
|
+
dragX.value = touchX;
|
|
90
|
+
dragY.value = touchY;
|
|
91
|
+
dragPieceCode.value = piece.code;
|
|
92
|
+
cbs.onPieceSelected(square);
|
|
93
|
+
} else {
|
|
94
|
+
// Tapped empty square or opponent piece — clear selection
|
|
95
|
+
activeSquare.value = null;
|
|
96
|
+
dragPieceCode.value = null;
|
|
97
|
+
if (currentSelected) {
|
|
98
|
+
cbs.onSelectionCleared();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, [squareSize, orientation, player, moveMethod, activeSquare, dragX, dragY, isDragging, dragPieceCode]);
|
|
102
|
+
const handleEnd = useCallback((touchX, touchY) => {
|
|
103
|
+
const fromSquare = activeSquare.value;
|
|
104
|
+
if (!fromSquare) return;
|
|
105
|
+
const toSquare = xyToSquare(touchX, touchY, squareSize, orientation);
|
|
106
|
+
isDragging.value = false;
|
|
107
|
+
if (fromSquare !== toSquare) {
|
|
108
|
+
callbacksRef.current.onPieceMoved(fromSquare, toSquare);
|
|
109
|
+
}
|
|
110
|
+
activeSquare.value = null;
|
|
111
|
+
dragPieceCode.value = null;
|
|
112
|
+
}, [squareSize, orientation, activeSquare, isDragging, dragPieceCode]);
|
|
113
|
+
|
|
114
|
+
// --- Build the gesture (STABLE — only changes on layout/config changes) ---
|
|
115
|
+
const canDrag = moveMethod !== 'click';
|
|
116
|
+
const gesture = useMemo(() => {
|
|
117
|
+
return Gesture.Pan().enabled(gestureEnabled).minDistance(0) // Also detect taps (zero-distance pans)
|
|
118
|
+
.onBegin(e => {
|
|
119
|
+
'worklet';
|
|
120
|
+
|
|
121
|
+
// Bridge to JS for piece lookup + selection logic
|
|
122
|
+
runOnJS(handleBegin)(e.x, e.y);
|
|
123
|
+
}).onStart(() => {
|
|
124
|
+
'worklet';
|
|
125
|
+
|
|
126
|
+
if (!canDrag || !activeSquare.value) return;
|
|
127
|
+
isDragging.value = true;
|
|
128
|
+
}).onUpdate(e => {
|
|
129
|
+
'worklet';
|
|
130
|
+
|
|
131
|
+
if (!canDrag || !isDragging.value) return;
|
|
132
|
+
// Only 2 shared value writes — no JS bridge, no re-renders
|
|
133
|
+
dragX.value = e.x;
|
|
134
|
+
dragY.value = e.y;
|
|
135
|
+
}).onEnd(e => {
|
|
136
|
+
'worklet';
|
|
137
|
+
|
|
138
|
+
if (!isDragging.value || !activeSquare.value) return;
|
|
139
|
+
runOnJS(handleEnd)(e.x, e.y);
|
|
140
|
+
}).onFinalize(() => {
|
|
141
|
+
'worklet';
|
|
142
|
+
|
|
143
|
+
// Safety reset if gesture was interrupted
|
|
144
|
+
isDragging.value = false;
|
|
145
|
+
});
|
|
146
|
+
}, [gestureEnabled, canDrag, handleBegin, handleEnd,
|
|
147
|
+
// Shared values are stable refs — listed for exhaustive-deps but don't cause recreations
|
|
148
|
+
activeSquare, dragX, dragY, isDragging]);
|
|
149
|
+
return {
|
|
150
|
+
gesture,
|
|
151
|
+
gestureState
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=use-board-gesture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useMemo","useCallback","useRef","Gesture","useSharedValue","runOnJS","xyToSquare","useBoardGesture","squareSize","orientation","gestureEnabled","player","moveMethod","pieces","callbacks","selectedSquare","legalMoves","activeSquare","dragX","dragY","isDragging","dragPieceCode","gestureState","piecesRef","current","selectedSquareRef","legalMovesRef","callbacksRef","handleBegin","touchX","touchY","square","currentPieces","currentSelected","currentLegalMoves","cbs","canClick","piece","find","p","isPlayerPiece","color","legalSquares","Set","map","m","has","onPieceMoved","value","code","onPieceSelected","onSelectionCleared","handleEnd","fromSquare","toSquare","canDrag","gesture","Pan","enabled","minDistance","onBegin","e","x","y","onStart","onUpdate","onEnd","onFinalize"],"sourceRoot":"..\\..\\src","sources":["use-board-gesture.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
|
+
const FEN_PIECE_MAP = {
|
|
5
|
+
p: {
|
|
6
|
+
code: 'bp',
|
|
7
|
+
color: 'b'
|
|
8
|
+
},
|
|
9
|
+
r: {
|
|
10
|
+
code: 'br',
|
|
11
|
+
color: 'b'
|
|
12
|
+
},
|
|
13
|
+
n: {
|
|
14
|
+
code: 'bn',
|
|
15
|
+
color: 'b'
|
|
16
|
+
},
|
|
17
|
+
b: {
|
|
18
|
+
code: 'bb',
|
|
19
|
+
color: 'b'
|
|
20
|
+
},
|
|
21
|
+
q: {
|
|
22
|
+
code: 'bq',
|
|
23
|
+
color: 'b'
|
|
24
|
+
},
|
|
25
|
+
k: {
|
|
26
|
+
code: 'bk',
|
|
27
|
+
color: 'b'
|
|
28
|
+
},
|
|
29
|
+
P: {
|
|
30
|
+
code: 'wp',
|
|
31
|
+
color: 'w'
|
|
32
|
+
},
|
|
33
|
+
R: {
|
|
34
|
+
code: 'wr',
|
|
35
|
+
color: 'w'
|
|
36
|
+
},
|
|
37
|
+
N: {
|
|
38
|
+
code: 'wn',
|
|
39
|
+
color: 'w'
|
|
40
|
+
},
|
|
41
|
+
B: {
|
|
42
|
+
code: 'wb',
|
|
43
|
+
color: 'w'
|
|
44
|
+
},
|
|
45
|
+
Q: {
|
|
46
|
+
code: 'wq',
|
|
47
|
+
color: 'w'
|
|
48
|
+
},
|
|
49
|
+
K: {
|
|
50
|
+
code: 'wk',
|
|
51
|
+
color: 'w'
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const FILES = 'abcdefgh';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parse the piece-placement part of a FEN string into an array of pieces.
|
|
58
|
+
* Pure function — no React dependencies, suitable for worklets if needed.
|
|
59
|
+
*/
|
|
60
|
+
function parseFenPieces(fen) {
|
|
61
|
+
const placement = fen.split(' ')[0];
|
|
62
|
+
const ranks = placement.split('/');
|
|
63
|
+
const pieces = [];
|
|
64
|
+
for (let rankIdx = 0; rankIdx < ranks.length; rankIdx++) {
|
|
65
|
+
const rank = ranks[rankIdx];
|
|
66
|
+
let fileIdx = 0;
|
|
67
|
+
for (const char of rank) {
|
|
68
|
+
if (char >= '1' && char <= '8') {
|
|
69
|
+
fileIdx += parseInt(char, 10);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const mapping = FEN_PIECE_MAP[char];
|
|
73
|
+
if (mapping) {
|
|
74
|
+
// FEN ranks are from rank 8 (index 0) down to rank 1 (index 7)
|
|
75
|
+
const square = `${FILES[fileIdx]}${8 - rankIdx}`;
|
|
76
|
+
pieces.push({
|
|
77
|
+
code: mapping.code,
|
|
78
|
+
square,
|
|
79
|
+
color: mapping.color
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
fileIdx++;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return pieces;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Convert square notation to pixel coordinates (top-left corner).
|
|
90
|
+
* Orientation-aware: flips the board when playing as black.
|
|
91
|
+
*/
|
|
92
|
+
export function squareToXY(square, squareSize, orientation) {
|
|
93
|
+
'worklet';
|
|
94
|
+
|
|
95
|
+
const fileIdx = square.charCodeAt(0) - 97; // 'a'=0 .. 'h'=7
|
|
96
|
+
const rankIdx = parseInt(square[1], 10) - 1; // '1'=0 .. '8'=7
|
|
97
|
+
|
|
98
|
+
const col = orientation === 'white' ? fileIdx : 7 - fileIdx;
|
|
99
|
+
const row = orientation === 'white' ? 7 - rankIdx : rankIdx;
|
|
100
|
+
return {
|
|
101
|
+
x: col * squareSize,
|
|
102
|
+
y: row * squareSize
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Convert pixel coordinates to a square notation string.
|
|
108
|
+
* Clamps to board bounds. Orientation-aware.
|
|
109
|
+
*/
|
|
110
|
+
export function xyToSquare(x, y, squareSize, orientation) {
|
|
111
|
+
'worklet';
|
|
112
|
+
|
|
113
|
+
const col = Math.max(0, Math.min(7, Math.floor(x / squareSize)));
|
|
114
|
+
const row = Math.max(0, Math.min(7, Math.floor(y / squareSize)));
|
|
115
|
+
const fileIdx = orientation === 'white' ? col : 7 - col;
|
|
116
|
+
const rankIdx = orientation === 'white' ? 7 - row : row;
|
|
117
|
+
|
|
118
|
+
// String.fromCharCode not available in worklets — use lookup
|
|
119
|
+
const files = 'abcdefgh';
|
|
120
|
+
return `${files[fileIdx]}${rankIdx + 1}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Manages the piece list derived from FEN, with stable IDs for React keys.
|
|
125
|
+
*
|
|
126
|
+
* Stable IDs prevent unmount/remount cycles when pieces change position.
|
|
127
|
+
* A piece keeps its ID as long as it exists on the board — only capture
|
|
128
|
+
* (removal) or promotion (code change) creates a new ID.
|
|
129
|
+
*/
|
|
130
|
+
export function useBoardPieces(fen) {
|
|
131
|
+
// Track piece-code counters across renders for stable ID assignment
|
|
132
|
+
const idCounterRef = useRef({});
|
|
133
|
+
const prevPiecesRef = useRef([]);
|
|
134
|
+
return useMemo(() => {
|
|
135
|
+
const parsed = parseFenPieces(fen);
|
|
136
|
+
const prev = prevPiecesRef.current;
|
|
137
|
+
const prevBySquare = new Map(prev.map(p => [p.square, p]));
|
|
138
|
+
|
|
139
|
+
// Try to reuse IDs from previous render:
|
|
140
|
+
// 1. Same code on same square -> keep ID (piece didn't move)
|
|
141
|
+
// 2. Same code moved to a new square -> find unmatched previous piece of same code
|
|
142
|
+
const usedPrevIds = new Set();
|
|
143
|
+
const result = [];
|
|
144
|
+
|
|
145
|
+
// First pass: exact square matches (piece stayed or appeared on same square)
|
|
146
|
+
const unmatched = [];
|
|
147
|
+
for (const p of parsed) {
|
|
148
|
+
const existing = prevBySquare.get(p.square);
|
|
149
|
+
if (existing && existing.code === p.code && !usedPrevIds.has(existing.id)) {
|
|
150
|
+
usedPrevIds.add(existing.id);
|
|
151
|
+
result.push({
|
|
152
|
+
...p,
|
|
153
|
+
id: existing.id
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
unmatched.push(p);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Second pass: match unmatched pieces by code to previous pieces that moved
|
|
161
|
+
for (const p of unmatched) {
|
|
162
|
+
let matchedId = null;
|
|
163
|
+
for (const prevPiece of prev) {
|
|
164
|
+
if (prevPiece.code === p.code && !usedPrevIds.has(prevPiece.id)) {
|
|
165
|
+
matchedId = prevPiece.id;
|
|
166
|
+
usedPrevIds.add(prevPiece.id);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (matchedId) {
|
|
171
|
+
result.push({
|
|
172
|
+
...p,
|
|
173
|
+
id: matchedId
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
// New piece (promotion, or first render) — assign fresh ID
|
|
177
|
+
const counter = idCounterRef.current;
|
|
178
|
+
counter[p.code] = (counter[p.code] ?? 0) + 1;
|
|
179
|
+
result.push({
|
|
180
|
+
...p,
|
|
181
|
+
id: `${p.code}-${counter[p.code]}`
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
prevPiecesRef.current = result;
|
|
186
|
+
return result;
|
|
187
|
+
}, [fen]);
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=use-board-pieces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useMemo","useRef","FEN_PIECE_MAP","p","code","color","r","n","b","q","k","P","R","N","B","Q","K","FILES","parseFenPieces","fen","placement","split","ranks","pieces","rankIdx","length","rank","fileIdx","char","parseInt","mapping","square","push","squareToXY","squareSize","orientation","charCodeAt","col","row","x","y","xyToSquare","Math","max","min","floor","files","useBoardPieces","idCounterRef","prevPiecesRef","parsed","prev","current","prevBySquare","Map","map","usedPrevIds","Set","result","unmatched","existing","get","has","id","add","matchedId","prevPiece","counter"],"sourceRoot":"..\\..\\src","sources":["use-board-pieces.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useRef, useCallback } from 'react';
|
|
4
|
+
import { Chess } from 'chess.js';
|
|
5
|
+
/**
|
|
6
|
+
* Manages the internal chess.js instance for legal move validation.
|
|
7
|
+
*
|
|
8
|
+
* This mirrors the visual board state. When the parent passes a new FEN,
|
|
9
|
+
* the internal chess.js is synced. Legal move queries and move application
|
|
10
|
+
* happen against this instance.
|
|
11
|
+
*
|
|
12
|
+
* The chess.js instance lives in a ref — no React state, no re-renders.
|
|
13
|
+
*/
|
|
14
|
+
export function useBoardState(initialFen) {
|
|
15
|
+
const chessRef = useRef(null);
|
|
16
|
+
if (!chessRef.current) chessRef.current = new Chess(initialFen);
|
|
17
|
+
const getLegalMoves = useCallback(square => {
|
|
18
|
+
try {
|
|
19
|
+
const moves = chessRef.current.moves({
|
|
20
|
+
square: square,
|
|
21
|
+
verbose: true
|
|
22
|
+
});
|
|
23
|
+
return moves.map(m => ({
|
|
24
|
+
square: m.to,
|
|
25
|
+
isCapture: m.captured !== undefined
|
|
26
|
+
}));
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}, []);
|
|
31
|
+
const isPlayerPiece = useCallback((square, pieces, player) => {
|
|
32
|
+
const piece = pieces.find(p => p.square === square);
|
|
33
|
+
if (!piece) return false;
|
|
34
|
+
if (player === 'both') return true;
|
|
35
|
+
const pieceColor = piece.color === 'w' ? 'white' : 'black';
|
|
36
|
+
return pieceColor === player;
|
|
37
|
+
}, []);
|
|
38
|
+
const applyMove = useCallback((from, to, promotion) => {
|
|
39
|
+
try {
|
|
40
|
+
chessRef.current.move({
|
|
41
|
+
from: from,
|
|
42
|
+
to: to,
|
|
43
|
+
promotion: promotion
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
applied: true,
|
|
47
|
+
fen: chessRef.current.fen()
|
|
48
|
+
};
|
|
49
|
+
} catch {
|
|
50
|
+
return {
|
|
51
|
+
applied: false
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}, []);
|
|
55
|
+
const undoMove = useCallback(() => {
|
|
56
|
+
const result = chessRef.current.undo();
|
|
57
|
+
return result ? chessRef.current.fen() : null;
|
|
58
|
+
}, []);
|
|
59
|
+
const loadFen = useCallback(fen => {
|
|
60
|
+
chessRef.current.load(fen);
|
|
61
|
+
}, []);
|
|
62
|
+
const getFen = useCallback(() => chessRef.current.fen(), []);
|
|
63
|
+
const getTurn = useCallback(() => chessRef.current.turn(), []);
|
|
64
|
+
return {
|
|
65
|
+
getLegalMoves,
|
|
66
|
+
isPlayerPiece,
|
|
67
|
+
applyMove,
|
|
68
|
+
undoMove,
|
|
69
|
+
loadFen,
|
|
70
|
+
getFen,
|
|
71
|
+
getTurn
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=use-board-state.js.map
|