From 0402105238f24785a1229dbbb37f2e4dba958f88 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 29 Jan 2019 10:47:28 -0500
Subject: playing with a docking view.

---
 src/Main.tsx | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/src/Main.tsx b/src/Main.tsx
index 7560abca7..6ccb940f4 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -12,10 +12,9 @@ import { configure, runInAction, action } from 'mobx';
 import { NodeStore } from './stores/NodeStore';
 import { Documents } from './documents/Documents';
 import { DocumentDecorations } from './DocumentDecorations';
-import { CollectionFreeFormView } from './views/freeformcanvas/CollectionFreeFormView';
+import { CollectionFreeFormView } from './views/collections/CollectionFreeFormView';
 import { ListField } from './fields/ListField';
 import { DocumentView } from './views/nodes/DocumentView';
-import { DocumentViewModel } from './viewmodels/DocumentViewModel';
 import { ContextMenu } from './views/ContextMenu';
 
 configure({
@@ -60,16 +59,20 @@ runInAction(() => {
         x: 0, y: 400
     });
     let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
-        x: 650, y: 500
+        x: 650, y: 500, width: 600, height: 600
+    });
+    let doc6 = Documents.DockDocument("", {
+        x: 350, y: 100
     });
     let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField);
     if (!mainNodes) {
         mainNodes = new ListField<Document>();
         mainContainer.SetField(KeyStore.Data, mainNodes);
     }
-    mainNodes.Data.push(doc1);
-    mainNodes.Data.push(doc2);
-    mainNodes.Data.push(doc4);
-    mainNodes.Data.push(doc3);
-    mainNodes.Data.push(doc5);
+    // mainNodes.Data.push(doc1);
+    // mainNodes.Data.push(doc2);
+    // mainNodes.Data.push(doc4);
+    // mainNodes.Data.push(doc3);
+    // mainNodes.Data.push(doc5);
+    mainNodes.Data.push(doc6);
 });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From ed982a553d1831353e312ae8137afa95ef84ebe5 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 29 Jan 2019 12:20:13 -0500
Subject: semi working docking, but zooming is now broken.

---
 build/index.html                                  |   1 -
 src/DocumentDecorations.tsx                       |   5 +-
 src/Main.tsx                                      |  10 +-
 src/Utils.ts                                      |   2 +-
 src/documents/Documents.ts                        |   4 +-
 src/views/collections/CollectionDockingView.scss  | 759 +++++++++++-----------
 src/views/collections/CollectionDockingView.tsx   |  59 +-
 src/views/collections/CollectionFreeFormView.scss |   7 +-
 src/views/nodes/DocumentView.tsx                  |  19 +-
 9 files changed, 444 insertions(+), 422 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/build/index.html b/build/index.html
index 49872f02b..4027667cd 100644
--- a/build/index.html
+++ b/build/index.html
@@ -2,7 +2,6 @@
 
 <head>
   <title>Dash Web</title>
-  <link href="./node_modules/flexlayout-react/style/dark.css" rel="stylesheet" />
   <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
 </head>
 
