From 77d66d159d75442ff5635c4bf4843b6155883cc2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 10 Oct 2019 14:04:22 -0400
Subject: removed CollectionVideoView

---
 .../views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index df089eb00..fe92eed10 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -10,7 +10,7 @@ export interface CollectionFreeFormLinkViewProps {
     A: Doc;
     B: Doc;
     LinkDocs: Doc[];
-    addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument: (document: Doc) => boolean;
     removeDocument: (document: Doc) => boolean;
 }
 
-- 
cgit v1.2.3-70-g09d2


From 929584999080616adb350df638b640f770c6f44a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 17 Oct 2019 19:47:01 -0400
Subject: fixed some display issues with templates and linking

---
 .../collectionFreeForm/CollectionFreeFormLinkView.tsx    |  4 ++--
 .../collectionFreeForm/CollectionFreeFormLinksView.tsx   |  6 ++----
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  7 ++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx       |  6 +++---
 src/client/views/nodes/DocuLinkBox.scss                  |  8 ++++++++
 src/client/views/nodes/DocuLinkBox.tsx                   | 16 ++++++++--------
 6 files changed, 27 insertions(+), 20 deletions(-)
 create mode 100644 src/client/views/nodes/DocuLinkBox.scss

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index fe92eed10..a59fda6d9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -37,8 +37,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     }
     render() {
         // let l = this.props.LinkDocs;
-        let a = this.props.A;
-        let b = this.props.B;
+        let a = this.props.A.layout instanceof Doc ? this.props.A.layout : this.props.A;
+        let b = this.props.B.layout instanceof Doc ? this.props.B.layout : this.props.B;
         let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2);
         let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2);
         let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index a81f5315a..189981e35 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,10 +1,8 @@
-import { computed, IReactionDisposer, reaction } from "mobx";
+import { computed, IReactionDisposer } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { listSpec } from "../../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Cast, FieldValue } from "../../../../new_fields/Types";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DocumentView } from "../../nodes/DocumentView";
 import { CollectionViewProps } from "../CollectionSubView";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2b0ef8ada..5dccb5bf0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -408,8 +408,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 this.props.Document.scrollY = NumCast(doc.y) - offset;
             }
         } else {
-            const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
-            const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+            let layoutdoc = doc.layout instanceof Doc ? doc.layout : doc;
+            const newPanX = NumCast(layoutdoc.x) + NumCast(layoutdoc.width) / 2;
+            const newPanY = NumCast(layoutdoc.y) + NumCast(layoutdoc.height) / 2;
             const newState = HistoryUtil.getState();
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
@@ -420,7 +421,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             this.Document.panTransformType = "Ease";
             Doc.BrushDoc(this.props.Document);
             this.props.focus(this.props.Document);
-            willZoom && this.setScaleToZoom(doc, scale);
+            willZoom && this.setScaleToZoom(layoutdoc, scale);
 
             afterFocus && setTimeout(() => {
                 if (afterFocus && afterFocus()) {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5e8ac3ecd..8153923de 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -34,8 +34,8 @@ export const PositionDocument = makeInterface(documentSchema, positionSchema);
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
     _disposer: IReactionDisposer | undefined = undefined;
     get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
-    get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : this.Document.x || 0; }
-    get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : this.Document.y || 0; }
+    get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : NumCast(this.layoutDoc.x); }
+    get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : NumCast(this.layoutDoc.y); }
     get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.layoutDoc[WidthSym](); }
     get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym](); }
     @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document, this.props.DataDoc) ? this.props.dataProvider(this.props.Document, this.props.DataDoc) : undefined; }
@@ -64,7 +64,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         this._disposer = reaction(() => [this.props.Document.animateToPos, this.props.Document.isAnimating],
             () => {
                 const target = this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined;
-                this._animPos = !target ? undefined : target[2] ? [this.Document.x || 0, this.Document.y || 0] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]);
+                this._animPos = !target ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]);
             }, { fireImmediately: true });
     }
 
