aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DragManager.ts82
-rw-r--r--src/client/util/Scripting.ts2
-rw-r--r--src/client/util/Transform.ts6
-rw-r--r--src/client/util/UndoManager.ts133
4 files changed, 208 insertions, 15 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 337ec855a..60910a40b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,3 +1,37 @@
+import { DocumentDecorations } from "../views/DocumentDecorations";
+import { CollectionDockingView } from "../views/collections/CollectionDockingView";
+import { Document } from "../../fields/Document"
+import { action } from "mobx";
+import { DocumentView } from "../views/nodes/DocumentView";
+
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document) {
+ let onRowMove = action((e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointerup', onRowUp);
+ DragManager.StartDrag(_reference.current!, { document: docFunc() });
+ });
+ let onRowUp = action((e: PointerEvent): void => {
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointerup', onRowUp);
+ });
+ let onItemDown = (e: React.PointerEvent) => {
+ // if (this.props.isSelected() || this.props.isTopMost) {
+ if (e.button == 0) {
+ e.stopPropagation();
+ if (e.shiftKey) {
+ CollectionDockingView.Instance.StartOtherDrag(docFunc(), e);
+ } else {
+ document.addEventListener("pointermove", onRowMove);
+ document.addEventListener('pointerup', onRowUp);
+ }
+ }
+ //}
+ }
+ return onItemDown;
+}
export namespace DragManager {
export function Root() {
@@ -43,6 +77,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 +94,8 @@ export namespace DragManager {
};
}
-
- let _lastPointerX: number = 0;
- let _lastPointerY: number = 0;
- export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options: DragOptions) {
+ 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);
@@ -75,6 +108,8 @@ export namespace DragManager {
let dragElement = ele.cloneNode(true) as HTMLElement;
dragElement.style.opacity = "0.7";
dragElement.style.position = "absolute";
+ dragElement.style.bottom = "";
+ dragElement.style.left = "";
dragElement.style.transformOrigin = "0 0";
dragElement.style.zIndex = "1000";
dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
@@ -87,38 +122,54 @@ export namespace DragManager {
dragDiv.appendChild(dragElement);
let hideSource = false;
- if (typeof options.hideSource === "boolean") {
- hideSource = options.hideSource;
- } else {
- hideSource = options.hideSource();
+ if (options) {
+ if (typeof options.hideSource === "boolean") {
+ hideSource = options.hideSource;
+ } else {
+ hideSource = options.hideSource();
+ }
}
const wasHidden = ele.hidden;
if (hideSource) {
ele.hidden = true;
}
-
const moveHandler = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
x += e.movementX;
y += e.movementY;
+ if (e.shiftKey) {
+ abortDrag();
+ const docView: DocumentView = dragData["documentView"];
+ const doc: Document = docView ? docView.props.Document : dragData["document"];
+ CollectionDockingView.Instance.StartOtherDrag(doc, { pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 });
+ }
dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
};
- const upHandler = (e: PointerEvent) => {
+
+ const abortDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- FinishDrag(dragElement, e, options, dragData);
+ dragDiv.removeChild(dragElement);
if (hideSource && !wasHidden) {
ele.hidden = false;
}
+ }
+ const upHandler = (e: PointerEvent) => {
+ abortDrag();
+ FinishDrag(ele, e, dragData, options);
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
}
- function FinishDrag(dragEle: HTMLElement, e: PointerEvent, options: DragOptions, dragData: { [index: string]: any }) {
- dragDiv.removeChild(dragEle);
+ function FinishDrag(dragEle: HTMLElement, e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions) {
+ let parent = dragEle.parentElement;
+ if (parent)
+ parent.removeChild(dragEle);
const target = document.elementFromPoint(e.x, e.y);
+ if (parent)
+ parent.appendChild(dragEle);
if (!target) {
return;
}
@@ -130,6 +181,9 @@ export namespace DragManager {
data: dragData
}
}));
- options.handlers.dragComplete({});
+ if (options) {
+ options.handlers.dragComplete({});
+ }
+ DocumentDecorations.Instance.Hidden = false;
}
} \ No newline at end of file
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 6bc5fa412..befb9df4c 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -6,7 +6,7 @@ import { NumberField as NumberFieldImport, NumberField } from "../../fields/Numb
import { ImageField as ImageFieldImport } from "../../fields/ImageField";
import { TextField as TextFieldImport, TextField } from "../../fields/TextField";
import { RichTextField as RichTextFieldImport } from "../../fields/RichTextField";
-import { KeyStore as KeyStoreImport } from "../../fields/Key";
+import { KeyStore as KeyStoreImport } from "../../fields/KeyStore";
export interface ExecutableScript {
(): any;
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 9fd4f7bef..3e1039166 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -102,6 +102,12 @@ export class Transform {
return [x * this._scale, y * this._scale];
}
+ transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } {
+ [x, y] = this.transformPoint(x, y);
+ [width, height] = this.transformDirection(width, height);
+ return { x, y, width, height };
+ }
+
inverse = () => {
return new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
}
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