diff --git a/src/DocumentDecorations.tsx b/src/DocumentDecorations.tsx
index 7899db9c9..d5a682b82 100644
--- a/src/DocumentDecorations.tsx
+++ b/src/DocumentDecorations.tsx
@@ -24,7 +24,10 @@ export class DocumentDecorations extends React.Component {
         return SelectionManager.SelectedDocuments().reduce((bounds, element) => {
             var spt = element.TransformToScreenPoint(0, 0);
             var bpt = element.TransformToScreenPoint(element.width, element.height);
-            return {
+            if (spt.ScreenX == undefined || spt.ScreenY == undefined ||
+                bpt.ScreenX == undefined || bpt.ScreenY == undefined)
+                return { x: bounds.x, y: bounds.y, r: bounds.r, b: bounds.b };
+            else return {
                 x: Math.min(spt.ScreenX, bounds.x), y: Math.min(spt.ScreenY, bounds.y),
                 r: Math.max(bpt.ScreenX, bounds.r), b: Math.max(bpt.ScreenY, bounds.b)
             }
diff --git a/src/Main.tsx b/src/Main.tsx
index 6ccb940f4..1b5d95132 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -54,14 +54,14 @@ runInAction(() => {
     let doc3 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
         x: 450, y: 500
     });
-    let docset = new Array<Document>(doc1, doc2);
+    let docset = new Array<Document>(doc1, doc2, doc3);
     let doc4 = Documents.CollectionDocument(docset, {
         x: 0, y: 400
     });
     let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
         x: 650, y: 500, width: 600, height: 600
     });
-    let doc6 = Documents.DockDocument("", {
+    let doc6 = Documents.DockDocument(docset, {
         x: 350, y: 100
     });
     let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField);
@@ -71,8 +71,10 @@ runInAction(() => {
     }
     // mainNodes.Data.push(doc1);
     // mainNodes.Data.push(doc2);
-    // mainNodes.Data.push(doc4);
+    mainNodes.Data.push(doc4);
     // mainNodes.Data.push(doc3);
-    // mainNodes.Data.push(doc5);
+    mainNodes.Data.push(doc5);
+    mainNodes.Data.push(doc1);
+    mainNodes.Data.push(doc2);
     mainNodes.Data.push(doc6);
 });
\ No newline at end of file
diff --git a/src/Utils.ts b/src/Utils.ts
index 948b6176e..cc1d8f6c6 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -13,7 +13,7 @@ export class Utils {
 
     public static GetScreenTransform(ele: HTMLElement): { scale: number, translateX: number, translateY: number } {
         const rect = ele.getBoundingClientRect();
-        const scale = rect.width / ele.offsetWidth;
+        const scale = ele.offsetWidth == 0 && rect.width == 0 ? 1 : rect.width / ele.offsetWidth;
         const translateX = rect.left;
         const translateY = rect.top;
 
diff --git a/src/documents/Documents.ts b/src/documents/Documents.ts
index a0bd021d8..7cc5de3a3 100644
--- a/src/documents/Documents.ts
+++ b/src/documents/Documents.ts
@@ -68,10 +68,10 @@ export namespace Documents {
         return dockProto;
     }
 
-    export function DockDocument(text: string, options: DocumentOptions = {}): Document {
+    export function DockDocument(documents: Array<Document>, options: DocumentOptions = {}): Document {
         let doc = GetDockPrototype().MakeDelegate();
         setupOptions(doc, options);
-        // doc.SetField(KeyStore.Data, new TextField(text));
+        doc.SetField(KeyStore.Data, new ListField(documents));
         return doc;
     }
 
diff --git a/src/views/collections/CollectionDockingView.scss b/src/views/collections/CollectionDockingView.scss
index dc29defdc..43af7c538 100644
--- a/src/views/collections/CollectionDockingView.scss
+++ b/src/views/collections/CollectionDockingView.scss
@@ -4,390 +4,383 @@
     top: 0;
     left: 0;
     overflow: hidden;
-    .collectiondockingview {
+    .flexlayout__layout {
+        width: 100%;
+        height: 100%;
         position: relative;
+        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;
+    }
+    
+    .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;
-        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;
-        }
-        
+        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
index 51323e94b..b6fff6ba0 100644
--- a/src/views/collections/CollectionDockingView.tsx
+++ b/src/views/collections/CollectionDockingView.tsx
@@ -37,7 +37,7 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
                         {
                             "type": "tab",
                             "name": "CHILD #1",
-                            "component": "grid",
+                            "component": "doc1",
                         }
                     ]
                 },
@@ -49,7 +49,19 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
                         {
                             "type": "tab",
                             "name": "CHILD #2",
-                            "component": "grid",
+                            "component": "doc2",
+                        }
+                    ]
+                },
+                {
+                    "type": "tabset",
+                    "weight": 50,
+                    "selected": 0,
+                    "children": [
+                        {
+                            "type": "tab",
+                            "name": "CHILD #3",
+                            "component": "doc3",
                         }
                     ]
                 }
@@ -107,7 +119,7 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
             e.stopPropagation();
             e.preventDefault();
         } else {
-            if (e.buttons === 1 && SelectionManager.IsSelected(this.props.ContainingDocumentView!)) {
+            if (e.buttons === 1 && this.active) {
                 e.stopPropagation();
             }
         }
@@ -117,14 +129,20 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         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>
+        console.log("Gettting " + component);
+        const { fieldKey, Document: Document } = this.props;
+        const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
+        if (component === "doc1") {
+            return (<DocumentView key={value[ 0 ].Id} ContainingCollectionView={this} Document={value[ 0 ]} ContainingDocumentView={this.props.ContainingDocumentView} />);
+        }
+        if (component === "doc2") {
+            return (<DocumentView key={value[ 1 ].Id} ContainingCollectionView={this} Document={value[ 1 ]} ContainingDocumentView={this.props.ContainingDocumentView} />);
+        }
+        if (component === "doc3") {
+            return (<DocumentView key={value[ 2 ].Id} ContainingCollectionView={this} Document={value[ 2 ]} ContainingDocumentView={this.props.ContainingDocumentView} />);
+        }
+        if (component === "text") {
+            return (<div className="panel">Panel {node.getName()}</div>);
         }
     }
 
@@ -132,21 +150,20 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         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));
+        // bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes.
+        var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0));
+        var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0));
         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 className="collectiondockingview-container" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef}
