react-chess
Version:
Renders a chess board using React
234 lines (194 loc) • 6.29 kB
JavaScript
const React = require('react')
const PropTypes = require('prop-types')
const Draggable = require('react-draggable')
const resizeAware = require('react-resize-aware')
const defaultLineup = require('./defaultLineup')
const pieceComponents = require('./pieces')
const decode = require('./decode')
const ResizeAware = resizeAware.default || resizeAware
const getDefaultLineup = () => defaultLineup.slice()
const noop = () => {
/* intentional noop */
}
const square = 100 / 8
const squareSize = `${square}%`
const squareStyles = {
width: squareSize,
paddingBottom: squareSize,
float: 'left',
position: 'relative',
pointerEvents: 'none'
}
const labelStyles = {fontSize: 'calc(7px + .5vw)', position: 'absolute', userSelect: 'none'}
const yLabelStyles = Object.assign({top: '5%', left: '5%'}, labelStyles)
const xLabelStyles = Object.assign({bottom: '5%', right: '5%'}, labelStyles)
class Chess extends React.Component {
constructor(...args) {
super(...args)
this.els = {}
this.state = {}
this.setBoardRef = el => (this.els.board = el)
this.handleDragStart = this.handleDragStart.bind(this)
this.handleDragStop = this.handleDragStop.bind(this)
this.handleDrag = this.handleDrag.bind(this)
this.handleResize = this.handleResize.bind(this)
}
getSquareColor(x, y) {
const {lightSquareColor, darkSquareColor} = this.props
const odd = x % 2
if (y % 2) {
return odd ? lightSquareColor : darkSquareColor
}
return odd ? darkSquareColor : lightSquareColor
}
componentDidMount() {
const boardSize = this.els.board.clientWidth
const tileSize = boardSize / 8
this.setState({boardSize, tileSize})
}
handleResize(size) {
const tileSize = size.width / 8
this.setState({boardSize: size.width, tileSize})
}
coordsToPosition(coords) {
const x = Math.round(coords.x / this.state.tileSize)
const y = Math.round(coords.y / this.state.tileSize)
return {
x,
y,
pos: `${String.fromCharCode(decode.charCodeOffset + x)}${8 - y}`
}
}
handleDrag(evt, drag) {
if (!this.props.highlightTarget) {
return
}
const {targetTile} = this.state
const {x, y} = this.coordsToPosition({
x: drag.node.offsetLeft + drag.x,
y: drag.node.offsetTop + drag.y
})
if (!targetTile || targetTile.x !== x || targetTile.y !== y) {
this.setState({targetTile: {x, y}})
}
}
handleDragStart(evt, drag) {
evt.preventDefault()
if (!this.props.allowMoves) {
return false
}
const node = drag.node
const dragFrom = this.coordsToPosition({x: node.offsetLeft, y: node.offsetTop})
const draggingPiece = this.findPieceAtPosition(dragFrom.pos)
if (this.props.onDragStart(draggingPiece, dragFrom.pos) === false) {
return false
}
this.setState({dragFrom, draggingPiece})
return evt
}
handleDragStop(evt, drag) {
const node = drag.node
const {dragFrom, draggingPiece} = this.state
const dragTo = this.coordsToPosition({x: node.offsetLeft + drag.x, y: node.offsetTop + drag.y})
this.setState({dragFrom: null, targetTile: null, draggingPiece: null})
if (dragFrom.pos !== dragTo.pos) {
this.props.onMovePiece(draggingPiece, dragFrom.pos, dragTo.pos)
return false
}
return true
}
findPieceAtPosition(pos) {
for (let i = 0; i < this.props.pieces.length; i++) {
const piece = this.props.pieces[i]
if (piece.indexOf(pos) === 2) {
return {notation: piece, name: piece.slice(0, 1), index: i, position: pos}
}
}
return null
}
renderLabelText(x, y) {
const isLeftColumn = x === 0
const isBottomRow = y === 7
if (!this.props.drawLabels || (!isLeftColumn && !isBottomRow)) {
return null
}
if (isLeftColumn && isBottomRow) {
return [
<span key="blx" style={xLabelStyles}>
a
</span>,
<span key="bly" style={yLabelStyles}>
1
</span>
]
}
const label = isLeftColumn ? 8 - y : String.fromCharCode(decode.charCodeOffset + x)
return <span style={isLeftColumn ? yLabelStyles : xLabelStyles}>{label}</span>
}
render() {
const {targetTile, draggingPiece, boardSize} = this.state
const tiles = []
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
const isTarget = targetTile && targetTile.x === x && targetTile.y === y
const background = this.getSquareColor(x, y)
const boxShadow = isTarget ? 'inset 0px 0px 0px 0.4vmin yellow' : undefined
const styles = Object.assign({background, boxShadow}, squareStyles)
tiles.push(
<div key={`rect-${x}-${y}`} style={styles}>
{this.renderLabelText(x, y)}
</div>
)
}
}
const pieces = this.props.pieces.map((decl, i) => {
const isMoving = draggingPiece && i === draggingPiece.index
const {x, y, piece} = decode.fromPieceDecl(decl)
const Piece = pieceComponents[piece]
return (
<Draggable
bounds="parent"
position={{x: 0, y: 0}}
onStart={this.handleDragStart}
onDrag={this.handleDrag}
onStop={this.handleDragStop}
key={`${piece}-${x}-${y}`}>
<Piece isMoving={isMoving} x={x} y={y} />
</Draggable>
)
})
const children = tiles.concat(pieces)
const boardStyles = {position: 'relative', overflow: 'hidden', width: '100%', height: boardSize}
return (
<ResizeAware
ref={this.setBoardRef}
onlyEvent
onResize={this.handleResize}
style={boardStyles}>
{children}
</ResizeAware>
)
}
}
Chess.propTypes = {
allowMoves: PropTypes.bool,
highlightTarget: PropTypes.bool,
drawLabels: PropTypes.bool,
lightSquareColor: PropTypes.string,
darkSquareColor: PropTypes.string,
onMovePiece: PropTypes.func,
onDragStart: PropTypes.func,
pieces: PropTypes.arrayOf(PropTypes.string)
}
Chess.defaultProps = {
allowMoves: true,
highlightTarget: true,
drawLabels: true,
onMovePiece: noop,
onDragStart: noop,
lightSquareColor: '#f0d9b5',
darkSquareColor: '#b58863',
pieces: getDefaultLineup()
}
Chess.getDefaultLineup = getDefaultLineup
module.exports = Chess