@foblex/drag-toolkit
Version:
A TypeScript library providing foundational classes and utilities for implementing drag-and-drop functionality in Angular applications, with support for both mouse and touch events, and designed to work with or without Angular’s NgZone (zoneless applicati
301 lines (291 loc) • 11.6 kB
JavaScript
class IPointerEvent {
constructor(event, target) {
this.event = event;
this.target = target;
this.event = event;
}
get originalEvent() {
return this.event;
}
get targetElement() {
return this.target || this.originalEvent.target;
}
setTarget(target) {
this.target = target;
}
preventDefault() {
this.originalEvent.preventDefault();
}
get isEventInLockedContext() {
return this.targetElement.closest('[fLockedContext]') !== null;
}
}
class IMouseEvent extends IPointerEvent {
constructor(event, target) {
super(event, target);
}
isMouseLeftButton() {
return this.originalEvent.button === 0;
}
isMouseRightButton() {
return this.originalEvent.buttons === 2;
}
getPosition() {
return { x: this.originalEvent.clientX, y: this.originalEvent.clientY };
}
}
class ITouchDownEvent extends IPointerEvent {
constructor(event) {
super(event);
}
isMouseLeftButton() {
return true;
}
isMouseRightButton() {
return false;
}
getPosition() {
return { x: this.originalEvent.touches[0].clientX, y: this.originalEvent.touches[0].clientY };
}
}
class ITouchMoveEvent extends IPointerEvent {
constructor(event, target) {
super(event, target);
}
isMouseLeftButton() {
return true;
}
isMouseRightButton() {
return false;
}
getPosition() {
return { x: this.originalEvent.targetTouches[0].clientX, y: this.originalEvent.targetTouches[0].clientY };
}
}
class ITouchUpEvent extends IPointerEvent {
constructor(event) {
super(event);
}
isMouseLeftButton() {
return true;
}
isMouseRightButton() {
return false;
}
getPosition() {
return { x: this.originalEvent.changedTouches[0].clientX, y: this.originalEvent.changedTouches[0].clientY };
}
}
class EventExtensions {
static isPassiveEventListenerSupported() {
if (EventExtensions.isSupported == null && typeof window !== 'undefined') {
try {
window.addEventListener('test', EventExtensions.emptyListener, { passive: true });
EventExtensions.isSupported = true;
}
catch (e) {
EventExtensions.isSupported = false;
}
}
return EventExtensions.isSupported;
}
static passiveEventListener(options) {
return EventExtensions.isPassiveEventListenerSupported() ? options : !!options.capture;
}
static activeListener() {
return EventExtensions.passiveEventListener({ passive: false });
}
static passiveListener() {
return EventExtensions.passiveEventListener({ passive: true });
}
static emptyListener() {
return () => {
};
}
}
class IPointerUpEvent extends IPointerEvent {
constructor(event, target) {
super(event, target);
}
isMouseLeftButton() {
const evt = this.originalEvent;
return evt.pointerType === 'mouse' && evt.button === 0;
}
isMouseRightButton() {
const evt = this.originalEvent;
return evt.pointerType === 'mouse' && evt.button === 2;
}
getPosition() {
const evt = this.originalEvent;
return { x: evt.clientX, y: evt.clientY };
}
getPointerType() {
return this.originalEvent.pointerType;
}
getPointerId() {
return this.originalEvent.pointerId;
}
}
const MOUSE_EVENT_IGNORE_TIME = 800;
class DragAndDropBase {
constructor(ngZone) {
this.ngZone = ngZone;
this.mouseListeners = EventExtensions.emptyListener();
this.touchListeners = EventExtensions.emptyListener();
this.startListeners = EventExtensions.emptyListener();
this.lastTouchEventTime = 0;
this.isDragStarted = false;
this.dragStartThreshold = 3;
this.dragStartDelay = 0;
this.dragStartTime = 0;
this.dragStartPosition = { x: 0, y: 0 };
this.moveHandler = this.checkDragSequenceToStart;
this.pointerDownElement = null;
this.onMouseDown = (event) => {
var _a;
const isSyntheticEvent = this.isSyntheticEvent(event);
const isFakeEvent = isFakeMousedownFromScreenReader(event);
const mouseEvent = new IMouseEvent(event);
this.pointerDownElement = mouseEvent.targetElement;
if (isSyntheticEvent || isFakeEvent || this.disabled) {
return;
}
let result = this.onPointerDown(mouseEvent);
if (result) {
this.dragStartTime = Date.now();
this.dragStartPosition = mouseEvent.getPosition();
(_a = this.ngZone) === null || _a === void 0 ? void 0 : _a.runOutsideAngular(() => {
var _a, _b, _c;
(_a = this.document) === null || _a === void 0 ? void 0 : _a.addEventListener('selectstart', this.onSelectStart, EventExtensions.activeListener());
(_b = this.document) === null || _b === void 0 ? void 0 : _b.addEventListener('mousemove', this.onMouseMove);
(_c = this.document) === null || _c === void 0 ? void 0 : _c.addEventListener('pointerup', this.onPointerUpEvent);
});
this.mouseListeners = () => {
var _a, _b, _c;
(_a = this.document) === null || _a === void 0 ? void 0 : _a.removeEventListener('selectstart', this.onSelectStart, EventExtensions.activeListener());
(_b = this.document) === null || _b === void 0 ? void 0 : _b.removeEventListener('mousemove', this.onMouseMove);
(_c = this.document) === null || _c === void 0 ? void 0 : _c.removeEventListener('pointerup', this.onPointerUpEvent);
};
}
};
this.onTouchDown = (event) => {
var _a;
const isFakeEvent = isFakeTouchstartFromScreenReader(event);
const touchEvent = new ITouchDownEvent(event);
this.pointerDownElement = touchEvent.targetElement;
if (isFakeEvent || this.disabled) {
return;
}
let result = this.onPointerDown(touchEvent);
if (result) {
this.dragStartTime = Date.now();
this.dragStartPosition = touchEvent.getPosition();
(_a = this.ngZone) === null || _a === void 0 ? void 0 : _a.runOutsideAngular(() => {
var _a, _b, _c;
(_a = this.document) === null || _a === void 0 ? void 0 : _a.addEventListener('selectstart', this.onSelectStart, EventExtensions.activeListener());
(_b = this.document) === null || _b === void 0 ? void 0 : _b.addEventListener('touchmove', this.onTouchMove);
(_c = this.document) === null || _c === void 0 ? void 0 : _c.addEventListener('pointerup', this.onPointerUpEvent);
});
this.touchListeners = () => {
var _a, _b, _c;
(_a = this.document) === null || _a === void 0 ? void 0 : _a.removeEventListener('selectstart', this.onSelectStart, EventExtensions.activeListener());
(_b = this.document) === null || _b === void 0 ? void 0 : _b.removeEventListener('touchmove', this.onTouchMove);
(_c = this.document) === null || _c === void 0 ? void 0 : _c.removeEventListener('pointerup', this.onPointerUpEvent);
};
}
};
this.onSelectStart = (event) => {
this.onSelect(event);
};
this.onMouseMove = (event) => {
this.moveHandler(new IMouseEvent(event));
};
this.onTouchMove = (event) => {
this.moveHandler(new ITouchMoveEvent(event));
};
this.onPointerUpEvent = (event) => {
if (this.isDragStarted) {
this.onPointerUp(new IPointerUpEvent(event));
}
this.endDragSequence();
};
}
isSyntheticEvent(event) {
return !!this.lastTouchEventTime &&
(this.lastTouchEventTime + MOUSE_EVENT_IGNORE_TIME > Date.now());
}
checkDragSequenceToStart(event) {
const pointerPosition = event.getPosition();
if (!this.isDragStarted && this.pointerDownElement) {
event.setTarget(this.pointerDownElement);
const distanceX = Math.abs(pointerPosition.x - this.dragStartPosition.x);
const distanceY = Math.abs(pointerPosition.y - this.dragStartPosition.y);
const isOverThreshold = distanceX + distanceY >= this.dragStartThreshold;
if (isOverThreshold) {
const isDelayElapsed = Date.now() >= this.dragStartTime + this.dragStartDelay;
if (!isDelayElapsed) {
this.endDragSequence();
return;
}
event.preventDefault();
this.prepareDragSequence(event);
this.isDragStarted = true;
this.moveHandler = this.onPointerMove;
if (isTouchEvent(event.originalEvent)) {
this.lastTouchEventTime = Date.now();
}
}
}
}
endDragSequence() {
this.isDragStarted = false;
this.pointerDownElement = null;
this.moveHandler = this.checkDragSequenceToStart;
this.mouseListeners();
this.mouseListeners = EventExtensions.emptyListener();
this.touchListeners();
this.touchListeners = EventExtensions.emptyListener();
this.finalizeDragSequence();
}
subscribe(fDocument) {
var _a;
if (this.document) {
this.unsubscribe();
}
this.document = fDocument;
(_a = this.ngZone) === null || _a === void 0 ? void 0 : _a.runOutsideAngular(() => {
fDocument.addEventListener('mousedown', this.onMouseDown, EventExtensions.activeListener());
fDocument.addEventListener('touchstart', this.onTouchDown, EventExtensions.passiveListener());
});
this.startListeners = () => {
fDocument.removeEventListener('mousedown', this.onMouseDown, EventExtensions.activeListener());
fDocument.removeEventListener('touchstart', this.onTouchDown, EventExtensions.passiveListener());
};
}
unsubscribe() {
this.startListeners();
this.startListeners = EventExtensions.emptyListener();
this.touchListeners();
this.touchListeners = EventExtensions.emptyListener();
this.mouseListeners();
this.mouseListeners = EventExtensions.emptyListener();
}
}
function isTouchEvent(event) {
return event.type[0] === 't';
}
function isFakeMousedownFromScreenReader(event) {
return event.buttons === 0 || (event.offsetX === 0 && event.offsetY === 0);
}
function isFakeTouchstartFromScreenReader(event) {
const touch = (event.touches && event.touches[0]) || (event.changedTouches && event.changedTouches[0]);
return (!!touch &&
touch.identifier === -1 &&
(touch.radiusX == null || touch.radiusX === 1) &&
(touch.radiusY == null || touch.radiusY === 1));
}
/**
* Generated bundle index. Do not edit.
*/
export { DragAndDropBase, EventExtensions, IMouseEvent, IPointerEvent, ITouchDownEvent, ITouchMoveEvent, ITouchUpEvent, MOUSE_EVENT_IGNORE_TIME };
//# sourceMappingURL=foblex-drag-toolkit.js.map