+                    style={{
+                        width: "100%",
+                        height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`,
+                    }} >
+                    <FlexLayout.Layout model={this._model} factory={this.factory} />
                 </div>
             </div>
         );
diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss
index 31e9985bc..2b68ba981 100644
--- a/src/views/collections/CollectionFreeFormView.scss
+++ b/src/views/collections/CollectionFreeFormView.scss
@@ -1,6 +1,11 @@
 .collectionfreeformview-container {
-    position: relative;
+    position: absolute;
     top: 0;
     left: 0;
     overflow: hidden;
+    .collectionfreeformview {
+        position: absolute;
+        top: 0;
+        left: 0;
+    }
 }
\ No newline at end of file
diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx
index 3ee70213f..517d691f9 100644
--- a/src/views/nodes/DocumentView.tsx
+++ b/src/views/nodes/DocumentView.tsx
@@ -15,6 +15,7 @@ import { DocumentDecorations } from "../../DocumentDecorations";
 import { ContextMenu } from "../ContextMenu";
 import { Opt } from "../../fields/Field";
 import { DragManager } from "../../util/DragManager";
+import { number } from "prop-types";
 const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
 
 interface DocumentViewProps {
@@ -193,14 +194,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     //
     // Converts a point in the coordinate space of a document to a screen space coordinate.
     //
-    public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: number, ScreenY: number } {
-
+    public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: Opt<number>, ScreenY: Opt<number> } {
+        if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) {
+            return { ScreenX: undefined, ScreenY: undefined };
+        }
         let W = this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0));
         let H = CollectionFreeFormView.BORDER_WIDTH;
         let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
         let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
-        let parentX = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2;
-        let parentY = (localY - H) * Ss + (Yy + Panyy) + H;
+        let parentX: Opt<number> = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2;
+        let parentY: Opt<number> = (localY - H) * Ss + (Yy + Panyy) + H;
 
         // if this collection view is nested within another collection view, then 
         // first transform the local point into the parent collection's coordinate space.
@@ -295,11 +298,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     render() {
+        let freeStyling = this.props.ContainingCollectionView instanceof CollectionFreeFormView;
         return (
             <div className="node" ref={this._mainCont} style={{
-                transform: this.transform,
-                width: this.width,
-                height: this.height,
+                transform: freeStyling ? this.transform : "",
+                width: freeStyling ? this.width : "100%",
+                height: freeStyling ? this.height : "100%",
             }}
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown}>
@@ -307,5 +311,4 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             </div>
         );
     }
-
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 7bc8bf62d9c9ff8e388103d0faa75855d43b8e16 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 29 Jan 2019 12:55:59 -0500
Subject: fixed zooming

---
 src/Main.tsx                                      |  7 ++--
 src/views/collections/CollectionFreeFormView.scss |  4 +--
 src/views/nodes/DocumentView.tsx                  | 42 +++++++++++++++--------
 3 files changed, 33 insertions(+), 20 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/src/Main.tsx b/src/Main.tsx
index 1b5d95132..566216373 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -61,7 +61,8 @@ runInAction(() => {
     let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
         x: 650, y: 500, width: 600, height: 600
     });
-    let doc6 = Documents.DockDocument(docset, {
+    let docset2 = new Array<Document>(doc1, doc4, doc3);
+    let doc6 = Documents.DockDocument(docset2, {
         x: 350, y: 100
     });
     let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField);
@@ -74,7 +75,7 @@ runInAction(() => {
     mainNodes.Data.push(doc4);
     // mainNodes.Data.push(doc3);
     mainNodes.Data.push(doc5);
-    mainNodes.Data.push(doc1);
-    mainNodes.Data.push(doc2);
+    // mainNodes.Data.push(doc1);
+    // mainNodes.Data.push(doc2);
     mainNodes.Data.push(doc6);
 });
\ No newline at end of file
diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss
index 2b68ba981..1563712fb 100644
--- a/src/views/collections/CollectionFreeFormView.scss
+++ b/src/views/collections/CollectionFreeFormView.scss
@@ -1,10 +1,10 @@
 .collectionfreeformview-container {
-    position: absolute;
+    position: relative;
     top: 0;
     left: 0;
     overflow: hidden;
     .collectionfreeformview {
-        position: absolute;
+        position: relative;
         top: 0;
         left: 0;
     }
diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx
index 517d691f9..3b9e6cc04 100644
--- a/src/views/nodes/DocumentView.tsx
+++ b/src/views/nodes/DocumentView.tsx
@@ -15,7 +15,6 @@ import { DocumentDecorations } from "../../DocumentDecorations";
 import { ContextMenu } from "../ContextMenu";
 import { Opt } from "../../fields/Field";
 import { DragManager } from "../../util/DragManager";
-import { number } from "prop-types";
 const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
 
 interface DocumentViewProps {
@@ -202,8 +201,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         let H = CollectionFreeFormView.BORDER_WIDTH;
         let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
         let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
-        let parentX: Opt<number> = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2;
-        let parentY: Opt<number> = (localY - H) * Ss + (Yy + Panyy) + H;
+        let parentX: Opt<any> = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2;
+        let parentY: Opt<any> = (localY - H) * Ss + (Yy + Panyy) + H;
 
         // if this collection view is nested within another collection view, then 
         // first transform the local point into the parent collection's coordinate space.
@@ -297,18 +296,31 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
 
+
     render() {
-        let freeStyling = this.props.ContainingCollectionView instanceof CollectionFreeFormView;
-        return (
-            <div className="node" ref={this._mainCont} style={{
-                transform: freeStyling ? this.transform : "",
-                width: freeStyling ? this.width : "100%",
-                height: freeStyling ? this.height : "100%",
-            }}
-                onContextMenu={this.onContextMenu}
-                onPointerDown={this.onPointerDown}>
-                <DocumentContents {...this.props} ContainingDocumentView={this} />
-            </div>
-        );
+        var freestyling = this.props.ContainingCollectionView === undefined || this.props.ContainingCollectionView instanceof CollectionFreeFormView;
+        if (freestyling)
+            return (
+                <div className="node" ref={this._mainCont} style={{
+                    transform: this.transform,
+                    width: this.width,
+                    height: this.height,
+                }}
+                    onContextMenu={this.onContextMenu}
+                    onPointerDown={this.onPointerDown}>
+                    <DocumentContents {...this.props} ContainingDocumentView={this} />
+                </div>
+            );
+        else
+            return (
+                <div className="node" ref={this._mainCont} style={{
+                    width: "100%",
+                    height: "100%",
+                }}
+                    onContextMenu={this.onContextMenu}
+                    onPointerDown={this.onPointerDown}>
+                    <DocumentContents {...this.props} ContainingDocumentView={this} />
+                </div>
+            );
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 02891812b01888aba3eada58d6051a80a79c1a18 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 29 Jan 2019 21:48:52 -0500
Subject: flexLayout fixes, but flexlayout seems hopelessly broken once it gets
 scaled.

---
 src/Main.tsx                                    |  2 +-
 src/util/DragManager.ts                         | 23 ++++++++++++++---------
 src/views/collections/CollectionDockingView.tsx | 12 +++++++-----
 src/views/nodes/DocumentView.tsx                | 10 ++++++----
 4 files changed, 28 insertions(+), 19 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/src/Main.tsx b/src/Main.tsx
index 566216373..c636ee42e 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -61,7 +61,7 @@ runInAction(() => {
     let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
         x: 650, y: 500, width: 600, height: 600
     });
-    let docset2 = new Array<Document>(doc1, doc4, doc3);
+    let docset2 = new Array<Document>(doc4, doc1, doc3);
     let doc6 = Documents.DockDocument(docset2, {
         x: 350, y: 100
     });
diff --git a/src/util/DragManager.ts b/src/util/DragManager.ts
index e523408a3..ca5b6c0ea 100644
--- a/src/util/DragManager.ts
+++ b/src/util/DragManager.ts
@@ -1,7 +1,7 @@
-import {Opt} from "../fields/Field";
-import {DocumentView} from "../views/nodes/DocumentView";
-import {DocumentDecorations} from "../DocumentDecorations";
-import {SelectionManager} from "./SelectionManager";
+import { Opt } from "../fields/Field";
+import { DocumentView } from "../views/nodes/DocumentView";
+import { DocumentDecorations } from "../DocumentDecorations";
+import { SelectionManager } from "./SelectionManager";
 
 export namespace DragManager {
     export let rootId = "root";
@@ -33,7 +33,7 @@ export namespace DragManager {
     }
 
     export class DropEvent {
-        constructor(readonly x: number, readonly y: number, readonly data: {[ id: string ]: any}) {}
+        constructor(readonly x: number, readonly y: number, readonly data: { [ id: string ]: any }) { }
     }
 
     export interface DropHandlers {
@@ -56,7 +56,10 @@ export namespace DragManager {
         };
     }
 
-    export function StartDrag(ele: HTMLElement, dragData: {[ id: string ]: any}, options: DragOptions) {
+
+    let _lastPointerX: number = 0;
+    let _lastPointerY: number = 0;
+    export function StartDrag(ele: HTMLElement, dragData: { [ id: string ]: any }, options: DragOptions) {
         if (!dragDiv) {
             const root = document.getElementById(rootId);
             if (!root) {
@@ -76,6 +79,8 @@ export namespace DragManager {
         dragElement.style.transformOrigin = "0 0";
         dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
         dragDiv.appendChild(dragElement);
+        _lastPointerX = dragData[ "xOffset" ] + rect.left;
+        _lastPointerY = dragData[ "yOffset" ] + rect.top;
 
         let hideSource = false;
         if (typeof options.hideSource === "boolean") {
@@ -91,8 +96,8 @@ export namespace DragManager {
         const moveHandler = (e: PointerEvent) => {
             e.stopPropagation();
             e.preventDefault();
-            x += e.movementX;
-            y += e.movementY;
+            x += e.clientX - _lastPointerX; _lastPointerX = e.clientX;
+            y += e.clientY - _lastPointerY; _lastPointerY = e.clientY;
             dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
         };
         const upHandler = (e: PointerEvent) => {
@@ -107,7 +112,7 @@ export namespace DragManager {
         document.addEventListener("pointerup", upHandler);
     }
 
-    function FinishDrag(dragEle: HTMLElement, e: PointerEvent, options: DragOptions, dragData: {[ index: string ]: any}) {
+    function FinishDrag(dragEle: HTMLElement, e: PointerEvent, options: DragOptions, dragData: { [ index: string ]: any }) {
         dragDiv.removeChild(dragEle);
         const target = document.elementFromPoint(e.x, e.y);
         if (!target) {
diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx
index cdcdf3bf1..19b212bde 100644
--- a/src/views/collections/CollectionDockingView.tsx
+++ b/src/views/collections/CollectionDockingView.tsx
@@ -77,6 +77,7 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
     }
 
     public static BORDER_WIDTH = 2;
+    public static TAB_HEADER_HEIGHT = 20;
 
     @computed
     public get active(): boolean {
@@ -131,7 +132,6 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         if (component === "button") {
             return <button>{node.getName()}</button>;
         }
-        console.log("Gettting " + component);
         const { fieldKey, Document: Document } = this.props;
         const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
         if (component === "doc1" && value.length > 0) {
@@ -153,8 +153,10 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
 
         const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
         // bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes.
-        var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0));
-        var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0));
+        var s = this.props.ContainingDocumentView!.ScalingToScreenSpace;
+        var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s;
+        var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s;
+
         return (
             <div className="border" style={{
                 borderStyle: "solid",
@@ -162,8 +164,8 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
             }}>
                 <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef}
                     style={{
-                        width: "100%",
-                        height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`,
+                        width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH,
+                        height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH
                     }} >
                     <FlexLayout.Layout model={this._model} factory={this.factory} />
                 </div>
diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx
index 5ce64b347..7ba62e0b8 100644
--- a/src/views/nodes/DocumentView.tsx
+++ b/src/views/nodes/DocumentView.tsx
@@ -178,8 +178,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             ContainerY = LocalY - CollectionFreeFormView.BORDER_WIDTH;
         }
 
-        let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
-        let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
+        let dockingViewChromeHack = this.props.ContainingCollectionView instanceof CollectionDockingView;
+        let Xx = dockingViewChromeHack ? 0 : this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
+        let Yy = dockingViewChromeHack ? CollectionDockingView.TAB_HEADER_HEIGHT : this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
         let Ss = this.props.Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1));
         let Panxx = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0));
         let Panyy = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0));
@@ -196,10 +197,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         // if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) {
         //     return { ScreenX: undefined, ScreenY: undefined };
         // }
+        let dockingViewChromeHack = this.props.ContainingCollectionView instanceof CollectionDockingView;
         let W = CollectionFreeFormView.BORDER_WIDTH; // this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0));
         let H = CollectionFreeFormView.BORDER_WIDTH;
-        let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
-        let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
+        let Xx = dockingViewChromeHack ? 0 : this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
+        let Yy = dockingViewChromeHack ? CollectionDockingView.TAB_HEADER_HEIGHT : this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
         let parentX: Opt<any> = (localX - W) * Ss + (Xx + Panxx) + W;
         let parentY: Opt<any> = (localY - H) * Ss + (Yy + Panyy) + H;
 
-- 
cgit v1.2.3-70-g09d2


From 3032decb31fa6c891520923ed49612ba5ecf7c93 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 30 Jan 2019 02:30:15 -0500
Subject: added golden layout.  doesn't work much better than the flex layout.

