kokopu-react 3.3.5 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/CREDITS.md +5 -1
  3. package/dist/lib/chessboard/BoardProperties.d.ts +4 -0
  4. package/dist/lib/chessboard/BoardProperties.js +1 -0
  5. package/dist/lib/chessboard/BoardProperties.js.map +1 -1
  6. package/dist/lib/chessboard/Chessboard.js +2 -1
  7. package/dist/lib/chessboard/Chessboard.js.map +1 -1
  8. package/dist/lib/chessboard/ChessboardImpl.d.ts +1 -0
  9. package/dist/lib/chessboard/ChessboardImpl.js +3 -1
  10. package/dist/lib/chessboard/ChessboardImpl.js.map +1 -1
  11. package/dist/lib/chessboard/Sound.d.ts +29 -0
  12. package/dist/lib/chessboard/Sound.js +53 -0
  13. package/dist/lib/chessboard/Sound.js.map +1 -0
  14. package/dist/lib/chessboard/sounds/move.ogg +0 -0
  15. package/dist/lib/icons/ChessPieceIcon.js +4 -4
  16. package/dist/lib/icons/ChessPieceIcon.js.map +1 -1
  17. package/dist/lib/navigationboard/NavigationBoard.d.ts +1 -0
  18. package/dist/lib/navigationboard/NavigationBoard.js +1 -1
  19. package/dist/lib/navigationboard/NavigationBoard.js.map +1 -1
  20. package/doc_src/demo/NavigationBoardWithMoveText.txt +1 -1
  21. package/doc_src/demo/PageChessboardMove.tsx +8 -0
  22. package/doc_src/demo/PageMovetextInteraction.tsx +1 -1
  23. package/doc_src/demo/PageNavigationBoardBase.tsx +11 -1
  24. package/doc_src/demo/PageNavigationBoardUncontrolledVsControlled.tsx +4 -2
  25. package/doc_src/demo/PageSynchroNavigationBoardMovetext.tsx +1 -1
  26. package/doc_src/webpack_configuration.md +2 -1
  27. package/package.json +25 -26
  28. package/scripts/doc.styleguide.config.js +1 -1
  29. package/scripts/doc.tsconfig.json +1 -1
  30. package/scripts/test-graphic.webpack.config.js +1 -1
  31. package/scripts/test-headless.webpack.config.js +1 -1
  32. package/src/chessboard/BoardProperties.ts +6 -0
  33. package/src/chessboard/Chessboard.tsx +2 -1
  34. package/src/chessboard/ChessboardImpl.tsx +3 -0
  35. package/src/chessboard/Sound.tsx +71 -0
  36. package/src/chessboard/sounds/move.ogg +0 -0
  37. package/src/decs.d.ts +5 -0
  38. package/src/icons/ChessPieceIcon.tsx +5 -5
  39. package/src/navigationboard/NavigationBoard.tsx +1 -1
  40. package/test/01_types.js +81 -82
  41. package/test/02_chessboard_util.js +12 -12
  42. package/test/06_chessboard_animation.js +21 -3
  43. package/test/11_movetext_interaction.js +7 -7
  44. package/test/12_navigation_board_base.js +2 -2
  45. package/test/common/graphic.js +12 -3
  46. package/test/graphic_references/06_chessboard_animation/sound/with_move.png +0 -0
  47. package/test/graphic_references/06_chessboard_animation/sound/without_move.png +0 -0
  48. package/test/graphic_test_app/05_chessboard_graphic/annotations.tsx +4 -4
  49. package/test/graphic_test_app/05_chessboard_graphic/base.tsx +1 -1
  50. package/test/graphic_test_app/05_chessboard_graphic/flipped.tsx +5 -5
  51. package/test/graphic_test_app/05_chessboard_graphic/move.tsx +2 -2
  52. package/test/graphic_test_app/05_chessboard_graphic/theme.tsx +3 -3
  53. package/test/graphic_test_app/06_chessboard_animation/base_1.tsx +6 -6
  54. package/test/graphic_test_app/06_chessboard_animation/base_2.tsx +6 -6
  55. package/test/graphic_test_app/06_chessboard_animation/base_3.tsx +6 -6
  56. package/test/graphic_test_app/06_chessboard_animation/on_redraw.tsx +3 -2
  57. package/test/graphic_test_app/06_chessboard_animation/sound.tsx +52 -0
  58. package/test/graphic_test_app/14_navigation_board_autoplay/controlled.tsx +1 -1
  59. package/test/graphic_test_app/common/test_app.tsx +5 -4
