diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/Server.ts | 23 | ||||
-rw-r--r-- | src/client/SocketStub.ts | 6 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 4 | ||||
-rw-r--r-- | src/client/util/Transform.ts | 94 | ||||
-rw-r--r-- | src/client/views/Main.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 13 | ||||
-rw-r--r-- | src/client/views/collections/CollectionFreeFormView.tsx | 25 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 7 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewBase.tsx | 11 | ||||
-rw-r--r-- | src/client/views/nodes/AnnotationView.tsx | 12 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 8 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 8 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 1 | ||||
-rw-r--r-- | src/fields/Document.ts | 7 | ||||
-rw-r--r-- | src/fields/Field.ts | 10 |
15 files changed, 189 insertions, 46 deletions
diff --git a/src/client/Server.ts b/src/client/Server.ts index 85e55a84e..0cb6e17c2 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -1,16 +1,16 @@ -import { Field, FieldWaiting, FIELD_ID, FIELD_WAITING, FieldValue } from "../fields/Field" +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<FIELD_ID, Field | FIELD_WAITING> = new ObservableMap(); + 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: FIELD_ID, callback: (field: Field) => void = (f) => { }, hackTimeout: number = -1) { + 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 @@ -24,15 +24,18 @@ export class Server { } static times = 0; // hack for testing - public static GetDocumentField(doc: Document, key: Key) { + 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; - return this.GetField(doc._proxies.get(key), - action((fieldfromserver: Field) => { - doc._proxies.delete(key); - doc.fields.set(key, fieldfromserver); - }) - , hackTimeout); + 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) { diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts index 58dedbf82..cea30cb8b 100644 --- a/src/client/SocketStub.ts +++ b/src/client/SocketStub.ts @@ -1,11 +1,11 @@ -import { Field, FIELD_ID } from "../fields/Field" +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<FIELD_ID, Field> = new ObservableMap(); + static FieldStore: ObservableMap<FieldId, Field> = new ObservableMap(); public static SEND_ADD_DOCUMENT(document: Document) { // Send a serialized version of the document to the server @@ -19,7 +19,7 @@ export class SocketStub { 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: FIELD_ID, callback: (field: Field) => void, timeout: number) { + 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); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8fbf99da6..7512d25cb 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -10,7 +10,7 @@ import { CollectionSchemaView } from "../views/collections/CollectionSchemaView" 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; @@ -115,7 +115,7 @@ export namespace Documents { } - let imageProtoId: FIELD_ID; + let imageProtoId: FieldId; function GetImagePrototype(): Document { if (imageProtoId === undefined) { let imageProto = new Document(); 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/client/views/Main.tsx b/src/client/views/Main.tsx index 8d474698f..b58704264 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -13,6 +13,7 @@ import "./Main.scss"; import { ContextMenu } from './ContextMenu'; import { DocumentView } from './nodes/DocumentView'; import { ImageField } from '../../fields/ImageField'; +import { Transform } from '../util/Transform'; configure({ @@ -84,7 +85,10 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { ReactDOM.render(( <div style={{ position: "absolute", width: "100%", height: "100%" }}> - <DocumentView Document={mainContainer} Scaling={1} ContainingCollectionView={undefined} DocumentView={undefined} /> + <DocumentView Document={mainContainer} + AddDocument={undefined} RemoveDocument={undefined} GetTransform={() => Transform.Identity} + Scaling={1} + ContainingCollectionView={undefined} DocumentView={undefined} /> <DocumentDecorations /> <ContextMenu /> </div>), diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 037b85712..bef301eb5 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -14,6 +14,7 @@ import * as GoldenLayout from "golden-layout"; import * as ReactDOM from 'react-dom'; import { DragManager } from "../../util/DragManager"; import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { Transform } from "../../util/Transform"; @observer export class CollectionDockingView extends CollectionViewBase { @@ -95,7 +96,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} Scaling={1} 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") { @@ -236,7 +241,11 @@ export class CollectionDockingView extends CollectionViewBase { container.getElement().html("<div id='" + containingDiv + "'></div>"); setTimeout(function () { ReactDOM.render(( - <DocumentView key={state.doc.Id} Scaling={1} Document={state.doc} ContainingCollectionView={me} DocumentView={undefined} /> + <DocumentView key={state.doc.Id} Document={state.doc} + AddDocument={me.addDocument} RemoveDocument={me.removeDocument} + GetTransform={() => Transform.Identity} + Scaling={1} + ContainingCollectionView={me} DocumentView={undefined} /> ), document.getElementById(containingDiv) ); diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index bf24965dc..e485c8e14 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -13,6 +13,7 @@ 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 { @@ -196,6 +197,23 @@ export class CollectionFreeFormView extends CollectionViewBase { } } + @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, []); @@ -218,7 +236,12 @@ export class CollectionFreeFormView extends CollectionViewBase { <div className="node-container" ref={this._nodeContainerRef}> {this.props.BackgroundView} {value.map(doc => { - return (<CollectionFreeFormDocumentView Scaling={this.resizeScaling} key={doc.Id} ContainingCollectionView={this} Document={doc} DocumentView={undefined} />); + return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc} + AddDocument={this.addDocument} + RemoveDocument={this.removeDocument} + GetTransform={this.getTransform} + Scaling={this.resizeScaling} + ContainingCollectionView={this} DocumentView={undefined} />); })} </div> </div> diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b2ee2f5f2..331c4f6fe 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -14,6 +14,7 @@ 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 { @@ -105,7 +106,11 @@ export class CollectionSchemaView extends CollectionViewBase { let content; if (this.selectedIndex != -1) { content = ( - <DocumentView Scaling={1} Document={children[this.selectedIndex]} DocumentView={undefined} ContainingCollectionView={this} /> + <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 /> diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index e854d3077..eea7908ce 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -10,12 +10,14 @@ 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>; } @@ -44,15 +46,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/client/views/nodes/AnnotationView.tsx b/src/client/views/nodes/AnnotationView.tsx deleted file mode 100644 index 2e50370a1..000000000 --- a/src/client/views/nodes/AnnotationView.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React = require('react') -import { CollectionViewProps } from '../collections/CollectionViewBase'; - -export class AnnotationView extends React.Component<CollectionViewProps> { - - render() { - return ( - <></> - ); - } - -}
\ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fafb470f9..4641ea290 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -10,6 +10,7 @@ import { ContextMenu } from "../ContextMenu"; import "./NodeView.scss"; import React = require("react"); import { DocumentView, DocumentViewProps } from "./DocumentView"; +import { Transform } from "../../util/Transform"; @observer @@ -17,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); @@ -218,6 +220,10 @@ 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 ( @@ -232,7 +238,7 @@ export class CollectionFreeFormDocumentView extends DocumentView { onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown}> - <DocumentView {...this.props} DocumentView={this} /> + <DocumentView {...this.props} GetTransform={this.getTransform} DocumentView={this.props.DocumentView} /> </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a2f8ceebe..691ac6311 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -15,14 +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> { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index eead43b9f..8f959762a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -12,6 +12,7 @@ import React = require("react") 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 diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 6f9752a8e..c682d8e94 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -1,15 +1,14 @@ -import { Field, Cast, Opt, FieldWaiting, FIELD_ID, FieldValue } 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 "../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() { diff --git a/src/fields/Field.ts b/src/fields/Field.ts index 6adee9b61..4a3968699 100644 --- a/src/fields/Field.ts +++ b/src/fields/Field.ts @@ -10,21 +10,21 @@ export function Cast<T extends Field>(field: FieldValue<Field>, ctor: { new(): T 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 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(); } |