diff --git a/src/client/views/nodes/DocuLinkBox.scss b/src/client/views/nodes/DocuLinkBox.scss
new file mode 100644
index 000000000..57c1a66e0
--- /dev/null
+++ b/src/client/views/nodes/DocuLinkBox.scss
@@ -0,0 +1,8 @@
+.docuLinkBox-cont {
+    cursor: default;
+    position: absolute;
+    width: 25px;
+    height: 25px;
+    border-radius: 20px;
+    pointer-events: all;
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index 2e968a9f6..8c41b9d4f 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -8,7 +8,7 @@ import { DocumentManager } from "../../util/DocumentManager";
 import { DragLinksAsDocuments } from "../../util/DragManager";
 import { DocComponent } from "../DocComponent";
 import { documentSchema } from "./DocumentView";
-import "./DocumentView.scss";
+import "./DocuLinkBox.scss";
 import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
 
@@ -40,7 +40,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
             let bounds = cdiv.getBoundingClientRect();
             let pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
             let separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
-            let dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy))
+            let dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy));
             if (separation > 100) {
                 DragLinksAsDocuments(this._ref.current!, pt[0], pt[1], this.props.ContainingCollectionDoc as Doc, this.props.Document); // Containging collection is the document, not a collection... hack.
                 document.removeEventListener("pointermove", this.onPointerMove);
@@ -50,7 +50,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
                 this.props.Document[this.props.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
             }
         }
-    })
+    });
     onPointerUp = (e: PointerEvent) => {
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
@@ -68,10 +68,10 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
         let y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100);
         let x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100);
         let c = StrCast(this.props.Document.backgroundColor, "lightblue");
