From 861e27f0c729a9fa690610294ffa34b5a677888b Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 26 Oct 2022 17:23:38 +0400 Subject: [PATCH] Move Dragger to TS --- src/utils/{Dragger.js => Dragger.ts} | 237 ++++++++++++++++++--------- 1 file changed, 158 insertions(+), 79 deletions(-) rename src/utils/{Dragger.js => Dragger.ts} (64%) diff --git a/src/utils/Dragger.js b/src/utils/Dragger.ts similarity index 64% rename from src/utils/Dragger.js rename to src/utils/Dragger.ts index c0efc8331..676ff8a53 100644 --- a/src/utils/Dragger.js +++ b/src/utils/Dragger.ts @@ -1,71 +1,146 @@ import { bindAll, isFunction, result, isUndefined } from 'underscore'; import { on, off, isEscKey, getPointerEvent } from './mixins'; +type Position = { + x: number; + y: number; + end?: boolean; +}; + +type PositionXY = keyof Omit; + +type Guide = { + x: number; + y: number; + lock?: number; + active?: boolean; +}; + +interface DraggerOptions { + /** + * Element on which the drag will be executed. By default, the document will be used + */ + container?: HTMLElement; + + /** + * Callback on drag start. + * @example + * onStart(ev, dragger) { + * console.log('pointer start', dragger.startPointer, 'position start', dragger.startPosition); + * } + */ + onStart?: (ev: Event, dragger: Dragger) => void; + + /** + * Callback on drag. + * @example + * onDrag(ev, dragger) { + * console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta); + * } + */ + onDrag?: (ev: Event, dragger: Dragger) => void; + + /** + * Callback on drag end. + * @example + * onEnd(ev, dragger) { + * console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta); + * } + */ + onEnd?: (ev: Event, dragger: Dragger, opts: { cancelled: boolean }) => void; + + /** + * Indicate a callback where to pass an object with new coordinates + */ + setPosition?: (position: Position) => void; + + /** + * Indicate a callback where to get initial coordinates. + * @example + * getPosition: () => { + * // ... + * return { x: 10, y: 100 } + * } + */ + getPosition?: () => Position; + + /** + * Indicate a callback where to get pointer coordinates. + */ + getPointerPosition?: (ev: Event) => Position; + + /** + * Static guides to be snapped. + */ + guidesStatic?: () => Guide[]; + + /** + * Target guides that will snap to static one. + */ + guidesTarget?: () => Guide[]; + + /** + * Offset before snap to guides. + * @default 5 + */ + snapOffset?: number; + + /** + * Document on which listen to pointer events. + */ + doc?: Document; + + /** + * Scale result points, can also be a function. + * @default 1 + */ + scale?: number; +} + const resetPos = () => ({ x: 0, y: 0 }); +const xyArr: PositionXY[] = ['x', 'y']; + export default class Dragger { + opts: DraggerOptions; + startPointer: Position; + delta: Position; + lastScroll: Position; + lastScrollDiff: Position; + startPosition: Position; + globScrollDiff: Position; + currentPointer: Position; + position: Position; + el?: HTMLElement; + guidesStatic: Guide[]; + guidesTarget: Guide[]; + lockedAxis?: any; + docs: Document[]; + trgX?: Guide; + trgY?: Guide; + /** * Init the dragger * @param {Object} opts */ - constructor(opts = {}) { + constructor(opts: DraggerOptions = {}) { this.opts = { - /** - * Element on which the drag will be executed. By default, the document will be used - */ - container: null, - /** - * Callback on start - * onStart(ev, dragger) { - * console.log('pointer start', dragger.startPointer, 'position start', dragger.startPosition); - * }, - */ - onStart: null, - /** - * Callback on drag - * onDrag(ev, dragger) { - * console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta); - * }, - */ - onDrag: null, - /** - * Callback on drag - * onEnd(ev, dragger) { - * console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta); - * }, - */ - onEnd: null, - /** - * Indicate a callback where to pass an object with new coordinates - */ - setPosition: null, - /** - * Indicate a callback where to get initial coordinates. - * getPosition: () => { - * ... - * return { x: 10, y: 100 } - * } - */ - getPosition: null, - - // Static guides to be snapped - guidesStatic: null, - - // Target guides that will snap to static one - guidesTarget: null, - - // Offset before snap to guides snapOffset: 5, - - // Document on which listen to pointer events - doc: 0, - - // Scale result points, can also be a function scale: 1, }; bindAll(this, 'drag', 'stop', 'keyHandle', 'handleScroll'); this.setOptions(opts); this.delta = resetPos(); + this.lastScroll = resetPos(); + this.lastScrollDiff = resetPos(); + this.startPointer = resetPos(); + this.startPosition = resetPos(); + this.globScrollDiff = resetPos(); + this.currentPointer = resetPos(); + this.position = resetPos(); + this.guidesStatic = []; + this.guidesTarget = []; + this.docs = []; return this; } @@ -73,14 +148,14 @@ export default class Dragger { * Update options * @param {Object} options */ - setOptions(opts = {}) { + setOptions(opts: Partial = {}) { this.opts = { ...this.opts, ...opts, }; } - toggleDrag(enable) { + toggleDrag(enable?: boolean) { const docs = this.getDocumentEl(); const container = this.getContainerEl(); const win = this.getWindowEl(); @@ -96,8 +171,8 @@ export default class Dragger { const { lastScroll, delta } = this; const actualScroll = this.getScrollInfo(); const scrollDiff = { - x: actualScroll.x - lastScroll.x, - y: actualScroll.y - lastScroll.y, + x: actualScroll.x - lastScroll!.x, + y: actualScroll.y - lastScroll!.y, }; this.move(delta.x + scrollDiff.x, delta.y + scrollDiff.y); this.lastScrollDiff = scrollDiff; @@ -107,10 +182,10 @@ export default class Dragger { * Start dragging * @param {Event} e */ - start(ev) { + start(ev: Event) { const { opts } = this; const { onStart } = opts; - this.toggleDrag(1); + this.toggleDrag(true); this.startPointer = this.getPointerPos(ev); this.guidesStatic = result(opts, 'guidesStatic') || []; this.guidesTarget = result(opts, 'guidesTarget') || []; @@ -125,7 +200,7 @@ export default class Dragger { * Drag event * @param {Event} event */ - drag(ev) { + drag(ev: Event) { const { opts, lastScrollDiff, globScrollDiff } = this; const { onDrag } = opts; const { startPointer } = this; @@ -142,7 +217,7 @@ export default class Dragger { this.lastScrollDiff = resetPos(); let { lockedAxis } = this; - // Lock one axis + // @ts-ignore Lock one axis if (ev.shiftKey) { lockedAxis = !lockedAxis && this.detectAxisLock(delta.x, delta.y); } else { @@ -155,8 +230,8 @@ export default class Dragger { delta.y = startPointer.y; } - const moveDelta = delta => { - ['x', 'y'].forEach(co => (delta[co] = delta[co] * result(opts, 'scale'))); + const moveDelta = (delta: Position) => { + xyArr.forEach(co => (delta[co] = delta[co] * result(opts, 'scale'))); this.delta = delta; this.move(delta.x, delta.y); isFunction(onDrag) && onDrag(ev, this); @@ -172,21 +247,21 @@ export default class Dragger { (trgX || trgY) && moveDelta(newDelta); } - // In case the mouse button was released outside of the window + // @ts-ignore In case the mouse button was released outside of the window ev.which === 0 && this.stop(ev); } /** * Check if the delta hits some guide */ - snapGuides(delta) { + snapGuides(delta: Position) { const newDelta = delta; let { trgX, trgY } = this; this.guidesTarget.forEach(trg => { // Skip the guide if its locked axis already exists if ((trg.x && this.trgX) || (trg.y && this.trgY)) return; - trg.active = 0; + trg.active = false; this.guidesStatic.forEach(stat => { if ((trg.y && stat.x) || (trg.x && stat.y)) return; @@ -209,8 +284,9 @@ export default class Dragger { trgX = this.trgX; trgY = this.trgY; - ['x', 'y'].forEach(co => { + xyArr.forEach(co => { const axis = co.toUpperCase(); + // @ts-ignore let trg = this[`trg${axis}`]; if (trg && !this.isPointIn(delta[co], trg.lock)) { @@ -230,22 +306,24 @@ export default class Dragger { }; } - isPointIn(src, trg, { offset } = {}) { - const ofst = offset || this.opts.snapOffset; + isPointIn(src: number, trg: number, { offset }: { offset?: number } = {}) { + const ofst = offset || this.opts.snapOffset || 0; return (src >= trg && src <= trg + ofst) || (src <= trg && src >= trg - ofst); } - setGuideLock(guide, value) { + setGuideLock(guide: Guide, value: any) { const axis = !isUndefined(guide.x) ? 'X' : 'Y'; const trgName = `trg${axis}`; if (value !== null) { - guide.active = 1; + guide.active = true; guide.lock = value; + // @ts-ignore this[trgName] = guide; } else { delete guide.active; delete guide.lock; + // @ts-ignore delete this[trgName]; } @@ -255,21 +333,21 @@ export default class Dragger { /** * Stop dragging */ - stop(ev, opts = {}) { + stop(ev: Event, opts: { cancel?: boolean } = {}) { const { delta } = this; - const cancelled = opts.cancel; + const cancelled = !!opts.cancel; const x = cancelled ? 0 : delta.x; const y = cancelled ? 0 : delta.y; this.toggleDrag(); this.lockedAxis = null; - this.move(x, y, 1); + this.move(x, y, true); const { onEnd } = this.opts; isFunction(onEnd) && onEnd(ev, this, { cancelled }); } - keyHandle(ev) { - if (isEscKey(ev)) { - this.stop(ev, { cancel: 1 }); + keyHandle(ev: Event) { + if (isEscKey(ev as KeyboardEvent)) { + this.stop(ev, { cancel: true }); } } @@ -278,7 +356,7 @@ export default class Dragger { * @param {integer} x * @param {integer} y */ - move(x, y, end) { + move(x: number, y: number, end?: boolean) { const { el, opts } = this; const pos = this.startPosition; if (!pos) return; @@ -308,6 +386,7 @@ export default class Dragger { const cont = this.getContainerEl(); return cont.map(item => { const doc = item.ownerDocument || item; + // @ts-ignore return doc.defaultView || doc.parentWindow; }); } @@ -315,7 +394,7 @@ export default class Dragger { /** * Returns documents */ - getDocumentEl(el) { + getDocumentEl(el?: HTMLElement): Document[] { const { doc } = this.opts; el = el || this.el; @@ -334,7 +413,7 @@ export default class Dragger { * @param {Event} event * @return {Object} */ - getPointerPos(ev) { + getPointerPos(ev: Event) { const getPos = this.opts.getPointerPosition; const pEv = getPointerEvent(ev); @@ -373,7 +452,7 @@ export default class Dragger { }; } - detectAxisLock(x, y) { + detectAxisLock(x: number, y: number) { const relX = x; const relY = y; const absX = Math.abs(relX);