kokopu-react 1.10.1 → 1.11.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.
@@ -6,11 +6,11 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports["default"] = void 0;
7
7
  var _propTypes = _interopRequireDefault(require("prop-types"));
8
8
  var _react = _interopRequireDefault(require("react"));
9
- var _reactDraggable = _interopRequireDefault(require("react-draggable"));
10
9
  var _kokopu = require("kokopu");
11
10
  var _colorsets2 = _interopRequireDefault(require("./impl/colorsets"));
12
11
  var _piecesets2 = _interopRequireDefault(require("./impl/piecesets"));
13
12
  var _ArrowTip = _interopRequireDefault(require("./impl/ArrowTip"));
13
+ var _DraggableHandle = _interopRequireDefault(require("./impl/DraggableHandle"));
14
14
  var _Motion = _interopRequireDefault(require("./impl/Motion"));
15
15
  var _TextSymbol = _interopRequireDefault(require("./impl/TextSymbol"));
16
16
  var _ErrorBox = _interopRequireDefault(require("./ErrorBox"));
@@ -68,10 +68,6 @@ var Chessboard = /*#__PURE__*/function (_React$Component) {
68
68
  windowWidth: window.innerWidth
69
69
  };
70
70
  _this.arrowTipIdSuffix = (0, _util.generateRandomId)();
71
- _this.handleRef = {};
72
- (0, _kokopu.forEachSquare)(function (sq) {
73
- _this.handleRef[sq] = /*#__PURE__*/_react["default"].createRef();
74
- });
75
71
  return _this;
76
72
  }
