kokopu-react 2.1.0 → 3.0.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 (140) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/lib/chessboard/Chessboard.d.ts +7 -2
  3. package/dist/lib/chessboard/Chessboard.js +44 -23
  4. package/dist/lib/chessboard/Chessboard.js.map +1 -1
  5. package/dist/lib/chessboard/ChessboardImpl.d.ts +2 -1
  6. package/dist/lib/chessboard/ChessboardImpl.js +9 -3
  7. package/dist/lib/chessboard/ChessboardImpl.js.map +1 -1
  8. package/dist/lib/chessboard/Draggable.d.ts +7 -1
  9. package/dist/lib/chessboard/Draggable.js +21 -29
  10. package/dist/lib/chessboard/Draggable.js.map +1 -1
  11. package/dist/lib/chessboard/colorsets.d.ts +4 -0
  12. package/dist/lib/chessboard/colorsets.js +4 -0
  13. package/dist/lib/chessboard/colorsets.js.map +1 -1
  14. package/dist/lib/chessboard/piecesets.d.ts +4 -0
  15. package/dist/lib/chessboard/piecesets.js +4 -0
  16. package/dist/lib/chessboard/piecesets.js.map +1 -1
  17. package/dist/lib/icons/ArrowMarkerIcon.js +1 -2
  18. package/dist/lib/icons/ArrowMarkerIcon.js.map +1 -1
  19. package/dist/lib/icons/ChessPieceIcon.d.ts +23 -0
  20. package/dist/lib/icons/ChessPieceIcon.js +65 -0
  21. package/dist/lib/icons/ChessPieceIcon.js.map +1 -0
  22. package/dist/lib/index.d.ts +2 -1
  23. package/dist/lib/index.js +2 -1
  24. package/dist/lib/index.js.map +1 -1
  25. package/dist/lib/movetext/Movetext.d.ts +1 -0
  26. package/dist/lib/movetext/Movetext.js +1 -2
  27. package/dist/lib/movetext/Movetext.js.map +1 -1
  28. package/dist/lib/movetext/MovetextImpl.d.ts +1 -0
  29. package/dist/lib/movetext/MovetextImpl.js +1 -1
  30. package/dist/lib/movetext/MovetextImpl.js.map +1 -1
  31. package/dist/lib/sanitization.d.ts +1 -0
  32. package/dist/lib/sanitization.js +11 -0
  33. package/dist/lib/sanitization.js.map +1 -1
  34. package/dist/lib/types.d.ts +11 -1
  35. package/dist/lib/types.js +7 -1
  36. package/dist/lib/types.js.map +1 -1
  37. package/doc_src/demo/PageChessboardBase.tsx +7 -0
  38. package/doc_src/demo/PageChessboardSmallScreens.tsx +11 -4
  39. package/doc_src/demo/PageMovetextBase.tsx +8 -0
  40. package/doc_src/examples/ChessPieceIcon.md +3 -0
  41. package/doc_src/home.md +14 -0
  42. package/doc_src/migrate_to_3.md +51 -0
  43. package/doc_src/webpack_configuration.md +27 -0
  44. package/package.json +18 -18
  45. package/scripts/doc.styleguide.config.js +14 -7
  46. package/scripts/docker-compose.yml +1 -1
  47. package/src/chessboard/Chessboard.tsx +56 -23
  48. package/src/chessboard/ChessboardImpl.tsx +10 -3
  49. package/src/chessboard/Draggable.tsx +22 -41
  50. package/src/chessboard/colorsets.ts +6 -0
  51. package/src/chessboard/piecesets.ts +6 -0
  52. package/src/icons/ChessPieceIcon.tsx +99 -0
  53. package/src/index.ts +4 -2
  54. package/src/movetext/Movetext.tsx +1 -0
  55. package/src/movetext/MovetextImpl.tsx +2 -0
  56. package/src/sanitization.ts +13 -0
  57. package/src/types.ts +17 -1
  58. package/test/02_chessboard_util.js +104 -36
  59. package/test/{04_marker_icons_graphic.js → 04_icons_graphic.js} +14 -2
  60. package/test/09_chessboard_small_screens.js +50 -0
  61. package/test/{09_movetext_graphic.js → 10_movetext_graphic.js} +5 -4
  62. package/test/{10_movetext_interaction.js → 11_movetext_interaction.js} +3 -3
  63. package/test/common/graphic.js +9 -0
  64. package/test/graphic_references/04_icons_graphic/chess_piece/customized_pieceset.png +0 -0
  65. package/test/graphic_references/04_icons_graphic/chess_piece/multiple_pieces.png +0 -0
  66. package/test/graphic_references/04_icons_graphic/chess_piece/single_piece.png +0 -0
  67. package/test/graphic_references/04_icons_graphic/marker/invalid_arrow_size.png +0 -0
  68. package/test/graphic_references/04_icons_graphic/marker/invalid_square_size.png +0 -0
  69. package/test/graphic_references/04_icons_graphic/marker/invalid_symbol.png +0 -0
  70. package/test/graphic_references/04_icons_graphic/marker/invalid_text_size.png +0 -0
  71. package/test/graphic_references/05_chessboard_graphic/base/from_fen.png +0 -0
  72. package/test/graphic_references/05_chessboard_graphic/flipped/default.png +0 -0
  73. package/test/graphic_references/06_chessboard_animation/base_1/capture.png +0 -0
  74. package/test/graphic_references/06_chessboard_animation/base_1/promotion.png +0 -0
  75. package/test/graphic_references/06_chessboard_animation/base_2/capture.png +0 -0
  76. package/test/graphic_references/06_chessboard_animation/base_2/promotion.png +0 -0
  77. package/test/graphic_references/06_chessboard_animation/base_3/capture.png +0 -0
  78. package/test/graphic_references/06_chessboard_animation/base_3/promotion.png +0 -0
  79. package/test/graphic_references/09_chessboard_small_screens/base/default.png +0 -0
  80. package/test/graphic_references/09_chessboard_small_screens/base/invalid_limits.png +0 -0
  81. package/test/graphic_references/09_chessboard_small_screens/base/width_690.png +0 -0
  82. package/test/graphic_references/09_chessboard_small_screens/base/width_700.png +0 -0
  83. package/test/graphic_references/09_chessboard_small_screens/base/width_710.png +0 -0
  84. package/test/graphic_references/09_chessboard_small_screens/base/width_720.png +0 -0
  85. package/test/graphic_references/09_chessboard_small_screens/base/width_730.png +0 -0
  86. package/test/graphic_references/09_chessboard_small_screens/base/width_740.png +0 -0
  87. package/test/graphic_references/09_chessboard_small_screens/base/width_750.png +0 -0
  88. package/test/graphic_references/10_movetext_graphic/options/other_diagram_options.png +0 -0
  89. package/test/graphic_test_app/04_icons_graphic/chess_piece.tsx +38 -0
  90. package/test/graphic_test_app/05_chessboard_graphic/base.tsx +2 -2
  91. package/test/graphic_test_app/05_chessboard_graphic/flipped.tsx +2 -2
  92. package/test/graphic_test_app/06_chessboard_animation/base_1.tsx +2 -2
  93. package/test/graphic_test_app/06_chessboard_animation/base_2.tsx +2 -2
  94. package/test/graphic_test_app/06_chessboard_animation/base_3.tsx +2 -2
  95. package/test/graphic_test_app/09_chessboard_small_screens/base.tsx +41 -0
  96. package/test/graphic_test_app/{09_movetext_graphic → 10_movetext_graphic}/options.tsx +1 -0
  97. /package/test/graphic_references/{04_marker_icons_graphic/base/invalid_symbol.png → 04_icons_graphic/chess_piece/invalid_multiple_pieces.png} +0 -0
  98. /package/test/graphic_references/{04_marker_icons_graphic/base/invalid_arrow_size.png → 04_icons_graphic/chess_piece/invalid_pieceset.png} +0 -0
  99. /package/test/graphic_references/{04_marker_icons_graphic/base/invalid_text_size.png → 04_icons_graphic/chess_piece/invalid_single_piece.png} +0 -0
  100. /package/test/graphic_references/{04_marker_icons_graphic/base/invalid_square_size.png → 04_icons_graphic/chess_piece/invalid_size.png} +0 -0
  101. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/colorized_arrow.png +0 -0
  102. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/colorized_square.png +0 -0
  103. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/colorized_symbol.png +0 -0
  104. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/colorized_text.png +0 -0
  105. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/default_arrow.png +0 -0
  106. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/default_square.png +0 -0
  107. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/default_text.png +0 -0
  108. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/wrapped_arrow.png +0 -0
  109. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/wrapped_square.png +0 -0
  110. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/wrapped_symbol.png +0 -0
  111. /package/test/graphic_references/{04_marker_icons_graphic/base → 04_icons_graphic/marker}/wrapped_text.png +0 -0
  112. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/base/game_1.png +0 -0
  113. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/base/game_2.png +0 -0
  114. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/base/game_3.png +0 -0
  115. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/base/game_4.png +0 -0
  116. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/error/pgn_parsing_error_1.png +0 -0
  117. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/error/pgn_parsing_error_2.png +0 -0
  118. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/error/wrong_game_index_1.png +0 -0
  119. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/error/wrong_game_index_2.png +0 -0
  120. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/error/wrong_game_index_type.png +0 -0
  121. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/error/wrong_game_type.png +0 -0
  122. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/html/filtered_tags_and_attributes.png +0 -0
  123. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/html/html_in_comments.png +0 -0
  124. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/html/html_in_headers.png +0 -0
  125. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/options/custom_piece_symbols.png +0 -0
  126. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/options/figurine_piece_symbols_and_diagram_options.png +0 -0
  127. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/options/hidden_diagrams.png +0 -0
  128. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/options/localized_piece_symbols.png +0 -0
  129. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/options/wrong_diagram_options_1.png +0 -0
  130. /package/test/graphic_references/{09_movetext_graphic → 10_movetext_graphic}/options/wrong_diagram_options_2.png +0 -0
  131. /package/test/graphic_references/{10_movetext_interaction → 11_movetext_interaction}/error/wrong_interaction_mode.png +0 -0
  132. /package/test/graphic_references/{10_movetext_interaction → 11_movetext_interaction}/select_moves/click_on_disabled.png +0 -0
  133. /package/test/graphic_references/{10_movetext_interaction → 11_movetext_interaction}/select_moves/click_on_unselected.png +0 -0
  134. /package/test/graphic_references/{10_movetext_interaction → 11_movetext_interaction}/select_moves/click_on_with_variations.png +0 -0
  135. /package/test/graphic_test_app/{04_marker_icons_graphic/base.tsx → 04_icons_graphic/marker.tsx} +0 -0
  136. /package/test/graphic_test_app/{09_movetext_graphic → 10_movetext_graphic}/base.tsx +0 -0
  137. /package/test/graphic_test_app/{09_movetext_graphic → 10_movetext_graphic}/error.tsx +0 -0
  138. /package/test/graphic_test_app/{09_movetext_graphic → 10_movetext_graphic}/html.tsx +0 -0
  139. /package/test/graphic_test_app/{10_movetext_interaction → 11_movetext_interaction}/error.tsx +0 -0
  140. /package/test/graphic_test_app/{10_movetext_interaction → 11_movetext_interaction}/select_moves.tsx +0 -0