@@ -83,11 +83,11 @@ export default class Page extends React.Component<object, PageState> {
83
83
  return (
84
84
  <Box display="flex" alignItems="top" justifyContent="space-around">
85
85
  <Box>
86
- <NavigationBoard game={pgn} animated initialNodeId="9b" />
86
+ <NavigationBoard game={pgn} animated sound initialNodeId="9b" />
87
87
  <Typography mt={2} textAlign="center">Uncontrolled</Typography>
88
88
  </Box>
89
89
  <Box>
90
- <NavigationBoard game={pgn} animated nodeId={this.state.nodeId} onNodeIdChanged={nodeId => this.setState({ nodeId: nodeId })} />
90
+ <NavigationBoard game={pgn} animated sound nodeId={this.state.nodeId} onNodeIdChanged={nodeId => this.setState({ nodeId: nodeId })} />
91
91
  <Typography mt={2} textAlign="center">Controlled</Typography>
92
92
  <Typography textAlign="center">{`Current node ID: ${this.state.nodeId}`}</Typography>
93
93
  </Box>
@@ -100,11 +100,13 @@ export default class Page extends React.Component<object, PageState> {
100
100
  const uncontrolledAttributes: string[] = [];
101
101
  uncontrolledAttributes.push('game={pgn}');
102
102
  uncontrolledAttributes.push('animated');
103
+ uncontrolledAttributes.push('sound');
103
104
  uncontrolledAttributes.push('initialNodeId="9b"');
104
105
 
105
106
  const controlledAttributes: string[] = [];
106
107
  controlledAttributes.push('game={pgn}');
107
108
  controlledAttributes.push('animated');
109
+ controlledAttributes.push('sound');
108
110
  controlledAttributes.push(`nodeId="${this.state.nodeId}"`);
109
111
  controlledAttributes.push('onNodeIdChanged={nodeId => handleNodeIdChanged(nodeId)}');
110
112
 
@@ -48,7 +48,7 @@ function NavigationBoardWithMoveText({ game }: { game: Game }) {
48
48
  const [ nodeId, setNodeId ] = React.useState('start');
49
49
  return (
50
50
  <Stack direction="row" spacing={2}>
51
- <NavigationBoard animated game={game} nodeId={nodeId} onNodeIdChanged={setNodeId} />
51
+ <NavigationBoard animated sound game={game} nodeId={nodeId} onNodeIdChanged={setNodeId} />
52
52
  <Movetext
53
53
  game={game} diagramOptions={{ squareSize: 28 }} interactionMode="selectMove" selection={nodeId}
54
54
  onMoveSelected={n => {
@@ -1,5 +1,6 @@
1
1
  Kokopu-React is not a "pure" JavaScript/TypeScript library, in that it is shipped with other type of files:
2
2
  - `*.css` files (stylesheets),
3
+ - `*.ogg` files (sounds),
3
4
  - `*.png` files (images),
4
5
  - `*.woff` and `*.woff2` files (fonts).
5
6
 
@@ -17,7 +18,7 @@ module.exports = {
17
18
  // ... or any other configuration that loads the matching files as global CSS.
18
19
  },
19
20
  {
20
- test: /\.(png|woff|woff2)$/i,
21
+ test: /\.(ogg|png|woff|woff2)$/i,
21
22
  type: 'asset/resource',
22
23
  // ... or any other configuration that loads the matching files as resources.
23
24
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kokopu-react",
3
- "version": "3.3.5",
3
+ "version": "3.4.1",
4
4
  "description": "A React-based library to create and display chessboard and chess-related components.",
5
5
  "keywords": [
6
6
  "chess",
@@ -43,44 +43,43 @@
43
43
  "htmlparser2": "^9.1.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@babel/preset-env": "^7.28.5",
46
+ "@babel/preset-env": "^7.29.5",
47
47
  "@babel/preset-react": "^7.28.5",
48
48
  "@emotion/react": "^11.14.0",
49
49
  "@emotion/styled": "^11.14.1",
50
- "@eslint/js": "^9.39.2",
51
- "@mui/material": "^7.3.6",
52
- "@stylistic/eslint-plugin": "^5.6.1",
53
- "@types/react": "^19.2.7",
50
+ "@eslint/js": "^9.39.4",
51
+ "@mui/material": "^7.3.11",
52
+ "@stylistic/eslint-plugin": "^5.10.0",
53
+ "@types/react": "^19.2.14",
54
54
  "@types/react-dom": "^19.2.3",
55
- "babel-loader": "^10.0.0",
56
- "copy-webpack-plugin": "^13.0.1",
55
+ "babel-loader": "^10.1.1",
56
+ "copy-webpack-plugin": "^14.0.0",
57
57
  "copyfiles": "^2.4.1",
58
58
  "coverage-istanbul-loader": "^3.0.5",
59
- "css-loader": "^7.1.2",
60
- "eslint": "^9.39.2",
59
+ "css-loader": "^7.1.4",
60
+ "eslint": "^9.39.4",
61
61
  "eslint-plugin-react": "^7.37.5",
62
- "eslint-plugin-react-hooks": "^7.0.1",
63
- "globals": "^16.5.0",
64
- "html-webpack-plugin": "^5.6.5",
62
+ "eslint-plugin-react-hooks": "^7.1.1",
63
+ "globals": "^17.6.0",
64
+ "html-webpack-plugin": "^5.6.7",
65
65
  "img-diff-js": "^0.5.4",
66
- "kokopu": "^4.13.1",
66
+ "kokopu": "^4.13.2",
67
67
  "mocha": "^11.7.5",
68
68
  "null-loader": "^4.0.1",
69
- "nyc": "^17.1.0",
70
- "react": "^19.2.3",
69
+ "nyc": "^18.0.0",
70
+ "react": "^19.2.6",
71
71
  "react-docgen-typescript": "~2.2.2",
72
- "react-dom": "^19.2.3",
72
+ "react-dom": "^19.2.6",
73
73
  "react-styleguidist": "^13.1.4",
74
- "rimraf": "^6.1.2",
75
- "selenium-webdriver": "^4.39.0",
76
- "ssh2-sftp-client": "^12.0.1",
74
+ "rimraf": "^6.1.3",
75
+ "selenium-webdriver": "^4.43.0",
76
+ "ssh2-sftp-client": "^12.1.1",
77
77
  "style-loader": "^4.0.0",
78
- "ts-loader": "^9.5.4",
78
+ "ts-loader": "^9.5.7",
79
79
  "typescript": "^5.9.3",
80
- "typescript-eslint": "^8.51.0",
81
- "unit.js": "^2.1.1",
82
- "webpack": "^5.104.1",
83
- "webpack-cli": "^6.0.1"
80
+ "typescript-eslint": "^8.59.2",
81
+ "webpack": "^5.106.2",
82
+ "webpack-cli": "^7.0.2"
84
83
  },
85
84
  "scripts": {
86
85
  "lint": "eslint doc_src src test",
@@ -94,7 +93,7 @@
94
93
  "test_env:stop": "docker compose -p kokopu-react-test-env -f scripts/docker-compose.yml down",
95
94
  "build": "npm run build:lib && npm run build:doc",
96
95
  "build:doc": "rimraf dist/docs && styleguidist build --config scripts/doc.styleguide.config.js && copyfiles -u 2 'doc_src/theming/*.png' dist/docs",
97
- "build:lib": "rimraf dist/lib && tsc --outDir dist/lib && copyfiles -u 1 'src/**/*.{css,png,woff,woff2}' dist/lib",
96
+ "build:lib": "rimraf dist/lib && tsc --outDir dist/lib && copyfiles -u 1 'src/**/*.{css,ogg,png,woff,woff2}' dist/lib",
98
97
  "start": "styleguidist server --open --config scripts/doc.styleguide.config.js",
99
98
  "clean": "rimraf build dist"
100
99
  }
@@ -134,7 +134,7 @@ module.exports = {
134
134
  use: [ 'style-loader', 'css-loader' ],
135
135
  },
136
136
  {
137
- test: /\.(png|woff|woff2)$/i,
137
+ test: /\.(ogg|png|woff|woff2)$/i,
138
138
  type: 'asset/resource',
139
139
  },
140
140
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "extends": "../tsconfig.json",
3
- "include": [ "../doc_src/demo/**/*" ],
3
+ "include": [ "../doc_src/demo/**/*", "../src/**/*" ],
4
4
  "compilerOptions": {
5
5
  "declaration": false
6
6
  }
@@ -85,7 +85,7 @@ module.exports = {
85
85
  use: [ 'style-loader', 'css-loader' ],
86
86
  },
87
87
  {
88
- test: /\.(png|woff|woff2)$/i,
88
+ test: /\.(ogg|png|woff|woff2)$/i,
89
89
  type: 'asset/resource',
90
90
  },
91
91
  {
@@ -49,7 +49,7 @@ module.exports = {
49
49
  use: 'null-loader',
50
50
  },
51
51
  {
52
- test: /\.(png|woff|woff2)$/i,
52
+ test: /\.(ogg|png|woff|woff2)$/i,
53
53
  use: 'null-loader',
54
54
  },
55
55
  ],
@@ -104,6 +104,11 @@ export interface DynamicBoardGraphicProps extends StaticBoardGraphicProps {
104
104
  * Whether moves are animated or not.
105
105
  */
106
106
  animated: boolean,
107
+
108
+ /**
109
+ * Whether sound effects are enabled or not.
110
+ */
111
+ sound: boolean,
107
112
  }
108
113
 
109
114
 
@@ -127,5 +132,6 @@ export function defaultDynamicBoardProps(): DynamicBoardGraphicProps {
127
132
  moveArrowVisible: true,
128
133
  moveArrowColor: 'b',
129
134
  animated: false,
135
+ sound: false,
130
136
  };
131
137
  }
@@ -228,6 +228,7 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
228
228
  throw new IllegalArgument('Chessboard', 'moveArrowColor');
229
229
  }
230
230
  const animated = sanitizeBoolean(this.props.animated);
231
+ const sound = sanitizeBoolean(this.props.sound);
231
232
  const colorset = colorsets[this.props.colorset];
232
233
  if (!colorset) {
233
234
  throw new IllegalArgument('Chessboard', 'colorset');
@@ -263,7 +264,7 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
263
264
  key={key}
264
265
  position={position} move={move} squareMarkers={sqm} textMarkers={txtm} arrowMarkers={arm} flipped={flipped}
265
266
  squareSize={actualSquareSize} coordinateVisible={actualCoordinateVisible} turnVisible={actualTurnVisible}
266
- moveArrowVisible={moveArrowVisible} moveArrowColor={this.props.moveArrowColor} animated={animated}
267
+ moveArrowVisible={moveArrowVisible} moveArrowColor={this.props.moveArrowColor} animated={animated} sound={sound}
267
268
  colorset={colorset} pieceset={pieceset}
268
269
  interactionMode={interactionMode} editedArrowColor={this.props.editedArrowColor}
269
270
  onPieceMoved={onPieceMoved} onSquareClicked={onSquareClicked} onArrowEdited={onArrowEdited} onMovePlayed={onMovePlayed}
@@ -33,6 +33,7 @@ import { AnnotationSymbolShape } from '../icons/AnnotationSymbolShape';
33
33
  import { ArrowTip } from '../icons/ArrowTip';
34
34
  import { Draggable } from './Draggable';
35
35
  import { Motion } from './Motion';
36
+ import { Sound } from './Sound';
36
37
 
37
38
  import '../icons/arrow.css';
38
39
  import './Chessboard.css';
@@ -66,6 +67,7 @@ interface ChessboardImplProps {
66
67
  moveArrowVisible: boolean,
67
68
  moveArrowColor: AnnotationColor,
68
69
  animated: boolean,
70
+ sound: boolean,
69
71
  colorset: Colorset,
70
72
  pieceset: Pieceset,
71
73
 
@@ -145,6 +147,7 @@ export class ChessboardImpl extends React.Component<ChessboardImplProps, Chessbo
145
147
  {rankCoordinates}
146
148
  {fileCoordinates}
147
149
  {this.renderBoardContent()}
150
+ {this.props.move && this.props.sound ? <Sound /> : undefined}
148
151
  </svg>
149
152
  );
150
153
  }
@@ -0,0 +1,71 @@
1
+ /*!
2
+ * -------------------------------------------------------------------------- *
3
+ * *
4
+ * Kokopu-React - A React-based library of chess-related components. *
5
+ * <https://www.npmjs.com/package/kokopu-react> *
6
+ * Copyright (C) 2021-2026 Yoann Le Montagner <yo35 -at- melix.net> *
7
+ * *
8
+ * Kokopu-React is free software: you can redistribute it and/or *
9
+ * modify it under the terms of the GNU Lesser General Public License *
10
+ * as published by the Free Software Foundation, either version 3 of *
11
+ * the License, or (at your option) any later version. *
12
+ * *
13
+ * Kokopu-React is distributed in the hope that it will be useful, *
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16
+ * GNU Lesser General Public License for more details. *
17
+ * *
18
+ * You should have received a copy of the GNU Lesser General *
19
+ * Public License along with this program. If not, see *
20
+ * <http://www.gnu.org/licenses/>. *
21
+ * *
22
+ * -------------------------------------------------------------------------- */
23
+
24
+
25
+ import * as React from 'react';
26
+
27
+ import moveSound from './sounds/move.ogg';
28
+
29
+
30
+ /**
31
+ * Play a sound.
32
+ */
33
+ export function Sound() {
34
+
35
+ React.useEffect(() => {
36
+ const audio = new Audio(moveSound);
37
+ safePlay(audio);
38
+ return () => audio.pause();
39
+ }, []);
40
+
41
+ return undefined;
42
+ }
43
+
44
+
45
+ declare global {
46
+
47
+ // WARNING: this global variable exists only for testing purposes. DO NOT USE IT IN PRODUCTION.
48
+ var __kokopu_debug_sound: string; // eslint-disable-line camelcase
49
+ }
50
+
51
+
52
+ async function safePlay(audio: HTMLAudioElement) {
53
+ try {
54
+
55
+ const debugSound = window.__kokopu_debug_sound === '';
56
+ // istanbul ignore else
57
+ if (debugSound) {
58
+ window['__kokopu_debug_sound'] = 'sound-in-progress';
59
+ }
60
+
61
+ await audio.play();
62
+
63
+ // istanbul ignore else
64
+ if (debugSound) {
65
+ window['__kokopu_debug_sound'] = 'sound-done';
66
+ }
67
+ }
68
+ catch (e) { // eslint-disable-line @typescript-eslint/no-unused-vars
69
+ // Ignore the errors (can typically happen if the audio is interrupted while fetching the sound file).
70
+ }
71
+ }
Binary file
package/src/decs.d.ts CHANGED
@@ -22,6 +22,11 @@
22
22
  * -------------------------------------------------------------------------- */
23
23
 
24
24
 
25
+ declare module '*.ogg' {
26
+ const path: string;
27
+ export default path;
28
+ }
29
+
25
30
  declare module '*.png' {
26
31
  const path: string;
27
32
  export default path;
@@ -55,7 +55,7 @@ export interface ChessPieceIconProps {
55
55
  export function ChessPieceIcon({ size, type, pieceset = DEFAULT_PIECESET }: ChessPieceIconProps) {
56
56
 
57
57
  // Sanitize the inputs.
58
- size = sanitizeBoundedInteger(size, MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, () => new IllegalArgument('ChessPieceIcon', 'size'));
58
+ const localSize = sanitizeBoundedInteger(size, MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, () => new IllegalArgument('ChessPieceIcon', 'size'));
59
59
  const types = sanitizeChessPieceIconType(type, () => new IllegalArgument('ChessPieceIcon', 'type'));
60
60
  const piecesetData = piecesets[pieceset];
61
61
  if (!piecesetData) {
@@ -63,11 +63,11 @@ export function ChessPieceIcon({ size, type, pieceset = DEFAULT_PIECESET }: Ches
63
63
  }
64
64
 
65
65
  // Render the component.
66
- const width = size * types.length;
67
- const viewBox = `0 0 ${width} ${size}`;
66
+ const width = localSize * types.length;
67
+ const viewBox = `0 0 ${width} ${localSize}`;
68
68
  return (
69
- <svg className="kokopu-chessPieceIcon" viewBox={viewBox} width={width} height={size}>
70
- {types.map((t, i) => <image key={i} x={i * size} y={0} width={size} height={size} href={piecesetData[t]} />)}
69
+ <svg className="kokopu-chessPieceIcon" viewBox={viewBox} width={width} height={localSize}>
70
+ {types.map((t, i) => <image key={i} x={i * localSize} y={0} width={localSize} height={localSize} href={piecesetData[t]} />)}
71
71
  </svg>
72
72
  );
73
73
  }
@@ -242,7 +242,7 @@ export class NavigationBoard extends React.Component<NavigationBoardProps, Navig
242
242
  smallScreenLimits={this.props.smallScreenLimits}
243
243
  moveArrowVisible={this.props.moveArrowVisible}
244
244
  moveArrowColor={this.props.moveArrowColor}
245
- animated={this.props.animated}
245
+ animated={this.props.animated} sound={this.props.sound}
246
246
  bottomComponent={({ squareSize }) => this.renderToolbar(game, node, squareSize, isPlaying)}
247
247
  />
248
248
  );