aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/util/UndoManager.ts121
-rw-r--r--src/client/views/Main.tsx24
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx2
3 files changed, 147 insertions, 0 deletions
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
new file mode 100644
index 000000000..34910bac3
--- /dev/null
+++ b/src/client/util/UndoManager.ts
@@ -0,0 +1,121 @@
+import { observable, action } from "mobx";
+import { Opt } from "../../fields/Field";
+
+export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
+ let fn: (...args: any[]) => any;
+ let patchedFn: Opt<(...args: any[]) => any>;
+
+ if (descriptor) {
+ fn = descriptor.value;
+ }
+
+ return {
+ configurable: true,
+ enumerable: false,
+ get() {
+ if (!patchedFn) {
+ patchedFn = (...args: any[]) => {
+ try {
+ UndoManager.StartBatch()
+ return fn.call(this, ...args)
+ } finally {
+ UndoManager.EndBatch()
+ }
+ };
+ }
+ return patchedFn;
+ },
+ set(newFn: any) {
+ patchedFn = undefined;
+ fn = newFn;
+ }
+ }
+}
+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
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 0e770cd11..2f8337d8e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -16,6 +16,7 @@ import { MessageStore, DocumentTransfer } from '../../server/Message';
import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { FieldWaiting } from '../../fields/Field';
+import { UndoManager } from '../util/UndoManager';
configure({
@@ -94,6 +95,11 @@ Documents.initProtos(() => {
x: 0, y: 300, width: 200, height: 200, title: "added note"
}));
})
+ let addSchemaNode = action(() => {
+ mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.SchemaDocument([Documents.TextDocument()], {
+ x: 0, y: 300, width: 200, height: 200, title: "added note"
+ }));
+ })
let clearDatabase = action(() => {
Utils.Emit(Server.Socket, MessageStore.DeleteAll, {});
@@ -129,10 +135,28 @@ Documents.initProtos(() => {
}} onClick={addColNode}>Add Collection</button>
<button style={{
position: 'absolute',
+ bottom: '100',
+ left: '0px',
+ width: '150px'
+ }} onClick={addSchemaNode}>Add Schema</button>
+ <button style={{
+ position: 'absolute',
bottom: '75px',
left: '0px',
width: '150px'
}} onClick={clearDatabase}>Clear Database</button>
+ <button style={{
+ position: 'absolute',
+ bottom: '25',
+ right: '0px',
+ width: '150px'
+ }} onClick={() => UndoManager.Undo()}>Undo</button>
+ <button style={{
+ position: 'absolute',
+ bottom: '0',
+ right: '0px',
+ width: '150px'
+ }} onClick={() => UndoManager.Redo()}>Redo</button>
</div>),
document.getElementById('root'));
})
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 412a4d03d..3a66ebb75 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -13,6 +13,7 @@ import { Documents } from "../../documents/Documents";
import { FieldWaiting } from "../../../fields/Field";
import { Transform } from "../../util/Transform";
import { DocumentView } from "../nodes/DocumentView";
+import { undoBatch } from "../../util/UndoManager";
@observer
export class CollectionFreeFormView extends CollectionViewBase {
@@ -37,6 +38,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
@computed
get resizeScaling() { return this.isAnnotationOverlay ? this.props.Document.GetNumber(KeyStore.Width, 0) / this.nativeWidth : 1; }
+ @undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
const doc: DocumentView = de.data["document"];