---
 package-lock.json                               |  43 +++++++++
 package.json                                    |   2 +
 src/Main.tsx                                    |   9 +-
 src/views/collections/CollectionDockingView.tsx | 112 +++++++++++++++++++++---
 src/views/nodes/DocumentView.tsx                |   5 +-
 5 files changed, 155 insertions(+), 16 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/package-lock.json b/package-lock.json
index b04a31ccd..192a10f9e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,14 @@
       "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==",
       "dev": true
     },
+    "@types/jquery": {
+      "version": "3.3.29",
+      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.29.tgz",
+      "integrity": "sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ==",
+      "requires": {
+        "@types/sizzle": "2.3.2"
+      }
+    },
     "@types/mocha": {
       "version": "5.2.5",
       "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz",
@@ -136,6 +144,11 @@
         "@types/react": "16.7.17"
       }
     },
+    "@types/sizzle": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
+      "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg=="
+    },
     "@types/uuid": {
       "version": "3.4.4",
       "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz",
@@ -4331,6 +4344,11 @@
         "to-regex": "3.0.2"
       }
     },
+    "native-promise-only": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
+      "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE="
+    },
     "negotiator": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@@ -8314,6 +8332,15 @@
       "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
       "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
     },
+    "react-mosaic": {
+      "version": "0.0.20",
+      "resolved": "https://registry.npmjs.org/react-mosaic/-/react-mosaic-0.0.20.tgz",
+      "integrity": "sha1-pSSr8uzyi5r2sh1NNQ/veCLvMJ4=",
+      "requires": {
+        "prop-types": "15.6.2",
+        "threads": "0.8.1"
+      }
+    },
     "read-pkg": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
@@ -9490,6 +9517,22 @@
         }
       }
     },
+    "threads": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/threads/-/threads-0.8.1.tgz",
+      "integrity": "sha1-40ARW1lHMW0vfuMSPEwsW/nHbXI=",
+      "requires": {
+        "eventemitter3": "2.0.3",
+        "native-promise-only": "0.8.1"
+      },
+      "dependencies": {
+        "eventemitter3": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
+          "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
+        }
+      }
+    },
     "through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
diff --git a/package.json b/package.json
index f0ab9ab35..3dfa158d8 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
   },
   "dependencies": {
     "@fortawesome/fontawesome-svg-core": "^1.2.12",
+    "@types/jquery": "^3.3.29",
     "@types/prosemirror-commands": "^1.0.1",
     "@types/prosemirror-history": "^1.0.1",
     "@types/prosemirror-keymap": "^1.0.1",
@@ -57,6 +58,7 @@
     "react-dom": "^16.7.0",
     "react-golden-layout": "^1.0.6",
     "react-jsx-parser": "^1.13.0",
+    "react-mosaic": "0.0.20",
     "uuid": "^3.3.2"
   }
 }
diff --git a/src/Main.tsx b/src/Main.tsx
index c636ee42e..118e745cd 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -44,7 +44,8 @@ ReactDOM.render((
         <DocumentView Document={mainContainer} ContainingCollectionView={undefined} ContainingDocumentView={undefined} />
         <DocumentDecorations />
         <ContextMenu />
-    </div>), document.getElementById('root'));
+    </div>),
+    document.getElementById('root'));
 
 runInAction(() => {
     let doc1 = Documents.TextDocument("Hello world");
@@ -63,7 +64,7 @@ runInAction(() => {
     });
     let docset2 = new Array<Document>(doc4, doc1, doc3);
     let doc6 = Documents.DockDocument(docset2, {
-        x: 350, y: 100
+        x: 350, y: 100, width: 600, height: 600
     });
     let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField);
     if (!mainNodes) {
@@ -72,9 +73,9 @@ runInAction(() => {
     }
     // mainNodes.Data.push(doc1);
     // mainNodes.Data.push(doc2);
-    mainNodes.Data.push(doc4);
+    //mainNodes.Data.push(doc4);
     // mainNodes.Data.push(doc3);
-    mainNodes.Data.push(doc5);
+    //mainNodes.Data.push(doc5);
     // mainNodes.Data.push(doc1);
     // mainNodes.Data.push(doc2);
     mainNodes.Data.push(doc6);
diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx
index 19b212bde..8dabe4490 100644
--- a/src/views/collections/CollectionDockingView.tsx
+++ b/src/views/collections/CollectionDockingView.tsx
@@ -13,7 +13,9 @@ import { ContextMenu } from "../ContextMenu";
 import "./CollectionDockingView.scss"
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-// import GoldenLayout, { Row, Stack, createGoldenLayoutComponent } from '../../../node_modules/react-golden-layout/src/internal/';
+import * as GoldenLayout from "golden-layout";
+import { CollectionFreeFormView } from './CollectionFreeFormView';
+import * as ReactDOM from 'react-dom';
 
 @observer
 export class CollectionDockingView extends React.Component<CollectionViewProps> {
@@ -87,15 +89,86 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         return isSelected || childSelected || topMost;
     }
 
+    myLayout: any = null;
     componentDidMount() {
-        // if (this._containerRef.current) {
-        //     DragManager.MakeDropTarget(this._containerRef.current, {
-        //         handlers: {
-        //             drop: this.drop
-        //         }
-        //     });
-        // }
+
+        const { fieldKey, Document: Document } = this.props;
+
+        const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
+        if (this._containerRef.current) {
+            if (this.myLayout == null) {
+                this.myLayout = new GoldenLayout({
+                    content: [ {
+                        type: 'row',
+                        content: [ {
+                            type: 'component',
+                            componentName: 'documentViewComponent',
+                            componentState: { x: 0 }
+                        }, {
+                            type: 'component',
+                            componentName: 'documentViewComponent',
+                            componentState: { x: 1 }
+                        } ]
+                    } ]
+                });
+
+                this.myLayout.on('stackCreated', function (stack: any) {
+                    stack
+                        .header
+                        .controlsContainer
+                        .find('.lm_close') //get the close icon
+                        .off('click') //unbind the current click handler
+                        .click(function () {
+                            //add your own
+                            if (confirm('really close this?')) {
+                                stack.remove();
+                            }
+                        });
+                });
+
+                this.myLayout.on('tabCreated', function (tab: any) {
+                    tab
+                        .closeElement
+                        .off('click') //unbind the current click handler
+                        .click(function () {
+                            //add your own
+                            if (confirm('really close this?')) {
+                                tab.contentItem.remove();
+                            }
+                        });
+                });
+
+                var me = this;
+                var cv = this.props.ContainingDocumentView;
+                this.myLayout.registerComponent('documentViewComponent', this.registerComponentWithCallback);
+
+                this.myLayout.container = this._containerRef.current;
+                this.myLayout.init();
+            }
+        }
     }
+    private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })();
+
+    private registerComponentWithCallback = (container: any, state: any) => {
+        const { fieldKey, Document: Document } = this.props;
+        const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
+        var containingDiv = "component_" + this.nextId();
+        container.getElement().html("<div id='" + containingDiv + "'></div>");
+        // var new_state = Object.assign({}, state);
+        // new_state[ "location" ] = containingDiv;
+        // container.setState(new_state);
+        var me = this;
+        var docToRender = value[ state.x ];
+        setTimeout(function () {
+            ReactDOM.render((
+                <div style={{ display: "grid" }}>
+                    <DocumentView key={docToRender.Id} Document={docToRender} ContainingCollectionView={me} ContainingDocumentView={me.props.ContainingDocumentView} />
+                </div>
+            ),
+                document.getElementById(containingDiv)
+            )
+        }, 1);
+    };
 
 
     @action
@@ -148,7 +221,9 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         }
     }
 
