@mui/x-internal-gestures
Version:
The core engine of GestureEvents, a modern and robust multi-pointer gesture detection library for JavaScript.
245 lines (221 loc) • 7.64 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MoveGesture = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _PointerGesture = require("../PointerGesture");
var _utils = require("../utils");
/**
* MoveGesture - Detects when a pointer enters, moves within, and leaves an element
*
* This gesture tracks pointer movements over an element, firing events when:
* - A pointer enters the element (start)
* - A pointer moves within the element (ongoing)
* - A pointer leaves the element (end)
*
* Unlike other gestures which often require specific actions to trigger,
* the move gesture fires automatically when pointers interact with the target element.
*
* This gesture only works with mouse pointers, not touch or pen.
*/
/**
* Configuration options for the MoveGesture
* Extends the base PointerGestureOptions
*/
/**
* Event data specific to move gesture events
* Includes the source pointer event and standard gesture data
*/
/**
* Type definition for the CustomEvent created by MoveGesture
*/
/**
* State tracking for the MoveGesture
*/
/**
* MoveGesture class for handling pointer movement over elements
*
* This gesture detects when pointers enter, move within, or leave target elements,
* and dispatches corresponding custom events.
*
* This gesture only works with hovering mouse pointers, not touch.
*/
class MoveGesture extends _PointerGesture.PointerGesture {
state = {
lastPosition: null
};
/**
* Movement threshold in pixels that must be exceeded before the gesture activates.
* Higher values reduce false positive gesture detection for small movements.
*/
constructor(options) {
super(options);
this.threshold = options.threshold || 0;
}
clone(overrides) {
return new MoveGesture((0, _extends2.default)({
name: this.name,
preventDefault: this.preventDefault,
stopPropagation: this.stopPropagation,
threshold: this.threshold,
minPointers: this.minPointers,
maxPointers: this.maxPointers,
requiredKeys: [...this.requiredKeys],
pointerMode: [...this.pointerMode],
preventIf: [...this.preventIf],
pointerOptions: structuredClone(this.pointerOptions)
}, overrides));
}
init(element, pointerManager, gestureRegistry, keyboardManager) {
super.init(element, pointerManager, gestureRegistry, keyboardManager);
// Add event listeners for entering and leaving elements
// These are different from pointer events handled by PointerManager
// @ts-expect-error, PointerEvent is correct.
this.element.addEventListener('pointerenter', this.handleElementEnter);
// @ts-expect-error, PointerEvent is correct.
this.element.addEventListener('pointerleave', this.handleElementLeave);
}
destroy() {
// Remove event listeners using the same function references
// @ts-expect-error, PointerEvent is correct.
this.element.removeEventListener('pointerenter', this.handleElementEnter);
// @ts-expect-error, PointerEvent is correct.
this.element.removeEventListener('pointerleave', this.handleElementLeave);
this.resetState();
super.destroy();
}
updateOptions(options) {
// Call parent method to handle common options
super.updateOptions(options);
}
resetState() {
this.isActive = false;
this.state = {
lastPosition: null
};
}
/**
* Handle pointer enter events for a specific element
* @param event The original pointer event
*/
handleElementEnter = event => {
if (event.pointerType !== 'mouse' && event.pointerType !== 'pen') {
return;
}
// Get pointers from the PointerManager
const pointers = this.pointerManager.getPointers() || new Map();
const pointersArray = Array.from(pointers.values());
// Only activate if we're within pointer count constraints
if (this.isWithinPointerCount(pointersArray, event.pointerType)) {
this.isActive = true;
const currentPosition = {
x: event.clientX,
y: event.clientY
};
this.state.lastPosition = currentPosition;
// Emit start event
this.emitMoveEvent(this.element, 'start', pointersArray, event);
this.emitMoveEvent(this.element, 'ongoing', pointersArray, event);
}
};
/**
* Handle pointer leave events for a specific element
* @param event The original pointer event
*/
handleElementLeave = event => {
if (event.pointerType !== 'mouse' && event.pointerType !== 'pen') {
return;
}
if (!this.isActive) {
return;
}
// Get pointers from the PointerManager
const pointers = this.pointerManager.getPointers() || new Map();
const pointersArray = Array.from(pointers.values());
// Emit end event and reset state
this.emitMoveEvent(this.element, 'end', pointersArray, event);
this.resetState();
};
/**
* Handle pointer events for the move gesture (only handles move events now)
* @param pointers Map of active pointers
* @param event The original pointer event
*/
handlePointerEvent = (pointers, event) => {
if (event.type !== 'pointermove' || event.pointerType !== 'mouse' && event.pointerType !== 'pen') {
return;
}
if (this.preventDefault) {
event.preventDefault();
}
if (this.stopPropagation) {
event.stopPropagation();
}
const pointersArray = Array.from(pointers.values());
// Find which element (if any) is being targeted
const targetElement = this.getTargetElement(event);
if (!targetElement) {
return;
}
if (!this.isWithinPointerCount(pointersArray, event.pointerType)) {
return;
}
if (this.shouldPreventGesture(targetElement, event.pointerType)) {
if (!this.isActive) {
return;
}
this.resetState();
this.emitMoveEvent(targetElement, 'end', pointersArray, event);
return;
}
// Update position
const currentPosition = {
x: event.clientX,
y: event.clientY
};
this.state.lastPosition = currentPosition;
if (!this.isActive) {
this.isActive = true;
this.emitMoveEvent(targetElement, 'start', pointersArray, event);
}
// Emit ongoing event
this.emitMoveEvent(targetElement, 'ongoing', pointersArray, event);
};
/**
* Emit move-specific events
* @param element The DOM element the event is related to
* @param phase The current phase of the gesture (start, ongoing, end)
* @param pointers Array of active pointers
* @param event The original pointer event
*/
emitMoveEvent(element, phase, pointers, event) {
const currentPosition = this.state.lastPosition || (0, _utils.calculateCentroid)(pointers);
// Get list of active gestures
const activeGestures = this.gesturesRegistry.getActiveGestures(element);
// Create custom event data
const customEventData = {
gestureName: this.name,
centroid: currentPosition,
target: event.target,
srcEvent: event,
phase,
pointers,
timeStamp: event.timeStamp,
activeGestures,
customData: this.customData
};
// Event names to trigger
const eventName = (0, _utils.createEventName)(this.name, phase);
// Dispatch custom events on the element
const domEvent = new CustomEvent(eventName, {
bubbles: true,
cancelable: true,
composed: true,
detail: customEventData
});
element.dispatchEvent(domEvent);
}
}
exports.MoveGesture = MoveGesture;