-        return <div onPointerDown={this.onPointerDown} onClick={this.onClick} title={StrCast((this.props.Document[this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1"]! as Doc).title)} ref={this._ref} style={{
-            cursor: "default", position: "absolute", background: c, width: "25px", height: "25px", borderRadius: "20px", textAlign: "center", left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
-            pointerEvents: "all",
-            transform: `scale(${1 / this.props.ContentScaling()})`,
-        }} />
+        return <div className="docuLinkBox-cont" onPointerDown={this.onPointerDown} onClick={this.onClick} title={StrCast((this.props.Document[this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1"]! as Doc).title)}
+            ref={this._ref} style={{
+                background: c, width: "25px", left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
+                transform: `scale(${1 / this.props.ContentScaling()})`
+            }} />;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 73cb8628d13cc71e58717e97ca073c8b3316dd63 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 17 Oct 2019 23:40:29 -0400
Subject: working version of link lines between anchors

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/DocumentManager.ts                 |  7 ++-
 src/client/util/LinkManager.ts                     |  6 ++-
 src/client/views/OverlayView.tsx                   |  2 +
 .../CollectionFreeFormLinkView.scss                |  3 --
 .../CollectionFreeFormLinkView.tsx                 | 63 ++++++----------------
 .../CollectionFreeFormLinksView.scss               |  5 +-
 .../CollectionFreeFormLinksView.tsx                | 44 +++++----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  8 ++-
 src/client/views/nodes/DocuLinkBox.tsx             |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/new_fields/Doc.ts                              |  2 +-
 12 files changed, 50 insertions(+), 97 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index edc2ac653..d979e0a3f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -724,6 +724,7 @@ export namespace DocUtils {
             linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2");
             linkDocProto.width = linkDocProto.height = 0;
             linkDocProto.isBackground = true;
+            linkDocProto.isButton = true;
 
             LinkManager.Instance.addLink(linkDocProto);
 
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index e23ac55c2..fc406c6ab 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -9,6 +9,7 @@ import { DocumentView } from '../views/nodes/DocumentView';
 import { LinkManager } from './LinkManager';
 import { Scripting } from './Scripting';
 import { SelectionManager } from './SelectionManager';
+import { DocumentType } from '../documents/DocumentTypes';
 
 
 export class DocumentManager {
@@ -110,14 +111,18 @@ export class DocumentManager {
 
     @computed
     public get LinkedDocumentViews() {
+        console.log("START");
         let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document)).reduce((pairs, dv) => {
+            console.log("DOC = " + dv.props.Document.title + " " + dv.props.Document.layout);
             let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
             pairs.push(...linksList.reduce((pairs, link) => {
                 if (link) {
                     let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
                     if (linkToDoc) {
                         DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
-                            pairs.push({ a: dv, b: docView1, l: link });
+                            if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
+                                pairs.push({ a: dv, b: docView1, l: link });
+                            }
                         });
                     }
                 }
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 8a668e8d8..35c0f023f 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -79,7 +79,7 @@ export class LinkManager {
         let related = LinkManager.Instance.getAllLinks().filter(link => {
             let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
             let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
-            return protomatch1 || protomatch2;
+            return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor);
         });
         return related;
     }
@@ -244,8 +244,10 @@ export class LinkManager {
     public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
         if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) {
             return Cast(linkDoc.anchor2, Doc, null);
-        } else {
+        } else if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor2, Doc, null))) {
             return Cast(linkDoc.anchor1, Doc, null);
+        } else if (Doc.AreProtosEqual(anchor, linkDoc)) {
+            return linkDoc;
         }
     }
 }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 95c7b2683..9869e24d1 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -12,6 +12,7 @@ import { Transform } from "../util/Transform";
 import { CollectionFreeFormDocumentView } from "./nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "./nodes/DocumentContentsView";
 import { NumCast } from "../../new_fields/Types";
+import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
 
 export type OverlayDisposer = () => void;
 
@@ -206,6 +207,7 @@ export class OverlayView extends React.Component {
                 <div>
                     {this._elements}
                 </div>
+                <CollectionFreeFormLinksView key="freeformLinks" />
                 {this.overlayDocs}
             </div>
         );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index cfd18ad35..1f1bca2f2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -1,6 +1,5 @@
 .collectionfreeformlinkview-linkLine {
     stroke: black;
-    transform: translate(10000px,10000px);
     opacity: 0.8;
     pointer-events: all;
     stroke-width: 3px;
@@ -8,13 +7,11 @@
 .collectionfreeformlinkview-linkCircle {
     stroke: rgb(0,0,0);
     opacity: 0.5;
-    transform: translate(10000px,10000px);
     pointer-events: all;
     cursor: pointer;
 }
 .collectionfreeformlinkview-linkText {
     stroke: rgb(0,0,0);
     opacity: 0.5;
-    transform: translate(10000px,10000px);
     pointer-events: all;
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index a59fda6d9..4e1faccd7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,63 +1,30 @@
 import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types";
-import { InkingControl } from "../../InkingControl";
+import { Doc } from "../../../../new_fields/Doc";
+import { Utils } from '../../../../Utils';
+import { DocumentView } from "../../nodes/DocumentView";
 import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { DocumentType } from "../../../documents/DocumentTypes";
 
 export interface CollectionFreeFormLinkViewProps {
-    A: Doc;
-    B: Doc;
+    A: DocumentView;
+    B: DocumentView;
     LinkDocs: Doc[];
-    addDocument: (document: Doc) => boolean;
-    removeDocument: (document: Doc) => boolean;
 }
 
 @observer
 export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
 
-    onPointerDown = (e: React.PointerEvent) => {
-        if (e.button === 0 && !InkingControl.Instance.selectedTool) {
-            let a = this.props.A;
-            let b = this.props.B;
-            let x1 = NumCast(a.x) + (BoolCast(a.isMinimized) ? 5 : a[WidthSym]() / 2);
-            let y1 = NumCast(a.y) + (BoolCast(a.isMinimized) ? 5 : a[HeightSym]() / 2);
-            let x2 = NumCast(b.x) + (BoolCast(b.isMinimized) ? 5 : b[WidthSym]() / 2);
-            let y2 = NumCast(b.y) + (BoolCast(b.isMinimized) ? 5 : b[HeightSym]() / 2);
-            // this.props.LinkDocs.map(l => {
-            //     let width = l[WidthSym]();
-            //     l.x = (x1 + x2) / 2 - width / 2;
-            //     l.y = (y1 + y2) / 2 + 10;
-            //     if (!this.props.removeDocument(l)) this.props.addDocument(l, false);
-            // });
-            e.stopPropagation();
-            e.preventDefault();
-        }
-    }
     render() {
-        // let l = this.props.LinkDocs;
-        let a = this.props.A.layout instanceof Doc ? this.props.A.layout : this.props.A;
-        let b = this.props.B.layout instanceof Doc ? this.props.B.layout : this.props.B;
-        let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2);
-        let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2);
-        let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
-        let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2);
-        let text = "";
-        // let first = this.props.LinkDocs[0];
-        // if (this.props.LinkDocs.length === 1) text += first.title + (first.linkDescription ? "(" + StrCast(first.linkDescription) + ")" : "");
-        // else text = "-multiple-";
-        return (
-            <>
-                <line key="linkLine" className="collectionfreeformlinkview-linkLine"
-                    x1={`${x1}`} y1={`${y1}`}
-                    x2={`${x2}`} y2={`${y2}`} />
-                {/* <circle key="linkCircle" className="collectionfreeformlinkview-linkCircle"
-                    cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={8} onPointerDown={this.onPointerDown} /> */}
-                <text key="linkText" textAnchor="middle" className="collectionfreeformlinkview-linkText" x={`${(x1 + x2) / 2}`} y={`${(y1 + y2) / 2}`}>
-                    {text}
-                </text>
-            </>
-        );
+        let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+        let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+        let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
+        let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
+        let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2);
+        let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2);
+        return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
+            x1={`${pt1[0]}`} y1={`${pt1[1]}`}
+            x2={`${pt2[0]}`} y2={`${pt2[1]}`} />);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
index 30e158603..cb5cef29c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
@@ -1,10 +1,9 @@
 .collectionfreeformlinksview-svgCanvas{
-    transform: translate(-10000px,-10000px);
     position: absolute;
     top: 0;
     left: 0;
-    width: 20000px; 
-    height: 20000px;
+    width: 100%; 
+    height: 100%;
     pointer-events: none;
   }
   .collectionfreeformlinksview-container {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 189981e35..cf0e55ccf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,17 +1,17 @@
 import { computed, IReactionDisposer } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { Cast, FieldValue } from "../../../../new_fields/Types";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DocumentView } from "../../nodes/DocumentView";
-import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
+import { Utils } from "../../../../Utils";
+import { SelectionManager } from "../../../util/SelectionManager";
 
 @observer
-export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
+export class CollectionFreeFormLinksView extends React.Component {
 
     _brushReactionDisposer?: IReactionDisposer;
     componentDidMount() {
@@ -71,35 +71,18 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
             this._brushReactionDisposer();
         }
     }
-    documentAnchors(view: DocumentView) {
-        let equalViews = [view];
-        let containerDoc = FieldValue(Cast(view.props.Document.annotationOn, Doc));
-        if (containerDoc) {
-            equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
-        }
-        if (view.props.ContainingCollectionDoc) {
-            let collid = view.props.ContainingCollectionDoc[Id];
-            DocListCast(this.props.Document[this.props.fieldKey]).
-                filter(child =>
-                    child[Id] === collid).map(view =>
-                        DocumentManager.Instance.getDocumentViews(view).map(view =>
-                            equalViews.push(view)));
-        }
-        return equalViews.filter(sv => sv.props.ContainingCollectionDoc === this.props.Document);
-    }
-
     @computed
     get uniqueConnections() {
         let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
-            let srcViews = this.documentAnchors(connection.a);
-            let targetViews = this.documentAnchors(connection.b);
+            let srcViews = [connection.a];
+            let targetViews = [connection.b];
 
-            let possiblePairs: { a: Doc, b: Doc, }[] = [];
-            srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
+            let possiblePairs: { a: DocumentView, b: DocumentView, }[] = [];
+            srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv, b: tv })));
             possiblePairs.map(possiblePair => {
                 if (!drawnPairs.reduce((found, drawnPair) => {
-                    let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b));
-                    let match2 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.b) && Doc.AreProtosEqual(possiblePair.b, drawnPair.a));
+                    let match1 = (Doc.AreProtosEqual(possiblePair.a.props.Document, drawnPair.a.props.Document) && Doc.AreProtosEqual(possiblePair.b.props.Document, drawnPair.b.props.Document));
+                    let match2 = (Doc.AreProtosEqual(possiblePair.a.props.Document, drawnPair.b.props.Document) && Doc.AreProtosEqual(possiblePair.b.props.Document, drawnPair.a.props.Document));
                     let match = match1 || match2;
                     if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
                         drawnPair.l.push(connection.l);
@@ -110,16 +93,15 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                 }
             });
             return drawnPairs;