77
73
  _createClass(Chessboard, [{
@@ -407,32 +403,26 @@ var Chessboard = /*#__PURE__*/function (_React$Component) {
407
403
  var dragEnabledForEditArrows = this.isEditArrowModeEnabled();
408
404
  var dragEnabledForPlayMoves = this.isPlayMoveModeEnabled() && position.isLegal() && position.square(sq).startsWith(position.turn()) && !this.state.promotionDrawer;
409
405
  if (dragEnabledForMovePieces || dragEnabledForEditArrows || dragEnabledForPlayMoves) {
410
- var dragPosition = this.state.draggedSquare === sq ? this.state.dragPosition : {
411
- x: 0,
412
- y: 0
413
- };
414
- var classNames = ['kokopu-handle', this.isEditArrowModeEnabled() ? 'kokopu-arrowDraggable' : 'kokopu-pieceDraggable'];
415
- return /*#__PURE__*/_react["default"].createElement(_reactDraggable["default"], {
406
+ return /*#__PURE__*/_react["default"].createElement(_DraggableHandle["default"], {
416
407
  key: 'handle-' + sq,
417
- position: dragPosition,
418
- nodeRef: this.handleRef[sq],
419
- onStart: function onStart(evt) {
420
- return _this8.handleDragStart(sq, evt);
421
- },
422
- onDrag: function onDrag(_, dragData) {
423
- return _this8.handleDrag(sq, dragData);
424
- },
425
- onStop: function onStop(_, dragData) {
426
- return _this8.handleDragStop(sq, dragData);
427
- }
428
- }, /*#__PURE__*/_react["default"].createElement("rect", {
429
- ref: this.handleRef[sq],
430
- className: classNames.join(' '),
431
408
  x: x,
432
409
  y: y,
433
410
  width: squareSize,
434
- height: squareSize
435
- }));
411
+ height: squareSize,
412
+ isArrowHandle: this.isEditArrowModeEnabled(),
413
+ onDragStart: function onDragStart(x0, y0) {
414
+ return _this8.handleDragStart(sq, x0, y0);
415
+ },
416
+ onDrag: function onDrag(dx, dy) {
417
+ return _this8.handleDrag(sq, dx, dy);
418
+ },
419
+ onDragStop: function onDragStop(dx, dy) {
420
+ return _this8.handleDragStop(sq, dx, dy);
421
+ },
422
+ onDragCanceled: function onDragCanceled() {
423
+ return _this8.handleDragCanceled();
424
+ }
425
+ });
436
426
  } else if (this.props.interactionMode === 'clickSquares') {
437
427
  return /*#__PURE__*/_react["default"].createElement("rect", {
438
428
  key: 'handle-' + sq,
@@ -633,15 +623,14 @@ var Chessboard = /*#__PURE__*/function (_React$Component) {
633
623
  }
634
624
  }, {
635
625
  key: "handleDragStart",
636
- value: function handleDragStart(sq, evt) {
637
- var squareBoundary = evt.target.getBoundingClientRect();
626
+ value: function handleDragStart(sq, x0, y0) {
638
627
  this.setState({
639
628
  inhibitedSquare: this.isMovePieceModeEnabled() || this.isPlayMoveModeEnabled() ? sq : '-',
640
629
  draggedSquare: sq,
641
630
  hoveredSquare: sq,
642
631
  cursorOffset: {
643
- x: evt.clientX - squareBoundary.left,
644
- y: evt.clientY - squareBoundary.top
632
+ x: x0,
633
+ y: y0
645
634
  },
646
635
  dragPosition: {
647
636
  x: 0,
@@ -651,28 +640,28 @@ var Chessboard = /*#__PURE__*/function (_React$Component) {
651
640
  }
652
641
  }, {
653
642
  key: "handleDrag",
654
- value: function handleDrag(sq, dragData) {
643
+ value: function handleDrag(sq, dx, dy) {
655
644
  var squareSize = this.getSquareSize();
656
645
  var _this$getSquareCoordi13 = this.getSquareCoordinates(squareSize, sq),
657
646
  x = _this$getSquareCoordi13.x,
658
647
  y = _this$getSquareCoordi13.y;
659
- var targetSq = this.getSquareAt(squareSize, x + dragData.x + this.state.cursorOffset.x, y + dragData.y + this.state.cursorOffset.y);
648
+ var targetSq = this.getSquareAt(squareSize, x + dx + this.state.cursorOffset.x, y + dy + this.state.cursorOffset.y);
660
649
  this.setState({
661
650
  hoveredSquare: targetSq,
662
651
  dragPosition: {
663
- x: dragData.x,
664
- y: dragData.y
652
+ x: dx,
653
+ y: dy
665
654
  }
666
655
  });
667
656
  }
668
657
  }, {
669
658
  key: "handleDragStop",
670
- value: function handleDragStop(sq, dragData) {
659
+ value: function handleDragStop(sq, dx, dy) {
671
660
  var squareSize = this.getSquareSize();
672
661
  var _this$getSquareCoordi14 = this.getSquareCoordinates(squareSize, sq),
673
662
  x = _this$getSquareCoordi14.x,
674
663
  y = _this$getSquareCoordi14.y;
675
- var targetSq = this.getSquareAt(squareSize, x + dragData.x + this.state.cursorOffset.x, y + dragData.y + this.state.cursorOffset.y);
664
+ var targetSq = this.getSquareAt(squareSize, x + dx + this.state.cursorOffset.x, y + dy + this.state.cursorOffset.y);
676
665
  this.setState({
677
666
  inhibitedSquare: '-',
678
667
  draggedSquare: '-',
@@ -721,6 +710,15 @@ var Chessboard = /*#__PURE__*/function (_React$Component) {
721
710
  }
722
711
  }
723
712
  }
713
+ }, {
714
+ key: "handleDragCanceled",
715
+ value: function handleDragCanceled() {
716
+ this.setState({
717
+ inhibitedSquare: '-',
718
+ draggedSquare: '-',
719
+ hoveredSquare: '-'
720
+ });
721
+ }
724
722
  }, {
725
723
  key: "handleDrawerCancelButtonClicked",
726
724
  value: function handleDrawerCancelButtonClicked() {
@@ -0,0 +1,277 @@
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
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _react = _interopRequireDefault(require("react"));
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
11
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12
+ 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, _toPropertyKey(descriptor.key), descriptor); } }
13
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
14
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
15
+ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
16
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
17
+ function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
18
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
19
+ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
20
+ function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
21
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
22
+ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
23
+ /**
24
+ * Invisible SVG rectangular handle that captures drag events.
25
+ */
26
+ var DraggableHandle = /*#__PURE__*/function (_React$Component) {
27
+ _inherits(DraggableHandle, _React$Component);
28
+ var _super = _createSuper(DraggableHandle);
29
+ function DraggableHandle(props) {
30
+ var _this;
31
+ _classCallCheck(this, DraggableHandle);
32
+ _this = _super.call(this, props);
33
+ _this.dragData = null;
34
+ _this.innerRef = /*#__PURE__*/_react["default"].createRef();
35
+ return _this;
36
+ }
37
+ _createClass(DraggableHandle, [{
38
+ key: "componentDidMount",
39
+ value: function componentDidMount() {
40
+ var _this2 = this;
41
+ if (!this.windowListeners) {
42
+ this.windowListeners = {
43
+ mouseDown: function mouseDown(evt) {
44
+ return _this2.handleMouseDown(evt);
45
+ },
46
+ mouseMove: function mouseMove(evt) {
47
+ return _this2.handleMouseMove(evt);
48
+ },
49
+ mouseUp: function mouseUp(evt) {
50
+ return _this2.handleMouseUp(evt);
51
+ },
52
+ touchStart: function touchStart(evt) {
53
+ return _this2.handleTouchStart(evt);
54
+ },
55
+ touchMove: function touchMove(evt) {
56
+ return _this2.handleTouchMove(evt);
57
+ },
58
+ touchEnd: function touchEnd(evt) {
59
+ return _this2.handleTouchEnd(evt);
60
+ },
61
+ touchCancel: function touchCancel(evt) {
62
+ return _this2.handleTouchCancel(evt);
63
+ }
64
+ };
65
+ this.innerRef.current.addEventListener('mousedown', this.windowListeners.mouseDown);
66
+ window.addEventListener('mousemove', this.windowListeners.mouseMove);
67
+ window.addEventListener('mouseup', this.windowListeners.mouseUp);
68
+ this.innerRef.current.addEventListener('touchstart', this.windowListeners.touchStart, {
69
+ passive: false
70
+ });
71
+ window.addEventListener('touchmove', this.windowListeners.touchMove, {
72
+ passive: false
73
+ });
74
+ window.addEventListener('touchend', this.windowListeners.touchEnd);
75
+ window.addEventListener('touchcancel', this.windowListeners.touchCancel);
76
+ }
77
+ this.dragData = null;
78
+ }
79
+ }, {
80
+ key: "componentWillUnmount",
81
+ value: function componentWillUnmount() {
82
+ if (this.windowListeners) {
83
+ this.innerRef.current.removeEventListener('mousedown', this.windowListeners.mouseDown);
84
+ window.removeEventListener('mousemove', this.windowListeners.mouseMove);
85
+ window.removeEventListener('mouseup', this.windowListeners.mouseUp);
86
+ this.innerRef.current.removeEventListener('touchstart', this.windowListeners.touchStart);
87
+ window.removeEventListener('touchmove', this.windowListeners.touchMove);
88
+ window.removeEventListener('touchend', this.windowListeners.touchEnd);
89
+ window.removeEventListener('touchcancel', this.windowListeners.touchCancel);
90
+ this.windowListeners = null;
91
+ }
92
+ }
93
+ }, {
94
+ key: "render",
95
+ value: function render() {
96
+ var classNames = ['kokopu-handle', this.props.isArrowHandle ? 'kokopu-arrowDraggable' : 'kokopu-pieceDraggable'];
97
+ return /*#__PURE__*/_react["default"].createElement("rect", {
98
+ ref: this.innerRef,
99
+ className: classNames.join(' '),
100
+ x: this.props.x,
101
+ y: this.props.y,
102
+ width: this.props.width,
103
+ height: this.props.height
104
+ });
105
+ }
106
+ }, {
107
+ key: "handleTouchStart",
108
+ value: function handleTouchStart(evt) {
109
+ if (this.dragData || evt.touches.length !== 1) {
110
+ return;
111
+ }
112
+ evt.preventDefault();
113
+ this.doInitDrag(evt.target, evt.touches[0]);
114
+ }
115
+ }, {
116
+ key: "handleTouchMove",
117
+ value: function handleTouchMove(evt) {
118
+ if (!this.dragData) {
119
+ return;
120
+ }
121
+ evt.preventDefault();
122
+ if (evt.touches.length === 1) {
123
+ this.doUpdateDrag(evt.touches[0]);
124
+ } else {
125
+ this.doCancelDrag();
126
+ }
127
+ }
128
+ }, {
129
+ key: "handleTouchEnd",
130
+ value: function handleTouchEnd(evt) {
131
+ if (!this.dragData) {
132
+ return;
133
+ }
134
+ evt.preventDefault();
135
+ if (evt.touches.length === 0 && evt.changedTouches.length === 1) {
136
+ this.doEndDrag(evt.changedTouches[0]);
137
+ } else {
138
+ this.doCancelDrag();
139
+ }
140
+ }
141
+ }, {
142
+ key: "handleTouchCancel",
143
+ value: function handleTouchCancel(evt) {
144
+ if (!this.dragData) {
145
+ return;
146
+ }
147
+ evt.preventDefault();
148
+ this.doCancelDrag();
149
+ }
150
+ }, {
151
+ key: "handleMouseDown",
152
+ value: function handleMouseDown(evt) {
153
+ if (this.dragData || evt.button !== 0) {
154
+ return;
155
+ }
156
+ evt.preventDefault();
157
+ this.doInitDrag(evt.target, evt);
158
+ }
159
+ }, {
160
+ key: "handleMouseMove",
161
+ value: function handleMouseMove(evt) {
162
+ if (!this.dragData) {
163
+ return;
164
+ }
165
+ evt.preventDefault();
166
+ this.doUpdateDrag(evt);
167
+ }
168
+ }, {
169
+ key: "handleMouseUp",
170
+ value: function handleMouseUp(evt) {
171
+ if (!this.dragData) {
172
+ return;
173
+ }
174
+ evt.preventDefault();
175
+ this.doEndDrag(evt);
176
+ }
177
+ }, {
178
+ key: "doInitDrag",
179
+ value: function doInitDrag(target, _ref) {
180
+ var pageX = _ref.pageX,
181
+ pageY = _ref.pageY,
182
+ clientX = _ref.clientX,
183
+ clientY = _ref.clientY;
184
+ this.dragData = {
185
+ originX: pageX,
186
+ originY: pageY
187
+ };
188
+ var handleBoundary = target.getBoundingClientRect();
189
+ var x0 = clientX - handleBoundary.left;
190
+ var y0 = clientY - handleBoundary.top;
191
+ if (this.props.onDragStart) {
192
+ this.props.onDragStart(x0, y0);
193
+ }
194
+ }
195
+ }, {
196
+ key: "doUpdateDrag",
197
+ value: function doUpdateDrag(_ref2) {
198
+ var pageX = _ref2.pageX,
199
+ pageY = _ref2.pageY;
200
+ var _this$dragData = this.dragData,
201
+ originX = _this$dragData.originX,
202
+ originY = _this$dragData.originY;
203
+ if (this.props.onDrag) {
204
+ this.props.onDrag(pageX - originX, pageY - originY);
205
+ }
206
+ }
207
+ }, {
208
+ key: "doEndDrag",
209
+ value: function doEndDrag(_ref3) {
210
+ var pageX = _ref3.pageX,
211
+ pageY = _ref3.pageY;
212
+ var _this$dragData2 = this.dragData,
213
+ originX = _this$dragData2.originX,
214
+ originY = _this$dragData2.originY;
215
+ this.dragData = null;
216
+ if (this.props.onDragStop) {
217
+ this.props.onDragStop(pageX - originX, pageY - originY);
218
+ }
219
+ }
220
+ }, {
221
+ key: "doCancelDrag",
222
+ value: function doCancelDrag() {
223
+ if (this.props.onDragCanceled) {
224
+ this.props.onDragCanceled();
225
+ }
226
+ }
227
+ }]);
228
+ return DraggableHandle;
229
+ }(_react["default"].Component);
230
+ exports["default"] = DraggableHandle;
231
+ DraggableHandle.propTypes = {
232
+ /**
233
+ * Initial X coordinate of the handle.
234
+ */
235
+ x: _propTypes["default"].number.isRequired,
236
+ /**
237
+ * Initial Y coordinate of the handle.
238
+ */
239
+ y: _propTypes["default"].number.isRequired,
240
+ /**
241
+ * Width of the handle.
242
+ */
243
+ width: _propTypes["default"].number.isRequired,
244
+ /**
245
+ * Height of the handle.
246
+ */
247
+ height: _propTypes["default"].number.isRequired,
248
+ /**
249
+ * `true` if the handle corresponds to a draggable arrow, `false` if it corresponds to a draggable piece.
250
+ */
251
+ isArrowHandle: _propTypes["default"].bool.isRequired,
252
+ /**
253
+ * Callback invoked when the drag starts.
254
+ *
255
+ * @param {number} x0 X-coordinate (relative to the handle) at which the drag has been initiated.
256
+ * @param {number} y0 X-coordinate (relative to the handle) at which the drag has been initiated.
257
+ */
258
+ onDragStart: _propTypes["default"].func,
259
+ /**
260
+ * Callback invoked during the drag.
261
+ *
262
+ * @param {number} dx X-coordinate of the translation vector applied during the drag.
263
+ * @param {number} dy Y-coordinate of the translation vector applied during the drag.
264
+ */
265
+ onDrag: _propTypes["default"].func,
266
+ /**
267
+ * Callback invoked when the drag stops.
268
+ *
269
+ * @param {number} dx X-coordinate of the translation vector applied during the drag.
270
+ * @param {number} dy Y-coordinate of the translation vector applied during the drag.
271
+ */
272
+ onDragStop: _propTypes["default"].func,
273
+ /**
274
+ * Callback invoked when the drag is canceled.
275
+ */
276
+ onDragCanceled: _propTypes["default"].func
277
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kokopu-react",
3
- "version": "1.10.1",
3
+ "version": "1.11.1",
4
4
  "description": "A React-based library to create and display chessboard and chess-related components.",
5
5
  "keywords": [
6
6
  "chess",
@@ -35,8 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "htmlparser2": "^8.0.1",
38
- "prop-types": "^15.8.1",
39
- "react-draggable": "^4.4.5"
38
+ "prop-types": "^15.8.1"
40
39
  },
41
40
  "devDependencies": {
42
41
  "@babel/cli": "^7.20.7",
@@ -61,7 +60,7 @@
61
60
  "react": "^18.2.0",
62
61
  "react-dom": "^18.2.0",
63
62
  "react-styleguidist": "^13.0.0",
64
- "selenium-webdriver": "^4.7.1",
63
+ "selenium-webdriver": "^4.8.0",
65
64
  "ssh2-sftp-client": "^9.0.4",
66
65
  "style-loader": "^3.3.1",
67
66
  "unit.js": "^2.1.1",
@@ -2,7 +2,7 @@ version: "3.7"
2
2
 
3
3
  services:
4
4
  web-client:
5
- image: selenium/standalone-firefox:4.7.1-20221208 # https://github.com/SeleniumHQ/docker-selenium to get the latest version
5
+ image: selenium/standalone-firefox:4.8.0-20230131 # 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)
package/src/Chessboard.js CHANGED
@@ -22,12 +22,12 @@
22
22
 
23
23
  import PropTypes from 'prop-types';
24
24
  import React from 'react';
25
- import Draggable from 'react-draggable';
26
25
  import { exception, MoveDescriptor, Position, coordinatesToSquare, forEachSquare, oppositeColor, squareColor, squareToCoordinates } from 'kokopu';
27
26
 
28
27
  import colorsets from './impl/colorsets';
29
28
  import piecesets from './impl/piecesets';
30
29
  import ArrowTip from './impl/ArrowTip';
30
+ import DraggableHandle from './impl/DraggableHandle';
31
31
  import Motion from './impl/Motion';
32
32
  import TextSymbol from './impl/TextSymbol';
33
33
  import ErrorBox from './ErrorBox';
@@ -67,9 +67,6 @@ export default class Chessboard extends React.Component {
67
67
  windowWidth: window.innerWidth,
68
68
  };
69
69
  this.arrowTipIdSuffix = generateRandomId();
70
-
71
- this.handleRef = {};
72
- forEachSquare(sq => { this.handleRef[sq] = React.createRef(); });
73
70
  }
74
71
 
75
72
  componentDidMount() {
@@ -313,18 +310,13 @@ export default class Chessboard extends React.Component {
313
310
  let dragEnabledForEditArrows = this.isEditArrowModeEnabled();
314
311
  let dragEnabledForPlayMoves = this.isPlayMoveModeEnabled() && position.isLegal() && position.square(sq).startsWith(position.turn()) && !this.state.promotionDrawer;
315
312
  if (dragEnabledForMovePieces || dragEnabledForEditArrows || dragEnabledForPlayMoves) {
316
- let dragPosition = this.state.draggedSquare === sq ? this.state.dragPosition : { x: 0, y: 0 };
317
- let classNames = [ 'kokopu-handle', this.isEditArrowModeEnabled() ? 'kokopu-arrowDraggable' : 'kokopu-pieceDraggable' ];
318
313
  return (
319
- <Draggable
320
- key={'handle-' + sq} position={dragPosition}
321
- nodeRef={this.handleRef[sq]}
322
- onStart={evt => this.handleDragStart(sq, evt)}
323
- onDrag={(_, dragData) => this.handleDrag(sq, dragData)}
324
- onStop={(_, dragData) => this.handleDragStop(sq, dragData)}
325
- >
326
- <rect ref={this.handleRef[sq]} className={classNames.join(' ')} x={x} y={y} width={squareSize} height={squareSize} />
327
- </Draggable>
314
+ <DraggableHandle key={'handle-' + sq} x={x} y={y} width={squareSize} height={squareSize} isArrowHandle={this.isEditArrowModeEnabled()}
315
+ onDragStart={(x0, y0) => this.handleDragStart(sq, x0, y0)}
316
+ onDrag={(dx, dy) => this.handleDrag(sq, dx, dy)}
317
+ onDragStop={(dx, dy) => this.handleDragStop(sq, dx, dy)}
318
+ onDragCanceled={() => this.handleDragCanceled()}
319
+ />
328
320
  );
329
321
  }
330
322
  else if (this.props.interactionMode === 'clickSquares') {
@@ -443,31 +435,30 @@ export default class Chessboard extends React.Component {
443
435
  this.setState({ windowWidth: window.innerWidth });
444
436
  }
445
437
 
446
- handleDragStart(sq, evt) {
447
- let squareBoundary = evt.target.getBoundingClientRect();
438
+ handleDragStart(sq, x0, y0) {
448
439
  this.setState({
449
440
  inhibitedSquare: this.isMovePieceModeEnabled() || this.isPlayMoveModeEnabled() ? sq : '-',
450
441
  draggedSquare: sq,
451
442
  hoveredSquare: sq,
452
- cursorOffset: { x: evt.clientX - squareBoundary.left, y: evt.clientY - squareBoundary.top },
443
+ cursorOffset: { x: x0, y: y0 },
453
444
  dragPosition: { x: 0, y: 0 },
454
445
  });
455
446
  }
456
447
 
457
- handleDrag(sq, dragData) {
448
+ handleDrag(sq, dx, dy) {
458
449
  let squareSize = this.getSquareSize();
459
450
  let { x, y } = this.getSquareCoordinates(squareSize, sq);
460
- let targetSq = this.getSquareAt(squareSize, x + dragData.x + this.state.cursorOffset.x, y + dragData.y + this.state.cursorOffset.y);
451
+ let targetSq = this.getSquareAt(squareSize, x + dx + this.state.cursorOffset.x, y + dy + this.state.cursorOffset.y);
461
452
  this.setState({
462
453
  hoveredSquare: targetSq,
463
- dragPosition: { x: dragData.x, y: dragData.y },
454
+ dragPosition: { x: dx, y: dy },
464
455
  });
465
456
  }
466
457
 
467
- handleDragStop(sq, dragData) {
458
+ handleDragStop(sq, dx, dy) {
468
459
  let squareSize = this.getSquareSize();
469
460
  let { x, y } = this.getSquareCoordinates(squareSize, sq);
470
- let targetSq = this.getSquareAt(squareSize, x + dragData.x + this.state.cursorOffset.x, y + dragData.y + this.state.cursorOffset.y);
461
+ let targetSq = this.getSquareAt(squareSize, x + dx + this.state.cursorOffset.x, y + dy + this.state.cursorOffset.y);
471
462
  this.setState({
472
463
  inhibitedSquare: '-',
473
464
  draggedSquare: '-',
@@ -517,6 +508,14 @@ export default class Chessboard extends React.Component {
517
508
  }
518
509
  }
519
510
 
511
+ handleDragCanceled() {
512
+ this.setState({
513
+ inhibitedSquare: '-',
514
+ draggedSquare: '-',
515
+ hoveredSquare: '-',
516
+ });
517
+ }
518
+
520
519
  handleDrawerCancelButtonClicked() {
521
520
  this.setState({
522
521
  inhibitedSquare: '-',
@@ -0,0 +1,235 @@
1
+ /******************************************************************************
2
+ * *
3
+ * This file is part of Kokopu-React, a JavaScript chess library. *
4
+ * Copyright (C) 2021-2023 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 PropTypes from 'prop-types';
24
+ import React from 'react';
25
+
26
+
27
+ /**
28
+ * Invisible SVG rectangular handle that captures drag events.
29
+ */
30
+ export default class DraggableHandle extends React.Component {
31
+
32
+ constructor(props) {
33
+ super(props);
34
+ this.dragData = null;
35
+ this.innerRef = React.createRef();
36
+ }
37
+
38
+ componentDidMount() {
39
+ if (!this.windowListeners) {
40
+ this.windowListeners = {
41
+ mouseDown: evt => this.handleMouseDown(evt),
42
+ mouseMove: evt => this.handleMouseMove(evt),
43
+ mouseUp: evt => this.handleMouseUp(evt),
44
+ touchStart: evt => this.handleTouchStart(evt),
45
+ touchMove: evt => this.handleTouchMove(evt),
46
+ touchEnd: evt => this.handleTouchEnd(evt),
47
+ touchCancel: evt => this.handleTouchCancel(evt),
48
+ };
49
+ this.innerRef.current.addEventListener('mousedown', this.windowListeners.mouseDown);
50
+ window.addEventListener('mousemove', this.windowListeners.mouseMove);
51
+ window.addEventListener('mouseup', this.windowListeners.mouseUp);
52
+ this.innerRef.current.addEventListener('touchstart', this.windowListeners.touchStart, { passive: false });
53
+ window.addEventListener('touchmove', this.windowListeners.touchMove, { passive: false });
54
+ window.addEventListener('touchend', this.windowListeners.touchEnd);
55
+ window.addEventListener('touchcancel', this.windowListeners.touchCancel);
56
+ }
57
+ this.dragData = null;
58
+ }
59
+
60
+ componentWillUnmount() {
61
+ if (this.windowListeners) {
62
+ this.innerRef.current.removeEventListener('mousedown', this.windowListeners.mouseDown);
63
+ window.removeEventListener('mousemove', this.windowListeners.mouseMove);
64
+ window.removeEventListener('mouseup', this.windowListeners.mouseUp);
65
+ this.innerRef.current.removeEventListener('touchstart', this.windowListeners.touchStart);
66
+ window.removeEventListener('touchmove', this.windowListeners.touchMove);
67
+ window.removeEventListener('touchend', this.windowListeners.touchEnd);
68
+ window.removeEventListener('touchcancel', this.windowListeners.touchCancel);
69
+ this.windowListeners = null;
70
+ }
71
+ }
72
+
73
+ render() {
74
+ let classNames = [ 'kokopu-handle', this.props.isArrowHandle ? 'kokopu-arrowDraggable' : 'kokopu-pieceDraggable' ];
75
+ return <rect ref={this.innerRef} className={classNames.join(' ')} x={this.props.x} y={this.props.y} width={this.props.width} height={this.props.height} />;
76
+ }
77
+
78
+ handleTouchStart(evt) {
79
+ if (this.dragData || evt.touches.length !== 1) {
80
+ return;
81
+ }
82
+ evt.preventDefault();
83
+ this.doInitDrag(evt.target, evt.touches[0]);
84
+ }
85
+
86
+ handleTouchMove(evt) {
87
+ if (!this.dragData) {
88
+ return;
89
+ }
90
+ evt.preventDefault();
91
+ if (evt.touches.length === 1) {
92
+ this.doUpdateDrag(evt.touches[0]);
93
+ }
94
+ else {
95
+ this.doCancelDrag();
96
+ }
97
+ }
98
+
99
+ handleTouchEnd(evt) {
100
+ if (!this.dragData) {
101
+ return;
102
+ }
103
+ evt.preventDefault();
104
+ if (evt.touches.length === 0 && evt.changedTouches.length === 1) {
105
+ this.doEndDrag(evt.changedTouches[0]);
106
+ }
107
+ else {
108
+ this.doCancelDrag();
109
+ }
110
+ }
111
+
112
+ handleTouchCancel(evt) {
113
+ if (!this.dragData) {
114
+ return;
115
+ }
116
+ evt.preventDefault();
117
+ this.doCancelDrag();
118
+ }
119
+
120
+ handleMouseDown(evt) {
121
+ if (this.dragData || evt.button !== 0) {
122
+ return;
123
+ }
124
+ evt.preventDefault();
125
+ this.doInitDrag(evt.target, evt);
126
+ }
127
+
128
+ handleMouseMove(evt) {
129
+ if (!this.dragData) {
130
+ return;
131
+ }
132
+ evt.preventDefault();
133
+ this.doUpdateDrag(evt);
134
+ }
135
+
136
+ handleMouseUp(evt) {
137
+ if (!this.dragData) {
138
+ return;
139
+ }
140
+ evt.preventDefault();
141
+ this.doEndDrag(evt);
142
+ }
143
+
144
+ doInitDrag(target, { pageX, pageY, clientX, clientY }) {
145
+ this.dragData = {
146
+ originX: pageX,
147
+ originY: pageY,
148
+ };
149
+ let handleBoundary = target.getBoundingClientRect();
150
+ let x0 = clientX - handleBoundary.left;
151
+ let y0 = clientY - handleBoundary.top;
152
+ if (this.props.onDragStart) {
153
+ this.props.onDragStart(x0, y0);
154
+ }
155
+ }
156
+
157
+ doUpdateDrag({ pageX, pageY }) {
158
+ let { originX, originY } = this.dragData;
159
+ if (this.props.onDrag) {
160
+ this.props.onDrag(pageX - originX, pageY - originY);
161
+ }
162
+ }
163
+
164
+ doEndDrag({ pageX, pageY }) {
165
+ let { originX, originY } = this.dragData;
166
+ this.dragData = null;
167
+ if (this.props.onDragStop) {
168
+ this.props.onDragStop(pageX - originX, pageY - originY);
169
+ }
170
+ }
171
+
172
+ doCancelDrag() {
173
+ if (this.props.onDragCanceled) {
174
+ this.props.onDragCanceled();
175
+ }
176
+ }
177
+
178
+ }
179
+
180
+ DraggableHandle.propTypes = {
181
+
182
+ /**
183
+ * Initial X coordinate of the handle.
184
+ */
185
+ x: PropTypes.number.isRequired,
186
+
187
+ /**
188
+ * Initial Y coordinate of the handle.
189
+ */
190
+ y: PropTypes.number.isRequired,
191
+
192
+ /**
193
+ * Width of the handle.
194
+ */
195
+ width: PropTypes.number.isRequired,
196
+
197
+ /**
198
+ * Height of the handle.
199
+ */
200
+ height: PropTypes.number.isRequired,
201
+
202
+ /**
203
+ * `true` if the handle corresponds to a draggable arrow, `false` if it corresponds to a draggable piece.
204
+ */
205
+ isArrowHandle: PropTypes.bool.isRequired,
206
+
207
+ /**
208
+ * Callback invoked when the drag starts.
209
+ *
210
+ * @param {number} x0 X-coordinate (relative to the handle) at which the drag has been initiated.
211
+ * @param {number} y0 X-coordinate (relative to the handle) at which the drag has been initiated.
212
+ */
213
+ onDragStart: PropTypes.func,
214
+
215
+ /**
216
+ * Callback invoked during the drag.
217
+ *
218
+ * @param {number} dx X-coordinate of the translation vector applied during the drag.
219
+ * @param {number} dy Y-coordinate of the translation vector applied during the drag.
220
+ */
221
+ onDrag: PropTypes.func,
222
+
223
+ /**
224
+ * Callback invoked when the drag stops.
225
+ *
226
+ * @param {number} dx X-coordinate of the translation vector applied during the drag.
227
+ * @param {number} dy Y-coordinate of the translation vector applied during the drag.
228
+ */
229
+ onDragStop: PropTypes.func,
230
+
231
+ /**
232
+ * Callback invoked when the drag is canceled.
233
+ */
234
+ onDragCanceled: PropTypes.func,
235
+ };