From a37faf30652c6c593923198ad34e54377e670caf Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 5 Jun 2019 08:09:10 -0400
Subject: fixed full screen text bug.  cleaned up imports

---
 .../views/collections/CollectionTreeView.tsx       | 26 +++++++++++-----------
 1 file changed, 13 insertions(+), 13 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 48da52ffa..9fb1da716 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,25 +1,24 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretDown, faCaretRight, faTrashAlt, faAngleRight } from '@fortawesome/free-solid-svg-icons';
+import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
-import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
-import { EditableView } from "../EditableView";
-import { CollectionSubView } from "./CollectionSubView";
-import "./CollectionTreeView.scss";
-import React = require("react");
-import { Document, listSpec } from '../../../new_fields/Schema';
-import { Cast, StrCast, BoolCast, FieldValue, NumCast } from '../../../new_fields/Types';
 import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
-import { ContextMenu } from '../ContextMenu';
-import { undoBatch } from '../../util/UndoManager';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { CollectionDockingView } from './CollectionDockingView';
-import { DocumentManager } from '../../util/DocumentManager';
+import { Document, listSpec } from '../../../new_fields/Schema';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
 import { Docs } from '../../documents/Documents';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
+import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { EditableView } from "../EditableView";
 import { MainView } from '../MainView';
 import { CollectionViewType } from './CollectionBaseView';
