84 lines
3.3 KiB
JavaScript
84 lines
3.3 KiB
JavaScript
import { isHTMLElement } from '../../utils/is-html-element.mjs';
|
|
import { isDragActive } from '../drag/state/is-active.mjs';
|
|
import { isNodeOrChild } from '../utils/is-node-or-child.mjs';
|
|
import { isPrimaryPointer } from '../utils/is-primary-pointer.mjs';
|
|
import { setupGesture } from '../utils/setup.mjs';
|
|
import { isElementKeyboardAccessible } from './utils/is-keyboard-accessible.mjs';
|
|
import { enableKeyboardPress } from './utils/keyboard.mjs';
|
|
import { isPressing } from './utils/state.mjs';
|
|
|
|
/**
|
|
* Filter out events that are not primary pointer events, or are triggering
|
|
* while a Motion gesture is active.
|
|
*/
|
|
function isValidPressEvent(event) {
|
|
return isPrimaryPointer(event) && !isDragActive();
|
|
}
|
|
/**
|
|
* Create a press gesture.
|
|
*
|
|
* Press is different to `"pointerdown"`, `"pointerup"` in that it
|
|
* automatically filters out secondary pointer events like right
|
|
* click and multitouch.
|
|
*
|
|
* It also adds accessibility support for keyboards, where
|
|
* an element with a press gesture will receive focus and
|
|
* trigger on Enter `"keydown"` and `"keyup"` events.
|
|
*
|
|
* This is different to a browser's `"click"` event, which does
|
|
* respond to keyboards but only for the `"click"` itself, rather
|
|
* than the press start and end/cancel. The element also needs
|
|
* to be focusable for this to work, whereas a press gesture will
|
|
* make an element focusable by default.
|
|
*
|
|
* @public
|
|
*/
|
|
function press(targetOrSelector, onPressStart, options = {}) {
|
|
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
|
|
const startPress = (startEvent) => {
|
|
const target = startEvent.currentTarget;
|
|
if (!isValidPressEvent(startEvent))
|
|
return;
|
|
isPressing.add(target);
|
|
const onPressEnd = onPressStart(target, startEvent);
|
|
const onPointerEnd = (endEvent, success) => {
|
|
window.removeEventListener("pointerup", onPointerUp);
|
|
window.removeEventListener("pointercancel", onPointerCancel);
|
|
if (isPressing.has(target)) {
|
|
isPressing.delete(target);
|
|
}
|
|
if (!isValidPressEvent(endEvent)) {
|
|
return;
|
|
}
|
|
if (typeof onPressEnd === "function") {
|
|
onPressEnd(endEvent, { success });
|
|
}
|
|
};
|
|
const onPointerUp = (upEvent) => {
|
|
onPointerEnd(upEvent, target === window ||
|
|
target === document ||
|
|
options.useGlobalTarget ||
|
|
isNodeOrChild(target, upEvent.target));
|
|
};
|
|
const onPointerCancel = (cancelEvent) => {
|
|
onPointerEnd(cancelEvent, false);
|
|
};
|
|
window.addEventListener("pointerup", onPointerUp, eventOptions);
|
|
window.addEventListener("pointercancel", onPointerCancel, eventOptions);
|
|
};
|
|
targets.forEach((target) => {
|
|
const pointerDownTarget = options.useGlobalTarget ? window : target;
|
|
pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
|
|
if (isHTMLElement(target)) {
|
|
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
|
|
if (!isElementKeyboardAccessible(target) &&
|
|
!target.hasAttribute("tabindex")) {
|
|
target.tabIndex = 0;
|
|
}
|
|
}
|
|
});
|
|
return cancelEvents;
|
|
}
|
|
|
|
export { press };
|