diff options
author | tschicke-brown <tyler_schicke@brown.edu> | 2019-02-22 12:36:11 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-22 12:36:11 -0500 |
commit | 85d7a3d9aee44f0b199dddace92f7a683bc33eb6 (patch) | |
tree | 1128cba0e4532e1ca9ad5eba8c85cec1c74c2c5a | |
parent | 3f9e4363e6601eac175ff71192d414fd6051d921 (diff) | |
parent | f58c27d102ffbe44eea09ff6e3900292d273a021 (diff) |
Merge pull request #9 from browngraphicslab/collectionView
Added CollectionView
-rw-r--r-- | src/client/Server.ts | 5 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 81 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 5 | ||||
-rw-r--r-- | src/client/views/Main.tsx | 8 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 22 | ||||
-rw-r--r-- | src/client/views/collections/CollectionFreeFormView.tsx | 61 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.scss | 17 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 16 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 116 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewBase.tsx | 61 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 16 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 3 | ||||
-rw-r--r-- | src/fields/BasicField.ts | 6 | ||||
-rw-r--r-- | src/fields/Document.ts | 9 | ||||
-rw-r--r-- | src/fields/KeyStore.ts | 1 | ||||
-rw-r--r-- | src/fields/ListField.ts | 19 |
16 files changed, 263 insertions, 183 deletions
diff --git a/src/client/Server.ts b/src/client/Server.ts index 3e61729ab..06ac22c61 100644 --- a/src/client/Server.ts +++ b/src/client/Server.ts @@ -60,13 +60,16 @@ export class Server { }); } - public static GetDocumentField(doc: Document, key: Key) { + public static GetDocumentField(doc: Document, key: Key, callback?: (field: Field) => void) { let field = doc._proxies.get(key.Id); if (field) { this.GetField(field, action((fieldfromserver: Opt<Field>) => { if (fieldfromserver) { doc.fields.set(key.Id, { key, field: fieldfromserver }); + if (callback) { + callback(fieldfromserver); + } } })); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 920068273..bfa6cb7a9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -11,6 +11,7 @@ import { ImageField } from "../../fields/ImageField"; import { ImageBox } from "../views/nodes/ImageBox"; import { CollectionFreeFormView } from "../views/collections/CollectionFreeFormView"; import { FieldId } from "../../fields/Field"; +import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; interface DocumentOptions { x?: number; @@ -24,12 +25,10 @@ interface DocumentOptions { export namespace Documents { export function initProtos(callback: () => void) { - Server.GetFields([collectionProtoId, textProtoId, imageProtoId, schemaProtoId, dockProtoId], (fields) => { + Server.GetFields([collectionProtoId, textProtoId, imageProtoId], (fields) => { collectionProto = fields[collectionProtoId] as Document; imageProto = fields[imageProtoId] as Document; textProto = fields[textProtoId] as Document; - dockProto = fields[dockProtoId] as Document; - schemaProto = fields[schemaProtoId] as Document; callback() }); } @@ -83,52 +82,6 @@ export namespace Documents { return doc; } - let schemaProto: Document; - const schemaProtoId = "schemaProto"; - function GetSchemaPrototype(): Document { - if (!schemaProto) { - schemaProto = new Document(schemaProtoId); - schemaProto.Set(KeyStore.X, new NumberField(0)); - schemaProto.Set(KeyStore.Y, new NumberField(0)); - schemaProto.Set(KeyStore.Width, new NumberField(300)); - schemaProto.Set(KeyStore.Height, new NumberField(150)); - schemaProto.Set(KeyStore.Layout, new TextField(CollectionSchemaView.LayoutString())); - schemaProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); - } - return schemaProto; - } - - export function SchemaDocument(documents: Array<Document>, options: DocumentOptions = {}): Document { - let doc = GetSchemaPrototype().MakeDelegate(); - setupOptions(doc, options); - doc.Set(KeyStore.Data, new ListField(documents)); - return doc; - } - - - let dockProto: Document; - const dockProtoId = "dockProto"; - function GetDockPrototype(): Document { - if (!dockProto) { - dockProto = new Document(); - dockProto.Set(KeyStore.X, new NumberField(0)); - dockProto.Set(KeyStore.Y, new NumberField(0)); - dockProto.Set(KeyStore.Width, new NumberField(300)); - dockProto.Set(KeyStore.Height, new NumberField(150)); - dockProto.Set(KeyStore.Layout, new TextField(CollectionDockingView.LayoutString())); - dockProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); - } - return dockProto; - } - - export function DockDocument(config: string, options: DocumentOptions = {}, id?: string): Document { - let doc = GetDockPrototype().MakeDelegate(id); - setupOptions(doc, options); - doc.SetText(KeyStore.Data, config); - return doc; - } - - let imageProto: Document; const imageProtoId = "imageProto"; function GetImagePrototype(): Document { @@ -141,7 +94,8 @@ export namespace Documents { 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(CollectionFreeFormView.LayoutString("AnnotationsKey"))); + imageProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("AnnotationsKey"))); + imageProto.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) 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, KeyStore.Annotations])); @@ -165,23 +119,36 @@ export namespace Documents { function GetCollectionPrototype(): Document { if (!collectionProto) { collectionProto = new Document(collectionProtoId); - collectionProto.Set(KeyStore.X, new NumberField(0)); - collectionProto.Set(KeyStore.Y, new NumberField(0)); collectionProto.Set(KeyStore.Scale, new NumberField(1)); collectionProto.Set(KeyStore.PanX, new NumberField(0)); 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("DataKey"))); + collectionProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("DataKey"))); collectionProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); } return collectionProto; } - export function CollectionDocument(documents: Array<Document>, options: DocumentOptions = {}, id?: string): Document { + export function CollectionDocument(data: Array<Document> | string, viewType: CollectionViewType, options: DocumentOptions = {}, id?: string): Document { let doc = GetCollectionPrototype().MakeDelegate(id); setupOptions(doc, options); - doc.Set(KeyStore.Data, new ListField(documents)); + if (typeof data === "string") { + doc.SetText(KeyStore.Data, data); + } else { + doc.SetData(KeyStore.Data, data, ListField); + } + doc.SetNumber(KeyStore.ViewType, viewType); return doc; } + + export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string) { + return CollectionDocument(documents, CollectionViewType.Freeform, options, id) + } + + export function SchemaDocument(documents: Array<Document>, options: DocumentOptions, id?: string) { + return CollectionDocument(documents, CollectionViewType.Schema, options, id) + } + + export function DockDocument(config: string, options: DocumentOptions, id?: string) { + return CollectionDocument(config, CollectionViewType.Docking, options, id) + } }
\ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4e109d475..395a37ba5 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; import './DocumentDecorations.scss' import { KeyStore } from '../../fields/KeyStore' import { NumberField } from "../../fields/NumberField"; +import { number } from "prop-types"; @observer export class DocumentDecorations extends React.Component { @@ -142,6 +143,10 @@ export class DocumentDecorations extends React.Component { if (this.Hidden) { return (null); } + if (isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + console.log("DocumentDecorations: Bounds Error") + return (null); + } return ( <div id="documentDecorations-container" style={{ width: (bounds.r - bounds.x + 40) + "px", diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index c7a6a44e8..fe1a999ec 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -61,16 +61,14 @@ Documents.initProtos(() => { let mainfreeform: Document; if (res) { mainContainer = ServerUtils.FromJson(res) as Document; - mainfreeform = mainContainer.Get(KeyStore.ActiveFrame) as Document; - if (!mainfreeform) - Server.GetField(mainContainer._proxies.get(KeyStore.ActiveFrame.Id)!, (field) => mainfreeform = field as Document); + mainContainer.GetAsync(KeyStore.ActiveFrame, field => mainfreeform = field as Document); } else { mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId); Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainContainer.ToJson())) setTimeout(() => { - mainfreeform = Documents.CollectionDocument([], { x: 0, y: 400, title: "mini collection" }); + mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainfreeform.ToJson())); var docs = [mainfreeform].map(doc => CollectionDockingView.makeDocumentConfig(doc)); @@ -90,7 +88,7 @@ Documents.initProtos(() => { })); }) let addColNode = action(() => { - mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.CollectionDocument([], { + mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.FreeformDocument([], { x: 0, y: 300, width: 200, height: 200, title: "added note" })); }) diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 60dc24b5f..752007439 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import { Document } from "../../../fields/Document"; -import { FieldId, Opt } from "../../../fields/Field"; +import { FieldId, Opt, Field } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { Utils } from "../../../Utils"; import { Server } from "../../Server"; @@ -14,13 +14,12 @@ import { DragManager } from "../../util/DragManager"; import { undoBatch } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; -import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { CollectionViewProps, COLLECTION_BORDER_WIDTH, SubCollectionViewProps } from "./CollectionView"; import React = require("react"); @observer -export class CollectionDockingView extends CollectionViewBase { +export class CollectionDockingView extends React.Component<SubCollectionViewProps> { public static Instance: CollectionDockingView; - public static LayoutString() { return CollectionViewBase.LayoutString("CollectionDockingView"); } public static makeDocumentConfig(document: Document) { return { type: 'react-component', @@ -41,7 +40,7 @@ export class CollectionDockingView extends CollectionViewBase { private _containerRef = React.createRef<HTMLDivElement>(); private _fullScreen: any = null; - constructor(props: CollectionViewProps) { + constructor(props: SubCollectionViewProps) { super(props); CollectionDockingView.Instance = this; (window as any).React = React; @@ -149,6 +148,13 @@ export class CollectionDockingView extends CollectionViewBase { this._goldenLayout.on('stackCreated', this.stackCreated); this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer); this._goldenLayout.container = this._containerRef.current; + if (this._goldenLayout.config.maximisedItemId === '__glMaximised') { + try { + this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise(); + } catch (e) { + this._goldenLayout.config.maximisedItemId = null; + } + } this._goldenLayout.init(); } } @@ -187,7 +193,7 @@ export class CollectionDockingView extends CollectionViewBase { } @action onPointerDown = (e: React.PointerEvent): void => { - if (e.button === 2 && this.active) { + if (e.button === 2 && this.props.active()) { e.stopPropagation(); e.preventDefault(); } else { @@ -195,7 +201,7 @@ export class CollectionDockingView extends CollectionViewBase { if (className == "lm_drag_handle" || className == "lm_close" || className == "lm_maximise" || className == "lm_minimise" || className == "lm_close_tab") { this._flush = true; } - if (e.buttons === 1 && this.active) { + if (e.buttons === 1 && this.props.active()) { e.stopPropagation(); } } @@ -262,7 +268,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { constructor(props: any) { super(props); - Server.GetField(this.props.documentId, f => this._document = f as Document) + Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document)); } @computed private get _nativeWidth() { return this._document!.GetNumber(KeyStore.NativeWidth, 0); } diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 986bcdcee..b78b1a3b6 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -1,10 +1,10 @@ import { observer } from "mobx-react"; import React = require("react"); -import { action, computed } from "mobx"; +import { action, computed, trace } from "mobx"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DragManager } from "../../util/DragManager"; import "./CollectionFreeFormView.scss"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH, CollectionViewProps } from "./CollectionViewBase"; +import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView"; import { KeyStore } from "../../../fields/KeyStore"; import { Document } from "../../../fields/Document"; import { ListField } from "../../../fields/ListField"; @@ -14,10 +14,10 @@ import { FieldWaiting } from "../../../fields/Field"; import { Transform } from "../../util/Transform"; import { DocumentView } from "../nodes/DocumentView"; import { undoBatch } from "../../util/UndoManager"; +import { jSXElement } from "babel-types"; @observer -export class CollectionFreeFormView extends CollectionViewBase { - public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionFreeFormView", fieldKey); } +export class CollectionFreeFormView extends React.Component<SubCollectionViewProps> { private _canvasRef = React.createRef<HTMLDivElement>(); private _lastX: number = 0; private _lastY: number = 0; @@ -38,7 +38,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @computed get resizeScaling() { return this.isAnnotationOverlay ? this.props.Document.GetNumber(KeyStore.Width, 0) / this.nativeWidth : 1; } - constructor(props: CollectionViewProps) { + constructor(props: SubCollectionViewProps) { super(props); } @@ -46,9 +46,11 @@ export class CollectionFreeFormView extends CollectionViewBase { @action drop = (e: Event, de: DragManager.DropEvent) => { const doc: DocumentView = de.data["document"]; - if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) { - doc.props.ContainingCollectionView.removeDocument(doc.props.Document); - this.addDocument(doc.props.Document); + if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this.props.CollectionView) { + if (doc.props.RemoveDocument) { + doc.props.RemoveDocument(doc.props.Document); + } + this.props.addDocument(doc.props.Document); } const xOffset = de.data["xOffset"] as number || 0; const yOffset = de.data["yOffset"] as number || 0; @@ -79,7 +81,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @action onPointerDown = (e: React.PointerEvent): void => { - if ((e.button === 2 && this.active) || + if ((e.button === 2 && this.props.active()) || !e.defaultPrevented) { document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); @@ -104,7 +106,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @action onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && this.active) { + if (!e.cancelBubble && this.props.active()) { e.preventDefault(); e.stopPropagation(); let x = this.props.Document.GetNumber(KeyStore.PanX, 0); @@ -220,16 +222,28 @@ export class CollectionFreeFormView extends CollectionViewBase { return Transform.Identity.translate(-x, -y).scale(1 / this.scale); } - render() { + @computed + get views() { const { fieldKey, Document } = this.props; - // const value: Document[] = Document.GetList<Document>(fieldKey, []); const lvalue = Document.GetT<ListField<Document>>(fieldKey, ListField); - if (!lvalue || lvalue === "<Waiting>") { - return <p>Error loading collection data</p> + if (lvalue && lvalue != FieldWaiting) { + return lvalue.Data.map(doc => { + return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc} + AddDocument={this.props.addDocument} + RemoveDocument={this.props.removeDocument} + ScreenToLocalTransform={this.getTransform} + isTopMost={false} + Scaling={1} + PanelSize={[doc.GetNumber(KeyStore.Width, 0), doc.GetNumber(KeyStore.Height, 0)]} + ContainingCollectionView={this.props.CollectionView} />); + }) } - const panx: number = Document.GetNumber(KeyStore.PanX, 0); - const pany: number = Document.GetNumber(KeyStore.PanY, 0); + return null; + } + render() { + const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0); + const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0); return ( <div className="collectionfreeformview-container" onPointerDown={this.onPointerDown} @@ -237,25 +251,14 @@ export class CollectionFreeFormView extends CollectionViewBase { onContextMenu={(e) => e.preventDefault()} onDrop={this.onDrop} onDragOver={this.onDragOver} - style={{ - borderWidth: `${COLLECTION_BORDER_WIDTH}px`, - }} + style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }} ref={this.createDropTarget}> <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 ? this.props.BackgroundView() : null} - {lvalue.Data.map(doc => { - return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc} - AddDocument={this.addDocument} - RemoveDocument={this.removeDocument} - ScreenToLocalTransform={this.getTransform} - isTopMost={false} - Scaling={1} - PanelSize={[doc.GetNumber(KeyStore.Width, 0), doc.GetNumber(KeyStore.Height, 0)]} - ContainingCollectionView={this} />); - })} + {this.views} </div> </div> ); diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index fb2729a92..ba9afee62 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -26,16 +26,29 @@ } .ReactTable { position: absolute; - display: inline-block; + // display: inline-block; + // overflow: auto; width: 100%; - overflow: auto; height: 100%; background: white; box-sizing: border-box; } .ReactTable .rt-table { overflow-y: auto; + overflow-x: auto; height: 100%; + + display: -webkit-inline-box; + direction: ltr; + // direction:rtl; + // display:block; + } + .ReactTable .rt-tbody { + //direction: ltr; + direction: rtl; + } + .ReactTable .rt-tr-group { + direction: ltr; } .ReactTable .rt-thead.-header { background:grey; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 5ec288b13..f25e721c0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -1,5 +1,5 @@ import React = require("react") -import { action, observable } from "mobx"; +import { action, observable, trace } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; @@ -13,12 +13,10 @@ import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView"; @observer -export class CollectionSchemaView extends CollectionViewBase { - public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionSchemaView", fieldKey); } - +export class CollectionSchemaView extends React.Component<SubCollectionViewProps> { private _mainCont = React.createRef<HTMLDivElement>(); private DIVIDER_WIDTH = 5; @@ -34,6 +32,7 @@ export class CollectionSchemaView extends CollectionViewBase { doc: rowProps.value[0], fieldKey: rowProps.value[1], isSelected: () => false, + select: () => { }, isTopMost: false } let contents = ( @@ -112,7 +111,7 @@ export class CollectionSchemaView extends CollectionViewBase { // e.preventDefault(); // } else { - if (e.buttons === 1 && this.active) { + if (e.buttons === 1 && this.props.active()) { e.stopPropagation(); } } @@ -139,18 +138,17 @@ export class CollectionSchemaView extends CollectionViewBase { const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author]) const children = this.props.Document.GetList<Document>(this.props.fieldKey, []); const selected = children.length > this.selectedIndex ? children[this.selectedIndex] : undefined; - let me = this; let content = this.selectedIndex == -1 || !selected ? (null) : ( <Measure onResize={this.setScaling}> {({ measureRef }) => <div ref={measureRef}> <DocumentView Document={selected} - AddDocument={this.addDocument} RemoveDocument={this.removeDocument} + AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument} ScreenToLocalTransform={this.getTransform} Scaling={this._parentScaling} isTopMost={false} PanelSize={[this._panelWidth, this._panelHeight]} - ContainingCollectionView={me} /> + ContainingCollectionView={this.props.CollectionView} /> </div> } </Measure> diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx new file mode 100644 index 000000000..651d85879 --- /dev/null +++ b/src/client/views/collections/CollectionView.tsx @@ -0,0 +1,116 @@ +import { action, computed } from "mobx"; +import { observer } from "mobx-react"; +import { Document } from "../../../fields/Document"; +import { Key } from "../../../fields/Key"; +import { ListField } from "../../../fields/ListField"; +import { SelectionManager } from "../../util/SelectionManager"; +import { ContextMenu } from "../ContextMenu"; +import React = require("react"); +import { Transform } from "../../util/Transform"; +import { KeyStore } from "../../../fields/KeyStore"; +import { NumberField } from "../../../fields/NumberField"; +import { CollectionFreeFormView } from "./CollectionFreeFormView"; +import { CollectionDockingView } from "./CollectionDockingView"; +import { CollectionSchemaView } from "./CollectionSchemaView"; +import { Opt } from "../../../fields/Field"; + + +export interface CollectionViewProps { + fieldKey: Key; + Document: Document; + ScreenToLocalTransform: () => Transform; + isSelected: () => boolean; + isTopMost: boolean; + select: (ctrlPressed: boolean) => void; + BackgroundView?: () => JSX.Element; +} + +export interface SubCollectionViewProps extends CollectionViewProps { + active: () => boolean; + addDocument: (doc: Document) => void; + removeDocument: (doc: Document) => boolean; + CollectionView: Opt<CollectionView>; +} + +export enum CollectionViewType { + Invalid, + Freeform, + Schema, + Docking, +} + +export const COLLECTION_BORDER_WIDTH = 2; + +@observer +export class CollectionView extends React.Component<CollectionViewProps> { + + public static LayoutString(fieldKey: string = "DataKey") { + return `<CollectionView Document={Document} + ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select} + isTopMost={isTopMost} BackgroundView={BackgroundView} />`; + } + public active = () => { + var isSelected = this.props.isSelected(); + var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); + var topMost = this.props.isTopMost; + return isSelected || childSelected || topMost; + } + @action + addDocument = (doc: Document): void => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>()) + value.push(doc); + } + + @action + removeDocument = (doc: Document): boolean => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>()) + let index = value.indexOf(doc); + if (index !== -1) { + value.splice(index, 1) + + SelectionManager.DeselectAll() + ContextMenu.Instance.clearItems() + return true; + } + return false + } + + get collectionViewType(): CollectionViewType { + let Document = this.props.Document; + let viewField = Document.GetT(KeyStore.ViewType, NumberField); + if (viewField === "<Waiting>") { + return CollectionViewType.Invalid; + } else if (viewField) { + return viewField.Data; + } else { + return CollectionViewType.Freeform; + } + } + + set collectionViewType(type: CollectionViewType) { + let Document = this.props.Document; + Document.SetData(KeyStore.ViewType, type, NumberField); + } + + render() { + let viewType = this.collectionViewType; + switch (viewType) { + case CollectionViewType.Freeform: + return (<CollectionFreeFormView {...this.props} + addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active} + CollectionView={this} />) + case CollectionViewType.Schema: + return (<CollectionSchemaView {...this.props} + addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active} + CollectionView={this} />) + case CollectionViewType.Docking: + return (<CollectionDockingView {...this.props} + addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active} + CollectionView={this} />) + default: + return <div></div> + } + } +}
\ No newline at end of file diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx deleted file mode 100644 index 0acc890d8..000000000 --- a/src/client/views/collections/CollectionViewBase.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { action, computed } from "mobx"; -import { observer } from "mobx-react"; -import { Document } from "../../../fields/Document"; -import { Key } from "../../../fields/Key"; -import { ListField } from "../../../fields/ListField"; -import { SelectionManager } from "../../util/SelectionManager"; -import { ContextMenu } from "../ContextMenu"; -import React = require("react"); -import { Transform } from "../../util/Transform"; - - -export interface CollectionViewProps { - fieldKey: Key; - Document: Document; - ScreenToLocalTransform: () => Transform; - isSelected: () => boolean; - isTopMost: boolean; - select: (ctrlPressed: boolean) => void; - BackgroundView?: () => JSX.Element; -} - -export const COLLECTION_BORDER_WIDTH = 2; - -@observer -export class CollectionViewBase extends React.Component<CollectionViewProps> { - - public static LayoutString(collectionType: string, fieldKey: string = "DataKey") { - return `<${collectionType} Document={Document} - ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select} - isTopMost={isTopMost} BackgroundView={BackgroundView} />`; - } - @computed - public get active(): boolean { - var isSelected = this.props.isSelected(); - var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); - var topMost = this.props.isTopMost; - return isSelected || childSelected || topMost; - } - @action - addDocument = (doc: Document): void => { - //TODO This won't create the field if it doesn't already exist - const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>()) - value.push(doc); - } - - @action - removeDocument = (doc: Document): boolean => { - //TODO This won't create the field if it doesn't already exist - const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>()) - 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/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7cf00a116..69e413c6f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -9,7 +9,7 @@ import { Utils } from "../../../Utils"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "../collections/CollectionViewBase"; +import { COLLECTION_BORDER_WIDTH, CollectionView, CollectionViewType } from "../collections/CollectionView"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; import "./DocumentView.scss"; @@ -22,7 +22,7 @@ import { TextField } from "../../../fields/TextField"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? export interface DocumentViewProps { - ContainingCollectionView: Opt<CollectionViewBase>; + ContainingCollectionView: Opt<CollectionView>; Document: Document; AddDocument?: (doc: Document) => void; @@ -114,7 +114,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || - this.props.ContainingCollectionView.active; + this.props.ContainingCollectionView.active(); } private _contextMenuCanOpen = false; @@ -211,10 +211,11 @@ export class DocumentView extends React.Component<DocumentViewProps> { @action onContextMenu = (e: React.MouseEvent): void => { + e.preventDefault() + e.stopPropagation(); if (!SelectionManager.IsSelected(this)) { return; } - e.preventDefault() if (!this._contextMenuCanOpen) { return; @@ -233,6 +234,9 @@ export class DocumentView extends React.Component<DocumentViewProps> { ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight }) ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) + ContextMenu.Instance.addItem({ description: "Freeform", event: () => { this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) } }) + ContextMenu.Instance.addItem({ description: "Schema", event: () => { this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) } }) + ContextMenu.Instance.addItem({ description: "Docking", event: () => { this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) } }) ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) SelectionManager.SelectDoc(this, e.ctrlKey); } @@ -275,7 +279,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { let backgroundLayout = this.backgroundLayout; if (backgroundLayout) { let backgroundView = () => (<JsxParser - components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }} bindings={documentBindings} jsx={this.backgroundLayout} showWarnings={true} @@ -294,7 +298,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} > <JsxParser - components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }} bindings={documentBindings} jsx={this.layout} showWarnings={true} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 918acff4c..821172d71 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -20,12 +20,13 @@ export interface FieldViewProps { fieldKey: Key; doc: Document; isSelected: () => boolean; + select: () => void; isTopMost: boolean; } @observer export class FieldView extends React.Component<FieldViewProps> { - public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} isTopMost={isTopMost} />`; } + public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; } @computed get field(): FieldValue<Field> { const { doc, fieldKey } = this.props; diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts index 91977b243..a92c4a236 100644 --- a/src/fields/BasicField.ts +++ b/src/fields/BasicField.ts @@ -32,7 +32,7 @@ export abstract class BasicField<T> extends Field { return; } let oldValue = this.data; - this.data = value; + this.setData(value); UndoManager.AddEvent({ undo: () => this.Data = oldValue, redo: () => this.Data = value @@ -40,6 +40,10 @@ export abstract class BasicField<T> extends Field { Server.UpdateField(this); } + protected setData(value: T) { + this.data = value; + } + @action TrySetValue(value: any): boolean { if (typeof value == typeof this.data) { diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 6667485b6..ff13732b3 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -90,6 +90,15 @@ export class Document extends Field { return field; } + GetAsync(key: Key, callback: (field: Field) => void): boolean { + //This currently doesn't deal with prototypes + if (this._proxies.has(key.Id)) { + Server.GetDocumentField(this, key, callback); + return true; + } + return false; + } + 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) { diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 7056886aa..6d6c6a546 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -16,6 +16,7 @@ export namespace KeyStore { export const ZIndex = new Key("ZIndex"); export const Data = new Key("Data"); export const Annotations = new Key("Annotations"); + export const ViewType = new Key("ViewType"); export const Layout = new Key("Layout"); export const BackgroundLayout = new Key("BackgroundLayout"); export const LayoutKeys = new Key("LayoutKeys"); diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts index 75c2eb343..700600804 100644 --- a/src/fields/ListField.ts +++ b/src/fields/ListField.ts @@ -1,4 +1,4 @@ -import { action, IArrayChange, IArraySplice, IObservableArray, observe } from "mobx"; +import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx"; import { Server } from "../client/Server"; import { UndoManager } from "../client/util/UndoManager"; import { Types } from "../server/Message"; @@ -13,7 +13,12 @@ export class ListField<T extends Field> extends BasicField<T[]> { if (save) { Server.UpdateField(this); } - observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => { + this.observeList(); + } + + private observeDisposer: Lambda | undefined; + private observeList(): void { + this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => { this.updateProxies() if (change.type == "splice") { UndoManager.AddEvent({ @@ -27,7 +32,15 @@ export class ListField<T extends Field> extends BasicField<T[]> { }) } Server.UpdateField(this); - }) + }); + } + + protected setData(value: T[]) { + if (this.observeDisposer) { + this.observeDisposer() + } + this.data = observable(value); + this.observeList(); } private updateProxies() { |