+import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionSubView } from "./CollectionSubView";
+import "./CollectionTreeView.scss";
+import React = require("react");
 
 
 export interface TreeViewProps {
@@ -220,6 +219,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         }
     }
     render() {
+        trace();
         let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
         if (!this.childDocs) {
             return (null);
-- 
cgit v1.2.3-70-g09d2


From ecd6d50cf6dd89f7d860096037efee81230eb535 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 7 Jun 2019 19:42:52 -0400
Subject: from last

---
 .../views/collections/CollectionTreeView.tsx       | 188 +++++++++++++++------
 1 file changed, 137 insertions(+), 51 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 9fb1da716..8dc10bcd1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,7 +6,7 @@ import { observer } from "mobx-react";
 import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { Document, listSpec } from '../../../new_fields/Schema';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types';
 import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
@@ -19,6 +19,7 @@ import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import React = require("react");
+import { Transform } from '../../util/Transform';
 
 
 export interface TreeViewProps {
@@ -27,6 +28,10 @@ export interface TreeViewProps {
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
     addDocTab: (doc: Doc, where: string) => void;
+    addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
+    ScreenToLocalTransform: () => Transform;
+    treeViewId: string;
+    parentKey: string;
 }
 
 export enum BulletType {
@@ -45,23 +50,49 @@ library.add(faCaretRight);
  * Component that takes in a document prop and a boolean whether it's collapsed or not.
  */
 class TreeView extends React.Component<TreeViewProps> {
+    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+    private treedropDisposer?: DragManager.DragDropDisposer;
+    protected createTreeDropTarget = (ele: HTMLDivElement) => {
+        this.treedropDisposer && this.treedropDisposer();
+        if (ele) {
+            this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } });
+        }
+    }
 
+    @observable _isOver: boolean = false;
     @observable _collapsed: boolean = true;
 
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
+    @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight");
+
+    @action onMouseEnter = () => { this._isOver = true; }
+    @action onMouseLeave = () => { this._isOver = false; }
 
-    @undoBatch openRight = async () => {
-        if (this.props.document.dockingConfig) {
-            MainView.Instance.openWorkspace(this.props.document);
-        } else {
-            this.props.addDocTab(this.props.document, "openRight");
+    onPointerEnter = (e: React.PointerEvent): void => {
+        this.props.document.libraryBrush = true;
+        if (e.buttons === 1) {
+            this._header!.current!.className = "treeViewItem-header";
+            document.addEventListener("pointermove", this.onDragMove, true);
         }
     }
-
-    get children() {
-        return Cast(this.props.document.data, listSpec(Doc), []); // bcz: needed?    .filter(doc => FieldValue(doc));
+    onPointerLeave = (e: React.PointerEvent): void => {
+        this.props.document.libraryBrush = false;
+        this._header!.current!.className = "treeViewItem-header";
+        document.removeEventListener("pointermove", this.onDragMove, true);
+    }
+    onDragMove = (e: PointerEvent): void => {
+        this.props.document.libraryBrush = false;
+        let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+        let rect = this._header!.current!.getBoundingClientRect();
+        let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+        let before = x[1] < bounds[1];
+        let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible);
+        this._header!.current!.className = "treeViewItem-header"
+        if (inside && this._bulletType != BulletType.List) this._header!.current!.className = "treeViewItem-header-inside";
+        else if (before) this._header!.current!.className = "treeViewItem-header-above";
+        else if (!before) this._header!.current!.className = "treeViewItem-header-below";
+        e.stopPropagation();
     }
-
     onPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
     }
@@ -69,19 +100,17 @@ class TreeView extends React.Component<TreeViewProps> {
     @action
     remove = (document: Document, key: string) => {
         let children = Cast(this.props.document[key], listSpec(Doc), []);
-        if (children) {
-            children.splice(children.indexOf(document), 1);
-        }
+        children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1);
     }
 
     @action
-    move: DragManager.MoveFunction = (document, target, addDoc) => {
-        if (this.props.document === target) {
-            return true;
+    move: DragManager.MoveFunction = (document: Doc, target: Doc, addDoc) => {
+        if (this.props.document !== target) {
+            //TODO This should check if it was removed
+            this.props.deleteDoc(document);
+            return addDoc(document);
         }
-        //TODO This should check if it was removed
-        this.remove(document, "data");
-        return addDoc(document);
+        return true;
     }
 
     renderBullet(type: BulletType) {
@@ -93,22 +122,12 @@ class TreeView extends React.Component<TreeViewProps> {
         }
         return <div className="bullet" onClick={onClicked}>{bullet ? <FontAwesomeIcon icon={bullet} /> : ""} </div>;
     }
-
-    @action
-    onMouseEnter = () => {
-        this._isOver = true;
-    }
-    @observable _isOver: boolean = false;
-    @action
-    onMouseLeave = () => {
-        this._isOver = false;
-    }
     /**
      * Renders the EditableView title element for placement into the tree.
      */
     renderTitle() {
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = SetupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.dropAction);
+        let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true);
         let editableView = (titleString: string) =>
             (<EditableView
                 oneLine={!this._isOver ? true : false}
@@ -129,9 +148,9 @@ class TreeView extends React.Component<TreeViewProps> {
                 {/* <FontAwesomeIcon icon="angle-right" size="lg" /> */}
             </div>);
         return (
-            <div className="docContainer" ref={reference} onPointerDown={onItemDown} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}
+            <div className="docContainer" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}
                 style={{ background: BoolCast(this.props.document.protoBrush, false) ? "#06123232" : BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0" }}
-                onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+            >
                 {editableView(StrCast(this.props.document.title))}
                 {openRight}
                 {/* {<div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div>} */}
@@ -152,14 +171,51 @@ class TreeView extends React.Component<TreeViewProps> {
             } else {
                 ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
             }
-            ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+            ContextMenu.Instance.displayMenu(e.pageX - 156, e.pageY - 15);
+            e.stopPropagation();
+        }
+    }
+    treeDrop = (e: Event, de: DragManager.DropEvent) => {
+        let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+        let rect = this._header!.current!.getBoundingClientRect();
+        let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+        let before = x[1] < bounds[1];
+        let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible);
+        if (de.data instanceof DragManager.DocumentDragData) {
+            let addDoc = (doc: Doc) => this.props.addDocument(doc, this.props.document, before);
+            if (inside) {
+                let docList = Cast(this.props.document.data, listSpec(Doc));
+                if (docList !== undefined) {
+                    addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }
+                }
+            }
+            let added = false;
+            if (de.data.dropAction || de.data.userDropAction) {
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false);
+            } else if (de.data.moveDocument) {
+                let movedDocs = de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments;
+                added = movedDocs.reduce((added: boolean, d) =>
+                    de.data.moveDocument(d, this.props.document, addDoc) || added, false);
+            } else {
+                added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false);
+            }
             e.stopPropagation();
+            return added;
         }
+        return false;
     }
 
