diff options
| author | Tyler Schicke <tyler_schicke@brown.edu> | 2019-02-19 06:21:26 -0500 |
|---|---|---|
| committer | Tyler Schicke <tyler_schicke@brown.edu> | 2019-02-19 06:21:26 -0500 |
| commit | 176ac4c965eec55331fda8c04e08517088f017e7 (patch) | |
| tree | d8c5ee1651fb0a1104d9636860c9417e5e306fa2 /src/client/views/collections | |
| parent | f9d55f59c9db0f9ea7e98729f9e0892a828ee3f6 (diff) | |
| parent | 963f137f10e6f08cc449b181c920069dc7cfd394 (diff) | |
Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web into server_database_merge
Diffstat (limited to 'src/client/views/collections')
6 files changed, 356 insertions, 164 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index d9e261f55..22c2f3172 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,20 +1,24 @@ import { observer } from "mobx-react"; import { KeyStore } from "../../../fields/KeyStore"; -import React = require("react"); import FlexLayout from "flexlayout-react"; -import { action, computed } from "mobx"; import { Document } from "../../../fields/Document"; import { DocumentView } from "../nodes/DocumentView"; import { ListField } from "../../../fields/ListField"; import { NumberField } from "../../../fields/NumberField"; 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 * as ReactDOM from 'react-dom'; +import { action, computed, observable } from "mobx"; import { DragManager } from "../../util/DragManager"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; import { FieldView } from "../nodes/FieldView"; +import { Transform } from "../../util/Transform"; +import "./CollectionDockingView.scss"; +import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import React = require("react"); +import * as ReactDOM from 'react-dom'; +import Measure from "react-measure"; +import { Utils } from "../../../Utils"; @observer export class CollectionDockingView extends CollectionViewBase { @@ -24,7 +28,7 @@ export class CollectionDockingView extends CollectionViewBase { private _containerRef = React.createRef<HTMLDivElement>(); @computed private get modelForFlexLayout() { - const { fieldKey: fieldKey, doc: Document } = this.props; + const { fieldKey: fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetData(fieldKey, ListField, []); var docs = value.map(doc => { return { type: 'tabset', weight: 50, selected: 0, children: [{ type: "tab", name: doc.Title, component: doc.Id }] }; @@ -39,11 +43,11 @@ export class CollectionDockingView extends CollectionViewBase { }); } @computed - private get modelForGoldenLayout(): any { - const { fieldKey: fieldKey, doc: Document } = this.props; + private get modelForGoldenLayout(): GoldenLayout { + const { fieldKey, 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: { @@ -63,10 +67,9 @@ export class CollectionDockingView extends CollectionViewBase { } private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })(); - @action onResize = () => { - var cur = this.props.DocumentViewForField!.MainContent.current; + 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 CollectionDockingView.myLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); @@ -89,11 +92,16 @@ export class CollectionDockingView extends CollectionViewBase { if (component === "button") { return <button>{node.getName()}</button>; } - const { fieldKey: fieldKey, doc: Document } = this.props; + const { fieldKey, Document } = this.props; 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} + ScreenToLocalTransform={() => Transform.Identity} + isTopMost={true} + Scaling={1} + ContainingCollectionView={this} />); } } if (component === "text") { @@ -111,7 +119,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; @@ -152,7 +160,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 // @@ -232,13 +239,19 @@ 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) - ); - if (CollectionDockingView.myLayout._maxstack != null) { - CollectionDockingView.myLayout._maxstack.click(); + let divContainer = document.getElementById(containingDiv) as HTMLDivElement; + if (divContainer) { + let props: DockingProps = { + ContainingDiv: containingDiv, + Document: state.doc, + Container: container, + CollectionDockingView: me, + HtmlElement: divContainer, + } + ReactDOM.render((<RenderClass {...props} />), divContainer); + if (CollectionDockingView.myLayout._maxstack) { + CollectionDockingView.myLayout._maxstack.click(); + } } }, 0); }); @@ -248,11 +261,13 @@ export class CollectionDockingView extends CollectionViewBase { render() { - const { fieldKey: fieldKey, doc: Document } = this.props; + 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. - var s = this.props.DocumentViewForField != undefined ? this.props.DocumentViewForField!.ScalingToScreenSpace : 1; - var w = Document.GetData(KeyStore.Width, NumberField, Number(0)) / s; - var h = Document.GetData(KeyStore.Height, NumberField, Number(0)) / s; + // 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; var chooseLayout = () => { if (!CollectionDockingView.UseGoldenLayout) @@ -260,18 +275,54 @@ export class CollectionDockingView extends CollectionViewBase { } return ( - <div className="border" style={{ - borderStyle: "solid", - borderWidth: `${COLLECTION_BORDER_WIDTH}px`, - }}> - <div className="collectiondockingview-container" id="menuContainer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef} - style={{ - width: CollectionDockingView.UseGoldenLayout || s > 1 ? "100%" : w - 2 * COLLECTION_BORDER_WIDTH, - height: CollectionDockingView.UseGoldenLayout || s > 1 ? "100%" : h - 2 * COLLECTION_BORDER_WIDTH - }} > - {chooseLayout()} - </div> + <div className="collectiondockingview-container" id="menuContainer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef} + style={{ + width: CollectionDockingView.UseGoldenLayout || s > 1 ? "100%" : w - 2 * COLLECTION_BORDER_WIDTH, + height: CollectionDockingView.UseGoldenLayout || s > 1 ? "100%" : h - 2 * COLLECTION_BORDER_WIDTH, + borderStyle: "solid", + borderWidth: `${COLLECTION_BORDER_WIDTH}px`, + }} > + {chooseLayout()} </div> ); } +} + +interface DockingProps { + ContainingDiv: string, + Document: Document, + Container: any, + HtmlElement: HTMLElement, + CollectionDockingView: CollectionDockingView, +} +@observer +export class RenderClass extends React.Component<DockingProps> { + @observable + private _parentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ParentScaling prop of the DocumentView + + render() { + let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0); + var layout = this.props.Document.GetText(KeyStore.Layout, ""); + var content = + <DocumentView key={this.props.Document.Id} Document={this.props.Document} + AddDocument={this.props.CollectionDockingView.addDocument} + RemoveDocument={this.props.CollectionDockingView.removeDocument} + Scaling={this._parentScaling} + ScreenToLocalTransform={() => { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this.props.HtmlElement); + return this.props.CollectionDockingView.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale) + }} + isTopMost={true} + ContainingCollectionView={this.props.CollectionDockingView} /> + + if (nativeWidth > 0 && (layout.indexOf("CollectionFreeForm") == -1 || layout.indexOf("AnnotationsKey") != -1)) { + return <Measure onResize={ + action((r: any) => this._parentScaling = nativeWidth > 0 ? r.entry.width / nativeWidth : 1)} + > + {({ measureRef }) => <div ref={measureRef}> {content} </div>} + </Measure> + } + return <div> {content} </div> + } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss index e9d134e7b..4cf474f77 100644 --- a/src/client/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 index e6b1d103d..1f15069fd 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -1,50 +1,60 @@ 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, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; -import { SelectionManager } from "../../util/SelectionManager"; import { KeyStore } from "../../../fields/KeyStore"; 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 { FakeJsxArgs } from "../nodes/DocumentView"; -import { FieldView } from "../nodes/FieldView"; +import { Transform } from "../../util/Transform"; +import { DocumentView } from "../nodes/DocumentView"; @observer export class CollectionFreeFormView extends CollectionViewBase { - public static LayoutString() { return FieldView.LayoutString(CollectionFreeFormView); } + public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionFreeFormView", fieldKey); } private _canvasRef = React.createRef<HTMLDivElement>(); - private _nodeContainerRef = React.createRef<HTMLDivElement>(); private _lastX: number = 0; private _lastY: number = 0; + private _downX: number = 0; + private _downY: number = 0; + + @computed + get isAnnotationOverlay() { return this.props.fieldKey == KeyStore.Annotations; } + + @computed + get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); } + @computed + get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); } + + @computed + get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); } + + @computed + get resizeScaling() { return this.isAnnotationOverlay ? this.props.Document.GetNumber(KeyStore.Width, 0) / this.nativeWidth : 1; } @action drop = (e: Event, de: DragManager.DropEvent) => { - const doc = de.data["document"]; + const doc: DocumentView = 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 { scale, translateX, translateY } = Utils.GetScreenTransform(this._canvasRef.current!); - let sscale = this.props.DocumentViewForField!.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); + 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; + //this should be able to use translate and scale methods on an Identity transform, no? + const transform = me.getTransform(); + const screenX = de.x - xOffset; + const screenY = de.y - yOffset; + const [x, y] = transform.transformPoint(screenX, screenY); + doc.props.Document.SetNumber(KeyStore.X, x); + doc.props.Document.SetNumber(KeyStore.Y, y); + this.bringToFront(doc); e.stopPropagation(); } @@ -70,8 +80,8 @@ export class CollectionFreeFormView extends CollectionViewBase { document.addEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); - this._lastX = e.pageX; - this._lastY = e.pageY; + this._downX = this._lastX = e.pageX; + this._downY = this._lastY = e.pageY; } } @@ -80,20 +90,23 @@ export class CollectionFreeFormView extends CollectionViewBase { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); e.stopPropagation(); - SelectionManager.DeselectAll(); + if (Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) { + if (!this.props.isSelected()) { + this.props.select(false); + } + } } @action onPointerMove = (e: PointerEvent): void => { - var me = this; if (!e.cancelBubble && this.active) { e.preventDefault(); e.stopPropagation(); - let currScale: number = this.props.DocumentViewForField!.ScalingToScreenSpace; - let x = this.props.doc.GetData(KeyStore.PanX, NumberField, Number(0)); - let y = this.props.doc.GetData(KeyStore.PanY, NumberField, Number(0)); - this.props.doc.SetData(KeyStore.PanX, x + (e.pageX - this._lastX) / currScale, NumberField); - this.props.doc.SetData(KeyStore.PanY, y + (e.pageY - this._lastY) / currScale, NumberField); + let x = this.props.Document.GetNumber(KeyStore.PanX, 0); + let y = this.props.Document.GetNumber(KeyStore.PanY, 0); + let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); + + this.SetPan(x + dx, y + dy); } this._lastX = e.pageX; this._lastY = e.pageY; @@ -102,20 +115,28 @@ export class CollectionFreeFormView extends CollectionViewBase { @action onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); + e.preventDefault(); + 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 transform = this.getTransform(); - let { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.DocumentViewForField!.TransformToLocalPoint(e.pageX, e.pageY); + let deltaScale = (1 - (e.deltaY / coefficient)); + let [x, y] = transform.transformPoint(e.clientX, e.clientY); - var deltaScale = (1 - (e.deltaY / 1000)) * Ss; + let localTransform = this.getLocalTransform(); + localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y) - var newContainerX = LocalX * deltaScale + Panxx + Xx; - var newContainerY = LocalY * deltaScale + Panyy + Yy; - - let dx = ContainerX - newContainerX; - let dy = ContainerY - newContainerY; + this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale); + this.SetPan(localTransform.TranslateX, localTransform.TranslateY); + } - this.props.doc.Set(KeyStore.Scale, new NumberField(deltaScale)); - this.props.doc.SetData(KeyStore.PanX, Panxx + dx, NumberField); - this.props.doc.SetData(KeyStore.PanY, Panyy + dy, NumberField); + @action + private SetPan(panX: number, panY: number) { + const newPanX = Math.max((1 - this.zoomScaling) * this.nativeWidth, Math.min(0, panX)); + const newPanY = Math.max((1 - this.zoomScaling) * this.nativeHeight, Math.min(0, panY)); + this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX); + this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY); } @action @@ -125,22 +146,22 @@ export class CollectionFreeFormView extends CollectionViewBase { let fReader = new FileReader() let file = e.dataTransfer.items[0].getAsFile(); let that = this; - const panx: number = this.props.doc.GetData(KeyStore.PanX, NumberField, Number(0)); - const pany: number = this.props.doc.GetData(KeyStore.PanY, NumberField, Number(0)); + const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0); + const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0); 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, { x: x, y: y }) - let docs = that.props.doc.GetT(KeyStore.Data, ListField); + let docs = that.props.Document.GetT(KeyStore.Data, ListField); if (docs != FieldWaiting) { if (!docs) { docs = new ListField<Document>(); - that.props.doc.Set(KeyStore.Data, docs) + that.props.Document.Set(KeyStore.Data, docs) } docs.Data.push(doc); } @@ -152,12 +173,12 @@ export class CollectionFreeFormView extends CollectionViewBase { } } - onDragOver = (e: React.DragEvent): void => { + onDragOver = (): void => { } @action - bringToFront(doc: CollectionFreeFormDocumentView) { - const { fieldKey: fieldKey, doc: Document } = this.props; + 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); @@ -173,8 +194,29 @@ export class CollectionFreeFormView extends CollectionViewBase { } } + @computed + get translate(): [number, number] { + const x = this.props.Document.GetNumber(KeyStore.PanX, 0); + const y = this.props.Document.GetNumber(KeyStore.PanY, 0); + return [x, y]; + } + + @computed + get scale(): number { + return this.props.Document.GetNumber(KeyStore.Scale, 1); + } + + getTransform = (): Transform => { + return this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).transform(this.getLocalTransform()) + } + + getLocalTransform = (): Transform => { + const [x, y] = this.translate; + return Transform.Identity.translate(-x, -y).scale(1 / this.scale); + } + render() { - const { fieldKey: fieldKey, doc: Document } = this.props; + const { fieldKey, Document } = this.props; // const value: Document[] = Document.GetList<Document>(fieldKey, []); const lvalue = Document.GetT<ListField<Document>>(fieldKey, ListField); if (!lvalue || lvalue === "<Waiting>") { @@ -182,27 +224,32 @@ export class CollectionFreeFormView extends CollectionViewBase { } 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.createDropTarget}> - <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}> - {lvalue.Data.map(doc => { - return (<CollectionFreeFormDocumentView key={doc.Id} ContainingCollectionView={this} Document={doc} DocumentView={undefined} />); - })} - </div> - </div> + <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.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} + ContainingCollectionView={this} />); + })} </div> </div> ); diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 707b44db6..633e3ca1b 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -1,3 +1,62 @@ + +.collectionSchemaView-container { + border-style: solid; + box-sizing: border-box; + position: absolute; + width: 100%; + height: 100%; + .collectionfreeformview-container { + border-width: 0px; + .collectionfreeformview > .jsx-parser{ + position:absolute + } + } + .imageBox-cont { + position:relative; + max-height:100%; + } + .ReactTable { + position: absolute; + display: inline-block; + width: 100%; + overflow: auto; + height: 100%; + background: white; + box-sizing: border-box; + } + .ReactTable .rt-thead.-header { + background:grey; + } + .ReactTable .rt-th, .ReactTable .rt-td { + max-height: 75px; + } + .ReactTable .rt-tbody .rt-tr-group:last-child { + border-bottom: grey; + border-bottom-style: solid; + border-bottom-width: 1; + } + .ReactTable .rt-td { + border-width: 1; + border-right-color: #aaa + } + .ReactTable .rt-tr-group { + border-width: 1; + border-bottom-color: #aaa + } + .imageBox-cont img { + object-fit: contain; + height: 100% + } + .documentView-node:first-child { + background: grey; + .imageBox-cont img { + object-fit: contain; + max-width: 100%; + height: 100% + } + } +} + .Resizer { box-sizing: border-box; background: #000; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 9f32ccb72..719783fd7 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -7,13 +7,15 @@ import { observable, action, computed } from "mobx"; import SplitPane from "react-split-pane" import "./CollectionSchemaView.scss" import { ScrollBox } from "../../util/ScrollBox"; -import { CollectionViewBase } from "./CollectionViewBase"; +import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; import { DocumentView } from "../nodes/DocumentView"; import { EditableView } from "../EditableView"; import { CompileScript, ToField } from "../../util/Scripting"; -import { KeyStore as KS } from "../../../fields/KeyStore"; +import { KeyStore } from "../../../fields/KeyStore"; import { Document } from "../../../fields/Document"; import { Field } from "../../../fields/Field"; +import { Transform } from "../../util/Transform"; +import Measure from "react-measure"; @observer export class CollectionSchemaView extends CollectionViewBase { @@ -26,13 +28,14 @@ export class CollectionSchemaView extends CollectionViewBase { let props: FieldViewProps = { doc: rowProps.value[0], fieldKey: rowProps.value[1], - DocumentViewForField: undefined + isSelected: () => false, + isTopMost: false } let contents = ( <FieldView {...props} /> ) return ( - <EditableView contents={contents} GetValue={() => { + <EditableView contents={contents} height={36} GetValue={() => { let field = props.doc.Get(props.fieldKey); if (field && field instanceof Field) { return field.ToScriptString(); @@ -86,56 +89,74 @@ 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(); } } } + + @observable + private _parentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ParentScaling prop of the DocumentView render() { - const { doc: Document, fieldKey: fieldKey } = this.props; + 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) { content = ( - <DocumentView Document={children[this.selectedIndex]} DocumentView={undefined} ContainingCollectionView={this} /> + <Measure onResize={action((r: any) => { + var doc = children[this.selectedIndex]; + var n = doc.GetNumber(KeyStore.NativeWidth, 0); + if (n > 0 && r.entry.width > 0) { + this._parentScaling = r.entry.width / n; + } + })}> + {({ measureRef }) => + <div ref={measureRef}> + <DocumentView Document={children[this.selectedIndex]} + AddDocument={this.addDocument} RemoveDocument={this.removeDocument} + ScreenToLocalTransform={() => Transform.Identity}//TODO This should probably be an actual transform + Scaling={this._parentScaling} + isTopMost={false} + ContainingCollectionView={me} /> + </div> + } + </Measure> ) } else { content = <div /> } return ( - <div onPointerDown={this.onPointerDown} > - <SplitPane split={"vertical"} defaultSize="60%"> - <ScrollBox> - <ReactTable - data={children} - pageSize={children.length} - page={0} - showPagination={false} - style={{ - display: "inline-block" - }} - columns={columns.map(col => { - return ( - { - Header: col.Name, - accessor: (doc: Document) => [doc, col], - id: col.Id - }) - })} - column={{ - ...ReactTableDefaults.column, - Cell: this.renderCell - }} - getTrProps={this.getTrProps} - /> - </ScrollBox> + <div onPointerDown={this.onPointerDown} className="collectionSchemaView-container" + style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }} > + <SplitPane split={"vertical"} defaultSize="60%" style={{ height: "100%", position: "relative", overflow: "none" }}> + <ReactTable + data={children} + pageSize={children.length} + page={0} + showPagination={false} + columns={columns.map(col => { + return ( + { + Header: col.Name, + accessor: (doc: Document) => [doc, col], + id: col.Id + }) + })} + column={{ + ...ReactTableDefaults.column, + Cell: this.renderCell + }} + getTrProps={this.getTrProps} + /> {content} </SplitPane> </div> diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 4fcbb1699..0a90bd0f2 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -10,39 +10,58 @@ import React = require("react"); import { DocumentView } from "../nodes/DocumentView"; import { CollectionDockingView } from "./CollectionDockingView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; -import { FieldViewProps } from "../nodes/FieldView"; +import { Transform } from "../../util/Transform"; + + +export interface CollectionViewProps { + fieldKey: Key; + Document: Document; + ContainingDocumentView: Opt<DocumentView>; + 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<FieldViewProps> { +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} />`; + } @computed public get active(): boolean { - var isSelected = (this.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.DocumentViewForField)); + var isSelected = (this.props.ContainingDocumentView && SelectionManager.IsSelected(this.props.ContainingDocumentView)); var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); - var topMost = this.props.DocumentViewForField != undefined && ( - this.props.DocumentViewForField.props.ContainingCollectionView == undefined || - this.props.DocumentViewForField.props.ContainingCollectionView instanceof CollectionDockingView); + 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.doc.GetData(this.props.fieldKey, ListField, new Array<Document>()) + const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>()) value.push(doc); } @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.doc.GetData(this.props.fieldKey, ListField, new Array<Document>()) - if (value.indexOf(doc) !== -1) { - value.splice(value.indexOf(doc), 1) + 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 |
