diff options
author | Monika Hedman <monika_hedman@brown.edu> | 2019-02-12 16:26:22 -0500 |
---|---|---|
committer | Monika Hedman <monika_hedman@brown.edu> | 2019-02-12 16:26:22 -0500 |
commit | 05a710e2a541a07347d1626489a1811874126c79 (patch) | |
tree | feb751e46036d2104bb81c5bcc4e4ab0b6336c08 /src | |
parent | 6445930e05e8eb81a36930615926712986bc1a9d (diff) | |
parent | 7a93f60c9529e5d175e617fc7c07145a9b33e572 (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into hedmanLocal
Diffstat (limited to 'src')
-rw-r--r-- | src/Server.tsx | 106 | ||||
-rw-r--r-- | src/client/Server.ts | 64 | ||||
-rw-r--r-- | src/client/SocketStub.ts | 78 | ||||
-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.ts | 94 | ||||
-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.tsx | 39 | ||||
-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.tsx | 251 | ||||
-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.ts | 25 | ||||
-rw-r--r-- | src/fields/DocumentReference.ts | 11 | ||||
-rw-r--r-- | src/fields/Field.ts | 26 | ||||
-rw-r--r-- | src/fields/ImageField.ts | 6 | ||||
-rw-r--r-- | src/fields/Key.ts | 7 | ||||
-rw-r--r-- | src/fields/ListField.ts | 4 | ||||
-rw-r--r-- | src/fields/NumberField.ts | 4 | ||||
-rw-r--r-- | src/fields/RichTextField.ts | 4 | ||||
-rw-r--r-- | src/fields/TextField.ts | 4 | ||||
-rw-r--r-- | src/server/index.js | 13 | ||||
-rw-r--r-- | src/server/index.ts | 29 | ||||
-rw-r--r-- | src/stores/NodeCollectionStore.ts | 26 | ||||
-rw-r--r-- | src/stores/NodeStore.ts | 24 | ||||
-rw-r--r-- | src/stores/RootStore.ts | 15 | ||||
-rw-r--r-- | src/stores/StaticTextNodeStore.ts | 16 | ||||
-rw-r--r-- | src/stores/VideoNodeStore.ts | 17 | ||||
-rw-r--r-- | src/views/collections/CollectionFreeFormView.tsx | 210 | ||||
-rw-r--r-- | src/views/nodes/TextNodeView.tsx | 28 | ||||
-rw-r--r-- | src/views/nodes/TopBar.tsx | 46 | ||||
-rw-r--r-- | src/views/nodes/VideoNodeView.scss | 5 | ||||
-rw-r--r-- | src/views/nodes/VideoNodeView.tsx | 29 |
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 |