kokopu-react 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/dist/lib/Chessboard.js +3 -5
  4. package/dist/lib/ErrorBox.js +84 -4
  5. package/dist/lib/Movetext.js +839 -0
  6. package/dist/lib/css/chessboard.css +5 -0
  7. package/dist/lib/css/error_box.css +12 -1
  8. package/dist/lib/css/{label.css → fonts.css} +10 -5
  9. package/dist/lib/css/movetext.css +117 -0
  10. package/dist/lib/css/symbol.css +8 -0
  11. package/dist/lib/fonts/chessalpha.woff +0 -0
  12. package/dist/lib/fonts/chessalpha.woff2 +0 -0
  13. package/dist/lib/i18n.js +16 -1
  14. package/dist/lib/impl/HtmlSanitizer.js +171 -0
  15. package/dist/lib/impl/TextSymbol.js +1 -3
  16. package/dist/lib/index.js +8 -0
  17. package/doc_src/demo/{PageChessboardEdition.js → PageChessboardInteraction.js} +1 -3
  18. package/doc_src/demo/PageMovetextBase.js +214 -0
  19. package/doc_src/demo/PageMovetextInteraction.js +155 -0
  20. package/doc_src/demo/demo.css +7 -0
  21. package/doc_src/demo/game-1.pgn +23 -0
  22. package/doc_src/demo/game-2.pgn +22 -0
  23. package/doc_src/demo/game-invalid.pgn +10 -0
  24. package/doc_src/examples/Movetext.md +15 -0
  25. package/doc_src/home.md +2 -0
  26. package/graphic_test_app/12_movetext_simple.js +41 -0
  27. package/graphic_test_app/13_movetext_error.js +38 -0
  28. package/graphic_test_app/14_movetext_html.js +40 -0
  29. package/graphic_test_app/15_movetext_options.js +42 -0
  30. package/graphic_test_app/16_movetext_interaction.js +42 -0
  31. package/graphic_test_app/common/dummy.pgn +8 -0
  32. package/graphic_test_app/common/games.pgn +167 -0
  33. package/graphic_test_app/common/heartbeat.txt +1 -0
  34. package/graphic_test_app/common/test_app.css +19 -3
  35. package/graphic_test_app/common/test_app.js +5 -5
  36. package/package.json +22 -21
  37. package/scripts/test_graphic/docker-compose.yml +1 -1
  38. package/scripts/test_graphic/webpack-config.js +6 -2
  39. package/scripts/test_headless.webpack-config.js +1 -1
  40. package/src/Chessboard.js +3 -4
  41. package/src/ErrorBox.js +79 -0
  42. package/src/Movetext.js +616 -0
  43. package/src/css/chessboard.css +5 -0
  44. package/src/css/error_box.css +12 -1
  45. package/src/css/{label.css → fonts.css} +10 -5
  46. package/src/css/movetext.css +117 -0
  47. package/src/css/symbol.css +8 -0
  48. package/src/fonts/chessalpha.woff +0 -0
  49. package/src/fonts/chessalpha.woff2 +0 -0
  50. package/src/i18n.js +11 -0
  51. package/src/impl/HtmlSanitizer.js +120 -0
  52. package/src/impl/TextSymbol.js +1 -2
  53. package/src/index.js +1 -0
  54. package/test/7_movetext_graphic.js +43 -0
  55. package/test/8_movetext_interaction.js +99 -0
  56. package/test/common/graphic.js +1 -1
  57. package/test/references/05_chessboard_move/3.png +0 -0
  58. package/test/references/12_movetext_simple/0.png +0 -0
  59. package/test/references/12_movetext_simple/1.png +0 -0
  60. package/test/references/12_movetext_simple/2.png +0 -0
  61. package/test/references/12_movetext_simple/3.png +0 -0
  62. package/test/references/13_movetext_error/0.png +0 -0
  63. package/test/references/13_movetext_error/1.png +0 -0
  64. package/test/references/13_movetext_error/2.png +0 -0
  65. package/test/references/13_movetext_error/3.png +0 -0
  66. package/test/references/14_movetext_html/0.png +0 -0
  67. package/test/references/14_movetext_html/1.png +0 -0
  68. package/test/references/14_movetext_html/2.png +0 -0
  69. package/test/references/15_movetext_options/0.png +0 -0
  70. package/test/references/15_movetext_options/1.png +0 -0
  71. package/test/references/15_movetext_options/2.png +0 -0
  72. package/test/references/15_movetext_options/3.png +0 -0
  73. package/test/references/16_movetext_interaction/0.png +0 -0
  74. package/test/references/16_movetext_interaction/1.png +0 -0
  75. package/test/references/16_movetext_interaction/2.png +0 -0
  76. package/graphic_test_app/common/healthcheck.txt +0 -1