-        }, [] as { a: Doc, b: Doc, l: Doc[] }[]);
-        return connections.map(c => <CollectionFreeFormLinkView key={c.l.reduce((p, l) => p + l[Id], "")} A={c.a} B={c.b} LinkDocs={c.l}
-            removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
+        }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
+        return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
     }
 
     render() {
         return (
             <div className="collectionfreeformlinksview-container">
                 <svg className="collectionfreeformlinksview-svgCanvas">
-                    {this.uniqueConnections}
+                    {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections}
                 </svg>
                 {this.props.children}
             </div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5dccb5bf0..b3270d507 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -705,11 +705,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
                     <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
-                        <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
-                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
-                                {this.childViews}
-                            </InkingCanvas>
-                        </CollectionFreeFormLinksView>
+                        <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
+                            {this.childViews}
+                        </InkingCanvas>
                         <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
                     </CollectionFreeFormViewPannableContents>
                 </MarqueeView>
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index 8c41b9d4f..f56bf4ad3 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -54,12 +54,12 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
     onPointerUp = (e: PointerEvent) => {
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
-        if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey)) {
+        if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) {
             this.props.select(false);
         }
     }
     onClick = (e: React.MouseEvent) => {
-        if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey)) {
+        if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) {
             DocumentManager.Instance.FollowLink(this.props.Document, this.props.Document[this.props.fieldKey] as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false);
         }
         e.stopPropagation();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6775655eb..0bcaa99aa 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -697,7 +697,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     opacity: this.Document.opacity
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-                onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)}
+                onPointerEnter={() => { Doc.UnBrushAllDocs(); Doc.BrushDoc(this.props.Document); }} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)}
             >
                 {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) =>
                     <div style={{ pointerEvents: "none", position: "absolute", transformOrigin: "top left", width: "100%", height: "100%", transform: `scale(${this.props.Document.fitWidth ? 1 : 1 / this.props.ContentScaling()})` }}> <DocumentView {...this.props} backgroundColor={returnTransparent} Document={d} layoutKey={this.linkEndpoint(d)} /> </div>)}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 04f310785..26355d0e7 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -734,7 +734,7 @@ export namespace Doc {
 
     }
     export function UnBrushAllDocs() {
-        manager.BrushedDoc.clear();
+        brushManager.BrushedDoc.clear();
     }
 
     export function setChildLayout(target: Doc, source?: Doc) {
-- 
cgit v1.2.3-70-g09d2


From b3ccf5df0bab87d76b7227ffc5f147b4e4b41b90 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 18 Oct 2019 14:25:29 -0400
Subject: fixes so that link lines update when endpoints change.

---
 .../collectionFreeForm/CollectionFreeFormLinkView.tsx  | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 4e1faccd7..c5bc2e7fb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -6,6 +6,7 @@ import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { DocumentType } from "../../../documents/DocumentTypes";
+import { observable, action } from "mobx";
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -15,8 +16,25 @@ export interface CollectionFreeFormLinkViewProps {
 
 @observer
 export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
+    @observable _alive: number = 0;
+    @action
+    componentDidMount() {
+        this._alive = 1;
+        setTimeout(this.rerender, 50);
+    }
+    @action
+    componentWillUnmount() {
+        this._alive = 0;
+    }
+    rerender = action(() => {
+        if (this._alive) {
+            setTimeout(this.rerender, 50);
+            this._alive++;
+        }
+    })
 
     render() {
+        let y = this._alive;
         let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
         let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
         let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
-- 
cgit v1.2.3-70-g09d2


From 1f3576a69a6a0396d07e965c700bb7d69d77a0a3 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 18 Oct 2019 17:06:41 -0400
Subject: fixed up creator buttons - factored out convert to buttons code into
 a dropConverter script.  fixed issues with template loading for images.

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/DragManager.ts                     |  3 ++
 src/client/util/DropConverter.ts                   | 46 ++++++++++++++++++++++
 src/client/views/CollectionLinearView.tsx          | 40 -------------------
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  6 ++-
 .../views/collections/CollectionTreeView.scss      |  1 -
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 .../CollectionFreeFormLinkView.tsx                 |  2 +-
 .../authentication/models/current_user_utils.ts    | 11 ++++--
 10 files changed, 65 insertions(+), 50 deletions(-)
 create mode 100644 src/client/util/DropConverter.ts

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 651662ded..32fda1954 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -105,7 +105,7 @@ export interface DocumentOptions {
     yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts
     panel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
     targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
-    convertToButtons?: boolean; // whether documents dropped onto a collection should be converted to buttons that will construct the dropped document
+    dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
     // [key: string]: Opt<Field>;
 }
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 576b16bc8..bbc29585c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -14,6 +14,8 @@ import { ScriptField } from "../../new_fields/ScriptField";
 import { List } from "../../new_fields/List";
 import { PrefetchProxy } from "../../new_fields/Proxy";
 import { listSpec } from "../../new_fields/Schema";
+import { Scripting } from "./Scripting";
+import { convertDropDataToButtons } from "./DropConverter";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(
@@ -497,3 +499,4 @@ export namespace DragManager {
         }
     }
 }
+Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); });
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
new file mode 100644
index 000000000..eea3da1bc
--- /dev/null
+++ b/src/client/util/DropConverter.ts
@@ -0,0 +1,46 @@
+import { DragManager } from "./DragManager";
+import { CollectionViewType } from "../views/collections/CollectionBaseView";
+import { Doc, DocListCast } from "../../new_fields/Doc";
+import { DocumentType } from "../documents/DocumentTypes";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { StrCast } from "../../new_fields/Types";
+import { Docs } from "../documents/Documents";
+import { ScriptField } from "../../new_fields/ScriptField";
+
+
+function makeTemplate(doc: Doc): boolean {
+    let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+    let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0];
+    let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, "");
+    let docs = DocListCast(layoutDoc[fieldKey]);
+    let any = false;
+    docs.map(d => {
+        if (!StrCast(d.title).startsWith("-")) {
+            any = true;
+            return Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc));
+        }
+        if (d.type === DocumentType.COL) return makeTemplate(d);
+        return false;
+    });
+    return any;
+}
+export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
+    data && data.draggedDocuments.map((doc, i) => {
+        let dbox = doc;
+        if (!doc.onDragStart && !doc.onClick && doc.viewType !== CollectionViewType.Linear) {
+            let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+            if (layoutDoc.type === DocumentType.COL) {
+                layoutDoc.isTemplateDoc = makeTemplate(layoutDoc);
+            } else {
+                layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && !data.userDropAction;
+            }
+            dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" });
+            dbox.dragFactory = layoutDoc;
+            dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
+            dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
+        } else if (doc.viewType === CollectionViewType.Linear) {
+            dbox.ignoreClick = true;
+        }
+        data.droppedDocuments[i] = dbox;
+    });
+}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index 04e131135..e8ef20899 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -47,46 +47,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
         }
     }
 
