SafetyScreen-ui/node_modules/@interactjs/core/interactions.js

278 lines
7.6 KiB
JavaScript
Raw Normal View History

2025-03-29 19:33:58 +08:00
import { Scope, SignalArgs } from "./scope.js";
import browser from "../utils/browser.js";
import domObjects from "../utils/domObjects.js";
import { nodeContains } from "../utils/domUtils.js";
import * as pointerUtils from "../utils/pointerUtils.js";
import InteractionBase from "./Interaction.js";
import interactablePreventDefault from "./interactablePreventDefault.js";
import finder from "./interactionFinder.js";
const methodNames = ['pointerDown', 'pointerMove', 'pointerUp', 'updatePointer', 'removePointer', 'windowBlur'];
function install(scope) {
const listeners = {};
for (const method of methodNames) {
listeners[method] = doOnInteractions(method, scope);
}
const pEventTypes = browser.pEventTypes;
let docEvents;
if (domObjects.PointerEvent) {
docEvents = [{
type: pEventTypes.down,
listener: releasePointersOnRemovedEls
}, {
type: pEventTypes.down,
listener: listeners.pointerDown
}, {
type: pEventTypes.move,
listener: listeners.pointerMove
}, {
type: pEventTypes.up,
listener: listeners.pointerUp
}, {
type: pEventTypes.cancel,
listener: listeners.pointerUp
}];
} else {
docEvents = [{
type: 'mousedown',
listener: listeners.pointerDown
}, {
type: 'mousemove',
listener: listeners.pointerMove
}, {
type: 'mouseup',
listener: listeners.pointerUp
}, {
type: 'touchstart',
listener: releasePointersOnRemovedEls
}, {
type: 'touchstart',
listener: listeners.pointerDown
}, {
type: 'touchmove',
listener: listeners.pointerMove
}, {
type: 'touchend',
listener: listeners.pointerUp
}, {
type: 'touchcancel',
listener: listeners.pointerUp
}];
}
docEvents.push({
type: 'blur',
listener(event) {
for (const interaction of scope.interactions.list) {
interaction.documentBlur(event);
}
}
}); // for ignoring browser's simulated mouse events
scope.prevTouchTime = 0;
scope.Interaction = class extends InteractionBase {
get pointerMoveTolerance() {
return scope.interactions.pointerMoveTolerance;
}
set pointerMoveTolerance(value) {
scope.interactions.pointerMoveTolerance = value;
}
_now() {
return scope.now();
}
};
scope.interactions = {
// all active and idle interactions
list: [],
new(options) {
options.scopeFire = (name, arg) => scope.fire(name, arg);
const interaction = new scope.Interaction(options);
scope.interactions.list.push(interaction);
return interaction;
},
listeners,
docEvents,
pointerMoveTolerance: 1
};
function releasePointersOnRemovedEls() {
// for all inactive touch interactions with pointers down
for (const interaction of scope.interactions.list) {
if (!interaction.pointerIsDown || interaction.pointerType !== 'touch' || interaction._interacting) {
continue;
} // if a pointer is down on an element that is no longer in the DOM tree
for (const pointer of interaction.pointers) {
if (!scope.documents.some(({
doc
}) => nodeContains(doc, pointer.downTarget))) {
// remove the pointer from the interaction
interaction.removePointer(pointer.pointer, pointer.event);
}
}
}
}
scope.usePlugin(interactablePreventDefault);
}
function doOnInteractions(method, scope) {
return function (event) {
const interactions = scope.interactions.list;
const pointerType = pointerUtils.getPointerType(event);
const [eventTarget, curEventTarget] = pointerUtils.getEventTargets(event);
const matches = []; // [ [pointer, interaction], ...]
if (/^touch/.test(event.type)) {
scope.prevTouchTime = scope.now(); // @ts-expect-error
for (const changedTouch of event.changedTouches) {
const pointer = changedTouch;
const pointerId = pointerUtils.getPointerId(pointer);
const searchDetails = {
pointer,
pointerId,
pointerType,
eventType: event.type,
eventTarget,
curEventTarget,
scope
};
const interaction = getInteraction(searchDetails);
matches.push([searchDetails.pointer, searchDetails.eventTarget, searchDetails.curEventTarget, interaction]);
}
} else {
let invalidPointer = false;
if (!browser.supportsPointerEvent && /mouse/.test(event.type)) {
// ignore mouse events while touch interactions are active
for (let i = 0; i < interactions.length && !invalidPointer; i++) {
invalidPointer = interactions[i].pointerType !== 'mouse' && interactions[i].pointerIsDown;
} // try to ignore mouse events that are simulated by the browser
// after a touch event
invalidPointer = invalidPointer || scope.now() - scope.prevTouchTime < 500 || // on iOS and Firefox Mobile, MouseEvent.timeStamp is zero if simulated
event.timeStamp === 0;
}
if (!invalidPointer) {
const searchDetails = {
pointer: event,
pointerId: pointerUtils.getPointerId(event),
pointerType,
eventType: event.type,
curEventTarget,
eventTarget,
scope
};
const interaction = getInteraction(searchDetails);
matches.push([searchDetails.pointer, searchDetails.eventTarget, searchDetails.curEventTarget, interaction]);
}
} // eslint-disable-next-line no-shadow
for (const [pointer, eventTarget, curEventTarget, interaction] of matches) {
interaction[method](pointer, event, eventTarget, curEventTarget);
}
};
}
function getInteraction(searchDetails) {
const {
pointerType,
scope
} = searchDetails;
const foundInteraction = finder.search(searchDetails);
const signalArg = {
interaction: foundInteraction,
searchDetails
};
scope.fire('interactions:find', signalArg);
return signalArg.interaction || scope.interactions.new({
pointerType
});
}
function onDocSignal({
doc,
scope,
options
}, eventMethodName) {
const {
interactions: {
docEvents
},
events
} = scope;
const eventMethod = events[eventMethodName];
if (scope.browser.isIOS && !options.events) {
options.events = {
passive: false
};
} // delegate event listener
for (const eventType in events.delegatedEvents) {
eventMethod(doc, eventType, events.delegateListener);
eventMethod(doc, eventType, events.delegateUseCapture, true);
}
const eventOptions = options && options.events;
for (const {
type,
listener
} of docEvents) {
eventMethod(doc, type, listener, eventOptions);
}
}
const interactions = {
id: 'core/interactions',
install,
listeners: {
'scope:add-document': arg => onDocSignal(arg, 'add'),
'scope:remove-document': arg => onDocSignal(arg, 'remove'),
'interactable:unset': ({
interactable
}, scope) => {
// Stop and destroy related interactions when an Interactable is unset
for (let i = scope.interactions.list.length - 1; i >= 0; i--) {
const interaction = scope.interactions.list[i];
if (interaction.interactable !== interactable) {
continue;
}
interaction.stop();
scope.fire('interactions:destroy', {
interaction
});
interaction.destroy();
if (scope.interactions.list.length > 2) {
scope.interactions.list.splice(i, 1);
}
}
}
},
onDocSignal,
doOnInteractions,
methodNames
};
export default interactions;
//# sourceMappingURL=interactions.js.map