@@ -0,0 +1,51 @@
1
+ Kokopu-React version 3.0.0 introduces some breaking changes with regard to the previous versions.
2
+ Those changes may require some fixes or adjustments in your codebase when upgrading Kokopu-React
3
+ from 2.x to 3.0.0 (or any subsequent version).
4
+
5
+ To upgrade from an older version, follow the [migration guide to 2.x](#/Migrate%20to%202.x) beforehand.
6
+
7
+
8
+
9
+ Methods `Chessboard.size(..)`
10
+ -----------------------------
11
+
12
+ As of 3.0.0, the method arguments `squareSize`, `coordinateVisible` and `smallScreenLimits`
13
+ must be passed as named arguments, as in the following example:
14
+
15
+ ```plain
16
+ const squareSize = 42;
17
+ const coordinateVisible = true;
18
+ const smallScreenLimits = [ /* whatever */ ];
19
+
20
+ const { width, height } = Chessboard.size({
21
+ squareSize: squareSize,
22
+ coordinateVisible: coordinateVisible,
23
+ smallScreenLimits: smallScreenLimits,
24
+ });
25
+
26
+ // Used to be, before 3.0.0:
27
+ // const { width, height } = Chessboard.size(squareSize, coordinateVisible, smallScreenLimits);
28
+ ```
29
+
30
+
31
+
32
+ Methods `Chessboard.adaptSquareSize(..)`
33
+ ----------------------------------------
34
+
35
+ As of 3.0.0, the method arguments `coordinateVisible` and `smallScreenLimits`
36
+ must be passed as named arguments, as in the following example:
37
+
38
+ ```plain
39
+ const width = 600;
40
+ const height = 400;
41
+ const coordinateVisible = true;
42
+ const smallScreenLimits = [ /* whatever */ ];
43
+
44
+ const squareSize = Chessboard.adaptSquareSize(width, height, {
45
+ coordinateVisible: coordinateVisible,
46
+ smallScreenLimits: smallScreenLimits,
47
+ });
48
+
49
+ // Used to be, before 3.0.0:
50
+ // const squareSize = Chessboard.adaptSquareSize(width, height, coordinateVisible, smallScreenLimits);
51
+ ```
@@ -0,0 +1,27 @@
1
+ Kokopu-React is not a "pure" JavaScript/TypeScript library, in that it is shipped with other type of files:
2
+ - `*.css` files (stylesheets),
3
+ - `*.png` files (images),
4
+ - `*.woff` and `*.woff2` files (fonts).
5
+
6
+ If you are using a bundler such as [Webpack](https://webpack.js.org/) to package an application that depends on Kokopu-React,
7
+ it must be configured so that these files are handled properly. An example of Webpack loading rules suitable for Kokopu-React is provided below:
8
+
9
+ ```plain
10
+ module.exports = {
11
+ // ...
12
+ module: {
13
+ rules: [
14
+ {
15
+ test: /\.css$/i,
16
+ use: [ 'style-loader', 'css-loader' ],
17
+ // ... or any other configuration that loads the matching files as global CSS.
18
+ },
19
+ {
20
+ test: /\.(png|woff|woff2)$/i,
21
+ type: 'asset/resource',
22
+ // ... or any other configuration that loads the matching files as resources.
23
+ },
24
+ ],
25
+ },
26
+ };
27
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kokopu-react",
3
- "version": "2.1.0",
3
+ "version": "3.0.1",
4
4
  "description": "A React-based library to create and display chessboard and chess-related components.",
5
5
  "keywords": [
6
6
  "chess",
@@ -42,23 +42,23 @@
42
42
  "htmlparser2": "^9.0.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@babel/preset-env": "^7.22.2",
46
- "@babel/preset-react": "^7.22.0",
47
- "@emotion/react": "^11.11.0",
45
+ "@babel/preset-env": "^7.22.9",
46
+ "@babel/preset-react": "^7.22.5",
47
+ "@emotion/react": "^11.11.1",
48
48
  "@emotion/styled": "^11.11.0",
49
- "@mui/material": "^5.13.2",
50
- "@types/react": "^18.2.7",
51
- "@types/react-dom": "^18.2.4",
52
- "@typescript-eslint/eslint-plugin": "^5.59.7",
53
- "@typescript-eslint/parser": "^5.59.7",
54
- "babel-loader": "^9.1.2",
49
+ "@mui/material": "^5.14.0",
50
+ "@types/react": "^18.2.15",
51
+ "@types/react-dom": "^18.2.7",
52
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
53
+ "@typescript-eslint/parser": "^6.0.0",
54
+ "babel-loader": "^9.1.3",
55
55
  "copy-webpack-plugin": "^11.0.0",
56
56
  "copyfiles": "^2.4.1",
57
57
  "coverage-istanbul-loader": "^3.0.5",
58
- "css-loader": "^6.7.4",
59
- "eslint": "^8.41.0",
58
+ "css-loader": "^6.8.1",
59
+ "eslint": "^8.44.0",
60
60
  "eslint-plugin-react": "^7.32.2",
61
- "html-webpack-plugin": "^5.5.1",
61
+ "html-webpack-plugin": "^5.5.3",
62
62
  "img-diff-js": "^0.5.2",
63
63
  "kokopu": "^4.7.0",
64
64
  "mocha": "^10.2.0",
@@ -69,14 +69,14 @@
69
69
  "react-dom": "^18.2.0",
70
70
  "react-styleguidist": "^13.1.1",
71
71
  "rimraf": "^5.0.1",
72
- "selenium-webdriver": "^4.9.2",
72
+ "selenium-webdriver": "^4.10.0",
73
73
  "ssh2-sftp-client": "^9.1.0",
74
74
  "style-loader": "^3.3.3",
75
- "ts-loader": "^9.4.3",
76
- "typescript": "^5.0.4",
75
+ "ts-loader": "^9.4.4",
76
+ "typescript": "^5.1.6",
77
77
  "unit.js": "^2.1.1",
78
- "webpack": "^5.84.1",
79
- "webpack-cli": "^5.1.1"
78
+ "webpack": "^5.88.1",
79
+ "webpack-cli": "^5.1.4"
80
80
  },
81
81
  "scripts": {
82
82
  "lint": "eslint doc_src src test",
@@ -37,7 +37,8 @@ fs.mkdirSync(path.resolve(__dirname, tmpDir), { recursive: true });
37
37
  // -------------------------------
38
38
 
39
39
  const componentSectionTitle = 'Components';
40
- const components = [ 'errorbox/ErrorBox', 'icons/SquareMarkerIcon', 'icons/TextMarkerIcon', 'icons/ArrowMarkerIcon', 'chessboard/Chessboard', 'movetext/Movetext' ];
40
+ const components = [ 'errorbox/ErrorBox', 'icons/SquareMarkerIcon', 'icons/TextMarkerIcon', 'icons/ArrowMarkerIcon', 'icons/ChessPieceIcon',
41
+ 'chessboard/Chessboard', 'movetext/Movetext' ];
41
42
 
42
43
 
43
44
  // Demo section configuration
@@ -59,13 +60,11 @@ const demoPages = [
59
60
 
60
61
  // Generate the Markdown file corresponding to each demo page.
61
62
  demoPages.forEach(demoPage => {
62
- const filename = `${tmpDir}/${demoPage.id}.md`;
63
- fs.writeFileSync(path.resolve(__dirname, filename),
63
+ fs.writeFileSync(path.resolve(__dirname, `${tmpDir}/${demoPage.id}.md`),
64
64
  '```js\n' +
65
65
  `<Page${demoPage.id} />\n` +
66
66
  '```\n'
67
67
  );
68
- demoPage.filename = filename;
69
68
  });