+
     render() {
+
         const { fieldKey, Document: Document } = this.props;
 
         const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
@@ -157,6 +232,8 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s;
         var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s;
 
+
+
         return (
             <div className="border" style={{
                 borderStyle: "solid",
@@ -164,12 +241,25 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
             }}>
                 <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef}
                     style={{
-                        width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH,
-                        height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH
+                        width: "100%",
+                        height: "100%"
                     }} >
-                    <FlexLayout.Layout model={this._model} factory={this.factory} />
                 </div>
             </div>
         );
+        // return (
+        //     <div className="border" style={{
+        //         borderStyle: "solid",
+        //         borderWidth: `${CollectionDockingView.BORDER_WIDTH}px`,
+        //     }}>
+        //         <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef}
+        //             style={{
+        //                 width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH,
+        //                 height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH
+        //             }} >
+        //             <FlexLayout.Layout model={this._model} factory={this.factory} />
+        //         </div>
+        //     </div>
+        // );
     }
 }
\ No newline at end of file
diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx
index 7ba62e0b8..38e695ed2 100644
--- a/src/views/nodes/DocumentView.tsx
+++ b/src/views/nodes/DocumentView.tsx
@@ -93,6 +93,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     private _downX: number = 0;
     private _downY: number = 0;
 