-    onPointerEnter = (e: React.PointerEvent): void => { this.props.document.libraryBrush = true; };
-    onPointerLeave = (e: React.PointerEvent): void => { this.props.document.libraryBrush = false; };
+    public static AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean) {
+        let list = Cast(target[key], listSpec(Doc));
+        if (list) {
+            let ind = relativeTo ? list.indexOf(relativeTo) : -1;
+            if (ind === -1) list.push(doc);
+            else list.splice(before ? ind : ind + 1, 0, doc);
+        }
+        return true;
+    }
 
+    _bulletType: BulletType = BulletType.List;
     render() {
         let bulletType = BulletType.List;
         let contentElement: (JSX.Element | null)[] = [];
@@ -170,16 +226,17 @@ class TreeView extends React.Component<TreeViewProps> {
         }
         keys.map(key => {
             let docList = DocListCast(this.props.document[key]);
+            let remDoc = (doc: Doc) => this.remove(doc, key);
+            let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before);
             let doc = Cast(this.props.document[key], Doc);
             if (doc instanceof Doc || docList.length) {
                 if (!this._collapsed) {
                     bulletType = BulletType.Collapsible;
-                    let spacing = (key === "data") ? 0 : -10;
                     contentElement.push(<ul key={key + "more"}>
                         {(key === "data") ? (null) :
                             <span className="collectionTreeView-keyHeader" style={{ display: "block", marginTop: "7px" }} key={key}>{key}</span>}
-                        <div style={{ display: "block", marginTop: `${spacing}px` }}>
-                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, key !== "data", (doc: Doc) => this.remove(doc, key), this.move, this.props.dropAction, this.props.addDocTab)}
+                        <div style={{ display: "block" }}>
+                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, this.props.treeViewId, key, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform)}
                         </div>
                     </ul >);
                 } else {
@@ -187,29 +244,52 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         });
+        this._bulletType = bulletType;
         return <div className="treeViewItem-container"
+            ref={this.createTreeDropTarget}
             onContextMenu={this.onWorkspaceContextMenu}>
             <li className="collection-child">
-                {this.renderBullet(bulletType)}
-                {this.renderTitle()}
+                <div className="treeViewItem-header" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                    {this.renderBullet(bulletType)}
+                    {this.renderTitle()}
+                </div>
                 {contentElement}
             </li>
         </div>;
     }
-    public static GetChildElements(docs: Doc[], allowMinimized: boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void) {
-        return docs.filter(child => !child.excludeFromLibrary && (allowMinimized || !child.isMinimized)).map(child =>
-            <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} />);
+    public static GetChildElements(
+        docs: Doc[],
+        treeViewId: string,
+        key: string,
+        add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean,
+        remove: ((doc: Doc) => void),
+        move: DragManager.MoveFunction,
+        dropAction: dropActionType,
+        addDocTab: (doc: Doc, where: string) => void,
+        screenToLocalXf: () => Transform
+    ) {
+        return docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)).map(child =>
+            <TreeView document={child} treeViewId={treeViewId} key={child[Id]} deleteDoc={remove} addDocument={add} moveDocument={move}
+                dropAction={dropAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} parentKey={key} />);
     }
 }
 
 @observer
 export class CollectionTreeView extends CollectionSubView(Document) {
+    private treedropDisposer?: DragManager.DragDropDisposer;
+    protected createTreeDropTarget = (ele: HTMLDivElement) => {
+        if (this.treedropDisposer) {
+            this.treedropDisposer();
+        }
+        if (ele) {
+            this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        }
+    }
+
     @action
     remove = (document: Document) => {
-        let children = Cast(this.props.Document.data, listSpec(Doc), []);
-        if (children) {
-            children.splice(children.indexOf(document), 1);
-        }
+        let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+        children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1);
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
@@ -218,20 +298,26 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)) });
         }
     }
