aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DragManager.ts19
-rw-r--r--src/client/util/SelectionManager.ts12
-rw-r--r--src/client/util/Transform.ts113
-rw-r--r--src/client/util/UndoManager.ts133
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