+    constructor(props: DocumentViewProps) {
+        super(props);
+    }
     get screenRect(): ClientRect | DOMRect {
         if (this._mainCont.current) {
             return this._mainCont.current.getBoundingClientRect();
@@ -223,7 +226,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._contextMenuCanOpen = e.button == 2;
-        if (this.active) {
+        if (this.active && !e.isDefaultPrevented()) {
             e.stopPropagation();
             if (e.buttons === 2) {
                 e.preventDefault();
-- 
cgit v1.2.3-70-g09d2


From 13518146c955f012a6f6cd2b802f80aeeffcd58d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 30 Jan 2019 09:45:15 -0500
Subject: docking for top-level only

---
 src/DocumentDecorations.tsx                      | 10 ++++++----
 src/Main.tsx                                     |  8 ++++----
 src/views/collections/CollectionFreeFormView.tsx |  5 ++++-
 src/views/nodes/DocumentView.tsx                 | 10 ++++------
 4 files changed, 18 insertions(+), 15 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/src/DocumentDecorations.tsx b/src/DocumentDecorations.tsx
index d71cda539..1cf875ea5 100644
--- a/src/DocumentDecorations.tsx
+++ b/src/DocumentDecorations.tsx
@@ -3,6 +3,7 @@ import React = require("react");
 import { SelectionManager } from "./util/SelectionManager";
 import { observer } from "mobx-react";
 import './DocumentDecorations.scss'
+import { CollectionFreeFormView } from "./views/collections/CollectionFreeFormView";
 
 @observer
 export class DocumentDecorations extends React.Component {
@@ -20,12 +21,13 @@ export class DocumentDecorations extends React.Component {
     @computed
     get Bounds(): { x: number, y: number, b: number, r: number } {
         return SelectionManager.SelectedDocuments().reduce((bounds, element) => {
+            if (element.props.ContainingCollectionView != undefined &&
+                !(element.props.ContainingCollectionView instanceof CollectionFreeFormView)) {
+                return bounds;
+            }
             var spt = element.TransformToScreenPoint(0, 0);
             var bpt = element.TransformToScreenPoint(element.width, element.height);
-            if (spt.ScreenX == undefined || spt.ScreenY == undefined ||
-                bpt.ScreenX == undefined || bpt.ScreenY == undefined)
-                return { x: bounds.x, y: bounds.y, r: bounds.r, b: bounds.b };
-            else return {
+            return {
                 x: Math.min(spt.ScreenX, bounds.x), y: Math.min(spt.ScreenY, bounds.y),
                 r: Math.max(bpt.ScreenX, bounds.r), b: Math.max(bpt.ScreenY, bounds.b)
             }
diff --git a/src/Main.tsx b/src/Main.tsx
index 118e745cd..33e38004f 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -22,7 +22,7 @@ configure({
 });
 
 const mainNodeCollection = new Array<Document>();
-let mainContainer = Documents.CollectionDocument(mainNodeCollection, {
+let mainContainer = Documents.DockDocument(mainNodeCollection, {
     x: 0, y: 0, width: window.screen.width, height: window.screen.height
 })
 
@@ -73,10 +73,10 @@ runInAction(() => {
     }
     // mainNodes.Data.push(doc1);
     // mainNodes.Data.push(doc2);
-    //mainNodes.Data.push(doc4);
+    mainNodes.Data.push(doc4);
     // mainNodes.Data.push(doc3);
-    //mainNodes.Data.push(doc5);
+    mainNodes.Data.push(doc5);
     // mainNodes.Data.push(doc1);
     // mainNodes.Data.push(doc2);
-    mainNodes.Data.push(doc6);
+    //mainNodes.Data.push(doc6);
 });
\ No newline at end of file
diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx
index c84b8e3e5..736bcb786 100644
--- a/src/views/collections/CollectionFreeFormView.tsx
+++ b/src/views/collections/CollectionFreeFormView.tsx
@@ -13,6 +13,7 @@ import { ContextMenu } from "../ContextMenu";
 import { DragManager } from "../../util/DragManager";
 import "./CollectionFreeFormView.scss";
 import { Utils } from "../../Utils";
+import { CollectionDockingView } from "./CollectionDockingView";
 
 @observer
 export class CollectionFreeFormView extends React.Component<CollectionViewProps> {
@@ -29,7 +30,9 @@ export class CollectionFreeFormView extends React.Component<CollectionViewProps>
     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;
+        var topMost = this.props.ContainingDocumentView != undefined && (
+            this.props.ContainingDocumentView.props.ContainingCollectionView == undefined ||
+            this.props.ContainingDocumentView.props.ContainingCollectionView instanceof CollectionDockingView);
         return isSelected || childSelected || topMost;
     }
 
diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx
index 38e695ed2..86d5ed305 100644
--- a/src/views/nodes/DocumentView.tsx
+++ b/src/views/nodes/DocumentView.tsx
@@ -196,17 +196,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     //
     // Converts a point in the coordinate space of a document to a screen space coordinate.
     //
-    public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: Opt<number>, ScreenY: Opt<number> } {
-        // if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) {
-        //     return { ScreenX: undefined, ScreenY: undefined };
-        // }
+    public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: number, ScreenY: number } {
+
         let dockingViewChromeHack = this.props.ContainingCollectionView instanceof CollectionDockingView;
         let W = CollectionFreeFormView.BORDER_WIDTH; // this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0));
         let H = CollectionFreeFormView.BORDER_WIDTH;
         let Xx = dockingViewChromeHack ? 0 : this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0));
         let Yy = dockingViewChromeHack ? CollectionDockingView.TAB_HEADER_HEIGHT : this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0));
-        let parentX: Opt<any> = (localX - W) * Ss + (Xx + Panxx) + W;
-        let parentY: Opt<any> = (localY - H) * Ss + (Yy + Panyy) + H;
+        let parentX = (localX - W) * Ss + (Xx + Panxx) + W;
+        let parentY = (localY - H) * Ss + (Yy + Panyy) + H;
 
         // if this collection view is nested within another collection view, then 
         // first transform the local point into the parent collection's coordinate space.
-- 
cgit v1.2.3-70-g09d2


From 022b8a3a562a30062f714f9ce7082b8983a89513 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 30 Jan 2019 10:29:59 -0500
Subject: cleaned up docking a little

---
 src/Main.tsx                                    | 39 +++++++++++++------------
 src/documents/Documents.ts                      |  4 +++
 src/fields/Key.ts                               |  1 +
 src/views/collections/CollectionDockingView.tsx | 30 ++++++++-----------
 4 files changed, 38 insertions(+), 36 deletions(-)

(limited to 'src/Main.tsx')

diff --git a/src/Main.tsx b/src/Main.tsx
index 33e38004f..02b866753 100644
--- a/src/Main.tsx
+++ b/src/Main.tsx
@@ -23,7 +23,7 @@ configure({
 
 const mainNodeCollection = new Array<Document>();
 let mainContainer = Documents.DockDocument(mainNodeCollection, {
-    x: 0, y: 0, width: window.screen.width, height: window.screen.height
+    x: 0, y: 0, width: window.screen.width, height: window.screen.height, title: "main container"
 })
 
 window.addEventListener("drop", function (e) {
@@ -38,17 +38,10 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
     }
 }), true)
 
-ReactDOM.render((
-    <div style={{ display: "grid" }}>
-        <h1>Dash Web</h1>
-        <DocumentView Document={mainContainer} ContainingCollectionView={undefined} ContainingDocumentView={undefined} />
-        <DocumentDecorations />
-        <ContextMenu />
-    </div>),
-    document.getElementById('root'));
 
-runInAction(() => {
-    let doc1 = Documents.TextDocument("Hello world");
+//runInAction(() => 
+{
+    let doc1 = Documents.TextDocument("Hello world", { title: "hello" });
     let doc2 = doc1.MakeDelegate();
     doc2.SetField(KS.X, new NumberField(150));
     doc2.SetField(KS.Y, new NumberField(20));
@@ -57,19 +50,18 @@ runInAction(() => {
     });
     let docset = new Array<Document>(doc1, doc2, doc3);
     let doc4 = Documents.CollectionDocument(docset, {
-        x: 0, y: 400
+        x: 0, y: 400, title: "mini collection"
     });
     let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
-        x: 650, y: 500, width: 600, height: 600
+        x: 650, y: 500, width: 600, height: 600, title: "cat"
     });
     let docset2 = new Array<Document>(doc4, doc1, doc3);
     let doc6 = Documents.DockDocument(docset2, {
-        x: 350, y: 100, width: 600, height: 600
+        x: 350, y: 100, width: 600, height: 600, title: "docking collection"
     });
-    let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField);
+    let mainNodes = null;// mainContainer.GetFieldT(KeyStore.Data, ListField);
     if (!mainNodes) {
         mainNodes = new ListField<Document>();
-        mainContainer.SetField(KeyStore.Data, mainNodes);
     }
     // mainNodes.Data.push(doc1);
     // mainNodes.Data.push(doc2);
@@ -77,6 +69,17 @@ runInAction(() => {
     // mainNodes.Data.push(doc3);
     mainNodes.Data.push(doc5);
     // mainNodes.Data.push(doc1);
-    // mainNodes.Data.push(doc2);
+    mainNodes.Data.push(doc2);
     //mainNodes.Data.push(doc6);
-});
\ No newline at end of file
+    mainContainer.SetField(KeyStore.Data, mainNodes);
+}
+//);
+
+ReactDOM.render((
+    <div style={{ display: "grid" }}>
+        <h1>Dash Web</h1>
+        <DocumentView Document={mainContainer} ContainingCollectionView={undefined} ContainingDocumentView={undefined} />
+        <DocumentDecorations />
+        <ContextMenu />
+    </div>),
+    document.getElementById('root'));
\ No newline at end of file
diff --git a/src/documents/Documents.ts b/src/documents/Documents.ts
index 7cc5de3a3..318a6d7a3 100644
--- a/src/documents/Documents.ts
+++ b/src/documents/Documents.ts
@@ -11,6 +11,7 @@ interface DocumentOptions {
     y?: number;
     width?: number;
     height?: number;
+    title?: string;
 }
 
 export namespace Documents {
@@ -27,6 +28,9 @@ export namespace Documents {
         if (options.height) {
             doc.SetFieldValue(KeyStore.Height, options.height, NumberField);
         }
+        if (options.title) {
+            doc.SetFieldValue(KeyStore.Title, options.title, TextField);
+        }
         doc.SetFieldValue(KeyStore.Scale, 1, NumberField);
         doc.SetFieldValue(KeyStore.PanX, 0, NumberField);
         doc.SetFieldValue(KeyStore.PanY, 0, NumberField);
diff --git a/src/fields/Key.ts b/src/fields/Key.ts
index f8418d5c0..61b3cdd37 100644
--- a/src/fields/Key.ts
+++ b/src/fields/Key.ts
@@ -34,6 +34,7 @@ export namespace KeyStore {
     export let Prototype = new Key("Prototype");
     export let X = new Key("X");
     export let Y = new Key("Y");
+    export let Title = new Key("Title");
     export let PanX = new Key("PanX");
     export let PanY = new Key("PanY");
     export let Scale = new Key("Scale");
diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx
index 7dfec1c54..e07e53168 100644
--- a/src/views/collections/CollectionDockingView.tsx
+++ b/src/views/collections/CollectionDockingView.tsx
@@ -16,6 +16,7 @@ import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import * as GoldenLayout from "golden-layout";
 import { CollectionFreeFormView } from './CollectionFreeFormView';
 import * as ReactDOM from 'react-dom';
+import { TextField } from "../../fields/TextField";
 
 @observer
 export class CollectionDockingView extends React.Component<CollectionViewProps> {
@@ -97,18 +98,14 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
         if (this._containerRef.current) {
             if (this.myLayout == null) {
+                var docs = value.map(doc => {
+                    var d = { fcomponent: undefined, type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc } };
+                    return d;
+                });
                 this.myLayout = new GoldenLayout({
                     content: [ {
                         type: 'row',
-                        content: [ {
-                            type: 'component',
-                            componentName: 'documentViewComponent',
-                            componentState: { x: 0 }
-                        }, {
-                            type: 'component',
-                            componentName: 'documentViewComponent',
-                            componentState: { x: 1 }
-                        } ]
+                        content: docs
                     } ]
                 });
 
@@ -127,6 +124,9 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
                 });
 
                 this.myLayout.on('tabCreated', function (tab: any) {
+                    var docToRender = tab.contentItem.config.componentState.doc;
+                    var dflt: string = "<untitled>";
+                    tab.setTitle(docToRender.GetFieldValue(KeyStore.Title, TextField, dflt));
                     tab
                         .closeElement
                         .off('click') //unbind the current click handler
@@ -147,15 +147,11 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
     private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })();
 
     private registerComponentWithCallback = (container: any, state: any) => {
-        const { fieldKey, Document: Document } = this.props;
-        const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []);
         var containingDiv = "component_" + this.nextId();
         container.getElement().html("<div id='" + containingDiv + "'></div>");
-        // var new_state = Object.assign({}, state);
-        // new_state[ "location" ] = containingDiv;
-        // container.setState(new_state);
         var me = this;
-        var docToRender = value[ state.x ];
+        var docToRender = state.doc;
+        // bcz: ugly way to do this.  wait until containoing div has been created, then look it up in the DOM and render our document view into it.
         setTimeout(function () {
             ReactDOM.render((
                 <div style={{ display: "grid" }}>
@@ -164,7 +160,7 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
             ),
                 document.getElementById(containingDiv)
             )
-        }, 1);
+        }, 0);
     };
 
 
@@ -229,8 +225,6 @@ export class CollectionDockingView extends React.Component<CollectionViewProps>
         var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s;
         var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s;
 
-
-
         return (
             <div className="border" style={{
                 borderStyle: "solid",
-- 
cgit v1.2.3-70-g09d2

