diff options
| author | tschicke-brown <tyler_schicke@brown.edu> | 2019-02-19 21:58:18 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-19 21:58:18 -0500 |
| commit | dae1c5a1ac8b141ea26711db2977faa0be655137 (patch) | |
| tree | 6fd1cfe02eaeaf1dc074a26cc4cde39c3313d1f3 /src/client/views/collections | |
| parent | 02db8b435396f3b6ea605ce327828f02b41080ef (diff) | |
| parent | 6bd1ed0fc3d070343ecd72de13383f9ad8b1e250 (diff) | |
Merge pull request #8 from browngraphicslab/server_database_merge
Server database merge
Diffstat (limited to 'src/client/views/collections')
4 files changed, 105 insertions, 93 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 885a4bece..ceb638ab4 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,21 +1,23 @@ import * as GoldenLayout from "golden-layout"; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, computed, reaction, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Document } from "../../../fields/Document"; -import { KeyStore } from "../../../fields/Key"; -import { ListField } from "../../../fields/ListField"; +import { action, computed, observable, reaction, trace } from "mobx"; import { DragManager } from "../../util/DragManager"; import { DocumentView } from "../nodes/DocumentView"; +import { Document } from "../../../fields/Document"; import "./CollectionDockingView.scss"; -import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { CollectionViewBase, COLLECTION_BORDER_WIDTH, CollectionViewProps } from "./CollectionViewBase"; import React = require("react"); import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import { Utils } from "../../../Utils"; -import { FieldId } from "../../../fields/Field"; +import { FieldId, FieldWaiting, Field } from "../../../fields/Field"; import { Server } from "../../Server"; +import { observer } from "mobx-react"; +import { ListField } from "../../../fields/ListField"; +import { KeyStore } from "../../../fields/KeyStore"; +import { Opt } from "../../../fields/Field"; +import { TextField } from "../../../fields/TextField"; @observer export class CollectionDockingView extends CollectionViewBase { @@ -41,6 +43,7 @@ export class CollectionDockingView extends CollectionViewBase { private _containerRef = React.createRef<HTMLDivElement>(); private _makeFullScreen: boolean = false; private _maximizedStack: any = null; + private _forceRecreate: boolean = false; constructor(props: CollectionViewProps) { super(props); @@ -91,16 +94,10 @@ export class CollectionDockingView extends CollectionViewBase { type: 'stack', content: [CollectionDockingView.makeDocumentConfig(document)] }; - var newContentItem = new this._goldenLayout._typeToItem[newItemStackConfig.type](this._goldenLayout, newItemStackConfig, parent); + var newContentItem = new this._goldenLayout._typeToItem[newItemStackConfig.type](this._goldenLayout, newItemStackConfig, null); if (this._goldenLayout.root.contentItems[0].isRow) { - var rowlayout = this._goldenLayout.root.contentItems[0]; - var lastRowItem = rowlayout.contentItems[rowlayout.contentItems.length - 1]; - - lastRowItem.config["width"] *= 0.5; - newContentItem.config["width"] = lastRowItem.config["width"]; - rowlayout.addChild(newContentItem, rowlayout.contentItems.length, true); - rowlayout.callDownwards('setSize'); + this._goldenLayout.root.contentItems[0].addChild(newContentItem); } else { var collayout = this._goldenLayout.root.contentItems[0]; @@ -112,19 +109,35 @@ export class CollectionDockingView extends CollectionViewBase { collayout.config["width"] = 50; newContentItem.config["width"] = 50; - collayout.parent.callDownwards('setSize'); } + this.stateChanged(); // shouldn't need to do either of these ... but our react component doesn't render when we add it manually like this. + this.setupGoldenLayout(true); // this forces it to render by the brute force method of recreating the whole golden layout } - componentDidMount: () => void = () => { - if (this._containerRef.current) { - - this._goldenLayout = new GoldenLayout(JSON.parse(this.props.Document.GetText(KeyStore.Data, ""))); + setupGoldenLayout(force: boolean = false) { + var config = this.props.Document.GetText(KeyStore.Data, ""); + if (config) { + if (!this._goldenLayout) + this._goldenLayout = new GoldenLayout(JSON.parse(config)); + else { + if (!force && JSON.stringify(this._goldenLayout.toConfig()) == JSON.stringify(JSON.parse(config))) + return; + this._goldenLayout.destroy(); + this._goldenLayout = new GoldenLayout(JSON.parse(config)); + } this._goldenLayout.on('tabCreated', this.tabCreated); this._goldenLayout.on('stackCreated', this.stackCreated); this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer); this._goldenLayout.container = this._containerRef.current; this._goldenLayout.init(); + this._goldenLayout.on('stateChanged', this.stateChanged); + } + } + componentDidMount: () => void = () => { + if (this._containerRef.current) { + reaction( + () => this.props.Document.GetText(KeyStore.Data, ""), + () => this.setupGoldenLayout(), { fireImmediately: true }); window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window } @@ -134,7 +147,7 @@ export class CollectionDockingView extends CollectionViewBase { } @action onResize = (event: any) => { - var cur = this.props.ContainingDocumentView!.MainContent.current; + var cur = this._containerRef.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 this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); @@ -152,21 +165,26 @@ export class CollectionDockingView extends CollectionViewBase { } } + stateChanged = () => { + // if (!this._pointerIsDown) { + var json = JSON.stringify(this._goldenLayout.toConfig()); + this.props.Document.SetText(KeyStore.Data, json) + //} + } + tabCreated = (tab: any) => { - { - if (this._dragDiv) { - this._dragDiv.removeChild(this._dragElement); - this._dragParent!.removeChild(this._dragFakeElement!); - this._dragParent!.appendChild(this._dragElement!); - DragManager.Root().removeChild(this._dragDiv); - this._dragDiv = null; - } - //tab.setTitle(tab.contentItem.config.componentState.title); - tab.closeElement.off('click') //unbind the current click handler - .click(function () { - tab.contentItem.remove(); - }); + if (this._dragDiv) { + this._dragDiv.removeChild(this._dragElement); + this._dragParent!.removeChild(this._dragFakeElement!); + this._dragParent!.appendChild(this._dragElement!); + DragManager.Root().removeChild(this._dragDiv); + this._dragDiv = null; + this.stateChanged(); } + tab.closeElement.off('click') //unbind the current click handler + .click(function () { + tab.contentItem.remove(); + }); } stackCreated = (stack: any) => { @@ -187,13 +205,8 @@ export class CollectionDockingView extends CollectionViewBase { render() { - const { fieldKey: fieldKey, Document: 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. - // tfs: we should be able to use this.props.ScreenToLocalTransform to get s right? - var s = this.props.ContainingDocumentView != undefined ? this.props.ContainingDocumentView!.ScalingToScreenSpace : 1; - var w = Document.GetNumber(KeyStore.Width, 0) / s; - var h = Document.GetNumber(KeyStore.Height, 0) / s; + this.props.Document.GetNumber(KeyStore.Width, 0); // bcz: needed to force render when window size changes + this.props.Document.GetNumber(KeyStore.Height, 0); return ( <div className="collectiondockingview-container" id="menuContainer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef} @@ -213,28 +226,35 @@ interface DockedFrameProps { @observer export class DockedFrameRenderer extends React.Component<DockedFrameProps> { - private _mainCont = React.createRef<HTMLDivElement>(); + _mainCont: any = null; constructor(props: any) { super(props); + Server.GetField(this.props.documentId, (f) => { this.Document = f as Document; }) + } + + setMainCont: any = (element: any) => { + this._mainCont = element; } @observable private _parentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ParentScaling prop of the DocumentView - @computed - private get Document() { return Server.GetField(this.props.documentId, () => { }) as Document } + @observable + private Document: Opt<Document>; render() { + if (!this.Document) + return <div></div> let nativeWidth = this.Document.GetNumber(KeyStore.NativeWidth, 0); var layout = this.Document.GetText(KeyStore.Layout, ""); var content = - <div ref={this._mainCont}> + <div ref={this.setMainCont}> <DocumentView key={this.Document.Id} Document={this.Document} AddDocument={undefined} RemoveDocument={undefined} Scaling={this._parentScaling} ScreenToLocalTransform={() => { - let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!); + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont); var props = CollectionDockingView.Instance.props; return props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._parentScaling) }} diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 2c0a3f478..54757cce5 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -1,13 +1,11 @@ import { observer } from "mobx-react"; import React = require("react"); -import { action, observable, computed } from "mobx"; +import { action, 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 { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { KeyStore } from "../../../fields/KeyStore"; import { Document } from "../../../fields/Document"; import { ListField } from "../../../fields/ListField"; import { NumberField } from "../../../fields/NumberField"; @@ -19,17 +17,12 @@ import { DocumentView } from "../nodes/DocumentView"; @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.fieldKey == KeyStore.Annotations; } @@ -47,7 +40,6 @@ export class CollectionFreeFormView extends CollectionViewBase { @action drop = (e: Event, de: DragManager.DropEvent) => { const doc: DocumentView = de.data["document"]; - var me = this; if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) { doc.props.ContainingCollectionView.removeDocument(doc.props.Document); this.addDocument(doc.props.Document); @@ -55,7 +47,7 @@ export class CollectionFreeFormView extends CollectionViewBase { const xOffset = de.data["xOffset"] as number || 0; const yOffset = de.data["yOffset"] as number || 0; //this should be able to use translate and scale methods on an Identity transform, no? - const transform = me.getTransform(); + const transform = this.getTransform(); const screenX = de.x - xOffset; const screenY = de.y - yOffset; const [x, y] = transform.transformPoint(screenX, screenY); @@ -65,9 +57,13 @@ export class CollectionFreeFormView extends CollectionViewBase { e.stopPropagation(); } - componentDidMount() { - if (this._containerRef.current) { - DragManager.MakeDropTarget(this._containerRef.current, { + private dropDisposer?: DragManager.DragDropDisposer; + createDropTarget = (ele: HTMLDivElement) => { + if (this.dropDisposer) { + this.dropDisposer(); + } + if (ele) { + this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop } @@ -119,7 +115,6 @@ export class CollectionFreeFormView extends CollectionViewBase { 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?? @@ -155,7 +150,7 @@ export class CollectionFreeFormView extends CollectionViewBase { let x = e.pageX - panx let y = e.pageY - pany - fReader.addEventListener("load", action("drop", (event) => { + fReader.addEventListener("load", action("drop", () => { if (fReader.result) { let url = "" + fReader.result; let doc = Documents.ImageDocument(url, { @@ -177,25 +172,25 @@ export class CollectionFreeFormView extends CollectionViewBase { } } - onDragOver = (e: React.DragEvent): void => { + onDragOver = (): void => { } @action bringToFront(doc: DocumentView) { const { fieldKey: fieldKey, Document: 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); + const value: Document[] = Document.GetList<Document>(fieldKey, []).slice(); + value.sort((doc1, doc2) => { + if (doc1 === doc.props.Document) { + return 1; } - }) - - if (doc.props.Document.GetNumber(KeyStore.ZIndex, 0) != 0) { - doc.props.Document.SetData(KeyStore.ZIndex, 0, NumberField); - } + if (doc2 === doc.props.Document) { + return -1; + } + return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0); + }).map((doc, index) => { + doc.SetNumber(KeyStore.ZIndex, index + 1) + }); } @computed @@ -220,11 +215,14 @@ export class CollectionFreeFormView extends CollectionViewBase { } render() { - const Document: Document = this.props.Document; - const value: Document[] = Document.GetList<Document>(this.props.fieldKey, []); + 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> + } const panx: number = Document.GetNumber(KeyStore.PanX, 0); const pany: number = Document.GetNumber(KeyStore.PanY, 0); - var me = this; return ( <div className="collectionfreeformview-container" @@ -236,13 +234,13 @@ export class CollectionFreeFormView extends CollectionViewBase { style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }} - ref={this._containerRef}> + 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} - {value.map(doc => { + {lvalue.Data.map(doc => { return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc} AddDocument={this.addDocument} RemoveDocument={this.removeDocument} diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 5c95aca99..719783fd7 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -11,7 +11,7 @@ import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBas import { DocumentView } from "../nodes/DocumentView"; import { EditableView } from "../EditableView"; import { CompileScript, ToField } from "../../util/Scripting"; -import { KeyStore as KS, Key, KeyStore } from "../../../fields/Key"; +import { KeyStore } from "../../../fields/KeyStore"; import { Document } from "../../../fields/Document"; import { Field } from "../../../fields/Field"; import { Transform } from "../../util/Transform"; @@ -19,7 +19,7 @@ import Measure from "react-measure"; @observer export class CollectionSchemaView extends CollectionViewBase { - public static LayoutString() { return CollectionViewBase.LayoutString("CollectionSchemaView"); } + public static LayoutString() { return FieldView.LayoutString(CollectionSchemaView); } @observable selectedIndex = 0; @@ -106,8 +106,8 @@ export class CollectionSchemaView extends CollectionViewBase { render() { const { Document: Document, fieldKey: fieldKey } = this.props; const children = Document.GetList<Document>(fieldKey, []); - const columns = Document.GetList(KS.ColumnsKey, - [KS.Title, KS.Data, KS.Author]) + const columns = Document.GetList(KeyStore.ColumnsKey, + [KeyStore.Title, KeyStore.Data, KeyStore.Author]) let content; var me = this; if (this.selectedIndex != -1) { diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 1e20da8d2..0acc890d8 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -1,22 +1,17 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; -import { Opt } from "../../../fields/Field"; -import { Key, KeyStore } from "../../../fields/Key"; +import { Key } 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 { fieldKey: Key; Document: Document; - ContainingDocumentView: Opt<DocumentView>; ScreenToLocalTransform: () => Transform; isSelected: () => boolean; isTopMost: boolean; @@ -32,12 +27,11 @@ 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} - ContainingDocumentView={DocumentView} BackgroundView={BackgroundView} />`; + isTopMost={isTopMost} BackgroundView={BackgroundView} />`; } @computed public get active(): boolean { - var isSelected = (this.props.ContainingDocumentView && SelectionManager.IsSelected(this.props.ContainingDocumentView)); + var isSelected = this.props.isSelected(); var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); var topMost = this.props.isTopMost; return isSelected || childSelected || topMost; |
