|
|
|
/*
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import cancelEvent from '../helpers/dom/cancelEvent';
|
|
|
|
import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
|
|
|
|
import safeAssign from '../helpers/object/safeAssign';
|
|
|
|
import contextMenuController from '../helpers/contextMenuController';
|
|
|
|
|
|
|
|
const getEvent = (e: TouchEvent | MouseEvent) => {
|
|
|
|
return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent;
|
|
|
|
};
|
|
|
|
|
|
|
|
const attachGlobalListenerTo = window;
|
|
|
|
|
|
|
|
let RESET_GLOBAL = false;
|
|
|
|
contextMenuController.addEventListener('toggle', (visible) => {
|
|
|
|
RESET_GLOBAL = visible;
|
|
|
|
});
|
|
|
|
|
|
|
|
export type SwipeHandlerOptions = {
|
|
|
|
element: SwipeHandler['element'],
|
|
|
|
onSwipe: SwipeHandler['onSwipe'],
|
|
|
|
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
|
|
|
|
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
|
|
|
|
onReset?: SwipeHandler['onReset'],
|
|
|
|
cursor?: SwipeHandler['cursor'],
|
|
|
|
cancelEvent?: SwipeHandler['cancelEvent'],
|
|
|
|
listenerOptions?: SwipeHandler['listenerOptions']
|
|
|
|
};
|
|
|
|
|
|
|
|
export default class SwipeHandler {
|
|
|
|
private element: HTMLElement;
|
|
|
|
private onSwipe: (xDiff: number, yDiff: number, e: TouchEvent | MouseEvent) => boolean | void;
|
|
|
|
private verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean | Promise<boolean>;
|
|
|
|
private onFirstSwipe: () => void;
|
|
|
|
private onReset: () => void;
|
|
|
|
private cursor: 'grabbing' | 'move' | 'row-resize' | 'col-resize' | 'nesw-resize' | 'nwse-resize' | 'ne-resize' | 'se-resize' | 'sw-resize' | 'nw-resize' | 'n-resize' | 'e-resize' | 's-resize' | 'w-resize' | '' = 'grabbing';
|
|
|
|
private cancelEvent = true;
|
|
|
|
private listenerOptions: boolean | AddEventListenerOptions = false;
|
|
|
|
private setCursorTo: HTMLElement;
|
|
|
|
|
|
|
|
private hadMove = false;
|
|
|
|
private xDown: number = null;
|
|
|
|
private yDown: number = null;
|
|
|
|
|
|
|
|
constructor(options: SwipeHandlerOptions) {
|
|
|
|
safeAssign(this, options);
|
|
|
|
|
|
|
|
this.setCursorTo = this.element;
|
|
|
|
|
|
|
|
this.setListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
public setListeners() {
|
|
|
|
if(!IS_TOUCH_SUPPORTED) {
|
|
|
|
this.element.addEventListener('mousedown', this.handleStart, this.listenerOptions);
|
|
|
|
attachGlobalListenerTo.addEventListener('mouseup', this.reset);
|
|
|
|
} else {
|
|
|
|
this.element.addEventListener('touchstart', this.handleStart, this.listenerOptions);
|
|
|
|
attachGlobalListenerTo.addEventListener('touchend', this.reset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public removeListeners() {
|
|
|
|
if(!IS_TOUCH_SUPPORTED) {
|
|
|
|
this.element.removeEventListener('mousedown', this.handleStart, this.listenerOptions);
|
|
|
|
attachGlobalListenerTo.removeEventListener('mouseup', this.reset);
|
|
|
|
} else {
|
|
|
|
this.element.removeEventListener('touchstart', this.handleStart, this.listenerOptions);
|
|
|
|
attachGlobalListenerTo.removeEventListener('touchend', this.reset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public setCursor(cursor: SwipeHandler['cursor']) {
|
|
|
|
this.cursor = cursor;
|
|
|
|
|
|
|
|
if(!IS_TOUCH_SUPPORTED && this.hadMove) {
|
|
|
|
this.setCursorTo.style.setProperty('cursor', this.cursor, 'important');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reset = (e?: Event) => {
|
|
|
|
/* if(e) {
|
|
|
|
cancelEvent(e);
|
|
|
|
} */
|
|
|
|
|
|
|
|
if(IS_TOUCH_SUPPORTED) {
|
|
|
|
attachGlobalListenerTo.removeEventListener('touchmove', this.handleMove, {capture: true});
|
|
|
|
} else {
|
|
|
|
attachGlobalListenerTo.removeEventListener('mousemove', this.handleMove);
|
|
|
|
this.setCursorTo.style.cursor = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.onReset && this.hadMove) {
|
|
|
|
this.onReset();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.xDown = this.yDown = null;
|
|
|
|
this.hadMove = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
handleStart = async(_e: TouchEvent | MouseEvent) => {
|
|
|
|
const e = getEvent(_e);
|
|
|
|
if(this.verifyTouchTarget && !(await this.verifyTouchTarget(_e))) {
|
|
|
|
return this.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.xDown = e.clientX;
|
|
|
|
this.yDown = e.clientY;
|
|
|
|
|
|
|
|
if(IS_TOUCH_SUPPORTED) {
|
|
|
|
attachGlobalListenerTo.addEventListener('touchmove', this.handleMove, {passive: false, capture: true});
|
|
|
|
} else {
|
|
|
|
attachGlobalListenerTo.addEventListener('mousemove', this.handleMove, false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
handleMove = (_e: TouchEvent | MouseEvent) => {
|
|
|
|
if(this.xDown === null || this.yDown === null || RESET_GLOBAL) {
|
|
|
|
this.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.cancelEvent) {
|
|
|
|
cancelEvent(_e);
|
|
|
|
}
|
|
|
|
|
|
|
|
const e = getEvent(_e);
|
|
|
|
const xUp = e.clientX;
|
|
|
|
const yUp = e.clientY;
|
|
|
|
|
|
|
|
const xDiff = this.xDown - xUp;
|
|
|
|
const yDiff = this.yDown - yUp;
|
|
|
|
|
|
|
|
if(!this.hadMove) {
|
|
|
|
if(!xDiff && !yDiff) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.hadMove = true;
|
|
|
|
|
|
|
|
if(!IS_TOUCH_SUPPORTED) {
|
|
|
|
this.setCursorTo.style.setProperty('cursor', this.cursor, 'important');
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.onFirstSwipe) {
|
|
|
|
this.onFirstSwipe();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if(Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
|
|
|
|
// if(xDiff > 0) { /* left swipe */
|
|
|
|
|
|
|
|
// } else { /* right swipe */
|
|
|
|
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// if(yDiff > 0) { /* up swipe */
|
|
|
|
|
|
|
|
// } else { /* down swipe */
|
|
|
|
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
/* reset values */
|
|
|
|
const onSwipeResult = this.onSwipe(xDiff, yDiff, _e);
|
|
|
|
if(onSwipeResult !== undefined && onSwipeResult) {
|
|
|
|
this.reset();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|