aboutsummaryrefslogtreecommitdiff
path: root/src/views/collections
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-01-29 10:47:28 -0500
committerbob <bcz@cs.brown.edu>2019-01-29 10:47:28 -0500
commit0402105238f24785a1229dbbb37f2e4dba958f88 (patch)
tree0b932c896d956c9b3ccf4be054a2bebea1a04d8e /src/views/collections
parent832297c980e8de78ead9cba85dad4d46bdd32b88 (diff)
playing with a docking view.
Diffstat (limited to 'src/views/collections')
-rw-r--r--src/views/collections/CollectionDockingView.scss393
-rw-r--r--src/views/collections/CollectionDockingView.tsx154
-rw-r--r--src/views/collections/CollectionFreeFormView.scss6
-rw-r--r--src/views/collections/CollectionFreeFormView.tsx207
4 files changed, 760 insertions, 0 deletions
diff --git a/src/views/collections/CollectionDockingView.scss b/src/views/collections/CollectionDockingView.scss
new file mode 100644
index 000000000..dc29defdc
--- /dev/null
+++ b/src/views/collections/CollectionDockingView.scss
@@ -0,0 +1,393 @@
+
+.collectiondockingview-container {
+ position: relative;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+ .collectiondockingview {
+ position: relative;
+ top: 0;
+ left: 0;.flexlayout__layout {
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ position: absolute;
+ overflow:hidden;
+ }
+
+ .flexlayout__splitter {
+ background-color: black;
+ }
+
+ .flexlayout__splitter:hover {
+ background-color: #333;
+ }
+
+ .flexlayout__splitter_drag {
+ border-radius: 5px;
+ background-color: #444;
+ z-index: 1000;
+ }
+
+ .flexlayout__outline_rect {
+ position: absolute;
+ cursor: move;
+ border: 2px solid #cfe8ff;
+ box-shadow: inset 0 0 60px rgba(0, 0, 0, .2);
+ border-radius: 5px;
+ z-index: 1000;
+ box-sizing: border-box;
+ }
+
+ .flexlayout__outline_rect_edge {
+ cursor: move;
+ border: 2px solid #b7d1b5;
+ box-shadow: inset 0 0 60px rgba(0, 0, 0, .2);
+ border-radius: 5px;
+ z-index: 1000;
+ box-sizing: border-box;
+ }
+
+ .flexlayout__edge_rect {
+ position: absolute;
+ z-index: 1000;
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, .2);
+ background-color: lightgray;
+ }
+
+ .flexlayout__drag_rect {
+ position: absolute;
+ cursor: move;
+ border: 2px solid #aaaaaa;
+ box-shadow: inset 0 0 60px rgba(0, 0, 0, .3);
+ border-radius: 5px;
+ z-index: 1000;
+ box-sizing: border-box;
+ background-color:#eeeeee;
+ opacity: 0.9;
+ text-align: center;
+
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ overflow:hidden;
+ padding:10px;
+ word-wrap: break-word;
+ }
+
+ .flexlayout__tabset {
+ overflow: hidden;
+ background-color: #222;
+ box-sizing: border-box;
+ }
+
+ .flexlayout__tab {
+ overflow: auto;
+ position:absolute;
+ box-sizing: border-box;
+ background-color: #222;
+ color:white;
+ }
+
+ .flexlayout__tab_button {
+ cursor: pointer;
+ padding: 2px 8px 3px 8px;
+ margin: 2px;
+ /*box-shadow: inset 0px 0px 5px rgba(0, 0, 0, .15);*/
+ /*border-top-left-radius: 3px;*/
+ /*border-top-right-radius: 3px;*/
+ float:left;
+ vertical-align: top;
+ box-sizing: border-box;
+
+ }
+
+ .flexlayout__tab_button--selected {
+ color: #ddd;
+ background-color: #222;
+ }
+
+ .flexlayout__tab_button--unselected {
+ color: gray;
+ }
+
+ .flexlayout__tab_button_leading {
+ float: left;
+ display:inline-block;
+ }
+
+ .flexlayout__tab_button_content {
+ float: left;
+ display:inline-block;
+ }
+
+ .flexlayout__tab_button_textbox {
+ float: left;
+ border: none;
+ color:lightgreen;
+ background-color:#222;
+ }
+ .flexlayout__tab_button_textbox:focus {
+ outline: none;
+ }
+
+ .flexlayout__tab_button_trailing {
+ float: left;
+ display:inline-block;
+ margin-left:5px;
+ margin-top:3px;
+ width:8px;
+ height:8px;
+ }
+
+ .flexlayout__tab_button:hover .flexlayout__tab_button_trailing,
+ .flexlayout__tab_button--selected .flexlayout__tab_button_trailing{
+ //background: transparent url("../images/close_white.png") no-repeat center;
+ }
+
+ .flexlayout__tab_button_overflow {
+ float: left;
+ width: 20px;
+ height:15px;
+ margin-top:2px;
+ padding-left:12px;
+ border:none;
+ font-size: 10px;
+ color:lightgray;
+ font-family: Arial, sans-serif;
+ //background: transparent url("../images/more.png") no-repeat left;
+ }
+
+ .flexlayout__tabset_header
+ {
+ position: absolute;
+ left: 0;
+ right: 0;
+ color:#eee;
+ background-color: #212121;
+ padding:3px 3px 3px 5px;
+ /*box-shadow: inset 0px 0px 3px 0px rgba(136, 136, 136, 0.54);*/
+ box-sizing: border-box;
+ }
+
+ .flexlayout__tab_header_inner {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 10000px;
+ }
+
+ .flexlayout__tab_header_outer {
+ background-color: black;
+ position: absolute;
+ left: 0;
+ right: 0;
+ /*top: 0px;*/
+ /*height: 100px;*/
+ overflow: hidden;
+ }
+
+ .flexlayout__tabset-selected
+ {
+ //background-image: linear-gradient(#111, #444);
+ }
+
+ .flexlayout__tabset-maximized
+ {
+ //background-image: linear-gradient(#666, #333);
+ }
+
+ .flexlayout__tab_toolbar {
+ position:absolute;
+ display:flex;
+ flex-direction: row-reverse;
+ align-items: center;
+ top:0;
+ bottom:0;
+ right:0;
+ }
+
+ .flexlayout__tab_toolbar_button-min {
+ width:20px;
+ height:20px;
+ border:none;
+ outline-width: 0;
+ //background: transparent url("../images/maximize.png") no-repeat center;
+
+ }
+ .flexlayout__tab_toolbar_button-max {
+ width:20px;
+ height:20px;
+ border:none;
+ outline-width: 0;
+ //background: transparent url("../images/restore.png") no-repeat center;
+ }
+
+ .flexlayout__popup_menu {
+ }
+
+ .flexlayout__popup_menu_item {
+ padding: 2px 10px 2px 10px;
+ color:#ddd;
+ }
+
+ .flexlayout__popup_menu_item:hover {
+ background-color: #444444;
+ }
+
+ .flexlayout__popup_menu_container {
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, .15);
+ border: 1px solid #555;
+ background: #222;
+ border-radius:3px;
+ position:absolute;
+ z-index:1000;
+ }
+
+
+ .flexlayout__border_top {
+ background-color:black;
+ border-bottom: 1px solid #ddd;
+ box-sizing: border-box;
+ overflow:hidden;
+ }
+
+ .flexlayout__border_bottom {
+ background-color:black;
+ border-top: 1px solid #333;
+ box-sizing: border-box;
+ overflow:hidden;
+ }
+ .flexlayout__border_left {
+ background-color:black;
+ border-right: 1px solid #333;
+ box-sizing: border-box;
+ overflow:hidden;
+ }
+
+ .flexlayout__border_right {
+ background-color:black;
+ border-left: 1px solid #333;
+ box-sizing: border-box;
+ overflow:hidden;
+ }
+
+ .flexlayout__border_inner_bottom{
+ display: flex;
+ }
+
+ .flexlayout__border_inner_left {
+ position:absolute;
+ white-space: nowrap;
+ right: 23px;
+ transform-origin: top right;
+ transform: rotate(-90deg);
+ }
+
+ .flexlayout__border_inner_right {
+ position:absolute;
+ white-space: nowrap;
+ left: 23px;
+ transform-origin: top left;
+ transform: rotate(90deg);
+ }
+
+ .flexlayout__border_button {
+ background-color: #222;
+ color:white;
+ display:inline-block;
+ white-space:nowrap;
+
+ cursor: pointer;
+ padding: 2px 8px 3px 8px;
+ margin: 2px;
+ vertical-align: top;
+ box-sizing: border-box;
+ }
+
+ .flexlayout__border_button--selected {
+ color: #ddd;
+ background-color: #222;
+ }
+
+ .flexlayout__border_button--unselected {
+ color: gray;
+ }
+
+ .flexlayout__border_button_leading {
+ float: left;
+ display:inline;
+ }
+
+ .flexlayout__border_button_content {
+ display:inline-block;
+ }
+
+ .flexlayout__border_button_textbox {
+ float: left;
+ border: none;
+ color:green;
+ background-color:#ddd;
+ }
+ .flexlayout__border_button_textbox:focus {
+ outline: none;
+ }
+
+ .flexlayout__border_button_trailing {
+ display:inline-block;
+ margin-left:5px;
+ margin-top:3px;
+ width:8px;
+ height:8px;
+ }
+
+ .flexlayout__border_button:hover .flexlayout__border_button_trailing,
+ .flexlayout__border_button--selected .flexlayout__border_button_trailing{
+ //background: transparent url("../images/close_white.png") no-repeat center;
+ }
+
+
+ .flexlayout__border_toolbar_left {
+ position:absolute;
+ display: flex;
+ flex-direction: column-reverse;
+ align-items: center;
+ bottom:0;
+ left:0;
+ right:0;
+ }
+
+ .flexlayout__border_toolbar_right {
+ position:absolute;
+ display: flex;
+ flex-direction: column-reverse;
+ align-items: center;
+ bottom:0;
+ left:0;
+ right:0;
+ }
+
+ .flexlayout__border_toolbar_top {
+ position:absolute;
+ display: flex;
+ flex-direction: row-reverse;
+ align-items: center;
+ top:0;
+ bottom:0;
+ right:0;
+ }
+
+ .flexlayout__border_toolbar_bottom {
+ position:absolute;
+ display: flex;
+ flex-direction: row-reverse;
+ align-items: center;
+ top:0;
+ bottom:0;
+ right:0;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx
new file mode 100644
index 000000000..51323e94b
--- /dev/null
+++ b/src/views/collections/CollectionDockingView.tsx
@@ -0,0 +1,154 @@
+import { observer } from "mobx-react";
+import { Key, KeyStore } from "../../fields/Key";
+import React = require("react");
+import FlexLayout from "flexlayout-react";
+import { action, observable, computed } from "mobx";
+import { Document } from "../../fields/Document";
+import { DocumentView, DocumentFieldViewProps, CollectionViewProps } from "../nodes/DocumentView";
+import { ListField } from "../../fields/ListField";
+import { NumberField } from "../../fields/NumberField";
+import { SSL_OP_SINGLE_DH_USE } from "constants";
+import { SelectionManager } from "../../util/SelectionManager";
+import { ContextMenu } from "../ContextMenu";
+import "./CollectionDockingView.scss"
+
+
+@observer
+export class CollectionDockingView extends React.Component<CollectionViewProps> {
+
+ public static LayoutString() { return '<CollectionDockingView Document={Document} fieldKey={DataKey} ContainingDocumentView={ContainingDocumentView}/>'; }
+
+
+ private _times: number = 0;
+ private _containerRef = React.createRef<HTMLDivElement>();
+ private _canvasRef = React.createRef<HTMLDivElement>();
+ private _json = {
+ global: {},
+ borders: [],
+ layout: {
+ "type": "row",
+ "weight": 100,
+ "children": [
+ {
+ "type": "tabset",
+ "weight": 50,
+ "selected": 0,
+ "children": [
+ {
+ "type": "tab",
+ "name": "CHILD #1",
+ "component": "grid",
+ }
+ ]
+ },
+ {
+ "type": "tabset",
+ "weight": 50,
+ "selected": 0,
+ "children": [
+ {
+ "type": "tab",
+ "name": "CHILD #2",
+ "component": "grid",
+ }
+ ]
+ }
+ ]
+ }
+ };
+ private _model: any;
+ constructor(props: CollectionViewProps) {
+ super(props);
+ this._model = FlexLayout.Model.fromJson(this._json);
+ }
+
+ public static BORDER_WIDTH = 2;
+
+ @computed
+ public get active(): boolean {
+ var isSelected = (this.props.ContainingDocumentView != undefined && SelectionManager.IsSelected(this.props.ContainingDocumentView));
+ var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this);
+ var topMost = this.props.ContainingDocumentView != undefined && this.props.ContainingDocumentView.props.ContainingCollectionView == undefined;
+ return isSelected || childSelected || topMost;
+ }
+
+ componentDidMount() {
+ // if (this._containerRef.current) {
+ // DragManager.MakeDropTarget(this._containerRef.current, {
+ // handlers: {
+ // drop: this.drop
+ // }
+ // });
+ // }
+ }
+
+
+ @action
+ addDocument = (doc: Document): void => {
+ //TODO This won't create the field if it doesn't already exist
+ const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array<Document>())
+ value.push(doc);
+ }
+
+ @action
+ removeDocument = (doc: Document): void => {
+ //TODO This won't create the field if it doesn't already exist
+ const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array<Document>())
+ if (value.indexOf(doc) !== -1) {
+ value.splice(value.indexOf(doc), 1)
+
+ SelectionManager.DeselectAll()
+ ContextMenu.Instance.clearItems()
+ }
+ }
+ @action
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (e.button === 2 && this.active) {
+ e.stopPropagation();
+ e.preventDefault();
+ } else {
+ if (e.buttons === 1 && SelectionManager.IsSelected(this.props.ContainingDocumentView!)) {
+ e.stopPropagation();
+ }
+ }
+ }
+ factory = (node: any): any => {
+ var component = node.getComponent();
+ if (component === "button") {
+ return <button>{node.getName()}</button>;
+ }
+ if (component === "grid") {
+ let which = this._times++ % 3;
+ if (which == 0)
+ return <div style={{ backgroundColor: "blue", width: 100, height: 100 }}></div>
+ if (which == 1)
+ return <div style={{ backgroundColor: "yellow", width: 100, height: 100 }}></div>
+ if (which == 2)
+ return <div style={{ backgroundColor: "red", width: 100, height: 100 }}></div>
+ }
+ }
+
+ render() {
+ const { fieldKey, Document: Document } = this.props;
+
+ const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
+ const panx: number = Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0));
+ const pany: number = Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0));
+ const currScale: number = Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1));
+ return (
+ <div className="border" style={{
+ borderStyle: "solid",
+ borderWidth: `${CollectionDockingView.BORDER_WIDTH}px`,
+ }}>
+ <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} style={{
+ width: "100%",
+ height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`,
+ }} ref={this._containerRef}>
+ <div className="collectiondockingview" ref={this._canvasRef}>
+ <FlexLayout.Layout model={this._model} factory={this.factory} />
+ </div>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss
new file mode 100644
index 000000000..31e9985bc
--- /dev/null
+++ b/src/views/collections/CollectionFreeFormView.scss
@@ -0,0 +1,6 @@
+.collectionfreeformview-container {
+ position: relative;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+} \ No newline at end of file
diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx
new file mode 100644
index 000000000..479d883d6
--- /dev/null
+++ b/src/views/collections/CollectionFreeFormView.tsx
@@ -0,0 +1,207 @@
+import { observer } from "mobx-react";
+import { Key, KeyStore } from "../../fields/Key";
+import React = require("react");
+import { action, observable, computed } from "mobx";
+import { Document } from "../../fields/Document";
+import { DocumentView, CollectionViewProps } from "../nodes/DocumentView";
+import { ListField } from "../../fields/ListField";
+import { NumberField } from "../../fields/NumberField";
+import { SSL_OP_SINGLE_DH_USE } from "constants";
+import { SelectionManager } from "../../util/SelectionManager";
+import { Documents } from "../../documents/Documents";
+import { ContextMenu } from "../ContextMenu";
+import { DragManager } from "../../util/DragManager";
+import "./CollectionFreeFormView.scss";
+import { Utils } from "../../Utils";
+
+@observer
+export class CollectionFreeFormView extends React.Component<CollectionViewProps> {
+ private _containerRef = React.createRef<HTMLDivElement>();
+ private _canvasRef = React.createRef<HTMLDivElement>();
+
+ constructor(props: CollectionViewProps) {
+ super(props);
+ }
+
+ public static BORDER_WIDTH = 2;
+
+ @computed
+ public get active(): boolean {
+ var isSelected = (this.props.ContainingDocumentView != undefined && SelectionManager.IsSelected(this.props.ContainingDocumentView));
+ var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this);
+ var topMost = this.props.ContainingDocumentView != undefined && this.props.ContainingDocumentView.props.ContainingCollectionView == undefined;
+ return isSelected || childSelected || topMost;
+ }
+
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ const ele = this._canvasRef.current;
+ if (!ele) {
+ return;
+ }
+ const doc = de.data[ "document" ];
+ const xOffset = de.data[ "xOffset" ] as number || 0;
+ const yOffset = de.data[ "yOffset" ] as number || 0;
+ if (doc instanceof DocumentView) {
+ if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) {
+ doc.props.ContainingCollectionView.removeDocument(doc.props.Document);
+ this.addDocument(doc.props.Document);
+ }
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
+ const screenX = de.x - xOffset;
+ const screenY = de.y - yOffset;
+ const docX = (screenX - translateX) / scale;
+ const docY = (screenY - translateY) / scale;
+ doc.x = docX;
+ doc.y = docY;
+ }
+ e.stopPropagation();
+ }
+
+ componentDidMount() {
+ if (this._containerRef.current) {
+ DragManager.MakeDropTarget(this._containerRef.current, {
+ handlers: {
+ drop: this.drop
+ }
+ });
+ }
+ }
+
+ _lastX: number = 0;
+ _lastY: number = 0;
+ @action
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (e.button === 2 && this.active) {
+ e.stopPropagation();
+ e.preventDefault();
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.addEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ }
+ }
+
+ @action
+ onPointerUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ e.stopPropagation();
+ }
+
+ @action
+ onPointerMove = (e: PointerEvent): void => {
+ if (!e.cancelBubble) {
+ e.preventDefault();
+ e.stopPropagation();
+ let currScale: number = this.props.ContainingDocumentView!.ScalingToScreenSpace;
+ let x = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0));
+ let y = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0));
+ this.props.Document.SetFieldValue(KeyStore.PanX, x + (e.pageX - this._lastX) / currScale, NumberField);
+ this.props.Document.SetFieldValue(KeyStore.PanY, y + (e.pageY - this._lastY) / currScale, NumberField);
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ }
+ }
+
+ @action
+ onPointerWheel = (e: React.WheelEvent): void => {
+ e.stopPropagation();
+
+ let { LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY);
+
+ var deltaScale = (1 - (e.deltaY / 1000)) * Ss;
+
+ var newContainerX = LocalX * deltaScale + W / 2 - W / 2 * deltaScale + Panxx + Xx;
+ var newContainerY = LocalY * deltaScale + Panyy + Yy;
+
+ let dx = ContainerX - newContainerX;
+ let dy = ContainerY - newContainerY;
+
+ this.props.Document.SetField(KeyStore.Scale, new NumberField(deltaScale));
+ this.props.Document.SetFieldValue(KeyStore.PanX, Panxx + dx, NumberField);
+ this.props.Document.SetFieldValue(KeyStore.PanY, Panyy + dy, NumberField);
+ }
+
+ onDrop = (e: React.DragEvent): void => {
+ e.stopPropagation()
+ e.preventDefault()
+ let fReader = new FileReader()
+ let file = e.dataTransfer.items[ 0 ].getAsFile();
+ let that = this;
+ const panx: number = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0));
+ const pany: number = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0));
+ let x = e.pageX - panx
+ let y = e.pageY - pany
+
+ fReader.addEventListener("load", action("drop", (event) => {
+ if (fReader.result) {
+ let url = "" + fReader.result;
+ let doc = Documents.ImageDocument(url, {
+ x: x, y: y
+ })
+ let docs = that.props.Document.GetFieldT(KeyStore.Data, ListField);
+ if (!docs) {
+ docs = new ListField<Document>();
+ that.props.Document.SetField(KeyStore.Data, docs)
+ }
+ docs.Data.push(doc);
+ }
+ }), false)
+
+ if (file) {
+ fReader.readAsDataURL(file)
+ }
+ }
+
+ @action
+ addDocument = (doc: Document): void => {
+ //TODO This won't create the field if it doesn't already exist
+ const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array<Document>())
+ value.push(doc);
+ }
+
+ @action
+ removeDocument = (doc: Document): void => {
+ //TODO This won't create the field if it doesn't already exist
+ const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array<Document>())
+ if (value.indexOf(doc) !== -1) {
+ value.splice(value.indexOf(doc), 1)
+
+ SelectionManager.DeselectAll()
+ ContextMenu.Instance.clearItems()
+ }
+ }
+
+ onDragOver = (e: React.DragEvent): void => {
+ }
+ render() {
+ const { fieldKey, Document: Document } = this.props;
+
+ const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
+ const panx: number = Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0));
+ const pany: number = Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0));
+ const currScale: number = Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1));
+ return (
+ <div className="border" style={{
+ borderStyle: "solid",
+ borderWidth: `${CollectionFreeFormView.BORDER_WIDTH}px`,
+ }}>
+ <div className="collectionfreeformview-container" onPointerDown={this.onPointerDown} onWheel={this.onPointerWheel} onContextMenu={(e) => e.preventDefault()} style={{
+ width: "100%",
+ height: `calc(100% - 2*${CollectionFreeFormView.BORDER_WIDTH}px)`,
+ }} onDrop={this.onDrop} onDragOver={this.onDragOver} ref={this._containerRef}>
+ <div className="collectionfreeformview" style={{ transform: `translate(${panx}px, ${pany}px) scale(${currScale}, ${currScale})`, transformOrigin: `left, top` }} ref={this._canvasRef}>
+
+ <div className="node-container">
+ {value.map(doc => {
+ return (<DocumentView key={doc.Id} ContainingCollectionView={this} Document={doc} ContainingDocumentView={this.props.ContainingDocumentView} />);
+ })}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file