@@ -22,6 +22,11 @@
22
22
 
23
23
  .kokopu-coordinate {
24
24
  fill: currentcolor;
25
+ font-style: normal;
26
+ font-family: sans-serif;
27
+ text-anchor: middle;
28
+ dominant-baseline: central;
29
+ letter-spacing: 0px;
25
30
  }
26
31
 
27
32
  .kokopu-annotation {
@@ -36,6 +36,17 @@
36
36
  font-style: italic;
37
37
  }
38
38
 
39
- .kokopu-errorMessage {
39
+ .kokopu-errorMessage,
40
+ .kokopu-errorExcerpt {
40
41
  margin-top: 0.4em;
41
42
  }
43
+
44
+ .kokopu-errorExcerpt {
45
+ white-space: pre-wrap;
46
+ font-family: monospace;
47
+ line-height: 1.5em;
48
+ color: #400;
49
+ background-color: #ffd;
50
+ border: solid 1px #aa0;
51
+ padding: 0.75em 1em 0.15em;
52
+ }
@@ -20,10 +20,15 @@
20
20
  ******************************************************************************/
21
21
 
22
22
 
23
- .kokopu-label {
23
+ @font-face {
24
+ font-family: 'Chess Alpha';
25
+ src:
26
+ url('../fonts/chessalpha.woff2') format('woff2'),
27
+ url('../fonts/chessalpha.woff') format('woff');
28
+ font-weight: normal;
24
29
  font-style: normal;
25
- font-family: sans-serif;
26
- text-anchor: middle;
27
- dominant-baseline: central;
28
- letter-spacing: 0px;
30
+ }
31
+
32
+ .kokopu-font-alpha {
33
+ font-family: 'Chess Alpha', sans-serif;
29
34
  }
@@ -0,0 +1,117 @@
1
+ /******************************************************************************
2
+ * *
3
+ * This file is part of Kokopu-React, a JavaScript chess library. *
4
+ * Copyright (C) 2021-2022 Yoann Le Montagner <yo35 -at- melix.net> *
5
+ * *
6
+ * This program is free software: you can redistribute it and/or *
7
+ * modify it under the terms of the GNU Lesser General Public License *
8
+ * as published by the Free Software Foundation, either version 3 of *
9
+ * the License, or (at your option) any later version. *
10
+ * *
11
+ * This program is distributed in the hope that it will be useful, *
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
+ * GNU Lesser General Public License for more details. *
15
+ * *
16
+ * You should have received a copy of the GNU Lesser General *
17
+ * Public License along with this program. If not, see *
18
+ * <http://www.gnu.org/licenses/>. *
19
+ * *
20
+ ******************************************************************************/
21
+
22
+
23
+ .kokopu-colorTag {
24
+ width : 1.2em;
25
+ height: 1.2em;
26
+ vertical-align: middle;
27
+ display: inline-block;
28
+ border: 1px solid black;
29
+ margin-right: 0.25em;
30
+ }
31
+
32
+ .kokopu-headerGroup-whitePlayer .kokopu-colorTag {
33
+ background-color: white;
34
+ }
35
+
36
+ .kokopu-headerGroup-blackPlayer .kokopu-colorTag {
37
+ background-color: black;
38
+ }
39
+
40
+ .kokopu-headerGroup-titleRating:before,
41
+ .kokopu-header-round:before {
42
+ content: "\a0("; /* \a0 == &nbsp; */
43
+ }
44
+
45
+ .kokopu-headerGroup-titleRating:after,
46
+ .kokopu-header-round:after {
47
+ content: ")";
48
+ }
49
+
50
+ .kokopu-header-round,
51
+ .kokopu-header-annotator {
52
+ font-style: italic;
53
+ }
54
+
55
+ * + .kokopu-move:before,
56
+ * + .kokopu-gameResult:before,
57
+ * + span.kokopu-comment:before {
58
+ content: " ";
59
+ }
60
+
61
+ span.kokopu-variation:before {
62
+ content: " (";
63
+ }
64
+
65
+ span.kokopu-variation:after {
66
+ content: ")";
67
+ }
68
+
69
+ * + .kokopu-nag:before {
70
+ content: "\a0"; /* \a0 == &nbsp; */
71
+ }
72
+
73
+ div.kokopu-subVariation {
74
+ margin-left: 1.5em;
75
+ }
76
+
77
+ .kokopu-move,
78
+ .kokopu-gameResult {
79
+ font-weight: bold;
80
+ }
81
+
82
+ .kokopu-subVariation .kokopu-move {
83
+ font-weight: inherit;
84
+ }
85
+
86
+ .kokopu-diagram {
87
+ text-align: center;
88
+ line-height: 0px;
89
+ }
90
+
91
+ .kokopu-moveContent {
92
+ display: inline-block;
93
+ padding-left: 1px;
94
+ padding-right: 1px;
95
+ }
96
+
97
+ .kokopu-selectedMove .kokopu-moveContent {
98
+ background-color: currentcolor;
99
+ }
100
+
101
+ .kokopu-selectedMove .kokopu-moveContent > * {
102
+ filter: invert(1);
103
+ }
104
+
105
+ .kokopu-clickableMove .kokopu-moveContent:hover {
106
+ text-decoration: underline;
107
+ cursor: pointer;
108
+ }
109
+
110
+ .kokopu-focusFieldContainer {
111
+ position: fixed;
112
+ bottom: 0px;
113
+ right: 0px;
114
+ width: 0px;
115
+ height: 0px;
116
+ overflow: hidden;
117
+ }
@@ -23,3 +23,11 @@
23
23
  .kokopu-symbolCircle {
24
24
  fill: transparent;
25
25
  }
26
+
27
+ .kokopu-symbolText {
28
+ font-style: normal;
29
+ font-family: sans-serif;
30
+ text-anchor: middle;
31
+ dominant-baseline: central;
32
+ letter-spacing: 0px;
33
+ }
Binary file
Binary file
package/dist/lib/i18n.js CHANGED
@@ -26,10 +26,25 @@ exports["default"] = void 0;
26
26
  * *
27
27
  ******************************************************************************/
28
28
  var i18n = {
29
+ // Error box
30
+ LINE: 'line {0}',
31
+ // Chessboard
29
32
  INVALID_FEN_ERROR_TITLE: 'Invalid FEN string.',
30
33
  INVALID_NOTATION_ERROR_TITLE: 'Invalid move notation.',
31
34
  INVALID_POSITION_ATTRIBUTE_ERROR_MESSAGE: 'Invalid "position" attribute.',
32
- INVALID_MOVE_ATTRIBUTE_ERROR_MESSAGE: 'Invalid "move" attribute.'
35
+ INVALID_MOVE_ATTRIBUTE_ERROR_MESSAGE: 'Invalid "move" attribute.',
36
+ // Movetext
37
+ PIECE_SYMBOLS: {
38
+ 'K': 'K',
39
+ 'Q': 'Q',
40
+ 'R': 'R',
41
+ 'B': 'B',
42
+ 'N': 'N',
43
+ 'P': 'P'
44
+ },
45
+ ANNOTATED_BY: 'Annotated by {0}',
46
+ INVALID_PGN_ERROR_TITLE: 'Invalid PGN string.',
47
+ INVALID_GAME_ATTRIBUTE_ERROR_MESSAGE: 'Invalid "game" attribute.'
33
48
  };
34
49
  var _default = i18n;
35
50
  exports["default"] = _default;
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports["default"] = void 0;
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var htmlparser2 = _interopRequireWildcard(require("htmlparser2"));
13
+
14
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
+
16
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
+
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
19
+
20
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
21
+
22
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
23
+
24
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
25
+
26
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
27
+
28
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
29
+
30
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
31
+
32
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
33
+
34
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
35
+
36
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
37
+
38
+ /**
39
+ * HTML sanitizer: parse HTML string, keeping only the allowed HTML nodes, and the corresponding React object.
40
+ */
41
+ var HtmlSanitizer = /*#__PURE__*/function () {
42
+ function HtmlSanitizer(_ref) {
43
+ var allowedTags = _ref.allowedTags,
44
+ allowedAttributes = _ref.allowedAttributes;
45
+
46
+ _classCallCheck(this, HtmlSanitizer);
47
+
48
+ this._allowedTags = allowedTags ? new Set(allowedTags) : new Set();
49
+ this._allowedAttributes = allowedAttributes ? new Map(Object.entries(allowedAttributes).map(function (_ref2) {
50
+ var _ref3 = _slicedToArray(_ref2, 2),
51
+ k = _ref3[0],
52
+ v = _ref3[1];
53
+
54
+ return [k, new Set(v)];
55
+ })) : new Map();
56
+ this._keyCounter = 0;
57
+ }
58
+ /**
59
+ * @param {string} text HTML string to be parsed.
60
+ * @returns {*} React object corresponding to the given HTML string. Can also be a fragment, or just a simple string if the given HTML string
61
+ * does not contain any HTML tag.
62
+ */
63
+
64
+
65
+ _createClass(HtmlSanitizer, [{
66
+ key: "parse",
67
+ value: function parse(text) {
68
+ var _this = this;
69
+
70
+ var dom = htmlparser2.parseDocument(text); // Ensure that the returned object is a non-empty node type.
71
+
72
+ if (dom.type !== 'root') {
73
+ return undefined;
74
+ } // Process the children of the root node.
75
+
76
+
77
+ var result = [];
78
+ dom.children.forEach(function (child) {
79
+ var childElement = _this._processNode(child);
80
+
81
+ if (childElement) {
82
+ result.push(childElement);
83
+ }
84
+ }); // Return the result.
85
+
86
+ if (result.length === 0) {
87
+ return undefined;
88
+ } else if (result.length === 1) {
89
+ return result[0];
90
+ } else {
91
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, {
92
+ key: this._allocateKey()
93
+ }, result);
94
+ }
95
+ }
96
+ }, {
97
+ key: "_processNode",
98
+ value: function _processNode(node) {
99
+ var _this2 = this;
100
+
101
+ if (node.type === 'text') {
102
+ return node.data;
103
+ } else if (node.type === 'tag') {
104
+ var children = node.children.map(function (child) {
105
+ return _this2._processNode(child);
106
+ });
107
+
108
+ if (this._allowedTags.has(node.name)) {
109
+ var attributes = this._filterAttributes(node.name, node.attribs);
110
+
111
+ attributes['key'] = this._allocateKey();
112
+ return /*#__PURE__*/_react["default"].createElement(node.name, attributes, children);
113
+ } else {
114
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, {
115
+ key: this._allocateKey()
116
+ }, children);
117
+ }
118
+ } else {
119
+ return undefined;
120
+ }
121
+ }
122
+ }, {
123
+ key: "_filterAttributes",
124
+ value: function _filterAttributes(nodeName, attributes) {
125
+ var result = {};
126
+
127
+ for (var _i2 = 0, _Object$entries = Object.entries(attributes); _i2 < _Object$entries.length; _i2++) {
128
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2),
129
+ attribute = _Object$entries$_i[0],
130
+ value = _Object$entries$_i[1];
131
+
132
+ if (this._isAttributeAllowed(nodeName, attribute)) {
133
+ result[attribute === 'class' ? 'className' : attribute] = value;
134
+ }
135
+ }
136
+
137
+ return result;
138
+ }
139
+ }, {
140
+ key: "_isAttributeAllowed",
141
+ value: function _isAttributeAllowed(nodeName, attribute) {
142
+ if (attribute === 'key' && attribute === 'ref' && attribute === 'className') {
143
+ // Those attributes are always forbidden.
144
+ return false;
145
+ }
146
+
147
+ var allowedAttributesForCurrent = this._allowedAttributes.get(nodeName);
148
+
149
+ if (allowedAttributesForCurrent && allowedAttributesForCurrent.has(attribute)) {
150
+ return true;
151
+ }
152
+
153
+ var allowedAttributesForAll = this._allowedAttributes.get('*');
154
+
155
+ if (allowedAttributesForAll && allowedAttributesForAll.has(attribute)) {
156
+ return true;
157
+ }
158
+
159
+ return false;
160
+ }
161
+ }, {
162
+ key: "_allocateKey",
163
+ value: function _allocateKey() {
164
+ return 'auto-' + this._keyCounter++;
165
+ }
166
+ }]);
167
+
168
+ return HtmlSanitizer;
169
+ }();
170
+
171
+ exports["default"] = HtmlSanitizer;
@@ -9,8 +9,6 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
 