-    makeTemplate = (doc: Doc): boolean => {
-        let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
-        let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0];
-        let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, "");
-        let docs = DocListCast(layoutDoc[fieldKey]);
-        let any = false;
-        docs.map(d => {
-            if (!StrCast(d.title).startsWith("-")) {
-                any = true;
-                return Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc));
-            }
-            if (d.type === DocumentType.COL) return this.makeTemplate(d);
-            return false;
-        });
-        return any;
-    }
-
-    drop = action((e: Event, de: DragManager.DropEvent) => {
-        (de.data as DragManager.DocumentDragData).draggedDocuments.map((doc, i) => {
-            let dbox = doc;
-            if (!doc.onDragStart && !doc.onClick && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) {
-                let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
-                if (layoutDoc.type === DocumentType.COL) {
-                    layoutDoc.isTemplateDoc = this.makeTemplate(layoutDoc);
-                } else {
-                    layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && de.data instanceof DragManager.DocumentDragData && !de.data.userDropAction;
-                }
-                dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" });
-                dbox.dragFactory = layoutDoc;
-                dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
-                dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
-            } else if (doc.viewType === CollectionViewType.Linear) {
-                dbox.ignoreClick = true;
-            }
-            (de.data as DragManager.DocumentDragData).droppedDocuments[i] = dbox;
-        });
-        e.stopPropagation();
-        return super.drop(e, de);
-    });
-
     public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
 
     dimension = () => NumCast(this.props.Document.height); // 2 * the padding
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e54374ad7..f9f040c6b 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -65,7 +65,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
             let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
             return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
