index.mjs 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { isDragActive } from '../drag/state/is-active.mjs';
  2. import { isNodeOrChild } from '../utils/is-node-or-child.mjs';
  3. import { isPrimaryPointer } from '../utils/is-primary-pointer.mjs';
  4. import { setupGesture } from '../utils/setup.mjs';
  5. import { isElementKeyboardAccessible } from './utils/is-keyboard-accessible.mjs';
  6. import { enableKeyboardPress } from './utils/keyboard.mjs';
  7. import { isPressing } from './utils/state.mjs';
  8. /**
  9. * Filter out events that are not primary pointer events, or are triggering
  10. * while a Motion gesture is active.
  11. */
  12. function isValidPressEvent(event) {
  13. return isPrimaryPointer(event) && !isDragActive();
  14. }
  15. /**
  16. * Create a press gesture.
  17. *
  18. * Press is different to `"pointerdown"`, `"pointerup"` in that it
  19. * automatically filters out secondary pointer events like right
  20. * click and multitouch.
  21. *
  22. * It also adds accessibility support for keyboards, where
  23. * an element with a press gesture will receive focus and
  24. * trigger on Enter `"keydown"` and `"keyup"` events.
  25. *
  26. * This is different to a browser's `"click"` event, which does
  27. * respond to keyboards but only for the `"click"` itself, rather
  28. * than the press start and end/cancel. The element also needs
  29. * to be focusable for this to work, whereas a press gesture will
  30. * make an element focusable by default.
  31. *
  32. * @public
  33. */
  34. function press(targetOrSelector, onPressStart, options = {}) {
  35. const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
  36. const startPress = (startEvent) => {
  37. const target = startEvent.currentTarget;
  38. if (!isValidPressEvent(startEvent) || isPressing.has(target))
  39. return;
  40. isPressing.add(target);
  41. const onPressEnd = onPressStart(target, startEvent);
  42. const onPointerEnd = (endEvent, success) => {
  43. window.removeEventListener("pointerup", onPointerUp);
  44. window.removeEventListener("pointercancel", onPointerCancel);
  45. if (!isValidPressEvent(endEvent) || !isPressing.has(target)) {
  46. return;
  47. }
  48. isPressing.delete(target);
  49. if (typeof onPressEnd === "function") {
  50. onPressEnd(endEvent, { success });
  51. }
  52. };
  53. const onPointerUp = (upEvent) => {
  54. onPointerEnd(upEvent, target === window ||
  55. target === document ||
  56. options.useGlobalTarget ||
  57. isNodeOrChild(target, upEvent.target));
  58. };
  59. const onPointerCancel = (cancelEvent) => {
  60. onPointerEnd(cancelEvent, false);
  61. };
  62. window.addEventListener("pointerup", onPointerUp, eventOptions);
  63. window.addEventListener("pointercancel", onPointerCancel, eventOptions);
  64. };
  65. targets.forEach((target) => {
  66. const pointerDownTarget = options.useGlobalTarget ? window : target;
  67. pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
  68. if (target instanceof HTMLElement) {
  69. target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
  70. if (!isElementKeyboardAccessible(target) &&
  71. !target.hasAttribute("tabindex")) {
  72. target.tabIndex = 0;
  73. }
  74. }
  75. });
  76. return cancelEvents;
  77. }
  78. export { press };