diff options
Diffstat (limited to 'src/util/DragManager.ts')
-rw-r--r-- | src/util/DragManager.ts | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/util/DragManager.ts b/src/util/DragManager.ts new file mode 100644 index 000000000..1da590072 --- /dev/null +++ b/src/util/DragManager.ts @@ -0,0 +1,109 @@ +import { Opt } from "../fields/Field"; + +export namespace DragManager { + export let rootId = "root"; + let dragDiv: HTMLDivElement; + + export enum DragButtons { + Left = 1, Right = 2, Both = Left | Right + } + + interface DragOptions { + handlers: DragHandlers; + + buttons: number; + } + + export interface DragDisposer { + (): void; + } + + export class DragStartEvent { + private _cancelled: boolean = false; + get cancelled() { return this._cancelled }; + + cancel() { this._cancelled = true; }; + } + + export class DragCompleteEvent { + + } + + export interface DragHandlers { + dragStart: (e: DragStartEvent) => void; + dragComplete: (e: DragCompleteEvent) => void; + } + + export function MakeDraggable(element: HTMLElement, options: DragOptions): DragDisposer { + if ("draggable" in element.dataset) { + throw new Error("Element is already draggable, can't make it draggable again"); + } + element.dataset["draggable"] = "true"; + const dispose = () => { + document.removeEventListener("pointerup", upHandler); + document.removeEventListener("pointermove", startDragHandler); + } + const startDragHandler = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + dispose(); + StartDrag(element, e, options); + } + const upHandler = (e: PointerEvent) => { + dispose(); + }; + const downHandler = (e: PointerEvent) => { + document.addEventListener("pointermove", startDragHandler); + document.addEventListener("pointerup", upHandler); + }; + element.addEventListener("pointerdown", downHandler); + + return () => { + element.removeEventListener("pointerdown", downHandler); + element.dataset["draggable"] = undefined; + } + } + + function StartDrag(ele: HTMLElement, e: PointerEvent, options: DragOptions) { + if (!dragDiv) { + const root = document.getElementById(rootId); + if (!root) { + throw new Error("No root element found"); + } + dragDiv = document.createElement("div"); + root.appendChild(dragDiv); + } + if ((e.buttons & options.buttons) === 0) { + return; + } + let event = new DragStartEvent(); + options.handlers.dragStart(event); + if (event.cancelled) { + return; + } + let x = e.x, y = e.y; + let dragElement = ele.cloneNode(true) as HTMLElement; + dragElement.style.position = "absolute"; + dragElement.style.transform = `translate(${x}px, ${y}px)`; + dragDiv.appendChild(dragElement); + + const moveHandler = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + x += e.movementX; + y += e.movementY; + dragElement.style.transform = `translate(${x}px, ${y}px)`; + }; + const upHandler = (e: PointerEvent) => { + document.removeEventListener("pointermove", moveHandler, true); + document.removeEventListener("pointerup", upHandler); + FinishDrag(dragElement, options); + }; + document.addEventListener("pointermove", moveHandler, true); + document.addEventListener("pointerup", upHandler); + } + + function FinishDrag(ele: HTMLElement, options: DragOptions) { + dragDiv.removeChild(ele); + } +}
\ No newline at end of file |