@dxos/react-ui-gameboard 0.8.4-main.c1de068 → 0.8.4-main.c85a9c8dae
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/README.md +2 -0
- package/dist/lib/browser/index.mjs +736 -788
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +736 -788
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Chessboard/Chessboard.d.ts +20 -0
- package/dist/types/src/components/Chessboard/Chessboard.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/Chessboard.stories.d.ts +30 -0
- package/dist/types/src/components/Chessboard/Chessboard.stories.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/chess.d.ts +60 -0
- package/dist/types/src/components/Chessboard/chess.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/chess.test.d.ts +2 -0
- package/dist/types/src/components/Chessboard/chess.test.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/index.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/Gameboard.d.ts +37 -0
- package/dist/types/src/components/Gameboard/Gameboard.d.ts.map +1 -0
- package/dist/types/src/{Gameboard → components/Gameboard}/Piece.d.ts +3 -2
- package/dist/types/src/components/Gameboard/Piece.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/Square.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/index.d.ts +4 -0
- package/dist/types/src/components/Gameboard/index.d.ts.map +1 -0
- package/dist/types/src/{Gameboard → components/Gameboard}/types.d.ts +3 -2
- package/dist/types/src/components/Gameboard/types.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/util.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +3 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/gen/pieces/chess/alpha/bB.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/bB.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/alpha/bK.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/bN.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/bP.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/bQ.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/bQ.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/alpha/bR.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/index.d.ts +12 -12
- package/dist/types/src/gen/pieces/chess/alpha/wB.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/wK.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/wN.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/wP.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/wQ.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/alpha/wR.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bB.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bB.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/bK.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bK.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/bN.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bN.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/bP.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bQ.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bQ.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/bR.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/bR.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/index.d.ts +12 -12
- package/dist/types/src/gen/pieces/chess/cburnett/wB.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/wB.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/wK.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/wK.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/wN.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/wN.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/wP.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/wQ.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/wQ.d.ts.map +1 -1
- package/dist/types/src/gen/pieces/chess/cburnett/wR.d.ts +2 -2
- package/dist/types/src/gen/pieces/chess/cburnett/wR.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -22
- package/src/{Chessboard → components/Chessboard}/Chessboard.stories.tsx +32 -29
- package/src/{Chessboard → components/Chessboard}/Chessboard.tsx +72 -60
- package/src/components/Chessboard/chess.test.ts +19 -0
- package/src/components/Chessboard/chess.ts +325 -0
- package/src/components/Gameboard/Gameboard.tsx +139 -0
- package/src/{Gameboard → components/Gameboard}/Piece.tsx +27 -23
- package/src/{Gameboard → components/Gameboard}/Square.tsx +8 -6
- package/src/{Gameboard → components/Gameboard}/index.ts +0 -3
- package/src/{Gameboard → components/Gameboard}/types.ts +4 -2
- package/src/components/index.ts +6 -0
- package/src/gen/pieces/chess/alpha/bB.tsx +9 -6
- package/src/gen/pieces/chess/alpha/bK.tsx +7 -7
- package/src/gen/pieces/chess/alpha/bN.tsx +7 -7
- package/src/gen/pieces/chess/alpha/bP.tsx +5 -5
- package/src/gen/pieces/chess/alpha/bQ.tsx +9 -6
- package/src/gen/pieces/chess/alpha/bR.tsx +6 -6
- package/src/gen/pieces/chess/alpha/index.ts +12 -12
- package/src/gen/pieces/chess/alpha/wB.tsx +7 -7
- package/src/gen/pieces/chess/alpha/wK.tsx +7 -7
- package/src/gen/pieces/chess/alpha/wN.tsx +7 -7
- package/src/gen/pieces/chess/alpha/wP.tsx +7 -7
- package/src/gen/pieces/chess/alpha/wQ.tsx +7 -7
- package/src/gen/pieces/chess/alpha/wR.tsx +7 -7
- package/src/gen/pieces/chess/cburnett/bB.tsx +20 -9
- package/src/gen/pieces/chess/cburnett/bK.tsx +25 -15
- package/src/gen/pieces/chess/cburnett/bN.tsx +20 -13
- package/src/gen/pieces/chess/cburnett/bP.tsx +6 -6
- package/src/gen/pieces/chess/cburnett/bQ.tsx +19 -13
- package/src/gen/pieces/chess/cburnett/bR.tsx +27 -11
- package/src/gen/pieces/chess/cburnett/index.ts +12 -12
- package/src/gen/pieces/chess/cburnett/wB.tsx +19 -9
- package/src/gen/pieces/chess/cburnett/wK.tsx +19 -12
- package/src/gen/pieces/chess/cburnett/wN.tsx +16 -9
- package/src/gen/pieces/chess/cburnett/wP.tsx +7 -7
- package/src/gen/pieces/chess/cburnett/wQ.tsx +19 -9
- package/src/gen/pieces/chess/cburnett/wR.tsx +19 -9
- package/src/index.ts +1 -2
- package/dist/types/src/Chessboard/Chessboard.d.ts +0 -15
- package/dist/types/src/Chessboard/Chessboard.d.ts.map +0 -1
- package/dist/types/src/Chessboard/Chessboard.stories.d.ts +0 -16
- package/dist/types/src/Chessboard/Chessboard.stories.d.ts.map +0 -1
- package/dist/types/src/Chessboard/chess.d.ts +0 -40
- package/dist/types/src/Chessboard/chess.d.ts.map +0 -1
- package/dist/types/src/Chessboard/index.d.ts.map +0 -1
- package/dist/types/src/Gameboard/Gameboard.d.ts +0 -23
- package/dist/types/src/Gameboard/Gameboard.d.ts.map +0 -1
- package/dist/types/src/Gameboard/Piece.d.ts.map +0 -1
- package/dist/types/src/Gameboard/Square.d.ts.map +0 -1
- package/dist/types/src/Gameboard/context.d.ts +0 -10
- package/dist/types/src/Gameboard/context.d.ts.map +0 -1
- package/dist/types/src/Gameboard/index.d.ts +0 -7
- package/dist/types/src/Gameboard/index.d.ts.map +0 -1
- package/dist/types/src/Gameboard/types.d.ts.map +0 -1
- package/dist/types/src/Gameboard/util.d.ts.map +0 -1
- package/src/Chessboard/chess.ts +0 -213
- package/src/Gameboard/Gameboard.tsx +0 -103
- package/src/Gameboard/context.ts +0 -22
- /package/dist/types/src/{Chessboard → components/Chessboard}/index.d.ts +0 -0
- /package/dist/types/src/{Gameboard → components/Gameboard}/Square.d.ts +0 -0
- /package/dist/types/src/{Gameboard → components/Gameboard}/util.d.ts +0 -0
- /package/src/{Chessboard → components/Chessboard}/index.ts +0 -0
- /package/src/{Gameboard → components/Gameboard}/util.ts +0 -0
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-gameboard",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.c85a9c8dae",
|
|
4
4
|
"description": "Game board.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
13
|
"type": "module",
|
|
10
14
|
"exports": {
|
|
11
15
|
".": {
|
|
16
|
+
"source": "./src/index.ts",
|
|
12
17
|
"types": "./dist/types/src/index.d.ts",
|
|
13
18
|
"browser": "./dist/lib/browser/index.mjs",
|
|
14
19
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
@@ -23,36 +28,36 @@
|
|
|
23
28
|
"src"
|
|
24
29
|
],
|
|
25
30
|
"dependencies": {
|
|
26
|
-
"@atlaskit/pragmatic-drag-and-drop": "
|
|
27
|
-
"@atlaskit/pragmatic-drag-and-drop-hitbox": "
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
31
|
+
"@atlaskit/pragmatic-drag-and-drop": "1.7.7",
|
|
32
|
+
"@atlaskit/pragmatic-drag-and-drop-hitbox": "1.1.0",
|
|
33
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
34
|
+
"@radix-ui/react-context": "1.1.1",
|
|
30
35
|
"chess.js": "^1.0.0",
|
|
31
36
|
"react-resize-detector": "^11.0.1",
|
|
32
|
-
"@dxos/debug": "0.8.4-main.
|
|
33
|
-
"@dxos/invariant": "0.8.4-main.
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
+
"@dxos/debug": "0.8.4-main.c85a9c8dae",
|
|
38
|
+
"@dxos/invariant": "0.8.4-main.c85a9c8dae",
|
|
39
|
+
"@dxos/node-std": "0.8.4-main.c85a9c8dae",
|
|
40
|
+
"@dxos/util": "0.8.4-main.c85a9c8dae",
|
|
41
|
+
"@dxos/log": "0.8.4-main.c85a9c8dae"
|
|
37
42
|
},
|
|
38
43
|
"devDependencies": {
|
|
39
44
|
"@svgr/cli": "^8.1.0",
|
|
40
45
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
41
|
-
"@types/react": "~
|
|
42
|
-
"@types/react-dom": "~
|
|
46
|
+
"@types/react": "~19.2.7",
|
|
47
|
+
"@types/react-dom": "~19.2.3",
|
|
43
48
|
"lodash.defaultsdeep": "^4.6.1",
|
|
44
|
-
"react": "~
|
|
45
|
-
"react-dom": "~
|
|
46
|
-
"vite": "
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/react-ui
|
|
49
|
-
"@dxos/
|
|
49
|
+
"react": "~19.2.3",
|
|
50
|
+
"react-dom": "~19.2.3",
|
|
51
|
+
"vite": "^7.1.11",
|
|
52
|
+
"@dxos/storybook-utils": "0.8.4-main.c85a9c8dae",
|
|
53
|
+
"@dxos/react-ui": "0.8.4-main.c85a9c8dae",
|
|
54
|
+
"@dxos/ui-theme": "0.8.4-main.c85a9c8dae"
|
|
50
55
|
},
|
|
51
56
|
"peerDependencies": {
|
|
52
|
-
"react": "~
|
|
53
|
-
"react-dom": "~
|
|
54
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
55
|
-
"@dxos/
|
|
57
|
+
"react": "~19.2.3",
|
|
58
|
+
"react-dom": "~19.2.3",
|
|
59
|
+
"@dxos/react-ui": "0.8.4-main.c85a9c8dae",
|
|
60
|
+
"@dxos/ui-theme": "0.8.4-main.c85a9c8dae"
|
|
56
61
|
},
|
|
57
62
|
"publishConfig": {
|
|
58
63
|
"access": "public"
|
|
@@ -2,31 +2,33 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
6
8
|
|
|
7
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
9
|
-
|
|
10
|
-
import { log } from '@dxos/log';
|
|
11
9
|
import { Button, Toolbar } from '@dxos/react-ui';
|
|
12
|
-
import { withLayout, withTheme } from '@dxos/
|
|
10
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
12
|
+
|
|
13
|
+
import { Gameboard, type GameboardRootProps, type Move, type Player } from '../Gameboard';
|
|
13
14
|
|
|
14
|
-
import { Chessboard, type ChessboardProps } from './Chessboard';
|
|
15
15
|
import { ChessModel } from './chess';
|
|
16
|
-
import {
|
|
16
|
+
import { Chessboard, type ChessboardProps } from './Chessboard';
|
|
17
17
|
|
|
18
|
-
type
|
|
19
|
-
|
|
18
|
+
type DefaultStoryProps = Pick<ChessboardProps, 'orientation' | 'showLabels' | 'debug'> & {
|
|
19
|
+
pgn?: string;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const DefaultStory = ({
|
|
23
|
-
const
|
|
22
|
+
const DefaultStory = ({ orientation: _orientation, pgn, ...props }: DefaultStoryProps) => {
|
|
23
|
+
const registry = useContext(RegistryContext);
|
|
24
|
+
const model = useMemo(() => new ChessModel(registry, pgn), [registry, pgn]);
|
|
24
25
|
const [orientation, setOrientation] = useState<Player | undefined>(_orientation);
|
|
25
26
|
|
|
26
|
-
const handleDrop = useCallback<NonNullable<GameboardRootProps['onDrop']>>(
|
|
27
|
+
const handleDrop = useCallback<NonNullable<GameboardRootProps<ChessModel>['onDrop']>>(
|
|
27
28
|
(move: Move) => {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const result = model.makeMove(move);
|
|
30
|
+
console.log(model.pgn);
|
|
31
|
+
return result;
|
|
30
32
|
},
|
|
31
33
|
[model],
|
|
32
34
|
);
|
|
@@ -34,7 +36,7 @@ const DefaultStory = ({ fen, orientation: _orientation, ...props }: RenderProps)
|
|
|
34
36
|
return (
|
|
35
37
|
<div className='flex flex-col grow gap-2 overflow-hidden'>
|
|
36
38
|
<Toolbar.Root>
|
|
37
|
-
<Button onClick={() => model.
|
|
39
|
+
<Button onClick={() => model.update()}>Reset</Button>
|
|
38
40
|
<Button onClick={() => model.makeRandomMove()}>Move</Button>
|
|
39
41
|
<div className='grow'></div>
|
|
40
42
|
<Button
|
|
@@ -44,7 +46,7 @@ const DefaultStory = ({ fen, orientation: _orientation, ...props }: RenderProps)
|
|
|
44
46
|
</Button>
|
|
45
47
|
</Toolbar.Root>
|
|
46
48
|
<Gameboard.Root model={model} onDrop={handleDrop}>
|
|
47
|
-
<Gameboard.Content>
|
|
49
|
+
<Gameboard.Content grow contain>
|
|
48
50
|
<Chessboard orientation={orientation} {...props} />
|
|
49
51
|
</Gameboard.Content>
|
|
50
52
|
</Gameboard.Root>
|
|
@@ -52,8 +54,9 @@ const DefaultStory = ({ fen, orientation: _orientation, ...props }: RenderProps)
|
|
|
52
54
|
);
|
|
53
55
|
};
|
|
54
56
|
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
+
const GridStory = () => {
|
|
58
|
+
const registry = useContext(RegistryContext);
|
|
59
|
+
const models = useMemo(() => Array.from({ length: 9 }).map(() => new ChessModel(registry)), [registry]);
|
|
57
60
|
useEffect(() => {
|
|
58
61
|
const i = setInterval(() => {
|
|
59
62
|
const model = models[Math.floor(Math.random() * models.length)];
|
|
@@ -77,34 +80,34 @@ const Grid = (props: RenderProps) => {
|
|
|
77
80
|
);
|
|
78
81
|
};
|
|
79
82
|
|
|
80
|
-
const meta
|
|
83
|
+
const meta = {
|
|
81
84
|
title: 'ui/react-ui-gameboard/Chessboard',
|
|
82
85
|
component: Chessboard,
|
|
83
86
|
render: DefaultStory,
|
|
84
|
-
decorators: [withTheme, withLayout({
|
|
85
|
-
}
|
|
87
|
+
decorators: [withRegistry, withTheme(), withLayout({ layout: 'column' })],
|
|
88
|
+
} satisfies Meta<typeof Chessboard>;
|
|
86
89
|
|
|
87
90
|
export default meta;
|
|
88
91
|
|
|
89
|
-
type Story = StoryObj<typeof
|
|
92
|
+
type Story = StoryObj<typeof meta>;
|
|
90
93
|
|
|
91
94
|
export const Default: Story = {};
|
|
92
95
|
|
|
93
96
|
export const Promotion: Story = {
|
|
94
97
|
args: {
|
|
95
|
-
|
|
98
|
+
pgn: '1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. c3 Nf6 5. d4 exd4 6. cxd4 Bb4+ 7. Nc3 d5 8. exd5 Nxd5 9. O-O Be6 10. Qb3 Na5 11. Qa4+ c6 12. Bxd5 Bxc3 13. Bxe6 fxe6 14. d5 Qg5 15. dxe6 Kf8 16. e7+ Kg8 *',
|
|
96
99
|
},
|
|
97
100
|
};
|
|
98
101
|
|
|
99
102
|
export const Debug: Story = {
|
|
100
103
|
args: {
|
|
101
|
-
|
|
102
|
-
showLabels: true,
|
|
104
|
+
pgn: '1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. c3 Nf6 5. d4 exd4 6. cxd4 Bb4+ 7. Nc3 d5 8. exd5 Nxd5 9. O-O Be6 10. Qb3 Na5 11. Qa4+ c6 12. Bxd5 Bxc3 13. Bxe6 fxe6 *',
|
|
103
105
|
orientation: 'black',
|
|
104
|
-
|
|
106
|
+
showLabels: true,
|
|
107
|
+
debug: true,
|
|
105
108
|
},
|
|
106
109
|
};
|
|
107
110
|
|
|
108
|
-
export const
|
|
109
|
-
render:
|
|
111
|
+
export const Grid = {
|
|
112
|
+
render: GridStory,
|
|
110
113
|
};
|
|
@@ -2,26 +2,36 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { Atom, useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { type PropsWithChildren, forwardRef, memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
7
|
import { useResizeDetector } from 'react-resize-detector';
|
|
7
8
|
|
|
8
|
-
import { type ThemedClassName,
|
|
9
|
-
import { mx } from '@dxos/
|
|
10
|
-
import {
|
|
9
|
+
import { type ThemedClassName, useForwardedRef } from '@dxos/react-ui';
|
|
10
|
+
import { mx } from '@dxos/ui-theme';
|
|
11
|
+
import { isNonNullable } from '@dxos/util';
|
|
11
12
|
|
|
12
|
-
import { boardStyles, type ChessPiece, ChessPieces, getSquareColor, locationToPos } from './chess';
|
|
13
13
|
import {
|
|
14
14
|
type DOMRectBounds,
|
|
15
|
+
Gameboard,
|
|
15
16
|
type Location,
|
|
17
|
+
type PieceMap,
|
|
16
18
|
type PieceRecord,
|
|
17
19
|
type Player,
|
|
18
|
-
Piece,
|
|
19
|
-
Square,
|
|
20
20
|
getRelativeBounds,
|
|
21
21
|
locationToString,
|
|
22
|
-
|
|
22
|
+
useGameboardContext,
|
|
23
23
|
} from '../Gameboard';
|
|
24
24
|
|
|
25
|
+
import { type ChessModel, type ChessPiece, ChessPieces, boardStyles, getSquareColor, locationToPos } from './chess';
|
|
26
|
+
|
|
27
|
+
/** Fallback atom for when model is undefined. */
|
|
28
|
+
const EMPTY_PIECES_ATOM = Atom.make<PieceMap<ChessPiece>>({});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Chessboard layout.
|
|
32
|
+
*/
|
|
33
|
+
const CHESSBOARD_NAME = 'Chessboard';
|
|
34
|
+
|
|
25
35
|
export type ChessboardProps = ThemedClassName<
|
|
26
36
|
PropsWithChildren<{
|
|
27
37
|
orientation?: Player;
|
|
@@ -32,16 +42,15 @@ export type ChessboardProps = ThemedClassName<
|
|
|
32
42
|
}>
|
|
33
43
|
>;
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const { ref: containerRef, width, height } = useResizeDetector({ refreshRate: 200 });
|
|
42
|
-
const { model, promoting, onPromotion } = useBoardContext();
|
|
45
|
+
const ChessboardComponent = forwardRef<HTMLDivElement, ChessboardProps>(
|
|
46
|
+
({ classNames, orientation, showLabels, debug, rows = 8, cols = 8 }, forwardedRef) => {
|
|
47
|
+
const targetRef = useForwardedRef(forwardedRef);
|
|
48
|
+
const { width, height } = useResizeDetector({ targetRef, refreshRate: 200 });
|
|
49
|
+
const { model, promoting, onPromotion } = useGameboardContext<ChessModel>(CHESSBOARD_NAME);
|
|
50
|
+
const pieces = useAtomValue(model?.pieces ?? EMPTY_PIECES_ATOM);
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
// Board squares.
|
|
53
|
+
const squares = useMemo<Location[]>(() => {
|
|
45
54
|
return Array.from({ length: rows }, (_, i) => (orientation === 'black' ? i : rows - 1 - i)).flatMap((row) =>
|
|
46
55
|
Array.from({ length: cols }).map((_, col) => [row, col] as Location),
|
|
47
56
|
);
|
|
@@ -49,24 +58,24 @@ export const Chessboard = memo(
|
|
|
49
58
|
|
|
50
59
|
// Use DOM grid layout to position squares.
|
|
51
60
|
const layout = useMemo(() => {
|
|
52
|
-
return
|
|
61
|
+
return squares.map((location) => {
|
|
53
62
|
return (
|
|
54
63
|
<div
|
|
55
64
|
key={locationToString(location)}
|
|
56
65
|
{...{
|
|
57
|
-
|
|
66
|
+
'data-location': locationToString(location),
|
|
58
67
|
}}
|
|
59
68
|
/>
|
|
60
69
|
);
|
|
61
70
|
});
|
|
62
|
-
}, [
|
|
71
|
+
}, [squares]);
|
|
63
72
|
|
|
64
73
|
// Build map of square locations to bounds.
|
|
65
74
|
const [grid, setGrid] = useState<Record<string, DOMRectBounds>>({});
|
|
66
75
|
const gridRef = useRef<HTMLDivElement>(null);
|
|
67
76
|
useEffect(() => {
|
|
68
77
|
setGrid(
|
|
69
|
-
|
|
78
|
+
squares.reduce(
|
|
70
79
|
(acc, location) => {
|
|
71
80
|
const square = getSquareLocation(gridRef.current!, location)!;
|
|
72
81
|
const bounds = getRelativeBounds(gridRef.current!, square);
|
|
@@ -75,7 +84,7 @@ export const Chessboard = memo(
|
|
|
75
84
|
{} as Record<string, DOMRectBounds>,
|
|
76
85
|
),
|
|
77
86
|
);
|
|
78
|
-
}, [
|
|
87
|
+
}, [squares, width, height]);
|
|
79
88
|
|
|
80
89
|
// Get the bounds of each square and piece.
|
|
81
90
|
const positions = useMemo<{ piece: PieceRecord; bounds: DOMRectBounds }[]>(() => {
|
|
@@ -83,7 +92,7 @@ export const Chessboard = memo(
|
|
|
83
92
|
return [];
|
|
84
93
|
}
|
|
85
94
|
|
|
86
|
-
return Object.values(
|
|
95
|
+
return Object.values(pieces)
|
|
87
96
|
.map((piece) => {
|
|
88
97
|
if (piece.id === promoting?.id) {
|
|
89
98
|
return null;
|
|
@@ -92,17 +101,19 @@ export const Chessboard = memo(
|
|
|
92
101
|
const bounds = grid[locationToString(piece.location)];
|
|
93
102
|
return { piece, bounds };
|
|
94
103
|
})
|
|
95
|
-
.filter(
|
|
96
|
-
}, [grid,
|
|
104
|
+
.filter(isNonNullable);
|
|
105
|
+
}, [grid, pieces, promoting]);
|
|
97
106
|
|
|
98
107
|
return (
|
|
99
|
-
<div ref={
|
|
108
|
+
<div ref={targetRef} tabIndex={0} className={mx('relative outline-hidden', classNames)}>
|
|
109
|
+
{/* DOM Layout. */}
|
|
100
110
|
<div ref={gridRef} className='grid grid-rows-8 grid-cols-8 aspect-square select-none'>
|
|
101
111
|
{layout}
|
|
102
112
|
</div>
|
|
113
|
+
{/* Squares. */}
|
|
103
114
|
<div>
|
|
104
|
-
{
|
|
105
|
-
<Square
|
|
115
|
+
{squares.map((location) => (
|
|
116
|
+
<Gameboard.Square
|
|
106
117
|
key={locationToString(location)}
|
|
107
118
|
location={location}
|
|
108
119
|
label={showLabels ? locationToPos(location) : undefined}
|
|
@@ -111,9 +122,10 @@ export const Chessboard = memo(
|
|
|
111
122
|
/>
|
|
112
123
|
))}
|
|
113
124
|
</div>
|
|
125
|
+
{/* Pieces. */}
|
|
114
126
|
<div className={mx(promoting && 'opacity-50')}>
|
|
115
127
|
{positions.map(({ bounds, piece }) => (
|
|
116
|
-
<Piece
|
|
128
|
+
<Gameboard.Piece
|
|
117
129
|
key={piece.id}
|
|
118
130
|
piece={piece}
|
|
119
131
|
bounds={bounds}
|
|
@@ -123,32 +135,29 @@ export const Chessboard = memo(
|
|
|
123
135
|
/>
|
|
124
136
|
))}
|
|
125
137
|
</div>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
</div>
|
|
138
|
+
{/* Promotion selector. */}
|
|
139
|
+
{promoting && (
|
|
140
|
+
<PromotionSelector
|
|
141
|
+
grid={grid}
|
|
142
|
+
piece={promoting}
|
|
143
|
+
onSelect={(piece) => {
|
|
144
|
+
onPromotion({
|
|
145
|
+
from: Object.values(pieces).find((p) => p.id === promoting.id)!.location,
|
|
146
|
+
to: piece.location,
|
|
147
|
+
piece: promoting.type,
|
|
148
|
+
promotion: piece.type,
|
|
149
|
+
});
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
142
153
|
</div>
|
|
143
154
|
);
|
|
144
155
|
},
|
|
145
156
|
);
|
|
146
157
|
|
|
147
|
-
|
|
158
|
+
ChessboardComponent.displayName = CHESSBOARD_NAME;
|
|
148
159
|
|
|
149
|
-
const
|
|
150
|
-
return container.querySelector(`[data-location="${locationToString(location)}"]`);
|
|
151
|
-
};
|
|
160
|
+
export const Chessboard = memo(ChessboardComponent);
|
|
152
161
|
|
|
153
162
|
const PromotionSelector = ({
|
|
154
163
|
grid,
|
|
@@ -176,19 +185,22 @@ const PromotionSelector = ({
|
|
|
176
185
|
onSelect({ ...piece, type: selected.type });
|
|
177
186
|
};
|
|
178
187
|
|
|
179
|
-
// TODO(burdon): Circle.
|
|
180
188
|
return (
|
|
181
|
-
|
|
189
|
+
<>
|
|
182
190
|
{positions.map(({ piece, bounds }) => (
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
<Gameboard.Piece
|
|
192
|
+
key={piece.id}
|
|
193
|
+
classNames={mx('border-2 border-neutral-700 rounded-full', boardStyles.promotion)}
|
|
194
|
+
piece={piece}
|
|
195
|
+
bounds={bounds}
|
|
196
|
+
Component={ChessPieces[piece.type as ChessPiece]}
|
|
197
|
+
onClick={() => handleSelect(piece)}
|
|
198
|
+
/>
|
|
191
199
|
))}
|
|
192
|
-
|
|
200
|
+
</>
|
|
193
201
|
);
|
|
194
202
|
};
|
|
203
|
+
|
|
204
|
+
const getSquareLocation = (container: HTMLElement, location: Location): HTMLElement | null => {
|
|
205
|
+
return container.querySelector(`[data-location="${locationToString(location)}"]`);
|
|
206
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Chess as ChessJS } from 'chess.js';
|
|
6
|
+
import { describe, it } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { createPieceMap } from './chess';
|
|
9
|
+
|
|
10
|
+
describe('ChessModel', () => {
|
|
11
|
+
it('should update pieces', ({ expect }) => {
|
|
12
|
+
const chess = new ChessJS();
|
|
13
|
+
chess.loadPgn(
|
|
14
|
+
'1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. c3 Nf6 5. d4 exd4 6. cxd4 Bb4+ 7. Nc3 d5 8. exd5 Nxd5 9. O-O Be6 10. Qb3 Na5 11. Qa4+ c6 12. Bxd5 Bxc3 13. Bxe6 fxe6 *',
|
|
15
|
+
);
|
|
16
|
+
const pieces = createPieceMap(chess);
|
|
17
|
+
expect(pieces).to.exist;
|
|
18
|
+
});
|
|
19
|
+
});
|