UNPKG

@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
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