diff options
Diffstat (limited to 'src/client/util')
-rw-r--r-- | src/client/util/DragManager.ts | 19 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 12 | ||||
-rw-r--r-- | src/client/util/Transform.ts | 113 | ||||
-rw-r--r-- | src/client/util/UndoManager.ts | 133 |
4 files changed, 264 insertions, 13 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f4dcce7c8..eb4b3aeaa 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,3 +1,4 @@ +import { DocumentDecorations } from "../views/DocumentDecorations"; export namespace DragManager { export function Root() { @@ -43,6 +44,7 @@ export namespace DragManager { drop: (e: Event, de: DropEvent) => void; } + export function MakeDropTarget(element: HTMLElement, options: DropOptions): DragDropDisposer { if ("canDrop" in element.dataset) { throw new Error("Element is already droppable, can't make it droppable again"); @@ -59,10 +61,8 @@ export namespace DragManager { }; } - - let _lastPointerX: number = 0; - let _lastPointerY: number = 0; export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options: DragOptions) { + DocumentDecorations.Instance.Hidden = true; if (!dragDiv) { dragDiv = document.createElement("div"); DragManager.Root().appendChild(dragDiv); @@ -78,9 +78,13 @@ export namespace DragManager { dragElement.style.transformOrigin = "0 0"; dragElement.style.zIndex = "1000"; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; + dragElement.style.width = `${rect.width / scaleX}px`; + dragElement.style.height = `${rect.height / scaleY}px`; + // It seems like the above code should be able to just be this: + // dragElement.style.transform = `translate(${x}px, ${y}px)`; + // dragElement.style.width = `${rect.width}px`; + // dragElement.style.height = `${rect.height}px`; dragDiv.appendChild(dragElement); - _lastPointerX = dragData["xOffset"] + rect.left; - _lastPointerY = dragData["yOffset"] + rect.top; let hideSource = false; if (typeof options.hideSource === "boolean") { @@ -96,8 +100,8 @@ export namespace DragManager { const moveHandler = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - x += e.clientX - _lastPointerX; _lastPointerX = e.clientX; - y += e.clientY - _lastPointerY; _lastPointerY = e.clientY; + x += e.movementX; + y += e.movementY; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; }; const upHandler = (e: PointerEvent) => { @@ -127,5 +131,6 @@ export namespace DragManager { } })); options.handlers.dragComplete({}); + DocumentDecorations.Instance.Hidden = false; } }
\ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 0759ae110..1a711ae64 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,13 +1,13 @@ -import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; import { observable, action } from "mobx"; +import { DocumentView } from "../views/nodes/DocumentView"; export namespace SelectionManager { class Manager { @observable - SelectedDocuments: Array<CollectionFreeFormDocumentView> = []; + SelectedDocuments: Array<DocumentView> = []; @action - SelectDoc(doc: CollectionFreeFormDocumentView, ctrlPressed: boolean): void { + SelectDoc(doc: DocumentView, ctrlPressed: boolean): void { // if doc is not in SelectedDocuments, add it if (!ctrlPressed) { manager.SelectedDocuments = []; @@ -21,11 +21,11 @@ export namespace SelectionManager { const manager = new Manager; - export function SelectDoc(doc: CollectionFreeFormDocumentView, ctrlPressed: boolean): void { + export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void { manager.SelectDoc(doc, ctrlPressed) } - export function IsSelected(doc: CollectionFreeFormDocumentView): boolean { + export function IsSelected(doc: DocumentView): boolean { return manager.SelectedDocuments.indexOf(doc) !== -1; } @@ -33,7 +33,7 @@ export namespace SelectionManager { manager.SelectedDocuments = [] } - export function SelectedDocuments(): Array<CollectionFreeFormDocumentView> { + export function SelectedDocuments(): Array<DocumentView> { return manager.SelectedDocuments; } }
\ No newline at end of file diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts new file mode 100644 index 000000000..9fd4f7bef --- /dev/null +++ b/src/client/util/Transform.ts @@ -0,0 +1,113 @@ +export class Transform { + private _translateX: number = 0; + private _translateY: number = 0; + private _scale: number = 1; + + static get Identity(): Transform { + return new Transform(0, 0, 1); + } + + get TranslateX(): number { return this._translateX; } + get TranslateY(): number { return this._translateY; } + get Scale(): number { return this._scale; } + + constructor(x: number, y: number, scale: number) { + this._translateX = x; + this._translateY = y; + this._scale = scale; + } + + translate = (x: number, y: number): Transform => { + this._translateX += x; + this._translateY += y; + return this; + } + + scale = (scale: number): Transform => { + this._scale *= scale; + this._translateX *= scale; + this._translateY *= scale; + return this; + } + + scaleAbout = (scale: number, x: number, y: number): Transform => { + this._translateX += x * this._scale - x * this._scale * scale; + this._translateY += y * this._scale - y * this._scale * scale; + this._scale *= scale; + return this; + } + + transform = (transform: Transform): Transform => { + this._translateX = transform._translateX + transform._scale * this._translateX; + this._translateY = transform._translateY + transform._scale * this._translateY; + this._scale *= transform._scale; + return this; + } + + preTranslate = (x: number, y: number): Transform => { + this._translateX += this._scale * x; + this._translateY += this._scale * y; + return this; + } + + preScale = (scale: number): Transform => { + this._scale *= scale; + return this; + } + + preTransform = (transform: Transform): Transform => { + this._translateX += transform._translateX * this._scale; + this._translateY += transform._translateY * this._scale; + this._scale *= transform._scale; + return this; + } + + translated = (x: number, y: number): Transform => { + return this.copy().translate(x, y); + } + + preTranslated = (x: number, y: number): Transform => { + return this.copy().preTranslate(x, y); + } + + scaled = (scale: number): Transform => { + return this.copy().scale(scale); + } + + scaledAbout = (scale: number, x: number, y: number): Transform => { + return this.copy().scaleAbout(scale, x, y); + } + + preScaled = (scale: number): Transform => { + return this.copy().preScale(scale); + } + + transformed = (transform: Transform): Transform => { + return this.copy().transform(transform); + } + + preTransformed = (transform: Transform): Transform => { + return this.copy().preTransform(transform); + } + + transformPoint = (x: number, y: number): [number, number] => { + x *= this._scale; + x += this._translateX; + y *= this._scale; + y += this._translateY; + return [x, y]; + } + + transformDirection = (x: number, y: number): [number, number] => { + return [x * this._scale, y * this._scale]; + } + + inverse = () => { + return new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale) + } + + copy = () => { + return new Transform(this._translateX, this._translateY, this._scale); + } + +}
\ No newline at end of file diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts new file mode 100644 index 000000000..46ad558f3 --- /dev/null +++ b/src/client/util/UndoManager.ts @@ -0,0 +1,133 @@ +import { observable, action } from "mobx"; + +function propertyDecorator(target: any, key: string | symbol) { + Object.defineProperty(target, key, { + configurable: true, + enumerable: false, + get: function () { + return 5; + }, + set: function (value: any) { + Object.defineProperty(this, key, { + enumerable: false, + writable: true, + configurable: true, + value: function (...args: any[]) { + try { + UndoManager.StartBatch(); + return value.apply(this, args); + } finally { + UndoManager.EndBatch(); + } + } + }) + } + }) +} +export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any { + if (!descriptor) { + propertyDecorator(target, key); + return; + } + const oldFunction = descriptor.value; + + descriptor.value = function (...args: any[]) { + try { + UndoManager.StartBatch() + return oldFunction.apply(this, args) + } finally { + UndoManager.EndBatch() + } + } + + return descriptor; +} + +export namespace UndoManager { + export interface UndoEvent { + undo: () => void; + redo: () => void; + } + type UndoBatch = UndoEvent[]; + + let undoStack: UndoBatch[] = observable([]); + let redoStack: UndoBatch[] = observable([]); + let currentBatch: UndoBatch | undefined; + let batchCounter = 0; + let undoing = false; + + export function AddEvent(event: UndoEvent): void { + if (currentBatch && batchCounter && !undoing) { + currentBatch.push(event); + } + } + + export function CanUndo(): boolean { + return undoStack.length > 0; + } + + export function CanRedo(): boolean { + return redoStack.length > 0; + } + + export function StartBatch(): void { + batchCounter++; + if (batchCounter > 0) { + currentBatch = []; + } + } + + export const EndBatch = action(() => { + batchCounter--; + if (batchCounter === 0 && currentBatch && currentBatch.length) { + undoStack.push(currentBatch); + redoStack.length = 0; + currentBatch = undefined; + } + }) + + export function RunInBatch(fn: () => void) { + StartBatch(); + fn(); + EndBatch(); + } + + export const Undo = action(() => { + if (undoStack.length === 0) { + return; + } + + let commands = undoStack.pop(); + if (!commands) { + return; + } + + undoing = true; + for (let i = commands.length - 1; i >= 0; i--) { + commands[i].undo(); + } + undoing = false; + + redoStack.push(commands); + }) + + export const Redo = action(() => { + if (redoStack.length === 0) { + return; + } + + let commands = redoStack.pop(); + if (!commands) { + return; + } + + undoing = true; + for (let i = 0; i < commands.length; i++) { + commands[i].redo(); + } + undoing = false; + + undoStack.push(commands); + }) + +}
\ No newline at end of file |