2021-02-04 03:17:17 +00:00
|
|
|
import {dom, EventCB} from 'grainjs';
|
|
|
|
|
|
|
|
const DOUBLE_TAP_INTERVAL_MS = 500;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper to handle 'dblclick' events on either browser or mobile.
|
|
|
|
*
|
|
|
|
* This is equivalent to a 'dblclick' handler when touch events are not supported. When they are,
|
|
|
|
* the callback will be called on second touch within a short time of a first one. (In that case,
|
|
|
|
* preventDefault() prevents a 'dblclick' event from being emulated.)
|
|
|
|
*
|
|
|
|
* Background: though mobile browsers we care about already generate 'click' and 'dblclick' events
|
|
|
|
* in response to touch events, it doesn't seem to be treated as a direct user interaction. E.g.
|
|
|
|
* double-click to edit a cell should focus the editor and open the mobile keyboard, but a
|
|
|
|
* JS-issued focus() call only works when triggered by a direct user interaction, and synthesized
|
|
|
|
* dblclick doesn't seem to do that.
|
|
|
|
*
|
|
|
|
* Helpful links on emulated (synthesized) events:
|
|
|
|
* - https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent
|
|
|
|
* - https://github.com/w3c/pointerevents/issues/171
|
|
|
|
*/
|
|
|
|
export function onDblClickMatchElem(elem: EventTarget, selector: string, callback: EventCB): void {
|
2021-02-09 15:05:47 +00:00
|
|
|
// According to https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action, this "removes the
|
|
|
|
// need for browsers to delay the generation of click events when the user taps the screen".
|
|
|
|
// Without it, the delay (e.g. on mobile Chrome) prevents cursor from moving on double-tap.
|
|
|
|
dom.styleElem(elem as HTMLElement, 'touch-action', 'manipulation');
|
2021-02-04 03:17:17 +00:00
|
|
|
dom.onMatchElem(elem, selector, 'dblclick', (ev, _elem) => {
|
|
|
|
callback(ev, _elem);
|
|
|
|
});
|
|
|
|
|
|
|
|
let lastTapTime = 0;
|
2021-02-09 15:05:47 +00:00
|
|
|
let lastTapElem: EventTarget|null = null;
|
2021-02-04 03:17:17 +00:00
|
|
|
dom.onMatchElem(elem, selector, 'touchend', (ev, _elem) => {
|
|
|
|
const currentTime = Date.now();
|
|
|
|
const tapLength = currentTime - lastTapTime;
|
2021-02-09 15:05:47 +00:00
|
|
|
const sameElem = (_elem === lastTapElem);
|
2021-02-04 03:17:17 +00:00
|
|
|
lastTapTime = currentTime;
|
2021-02-09 15:05:47 +00:00
|
|
|
lastTapElem = _elem;
|
|
|
|
// Only consider a gesture a double-tap if it's on the same cell. Otherwise, two-finger
|
|
|
|
// gestures, such as zooming, may trigger this too.
|
|
|
|
if (sameElem && tapLength < DOUBLE_TAP_INTERVAL_MS && tapLength > 0) {
|
2021-02-04 03:17:17 +00:00
|
|
|
ev.preventDefault();
|
|
|
|
callback(ev, _elem);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|