10
10
  var _react = _interopRequireDefault(require("react"));
11
11
 
12
- require("../css/label.css");
13
-
14
12
  require("../css/symbol.css");
15
13
 
16
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
@@ -63,7 +61,7 @@ function TextSymbol(props) {
63
61
  } else {
64
62
  var symbol = props.symbol === 'plus' ? '+' : props.symbol === 'times' ? "\xD7" : props.symbol;
65
63
  return /*#__PURE__*/_react["default"].createElement("text", {
66
- className: "kokopu-label",
64
+ className: "kokopu-symbolText",
67
65
  x: props.x,
68
66
  y: props.y,
69
67
  fill: props.color,
package/dist/lib/index.js CHANGED
@@ -21,6 +21,12 @@ Object.defineProperty(exports, "ErrorBox", {
21
21
  return _ErrorBox["default"];
22
22
  }
23
23
  });
24
+ Object.defineProperty(exports, "Movetext", {
25
+ enumerable: true,
26
+ get: function get() {
27
+ return _Movetext["default"];
28
+ }
29
+ });
24
30
  Object.defineProperty(exports, "SquareMarkerIcon", {
25
31
  enumerable: true,
26
32
  get: function get() {
@@ -84,6 +90,8 @@ var _Chessboard = _interopRequireDefault(require("./Chessboard"));
84
90
 
85
91
  var _ErrorBox = _interopRequireDefault(require("./ErrorBox"));
86
92
 
93
+ var _Movetext = _interopRequireDefault(require("./Movetext"));
94
+
87
95
  var _SquareMarkerIcon = _interopRequireDefault(require("./SquareMarkerIcon"));
88
96
 
89
97
  var _TextMarkerIcon = _interopRequireDefault(require("./TextMarkerIcon"));
@@ -92,9 +92,7 @@ export default class Page extends React.Component {
92
92
  </ButtonGroup>
93
93
  </Stack>
94
94
  <Box>
95
- <Typography gutterBottom>
96
- Edition mode
97
- </Typography>
95
+ <Typography gutterBottom>Interaction mode</Typography>
98
96
  <RadioGroup value={this.state.interactionMode} onChange={evt => this.set('interactionMode', evt.target.value)}>
99
97
  <FormControlLabel value="" control={<Radio color="primary" />} label="None" />
100
98
  <Stack direction="row" spacing={2} alignItems="center">
@@ -0,0 +1,214 @@
1
+ /******************************************************************************
2
+ * *
3
+ * This file is part of Kokopu-React, a JavaScript chess library. *
4
+ * Copyright (C) 2021-2022 Yoann Le Montagner <yo35 -at- melix.net> *
5
+ * *
6
+ * This program is free software: you can redistribute it and/or *
7
+ * modify it under the terms of the GNU Lesser General Public License *
8
+ * as published by the Free Software Foundation, either version 3 of *
9
+ * the License, or (at your option) any later version. *
10
+ * *
11
+ * This program is distributed in the hope that it will be useful, *
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
+ * GNU Lesser General Public License for more details. *
15
+ * *
16
+ * You should have received a copy of the GNU Lesser General *
17
+ * Public License along with this program. If not, see *
18
+ * <http://www.gnu.org/licenses/>. *
19
+ * *
20
+ ******************************************************************************/
21
+
22
+
23
+ import React from 'react';
24
+
25
+ import { Chessboard, Movetext } from '../../src/index';
26
+ import { buildComponentDemoCode } from './util';
27
+
28
+ import Box from '@mui/material/Box';
29
+ import Button from '@mui/material/Button';
30
+ import ButtonGroup from '@mui/material/ButtonGroup';
31
+ import FormControl from '@mui/material/FormControl';
32
+ import FormControlLabel from '@mui/material/FormControlLabel';
33
+ import InputLabel from '@mui/material/InputLabel';
34
+ import MenuItem from '@mui/material/MenuItem';
35
+ import Select from '@mui/material/Select';
36
+ import Slider from '@mui/material/Slider';
37
+ import Stack from '@mui/material/Stack';
38
+ import Switch from '@mui/material/Switch';
39
+ import TextField from '@mui/material/TextField';
40
+ import Typography from '@mui/material/Typography';
41
+
42
+ import './demo.css';
43
+ import game1 from './game-1.pgn';
44
+ import game2 from './game-2.pgn';
45
+ import invalidGame from './game-invalid.pgn';
46
+
47
+
48
+ export default class Page extends React.Component {
49
+
50
+ constructor(props) {
51
+ super(props);
52
+ this.state = {
53
+ pgn: game1,
54
+ headerVisible: true,
55
+ diagramVisible: true,
56
+ pieceSymbols: 'figurines',
57
+ customSymbols: { K:'R', Q:'D', R:'T', B:'F', N:'C', P:'P' },
58
+ diagramOptions: {
59
+ flipped: false,
60
+ squareSize: 32,
61
+ coordinateVisible: true,
62
+ colorset: 'original',
63
+ pieceset: 'cburnett',
64
+ },
65
+ };
66
+ }
67
+
68
+ render() {
69
+ return (
70
+ <Stack spacing={2} mt={2}>
71
+ {this.renderControls()}
72
+ {this.renderMovetext()}
73
+ {this.renderCode()}
74
+ </Stack>
75
+ );
76
+ }
77
+
78
+ renderControls() {
79
+ let opts = this.state.diagramOptions;
80
+ return (<>
81
+ <Stack direction="row" spacing={2} alignItems="center">
82
+ <ButtonGroup color="primary" size="small">
83
+ <Button onClick={() => this.set('pgn', game1)}>Game 1</Button>
84
+ <Button onClick={() => this.set('pgn', game2)}>Game 2</Button>
85
+ <Button onClick={() => this.set('pgn', invalidGame)}>Invalid game</Button>
86
+ </ButtonGroup>
87
+ <FormControlLabel label="Show headers"
88
+ control={<Switch checked={this.state.headerVisible} onChange={() => this.set('headerVisible', !this.state.headerVisible)} color="primary" />}
89
+ />
90
+ <FormControlLabel label="Show diagrams"
91
+ control={<Switch checked={this.state.diagramVisible} onChange={() => this.set('diagramVisible', !this.state.diagramVisible)} color="primary" />}
92
+ />
93
+ </Stack>
94
+ <Stack direction="row" spacing={2} alignItems="center">
95
+ <FormControl variant="standard">
96
+ <InputLabel id="piecesymbols-label">Piece symbols</InputLabel>
97
+ <Select labelId="piecesymbols-label" sx={{ width: '8em' }} value={this.state.pieceSymbols} onChange={evt => this.set('pieceSymbols', evt.target.value)}>
98
+ <MenuItem value="native">Native</MenuItem>
99
+ <MenuItem value="figurines">Figurines</MenuItem>
100
+ <MenuItem value="custom">Custom</MenuItem>
101
+ </Select>
102
+ </FormControl>
103
+ {this.renderCustomSymbolSelector()}
104
+ </Stack>
105
+ <Stack direction="row" spacing={2} alignItems="center">
106
+ <FormControlLabel label="Flip diagram(s)"
107
+ control={<Switch checked={opts.flipped} onChange={() => this.setDiagramOption('flipped', !opts.flipped)} color="primary" />}
108
+ />
109
+ <FormControlLabel label="Show coordinates in diagram(s)"
110
+ control={<Switch checked={opts.coordinateVisible} onChange={() => this.setDiagramOption('coordinateVisible', !opts.coordinateVisible)} color="primary" />}
111
+ />
112
+ <FormControl variant="standard">
113
+ <InputLabel id="colorset-label">Colorset</InputLabel>
114
+ <Select labelId="colorset-label" sx={{ width: '8em' }} value={opts.colorset} onChange={evt => this.setDiagramOption('colorset', evt.target.value)}>
115
+ {Object.keys(Chessboard.colorsets()).sort().map(colorset => <MenuItem key={colorset} value={colorset}>{colorset}</MenuItem>)}
116
+ </Select>
117
+ </FormControl>
118
+ <FormControl variant="standard">
119
+ <InputLabel id="pieceset-label">Pieceset</InputLabel>
120
+ <Select labelId="pieceset-label" sx={{ width: '8em' }} value={opts.pieceset} onChange={evt => this.setDiagramOption('pieceset', evt.target.value)}>
121
+ {Object.keys(Chessboard.piecesets()).sort().map(pieceset => <MenuItem key={pieceset} value={pieceset}>{pieceset}</MenuItem>)}
122
+ </Select>
123
+ </FormControl>
124
+ </Stack>
125
+ <Box>
126
+ <Typography gutterBottom>Diagram square size</Typography>
127
+ <Slider
128
+ value={opts.squareSize} onChange={(_, newValue) => this.setDiagramOption('squareSize', newValue)}
129
+ min={Chessboard.minSquareSize()} max={Chessboard.maxSquareSize()} step={1} valueLabelDisplay="on" color="primary"
130
+ />
131
+ </Box>
132
+ </>);
133
+ }
134
+
135
+ renderCustomSymbolSelector() {
136
+ if (this.state.pieceSymbols !== 'custom') {
137
+ return undefined;
138
+ }
139
+ let symbols = this.state.customSymbols;
140
+ return (<>
141
+ <TextField variant="standard" sx={{ width: '3em' }} label="King" value={symbols.K} onChange={evt => this.setCustomSymbol('K', evt.target.value)} />
142
+ <TextField variant="standard" sx={{ width: '3em' }} label="Queen" value={symbols.Q} onChange={evt => this.setCustomSymbol('Q', evt.target.value)} />
143
+ <TextField variant="standard" sx={{ width: '3em' }} label="Rook" value={symbols.R} onChange={evt => this.setCustomSymbol('R', evt.target.value)} />
144
+ <TextField variant="standard" sx={{ width: '3em' }} label="Bishop" value={symbols.B} onChange={evt => this.setCustomSymbol('B', evt.target.value)} />
145
+ <TextField variant="standard" sx={{ width: '3em' }} label="Knight" value={symbols.N} onChange={evt => this.setCustomSymbol('N', evt.target.value)} />
146
+ <TextField variant="standard" sx={{ width: '3em' }} label="Pawn" value={symbols.P} onChange={evt => this.setCustomSymbol('P', evt.target.value)} />
147
+ </>);
148
+ }
149
+
150
+ renderMovetext() {
151
+ return (
152
+ <Box>
153
+ <Movetext
154
+ game={this.state.pgn}
155
+ headerVisible={this.state.headerVisible}
156
+ diagramVisible={this.state.diagramVisible}
157
+ pieceSymbols={this.state.pieceSymbols === 'custom' ? this.state.customSymbols : this.state.pieceSymbols}
158
+ diagramOptions={this.state.diagramOptions}
159
+ />
160
+ </Box>
161
+ );
162
+ }
163
+
164
+ renderCode() {
165
+ let attributes = [];
166
+ attributes.push('game={pgn}');
167
+ attributes.push(`headerVisible={${this.state.headerVisible}}`);
168
+ attributes.push(`pieceSymbols=${this.getPieceSymbolsAsText()}`);
169
+ attributes.push(`diagramVisible={${this.state.diagramVisible}}`);
170
+ attributes.push(`diagramOptions={{ ${this.getDiagramAttributesAsText()} }}`);
171
+ let pgnDeclaration = 'const pgn = `\n' + this.state.pgn.trim() + '`;\n\n';
172
+ return <pre className="kokopu-demoCode">{pgnDeclaration + buildComponentDemoCode('Movetext', attributes)}</pre>;
173
+ }
174
+
175
+ set(attributeName, newValue) {
176
+ let newState = {};
177
+ newState[attributeName] = newValue;
178
+ this.setState(newState);
179
+ }
180
+
181
+ setCustomSymbol(key, value) {
182
+ let symbols = {...this.state.customSymbols};
183
+ symbols[key] = value;
184
+ this.set('customSymbols', symbols);
185
+ }
186
+
187
+ getPieceSymbolsAsText() {
188
+ if (this.state.pieceSymbols === 'custom') {
189
+ let symbols = this.state.customSymbols;
190
+ return `{{ K:'${symbols.K}', Q:'${symbols.Q}', R:'${symbols.R}', B:'${symbols.B}', N:'${symbols.N}', P:'${symbols.P}' }}`;
191
+ }
192
+ else {
193
+ return `"${this.state.pieceSymbols}"`;
194
+ }
195
+ }
196
+
197
+ setDiagramOption(key, value) {
198
+ let diagramOptions = {...this.state.diagramOptions};
199
+ diagramOptions[key] = value;
200
+ this.set('diagramOptions', diagramOptions);
201
+ }
202
+
203
+ getDiagramAttributesAsText() {
204
+ let attributes = [];
205
+ if (this.state.diagramOptions.flipped) {
206
+ attributes.push('flipped:true');
207
+ }
208
+ attributes.push(`squareSize:${this.state.diagramOptions.squareSize}`);
209
+ attributes.push(`coordinateVisible:${this.state.diagramOptions.coordinateVisible}`);
210
+ attributes.push(`colorset:'${this.state.diagramOptions.colorset}'`);
211
+ attributes.push(`pieceset:'${this.state.diagramOptions.pieceset}'`);
212
+ return attributes.join(', ');
213
+ }
214
+ }