155 lines
4.8 KiB
TypeScript
155 lines
4.8 KiB
TypeScript
import { h, VNode } from 'snabbdom';
|
|
import { Chessground } from 'chessground';
|
|
import { Config as CgConfig } from 'chessground/config';
|
|
import { MouchEvent } from 'chessground/types';
|
|
import * as util from 'chessground/util';
|
|
import EditorCtrl from './ctrl';
|
|
|
|
export default function (ctrl: EditorCtrl): VNode {
|
|
return h('div.cg-wrap', {
|
|
hook: {
|
|
insert: vnode => {
|
|
const el = vnode.elm as HTMLElement;
|
|
ctrl.chessground = Chessground(el, makeConfig(ctrl));
|
|
bindEvents(el, ctrl);
|
|
},
|
|
destroy: _ => ctrl.chessground!.destroy(),
|
|
},
|
|
});
|
|
}
|
|
|
|
function bindEvents(el: HTMLElement, ctrl: EditorCtrl): void {
|
|
const handler = onMouseEvent(ctrl);
|
|
['touchstart', 'touchmove', 'mousedown', 'mousemove', 'contextmenu'].forEach(function (ev) {
|
|
el.addEventListener(ev, handler);
|
|
});
|
|
}
|
|
|
|
function isLeftButton(e: MouchEvent): boolean {
|
|
return e.buttons === 1 || e.button === 1;
|
|
}
|
|
|
|
function isLeftClick(e: MouchEvent): boolean {
|
|
return isLeftButton(e) && !e.ctrlKey;
|
|
}
|
|
|
|
function isRightClick(e: MouchEvent): boolean {
|
|
return util.isRightButton(e) || (!!e.ctrlKey && isLeftButton(e));
|
|
}
|
|
|
|
let downKey: Key | undefined;
|
|
let lastKey: Key | undefined;
|
|
let placeDelete: boolean | undefined;
|
|
|
|
function onMouseEvent(ctrl: EditorCtrl): (e: MouchEvent) => void {
|
|
return function (e: MouchEvent): void {
|
|
const sel = ctrl.selected();
|
|
|
|
// do not generate corresponding mouse event
|
|
// (https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)
|
|
if (sel !== 'pointer' && e.cancelable !== false && (e.type === 'touchstart' || e.type === 'touchmove'))
|
|
e.preventDefault();
|
|
|
|
if (isLeftClick(e) || e.type === 'touchstart' || e.type === 'touchmove') {
|
|
if (
|
|
sel === 'pointer' ||
|
|
(ctrl.chessground &&
|
|
ctrl.chessground.state.draggable.current &&
|
|
ctrl.chessground.state.draggable.current.newPiece)
|
|
)
|
|
return;
|
|
const pos = util.eventPosition(e);
|
|
if (!pos) return;
|
|
const key = ctrl.chessground!.getKeyAtDomPos(pos);
|
|
if (!key) return;
|
|
if (e.type === 'mousedown' || e.type === 'touchstart') downKey = key;
|
|
if (sel === 'trash') deleteOrHidePiece(ctrl, key, e);
|
|
else {
|
|
const existingPiece = ctrl.chessground!.state.pieces.get(key);
|
|
const piece = {
|
|
color: sel[0],
|
|
role: sel[1],
|
|
};
|
|
const samePiece = existingPiece && piece.color == existingPiece.color && piece.role == existingPiece.role;
|
|
|
|
if ((e.type === 'mousedown' || e.type === 'touchstart') && samePiece) {
|
|
deleteOrHidePiece(ctrl, key, e);
|
|
placeDelete = true;
|
|
const endEvents = { mousedown: 'mouseup', touchstart: 'touchend' };
|
|
document.addEventListener(endEvents[e.type], () => (placeDelete = false), { once: true });
|
|
} else if (!placeDelete && (e.type === 'mousedown' || e.type === 'touchstart' || key !== lastKey)) {
|
|
ctrl.chessground!.setPieces(new Map([[key, piece]]));
|
|
ctrl.onChange();
|
|
ctrl.chessground!.cancelMove();
|
|
}
|
|
}
|
|
lastKey = key;
|
|
} else if (isRightClick(e)) {
|
|
if (sel !== 'pointer') {
|
|
ctrl.chessground!.state.drawable.current = undefined;
|
|
ctrl.chessground!.state.drawable.shapes = [];
|
|
|
|
if (e.type === 'contextmenu' && sel != 'trash') {
|
|
ctrl.chessground!.cancelMove();
|
|
sel[0] = util.opposite(sel[0]);
|
|
ctrl.redraw();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function deleteOrHidePiece(ctrl: EditorCtrl, key: Key, e: Event): void {
|
|
if (e.type === 'touchstart') {
|
|
if (ctrl.chessground!.state.pieces.has(key)) {
|
|
(ctrl.chessground!.state.draggable.current!.element as HTMLElement).style.display = 'none';
|
|
ctrl.chessground!.cancelMove();
|
|
}
|
|
document.addEventListener('touchend', () => deletePiece(ctrl, key), { once: true });
|
|
} else if (e.type === 'mousedown' || key !== downKey) {
|
|
deletePiece(ctrl, key);
|
|
}
|
|
}
|
|
|
|
function deletePiece(ctrl: EditorCtrl, key: Key): void {
|
|
ctrl.chessground!.setPieces(new Map([[key, undefined]]));
|
|
ctrl.onChange();
|
|
}
|
|
|
|
function makeConfig(ctrl: EditorCtrl): CgConfig {
|
|
return {
|
|
fen: ctrl.initialFen,
|
|
orientation: ctrl.options.orientation || 'white',
|
|
coordinates: !ctrl.cfg.embed,
|
|
autoCastle: false,
|
|
addPieceZIndex: ctrl.cfg.is3d,
|
|
movable: {
|
|
free: true,
|
|
color: 'both',
|
|
},
|
|
animation: {
|
|
duration: ctrl.cfg.animation.duration,
|
|
},
|
|
premovable: {
|
|
enabled: false,
|
|
},
|
|
drawable: {
|
|
enabled: true,
|
|
defaultSnapToValidMove: (lichess.storage.get('arrow.snap') || 1) != '0',
|
|
},
|
|
draggable: {
|
|
showGhost: true,
|
|
deleteOnDropOff: true,
|
|
},
|
|
selectable: {
|
|
enabled: false,
|
|
},
|
|
highlight: {
|
|
lastMove: false,
|
|
},
|
|
events: {
|
|
change: ctrl.onChange.bind(ctrl),
|
|
},
|
|
};
|
|
}
|