-                {this.getDisplayDoc(pair.layout as Doc, pair.data, dxf, width)}
+                {pair.layout instanceof Doc && this.getDisplayDoc(pair.layout, pair.data, dxf, width)}
             </div>;
         });
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1f8c0b18f..bc61492d0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { RouteStore } from "../../../server/RouteStore";
 import { Utils } from "../../../Utils";
@@ -23,6 +23,8 @@ import React = require("react");
 var path = require('path');
 import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
+import { CollectionViewType } from "./CollectionBaseView";
+import { ObjectField } from "../../../new_fields/ObjectField";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc) => boolean;
@@ -126,6 +128,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         @undoBatch
         @action
         protected drop(e: Event, de: DragManager.DropEvent): boolean {
+            (this.props.Document.dropConverter instanceof ScriptField) &&
+                this.props.Document.dropConverter.script.run({ dragData: de.data });
             if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
                 if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
                     this.childDocs.map(doc =>
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index bff8ce5c1..7d0c900a6 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -10,7 +10,6 @@
     width:100%;
     position: relative;
     top:0;
-    padding-top: 20px;
     padding-left: 10px;
     padding-right: 10px;
     background: $light-color-secondary;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2c77c4b5b..5d88e6290 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -576,7 +576,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
         return !this.childDocs ? (null) : (
             <div id="body" className="collectionTreeView-dropTarget"
-                style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray") }}
+                style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
                 onContextMenu={this.onContextMenu}
                 onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                 onDrop={this.onTreeDrop}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index c5bc2e7fb..962fe2a1c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -31,7 +31,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             setTimeout(this.rerender, 50);
             this._alive++;
         }
-    })
+    });
 
     render() {
         let y = this._alive;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index e6f202685..9c41c7651 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -13,6 +13,7 @@ import { Cast, PromiseValue } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
 import { RouteStore } from "../../RouteStore";
 import { InkingControl } from "../../../client/views/InkingControl";
+import { DragManager } from "../../../client/util/DragManager";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -61,7 +62,7 @@ export class CurrentUserUtils {
             nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
             onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
             unchecked: data.unchecked ? ComputedField.MakeFunction(data.unchecked) : undefined, activePen: data.activePen,
-            backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory
+            backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
         }));
     }
 
@@ -69,7 +70,8 @@ export class CurrentUserUtils {
     static setupCreatePanel(sidebarContainer: Doc, doc: Doc) {
         // setup a masonry view of all he creators
         const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(doc), {
-            width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons"
+            width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons",
+            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), yMargin: 0
         });
         // setup a color picker
         const color = Docs.Create.ColorDocument({
@@ -81,7 +83,7 @@ export class CurrentUserUtils {
             panel: Docs.Create.StackingDocument([dragCreators, color], {
                 width: 500, height: 800, chromeStatus: "disabled", title: "creator stack"
             }),
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel")
+            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel"),
         });
     }
 
