Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds drag and drop capability to 2d token #316

Merged
merged 13 commits into from
Jan 21, 2019
104 changes: 79 additions & 25 deletions examples/react/chess/board.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import Chess from 'chess.js';
import Checkerboard from './checkerboard';
import { Checkerboard, cartesianToAlgebraic } from './checkerboard';
import { Token } from 'boardgame.io/ui';
import Bishop from './pieces/bishop';
import King from './pieces/king';
Expand All @@ -19,7 +19,7 @@ import Queen from './pieces/queen';
import Rook from './pieces/rook';

const COL_NAMES = 'abcdefgh';
const SELECTED_COLOR = 'green';
const HIGHLIGHTED_COLOR = 'green';
const MOVABLE_COLOR = 'palegreen';

class Board extends React.Component {
Expand All @@ -40,13 +40,15 @@ class Board extends React.Component {

state = {
selected: '',
highlighted: '',
dragged: '',
};

// eslint-disable-next-line react/no-deprecated
componentWillReceiveProps(nextProps) {
if (nextProps.G.pgn) {
this.chess.load_pgn(nextProps.G.pgn);
this.setState({ selected: '' });
this.setState({ ...this.state, selected: '', highlighted: '' });
}
}

Expand Down Expand Up @@ -77,54 +79,96 @@ class Board extends React.Component {
}

if (!this.state.selected && this._isSelectable(square)) {
this.setState({ selected: square });
}

if (this.state.selected) {
let moves = this._getMoves();
let move = moves.find(
move => move.from == this.state.selected && move.to == square
);
if (move) {
this.props.moves.move(move.san);
} else {
this.setState({ selected: '' });
}
this.setState({ ...this.state, selected: square, highlighted: square });
} else if (this.state.selected) {
this._tryMove(this.state.selected, square);
}
};

_getHighlightedSquares() {
let result = {};
if (this.state.selected) {
result[this.state.selected] = SELECTED_COLOR;
}
for (let move of this._getMoves()) {
result[move.to] = MOVABLE_COLOR;
}
if (this.state.highlighted) {
result[this.state.highlighted] = HIGHLIGHTED_COLOR;
}
return result;
}

_shouldDrag = ({ x, y }) => {
const square = cartesianToAlgebraic(x, y);
const result = this.props.isActive && this._isSelectable(square);
if (result) {
this.setState({
...this.state,
dragged: this._getInitialCell(square),
});
return true;
}
};

_onDrag = ({ x, y, originalX, originalY }) => {
if (Math.sqrt((x - originalX) ** 2 + (y - originalY) ** 2) > 0.2) {
this.setState({
...this.state,
selected: this._getSquare(originalX, originalY),
highlighted: this._getSquare(x, y),
});
} else {
this.setState({
...this.state,
selected: '',
highlighted: '',
});
}
};

_onDrop = ({ x, y }) => {
if (this.state.selected) {
this.setState({ ...this.state, dragged: '' });
this._tryMove(this.state.selected, this._getSquare(x, y));
}
};

_getSquare(x, y) {
return cartesianToAlgebraic(this._getInRange(x), this._getInRange(y));
}

_getInRange(x) {
return Math.max(Math.min(Math.round(x), 7), 0);
}

_getPieces() {
let dragged = [];
let result = [];
for (let y = 1; y <= 8; y++) {
for (let x = 0; x < 8; x++) {
let square = COL_NAMES[x] + y;
let p = this.chess.get(square);
if (p) {
result.push(
let piece = this.chess.get(square);
if (piece) {
const token = (
<Token
draggable={true}
shouldDrag={this._shouldDrag}
onDrag={this._onDrag}
onDrop={this._onDrop}
square={square}
animate={true}
key={this._getInitialCell(square)}
onClick={this.click.bind(this)}
>
{this._getPieceByTypeAndColor(p.type, p.color)}
{this._getPieceByTypeAndColor(piece.type, piece.color)}
</Token>
);
if (square === this.state.dragged) {
result.push(token);
} else {
dragged.push(token);
}
}
}
}
return result;
return dragged.concat(result);
}

_getPieceByTypeAndColor(type, color) {
Expand Down Expand Up @@ -209,6 +253,16 @@ class Board extends React.Component {
square: this.state.selected,
});
}

_tryMove(from, to) {
const moves = this._getMoves();
const move = moves.find(move => move.from == from && move.to == to);
if (move) {
this.props.moves.move(move.san);
} else {
this.setState({ ...this.state, selected: '', highlighted: '' });
}
}
}

export default Board;
46 changes: 26 additions & 20 deletions examples/react/chess/checkerboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { Grid } from 'boardgame.io/ui';
* </Token>
* </Checkerboard>
*/
class Checkerboard extends React.Component {
export class Checkerboard extends React.Component {
static propTypes = {
rows: PropTypes.number,
cols: PropTypes.number,
Expand All @@ -61,14 +61,14 @@ class Checkerboard extends React.Component {
};

onClick = ({ x, y }) => {
this.props.onClick({ square: this._cartesianToAlgebraic(x, y) });
this.props.onClick({ square: cartesianToAlgebraic(x, y, this.props.rows) });
};

render() {
// Convert the square="" prop to x and y.
const tokens = React.Children.map(this.props.children, child => {
const square = child.props.square;
const { x, y } = this._algebraicToCartesian(square);
const { x, y } = algebraicToCartesian(square, this.props.rows);
return React.cloneElement(child, { x, y });
});

Expand All @@ -87,7 +87,7 @@ class Checkerboard extends React.Component {

// Add highlighted squares.
for (const square in this.props.highlightedSquares) {
const { x, y } = this._algebraicToCartesian(square);
const { x, y } = algebraicToCartesian(square, this.props.rows);
const key = `${x},${y}`;
colorMap[key] = this.props.highlightedSquares[square];
}
Expand All @@ -104,23 +104,29 @@ class Checkerboard extends React.Component {
</Grid>
);
}
}

_algebraicToCartesian(square) {
let regexp = /([A-Za-z])(\d+)/g;
let match = regexp.exec(square);
if (match == null) {
throw 'Invalid square provided: ' + square;
}
let colSymbol = match[1].toLowerCase();
let col = colSymbol.charCodeAt(0) - 'a'.charCodeAt(0);
let row = parseInt(match[2]);
return { x: col, y: this.props.rows - row };
}

_cartesianToAlgebraic(x, y) {
let colSymbol = String.fromCharCode(x + 'a'.charCodeAt(0));
return colSymbol + (this.props.rows - y);
/**
* Given an algebraic notation, returns x and y values.
* Example: A1 returns { x: 0, y: 0 }
*/
export function algebraicToCartesian(square, rows = 8) {
let regexp = /([A-Za-z])(\d+)/g;
let match = regexp.exec(square);
if (match == null) {
throw 'Invalid square provided: ' + square;
}
let colSymbol = match[1].toLowerCase();
let col = colSymbol.charCodeAt(0) - 'a'.charCodeAt(0);
let row = parseInt(match[2]);
return { x: col, y: rows - row };
}

export default Checkerboard;
/**
* Given an x and y values, returns algebraic notation.
* Example: 0, 0 returns A1
*/
export function cartesianToAlgebraic(x, y, rows = 8) {
let colSymbol = String.fromCharCode(x + 'a'.charCodeAt(0));
return colSymbol + (rows - y);
}
2 changes: 1 addition & 1 deletion examples/react/chess/checkerboard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import Checkerboard from './checkerboard';
import { Checkerboard } from './checkerboard';
import { Token } from 'boardgame.io/ui';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Expand Down
Loading