+
+    onTreeDrop = (e: React.DragEvent) => {
+        this.onDrop(e, {});
+    }
     render() {
-        trace();
-        let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
+        let dropAction = Cast(this.props.Document.dropAction, "string") as dropActionType;
         if (!this.childDocs) {
             return (null);
         }
-        let childElements = TreeView.GetChildElements(this.childDocs, false, this.remove, this.props.moveDocument, dropAction, this.props.addDocTab);
+        let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+        let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
+        let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform);
 
         return (
             <div id="body" className="collectionTreeView-dropTarget"
                 style={{ borderRadius: "inherit" }}
                 onContextMenu={this.onContextMenu}
                 onWheel={(e: React.WheelEvent) => this.props.isSelected() && e.stopPropagation()}
-                onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
+                onDrop={this.onTreeDrop}
+                ref={this.createTreeDropTarget}>
                 <div className="coll-title">
                     <EditableView
                         contents={this.props.Document.title}
-- 
cgit v1.2.3-70-g09d2


From c56851026c4054cde26ea36a9a24db14fac6c49c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 10 Jun 2019 13:03:47 -0400
Subject: fixes for treeview highlighting, templates, field deletion,
 overlaytext box positioning

---
 src/Utils.ts                                       |  2 +-
 src/client/util/DragManager.ts                     |  6 ++++--
 src/client/util/SelectionManager.ts                |  9 +++++---
 src/client/views/MainOverlayTextBox.tsx            |  8 +++++---
 src/client/views/Templates.tsx                     | 16 ++++-----------
 .../views/collections/CollectionTreeView.tsx       | 24 ++++++++++++++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +--
 src/client/views/nodes/DocumentView.tsx            |  6 +++---
 src/new_fields/util.ts                             |  4 ++--
 9 files changed, 42 insertions(+), 36 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 611c61135..e8a80bdc3 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -15,7 +15,7 @@ export class Utils {
         return v5(seed, v5.URL);
     }
 
-    public static GetScreenTransform(ele: HTMLElement): { scale: number, translateX: number, translateY: number } {
+    public static GetScreenTransform(ele?: HTMLElement): { scale: number, translateX: number, translateY: number } {
         if (!ele) {
             return { scale: 1, translateX: 1, translateY: 1 };
         }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4c9f798a8..8ee1ad2af 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,9 +1,10 @@
-import { action, runInAction } from "mobx";
+import { action, runInAction, observable } from "mobx";
 import { Doc, DocListCastAsync } from "../../new_fields/Doc";
 import { Cast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
+import { SelectionManager } from "./SelectionManager";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc>, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, options?: any, dontHideOnDrop?: boolean) {
@@ -194,7 +195,7 @@ export namespace DragManager {
             dragDiv.style.pointerEvents = "none";
             DragManager.Root().appendChild(dragDiv);
         }
-
+        SelectionManager.SetIsDragging(true);
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
         let xs: number[] = [];
@@ -293,6 +294,7 @@ export namespace DragManager {
         };
 
         let hideDragElements = () => {
+            SelectionManager.SetIsDragging(false);
             dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             eles.map(ele => (ele.hidden = false));
         };
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0e22d576c..ddad4b818 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,4 +1,4 @@
-import { observable, action } from "mobx";
+import { observable, action, runInAction } from "mobx";
 import { Doc } from "../../new_fields/Doc";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
@@ -6,8 +6,8 @@ import { NumCast } from "../../new_fields/Types";
 
 export namespace SelectionManager {
     class Manager {
-        @observable
-        SelectedDocuments: Array<DocumentView> = [];
+        @observable IsDragging: boolean = false;
+        @observable SelectedDocuments: Array<DocumentView> = [];
 
         @action
         SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
@@ -51,6 +51,9 @@ export namespace SelectionManager {
         if (found) manager.SelectDoc(found, false);
     }
 
+    export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
+    export function GetIsDragging() { return manager.IsDragging; }
+
     export function SelectedDocuments(): Array<DocumentView> {
         return manager.SelectedDocuments;
     }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 6bbd70a25..0eb3e9707 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -1,7 +1,7 @@
 import { action, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { emptyFunction, returnTrue, returnZero } from '../../Utils';
+import { emptyFunction, returnTrue, returnZero, Utils } from '../../Utils';
 import { DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import "normalize.css";
@@ -29,7 +29,8 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         MainOverlayTextBox.Instance = this;
         reaction(() => FormattedTextBox.InputBoxOverlay,
             (box?: FormattedTextBox) => {
-                if (box) this.setTextDoc(box.props.fieldKey, box.CurrentDiv, box.props.ScreenToLocalTransform);
+                let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
+                if (box) this.setTextDoc(box.props.fieldKey, box.CurrentDiv, () => new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale));
                 else this.setTextDoc();
             });
     }
@@ -93,9 +94,10 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) {
             let textRect = this._textTargetDiv.getBoundingClientRect();
             let s = this._textXf().Scale;
+            let auto = FormattedTextBox.InputBoxOverlay.props.height;
             return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${textRect.top}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
                 <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
-                    style={{ width: `${textRect.width * s}px`, height: `${textRect.height * s}px` }}>
+                    style={{ width: `${textRect.width * s}px`, height: auto ? "auto" : `${textRect.height * s}px` }}>
                     <FormattedTextBox color={`${this._textColor}`} fieldKey={this._textFieldKey} hideOnLeave={this._textHideOnLeave} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
                         ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} />
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 8943bbcaf..3cd525afa 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -41,21 +41,13 @@ export namespace Templates {
 
     export const Caption = new Template("Caption", TemplatePosition.OutterBottom,
         `<div>
-            <div style="height:100%; width:100%;position:absolute;">{layout}</div>
+            <div style="height:100%; width:100%;">{layout}</div>
             <div style="bottom: 0; font-size:14px; width:100%; position:absolute">
-                <FormattedTextBox {...props} fieldKey={"caption"} hideOnLeave={"true"} />
+                <FormattedTextBox {...props} height="min-content" fieldKey={"caption"} hideOnLeave={"true"} />
             </div>
         </div>` );
 
-    export const ImageTitle = new Template("Image Title", TemplatePosition.InnerTop,
-        `<div style="height:100%">
-            <div style="height:100%; width:100%;position:absolute;">{layout}</div>
-            <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; ">
-                <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
-            </div>
-        </div>` );
-
-    export const TextTitle = new Template("Text Title", TemplatePosition.InnerTop,
+    export const Title = new Template("Title", TemplatePosition.InnerTop,
         `<div style="height:100%">
             <div style="height:25px; width:100%; background-color: rgba(0, 0, 0, .4); color: white; ">
                 <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
@@ -92,7 +84,7 @@ export namespace Templates {
             </div > `);
     }
 
-    export const TemplateList: Template[] = [TextTitle, Header, ImageTitle, Caption, Bullet];
+    export const TemplateList: Template[] = [Title, Header, Caption, Bullet];
 
     export function sortTemplates(a: Template, b: Template) {
         if (a.Position < b.Position) { return -1; }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8dc10bcd1..8a6764c58 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -20,6 +20,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import React = require("react");
 import { Transform } from '../../util/Transform';
+import { SelectionManager } from '../../util/SelectionManager';
 
 
 export interface TreeViewProps {
@@ -32,6 +33,7 @@ export interface TreeViewProps {
     ScreenToLocalTransform: () => Transform;
     treeViewId: string;
     parentKey: string;
+    active: () => boolean;
 }
 
 export enum BulletType {
@@ -69,7 +71,7 @@ class TreeView extends React.Component<TreeViewProps> {
     @action onMouseLeave = () => { this._isOver = false; }
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        this.props.document.libraryBrush = true;
+        this.props.active() && (this.props.document.libraryBrush = true);
         if (e.buttons === 1) {
             this._header!.current!.className = "treeViewItem-header";
             document.addEventListener("pointermove", this.onDragMove, true);
@@ -149,7 +151,10 @@ class TreeView extends React.Component<TreeViewProps> {
             </div>);
         return (
             <div className="docContainer" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}
-                style={{ background: BoolCast(this.props.document.protoBrush, false) ? "#06123232" : BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0" }}
+                style={{
+                    background: BoolCast(this.props.document.protoBrush, false) ? "#06123232" : BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0",
+                    pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
+                }}
             >
                 {editableView(StrCast(this.props.document.title))}
                 {openRight}
@@ -225,18 +230,19 @@ class TreeView extends React.Component<TreeViewProps> {
             while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
         }
         keys.map(key => {
-            let docList = DocListCast(this.props.document[key]);
+            let docList = Cast(this.props.document[key], listSpec(Doc));
             let remDoc = (doc: Doc) => this.remove(doc, key);
             let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before);
             let doc = Cast(this.props.document[key], Doc);
-            if (doc instanceof Doc || docList.length) {
+            if (doc instanceof Doc || docList) {
                 if (!this._collapsed) {
                     bulletType = BulletType.Collapsible;
                     contentElement.push(<ul key={key + "more"}>
                         {(key === "data") ? (null) :
                             <span className="collectionTreeView-keyHeader" style={{ display: "block", marginTop: "7px" }} key={key}>{key}</span>}
                         <div style={{ display: "block" }}>
-                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : docList, this.props.treeViewId, key, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform)}
+                            {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move,
+                                this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active)}
                         </div>
                     </ul >);
                 } else {
@@ -266,11 +272,12 @@ class TreeView extends React.Component<TreeViewProps> {
         move: DragManager.MoveFunction,
         dropAction: dropActionType,
         addDocTab: (doc: Doc, where: string) => void,
-        screenToLocalXf: () => Transform
+        screenToLocalXf: () => Transform,
+        active: () => boolean
     ) {
         return docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)).map(child =>
             <TreeView document={child} treeViewId={treeViewId} key={child[Id]} deleteDoc={remove} addDocument={add} moveDocument={move}
-                dropAction={dropAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} parentKey={key} />);
+                dropAction={dropAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} parentKey={key} active={active} />);
     }
 }
 
@@ -309,7 +316,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         }
         let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
         let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
-        let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform);
+        let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove,
+            moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active);
 
         return (
             <div id="body" className="collectionTreeView-dropTarget"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d23bef2d3..63f24ff53 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -6,7 +6,6 @@ import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
 import { BoolCast, Cast, FieldValue, NumCast } from "../../../../new_fields/Types";
 import { emptyFunction, returnOne } from "../../../../Utils";
-import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { HistoryUtil } from "../../../util/History";
@@ -26,7 +25,6 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { Docs } from "../../../documents/Documents";
 
 export const panZoomSchema = createSchema({
     panX: "number",
@@ -218,6 +216,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     setPan(panX: number, panY: number) {
+
         this.props.Document.panTransformType = "None";
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2ac6d12bf..19f5c7d36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -349,9 +349,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 dst.nativeHeight = src.nativeHeight;
             }
             else {
-                const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
-                const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
-                DocUtils.MakeLink(sourceDoc, destDoc, views.length ? views[0].props.Document : undefined);
+                // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
+                // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
+                DocUtils.MakeLink(sourceDoc, destDoc, this.props.ContainingCollectionView ? this.props.ContainingCollectionView.props.Document : undefined);
                 de.data.droppedDocuments.push(destDoc);
             }
         }
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index fe0da2e7a..8cb1db953 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -6,7 +6,7 @@ import { FieldValue } from "./Types";
 import { RefField } from "./RefField";
 import { ObjectField } from "./ObjectField";
 import { action } from "mobx";
-import { Parent, OnUpdate, Update, Id } from "./FieldSymbols";
+import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols";
 import { ComputedField } from "../fields/ScriptField";
 
 export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
@@ -86,7 +86,7 @@ export function deleteProperty(target: any, prop: string | number | symbol) {
         delete target[prop];
         return true;
     }
-    target[prop] = undefined;
+    target[SelfProxy][prop] = undefined;
     return true;
 }
 
-- 
cgit v1.2.3-70-g09d2