@@ -148,7 +150,8 @@ export class CurrentUserUtils {
 
         doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], {
             title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0",
-            backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true,
+            backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true,
+            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
         });
     }
 
-- 
cgit v1.2.3-70-g09d2


From c97b98c165e318da787361b8fc11382b0b98afa4 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 24 Oct 2019 11:54:03 -0400
Subject: several fixes to audioboxes.

---
 src/client/documents/Documents.ts                  |  3 ++-
 .../collections/CollectionMasonryViewFieldRow.tsx  |  2 +-
 .../views/collections/CollectionStackingView.scss  |  5 ++++
 .../CollectionFreeFormLinkView.scss                |  1 +
 .../CollectionFreeFormLinkView.tsx                 |  3 +++
 src/client/views/nodes/AudioBox.scss               | 28 ++++++++++++++--------
 src/client/views/nodes/AudioBox.tsx                | 19 +++++++--------
 src/client/views/nodes/DocuLinkBox.tsx             |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 9 files changed, 40 insertions(+), 25 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f26594e04..d1fcabc4a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -171,7 +171,7 @@ export namespace Docs {
             }],
             [DocumentType.AUDIO, {
                 layout: { view: AudioBox, dataField: data },
-                options: { height: 35, backgroundColor: "lightGray", borderRounding: "20%" }
+                options: { height: 35, backgroundColor: "lightGray" }
             }],
             [DocumentType.PDF, {
                 layout: { view: PDFBox, dataField: data },
@@ -714,6 +714,7 @@ export namespace DocUtils {
             linkDocProto.anchor2Timecode = target.doc.currentTimecode;
             linkDocProto.layoutKey1 = DocuLinkBox.LayoutString("anchor1");
             linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2");
+            linkDocProto.borderRounding = "20";
             linkDocProto.width = linkDocProto.height = 0;
             linkDocProto.isBackground = true;
             linkDocProto.isButton = true;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 4259e948b..0249b90b4 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -366,7 +366,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                         >
                             {headingView}
                             {collapsed ? (null) :
-                                < div >
+                                < div style={{ position: "relative" }}>
                                     <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
                                         ref={this._contRef}
                                         style={{
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 3f4b678c7..1ff2275d4 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -21,6 +21,11 @@
     overflow-y: auto;
     flex-wrap: wrap;
     transition: top .5s;
+    >div {
+        height: 100%;
+        position: relative;
+        display: inline-block;
+    }
 
     .collectionSchemaView-previewDoc {
         height: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index 1f1bca2f2..75af11537 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -3,6 +3,7 @@
     opacity: 0.8;
     pointer-events: all;
     stroke-width: 3px;
+    transition: opacity 0.5s ease-in;
 }
 .collectionfreeformlinkview-linkCircle {
     stroke: rgb(0,0,0);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 962fe2a1c..837413842 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -17,10 +17,12 @@ export interface CollectionFreeFormLinkViewProps {
 @observer
 export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
     @observable _alive: number = 0;
+    @observable _opacity: number = 1;
     @action
     componentDidMount() {
         this._alive = 1;
         setTimeout(this.rerender, 50);
+        setTimeout(action(() => this._opacity = 0.05), 50);
     }
     @action
     componentWillUnmount() {
@@ -42,6 +44,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2);
         let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2);
         return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
+            style={{ opacity: this._opacity }}
             x1={`${pt1[0]}`} y1={`${pt1[1]}`}
             x2={`${pt2[0]}`} y2={`${pt2[1]}`} />);
     }
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index ccbf0d75f..3b19a6dba 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -44,22 +44,27 @@
             width:100%;
             height: 80%;
             position: relative;
+            padding-right: 5px;
             display: flex;
             .audiobox-playhead {
                 position: relative;
                 margin-top: auto;
                 margin-bottom: auto;
                 width: 25px;
+                padding: 2px;
             }
             .audiobox-timeline {
                 position:relative;
                 height:100%;
                 width:100%;
+                background: white;
+                border: gray solid 1px;
+                border-radius: 3px;
                 .audiobox-current {
                     width: 1px;
                     height:100%;
                     background-color: red;
-                    position: absolute;;
+                    position: absolute;
                 }
                 .audiobox-linker, .audiobox-linker-mini {
                     position:absolute;
@@ -72,10 +77,11 @@
                     background-color: transparent;
                     box-shadow: black 2px 2px 1px;
                     .docuLinkBox-cont {
-                        width:15px !important;
-                        height:15px !important;
-                        left: calc(100% - 15px) !important;
-                        top: calc(100% - 15px) !important;
+                        position: relative !important;
+                        height: 100%  !important;
+                        width: 100% !important;
+                        left:unset !important;
+                        top:unset !important;
                     }
                 }
                 .audiobox-linker-mini {
@@ -86,10 +92,11 @@
                     margin-left: -1;
                     margin-top: -2;
                     .docuLinkBox-cont {
-                        width:8px !important;
-                        height:8px !important;
-                        left: calc(100% - 8px) !important;
-                        top: calc(100% - 8px) !important;
+                        position: relative !important;
+                        height: 100%  !important;
+                        width: 100% !important;
+                        left:unset !important;
+                        top:unset !important;
                     }
                 }
                 .audiobox-linker:hover, .audiobox-linker-mini:hover {
@@ -98,7 +105,8 @@
                 .audiobox-marker-container, .audiobox-marker-minicontainer {
                     position:absolute;
                     width:10px;
-                    height:100%;
+                    height:90%;
+                    top:2.5%;
                     background:gray;
                     border-radius: 5px;
                     box-shadow: black 2px 2px 1px;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index cc1c63d44..bd1caaf1b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -12,12 +12,11 @@ import { RouteStore } from "../../../server/RouteStore";
 import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx";
 import { DateField } from "../../../new_fields/DateField";
 import { SelectionManager } from "../../util/SelectionManager";
-import { Doc, DocListCast, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ContextMenu } from "../ContextMenu";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { DocumentManager } from "../../util/DocumentManager";
 import { DocumentView } from "./DocumentView";
 
 interface Window {
@@ -59,7 +58,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                 scrollLinkId && DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
                     let la1 = l.anchor1 as Doc;
                     let linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode);
-                    setTimeout(() => this.playFrom(linkTime), 250);
+                    setTimeout(() => { this.playFrom(linkTime); Doc.linkFollowHighlight(l); }, 250);
                 });
                 scrollLinkId && Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
             }, { fireImmediately: true });
@@ -75,11 +74,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     }
 
     timecodeChanged = () => {
-        const extensionDoc = this.extensionDoc;
         const htmlEle = this._ele;
-        const start = extensionDoc && DateCast(extensionDoc.recordingStart);
-        if (start && htmlEle) {
-            htmlEle && htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
+        if (this._audioState === "recorded" && htmlEle) {
+            htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
             DocListCast(this.dataDoc.links).map(l => {
                 let la1 = l.anchor1 as Doc;
                 let linkTime = NumCast(l.anchor2Timecode);
@@ -195,7 +192,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     }
 
     setRef = (e: HTMLAudioElement | null) => {
-        e && e.addEventListener("timeupdate", action(() => this.Document.currentTimecode = e!.currentTime));
+        e && e.addEventListener("timeupdate", this.timecodeChanged);
         this._ele = e;
     }
 
@@ -225,13 +222,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                     </button> :
                     <div className="audiobox-controls">
                         <div className="audiobox-player" onClick={this.onPlay}>
-                            <FontAwesomeIcon className="audiobox-playhead" icon={this._playing ? "pause" : "play"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
-                            <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon className="audiobox-playhead" icon="stop" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
+                            <div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this._playing ? "pause" : "play"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
+                            <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%" }} icon="stop" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
                             <div className="audiobox-timeline" onClick={e => e.stopPropagation()}
                                 onPointerDown={e => {
                                     if (e.button === 0 && !e.ctrlKey) {
                                         let rect = (e.target as any).getBoundingClientRect();
-                                        this._ele!.currentTime = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
+                                        this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
                                         this.pause();
                                         e.stopPropagation();
                                     }
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index f2ab6fcd8..8f68effe8 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -73,7 +73,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
         let c = StrCast(this.props.Document.backgroundColor, "lightblue");
         return <div className="docuLinkBox-cont" onPointerDown={this.onPointerDown} onClick={this.onClick} title={StrCast((this.props.Document[this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1"]! as Doc).title)}
             ref={this._ref} style={{
-                background: c, width: "25px", left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
+                background: c, left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
                 transform: `scale(${hasAnchor ? 0.333 : 1 / this.props.ContentScaling()})`
             }} />;
     }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 566b698bd..cf55a10ba 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -192,7 +192,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
             }
 
             let tsel = this._editorView.state.selection.$from;
-            tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 5000)));
+            tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000)));
             this._applyingChange = true;
             this.extensionDoc && (this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n"));
             this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now())));
-- 
cgit v1.2.3-70-g09d2