70
69
 
71
70
  // Generate the changelog page.
@@ -151,7 +150,7 @@ module.exports = {
151
150
  name: componentSectionTitle,
152
151
  content: componentsTocFilename,
153
152
  components: components.map(componentName => `${srcDir}/${componentName}.tsx`),
154
- sectionDepth: 1
153
+ sectionDepth: 1,
155
154
  },
156
155
  {
157
156
  name: demoSectionTitle,
@@ -160,11 +159,15 @@ module.exports = {
160
159
  sections: demoPages.map(demoPage => {
161
160
  return {
162
161
  name: demoPage.title,
163
- content: demoPage.filename,
164
- exampleMode: 'hide'
162
+ content: `${tmpDir}/${demoPage.id}.md`,
163
+ exampleMode: 'hide',
165
164
  };
166
165
  }),
167
166
  },
167
+ {
168
+ name: 'Webpack configuration',
169
+ content: `${docSrcDir}/webpack_configuration.md`,
170
+ },
168
171
  {
169
172
  name: 'Kokopu core library documentation',
170
173
  href: 'https://kokopu.yo35.org/',
@@ -177,6 +180,10 @@ module.exports = {
177
180
  name: 'Migrate to 2.x',
178
181
  content: `${docSrcDir}/migrate_to_2.md`,
179
182
  },
183
+ {
184
+ name: 'Migrate to 3.x',
185
+ content: `${docSrcDir}/migrate_to_3.md`,
186
+ },
180
187
  ],
181
188
  styleguideComponents: {
182
189
  LogoRenderer: path.resolve(__dirname, `${docSrcDir}/theming/LogoRenderer`),
@@ -2,7 +2,7 @@ version: "3.7"
2
2
 
3
3
  services:
4
4
  web-client:
5
- image: selenium/standalone-firefox:4.9.1-20230508 # https://github.com/SeleniumHQ/docker-selenium to get the latest version
5
+ image: selenium/standalone-firefox:4.10.0-20230607 # https://github.com/SeleniumHQ/docker-selenium to get the latest version
6
6
  shm_size: '2gb'
7
7
  ports:
8
8
  - 4444:4444 # http://localhost:4444 (driver)
@@ -26,12 +26,12 @@ import { exception as kokopuException, MoveDescriptor, Position, Square, GameVar
26
26
 
27
27
  import { IllegalArgument } from '../exception';
28
28
  import { i18n } from '../i18n';
29
- import { sanitizeString, sanitizeBoolean, sanitizeInteger, sanitizeBoundedInteger, sanitizeOptional } from '../sanitization';
29
+ import { sanitizeString, sanitizeBoolean, sanitizePartialObject, sanitizeInteger, sanitizeBoundedInteger, sanitizeOptional } from '../sanitization';
30
30
  import { MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, Colorset, Pieceset, AnnotationColor, AnnotationSymbol, SquareMarkerSet, TextMarkerSet, ArrowMarkerSet,
31
31
  isAnnotationColor, isAnnotationSymbol, parseSquareMarkers, parseTextMarkers, parseArrowMarkers } from '../types';
32
32
 
33
- import { colorsets } from './colorsets';
34
- import { piecesets } from './piecesets';
33
+ import { colorsets, DEFAULT_COLORSET } from './colorsets';
34
+ import { piecesets, DEFAULT_PIECESET } from './piecesets';
35
35
  import { ChessboardImpl, chessboardSize } from './ChessboardImpl';
36
36
  import { ErrorBox } from '../errorbox/ErrorBox';
37
37
 
@@ -43,6 +43,7 @@ export interface SmallScreenLimit {
43
43
  width: number;
44
44
  squareSize?: number;
45
45
  coordinateVisible?: boolean;
46
+ turnVisible?: boolean;
46
47
  }
47
48
 
48
49
 
@@ -98,6 +99,11 @@ export interface ChessboardProps {
98
99
  */
99
100
  coordinateVisible: boolean;
100
101
 
102
+ /**
103
+ * Whether the turn flag is visible or not.
104
+ */
105
+ turnVisible: boolean;
106
+
101
107
  /**
102
108
  * Whether moves are highlighted with an arrow or not.
103
109
  */
@@ -180,6 +186,9 @@ interface ChessboardState {
180
186
  }
181
187
 
182
188
 
189
+ const DEFAULT_SQUARE_SIZE = 40;
190
+
191
+
183
192
  /**
184
193
  * SVG image representing a chessboard diagram. Optionally, the user may interact with the board (move pieces, click on squares...).
185
194
  * Annotations such as square markers or arrows can also be added to the board.
@@ -189,13 +198,14 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
189
198
  static defaultProps: Partial<ChessboardProps> = {
190
199
  position: 'start',
191
200
  flipped: false,
192
- squareSize: 40,
201
+ squareSize: DEFAULT_SQUARE_SIZE,
193
202
  coordinateVisible: true,
203
+ turnVisible: true,
194
204
  moveArrowVisible: true,
195
205
  moveArrowColor: 'b',
196
206
  animated: false,
197
- colorset: 'original',
198
- pieceset: 'cburnett',
207
+ colorset: DEFAULT_COLORSET,
208
+ pieceset: DEFAULT_PIECESET,
199
209
  };
200
210
 
201
211
  private windowResizeListener = () => this.handleWindowResize();
@@ -242,6 +252,7 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
242
252
  const flipped = sanitizeBoolean(this.props.flipped);
243
253
  const squareSize = sanitizeBoundedInteger(this.props.squareSize, MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, () => new IllegalArgument('Chessboard', 'squareSize'));
244
254
  const coordinateVisible = sanitizeBoolean(this.props.coordinateVisible);
255
+ const turnVisible = sanitizeBoolean(this.props.turnVisible);
245
256
  const moveArrowVisible = sanitizeBoolean(this.props.moveArrowVisible);
246
257
  if (!isAnnotationColor(this.props.moveArrowColor)) {
247
258
  throw new IllegalArgument('Chessboard', 'moveArrowColor');
@@ -260,6 +271,7 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
260
271
  const smallScreenLimits = sanitizeSmallScreenLimits(this.props.smallScreenLimits, () => new IllegalArgument('Chessboard', 'smallScreenLimits'));
261
272
  const actualSquareSize = computeSquareSizeForSmallScreens(squareSize, smallScreenLimits, this.state.windowWidth);
262
273
  const actualCoordinateVisible = computeCoordinateVisibleForSmallScreens(coordinateVisible, smallScreenLimits, this.state.windowWidth);
274
+ const actualTurnVisible = computeTurnVisibleForSmallScreens(turnVisible, smallScreenLimits, this.state.windowWidth);
263
275
 
264
276
  // Validate the interaction attributes and the callbacks.
265
277
  const interactionMode = this.getInteractionModeAndValidateEditedArrowColor();
@@ -271,9 +283,10 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
271
283
 
272
284
  return (
273
285
  <ChessboardImpl key={key}
274
- position={position} move={move} squareMarkers={sqm} textMarkers={txtm} arrowMarkers={arm}
275
- flipped={flipped} squareSize={actualSquareSize} coordinateVisible={actualCoordinateVisible} moveArrowVisible={moveArrowVisible}
276
- moveArrowColor={this.props.moveArrowColor} animated={animated} colorset={colorset} pieceset={pieceset}
286
+ position={position} move={move} squareMarkers={sqm} textMarkers={txtm} arrowMarkers={arm} flipped={flipped}
287
+ squareSize={actualSquareSize} coordinateVisible={actualCoordinateVisible} turnVisible={actualTurnVisible}
288
+ moveArrowVisible={moveArrowVisible} moveArrowColor={this.props.moveArrowColor} animated={animated}
289
+ colorset={colorset} pieceset={pieceset}
277
290
  interactionMode={interactionMode} editedArrowColor={this.props.editedArrowColor}
278
291
  onPieceMoved={onPieceMoved} onSquareClicked={onSquareClicked} onArrowEdited={onArrowEdited} onMovePlayed={onMovePlayed}
279
292
  />
@@ -299,43 +312,50 @@ export class Chessboard extends React.Component<ChessboardProps, ChessboardState
299
312
  /**
300
313
  * Return the size of the chessboard, assuming it is built with the given attributes.
301
314
  */
302
- static size(squareSize: number, coordinateVisible: boolean, smallScreenLimits?: SmallScreenLimit[]): { width: number, height: number } {
315
+ static size(attr?: Partial<Pick<ChessboardProps, 'squareSize' | 'coordinateVisible' | 'turnVisible' | 'smallScreenLimits'>>): { width: number, height: number } {
316
+ let { squareSize, coordinateVisible, turnVisible, smallScreenLimits } = sanitizePartialObject(attr, () => new IllegalArgument('Chessboard.size()', 'attr'));
303
317
 
304
318
  // Sanitize the arguments.
305
- squareSize = sanitizeBoundedInteger(squareSize, MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, () => new IllegalArgument('Chessboard.size()', 'squareSize'));
306
- coordinateVisible = sanitizeBoolean(coordinateVisible);
307
- const limits = sanitizeSmallScreenLimits(smallScreenLimits, () => new IllegalArgument('Chessboard.size()', 'smallScreenLimits'));
319
+ squareSize = squareSize === undefined ? DEFAULT_SQUARE_SIZE :
320
+ sanitizeBoundedInteger(squareSize, MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, () => new IllegalArgument('Chessboard.size()', 'squareSize'));
321
+ coordinateVisible = coordinateVisible === undefined ? true : sanitizeBoolean(coordinateVisible);
322
+ turnVisible = turnVisible === undefined ? true : sanitizeBoolean(turnVisible);
323
+ smallScreenLimits = sanitizeSmallScreenLimits(smallScreenLimits, () => new IllegalArgument('Chessboard.size()', 'smallScreenLimits'));
308
324
 
309
325
  // Enforce small-screen limits, if any.
310
326
  if (typeof window !== 'undefined') {
311
- squareSize = computeSquareSizeForSmallScreens(squareSize, limits, window.innerWidth);
312
- coordinateVisible = computeCoordinateVisibleForSmallScreens(coordinateVisible, limits, window.innerWidth);
327
+ squareSize = computeSquareSizeForSmallScreens(squareSize, smallScreenLimits, window.innerWidth);
328
+ coordinateVisible = computeCoordinateVisibleForSmallScreens(coordinateVisible, smallScreenLimits, window.innerWidth);
329
+ turnVisible = computeTurnVisibleForSmallScreens(turnVisible, smallScreenLimits, window.innerWidth);
313
330
  }
314
331
 
315
332
  // Compute the dimensions.
316
- return chessboardSize(squareSize, coordinateVisible);
333
+ return chessboardSize(squareSize, coordinateVisible, turnVisible);
317
334
  }
318
335
 
319
336
  /**
320
337
  * Return the maximum square size that would allow the chessboard to fit in a rectangle of size `width x height`.
321
338
  */
322
- static adaptSquareSize(width: number, height: number, coordinateVisible: boolean, smallScreenLimits?: SmallScreenLimit[]): number {
339
+ static adaptSquareSize(width: number, height: number, attr?: Partial<Pick<ChessboardProps, 'coordinateVisible' | 'turnVisible' | 'smallScreenLimits'>>): number {
340
+ let { coordinateVisible, turnVisible, smallScreenLimits } = sanitizePartialObject(attr, () => new IllegalArgument('Chessboard.size()', 'attr'));
323
341
 
324
342
  // Sanitize the arguments.
325
343
  width = sanitizeInteger(width, () => new IllegalArgument('Chessboard.adaptSquareSize()', 'width'));
326
344
  height = sanitizeInteger(height, () => new IllegalArgument('Chessboard.adaptSquareSize()', 'height'));
327
- coordinateVisible = sanitizeBoolean(coordinateVisible);
328
- const limits = sanitizeSmallScreenLimits(smallScreenLimits, () => new IllegalArgument('Chessboard.adaptSquareSize()', 'smallScreenLimits'));
345
+ coordinateVisible = coordinateVisible === undefined ? true : sanitizeBoolean(coordinateVisible);
346
+ turnVisible = turnVisible === undefined ? true : sanitizeBoolean(turnVisible);
347
+ smallScreenLimits = sanitizeSmallScreenLimits(smallScreenLimits, () => new IllegalArgument('Chessboard.adaptSquareSize()', 'smallScreenLimits'));
329
348
 
330
349
  // Enforce small-screen limits, if any.
331
350
  let maxSquareSize = MAX_SQUARE_SIZE;
332
351
  if (typeof window !== 'undefined') {
333
- maxSquareSize = computeSquareSizeForSmallScreens(maxSquareSize, limits, window.innerWidth);
334
- coordinateVisible = computeCoordinateVisibleForSmallScreens(coordinateVisible, limits, window.innerWidth);
352
+ maxSquareSize = computeSquareSizeForSmallScreens(maxSquareSize, smallScreenLimits, window.innerWidth);
353
+ coordinateVisible = computeCoordinateVisibleForSmallScreens(coordinateVisible, smallScreenLimits, window.innerWidth);
354
+ turnVisible = computeTurnVisibleForSmallScreens(turnVisible, smallScreenLimits, window.innerWidth);
335
355
  }
336
356
 
337
357
  function isAdapted(squareSize: number) {
338
- const { width: actualWidth, height: actualHeight } = chessboardSize(squareSize, coordinateVisible);
358
+ const { width: actualWidth, height: actualHeight } = chessboardSize(squareSize, coordinateVisible!, turnVisible!);
339
359
  return actualWidth <= width && actualHeight <= height;
340
360
  }
341
361
 
@@ -410,7 +430,7 @@ function computeSquareSizeForSmallScreens(squareSize: number, smallScreenLimits:
410
430
 
411
431
 
412
432
  /**
413
- * Compute the actual square size taking into account the given small-screen limits and window width.
433
+ * Compute the actual coordinate visibility attribute taking into account the given small-screen limits and window width.
414
434
  */
415
435
  function computeCoordinateVisibleForSmallScreens(coordinateVisible: boolean, smallScreenLimits: SmallScreenLimit[], windowWidth: number) {
416
436
  const maximumCoordinateVisible = computeSmallScreenLimits(smallScreenLimits, windowWidth, limit => limit.coordinateVisible);
@@ -418,6 +438,15 @@ function computeCoordinateVisibleForSmallScreens(coordinateVisible: boolean, sma
418
438
  }
419
439
 
420
440
 
441
+ /**
442
+ * Compute the actual turn flag visibility attribute taking into account the given small-screen limits and window width.
443
+ */
444
+ function computeTurnVisibleForSmallScreens(turnVisible: boolean, smallScreenLimits: SmallScreenLimit[], windowWidth: number) {
445
+ const maximumTurnVisible = computeSmallScreenLimits(smallScreenLimits, windowWidth, limit => limit.turnVisible);
446
+ return maximumTurnVisible === undefined ? turnVisible : turnVisible && maximumTurnVisible;
447
+ }
448
+
449
+
421
450
  /**
422
451
  * Compute the limit applicable to the given window width.
423
452
  */
@@ -565,10 +594,14 @@ function sanitizeSmallScreenLimits(smallScreenLimits: SmallScreenLimit[] | undef
565
594
  }
566
595
  else {
567
596
  return smallScreenLimits.map(smallScreenLimit => {
597
+ if (typeof smallScreenLimit !== 'object' || smallScreenLimit === null) {
598
+ throw exceptionBuilder();
599
+ }
568
600
  return {
569
601
  width: sanitizeInteger(smallScreenLimit.width, exceptionBuilder),
570
602
  squareSize: sanitizeOptional(smallScreenLimit.squareSize, val => sanitizeBoundedInteger(val, MIN_SQUARE_SIZE, MAX_SQUARE_SIZE, exceptionBuilder)),
571
603
  coordinateVisible: sanitizeOptional(smallScreenLimit.coordinateVisible, sanitizeBoolean),
604
+ turnVisible: sanitizeOptional(smallScreenLimit.turnVisible, sanitizeBoolean)
572
605
  };
573
606
  });
574
607
  }
@@ -60,6 +60,7 @@ interface ChessboardImplProps {
60
60
  flipped: boolean;
61
61
  squareSize: number;
62
62
  coordinateVisible: boolean;
63
+ turnVisible: boolean;
63
64
  moveArrowVisible: boolean;
64
65
  moveArrowColor: AnnotationColor;
65
66
  animated: boolean;
@@ -127,7 +128,7 @@ export class ChessboardImpl extends React.Component<ChessboardImplProps, Chessbo
127
128
  // Render the whole canvas.
128
129
  const xmin = Math.round(-RANK_COORDINATE_WIDTH_FACTOR * fontSize);
129
130
  const ymin = 0;
130
- const xmax = 9 * this.props.squareSize + Math.round(TURN_FLAG_SPACING_FACTOR * this.props.squareSize);
131
+ const xmax = this.props.turnVisible ? 9 * this.props.squareSize + Math.round(TURN_FLAG_SPACING_FACTOR * this.props.squareSize) : 8 * this.props.squareSize;
131
132
  const ymax = 8 * this.props.squareSize + Math.round(FILE_COORDINATE_HEIGHT_FACTOR * fontSize);
132
133
  const viewBox = `${xmin} ${ymin} ${xmax - xmin} ${ymax - ymin}`;
133
134
  return (
@@ -433,6 +434,9 @@ export class ChessboardImpl extends React.Component<ChessboardImplProps, Chessbo
433
434
  }
434
435
 
435
436
  private renderTurnFlag(turn: Color) {
437
+ if (!this.props.turnVisible) {
438
+ return undefined;
439
+ }
436
440
  const x = 8 * this.props.squareSize + Math.round(TURN_FLAG_SPACING_FACTOR * this.props.squareSize);
437
441
  const y = turn === (this.props.flipped ? 'b' : 'w') ? 7 * this.props.squareSize : 0;
438
442
  return <image key={'turn-' + turn} x={x} y={y} width={this.props.squareSize} height={this.props.squareSize} href={this.getURLForTurnFlag(turn)} />;
@@ -612,9 +616,12 @@ export class ChessboardImpl extends React.Component<ChessboardImplProps, Chessbo
612
616
  /**
613
617
  * Return the size of the chessboard, assuming it is built with the given attributes.
614
618
  */
615
- export function chessboardSize(squareSize: number, coordinateVisible: boolean) {
616
- let width = 9 * squareSize + Math.round(TURN_FLAG_SPACING_FACTOR * squareSize);
619
+ export function chessboardSize(squareSize: number, coordinateVisible: boolean, turnVisible: boolean) {
620
+ let width = 8 * squareSize;
617
621
  let height = 8 * squareSize;
622
+ if (turnVisible) {
623
+ width += squareSize + Math.round(TURN_FLAG_SPACING_FACTOR * squareSize);
624
+ }
618
625
  if (coordinateVisible) {
619
626
  const fontSize = computeCoordinateFontSize(squareSize);
620
627
  width += Math.round(RANK_COORDINATE_WIDTH_FACTOR * fontSize);
@@ -72,17 +72,6 @@ interface DraggableProps {
72
72
  }
73
73
 
74
74
 
75
- interface InnerListeners {
76
- mouseDown: (evt: MouseEvent) => void;
77
- mouseMove: (evt: MouseEvent) => void;
78
- mouseUp: (evt: MouseEvent) => void;
79
- touchStart: (evt: TouchEvent) => void;
80
- touchMove: (evt: TouchEvent) => void;
81
- touchEnd: (evt: TouchEvent) => void;
82
- touchCancel: (evt: TouchEvent) => void;
83
- }
84
-
85
-
86
75
  interface DragData {
87
76
  originX: number;
88
77
  originY: number;
@@ -107,42 +96,34 @@ interface ClientCoordinates {
107
96
  export class Draggable extends React.Component<DraggableProps> {
108
97
 
109
98
  private innerRef: React.RefObject<SVGRectElement> = React.createRef();
110
- private innerListeners?: InnerListeners;
111
99
  private dragData?: DragData;
112
100
 
101
+ private mouseDownListener = (evt: MouseEvent) => this.handleMouseDown(evt);
102
+ private mouseMoveListener = (evt: MouseEvent) => this.handleMouseMove(evt);
103
+ private mouseUpListener = (evt: MouseEvent) => this.handleMouseUp(evt);
104
+ private touchStartListener = (evt: TouchEvent) => this.handleTouchStart(evt);
105
+ private touchMoveListener = (evt: TouchEvent) => this.handleTouchMove(evt);
106
+ private touchEndListener = (evt: TouchEvent) => this.handleTouchEnd(evt);
107
+ private touchCancelListener = (evt: TouchEvent) => this.handleTouchCancel(evt);
108
+
113
109
  componentDidMount() {
114
- if (!this.innerListeners) {
115
- this.innerListeners = {
116
- mouseDown: evt => this.handleMouseDown(evt),
117
- mouseMove: evt => this.handleMouseMove(evt),
118
- mouseUp: evt => this.handleMouseUp(evt),
119
- touchStart: evt => this.handleTouchStart(evt),
120
- touchMove: evt => this.handleTouchMove(evt),
121
- touchEnd: evt => this.handleTouchEnd(evt),
122
- touchCancel: evt => this.handleTouchCancel(evt),
123
- };
124
- this.innerRef.current!.addEventListener('mousedown', this.innerListeners.mouseDown);
125
- window.addEventListener('mousemove', this.innerListeners.mouseMove);
126
- window.addEventListener('mouseup', this.innerListeners.mouseUp);
127
- this.innerRef.current!.addEventListener('touchstart', this.innerListeners.touchStart, { passive: false });
128
- window.addEventListener('touchmove', this.innerListeners.touchMove, { passive: false });
129
- window.addEventListener('touchend', this.innerListeners.touchEnd);
130
- window.addEventListener('touchcancel', this.innerListeners.touchCancel);
131
- }
132
- this.dragData = undefined;
110
+ this.innerRef.current!.addEventListener('mousedown', this.mouseDownListener);
111
+ window.addEventListener('mousemove', this.mouseMoveListener);
112
+ window.addEventListener('mouseup', this.mouseUpListener);
113
+ this.innerRef.current!.addEventListener('touchstart', this.touchStartListener, { passive: false });
114
+ window.addEventListener('touchmove', this.touchMoveListener, { passive: false });
115
+ window.addEventListener('touchend', this.touchEndListener);
116
+ window.addEventListener('touchcancel', this.touchCancelListener);
133
117
  }
134
118
 
135
119
  componentWillUnmount() {
136
- if (this.innerListeners) {
137
- this.innerRef.current!.removeEventListener('mousedown', this.innerListeners.mouseDown);
138
- window.removeEventListener('mousemove', this.innerListeners.mouseMove);
139
- window.removeEventListener('mouseup', this.innerListeners.mouseUp);
140
- this.innerRef.current!.removeEventListener('touchstart', this.innerListeners.touchStart);
141
- window.removeEventListener('touchmove', this.innerListeners.touchMove);
142
- window.removeEventListener('touchend', this.innerListeners.touchEnd);
143
- window.removeEventListener('touchcancel', this.innerListeners.touchCancel);
144
- this.innerListeners = undefined;
145
- }
120
+ this.innerRef.current!.removeEventListener('mousedown', this.mouseDownListener);
121
+ window.removeEventListener('mousemove', this.mouseMoveListener);
122
+ window.removeEventListener('mouseup', this.mouseUpListener);
123
+ this.innerRef.current!.removeEventListener('touchstart', this.touchStartListener);
124
+ window.removeEventListener('touchmove', this.touchMoveListener);
125
+ window.removeEventListener('touchend', this.touchEndListener);
126
+ window.removeEventListener('touchcancel', this.touchCancelListener);
146
127
  }
147
128
 
148
129
  render() {
@@ -23,6 +23,12 @@
23
23
  import { Colorset } from '../types';
24
24
 
25
25
 
26
+ /**
27
+ * Default colorset.
28
+ */
29
+ export const DEFAULT_COLORSET = 'original';
30
+
31
+
26
32
  /**
27
33
  * Available colorsets.
28
34
  */
@@ -121,6 +121,12 @@ import skulls_bx from './sprites/skulls-bx.png';
121
121
  import skulls_wx from './sprites/skulls-wx.png';
122
122
 
123
123
 
124
+ /**
125
+ * Default pieceset.
126
+ */
127
+ export const DEFAULT_PIECESET = 'cburnett';
128
+
129
+
124
130
  /**
125
131
  * Available piecesets.
126
132
  */