aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Server.tsx106
-rw-r--r--src/client/Server.ts64
-rw-r--r--src/client/SocketStub.ts78
-rw-r--r--src/client/documents/Documents.ts (renamed from src/documents/Documents.ts)42
-rw-r--r--src/client/util/DragManager.ts (renamed from src/util/DragManager.ts)6
-rw-r--r--src/client/util/Scripting.ts (renamed from src/util/Scripting.ts)25
-rw-r--r--src/client/util/ScrollBox.tsx (renamed from src/util/ScrollBox.tsx)0
-rw-r--r--src/client/util/SelectionManager.ts (renamed from src/util/SelectionManager.ts)0
-rw-r--r--src/client/util/Transform.ts94
-rw-r--r--src/client/util/TypedEvent.ts (renamed from src/util/TypedEvent.ts)0
-rw-r--r--src/client/views/ContextMenu.scss (renamed from src/views/ContextMenu.scss)0
-rw-r--r--src/client/views/ContextMenu.tsx (renamed from src/views/ContextMenu.tsx)0
-rw-r--r--src/client/views/ContextMenuItem.tsx (renamed from src/views/ContextMenuItem.tsx)0
-rw-r--r--src/client/views/DocumentDecorations.scss (renamed from src/DocumentDecorations.scss)0
-rw-r--r--src/client/views/DocumentDecorations.tsx (renamed from src/DocumentDecorations.tsx)10
-rw-r--r--src/client/views/EditableView.tsx39
-rw-r--r--src/client/views/Main.scss (renamed from src/Main.scss)0
-rw-r--r--src/client/views/Main.tsx (renamed from src/Main.tsx)58
-rw-r--r--src/client/views/collections/CollectionDockingView.scss (renamed from src/views/collections/CollectionDockingView.scss)10
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx (renamed from src/views/collections/CollectionDockingView.tsx)85
-rw-r--r--src/client/views/collections/CollectionFreeFormView.scss (renamed from src/views/collections/CollectionFreeFormView.scss)9
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx251
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss (renamed from src/views/collections/CollectionSchemaView.scss)0
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx (renamed from src/views/collections/CollectionSchemaView.tsx)58
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx (renamed from src/views/collections/CollectionViewBase.tsx)32
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx (renamed from src/views/nodes/CollectionFreeFormDocumentView.tsx)39
-rw-r--r--src/client/views/nodes/DocumentView.tsx (renamed from src/views/nodes/DocumentView.tsx)63
-rw-r--r--src/client/views/nodes/FieldTextBox.scss (renamed from src/views/nodes/FieldTextBox.scss)0
-rw-r--r--src/client/views/nodes/FieldView.tsx (renamed from src/views/nodes/FieldView.tsx)16
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss (renamed from src/views/nodes/FormattedTextBox.scss)0
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx (renamed from src/views/nodes/FormattedTextBox.tsx)17
-rw-r--r--src/client/views/nodes/ImageBox.scss (renamed from src/views/nodes/ImageBox.scss)2
-rw-r--r--src/client/views/nodes/ImageBox.tsx (renamed from src/views/nodes/ImageBox.tsx)14
-rw-r--r--src/client/views/nodes/NodeView.scss (renamed from src/views/nodes/NodeView.scss)0
-rw-r--r--src/fields/Document.ts25
-rw-r--r--src/fields/DocumentReference.ts11
-rw-r--r--src/fields/Field.ts26
-rw-r--r--src/fields/ImageField.ts6
-rw-r--r--src/fields/Key.ts7
-rw-r--r--src/fields/ListField.ts4
-rw-r--r--src/fields/NumberField.ts4
-rw-r--r--src/fields/RichTextField.ts4
-rw-r--r--src/fields/TextField.ts4
-rw-r--r--src/server/index.js13
-rw-r--r--src/server/index.ts29
-rw-r--r--src/stores/NodeCollectionStore.ts26
-rw-r--r--src/stores/NodeStore.ts24
-rw-r--r--src/stores/RootStore.ts15
-rw-r--r--src/stores/StaticTextNodeStore.ts16
-rw-r--r--src/stores/VideoNodeStore.ts17
-rw-r--r--src/views/collections/CollectionFreeFormView.tsx210
-rw-r--r--src/views/nodes/TextNodeView.tsx28
-rw-r--r--src/views/nodes/TopBar.tsx46
-rw-r--r--src/views/nodes/VideoNodeView.scss5
-rw-r--r--src/views/nodes/VideoNodeView.tsx29
55 files changed, 950 insertions, 717 deletions
diff --git a/src/Server.tsx b/src/Server.tsx
deleted file mode 100644
index 04473424a..000000000
--- a/src/Server.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Field, FieldWaiting, FIELD_ID, DOC_ID, FIELD_WAITING } from "./fields/Field"
-import { Key, KeyStore } from "./fields/Key"
-import { ObservableMap, computed, action, observable } from "mobx";
-import { Document } from "./fields/Document"
-
-export class Server {
- static FieldStore: ObservableMap<FIELD_ID, Field> = new ObservableMap();
- static DocumentStore: ObservableMap<DOC_ID, ObservableMap<Key, FIELD_ID>> = new ObservableMap();
- public static ClientFieldsCached: ObservableMap<DOC_ID, Field | FIELD_WAITING> = new ObservableMap();
-
- // 'hack' is here temoporarily for simplicity when debugging things.
- // normally, you can't assume this will return a document since the server responds asynchronously
- // and there might not actually be a matching document on the server.
- // the right way to call this is from within a reaction where you test whether the return value is FieldWaiting.
- public static GetDocument(docid: DOC_ID, hack: boolean = false) {
- if (!this.ClientFieldsCached.has(docid)) {
- this.SEND_DOCUMENT_REQUEST(docid, hack);
- }
- return this.ClientFieldsCached.get(docid) as Document;
- }
- public static AddDocument(document: Document) {
- // Replace with call to server
- this.DocumentStore.set(document.Id, new ObservableMap());
- document.fields.forEach((field, key) => {
- this.FieldStore.set((field as Field).Id, (field as Field));
- this.DocumentStore.get(document.Id)!.set(key, (field as Field).Id);
- });
- }
- public static AddDocumentField(doc: Document, key: Key, value: Field) {
- // Replace with call to server
- if (this.DocumentStore.get(doc.Id))
- this.DocumentStore.get(doc.Id)!.set(key, value.Id);
- }
- public static DeleteDocumentField(doc: Document, key: Key) {
- // Replace with call to server
- if (this.DocumentStore.get(doc.Id))
- this.DocumentStore.get(doc.Id)!.delete(key);
- }
- public static SetFieldValue(field: Field, value: any) {
- // Replace with call to server
- if (this.FieldStore.get(field.Id))
- this.FieldStore.get(field.Id)!.TrySetValue(value);
- }
-
-
- @action
- public static GetDocumentField(doc: Document, key: Key) {
- var fieldid = doc._proxies.get(key);
- if (!this.ClientFieldsCached.has(fieldid)) {
- this.ClientFieldsCached.set(fieldid, FieldWaiting);
- this.SEND_DOCUMENT_FIELD_REQUEST(doc, key, fieldid);
- }
-
- var field = this.ClientFieldsCached.get(fieldid);
- if (field != FieldWaiting) {
- doc._proxies.delete(key); // perhaps another document inquired the same field
- }
- return field;
- }
- static times = 0; // hack for testing
-
- @action
- static cacheField(clientField: Field) {
- var cached = this.ClientFieldsCached.get(clientField.Id);
- if (!cached || cached == FieldWaiting) {
- this.ClientFieldsCached.set(clientField.Id, clientField);
- } else {
- // probably should overwrite the values within any field that was already here...
- }
- return this.ClientFieldsCached.get(clientField.Id) as Field;
- }
-
- public static SEND_DOCUMENT_FIELD_REQUEST(doc: Document, key: Key, fieldid: FIELD_ID) {
- //simulating a server call with a registered callback action
- setTimeout(() => this.receivedDocumentField(doc, key, fieldid, this.FieldStore.get(fieldid)),
- key == KeyStore.Data ? (this.times++ == 0 ? 5000 : 1000) : key == KeyStore.X ? 2500 : 500
- )
- }
-
- public static SEND_DOCUMENT_REQUEST(docid: DOC_ID, hack: boolean = false) {
- if (hack) { // temporary for debugging
- this.receivedDocument(docid, this.DocumentStore.get(docid)!)
- } else {
- //simulating a server call with a registered callback action
- setTimeout(() => this.receivedDocument(docid, this.DocumentStore.get(docid)!), 1500);
- }
- }
-
- @action
- static receivedDocument(docid: DOC_ID, fieldlist: ObservableMap<Key, FIELD_ID>) {
- var cachedDoc = this.cacheField(new Document(docid));
- fieldlist!.forEach((field: FIELD_ID, key: Key) => (cachedDoc as Document)._proxies.set(key, field));
- }
-
- @action
- static receivedDocumentField(doc: Document, key: Key, fieldid: FIELD_ID, fieldfromserver: Field | undefined) {
- doc._proxies.delete(key);
- var cachedField = this.cacheField(fieldfromserver!);
-
- // if the field is a document and it wasn't already cached, then we need to inquire all of its fields from the server...
- if (cachedField instanceof Document && fieldfromserver! == cachedField) {
- this.SEND_DOCUMENT_REQUEST(cachedField.Id);
- }
- doc.fields.set(key, cachedField);
- }
-}
diff --git a/src/client/Server.ts b/src/client/Server.ts
new file mode 100644
index 000000000..0cb6e17c2
--- /dev/null
+++ b/src/client/Server.ts
@@ -0,0 +1,64 @@
+import { Field, FieldWaiting, FieldId, FIELD_WAITING, FieldValue, Opt } from "../fields/Field"
+import { Key, KeyStore } from "../fields/Key"
+import { ObservableMap, action } from "mobx";
+import { Document } from "../fields/Document"
+import { SocketStub } from "./SocketStub";
+
+export class Server {
+ private static ClientFieldsCached: ObservableMap<FieldId, Field | FIELD_WAITING> = new ObservableMap();
+
+ // Retrieves the cached value of the field and sends a request to the server for the real value (if it's not cached).
+ // Call this is from within a reaction and test whether the return value is FieldWaiting.
+ // 'hackTimeout' is here temporarily for simplicity when debugging things.
+ public static GetField(fieldid: FieldId, callback: (field: Field) => void = (f) => { }, hackTimeout: number = -1) {
+ if (!this.ClientFieldsCached.get(fieldid)) {
+ this.ClientFieldsCached.set(fieldid, FieldWaiting);
+ //simulating a server call with a registered callback action
+ SocketStub.SEND_FIELD_REQUEST(fieldid,
+ action((field: Field) => callback(Server.cacheField(field))),
+ hackTimeout);
+ } else if (this.ClientFieldsCached.get(fieldid) != FieldWaiting) {
+ callback(this.ClientFieldsCached.get(fieldid) as Field);
+ }
+ return this.ClientFieldsCached.get(fieldid);
+ }
+
+ static times = 0; // hack for testing
+ public static GetDocumentField(doc: Document, key: Key): FieldValue<Field> {
+ var hackTimeout: number = key == KeyStore.Data ? (this.times++ == 0 ? 5000 : 1000) : key == KeyStore.X ? 2500 : 500;
+
+ let fieldId = doc._proxies.get(key);
+ if (fieldId) {
+ return this.GetField(fieldId,
+ action((fieldfromserver: Field) => {
+ doc._proxies.delete(key);
+ doc.fields.set(key, fieldfromserver);
+ })
+ , hackTimeout);
+ }
+ }
+
+ public static AddDocument(document: Document) {
+ SocketStub.SEND_ADD_DOCUMENT(document);
+ }
+ public static AddDocumentField(doc: Document, key: Key, value: Field) {
+ SocketStub.SEND_ADD_DOCUMENT_FIELD(doc, key, value);
+ }
+ public static DeleteDocumentField(doc: Document, key: Key) {
+ SocketStub.SEND_DELETE_DOCUMENT_FIELD(doc, key);
+ }
+ public static SetFieldValue(field: Field, value: any) {
+ SocketStub.SEND_SET_FIELD(field, value);
+ }
+
+ @action
+ private static cacheField(clientField: Field) {
+ var cached = this.ClientFieldsCached.get(clientField.Id);
+ if (!cached || cached == FieldWaiting) {
+ this.ClientFieldsCached.set(clientField.Id, clientField);
+ } else {
+ // probably should overwrite the values within any field that was already here...
+ }
+ return this.ClientFieldsCached.get(clientField.Id) as Field;
+ }
+}
diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts
new file mode 100644
index 000000000..cea30cb8b
--- /dev/null
+++ b/src/client/SocketStub.ts
@@ -0,0 +1,78 @@
+import { Field, FieldId } from "../fields/Field"
+import { Key, KeyStore } from "../fields/Key"
+import { ObservableMap, action } from "mobx";
+import { Document } from "../fields/Document"
+
+export class SocketStub {
+
+ static FieldStore: ObservableMap<FieldId, Field> = new ObservableMap();
+ public static SEND_ADD_DOCUMENT(document: Document) {
+
+ // Send a serialized version of the document to the server
+ // ...SOCKET(ADD_DOCUMENT, serialied document)
+
+ // server stores each document field in its repository of stored fields
+ document.fields.forEach((f, key) => this.FieldStore.set((f as Field).Id, f as Field));
+
+ // server stores stripped down document (w/ only field id proxies) in the field store
+ this.FieldStore.set(document.Id, new Document(document.Id));
+ document.fields.forEach((f, key) => (this.FieldStore.get(document.Id) as Document)._proxies.set(key, (f as Field).Id));
+ }
+
+ public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Field) => void, timeout: number) {
+
+ if (timeout < 0)// this is a hack to make things easier to setup until we have a server... won't be neededa fter that.
+ callback(this.FieldStore.get(fieldid) as Field);
+ else { // actual logic here...
+
+ // Send a request for fieldid to the server
+ // ...SOCKET(RETRIEVE_FIELD, fieldid)
+
+ // server responds (simulated with a timeout) and the callback is invoked
+ setTimeout(() =>
+
+ // when the field data comes back, call the callback() function
+ callback(this.FieldStore.get(fieldid) as Field),
+
+
+ timeout);
+ }
+ }
+
+ public static SEND_ADD_DOCUMENT_FIELD(doc: Document, key: Key, value: Field) {
+
+ // Send a serialized version of the field to the server along with the
+ // associated info of the document id and key where it is used.
+
+ // ...SOCKET(ADD_DOCUMENT_FIELD, document id, key id, serialized field)
+
+ // server updates its document to hold a proxy mapping from key => fieldId
+ var document = this.FieldStore.get(doc.Id) as Document;
+ if (document)
+ document._proxies.set(key, value.Id);
+
+ // server adds the field to its repository of fields
+ this.FieldStore.set(value.Id, value);
+ }
+
+ public static SEND_DELETE_DOCUMENT_FIELD(doc: Document, key: Key) {
+ // Send a request to delete the field stored under the specified key from the document
+
+ // ...SOCKET(DELETE_DOCUMENT_FIELD, document id, key id)
+
+ // Server removes the field id from the document's list of field proxies
+ var document = this.FieldStore.get(doc.Id) as Document;
+ if (document)
+ document._proxies.delete(key);
+ }
+
+ public static SEND_SET_FIELD(field: Field, value: any) {
+ // Send a request to set the value of a field
+
+ // ...SOCKET(SET_FIELD, field id, serialized field value)
+
+ // Server updates the value of the field in its fieldstore
+ if (this.FieldStore.get(field.Id))
+ this.FieldStore.get(field.Id)!.TrySetValue(value);
+ }
+}
diff --git a/src/documents/Documents.ts b/src/client/documents/Documents.ts
index 90124d36c..72fa608ad 100644
--- a/src/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,22 +1,24 @@
-import { Document } from "../fields/Document";
+import { Document } from "../../fields/Document";
import { Server } from "../Server";
-import { KeyStore } from "../fields/Key";
-import { TextField } from "../fields/TextField";
-import { NumberField } from "../fields/NumberField";
-import { ListField } from "../fields/ListField";
+import { KeyStore } from "../../fields/Key";
+import { TextField } from "../../fields/TextField";
+import { NumberField } from "../../fields/NumberField";
+import { ListField } from "../../fields/ListField";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { CollectionSchemaView } from "../views/collections/CollectionSchemaView";
-import { ImageField } from "../fields/ImageField";
+import { ImageField } from "../../fields/ImageField";
import { ImageBox } from "../views/nodes/ImageBox";
import { CollectionFreeFormView } from "../views/collections/CollectionFreeFormView";
-import { FIELD_ID } from "../fields/Field";
+import { FieldId } from "../../fields/Field";
interface DocumentOptions {
x?: number;
y?: number;
width?: number;
height?: number;
+ nativeWidth?: number;
+ nativeHeight?: number;
title?: string;
}
@@ -34,6 +36,12 @@ export namespace Documents {
if (options.height) {
doc.SetData(KeyStore.Height, options.height, NumberField);
}
+ if (options.nativeWidth) {
+ doc.SetData(KeyStore.NativeWidth, options.nativeWidth, NumberField);
+ }
+ if (options.nativeHeight) {
+ doc.SetData(KeyStore.NativeHeight, options.nativeHeight, NumberField);
+ }
if (options.title) {
doc.SetData(KeyStore.Title, options.title, TextField);
}
@@ -107,7 +115,7 @@ export namespace Documents {
}
- let imageProtoId: FIELD_ID;
+ let imageProtoId: FieldId;
function GetImagePrototype(): Document {
if (imageProtoId === undefined) {
let imageProto = new Document();
@@ -115,23 +123,31 @@ export namespace Documents {
imageProto.Set(KeyStore.Title, new TextField("IMAGE PROTO"));
imageProto.Set(KeyStore.X, new NumberField(0));
imageProto.Set(KeyStore.Y, new NumberField(0));
+ imageProto.Set(KeyStore.NativeWidth, new NumberField(300));
+ imageProto.Set(KeyStore.NativeHeight, new NumberField(300));
imageProto.Set(KeyStore.Width, new NumberField(300));
imageProto.Set(KeyStore.Height, new NumberField(300));
- imageProto.Set(KeyStore.Layout, new TextField(ImageBox.LayoutString()));
+ imageProto.Set(KeyStore.Layout, new TextField(CollectionFreeFormView.LayoutString("AnnotationsKey")));
+ imageProto.Set(KeyStore.BackgroundLayout, new TextField(ImageBox.LayoutString()));
// imageProto.SetField(KeyStore.Layout, new TextField('<div style={"background-image: " + {Data}} />'));
- imageProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
+ imageProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations]));
Server.AddDocument(imageProto);
return imageProto;
}
- return Server.GetDocument(imageProtoId, true)!;
+ return Server.GetField(imageProtoId) as Document;
}
export function ImageDocument(url: string, options: DocumentOptions = {}): Document {
let doc = GetImagePrototype().MakeDelegate();
setupOptions(doc, options);
doc.Set(KeyStore.Data, new ImageField(new URL(url)));
+
+ let annotation = Documents.TextDocument({ title: "hello" });
+ Server.AddDocument(annotation);
+ doc.Set(KeyStore.Annotations, new ListField([annotation]));
Server.AddDocument(doc);
- return Server.GetDocument(doc.Id, true)!;
+ var sdoc = Server.GetField(doc.Id) as Document;
+ return sdoc;
}
let collectionProto: Document;
@@ -145,7 +161,7 @@ export namespace Documents {
collectionProto.Set(KeyStore.PanY, new NumberField(0));
collectionProto.Set(KeyStore.Width, new NumberField(300));
collectionProto.Set(KeyStore.Height, new NumberField(300));
- collectionProto.Set(KeyStore.Layout, new TextField(CollectionFreeFormView.LayoutString()));
+ collectionProto.Set(KeyStore.Layout, new TextField(CollectionFreeFormView.LayoutString("DataKey")));
collectionProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
}
return collectionProto;
diff --git a/src/util/DragManager.ts b/src/client/util/DragManager.ts
index 63d6a88f8..f4dcce7c8 100644
--- a/src/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,9 +1,3 @@
-import { Opt } from "../fields/Field";
-import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { SelectionManager } from "./SelectionManager";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { Document } from "../fields/Document";
export namespace DragManager {
export function Root() {
diff --git a/src/util/Scripting.ts b/src/client/util/Scripting.ts
index 804c67bc5..6bc5fa412 100644
--- a/src/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,11 +1,12 @@
// import * as ts from "typescript"
let ts = (window as any).ts;
-import { Opt, Field, FieldWaiting } from "../fields/Field";
-import { Document as DocumentImport } from "../fields/Document";
-import { NumberField as NumberFieldImport } from "../fields/NumberField";
-import { TextField as TextFieldImport } from "../fields/TextField";
-import { RichTextField as RichTextFieldImport } from "../fields/RichTextField";
-import { KeyStore as KeyStoreImport } from "../fields/Key";
+import { Opt, Field } from "../../fields/Field";
+import { Document as DocumentImport } from "../../fields/Document";
+import { NumberField as NumberFieldImport, NumberField } from "../../fields/NumberField";
+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";
export interface ExecutableScript {
(): any;
@@ -14,7 +15,7 @@ export interface ExecutableScript {
}
function ExecScript(script: string, diagnostics: Opt<any[]>): ExecutableScript {
- const compiled = !(diagnostics && diagnostics != FieldWaiting && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
+ const compiled = !(diagnostics && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
let func: () => Opt<Field>;
if (compiled) {
@@ -23,6 +24,7 @@ function ExecScript(script: string, diagnostics: Opt<any[]>): ExecutableScript {
let Document = DocumentImport;
let NumberField = NumberFieldImport;
let TextField = TextFieldImport;
+ let ImageField = ImageFieldImport;
let RichTextField = RichTextFieldImport;
let window = undefined;
let document = undefined;
@@ -44,4 +46,13 @@ export function CompileScript(script: string): ExecutableScript {
let result = (window as any).ts.transpileModule(script, {})
return ExecScript(result.outputText, result.diagnostics);
+}
+
+export function ToField(data: any): Opt<Field> {
+ if (typeof data == "string") {
+ return new TextField(data);
+ } else if (typeof data == "number") {
+ return new NumberField(data);
+ }
+ return undefined;
} \ No newline at end of file
diff --git a/src/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index b6b088170..b6b088170 100644
--- a/src/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
diff --git a/src/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0759ae110..0759ae110 100644
--- a/src/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
new file mode 100644
index 000000000..7861ed308
--- /dev/null
+++ b/src/client/util/Transform.ts
@@ -0,0 +1,94 @@
+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);
+ }
+
+ 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;
+ }
+
+ translated = (x: number, y: number): Transform => {
+ return this.copy().translate(x, y);
+ }
+
+ preTranslate = (x: number, y: number): Transform => {
+ this._translateX += x * this._scale;
+ this._translateY += y * this._scale;
+ return this;
+ }
+
+ preTranslated = (x: number, y: number): Transform => {
+ return this.copy().preTranslate(x, y);
+ }
+
+ scale = (scale: number): Transform => {
+ this._scale *= scale;
+ return this;
+ }
+
+ scaled = (scale: number): Transform => {
+ return this.copy().scale(scale);
+ }
+
+ preScale = (scale: number): Transform => {
+ this._scale *= scale;
+ this._translateX *= scale;
+ this._translateY *= scale;
+ return this;
+ }
+
+ preScaled = (scale: number): Transform => {
+ return this.copy().preScale(scale);
+ }
+
+ transform = (transform: Transform): Transform => {
+ this._translateX += transform._translateX * this._scale;
+ this._translateY += transform._translateY * this._scale;
+ this._scale *= transform._scale;
+ return this;
+ }
+
+ transformed = (transform: Transform): Transform => {
+ return this.copy().transform(transform);
+ }
+
+ preTransform = (transform: Transform): Transform => {
+ this._translateX = transform._translateX + this._translateX * transform._scale;
+ this._translateY = transform._translateY + this._translateY * transform._scale;
+ this._scale *= transform._scale;
+ return this;
+ }
+
+ 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];
+ }
+
+ 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/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 0714a7f5c..0714a7f5c 100644
--- a/src/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
diff --git a/src/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index 234f82eb9..234f82eb9 100644
--- a/src/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
diff --git a/src/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 4f26a75d2..4f26a75d2 100644
--- a/src/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
diff --git a/src/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 8f00f8b3d..8f00f8b3d 100644
--- a/src/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
diff --git a/src/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index e8b93a18b..e8b93a18b 100644
--- a/src/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
diff --git a/src/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1cf875ea5..7efaa5533 100644
--- a/src/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,9 +1,9 @@
import { observable, computed } from "mobx";
import React = require("react");
-import { SelectionManager } from "./util/SelectionManager";
+import { SelectionManager } from "../util/SelectionManager";
import { observer } from "mobx-react";
import './DocumentDecorations.scss'
-import { CollectionFreeFormView } from "./views/collections/CollectionFreeFormView";
+import { CollectionFreeFormView } from "./collections/CollectionFreeFormView";
@observer
export class DocumentDecorations extends React.Component {
@@ -111,8 +111,10 @@ export class DocumentDecorations extends React.Component {
let actualdH = Math.max(element.height + (dH * scale), 20);
element.x += dX * (actualdW - element.width);
element.y += dY * (actualdH - element.height);
- element.width = actualdW;
- element.height = actualdH;
+ if (Math.abs(dW) > Math.abs(dH))
+ element.width = actualdW;
+ else
+ element.height = actualdH;
}
})
}
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
new file mode 100644
index 000000000..2e784d3f9
--- /dev/null
+++ b/src/client/views/EditableView.tsx
@@ -0,0 +1,39 @@
+import React = require('react')
+import { observer } from 'mobx-react';
+import { observable, action } from 'mobx';
+
+export interface EditableProps {
+ GetValue(): string;
+ SetValue(value: string): boolean;
+ contents: any;
+}
+
+@observer
+export class EditableView extends React.Component<EditableProps> {
+ @observable
+ editing: boolean = false;
+
+ @action
+ onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.key == "Enter" && !e.ctrlKey) {
+ this.props.SetValue(e.currentTarget.value);
+ this.editing = false;
+ } else if (e.key == "Escape") {
+ this.editing = false;
+ }
+ }
+
+ render() {
+ if (this.editing) {
+ return <input defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus onBlur={action(() => this.editing = false)}
+ style={{ width: "100%" }}></input>
+ } else {
+ return (
+ <div>
+ {this.props.contents}
+ <button onClick={action(() => this.editing = true)}>Edit</button>
+ </div>
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Main.scss b/src/client/views/Main.scss
index e73f62904..e73f62904 100644
--- a/src/Main.scss
+++ b/src/client/views/Main.scss
diff --git a/src/Main.tsx b/src/client/views/Main.tsx
index 7a11e6873..268d93d63 100644
--- a/src/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -3,17 +3,18 @@ import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { DocumentDecorations } from './DocumentDecorations';
-import { Documents } from './documents/Documents';
-import { Document } from './fields/Document';
-import { KeyStore, KeyStore as KS } from './fields/Key';
-import { ListField } from './fields/ListField';
-import { NumberField } from './fields/NumberField';
-import { TextField } from './fields/TextField';
+import { Documents } from '../documents/Documents';
+import { Document } from '../../fields/Document';
+import { KeyStore, KeyStore as KS } from '../../fields/Key';
+import { ListField } from '../../fields/ListField';
+import { NumberField } from '../../fields/NumberField';
+import { TextField } from '../../fields/TextField';
import "./Main.scss";
-import { ContextMenu } from './views/ContextMenu';
-import { DocumentView } from './views/nodes/DocumentView';
-import { CompileScript } from './util/Scripting';
-import { TempTreeView } from './TempTreeView';
+import { ContextMenu } from './../views/ContextMenu';
+import { DocumentView } from './../views/nodes/DocumentView';
+import { CompileScript } from './../util/Scripting';
+import { TempTreeView } from './../views/TempTreeView';
+import { Transform } from '../util/Transform';
configure({
@@ -21,7 +22,6 @@ configure({
});
const mainNodeCollection = new Array<Document>();
-let mainNodes = null;// mainContainer.GetFieldT(KeyStore.Data, ListField);
let mainContainer = Documents.DockDocument(mainNodeCollection, {
x: 0, y: 0, title: "main container"
})
@@ -41,28 +41,29 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
//runInAction(() =>
{
- let doc1 = Documents.TextDocument({ title: "hello" });
+ let doc1 = Documents.TextDocument({ title: "hello", width: 400, height: 300 });
let doc2 = doc1.MakeDelegate();
doc2.Set(KS.X, new NumberField(150));
doc2.Set(KS.Y, new NumberField(20));
- let doc3 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
- x: 450, y: 500, title: "cat 1"
+ let doc3 = Documents.ImageDocument("https://psmag.com/.image/t_share/MTMyNzc2NzM1MDY1MjgzMDM4/shutterstock_151341212jpg.jpg", {
+ x: 450, y: 100, title: "cat 1", width: 606, height: 386, nativeWidth: 606, nativeHeight: 386
});
- // const schemaDocs = Array.from(Array(5).keys()).map(v => Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
- // x: 50 + 100 * v, y: 50, width: 100, height: 100, title: "cat" + v
- // }));
- // schemaDocs[0].SetData(KS.Author, "Tyler", TextField);
- // schemaDocs[4].SetData(KS.Author, "Bob", TextField);
- // schemaDocs.push(doc2);
- // const doc7 = Documents.SchemaDocument(schemaDocs)
- const docset = [doc3]; // [doc1, doc2, doc3, doc7];
+ //doc3.Set(KeyStore.Data, new ImageField);
+ const schemaDocs = Array.from(Array(5).keys()).map(v => Documents.ImageDocument("https://psmag.com/.image/t_share/MTMyNzc2NzM1MDY1MjgzMDM4/shutterstock_151341212jpg.jpg", {
+ x: 50 + 100 * v, y: 50, width: 100, height: 100, title: "cat" + v, nativeWidth: 606, nativeHeight: 386
+ }));
+ schemaDocs[0].SetData(KS.Author, "Tyler", TextField);
+ schemaDocs[4].SetData(KS.Author, "Bob", TextField);
+ schemaDocs.push(doc2);
+ const doc7 = Documents.SchemaDocument(schemaDocs)
+ const docset = [doc1, doc2, doc3, doc7];
let doc4 = Documents.CollectionDocument(docset, {
x: 0, y: 400, title: "mini collection"
});
// let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
// x: 650, y: 500, width: 600, height: 600, title: "cat 2"
// });
- let docset2 = new Array<Document>(doc4);//, doc1, doc3);
+ let docset2 = [doc3, doc1, doc2];
let doc6 = Documents.CollectionDocument(docset2, {
x: 350, y: 100, width: 600, height: 600, title: "docking collection"
});
@@ -76,8 +77,8 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
mainNodes.Data.push(doc3);
// mainNodes.Data.push(doc5);
// mainNodes.Data.push(doc1);
- //mainNodes.Data.push(doc2);
- //mainNodes.Data.push(doc6);
+ // mainNodes.Data.push(doc2);
+ mainNodes.Data.push(doc6);
mainContainer.Set(KeyStore.Data, mainNodes);
}
//}
@@ -85,9 +86,12 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
ReactDOM.render((
<div style={{ position: "absolute", width: "100%", height: "100%" }}>
- <DocumentView Document={mainContainer} ContainingCollectionView={undefined} DocumentView={undefined} />
+ <DocumentView Document={mainContainer}
+ AddDocument={undefined} RemoveDocument={undefined} GetTransform={() => Transform.Identity}
+ Scaling={1}
+ ContainingCollectionView={undefined} DocumentView={undefined} />
<DocumentDecorations />
<ContextMenu />
- <TempTreeView mainCollection={mainNodes} />
+ {/* <TempTreeView mainCollection={mainNodes.Data} /> */}
</div>),
document.getElementById('root')); \ No newline at end of file
diff --git a/src/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index db924b57f..7c0b512a7 100644
--- a/src/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -126,7 +126,7 @@
}
.flexlayout__tab_button:hover .flexlayout__tab_button_trailing,
.flexlayout__tab_button--selected .flexlayout__tab_button_trailing {
- background: transparent url("../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center;
+ background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center;
}
.flexlayout__tab_button_overflow {
float: left;
@@ -138,7 +138,7 @@
font-size: 10px;
color: lightgray;
font-family: Arial, sans-serif;
- background: transparent url("../../../node_modules/flexlayout-react/images/more.png") no-repeat left;
+ background: transparent url("../../../../node_modules/flexlayout-react/images/more.png") no-repeat left;
}
.flexlayout__tabset_header {
position: absolute;
@@ -186,14 +186,14 @@
height: 20px;
border: none;
outline-width: 0;
- background: transparent url("../../../node_modules/flexlayout-react/images/maximize.png") no-repeat center;
+ background: transparent url("../../../../node_modules/flexlayout-react/images/maximize.png") no-repeat center;
}
.flexlayout__tab_toolbar_button-max {
width: 20px;
height: 20px;
border: none;
outline-width: 0;
- background: transparent url("../../../node_modules/flexlayout-react/images/restore.png") no-repeat center;
+ background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center;
}
.flexlayout__popup_menu {}
.flexlayout__popup_menu_item {
@@ -295,7 +295,7 @@
}
.flexlayout__border_button:hover .flexlayout__border_button_trailing,
.flexlayout__border_button--selected .flexlayout__border_button_trailing {
- background: transparent url("../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center;
+ background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center;
}
.flexlayout__border_toolbar_left {
position: absolute;
diff --git a/src/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e489e319a..d3e90d11c 100644
--- a/src/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,21 +1,21 @@
-import { observer } from "mobx-react";
-import { KeyStore } from "../../fields/Key";
-import React = require("react");
import FlexLayout from "flexlayout-react";
-import { action, observable, computed } from "mobx";
-import { Document } from "../../fields/Document";
-import { DocumentView } from "../nodes/DocumentView";
-import { ListField } from "../../fields/ListField";
-import { NumberField } from "../../fields/NumberField";
-import { SSL_OP_SINGLE_DH_USE } from "constants";
-import "./CollectionDockingView.scss"
+import * as GoldenLayout from "golden-layout";
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import * as GoldenLayout from "golden-layout";
+import { action, computed, reaction, observable } from "mobx";
+import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/Key";
+import { ListField } from "../../../fields/ListField";
+import { NumberField } from "../../../fields/NumberField";
import { DragManager } from "../../util/DragManager";
+import { Transform } from "../../util/Transform";
+import { DocumentView } from "../nodes/DocumentView";
+import "./CollectionDockingView.scss";
import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase";
-import { FieldWaiting } from "../../fields/Field";
+import React = require("react");
+import { changeDependenciesStateTo0 } from "mobx/lib/internal";
@observer
export class CollectionDockingView extends CollectionViewBase {
@@ -44,7 +44,7 @@ export class CollectionDockingView extends CollectionViewBase {
const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props;
const value: Document[] = Document.GetData(fieldKey, ListField, []);
var docs = value.map(doc => {
- return { type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc } };
+ return { type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc, scaling: 1 } };
});
return new GoldenLayout({
settings: {
@@ -70,8 +70,6 @@ export class CollectionDockingView extends CollectionViewBase {
@action
onResize = (event: any) => {
- if (this.props.ContainingDocumentView == FieldWaiting)
- return;
var cur = this.props.ContainingDocumentView!.MainContent.current;
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
@@ -99,7 +97,11 @@ export class CollectionDockingView extends CollectionViewBase {
const value: Document[] = Document.GetData(fieldKey, ListField, []);
for (var i: number = 0; i < value.length; i++) {
if (value[i].Id === component) {
- return (<DocumentView key={value[i].Id} ContainingCollectionView={this} Document={value[i]} DocumentView={undefined} />);
+ return (<DocumentView key={value[i].Id} Document={value[i]}
+ AddDocument={this.addDocument} RemoveDocument={this.removeDocument}
+ GetTransform={() => Transform.Identity}
+ Scaling={1}
+ ContainingCollectionView={this} DocumentView={undefined} />);
}
}
if (component === "text") {
@@ -109,6 +111,7 @@ export class CollectionDockingView extends CollectionViewBase {
public static myLayout: any = null;
+ public rcs: Array<RenderClass> = new Array();
private static _dragDiv: any = null;
private static _dragParent: HTMLElement | null = null;
private static _dragElement: HTMLDivElement;
@@ -117,7 +120,7 @@ export class CollectionDockingView extends CollectionViewBase {
var newItemConfig = {
type: 'component',
componentName: 'documentViewComponent',
- componentState: { doc: dragDoc }
+ componentState: { doc: dragDoc, scaling: 1 }
};
this._dragElement = dragElement;
this._dragParent = dragElement.parentElement;
@@ -158,7 +161,6 @@ export class CollectionDockingView extends CollectionViewBase {
CollectionDockingView.myLayout._maximizedStack = null;
}
}
-
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@@ -239,11 +241,8 @@ export class CollectionDockingView extends CollectionViewBase {
var containingDiv = "component_" + me.nextId();
container.getElement().html("<div id='" + containingDiv + "'></div>");
setTimeout(function () {
- ReactDOM.render((
- <DocumentView key={state.doc.Id} Document={state.doc} ContainingCollectionView={me} DocumentView={undefined} />
- ),
- document.getElementById(containingDiv)
- );
+ state.rc = new RenderClass(containingDiv, state.doc, me, container);
+ me.rcs.push(state.rc);
if (CollectionDockingView.myLayout._maxstack != null) {
CollectionDockingView.myLayout._maxstack.click();
}
@@ -255,8 +254,6 @@ export class CollectionDockingView extends CollectionViewBase {
render() {
- if (this.props.ContainingDocumentView == FieldWaiting)
- return;
const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props;
const value: Document[] = Document.GetData(fieldKey, ListField, []);
// bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes.
@@ -284,4 +281,42 @@ export class CollectionDockingView extends CollectionViewBase {
</div>
);
}
+}
+
+class RenderClass {
+
+ @observable _resizeCount: number = 0;
+
+ _collectionDockingView: CollectionDockingView;
+ _htmlElement: any;
+ _document: Document;
+ constructor(containingDiv: string, doc: Document, me: CollectionDockingView, container: any) {
+ this._collectionDockingView = me;
+ this._htmlElement = document.getElementById(containingDiv);
+ this._document = doc;
+ container.on('resize', action((e: any) => {
+ var nativeWidth = doc.GetNumber(KeyStore.NativeWidth, 0);
+ if (this._htmlElement != null && this._htmlElement.childElementCount > 0 && nativeWidth > 0) {
+ let scaling = nativeWidth > 0 ? this._htmlElement!.clientWidth / nativeWidth : 1;
+ (this._htmlElement!.children[0] as any).style.transformOrigin = "0px 0px";
+ (this._htmlElement!.children[0] as any).style.transform = `translate(0px,0px) scale(${scaling}, ${scaling}) `;
+ (this._htmlElement!.children[0] as any).style.width = nativeWidth.toString() + "px";
+ }
+ }));
+
+ this.render();
+ }
+ render() {
+ var nativeWidth = this._document.GetNumber(KeyStore.NativeWidth, 0);
+ let scaling = nativeWidth > 0 ? this._htmlElement!.clientWidth / nativeWidth : 1;
+ ReactDOM.render((
+ <DocumentView key={this._document.Id} Document={this._document}
+ AddDocument={this._collectionDockingView.addDocument} RemoveDocument={this._collectionDockingView.removeDocument}
+ GetTransform={() => Transform.Identity}
+ Scaling={scaling}
+ ContainingCollectionView={this._collectionDockingView} DocumentView={undefined} />
+ ),
+ this._htmlElement
+ );
+ }
} \ No newline at end of file
diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index e9d134e7b..4cf474f77 100644
--- a/src/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -1,4 +1,6 @@
.collectionfreeformview-container {
+ border-style: solid;
+ box-sizing: border-box;
position: relative;
top: 0;
left: 0;
@@ -10,11 +12,4 @@
top: 0;
left: 0;
}
-}
-
-.border {
- border-style: solid;
- box-sizing: border-box;
- width: 100%;
- height: 100%;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
new file mode 100644
index 000000000..15450d737
--- /dev/null
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -0,0 +1,251 @@
+import { observer } from "mobx-react";
+import React = require("react");
+import { action, observable, computed } from "mobx";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { DragManager } from "../../util/DragManager";
+import "./CollectionFreeFormView.scss";
+import { Utils } from "../../../Utils";
+import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase";
+import { SelectionManager } from "../../util/SelectionManager";
+import { Key, KeyStore } from "../../../fields/Key";
+import { Document } from "../../../fields/Document";
+import { ListField } from "../../../fields/ListField";
+import { NumberField } from "../../../fields/NumberField";
+import { Documents } from "../../documents/Documents";
+import { FieldWaiting } from "../../../fields/Field";
+import { Transform } from "../../util/Transform";
+
+@observer
+export class CollectionFreeFormView extends CollectionViewBase {
+ public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionFreeFormView", fieldKey); }
+ private _containerRef = React.createRef<HTMLDivElement>();
+ private _canvasRef = React.createRef<HTMLDivElement>();
+ private _lastX: number = 0;
+ private _lastY: number = 0;
+ private _downX: number = 0;
+ private _downY: number = 0;
+
+ constructor(props: CollectionViewProps) {
+ super(props);
+ }
+
+ @computed
+ get isAnnotationOverlay() { return this.props.CollectionFieldKey == KeyStore.Annotations; }
+
+ @computed
+ get nativeWidth() { return this.props.DocumentForCollection.GetNumber(KeyStore.NativeWidth, 0); }
+ @computed
+ get nativeHeight() { return this.props.DocumentForCollection.GetNumber(KeyStore.NativeHeight, 0); }
+
+ @computed
+ get zoomScaling() { return this.props.DocumentForCollection.GetNumber(KeyStore.Scale, 1); }
+
+ @computed
+ get resizeScaling() { return this.isAnnotationOverlay ? this.props.DocumentForCollection.GetNumber(KeyStore.Width, 0) / this.nativeWidth : 1; }
+
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ const doc = de.data["document"];
+ var me = this;
+ if (doc instanceof CollectionFreeFormDocumentView) {
+ if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) {
+ doc.props.ContainingCollectionView.removeDocument(doc.props.Document);
+ this.addDocument(doc.props.Document);
+ }
+ const xOffset = de.data["xOffset"] as number || 0;
+ const yOffset = de.data["yOffset"] as number || 0;
+ const { translateX, translateY } = Utils.GetScreenTransform(this._canvasRef.current!);
+ const currScale = this.resizeScaling * this.zoomScaling * this.props.ContainingDocumentView!.ScalingToScreenSpace;
+ const screenX = de.x - xOffset;
+ const screenY = de.y - yOffset;
+ doc.x = (screenX - translateX) / currScale;
+ doc.y = (screenY - translateY) / currScale;
+ this.bringToFront(doc);
+ }
+ e.stopPropagation();
+ }
+
+ componentDidMount() {
+ if (this._containerRef.current) {
+ DragManager.MakeDropTarget(this._containerRef.current, {
+ handlers: {
+ drop: this.drop
+ }
+ });
+ }
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent): void => {
+ if ((e.button === 2 && this.active) ||
+ !e.defaultPrevented) {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.addEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
+ this._downX = this._lastX = e.pageX;
+ this._downY = this._lastY = e.pageY;
+ }
+ }
+
+ @action
+ onPointerUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ e.stopPropagation();
+ if (Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) {
+ if (!SelectionManager.IsSelected(this.props.ContainingDocumentView as CollectionFreeFormDocumentView)) {
+ SelectionManager.SelectDoc(this.props.ContainingDocumentView as CollectionFreeFormDocumentView, false);
+ }
+ }
+ }
+
+ @action
+ onPointerMove = (e: PointerEvent): void => {
+ if (!e.cancelBubble && this.active) {
+ e.preventDefault();
+ e.stopPropagation();
+ let currScale: number = this.props.ContainingDocumentView!.ScalingToScreenSpace;
+ let x = this.props.DocumentForCollection.GetNumber(KeyStore.PanX, 0);
+ let y = this.props.DocumentForCollection.GetNumber(KeyStore.PanY, 0);
+
+ this.SetPan(x + (e.pageX - this._lastX) / currScale, y + (e.pageY - this._lastY) / currScale);
+ }
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ }
+
+ @action
+ onPointerWheel = (e: React.WheelEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ let modes = ['pixels', 'lines', 'page'];
+ let coefficient = 1000;
+ // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
+ // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
+
+ let { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY);
+
+ var deltaScale = (1 - (e.deltaY / coefficient)) * Ss;
+ var newDeltaScale = this.isAnnotationOverlay ? Math.max(1, deltaScale) : deltaScale;
+
+ this.props.DocumentForCollection.SetNumber(KeyStore.Scale, newDeltaScale);
+ this.SetPan(ContainerX - (LocalX * newDeltaScale + Xx), ContainerY - (LocalY * newDeltaScale + Yy));
+ }
+
+ @action
+ private SetPan(panX: number, panY: number) {
+ const newPanX = Math.max(-(this.resizeScaling * this.zoomScaling - this.resizeScaling) * this.nativeWidth, Math.min(0, panX));
+ const newPanY = Math.max(-(this.resizeScaling * this.zoomScaling - this.resizeScaling) * this.nativeHeight, Math.min(0, panY));
+ this.props.DocumentForCollection.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
+ this.props.DocumentForCollection.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
+ }
+
+ @action
+ onDrop = (e: React.DragEvent): void => {
+ e.stopPropagation()
+ e.preventDefault()
+ let fReader = new FileReader()
+ let file = e.dataTransfer.items[0].getAsFile();
+ let that = this;
+ const panx: number = this.props.DocumentForCollection.GetData(KeyStore.PanX, NumberField, Number(0));
+ const pany: number = this.props.DocumentForCollection.GetData(KeyStore.PanY, NumberField, Number(0));
+ let x = e.pageX - panx
+ let y = e.pageY - pany
+
+ fReader.addEventListener("load", action("drop", (event) => {
+ if (fReader.result) {
+ let url = "" + fReader.result;
+ let doc = Documents.ImageDocument(url, {
+ x: x, y: y
+ })
+ let docs = that.props.DocumentForCollection.GetT(KeyStore.Data, ListField);
+ if (docs != FieldWaiting) {
+ if (!docs) {
+ docs = new ListField<Document>();
+ that.props.DocumentForCollection.Set(KeyStore.Data, docs)
+ }
+ docs.Data.push(doc);
+ }
+ }
+ }), false)
+
+ if (file) {
+ fReader.readAsDataURL(file)
+ }
+ }
+
+ onDragOver = (e: React.DragEvent): void => {
+ }
+
+ @action
+ bringToFront(doc: CollectionFreeFormDocumentView) {
+ const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props;
+
+ const value: Document[] = Document.GetList<Document>(fieldKey, []);
+ var topmost = value.reduce((topmost, d) => Math.max(d.GetNumber(KeyStore.ZIndex, 0), topmost), -1000);
+ value.map(d => {
+ var zind = d.GetNumber(KeyStore.ZIndex, 0);
+ if (zind != topmost - 1 - (topmost - zind) && d != doc.props.Document) {
+ d.SetData(KeyStore.ZIndex, topmost - 1 - (topmost - zind), NumberField);
+ }
+ })
+
+ if (doc.props.Document.GetNumber(KeyStore.ZIndex, 0) != 0) {
+ doc.props.Document.SetData(KeyStore.ZIndex, 0, NumberField);
+ }
+ }
+
+ @computed
+ get translate(): [number, number] {
+ const x = this.props.DocumentForCollection.GetNumber(KeyStore.PanX, 0);
+ const y = this.props.DocumentForCollection.GetNumber(KeyStore.PanY, 0);
+ return [x, y];
+ }
+
+ @computed
+ get scale(): number {
+ return this.props.DocumentForCollection.GetNumber(KeyStore.Scale, 1);
+ }
+
+ getTransform = (): Transform => {
+ const [x, y] = this.translate;
+ return this.props.GetTransform().scaled(this.scale).translate(x, y);
+ }
+
+ render() {
+ const Document: Document = this.props.DocumentForCollection;
+ const value: Document[] = Document.GetList<Document>(this.props.CollectionFieldKey, []);
+ const panx: number = Document.GetNumber(KeyStore.PanX, 0);
+ const pany: number = Document.GetNumber(KeyStore.PanY, 0);
+ var me = this;
+
+ return (
+ <div className="collectionfreeformview-container"
+ onPointerDown={this.onPointerDown}
+ onWheel={this.onPointerWheel}
+ onContextMenu={(e) => e.preventDefault()}
+ onDrop={this.onDrop}
+ onDragOver={this.onDragOver}
+ style={{
+ borderWidth: `${COLLECTION_BORDER_WIDTH}px`,
+ }}
+ ref={this._containerRef}>
+ <div className="collectionfreeformview"
+ style={{ width: "100%", transformOrigin: "left top", transform: ` translate(${panx}px, ${pany}px) scale(${this.zoomScaling}, ${this.zoomScaling})` }}
+ ref={this._canvasRef}>
+
+ {this.props.BackgroundView}
+ {value.map(doc => {
+ return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc}
+ AddDocument={this.addDocument}
+ RemoveDocument={this.removeDocument}
+ GetTransform={this.getTransform}
+ Scaling={1}
+ ContainingCollectionView={this} DocumentView={undefined} />);
+ })}
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 707b44db6..707b44db6 100644
--- a/src/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
diff --git a/src/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8817cb496..f1e882e20 100644
--- a/src/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,8 +1,6 @@
import React = require("react")
import ReactTable, { ReactTableDefaults, CellInfo, ComponentPropsGetterRC, ComponentPropsGetterR } from "react-table";
import { observer } from "mobx-react";
-import { KeyStore as KS, Key } from "../../fields/Key";
-import { Document } from "../../fields/Document";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "react-table/react-table.css"
import { observable, action, computed } from "mobx";
@@ -11,6 +9,12 @@ import "./CollectionSchemaView.scss"
import { ScrollBox } from "../../util/ScrollBox";
import { CollectionViewBase } from "./CollectionViewBase";
import { DocumentView } from "../nodes/DocumentView";
+import { EditableView } from "../EditableView";
+import { CompileScript, ToField } from "../../util/Scripting";
+import { KeyStore as KS, Key } from "../../../fields/Key";
+import { Document } from "../../../fields/Document";
+import { Field } from "../../../fields/Field";
+import { Transform } from "../../util/Transform";
@observer
export class CollectionSchemaView extends CollectionViewBase {
@@ -23,11 +27,37 @@ export class CollectionSchemaView extends CollectionViewBase {
let props: FieldViewProps = {
doc: rowProps.value[0],
fieldKey: rowProps.value[1],
- DocumentViewForField: undefined
+ DocumentViewForField: undefined,
}
- return (
+ let contents = (
<FieldView {...props} />
)
+ return (
+ <EditableView contents={contents} GetValue={() => {
+ let field = props.doc.Get(props.fieldKey);
+ if (field && field instanceof Field) {
+ return field.ToScriptString();
+ }
+ return field || "";
+ }} SetValue={(value: string) => {
+ let script = CompileScript(value);
+ if (!script.compiled) {
+ return false;
+ }
+ let field = script();
+ if (field instanceof Field) {
+ props.doc.Set(props.fieldKey, field);
+ return true;
+ } else {
+ let dataField = ToField(field);
+ if (dataField) {
+ props.doc.Set(props.fieldKey, dataField);
+ return true;
+ }
+ }
+ return false;
+ }}></EditableView>
+ )
}
private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
@@ -57,10 +87,11 @@ export class CollectionSchemaView extends CollectionViewBase {
if (target.tagName == "SPAN" && target.className.includes("Resizer")) {
e.stopPropagation();
}
- if (e.button === 2 && this.active) {
- e.stopPropagation();
- e.preventDefault();
- } else {
+ // if (e.button === 2 && this.active) {
+ // e.stopPropagation();
+ // e.preventDefault();
+ // } else
+ {
if (e.buttons === 1 && this.active) {
e.stopPropagation();
}
@@ -74,7 +105,13 @@ export class CollectionSchemaView extends CollectionViewBase {
[KS.Title, KS.Data, KS.Author])
let content;
if (this.selectedIndex != -1) {
- content = (<DocumentView Document={children[this.selectedIndex]} DocumentView={undefined} ContainingCollectionView={this} />)
+ content = (
+ <DocumentView Document={children[this.selectedIndex]}
+ AddDocument={this.addDocument} RemoveDocument={this.removeDocument}
+ GetTransform={() => Transform.Identity}//TODO This should probably be an actual transform
+ Scaling={1}
+ DocumentView={undefined} ContainingCollectionView={this} />
+ )
} else {
content = <div />
}
@@ -88,7 +125,8 @@ export class CollectionSchemaView extends CollectionViewBase {
page={0}
showPagination={false}
style={{
- display: "inline-block"
+ display: "inline-block",
+ width: "100%"
}}
columns={columns.map(col => {
return (
diff --git a/src/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 4fce02ef6..7e2fcb39d 100644
--- a/src/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -1,21 +1,25 @@
import { action, computed } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../fields/Document";
-import { Opt, FieldWaiting } from "../../fields/Field";
-import { Key, KeyStore } from "../../fields/Key";
-import { ListField } from "../../fields/ListField";
+import { Document } from "../../../fields/Document";
+import { Opt } from "../../../fields/Field";
+import { Key, KeyStore } from "../../../fields/Key";
+import { ListField } from "../../../fields/ListField";
import { SelectionManager } from "../../util/SelectionManager";
import { ContextMenu } from "../ContextMenu";
import React = require("react");
import { DocumentView } from "../nodes/DocumentView";
import { CollectionDockingView } from "./CollectionDockingView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { Transform } from "../../util/Transform";
export interface CollectionViewProps {
CollectionFieldKey: Key;
DocumentForCollection: Document;
ContainingDocumentView: Opt<DocumentView>;
+ GetTransform: () => Transform;
+ BackgroundView: Opt<DocumentView>;
+ Scaling: number;
}
export const COLLECTION_BORDER_WIDTH = 2;
@@ -23,17 +27,16 @@ export const COLLECTION_BORDER_WIDTH = 2;
@observer
export class CollectionViewBase extends React.Component<CollectionViewProps> {
- public static LayoutString(collectionType: string) {
- return `<${collectionType} DocumentForCollection={Document} CollectionFieldKey={DataKey} ContainingDocumentView={DocumentView}/>`;
+ public static LayoutString(collectionType: string, fieldKey: string = "DataKey") {
+ return `<${collectionType} Scaling={Scaling} DocumentForCollection={Document} CollectionFieldKey={${fieldKey}} ContainingDocumentView={DocumentView} BackgroundView={BackgroundView} />`;
}
@computed
public get active(): boolean {
var isSelected = (this.props.ContainingDocumentView instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.ContainingDocumentView));
var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this);
- var topMost = this.props.ContainingDocumentView != undefined &&
- this.props.ContainingDocumentView != FieldWaiting && this.props.ContainingDocumentView.props.ContainingCollectionView != FieldWaiting && (
- this.props.ContainingDocumentView.props.ContainingCollectionView == undefined ||
- this.props.ContainingDocumentView.props.ContainingCollectionView instanceof CollectionDockingView);
+ var topMost = this.props.ContainingDocumentView != undefined && (
+ this.props.ContainingDocumentView.props.ContainingCollectionView == undefined ||
+ this.props.ContainingDocumentView.props.ContainingCollectionView instanceof CollectionDockingView);
return isSelected || childSelected || topMost;
}
@action
@@ -44,15 +47,18 @@ export class CollectionViewBase extends React.Component<CollectionViewProps> {
}
@action
- removeDocument = (doc: Document): void => {
+ removeDocument = (doc: Document): boolean => {
//TODO This won't create the field if it doesn't already exist
const value = this.props.DocumentForCollection.GetData(this.props.CollectionFieldKey, ListField, new Array<Document>())
- if (value.indexOf(doc) !== -1) {
- value.splice(value.indexOf(doc), 1)
+ let index = value.indexOf(doc);
+ if (index !== -1) {
+ value.splice(index, 1)
SelectionManager.DeselectAll()
ContextMenu.Instance.clearItems()
+ return true;
}
+ return false
}
} \ No newline at end of file
diff --git a/src/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5d6bcf4a3..95c28e414 100644
--- a/src/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,7 @@
import { action, computed } from "mobx";
import { observer } from "mobx-react";
-import { Key, KeyStore } from "../../fields/Key";
-import { NumberField } from "../../fields/NumberField";
+import { Key, KeyStore } from "../../../fields/Key";
+import { NumberField } from "../../../fields/NumberField";
import { DragManager } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -10,7 +10,7 @@ import { ContextMenu } from "../ContextMenu";
import "./NodeView.scss";
import React = require("react");
import { DocumentView, DocumentViewProps } from "./DocumentView";
-import { FieldWaiting } from "../../fields/Field";
+import { Transform } from "../../util/Transform";
@observer
@@ -18,6 +18,7 @@ export class CollectionFreeFormDocumentView extends DocumentView {
private _contextMenuCanOpen = false;
private _downX: number = 0;
private _downY: number = 0;
+ // private _mainCont = React.createRef<HTMLDivElement>();
constructor(props: DocumentViewProps) {
super(props);
@@ -49,25 +50,40 @@ export class CollectionFreeFormDocumentView extends DocumentView {
@computed
get transform(): string {
- return `translate(${this.x}px, ${this.y}px)`;
+ return `scale(${this.props.Scaling}, ${this.props.Scaling}) translate(${this.x}px, ${this.y}px)`;
}
@computed
get width(): number {
- return this.props.Document.GetData(KeyStore.Width, NumberField, Number(0));
+ return this.props.Document.GetNumber(KeyStore.Width, 0);
+ }
+
+ @computed
+ get nativeWidth(): number {
+ return this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
}
set width(w: number) {
this.props.Document.SetData(KeyStore.Width, w, NumberField)
+ if (this.nativeWidth > 0 && this.nativeHeight > 0) {
+ this.props.Document.SetNumber(KeyStore.Height, this.nativeHeight / this.nativeWidth * w)
+ }
}
@computed
get height(): number {
- return this.props.Document.GetData(KeyStore.Height, NumberField, Number(0));
+ return this.props.Document.GetNumber(KeyStore.Height, 0);
+ }
+ @computed
+ get nativeHeight(): number {
+ return this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
}
set height(h: number) {
- this.props.Document.SetData(KeyStore.Height, h, NumberField)
+ this.props.Document.SetData(KeyStore.Height, h, NumberField);
+ if (this.nativeWidth > 0 && this.nativeHeight > 0) {
+ this.props.Document.SetNumber(KeyStore.Width, this.nativeWidth / this.nativeHeight * h)
+ }
}
@computed
@@ -86,7 +102,7 @@ export class CollectionFreeFormDocumentView extends DocumentView {
@computed
get active(): boolean {
return SelectionManager.IsSelected(this) || this.props.ContainingCollectionView === undefined ||
- (this.props.ContainingCollectionView != FieldWaiting && this.props.ContainingCollectionView!.active);
+ this.props.ContainingCollectionView.active;
}
@computed
@@ -212,10 +228,15 @@ export class CollectionFreeFormDocumentView extends DocumentView {
}
}
+ getTransform = (): Transform => {
+ return this.props.GetTransform().translated(this.x, this.y);
+ }
+
render() {
var freestyling = this.props.ContainingCollectionView instanceof CollectionFreeFormView;
return (
<div className="node" ref={this._mainCont} style={{
+ transformOrigin: "left top",
transform: freestyling ? this.transform : "",
width: freestyling ? this.width : "100%",
height: freestyling ? this.height : "100%",
@@ -225,7 +246,7 @@ export class CollectionFreeFormDocumentView extends DocumentView {
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}>
- <DocumentView {...this.props} DocumentView={this} />
+ <DocumentView {...this.props} Scaling={this.width / this.nativeWidth} GetTransform={this.getTransform} DocumentView={this} />
</div>
);
}
diff --git a/src/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 81353cd60..3a73f2fde 100644
--- a/src/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,12 +1,12 @@
import { action, computed } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../fields/Document";
-import { Opt, FieldWaiting } from "../../fields/Field";
-import { Key, KeyStore } from "../../fields/Key";
-import { ListField } from "../../fields/ListField";
-import { NumberField } from "../../fields/NumberField";
-import { TextField } from "../../fields/TextField";
-import { Utils } from "../../Utils";
+import { Document } from "../../../fields/Document";
+import { Opt, FieldWaiting } from "../../../fields/Field";
+import { Key, KeyStore } from "../../../fields/Key";
+import { ListField } from "../../../fields/ListField";
+import { NumberField } from "../../../fields/NumberField";
+import { TextField } from "../../../fields/TextField";
+import { Utils } from "../../../Utils";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/CollectionFreeFormView";
import { CollectionSchemaView } from "../collections/CollectionSchemaView";
@@ -15,13 +15,20 @@ import { FormattedTextBox } from "../nodes/FormattedTextBox";
import { ImageBox } from "../nodes/ImageBox";
import "./NodeView.scss";
import React = require("react");
+import { Transform } from "../../util/Transform";
const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
export interface DocumentViewProps {
- Document: Document;
DocumentView: Opt<DocumentView> // needed only to set ContainingDocumentView on CollectionViewProps when invoked from JsxParser -- is there a better way?
ContainingCollectionView: Opt<CollectionViewBase>;
+
+ Document: Document;
+ AddDocument?: (doc: Document) => void;
+ RemoveDocument?: (doc: Document) => boolean;
+ GetTransform: () => Transform;
+ Scaling: number;
}
+
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
@@ -35,6 +42,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@computed
+ get backgroundLayout(): string {
+ return this.props.Document.GetData(KeyStore.BackgroundLayout, TextField, String("<p>Error loading layout data</p>"));
+ }
+
+ @computed
get layoutKeys(): Key[] {
return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>());
}
@@ -49,9 +61,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
//
@computed
public get ScalingToScreenSpace(): number {
- if (this.props.ContainingCollectionView != undefined && this.props.ContainingCollectionView != FieldWaiting &&
- this.props.ContainingCollectionView.props.ContainingDocumentView != undefined && this.props.ContainingCollectionView.props.ContainingDocumentView != FieldWaiting) {
- let ss = this.props.ContainingCollectionView.props.DocumentForCollection.GetData(KeyStore.Scale, NumberField, Number(1));
+ if (this.props.ContainingCollectionView != undefined &&
+ this.props.ContainingCollectionView.props.ContainingDocumentView != undefined) {
+ let ss = this.props.ContainingCollectionView.props.DocumentForCollection.GetNumber(KeyStore.Scale, 1);
return this.props.ContainingCollectionView.props.ContainingDocumentView.ScalingToScreenSpace * ss;
}
return 1;
@@ -63,8 +75,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
public TransformToLocalPoint(screenX: number, screenY: number) {
// if this collection view is nested within another collection view, then
// first transform the screen point into the parent collection's coordinate space.
- let { LocalX: parentX, LocalY: parentY } = this.props.ContainingCollectionView != undefined && this.props.ContainingCollectionView != FieldWaiting &&
- this.props.ContainingCollectionView.props.ContainingDocumentView != undefined && this.props.ContainingCollectionView.props.ContainingDocumentView != FieldWaiting ?
+ let { LocalX: parentX, LocalY: parentY } = this.props.ContainingCollectionView != undefined &&
+ this.props.ContainingCollectionView.props.ContainingDocumentView != undefined ?
this.props.ContainingCollectionView.props.ContainingDocumentView.TransformToLocalPoint(screenX, screenY) :
{ LocalX: screenX, LocalY: screenY };
let ContainerX: number = parentX - COLLECTION_BORDER_WIDTH;
@@ -81,7 +93,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
Yy = ry - COLLECTION_BORDER_WIDTH;
}
- let Ss = this.props.Document.GetData(KeyStore.Scale, NumberField, Number(1));
+ let Ss = this.props.Document.GetNumber(KeyStore.Scale, 1);
let Panxx = this.props.Document.GetData(KeyStore.PanX, NumberField, Number(0));
let Panyy = this.props.Document.GetData(KeyStore.PanY, NumberField, Number(0));
let LocalX = (ContainerX - (Xx + Panxx)) / Ss;
@@ -103,7 +115,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
if (this.props.ContainingCollectionView instanceof CollectionDockingView) {
var { translateX: rx, translateY: ry } = Utils.GetScreenTransform(this.MainContent.current!);
Xx = rx - COLLECTION_BORDER_WIDTH;
- Yy = ry - COLLECTION_BORDER_WIDTH;
+ Yy = ry - COLLECTION_BORDER_WIDTH + 18 * Ss;
}
let W = COLLECTION_BORDER_WIDTH;
@@ -113,9 +125,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
// if this collection view is nested within another collection view, then
// first transform the local point into the parent collection's coordinate space.
- let containingDocView = this.props.ContainingCollectionView != undefined && this.props.ContainingCollectionView != FieldWaiting ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined;
- if (containingDocView != undefined && containingDocView != FieldWaiting) {
- let ss = containingDocView.props.Document.GetData(KeyStore.Scale, NumberField, Number(1));
+ let containingDocView = this.props.ContainingCollectionView != undefined ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined;
+ if (containingDocView != undefined) {
+ let ss = containingDocView.props.Document.GetNumber(KeyStore.Scale, 1) * this.props.Scaling;
let panxx = containingDocView.props.Document.GetData(KeyStore.PanX, NumberField, Number(0)) + COLLECTION_BORDER_WIDTH * ss;
let panyy = containingDocView.props.Document.GetData(KeyStore.PanY, NumberField, Number(0)) + COLLECTION_BORDER_WIDTH * ss;
let { ScreenX, ScreenY } = containingDocView.TransformToScreenPoint(parentX, parentY, ss, panxx, panyy);
@@ -138,8 +150,21 @@ export class DocumentView extends React.Component<DocumentViewProps> {
if (bindings.DocumentView === undefined) {
bindings.DocumentView = this; // set the DocumentView to this if it hasn't already been set by a sub-class during its render method.
}
+ var annotated = <JsxParser
+ components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }}
+ bindings={bindings}
+ jsx={this.backgroundLayout}
+ showWarnings={true}
+ onError={(test: any) => { console.log(test) }}
+ />;
+ bindings["BackgroundView"] = this.backgroundLayout ? annotated : null;
+
+ var width = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
+ var strwidth = width > 0 ? width.toString() + "px" : "100%";
+ var height = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ var strheight = height > 0 ? height.toString() + "px" : "100%";
return (
- <div className="node" ref={this._mainCont} style={{ width: "100%", height: "100%", }}>
+ <div className="node" ref={this._mainCont} style={{ width: strwidth, height: strheight, transformOrigin: "left top", transform: `scale(${this.props.Scaling},${this.props.Scaling})` }}>
<JsxParser
components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }}
bindings={bindings}
diff --git a/src/views/nodes/FieldTextBox.scss b/src/client/views/nodes/FieldTextBox.scss
index b6ce2fabc..b6ce2fabc 100644
--- a/src/views/nodes/FieldTextBox.scss
+++ b/src/client/views/nodes/FieldTextBox.scss
diff --git a/src/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 05a7b91b9..12371eb2e 100644
--- a/src/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,15 +1,15 @@
import React = require("react")
-import { Document } from "../../fields/Document";
import { observer } from "mobx-react";
import { computed } from "mobx";
-import { Field, Opt, FieldWaiting } from "../../fields/Field";
-import { TextField } from "../../fields/TextField";
-import { NumberField } from "../../fields/NumberField";
-import { RichTextField } from "../../fields/RichTextField";
+import { Field, Opt, FieldWaiting, FieldValue } from "../../../fields/Field";
+import { Document } from "../../../fields/Document";
+import { TextField } from "../../../fields/TextField";
+import { NumberField } from "../../../fields/NumberField";
+import { RichTextField } from "../../../fields/RichTextField";
+import { ImageField } from "../../../fields/ImageField";
+import { Key } from "../../../fields/Key";
import { FormattedTextBox } from "./FormattedTextBox";
-import { ImageField } from "../../fields/ImageField";
import { ImageBox } from "./ImageBox";
-import { Key } from "../../fields/Key";
import { DocumentView } from "./DocumentView";
//
@@ -27,7 +27,7 @@ export interface FieldViewProps {
export class FieldView extends React.Component<FieldViewProps> {
public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} />`; }
@computed
- get field(): Opt<Field> {
+ get field(): FieldValue<Field> {
const { doc, fieldKey } = this.props;
return doc.Get(fieldKey);
}
diff --git a/src/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 492367fce..492367fce 100644
--- a/src/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
diff --git a/src/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 3e3e22e46..f6bd0c0f8 100644
--- a/src/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,18 +1,18 @@
import { action, IReactionDisposer, reaction } from "mobx";
-import { observer } from "mobx-react"
import { baseKeymap } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
import { schema } from "prosemirror-schema-basic";
import { EditorState, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
-import { Opt, FieldWaiting } from "../../fields/Field";
+import { Opt, FieldWaiting, FieldValue } from "../../../fields/Field";
import { SelectionManager } from "../../util/SelectionManager";
import "./FormattedTextBox.scss";
import React = require("react")
-import { RichTextField } from "../../fields/RichTextField";
+import { RichTextField } from "../../../fields/RichTextField";
import { FieldViewProps, FieldView } from "./FieldView";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
+import { observer } from "mobx-react";
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
@@ -31,7 +31,6 @@ import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView
// specified Key and assigns it to an HTML input node. When changes are made tot his node,
// this will edit the document and assign the new value to that field.
//]
-@observer
export class FormattedTextBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString("FormattedTextBox"); }
@@ -48,7 +47,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
}
dispatchTransaction = (tx: Transaction) => {
- if (this._editorView && this._editorView != FieldWaiting) {
+ if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
const { doc, fieldKey } = this.props;
@@ -85,17 +84,17 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
const field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
return field && field != FieldWaiting ? field.Data : undefined;
}, (field) => {
- if (field && this._editorView && this._editorView != FieldWaiting) {
+ if (field && this._editorView) {
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)));
}
})
}
componentWillUnmount() {
- if (this._editorView && this._editorView != FieldWaiting) {
+ if (this._editorView) {
this._editorView.destroy();
}
- if (this._reactionDisposer && this._reactionDisposer != FieldWaiting) {
+ if (this._reactionDisposer) {
this._reactionDisposer();
}
}
@@ -119,7 +118,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
return (<div className="formattedTextBox-cont"
style={{
color: "initial",
- whiteSpace: "initial"
+ whiteSpace: "initial",
}}
onPointerDown={this.onPointerDown}
ref={this._ref} />)
diff --git a/src/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 136fda1d0..2bd8b1d3c 100644
--- a/src/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,6 +1,8 @@
.imageBox-cont {
padding: 0vw;
+ position: absolute;
+ width: 100%
}
.imageBox-button {
diff --git a/src/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 123c76d19..2fa70734d 100644
--- a/src/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -4,12 +4,13 @@ import 'react-image-lightbox/style.css'; // This only needs to be imported once
import { SelectionManager } from "../../util/SelectionManager";
import "./ImageBox.scss";
import React = require("react")
-import { ImageField } from '../../fields/ImageField';
+import { ImageField } from '../../../fields/ImageField';
import { FieldViewProps, FieldView } from './FieldView';
import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
-import { FieldWaiting } from '../../fields/Field';
+import { FieldWaiting } from '../../../fields/Field';
import { observer } from "mobx-react"
-import { observable, action } from 'mobx';
+import { observable, action, spy } from 'mobx';
+import { KeyStore } from '../../../fields/Key';
@observer
export class ImageBox extends React.Component<FieldViewProps> {
@@ -67,7 +68,9 @@ export class ImageBox extends React.Component<FieldViewProps> {
mainSrc={images[this._photoIndex]}
nextSrc={images[(this._photoIndex + 1) % images.length]}
prevSrc={images[(this._photoIndex + images.length - 1) % images.length]}
- onCloseRequest={() => this.setState({ isOpen: false })}
+ onCloseRequest={action(() =>
+ this._isOpen = false
+ )}
onMovePrevRequest={action(() =>
this._photoIndex = (this._photoIndex + images.length - 1) % images.length
)}
@@ -82,10 +85,11 @@ export class ImageBox extends React.Component<FieldViewProps> {
let field = this.props.doc.Get(this.props.fieldKey);
let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
field instanceof ImageField ? field.Data.href : "http://www.cs.brown.edu/~bcz/face.gif";
+ let nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 1);
return (
<div className="imageBox-cont" onPointerDown={this.onPointerDown} ref={this._ref} >
- <img src={path} width="100%" alt="Image not found" />
+ <img src={path} width={nativeWidth} alt="Image not found" />
{this.lightbox(path)}
</div>)
}
diff --git a/src/views/nodes/NodeView.scss b/src/client/views/nodes/NodeView.scss
index dac1c0a8e..dac1c0a8e 100644
--- a/src/views/nodes/NodeView.scss
+++ b/src/client/views/nodes/NodeView.scss
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 3d74c047c..c682d8e94 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -1,23 +1,22 @@
-import { Field, Cast, Opt, FieldWaiting, FIELD_ID, DOC_ID } from "./Field"
+import { Field, Cast, Opt, FieldWaiting, FieldId, FieldValue } from "./Field"
import { Key, KeyStore } from "./Key"
import { NumberField } from "./NumberField";
import { ObservableMap, computed, action, observable } from "mobx";
import { TextField } from "./TextField";
import { ListField } from "./ListField";
-import { findDOMNode } from "react-dom";
-import { Server } from "../Server";
+import { Server } from "../client/Server";
export class Document extends Field {
- public fields: ObservableMap<Key, Opt<Field>> = new ObservableMap();
- public _proxies: ObservableMap<Key, FIELD_ID> = new ObservableMap();
+ public fields: ObservableMap<Key, Field> = new ObservableMap();
+ public _proxies: ObservableMap<Key, FieldId> = new ObservableMap();
@computed
public get Title() {
return this.GetText(KeyStore.Title, "<untitled>");
}
- Get(key: Key, ignoreProto: boolean = false): Opt<Field> {
- let field: Opt<Field>;
+ Get(key: Key, ignoreProto: boolean = false): FieldValue<Field> {
+ let field: FieldValue<Field>;
if (ignoreProto) {
if (this.fields.has(key)) {
field = this.fields.get(key);
@@ -25,7 +24,7 @@ export class Document extends Field {
field = Server.GetDocumentField(this, key);
}
} else {
- let doc: Opt<Document> = this;
+ let doc: FieldValue<Document> = this;
while (doc && doc != FieldWaiting && field != FieldWaiting) {
if (!doc.fields.has(key)) {
if (doc._proxies.has(key)) {
@@ -46,7 +45,7 @@ export class Document extends Field {
return field;
}
- GetT<T extends Field = Field>(key: Key, ctor: { new(...args: any[]): T }, ignoreProto: boolean = false): Opt<T> {
+ GetT<T extends Field = Field>(key: Key, ctor: { new(...args: any[]): T }, ignoreProto: boolean = false): FieldValue<T> {
var getfield = this.Get(key, ignoreProto);
if (getfield != FieldWaiting) {
return Cast(getfield, ctor);
@@ -119,13 +118,13 @@ export class Document extends Field {
this.SetData(key, value, NumberField, replaceWrongType);
}
- GetPrototype(): Opt<Document> {
+ GetPrototype(): FieldValue<Document> {
return this.GetT(KeyStore.Prototype, Document, true);
}
GetAllPrototypes(): Document[] {
let protos: Document[] = [];
- let doc: Opt<Document> = this;
+ let doc: FieldValue<Document> = this;
while (doc && doc != FieldWaiting) {
protos.push(doc);
doc = doc.GetPrototype();
@@ -141,6 +140,10 @@ export class Document extends Field {
return delegate;
}
+ ToScriptString(): string {
+ return "";
+ }
+
TrySetValue(value: any): boolean {
throw new Error("Method not implemented.");
}
diff --git a/src/fields/DocumentReference.ts b/src/fields/DocumentReference.ts
index 10dac9f92..983b162a3 100644
--- a/src/fields/DocumentReference.ts
+++ b/src/fields/DocumentReference.ts
@@ -1,4 +1,4 @@
-import { Field, Opt } from "./Field";
+import { Field, Opt, FieldValue } from "./Field";
import { Document } from "./Document";
import { Key } from "./Key";
@@ -15,12 +15,12 @@ export class DocumentReference extends Field {
super();
}
- Dereference(): Opt<Field> {
+ Dereference(): FieldValue<Field> {
return this.document.Get(this.key);
}
- DereferenceToRoot(): Opt<Field> {
- let field: Opt<Field> = this;
+ DereferenceToRoot(): FieldValue<Field> {
+ let field: FieldValue<Field> = this;
while (field instanceof DocumentReference) {
field = field.Dereference();
}
@@ -37,5 +37,8 @@ export class DocumentReference extends Field {
throw new Error("Method not implemented.");
}
+ ToScriptString(): string {
+ return "";
+ }
} \ No newline at end of file
diff --git a/src/fields/Field.ts b/src/fields/Field.ts
index 9880116c0..4a3968699 100644
--- a/src/fields/Field.ts
+++ b/src/fields/Field.ts
@@ -1,7 +1,7 @@
import { Utils } from "../Utils";
-export function Cast<T extends Field>(field: Opt<Field>, ctor: { new(): T }): Opt<T> {
+export function Cast<T extends Field>(field: FieldValue<Field>, ctor: { new(): T }): Opt<T> {
if (field) {
if (ctor && field instanceof ctor) {
return field;
@@ -10,36 +10,36 @@ export function Cast<T extends Field>(field: Opt<Field>, ctor: { new(): T }): Op
return undefined;
}
-export let FieldWaiting: FIELD_WAITING = "<Waiting>";
+export const FieldWaiting: FIELD_WAITING = "<Waiting>";
export type FIELD_WAITING = "<Waiting>";
-export type FIELD_ID = string | undefined;
-export type DOC_ID = FIELD_ID;
-export type Opt<T> = T | undefined | FIELD_WAITING;
+export type FieldId = string;
+export type Opt<T> = T | undefined;
+export type FieldValue<T> = Opt<T> | FIELD_WAITING;
export abstract class Field {
//FieldUpdated: TypedEvent<Opt<FieldUpdatedArgs>> = new TypedEvent<Opt<FieldUpdatedArgs>>();
- private id: FIELD_ID;
- get Id(): FIELD_ID {
+ private id: FieldId;
+ get Id(): FieldId {
return this.id;
}
- constructor(id: FIELD_ID = undefined) {
+ constructor(id: Opt<FieldId> = undefined) {
this.id = id || Utils.GenerateGuid();
}
- Dereference(): Opt<Field> {
+ Dereference(): FieldValue<Field> {
return this;
}
- DereferenceToRoot(): Opt<Field> {
+ DereferenceToRoot(): FieldValue<Field> {
return this;
}
- DereferenceT<T extends Field = Field>(ctor: { new(): T }): Opt<T> {
+ DereferenceT<T extends Field = Field>(ctor: { new(): T }): FieldValue<T> {
return Cast(this.Dereference(), ctor);
}
- DereferenceToRootT<T extends Field = Field>(ctor: { new(): T }): Opt<T> {
+ DereferenceToRootT<T extends Field = Field>(ctor: { new(): T }): FieldValue<T> {
return Cast(this.DereferenceToRoot(), ctor);
}
@@ -47,6 +47,8 @@ export abstract class Field {
return this.id === other.id;
}
+ abstract ToScriptString(): string;
+
abstract TrySetValue(value: any): boolean;
abstract GetValue(): any;
diff --git a/src/fields/ImageField.ts b/src/fields/ImageField.ts
index bc2e7cdf4..d82260f54 100644
--- a/src/fields/ImageField.ts
+++ b/src/fields/ImageField.ts
@@ -3,13 +3,17 @@ import { Field } from "./Field";
export class ImageField extends BasicField<URL> {
constructor(data: URL | undefined = undefined) {
- super(data == undefined ? new URL("http://cs.brown.edu/~bcz/face.gif") : data);
+ super(data == undefined ? new URL("http://cs.brown.edu/~bcz/bob_fettucine.jpg") : data);
}
toString(): string {
return this.Data.href;
}
+ ToScriptString(): string {
+ return `new ImageField("${this.Data}")`;
+ }
+
Copy(): Field {
return new ImageField(this.Data);
}
diff --git a/src/fields/Key.ts b/src/fields/Key.ts
index 5cd43f55e..8d92b89b6 100644
--- a/src/fields/Key.ts
+++ b/src/fields/Key.ts
@@ -27,6 +27,9 @@ export class Key extends Field {
return this;
}
+ ToScriptString(): string {
+ return name;
+ }
}
@@ -39,11 +42,15 @@ export namespace KeyStore {
export const PanX = new Key("PanX");
export const PanY = new Key("PanY");
export const Scale = new Key("Scale");
+ export const NativeWidth = new Key("NativeWidth");
+ export const NativeHeight = new Key("NativeHeight");
export const Width = new Key("Width");
export const Height = new Key("Height");
export const ZIndex = new Key("ZIndex");
export const Data = new Key("Data");
+ export const Annotations = new Key("Annotations");
export const Layout = new Key("Layout");
+ export const BackgroundLayout = new Key("BackgroundLayout");
export const LayoutKeys = new Key("LayoutKeys");
export const LayoutFields = new Key("LayoutFields");
export const ColumnsKey = new Key("SchemaColumns");
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index 8607ebe43..8843338c1 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -6,6 +6,10 @@ export class ListField<T extends Field> extends BasicField<T[]> {
super(data.slice());
}
+ ToScriptString(): string {
+ return "new ListField([" + this.Data.map(field => field.ToScriptString()).join(", ") + "])";
+ }
+
Copy(): Field {
return new ListField<T>(this.Data);
}
diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts
index c3444f644..03926d696 100644
--- a/src/fields/NumberField.ts
+++ b/src/fields/NumberField.ts
@@ -5,6 +5,10 @@ export class NumberField extends BasicField<number> {
super(data);
}
+ ToScriptString(): string {
+ return "new NumberField(this.Data)";
+ }
+
Copy() {
return new NumberField(this.Data);
}
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 24c7472d8..4a77c669c 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -5,6 +5,10 @@ export class RichTextField extends BasicField<string> {
super(data);
}
+ ToScriptString(): string {
+ return `new RichTextField(${this.Data})`;
+ }
+
Copy() {
return new RichTextField(this.Data);
}
diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts
index 95825d2ae..11d2ed7cd 100644
--- a/src/fields/TextField.ts
+++ b/src/fields/TextField.ts
@@ -5,6 +5,10 @@ export class TextField extends BasicField<string> {
super(data);
}
+ ToScriptString(): string {
+ return `new TextField("${this.Data}")`;
+ }
+
Copy() {
return new TextField(this.Data);
}
diff --git a/src/server/index.js b/src/server/index.js
new file mode 100644
index 000000000..15e763f9d
--- /dev/null
+++ b/src/server/index.js
@@ -0,0 +1,13 @@
+"use strict";
+exports.__esModule = true;
+var express = require("express");
+var app = express();
+var port = 8080; // default port to listen
+// define a route handler for the default home page
+app.get("/", function (req, res) {
+ res.send("Hello world!");
+});
+// start the Express server
+app.listen(port, function () {
+ console.log("server started at http://localhost:" + port);
+});
diff --git a/src/server/index.ts b/src/server/index.ts
new file mode 100644
index 000000000..640ad8180
--- /dev/null
+++ b/src/server/index.ts
@@ -0,0 +1,29 @@
+import * as express from 'express'
+const app = express()
+import * as webpack from 'webpack'
+import * as wdm from 'webpack-dev-middleware';
+import * as whm from 'webpack-hot-middleware';
+import * as path from 'path'
+const config = require('../../webpack.config')
+const compiler = webpack(config)
+const port = 1050; // default port to listen
+
+// define a route handler for the default home page
+app.get("/", (req, res) => {
+ res.sendFile(path.join(__dirname, '../../deploy/index.html'));
+});
+
+app.get("/hello", (req, res) => {
+ res.send("<p>Hello</p>");
+})
+
+app.use(wdm(compiler, {
+ publicPath: config.output.publicPath
+}))
+
+app.use(whm(compiler))
+
+// start the Express server
+app.listen(port, () => {
+ console.log(`server started at http://localhost:${port}`);
+}); \ No newline at end of file
diff --git a/src/stores/NodeCollectionStore.ts b/src/stores/NodeCollectionStore.ts
deleted file mode 100644
index 7fac83d51..000000000
--- a/src/stores/NodeCollectionStore.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { computed, observable, action } from "mobx";
-import { NodeStore } from "./NodeStore";
-import { Document } from "../fields/Document";
-
-export class NodeCollectionStore extends NodeStore {
-
- @observable
- public Scale: number = 1;
-
- @observable
- public Nodes: NodeStore[] = new Array<NodeStore>();
-
- @observable
- public Docs: Document[] = [];
-
- @computed
- public get Transform(): string {
- const halfWidth = window.innerWidth / 2, halfHeight = window.innerHeight / 2;
- return `translate(${this.X + halfWidth}px, ${this.Y + halfHeight}px) scale(${this.Scale}) translate(${-halfWidth}px, ${-halfHeight}px)`;
- }
-
- @action
- public AddNodes(stores: NodeStore[]): void {
- stores.forEach(store => this.Nodes.push(store));
- }
-} \ No newline at end of file
diff --git a/src/stores/NodeStore.ts b/src/stores/NodeStore.ts
deleted file mode 100644
index 6a734cf44..000000000
--- a/src/stores/NodeStore.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { computed, observable } from "mobx";
-import { Utils } from "../Utils";
-
-export class NodeStore {
-
- public Id: string = Utils.GenerateGuid();
-
- @observable
- public X: number = 0;
-
- @observable
- public Y: number = 0;
-
- @observable
- public Width: number = 0;
-
- @observable
- public Height: number = 0;
-
- @computed
- public get Transform(): string {
- return "translate(" + this.X + "px, " + this.Y + "px)";
- }
-} \ No newline at end of file
diff --git a/src/stores/RootStore.ts b/src/stores/RootStore.ts
deleted file mode 100644
index 847fb6807..000000000
--- a/src/stores/RootStore.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { action, observable } from "mobx";
-
-// This globally accessible store might come in handy, although you may decide that you don't need it.
-export class RootStore {
-
- private constructor() {
- // initialization code
- }
-
- private static _instance: RootStore;
-
- public static get Instance(): RootStore {
- return this._instance || (this._instance = new this());
- }
-} \ No newline at end of file
diff --git a/src/stores/StaticTextNodeStore.ts b/src/stores/StaticTextNodeStore.ts
deleted file mode 100644
index 7c342a7a2..000000000
--- a/src/stores/StaticTextNodeStore.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { observable } from "mobx";
-import { NodeStore } from "./NodeStore";
-
-export class StaticTextNodeStore extends NodeStore {
-
- constructor(initializer: Partial<StaticTextNodeStore>) {
- super();
- Object.assign(this, initializer);
- }
-
- @observable
- public Title: string = "";
-
- @observable
- public Text: string = "";
-} \ No newline at end of file
diff --git a/src/stores/VideoNodeStore.ts b/src/stores/VideoNodeStore.ts
deleted file mode 100644
index e5187ab07..000000000
--- a/src/stores/VideoNodeStore.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { observable } from "mobx";
-import { NodeStore } from "./NodeStore";
-
-export class VideoNodeStore extends NodeStore {
-
- constructor(initializer: Partial<VideoNodeStore>) {
- super();
- Object.assign(this, initializer);
- }
-
- @observable
- public Title: string = "";
-
- @observable
- public Url: string = "";
-
-} \ No newline at end of file
diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx
deleted file mode 100644
index 45d37ca4f..000000000
--- a/src/views/collections/CollectionFreeFormView.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import { observer } from "mobx-react";
-import { Key, KeyStore } from "../../fields/Key";
-import React = require("react");
-import { action, observable, computed } from "mobx";
-import { Document } from "../../fields/Document";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { ListField } from "../../fields/ListField";
-import { NumberField } from "../../fields/NumberField";
-import { SSL_OP_SINGLE_DH_USE } from "constants";
-import { Documents } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import "./CollectionFreeFormView.scss";
-import { Utils } from "../../Utils";
-import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase";
-import { SelectionManager } from "../../util/SelectionManager";
-import { FieldWaiting } from "../../fields/Field";
-
-@observer
-export class CollectionFreeFormView extends CollectionViewBase {
- public static LayoutString() { return CollectionViewBase.LayoutString("CollectionFreeFormView"); }
- private _containerRef = React.createRef<HTMLDivElement>();
- private _canvasRef = React.createRef<HTMLDivElement>();
- private _nodeContainerRef = React.createRef<HTMLDivElement>();
- private _lastX: number = 0;
- private _lastY: number = 0;
-
- constructor(props: CollectionViewProps) {
- super(props);
- }
-
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- const doc = de.data["document"];
- var me = this;
- if (doc instanceof CollectionFreeFormDocumentView) {
- if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this && doc.props.ContainingCollectionView != FieldWaiting) {
- doc.props.ContainingCollectionView.removeDocument(doc.props.Document);
- this.addDocument(doc.props.Document);
- }
- const xOffset = de.data["xOffset"] as number || 0;
- const yOffset = de.data["yOffset"] as number || 0;
- const { scale, translateX, translateY } = Utils.GetScreenTransform(this._canvasRef.current!);
- if (this.props.ContainingDocumentView != FieldWaiting) {
- let sscale = this.props.ContainingDocumentView!.props.Document.GetData(KeyStore.Scale, NumberField, Number(1))
- const screenX = de.x - xOffset;
- const screenY = de.y - yOffset;
- const docX = (screenX - translateX) / sscale / scale;
- const docY = (screenY - translateY) / sscale / scale;
- doc.x = docX;
- doc.y = docY;
- this.bringToFront(doc);
- }
- }
- e.stopPropagation();
- }
-
- componentDidMount() {
- if (this._containerRef.current) {
- DragManager.MakeDropTarget(this._containerRef.current, {
- handlers: {
- drop: this.drop
- }
- });
- }
- }
-
- @action
- onPointerDown = (e: React.PointerEvent): void => {
- if ((e.button === 2 && this.active) ||
- !e.defaultPrevented) {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- this._lastX = e.pageX;
- this._lastY = e.pageY;
- }
- }
-
- @action
- onPointerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- e.stopPropagation();
- SelectionManager.DeselectAll();
- }
-
- @action
- onPointerMove = (e: PointerEvent): void => {
- var me = this;
- if (!e.cancelBubble && this.active && this.props.ContainingDocumentView != FieldWaiting) {
- e.preventDefault();
- e.stopPropagation();
- let currScale: number = this.props.ContainingDocumentView!.ScalingToScreenSpace;
- let x = this.props.DocumentForCollection.GetData(KeyStore.PanX, NumberField, Number(0));
- let y = this.props.DocumentForCollection.GetData(KeyStore.PanY, NumberField, Number(0));
- this.props.DocumentForCollection.SetData(KeyStore.PanX, x + (e.pageX - this._lastX) / currScale, NumberField);
- this.props.DocumentForCollection.SetData(KeyStore.PanY, y + (e.pageY - this._lastY) / currScale, NumberField);
- }
- this._lastX = e.pageX;
- this._lastY = e.pageY;
- }
-
- @action
- onPointerWheel = (e: React.WheelEvent): void => {
- e.stopPropagation();
-
- if (this.props.ContainingDocumentView == FieldWaiting)
- return;
- let { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY);
-
- var deltaScale = (1 - (e.deltaY / 1000)) * Ss;
-
- var newContainerX = LocalX * deltaScale + Panxx + Xx;
- var newContainerY = LocalY * deltaScale + Panyy + Yy;
-
- let dx = ContainerX - newContainerX;
- let dy = ContainerY - newContainerY;
-
- this.props.DocumentForCollection.Set(KeyStore.Scale, new NumberField(deltaScale));
- this.props.DocumentForCollection.SetData(KeyStore.PanX, Panxx + dx, NumberField);
- this.props.DocumentForCollection.SetData(KeyStore.PanY, Panyy + dy, NumberField);
- }
-
- @action
- onDrop = (e: React.DragEvent): void => {
- e.stopPropagation()
- e.preventDefault()
- let fReader = new FileReader()
- let file = e.dataTransfer.items[0].getAsFile();
- let that = this;
- const panx: number = this.props.DocumentForCollection.GetData(KeyStore.PanX, NumberField, Number(0));
- const pany: number = this.props.DocumentForCollection.GetData(KeyStore.PanY, NumberField, Number(0));
- let x = e.pageX - panx
- let y = e.pageY - pany
-
- fReader.addEventListener("load", action("drop", (event) => {
- if (fReader.result) {
- let url = "" + fReader.result;
- let doc = Documents.ImageDocument(url, {
- x: x, y: y
- })
- let docs = that.props.DocumentForCollection.GetT(KeyStore.Data, ListField);
- if (docs != FieldWaiting) {
- if (!docs) {
- docs = new ListField<Document>();
- that.props.DocumentForCollection.Set(KeyStore.Data, docs)
- }
- docs.Data.push(doc);
- }
- }
- }), false)
-
- if (file) {
- fReader.readAsDataURL(file)
- }
- }
-
- onDragOver = (e: React.DragEvent): void => {
- }
-
- @action
- bringToFront(doc: CollectionFreeFormDocumentView) {
- const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props;
-
- const value: Document[] = Document.GetList<Document>(fieldKey, []);
- var topmost = value.reduce((topmost, d) => Math.max(d.GetNumber(KeyStore.ZIndex, 0), topmost), -1000);
- value.map(d => {
- var zind = d.GetNumber(KeyStore.ZIndex, 0);
- if (zind != topmost - 1 - (topmost - zind) && d != doc.props.Document) {
- d.SetData(KeyStore.ZIndex, topmost - 1 - (topmost - zind), NumberField);
- }
- })
-
- if (doc.props.Document.GetNumber(KeyStore.ZIndex, 0) != 0) {
- doc.props.Document.SetData(KeyStore.ZIndex, 0, NumberField);
- }
- }
-
- render() {
- const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props;
- const value: Document[] = Document.GetList<Document>(fieldKey, []);
- const panx: number = Document.GetNumber(KeyStore.PanX, 0);
- const pany: number = Document.GetNumber(KeyStore.PanY, 0);
- const currScale: number = Document.GetNumber(KeyStore.Scale, 1);
-
- return (
- <div className="border" style={{
- borderWidth: `${COLLECTION_BORDER_WIDTH}px`,
- }}>
- <div className="collectionfreeformview-container"
- onPointerDown={this.onPointerDown}
- onWheel={this.onPointerWheel}
- onContextMenu={(e) => e.preventDefault()}
- onDrop={this.onDrop}
- onDragOver={this.onDragOver}
- ref={this._containerRef}>
- <div className="collectionfreeformview" style={{ transform: `translate(${panx}px, ${pany}px) scale(${currScale}, ${currScale})`, transformOrigin: `left, top` }} ref={this._canvasRef}>
-
- <div className="node-container" ref={this._nodeContainerRef}>
- {value.map(doc => {
- return (<CollectionFreeFormDocumentView key={doc.Id} ContainingCollectionView={this} Document={doc} DocumentView={undefined} />);
- })}
- </div>
- </div>
- </div>
- </div>
- );
- }
-} \ No newline at end of file
diff --git a/src/views/nodes/TextNodeView.tsx b/src/views/nodes/TextNodeView.tsx
deleted file mode 100644
index ab762df12..000000000
--- a/src/views/nodes/TextNodeView.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import {observer} from "mobx-react";
-import {StaticTextNodeStore} from "../../stores/StaticTextNodeStore";
-import "./NodeView.scss";
-import {TopBar} from "./TopBar";
-import React = require("react");
-
-interface IProps {
- store: StaticTextNodeStore;
-}
-
-@observer
-export class TextNodeView extends React.Component<IProps> {
-
- render() {
- let store = this.props.store;
- return (
- <div className="node text-node" style={{transform: store.Transform}}>
- <TopBar store={store} />
- <div className="scroll-box">
- <div className="content">
- <h3 className="title">{store.Title}</h3>
- <p className="paragraph">{store.Text}</p>
- </div>
- </div>
- </div>
- );
- }
-} \ No newline at end of file
diff --git a/src/views/nodes/TopBar.tsx b/src/views/nodes/TopBar.tsx
deleted file mode 100644
index bb126e8b5..000000000
--- a/src/views/nodes/TopBar.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { observer } from "mobx-react";
-import { NodeStore } from "../../stores/NodeStore";
-import "./NodeView.scss";
-import React = require("react");
-
-interface IProps {
- store: NodeStore;
-}
-
-@observer
-export class TopBar extends React.Component<IProps> {
-
- private _isPointerDown = false;
-
- onPointerDown = (e: React.PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isPointerDown = true;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
-
- onPointerUp = (e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- this._isPointerDown = false;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- }
-
- onPointerMove = (e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
- if (!this._isPointerDown) {
- return;
- }
- this.props.store.X += e.movementX;
- this.props.store.Y += e.movementY;
- }
-
- render() {
- return <div className="top" onPointerDown={this.onPointerDown}></div>
- }
-}
diff --git a/src/views/nodes/VideoNodeView.scss b/src/views/nodes/VideoNodeView.scss
deleted file mode 100644
index f412c3519..000000000
--- a/src/views/nodes/VideoNodeView.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.node {
- video {
- width: 100%;
- }
-} \ No newline at end of file
diff --git a/src/views/nodes/VideoNodeView.tsx b/src/views/nodes/VideoNodeView.tsx
deleted file mode 100644
index 0a7b3d174..000000000
--- a/src/views/nodes/VideoNodeView.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { observer } from "mobx-react";
-import { VideoNodeStore } from "../../stores/VideoNodeStore";
-import "./NodeView.scss";
-import { TopBar } from "./TopBar";
-import "./VideoNodeView.scss";
-import React = require("react");
-
-interface IProps {
- store: VideoNodeStore;
-}
-
-@observer
-export class VideoNodeView extends React.Component<IProps> {
-
- render() {
- let store = this.props.store;
- return (
- <div className="node text-node" style={{ transform: store.Transform }}>
- <TopBar store={store} />
- <div className="scroll-box">
- <div className="content">
- <h3 className="title">{store.Title}</h3>
- <video src={store.Url} controls />
- </div>
- </div>
- </div>
- );
- }
-} \ No newline at end of file