@dxos/react-ui-gameboard 0.7.5-labs.e27f9b9 → 0.7.5-main.e9bb01b
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/dist/lib/browser/index.mjs +44 -147
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +43 -147
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +44 -147
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/Board/Board.d.ts.map +1 -1
- package/dist/types/src/Board/Piece.d.ts.map +1 -1
- package/dist/types/src/Board/context.d.ts +7 -7
- package/dist/types/src/Board/context.d.ts.map +1 -1
- package/dist/types/src/Board/index.d.ts +0 -1
- package/dist/types/src/Board/index.d.ts.map +1 -1
- package/dist/types/src/Board/types.d.ts +2 -6
- package/dist/types/src/Board/types.d.ts.map +1 -1
- package/dist/types/src/Chessboard/Chessboard.d.ts.map +1 -1
- package/dist/types/src/Chessboard/Chessboard.stories.d.ts +0 -2
- package/dist/types/src/Chessboard/Chessboard.stories.d.ts.map +1 -1
- package/dist/types/src/Chessboard/chess.d.ts +9 -5
- package/dist/types/src/Chessboard/chess.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/Board/Board.tsx +6 -24
- package/src/Board/Piece.tsx +9 -10
- package/src/Board/Square.tsx +1 -1
- package/src/Board/context.ts +2 -9
- package/src/Board/index.ts +0 -1
- package/src/Board/types.ts +2 -6
- package/src/Chessboard/Chessboard.stories.tsx +3 -45
- package/src/Chessboard/Chessboard.tsx +9 -76
- package/src/Chessboard/chess.ts +10 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-gameboard",
|
|
3
|
-
"version": "0.7.5-
|
|
3
|
+
"version": "0.7.5-main.e9bb01b",
|
|
4
4
|
"description": "Game board.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"@preact/signals-core": "^1.6.0",
|
|
25
25
|
"chess.js": "^1.0.0",
|
|
26
26
|
"react-resize-detector": "^11.0.1",
|
|
27
|
-
"@dxos/
|
|
28
|
-
"@dxos/
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
27
|
+
"@dxos/invariant": "0.7.5-main.e9bb01b",
|
|
28
|
+
"@dxos/log": "0.7.5-main.e9bb01b",
|
|
29
|
+
"@dxos/util": "0.7.5-main.e9bb01b",
|
|
30
|
+
"@dxos/node-std": "0.7.5-main.e9bb01b",
|
|
31
|
+
"@dxos/debug": "0.7.5-main.e9bb01b"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@svgr/cli": "^8.1.0",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"react": "~18.2.0",
|
|
40
40
|
"react-dom": "~18.2.0",
|
|
41
41
|
"vite": "5.4.7",
|
|
42
|
-
"@dxos/react-ui": "0.7.5-
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
42
|
+
"@dxos/react-ui": "0.7.5-main.e9bb01b",
|
|
43
|
+
"@dxos/storybook-utils": "0.7.5-main.e9bb01b",
|
|
44
|
+
"@dxos/react-ui-theme": "0.7.5-main.e9bb01b"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"react": "~18.2.0",
|
|
48
48
|
"react-dom": "~18.2.0",
|
|
49
|
-
"@dxos/react-ui": "0.7.5-
|
|
50
|
-
"@dxos/react-ui-theme": "0.7.5-
|
|
49
|
+
"@dxos/react-ui": "0.7.5-main.e9bb01b",
|
|
50
|
+
"@dxos/react-ui-theme": "0.7.5-main.e9bb01b"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
package/src/Board/Board.tsx
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
6
|
-
import React, { type PropsWithChildren,
|
|
6
|
+
import React, { type PropsWithChildren, useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
9
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
10
|
import { mx } from '@dxos/react-ui-theme';
|
|
11
11
|
|
|
12
12
|
import { Container } from './Container';
|
|
13
|
-
import { BoardContext
|
|
14
|
-
import { type BoardModel, isLocation, isPiece, type Move
|
|
13
|
+
import { BoardContext } from './context';
|
|
14
|
+
import { type BoardModel, isLocation, isPiece, type Move } from './types';
|
|
15
15
|
|
|
16
16
|
type RootProps = ThemedClassName<
|
|
17
17
|
PropsWithChildren<{
|
|
@@ -25,20 +25,8 @@ type RootProps = ThemedClassName<
|
|
|
25
25
|
*/
|
|
26
26
|
const Root = ({ children, classNames, model, onDrop }: RootProps) => {
|
|
27
27
|
const [dragging, setDragging] = useState(false);
|
|
28
|
-
const [promoting, setPromoting] = useState<PieceRecord | undefined>();
|
|
29
|
-
|
|
30
|
-
// Handle promotion.
|
|
31
|
-
const onPromotion = useCallback<BoardContextType['onPromotion']>((move) => {
|
|
32
|
-
log('onPromotion', { move });
|
|
33
|
-
setPromoting(undefined);
|
|
34
|
-
onDrop?.(move);
|
|
35
|
-
}, []);
|
|
36
28
|
|
|
37
29
|
useEffect(() => {
|
|
38
|
-
if (!model) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
30
|
// TODO(burdon): Should target specific container.
|
|
43
31
|
return monitorForElements({
|
|
44
32
|
onDragStart: ({ source }) => {
|
|
@@ -58,20 +46,14 @@ const Root = ({ children, classNames, model, onDrop }: RootProps) => {
|
|
|
58
46
|
return;
|
|
59
47
|
}
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
if (model.canPromote?.(move)) {
|
|
63
|
-
setPromoting({ ...piece, location: targetLocation });
|
|
64
|
-
} else {
|
|
65
|
-
onDrop?.(move);
|
|
66
|
-
}
|
|
67
|
-
|
|
49
|
+
onDrop?.({ source: piece.location, target: targetLocation, piece: piece.type });
|
|
68
50
|
setDragging(false);
|
|
69
51
|
},
|
|
70
52
|
});
|
|
71
|
-
}, [
|
|
53
|
+
}, []);
|
|
72
54
|
|
|
73
55
|
return (
|
|
74
|
-
<BoardContext.Provider value={{ model, dragging
|
|
56
|
+
<BoardContext.Provider value={{ model, dragging }}>
|
|
75
57
|
<Container classNames={mx('aspect-square', classNames)}>{children}</Container>
|
|
76
58
|
</BoardContext.Provider>
|
|
77
59
|
);
|
package/src/Board/Piece.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import { createPortal } from 'react-dom';
|
|
|
11
11
|
|
|
12
12
|
import { invariant } from '@dxos/invariant';
|
|
13
13
|
import { log } from '@dxos/log';
|
|
14
|
-
import {
|
|
14
|
+
import { useTrackProps, type ThemedClassName } from '@dxos/react-ui';
|
|
15
15
|
import { mx } from '@dxos/react-ui-theme';
|
|
16
16
|
|
|
17
17
|
import { useBoardContext } from './context';
|
|
@@ -30,8 +30,7 @@ export const Piece = memo(({ classNames, piece, orientation, bounds, label, Comp
|
|
|
30
30
|
useTrackProps({ classNames, piece, orientation, bounds, label, Component }, Piece.displayName, false);
|
|
31
31
|
const { model } = useBoardContext();
|
|
32
32
|
|
|
33
|
-
const { dragging: isDragging
|
|
34
|
-
const promotingRef = useDynamicRef(promoting);
|
|
33
|
+
const { dragging: isDragging } = useBoardContext();
|
|
35
34
|
const [dragging, setDragging] = useState(false);
|
|
36
35
|
const [preview, setPreview] = useState<HTMLElement>();
|
|
37
36
|
|
|
@@ -66,20 +65,20 @@ export const Piece = memo(({ classNames, piece, orientation, bounds, label, Comp
|
|
|
66
65
|
nativeSetDragImage,
|
|
67
66
|
});
|
|
68
67
|
},
|
|
69
|
-
canDrag: () =>
|
|
68
|
+
canDrag: () => model?.turn === piece.side,
|
|
70
69
|
onDragStart: () => setDragging(true),
|
|
71
|
-
onDrop: ({ location
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
onDrop: ({ location }) => {
|
|
71
|
+
const drop = location.current.dropTargets[0].data;
|
|
72
|
+
const loc = drop.location;
|
|
73
|
+
if (isLocation(loc)) {
|
|
74
|
+
setCurrent((current) => ({ ...current, location: loc }));
|
|
75
75
|
}
|
|
76
|
-
|
|
77
76
|
setDragging(false);
|
|
78
77
|
},
|
|
79
78
|
});
|
|
80
79
|
}, [model, piece]);
|
|
81
80
|
|
|
82
|
-
//
|
|
81
|
+
// Update position independently of render cycle (otherwise animation is interrupted).
|
|
83
82
|
useEffect(() => {
|
|
84
83
|
requestAnimationFrame(() => {
|
|
85
84
|
if (!ref.current || !bounds) {
|
package/src/Board/Square.tsx
CHANGED
|
@@ -45,7 +45,7 @@ export const Square = memo(({ location, bounds, label, classNames }: SquareProps
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
if (model?.isValidMove({
|
|
48
|
+
if (model?.isValidMove({ source: piece.location, target: location, piece: piece.type })) {
|
|
49
49
|
setState('validMove');
|
|
50
50
|
} else {
|
|
51
51
|
setState('invalidMove');
|
package/src/Board/context.ts
CHANGED
|
@@ -6,16 +6,9 @@ import { createContext, useContext } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { raise } from '@dxos/debug';
|
|
8
8
|
|
|
9
|
-
import { type
|
|
9
|
+
import { type BoardModel } from './types';
|
|
10
10
|
|
|
11
|
-
export
|
|
12
|
-
model?: BoardModel;
|
|
13
|
-
dragging?: boolean; // TODO(burdon): Change to PieceRecord.
|
|
14
|
-
promoting?: PieceRecord;
|
|
15
|
-
onPromotion: (move: Move) => void;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const BoardContext = createContext<BoardContextType | undefined>(undefined);
|
|
11
|
+
export const BoardContext = createContext<{ model?: BoardModel; dragging: boolean } | undefined>(undefined);
|
|
19
12
|
|
|
20
13
|
export const useBoardContext = () => {
|
|
21
14
|
return useContext(BoardContext) ?? raise(new Error('Missing BoardContext'));
|
package/src/Board/index.ts
CHANGED
package/src/Board/types.ts
CHANGED
|
@@ -23,8 +23,8 @@ export type PieceRecord<T extends PieceType = PieceType> = {
|
|
|
23
23
|
export type PieceMap<T extends PieceType = PieceType> = Record<string, PieceRecord<T>>;
|
|
24
24
|
|
|
25
25
|
export type Move = {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
source: Location;
|
|
27
|
+
target: Location;
|
|
28
28
|
piece: PieceType;
|
|
29
29
|
promotion?: PieceType;
|
|
30
30
|
};
|
|
@@ -47,12 +47,8 @@ export const isLocation = (token: unknown): token is Location =>
|
|
|
47
47
|
|
|
48
48
|
export const isEqualLocation = (l1: Location, l2: Location): boolean => l1[0] === l2[0] && l1[1] === l2[1];
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Generic board model.
|
|
52
|
-
*/
|
|
53
50
|
export interface BoardModel<T extends PieceType = PieceType> {
|
|
54
51
|
turn: Player;
|
|
55
52
|
pieces: ReadonlySignal<PieceMap<T>>;
|
|
56
53
|
isValidMove: (move: Move) => boolean;
|
|
57
|
-
canPromote?: (move: Move) => boolean;
|
|
58
54
|
}
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
8
|
-
import React, { useCallback,
|
|
8
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import { log } from '@dxos/log';
|
|
11
10
|
import { Button, Toolbar } from '@dxos/react-ui';
|
|
12
11
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
12
|
|
|
@@ -23,13 +22,7 @@ const Render = ({ fen, orientation: _orientation, ...props }: RenderProps) => {
|
|
|
23
22
|
const model = useMemo(() => new ChessModel(fen), [fen]);
|
|
24
23
|
const [orientation, setOrientation] = useState<Player | undefined>(_orientation);
|
|
25
24
|
|
|
26
|
-
const handleDrop = useCallback<NonNullable<BoardRootProps['onDrop']>>(
|
|
27
|
-
(move: Move) => {
|
|
28
|
-
log.info('handleDrop', { move });
|
|
29
|
-
return model.makeMove(move);
|
|
30
|
-
},
|
|
31
|
-
[model],
|
|
32
|
-
);
|
|
25
|
+
const handleDrop = useCallback<NonNullable<BoardRootProps['onDrop']>>((move: Move) => model.makeMove(move), [model]);
|
|
33
26
|
|
|
34
27
|
return (
|
|
35
28
|
<div className='flex flex-col grow gap-2 overflow-hidden'>
|
|
@@ -50,36 +43,11 @@ const Render = ({ fen, orientation: _orientation, ...props }: RenderProps) => {
|
|
|
50
43
|
);
|
|
51
44
|
};
|
|
52
45
|
|
|
53
|
-
const Grid = (props: RenderProps) => {
|
|
54
|
-
const models = useMemo(() => Array.from({ length: 9 }).map(() => new ChessModel()), []);
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const i = setInterval(() => {
|
|
57
|
-
const model = models[Math.floor(Math.random() * models.length)];
|
|
58
|
-
model.makeRandomMove();
|
|
59
|
-
}, 100);
|
|
60
|
-
return () => clearInterval(i);
|
|
61
|
-
}, []);
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<div className='h-full aspect-square mx-auto'>
|
|
65
|
-
<div className='grid grid-cols-3 gap-2'>
|
|
66
|
-
{models.map((model, i) => (
|
|
67
|
-
<div key={i} className='aspect-square'>
|
|
68
|
-
<Board.Root model={model}>
|
|
69
|
-
<Chessboard />
|
|
70
|
-
</Board.Root>
|
|
71
|
-
</div>
|
|
72
|
-
))}
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
46
|
const meta: Meta<typeof Render> = {
|
|
79
47
|
title: 'ui/react-ui-gameboard/Chessboard',
|
|
80
48
|
component: Chessboard,
|
|
81
49
|
render: Render,
|
|
82
|
-
decorators: [withTheme, withLayout({ fullscreen: true
|
|
50
|
+
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
83
51
|
};
|
|
84
52
|
|
|
85
53
|
export default meta;
|
|
@@ -88,12 +56,6 @@ type Story = StoryObj<typeof Render>;
|
|
|
88
56
|
|
|
89
57
|
export const Default: Story = {};
|
|
90
58
|
|
|
91
|
-
export const Promotion: Story = {
|
|
92
|
-
args: {
|
|
93
|
-
fen: '4k3/7P/8/8/8/8/1p6/4K3 w - - 0 1',
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
|
|
97
59
|
export const Debug: Story = {
|
|
98
60
|
args: {
|
|
99
61
|
debug: true,
|
|
@@ -102,7 +64,3 @@ export const Debug: Story = {
|
|
|
102
64
|
fen: 'q3k1nr/1pp1nQpp/3p4/1P2p3/4P3/B1PP1b2/B5PP/5K2 b k - 0 17',
|
|
103
65
|
},
|
|
104
66
|
};
|
|
105
|
-
|
|
106
|
-
export const Nine: Story = {
|
|
107
|
-
render: Grid,
|
|
108
|
-
};
|
|
@@ -6,10 +6,8 @@ import React, { type PropsWithChildren, useRef, useMemo, useEffect, useState, me
|
|
|
6
6
|
import { useResizeDetector } from 'react-resize-detector';
|
|
7
7
|
|
|
8
8
|
import { useTrackProps } from '@dxos/react-ui';
|
|
9
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
10
|
-
import { isNotFalsy } from '@dxos/util';
|
|
11
9
|
|
|
12
|
-
import {
|
|
10
|
+
import { type ChessPiece, ChessPieces, getSquareColor, locationToPos } from './chess';
|
|
13
11
|
import {
|
|
14
12
|
type DOMRectBounds,
|
|
15
13
|
type Location,
|
|
@@ -36,7 +34,7 @@ export type ChessboardProps = PropsWithChildren<{
|
|
|
36
34
|
export const Chessboard = memo(({ orientation, showLabels, debug, rows = 8, cols = 8 }: ChessboardProps) => {
|
|
37
35
|
useTrackProps({ orientation, showLabels, debug }, Chessboard.displayName, false);
|
|
38
36
|
const { ref: containerRef, width, height } = useResizeDetector({ refreshRate: 200 });
|
|
39
|
-
const { model
|
|
37
|
+
const { model } = useBoardContext();
|
|
40
38
|
|
|
41
39
|
const locations = useMemo<Location[]>(() => {
|
|
42
40
|
return Array.from({ length: rows }, (_, i) => (orientation === 'black' ? i : rows - 1 - i)).flatMap((row) =>
|
|
@@ -75,22 +73,16 @@ export const Chessboard = memo(({ orientation, showLabels, debug, rows = 8, cols
|
|
|
75
73
|
}, [locations, width, height]);
|
|
76
74
|
|
|
77
75
|
// Get the bounds of each square and piece.
|
|
78
|
-
const positions = useMemo<{
|
|
76
|
+
const positions = useMemo<{ bounds: DOMRectBounds; piece: PieceRecord }[]>(() => {
|
|
79
77
|
if (!gridRef.current) {
|
|
80
78
|
return [];
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
return Object.values(model?.pieces.value ?? {})
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const bounds = grid[locationToString(piece.location)];
|
|
90
|
-
return { piece, bounds };
|
|
91
|
-
})
|
|
92
|
-
.filter(isNotFalsy);
|
|
93
|
-
}, [grid, model?.pieces.value, promoting]);
|
|
81
|
+
return Object.values(model?.pieces.value ?? {}).map((piece) => {
|
|
82
|
+
const bounds = grid[locationToString(piece.location)];
|
|
83
|
+
return { bounds, piece };
|
|
84
|
+
});
|
|
85
|
+
}, [grid, model?.pieces.value]);
|
|
94
86
|
|
|
95
87
|
return (
|
|
96
88
|
<div ref={containerRef} className='relative'>
|
|
@@ -108,7 +100,7 @@ export const Chessboard = memo(({ orientation, showLabels, debug, rows = 8, cols
|
|
|
108
100
|
/>
|
|
109
101
|
))}
|
|
110
102
|
</div>
|
|
111
|
-
<div className=
|
|
103
|
+
<div className='grow'>
|
|
112
104
|
{positions.map(({ bounds, piece }) => (
|
|
113
105
|
<Piece
|
|
114
106
|
key={piece.id}
|
|
@@ -120,22 +112,6 @@ export const Chessboard = memo(({ orientation, showLabels, debug, rows = 8, cols
|
|
|
120
112
|
/>
|
|
121
113
|
))}
|
|
122
114
|
</div>
|
|
123
|
-
<div>
|
|
124
|
-
{promoting && (
|
|
125
|
-
<PromotionSelector
|
|
126
|
-
grid={grid}
|
|
127
|
-
piece={promoting}
|
|
128
|
-
onSelect={(piece) => {
|
|
129
|
-
onPromotion({
|
|
130
|
-
from: Object.values(model!.pieces.value).find((p) => p.id === promoting.id)!.location,
|
|
131
|
-
to: piece.location,
|
|
132
|
-
piece: promoting.type,
|
|
133
|
-
promotion: piece.type,
|
|
134
|
-
});
|
|
135
|
-
}}
|
|
136
|
-
/>
|
|
137
|
-
)}
|
|
138
|
-
</div>
|
|
139
115
|
</div>
|
|
140
116
|
);
|
|
141
117
|
});
|
|
@@ -145,46 +121,3 @@ Chessboard.displayName = 'Chessboard';
|
|
|
145
121
|
const getSquareLocation = (container: HTMLElement, location: Location): HTMLElement | null => {
|
|
146
122
|
return container.querySelector(`[data-location="${locationToString(location)}"]`);
|
|
147
123
|
};
|
|
148
|
-
|
|
149
|
-
const PromotionSelector = ({
|
|
150
|
-
grid,
|
|
151
|
-
piece,
|
|
152
|
-
onSelect,
|
|
153
|
-
}: {
|
|
154
|
-
grid: Record<string, DOMRectBounds>;
|
|
155
|
-
piece: PieceRecord;
|
|
156
|
-
onSelect: (piece: PieceRecord) => void;
|
|
157
|
-
}) => {
|
|
158
|
-
const positions = ['Q', 'N', 'R', 'B'].map((pieceType, i) => {
|
|
159
|
-
const location = [piece.location[0] + (piece.location[0] === 0 ? i : -i), piece.location[1]] as Location;
|
|
160
|
-
return {
|
|
161
|
-
piece: {
|
|
162
|
-
id: `promotion-${pieceType}`,
|
|
163
|
-
type: (piece.side === 'black' ? 'B' : 'W') + pieceType,
|
|
164
|
-
side: piece.side,
|
|
165
|
-
location,
|
|
166
|
-
},
|
|
167
|
-
bounds: grid[locationToString(location)],
|
|
168
|
-
};
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const handleSelect = (selected: PieceRecord) => {
|
|
172
|
-
onSelect({ ...piece, type: selected.type });
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// TODO(burdon): Circle.
|
|
176
|
-
return (
|
|
177
|
-
<div>
|
|
178
|
-
{positions.map(({ piece, bounds }) => (
|
|
179
|
-
<div key={piece.id} style={bounds} onClick={() => handleSelect(piece)}>
|
|
180
|
-
<Piece
|
|
181
|
-
piece={piece}
|
|
182
|
-
bounds={bounds}
|
|
183
|
-
Component={ChessPieces[piece.type as ChessPiece]}
|
|
184
|
-
classNames={mx('border-2 border-neutral-700 rounded-full', boardStyles.promotion)}
|
|
185
|
-
/>
|
|
186
|
-
</div>
|
|
187
|
-
))}
|
|
188
|
-
</div>
|
|
189
|
-
);
|
|
190
|
-
};
|
package/src/Chessboard/chess.ts
CHANGED
|
@@ -33,45 +33,30 @@ export const locationToPos = ([row, col]: Location): string => {
|
|
|
33
33
|
return String.fromCharCode(col + 'a'.charCodeAt(0)) + (row + 1);
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const styles = {
|
|
36
|
+
export const styles = {
|
|
37
37
|
neutral: {
|
|
38
|
-
black: 'bg-neutral-50',
|
|
39
38
|
white: 'bg-neutral-200',
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
original: {
|
|
43
|
-
black: 'bg-[#6C95B9]',
|
|
44
|
-
white: 'bg-[#CCD3DB]',
|
|
45
|
-
promotion: 'duration-500 bg-[#CCD3DB] opacity-70 hover:opacity-100',
|
|
39
|
+
black: 'bg-neutral-50',
|
|
46
40
|
},
|
|
47
41
|
blue: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
promotion: 'duration-500 bg-[#CBDCEB] opacity-70 hover:opacity-100',
|
|
51
|
-
},
|
|
52
|
-
green: {
|
|
53
|
-
black: 'bg-[#8EB486]',
|
|
54
|
-
white: 'bg-[#FDF7F4]',
|
|
55
|
-
promotion: 'duration-500 bg-[#FDF7F4] opacity-70 hover:opacity-100',
|
|
42
|
+
white: 'bg-[#ccd3db]',
|
|
43
|
+
black: 'bg-[#6c95b9]',
|
|
56
44
|
},
|
|
57
45
|
};
|
|
58
46
|
|
|
59
|
-
export const boardStyles = styles.original;
|
|
60
|
-
|
|
61
47
|
export const getSquareColor = ([row, col]: Location) => {
|
|
62
|
-
return (col + row) % 2 === 0 ?
|
|
48
|
+
return (col + row) % 2 === 0 ? styles.blue.white : styles.blue.black;
|
|
63
49
|
};
|
|
64
50
|
|
|
65
51
|
/**
|
|
66
52
|
* Attempt move.
|
|
67
53
|
*/
|
|
68
|
-
const makeMove = (game: Chess,
|
|
69
|
-
const
|
|
70
|
-
const
|
|
54
|
+
const makeMove = (game: Chess, { source, target }: Move): Chess | null => {
|
|
55
|
+
const s = locationToPos(source);
|
|
56
|
+
const t = locationToPos(target);
|
|
71
57
|
try {
|
|
72
|
-
log('makeMove', {
|
|
73
|
-
|
|
74
|
-
game.move({ from, to, promotion }, { strict: false });
|
|
58
|
+
log('makeMove', { s, t });
|
|
59
|
+
game.move({ from: s, to: t }, { strict: false });
|
|
75
60
|
return game;
|
|
76
61
|
} catch (err) {
|
|
77
62
|
// Ignore.
|
|
@@ -116,12 +101,6 @@ export class ChessModel implements BoardModel<ChessPiece> {
|
|
|
116
101
|
return makeMove(new Chess(this._game.fen()), move) !== null;
|
|
117
102
|
}
|
|
118
103
|
|
|
119
|
-
canPromote(move: Move): boolean {
|
|
120
|
-
const isPawnMove = move.piece === 'BP' || move.piece === 'WP';
|
|
121
|
-
const isToLastRank = move.to[0] === 0 || move.to[0] === 7;
|
|
122
|
-
return isPawnMove && isToLastRank;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
104
|
makeMove(move: Move): boolean {
|
|
126
105
|
const game = makeMove(this._game, move);
|
|
127
106
|
if (!game) {
|