All files / utils gesture-pointer.ts

96.87% Statements 31/32
95% Branches 19/20
100% Functions 2/2
96.77% Lines 30/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106        94x 94x   94x 94x                                                   94x           87x       87x 87x       87x 87x   87x     183x 183x 183x       145x         145x   38x       87x 87x 36x     51x 5x     46x 46x 46x 1x     45x 45x 45x         45x                      
/**
 * @module Utils
 */
 
import * as MC from "@lisn/globals/minification-constants";
import * as MH from "@lisn/globals/minification-helpers";
 
import { getVectorDirection } from "@lisn/utils/directions";
import { getBrowserSupport } from "@lisn/utils/event";
import { GestureFragment } from "@lisn/utils/gesture";
 
/**
 * Returns a {@link GestureFragment} for the given events. If the browser
 * supports Pointer events, then only "pointermove" events will be considered.
 * Otherwise, only "mousemove" events will be considered.
 *
 * If there are less than 2 such events in the given list of events, returns
 * `false`.
 *
 * If the gesture is to be considered terminated, e.g. because there is
 * "pointercancel" in the list or buttons other than the primary are pressed,
 * returns `null`.
 *
 * Pointer gestures always require the primary button to be pressed and the
 * resulting intent is always "drag", and `deltaZ` is always 1.
 *
 * @param [options.angleDiffThreshold] See {@link getVectorDirection}
 *
 * @returns `false` if there are less than 2 "pointermove"/"mousemove" events
 * in the list, `null` if the gesture is terminated, otherwise a
 * {@link GestureFragment}.
 *
 * @category Gestures
 */
export const getPointerGestureFragment = (
  events: Event | readonly Event[],
  options?: {
    angleDiffThreshold?: number;
  },
): GestureFragment | null | false => {
  Iif (!MH.isIterableObject(events)) {
    events = [events];
  }
 
  let isCancelled = false;
  const supports = getBrowserSupport();
 
  // If the browser supports pointer events, then only take those; otherwise
  // take the mouse events
  const pointerEventClass = supports._pointer ? PointerEvent : MouseEvent;
  const pointerUpType = supports._pointer ? MC.S_POINTERUP : MC.S_MOUSEUP;
 
  const filteredEvents: MouseEvent[] = MH.filter(
    events,
    (event): event is MouseEvent => {
      const eType = event.type;
      isCancelled = isCancelled || eType === MC.S_POINTERCANCEL;
      if (eType !== MC.S_CLICK && MH.isInstanceOf(event, pointerEventClass)) {
        // Only events where the primary button is pressed (unless it's a
        // pointerup event, in which case no buttons should be pressed) are
        // considered, otherwise consider it terminated
        isCancelled =
          isCancelled ||
          (eType === pointerUpType && event.buttons !== 0) ||
          (eType !== pointerUpType && event.buttons !== 1);
        // we don't handle touch pointer events
        return !MH.isTouchPointerEvent(event);
      }
      return false;
    },
  );
 
  const numEvents = MH.lengthOf(filteredEvents);
  if (numEvents < 2) {
    return false; // no enough events
  }
 
  if (isCancelled) {
    return null; // terminated
  }
 
  const firstEvent = filteredEvents[0];
  const lastEvent = filteredEvents[numEvents - 1];
  if (MH.getPointerType(firstEvent) !== MH.getPointerType(lastEvent)) {
    return null; // different devices, consider it terminated
  }
 
  const deltaX = lastEvent.clientX - firstEvent.clientX;
  const deltaY = lastEvent.clientY - firstEvent.clientY;
  const direction = getVectorDirection(
    [deltaX, deltaY],
    options?.angleDiffThreshold,
  );
 
  return direction === MC.S_NONE
    ? false
    : {
        device: MC.S_POINTER,
        direction,
        intent: MC.S_DRAG,
        deltaX,
        deltaY,
        deltaZ: 1,
      };
};