From f7a567b27b5c124e8c3bfe1e866162e2911607c5 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 17 Jun 2019 12:26:57 -0400
Subject: cleaned up minimization code, added minimization for stacking

---
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 147 ++++-----------------
 1 file changed, 28 insertions(+), 119 deletions(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 499b83c0f..f6b1c62ee 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,17 +1,12 @@
-import { computed, IReactionDisposer, reaction, action } from "mobx";
+import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { OmitKeys } from "../../../Utils";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
 import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
-import { UndoManager } from "../../util/UndoManager";
-import { SelectionManager } from "../../util/SelectionManager";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
@@ -27,13 +22,7 @@ const FreeformDocument = makeInterface(schema, positionSchema);
 
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
-    private _mainCont = React.createRef<HTMLDivElement>();
-    _bringToFrontDisposer?: IReactionDisposer;
-
-    @computed get transform() {
-        return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
-    }
-
+    @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; }
     @computed get X() { return FieldValue(this.Document.x, 0); }
     @computed get Y() { return FieldValue(this.Document.y, 0); }
     @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); }
@@ -59,91 +48,20 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.zoom)
-
-    @computed
-    get docView() {
-        return <DocumentView {...OmitKeys(this.props, ['zoomFade']).omit}
-            ContentScaling={this.contentScaling}
-            ScreenToLocalTransform={this.getTransform}
-            PanelWidth={this.panelWidth}
-            PanelHeight={this.panelHeight}
-            collapseToPoint={this.collapseToPoint}
-        />;
-    }
-
-    componentDidMount() {
-        this._bringToFrontDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => {
-            this.props.bringToFront(this.props.Document);
-            if (values instanceof List) {
-                let scrpt = this.props.ScreenToLocalTransform().transformPoint(values[0], values[1]);
-                this.animateBetweenIcon(true, scrpt, [this.Document.x || 0, this.Document.y || 0],
-                    this.Document.width || 0, this.Document.height || 0, values[2], values[3] ? true : false);
-            }
-        }, { fireImmediately: true });
-    }
-
-    componentWillUnmount() {
-        if (this._bringToFrontDisposer) this._bringToFrontDisposer();
-    }
-
-    static _undoBatch?: UndoManager.Batch = undefined;
-    @action
-    public collapseToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise<void> => {
-        SelectionManager.DeselectAll();
-        if (expandedDocs) {
-            if (!CollectionFreeFormDocumentView._undoBatch) {
-                CollectionFreeFormDocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
-            }
-            let isMinimized: boolean | undefined;
-            expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
-                let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
-                if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
-                    if (isMinimized === undefined) {
-                        isMinimized = BoolCast(maximizedDoc.isMinimized, false);
-                    }
-                    maximizedDoc.willMaximize = isMinimized;
-                    maximizedDoc.isMinimized = false;
-                    maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
-                }
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom);
+
+    animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
+        this.props.bringToFront(this.props.Document);
+        let targetPos = [this.Document.x || 0, this.Document.y || 0];
+        let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
+        DocumentView.animateBetweenIconFunc(this.props.Document,
+            this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => {
+                let pval = maximizing ?
+                    [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] :
+                    [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress];
+                this.Document.x = progress === 1 ? targetPos[0] : pval[0];
+                this.Document.y = progress === 1 ? targetPos[1] : pval[1];
             });
-            setTimeout(() => {
-                CollectionFreeFormDocumentView._undoBatch && CollectionFreeFormDocumentView._undoBatch.end();
-                CollectionFreeFormDocumentView._undoBatch = undefined;
-            }, 500);
-        }
-    }
-
-    animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, maximizing: boolean) {
-
-        setTimeout(() => {
-            let now = Date.now();
-            let progress = Math.min(1, (now - stime) / 200);
-            let pval = maximizing ?
-                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
-                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
-            this.props.Document.width = maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
-            this.props.Document.height = maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
-            this.props.Document.x = pval[0];
-            this.props.Document.y = pval[1];
-            if (first) {
-                this.props.Document.proto!.willMaximize = false;
-            }
-            if (now < stime + 200) {
-                this.animateBetweenIcon(false, icon, targ, width, height, stime, maximizing);
-            }
-            else {
-                if (!maximizing) {
-                    this.props.Document.proto!.isMinimized = true;
-                    this.props.Document.x = targ[0];
-                    this.props.Document.y = targ[1];
-                    this.props.Document.width = width;
-                    this.props.Document.height = height;
-                }
-                this.props.Document.proto!.isIconAnimating = undefined;
-            }
-        },
-            2);
     }
 
     borderRounding = () => {
@@ -155,34 +73,25 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     render() {
-        let maximizedDoc = FieldValue(Cast(this.props.Document.maximizedDocs, listSpec(Doc)));
-        let zoomFade = 1;
-        //var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
-        // let transform = this.getTransform().scale(this.contentScaling()).inverse();
-        // var [sptX, sptY] = transform.transformPoint(0, 0);
-        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        // let w = bptX - sptX;
-        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        const screenWidth = Math.min(50 * NumCast(this.props.Document.nativeWidth, 0), 1800);
-        let fadeUp = .75 * screenWidth;
-        let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth;
-        // zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1;
-
         return (
-            <div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
+            <div className="collectionFreeFormDocumentView-container"
                 style={{
-                    opacity: zoomFade,
-                    borderRadius: `${this.borderRounding()}px`,
                     transformOrigin: "left top",
+                    position: "absolute",
+                    backgroundColor: "transparent",
+                    borderRadius: `${this.borderRounding()}px`,
                     transform: this.transform,
-                    pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
                     width: this.width,
                     height: this.height,
-                    position: "absolute",
                     zIndex: this.Document.zIndex || 0,
-                    backgroundColor: "transparent"
                 }} >
-                {this.docView}
+                <DocumentView {...this.props}
+                    ContentScaling={this.contentScaling}
+                    ScreenToLocalTransform={this.getTransform}
+                    PanelWidth={this.panelWidth}
+                    PanelHeight={this.panelHeight}
+                    animateBetweenIcon={this.animateBetweenIcon}
+                />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 18 Jun 2019 20:58:32 -0400
Subject: Fixed linter errors

---
 src/client/northstar/dash-nodes/HistogramBox.tsx             |  3 +--
 src/client/util/DocumentManager.ts                           |  6 +++---
 src/client/util/DragManager.ts                               |  4 +++-
 src/client/util/RichTextSchema.tsx                           | 10 +++++-----
 src/client/util/TooltipTextMenu.tsx                          |  2 +-
 src/client/views/ContextMenu.tsx                             |  4 ++--
 src/client/views/DocumentDecorations.tsx                     |  2 +-
 src/client/views/MainOverlayTextBox.tsx                      |  2 +-
 src/client/views/MainView.tsx                                |  7 ++++---
 src/client/views/PresentationView.tsx                        |  4 ++--
 src/client/views/collections/CollectionBaseView.tsx          |  2 +-
 src/client/views/collections/CollectionDockingView.tsx       |  5 +++--
 src/client/views/collections/CollectionPDFView.tsx           |  2 +-
 src/client/views/collections/CollectionSchemaView.tsx        | 11 ++++++-----
 src/client/views/collections/CollectionStackingView.tsx      |  4 ++--
 src/client/views/collections/CollectionTreeView.tsx          | 10 +++++-----
 .../collectionFreeForm/CollectionFreeFormLinksView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.tsx     |  2 +-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx                      | 12 ++++++------
 src/client/views/nodes/FormattedTextBox.tsx                  |  2 +-
 src/client/views/nodes/ImageBox.tsx                          |  3 ++-
 src/client/views/pdf/PDFAnnotationLayer.tsx                  |  2 +-
 src/client/views/pdf/PDFMenu.tsx                             |  2 +-
 src/client/views/pdf/PDFViewer.tsx                           |  4 ++--
 src/server/index.ts                                          |  2 +-
 27 files changed, 59 insertions(+), 54 deletions(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index d7732ee86..a60eaea85 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -125,8 +125,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
                                 let mapped = brushingDocs.map((brush, i) => {
                                     brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length];
                                     let brushed = DocListCast(brush.brushingDocs);
-                                    if (!brushed.length)
-                                        return null;
+                                    if (!brushed.length) return null;
                                     return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] };
                                 });
                                 runInAction(() => this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped.filter(m => m) as { l: Doc, b: Doc }[]));
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ff0c1560b..862395d74 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -35,9 +35,9 @@ export class DocumentManager {
         let toReturn: DocumentView | null = null;
         let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
-        for (let i = 0; i < passes.length; i++) {
+        for (let pass of passes) {
             DocumentManager.Instance.DocumentViews.map(view => {
-                if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) {
+                if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                     toReturn = view;
                     return;
                 }
@@ -45,7 +45,7 @@ export class DocumentManager {
             if (!toReturn) {
                 DocumentManager.Instance.DocumentViews.map(view => {
                     let doc = view.props.Document.proto;
-                    if (doc && doc[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) {
+                    if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                         toReturn = view;
                     }
                 });
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 89566e777..c3c92daa5 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -282,11 +282,13 @@ export namespace DragManager {
             // }
             let set = dragElement.getElementsByTagName('*');
             if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none";
-            for (let i = 0; i < set.length; i++)
+            // tslint:disable-next-line: prefer-for-of
+            for (let i = 0; i < set.length; i++) {
                 if (set[i].hasAttribute("style")) {
                     let s = set[i];
                     (s as any).style.pointerEvents = "none";
                 }
+            }
 
 
             dragDiv.appendChild(dragElement);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index e1e595925..943cdb4d1 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -156,12 +156,12 @@ export const nodes: { [index: string]: NodeSpec } = {
                     title: dom.getAttribute("title"),
                     alt: dom.getAttribute("alt"),
                     width: Math.min(100, Number(dom.getAttribute("width"))),
-                }
+                };
             }
         }],
         toDOM(node) {
-            const attrs = { style: `width: ${node.attrs.width}` }
-            return ["video", { ...node.attrs, ...attrs }]
+            const attrs = { style: `width: ${node.attrs.width}` };
+            return ["video", { ...node.attrs, ...attrs }];
         }
     },
 
@@ -285,7 +285,7 @@ export const marks: { [index: string]: MarkSpec } = {
         toDOM() {
             return ['span', {
                 style: 'color: blue'
-            }]
+            }];
         }
     },
 
@@ -536,4 +536,4 @@ schema.nodeFromJSON = (json: any) => {
         node.attrs.oldtext = Slice.fromJSON(schema, node.attrs.oldtextslice);
     }
     return node;
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 0a61b7e7d..f2559db74 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -256,7 +256,7 @@ export class TooltipTextMenu {
             starButton.onclick = () => {
                 let state = this.view.state;
                 this.insertStar(state, this.view.dispatch);
-            }
+            };
 
             this.tooltip.appendChild(starButton);
         }
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index da374455e..eb1937683 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,8 +1,8 @@
 import React = require("react");
 import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem";
 import { observable, action } from "mobx";
-import { observer } from "mobx-react"
-import "./ContextMenu.scss"
+import { observer } from "mobx-react";
+import "./ContextMenu.scss";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons';
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ceca940b6..e60f8c86c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -466,7 +466,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect();
 
-            if (rect.width !== 0 && (dX != 0 || dY != 0 || dW != 0 || dH != 0)) {
+            if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) {
                 let doc = PositionDocument(element.props.Document);
                 let nwidth = doc.nativeWidth || 0;
                 let nheight = doc.nativeHeight || 0;
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 0de880175..b4ad5f4d7 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -40,7 +40,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                     this.TextDoc = box.props.Document;
                     let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
                     let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); };
-                    this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content")
+                    this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content");
                 }
                 else {
                     this.TextDoc = undefined;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index fd76cbbd3..7f979cd3b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -237,10 +237,11 @@ export class MainView extends React.Component {
     }
 
     onColorClick = (e: React.MouseEvent) => {
-        let target = (e.nativeEvent! as any).path[0];
-        let parent = (e.nativeEvent! as any).path[1];
-        if (target.localName === "input" || parent.localName === "span")
+        let target = (e.nativeEvent as any).path[0];
+        let parent = (e.nativeEvent as any).path[1];
+        if (target.localName === "input" || parent.localName === "span") {
             e.stopPropagation();
+        }
     }
 
 
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index d2d41a4ba..1dacbb663 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -40,8 +40,8 @@ class PresentationViewList extends React.Component<PresListProps> {
             //this doc is selected
             className += " presentationView-selected";
         }
-        let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; }
-        let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; }
+        let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; };
+        let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; };
         return (
             <div className={className} key={document[Id] + index}
                 onPointerEnter={onEnter} onPointerLeave={onLeave}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 5238ad114..1e42593d1 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -130,7 +130,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     removeDocument(doc: Doc): boolean {
-        let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView)
+        let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
         docView && SelectionManager.DeselectDoc(docView);
         const props = this.props;
         //TODO This won't create the field if it doesn't already exist
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f2b3528b8..5beb89315 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -347,7 +347,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             tab.header.parent.setActiveContentItem(tab.contentItem);
                         }
                         tab.setActive(true);
-                    }
+                    };
                     ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, location) => CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv);
                     tab.reactComponents = [upDiv];
                     tab.element.append(upDiv);
@@ -432,8 +432,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @observable private _document: Opt<Doc>;
     get _stack(): any {
         let parent = (this.props as any).glContainer.parent.parent;
-        if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1)
+        if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
             return parent.parent.contentItems[1];
+        }
         return parent;
     }
     constructor(props: any) {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index b62d3f7bb..b2d016934 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -40,7 +40,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
                 // console.log(this.props.Document[HeightSym]());
             },
             { fireImmediately: true }
-        )
+        );
     }
 
     public static LayoutString(fieldKey: string = "data") {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 14a7d19d0..faea8d44d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -87,8 +87,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             let columnDocs = DocListCast(schemaDoc.data);
             if (columnDocs) {
                 let ddoc = columnDocs.find(doc => doc.title === columnName);
-                if (ddoc)
+                if (ddoc) {
                     return ddoc;
+                }
             }
         }
         return this.props.Document;
@@ -285,7 +286,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
-        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth);
+        - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth)
 
 
     get documentKeysCheckList() {
@@ -334,7 +335,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             columns={this.tableColumns}
             column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
             getTrProps={this.getTrProps}
-        />
+        />;
     }
 
     @computed
@@ -360,7 +361,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             addDocTab={this.props.addDocTab}
             setPreviewScript={this.setPreviewScript}
             previewScript={this.previewScript}
-        />
+        />;
     }
     @action
     setPreviewScript = (script: string) => {
@@ -409,7 +410,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
     }
     private PanelWidth = () => this.nativeWidth * this.contentScaling();
     private PanelHeight = () => this.nativeHeight * this.contentScaling();
-    private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling())
+    private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling());
     get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; }
     @action
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e1ac3505b..f5ad4ee95 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -101,7 +101,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             let childFocus = (doc: Doc) => {
                 doc.libraryBrush = true;
                 this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered.
-            }
+            };
             return (<div className="collectionStackingView-masonryDoc"
                 key={d[Id]}
                 ref={dref}
@@ -131,7 +131,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     whenActiveChanged={this.props.whenActiveChanged}
                 />
             </div>);
-        })
+        });
     }
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b13694e9d..3e495b734 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -67,8 +67,8 @@ class TreeView extends React.Component<TreeViewProps> {
     @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; }
+    @action onMouseEnter = () => { this._isOver = true; };
+    @action onMouseLeave = () => { this._isOver = false; };
 
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active() && (this.props.document.libraryBrush = true);
@@ -89,8 +89,8 @@ class TreeView extends React.Component<TreeViewProps> {
         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";
+        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();
@@ -192,7 +192,7 @@ class TreeView extends React.Component<TreeViewProps> {
             if (inside) {
                 let docList = Cast(this.props.document.data, listSpec(Doc));
                 if (docList !== undefined) {
-                    addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }
+                    addDoc = (doc: Doc) => { docList && docList.push(doc); return true; };
                 }
             }
             let added = false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index c4dd534ed..be75c6c5c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -109,7 +109,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
                     }
                     return match || found;
                 }, false)) {
-                    drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] })
+                    drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
                 }
             });
             return drawnPairs;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 250b6bf5d..84841e469 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -342,7 +342,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private childViews = () => [
         <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
         ...this.views
-    ];
+    ]
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         const easing = () => this.props.Document.panTransformType === "Ease";
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index dedcc3172..05dc6f534 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -321,7 +321,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                             summary.imageSummary = imageSummary;
                             this.props.addDocument(imageSummary, false);
                         }
-                    })
+                    });
                     newCollection.proto!.summaryDoc = summary;
                     selected = [newCollection];
                     newCollection.x = bounds.left + bounds.width;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f6b1c62ee..858959d81 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -48,7 +48,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.zoom);
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
         this.props.bringToFront(this.props.Document);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7c058d91c..4992669df 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -168,8 +168,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => {
         setTimeout(() => {
             let now = Date.now();
-            let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1
-            doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress
+            let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1;
+            doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
             doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
             cb && cb(progress);
             if (now < stime + 200) {
@@ -229,7 +229,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (minimizedDoc) {
             let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(
                 NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
-            this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs))
+            this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
         }
     }
 
@@ -372,8 +372,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this._lastTap = Date.now();
     }
 
-    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }
-    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") };
+    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
+    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); };
     makeBtnClicked = (): void => {
         let doc = Doc.GetProto(this.props.Document);
         doc.isButton = !BoolCast(doc.isButton, false);
@@ -527,7 +527,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; };
 
     isSelected = () => SelectionManager.IsSelected(this);
-    @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }
+    @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
 
     @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
     @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 36d902c4f..df12f261b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -161,7 +161,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
                         Cast(stackDoc.data, listSpec(Doc))!.push(doc);
                         return true;
-                    })
+                    });
                 }
             }
         }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 6b8b64c5f..f208ce2ce 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -188,8 +188,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     }
     @action onError = () => {
         let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
-        if (timeout < 10)
+        if (timeout < 10) {
             setTimeout(this.retryPath, Math.min(10000, timeout * 5));
+        }
     }
     _curSuffix = "_m";
     render() {
diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx
index e92dcacbf..1f49e0d2f 100644
--- a/src/client/views/pdf/PDFAnnotationLayer.tsx
+++ b/src/client/views/pdf/PDFAnnotationLayer.tsx
@@ -19,6 +19,6 @@ export class PDFAnnotationLayer extends React.Component {
             <div className="pdfAnnotationLayer-cont" style={{ width: "100%", height: "100%", position: "relative", top: "-200%" }} onPointerDown={this.onPointerDown}>
 
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 7817e8c26..2ed891131 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -162,6 +162,6 @@ export default class PDFMenu extends React.Component {
                 </button>
                 <div className="pdfMenu-dragger" onPointerDown={this.dragStart} style={{ width: this._pinned ? "20px" : "0px" }} />
             </div >
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 69372f43b..8c0aaea00 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -198,7 +198,7 @@ class Viewer extends React.Component<IViewerProps> {
                 <Page
                     pdf={this.props.pdf}
                     page={page}
-                    numPages={this.props.pdf!.numPages}
+                    numPages={this.props.pdf.numPages}
                     key={`rendered-${page + 1}`}
                     name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
                     pageLoaded={this.pageLoaded}
@@ -219,7 +219,7 @@ class Viewer extends React.Component<IViewerProps> {
     @action
     getPageImage = async (page: number) => {
         let handleError = () => this.getRenderedPage(page);
-        if (this._isPage[page] != "image") {
+        if (this._isPage[page] !== "image") {
             this._isPage[page] = "image";
             const address = this.props.url;
             let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
diff --git a/src/server/index.ts b/src/server/index.ts
index 7ef542b01..2901f61ed 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -172,7 +172,7 @@ function LoadPage(file: string, pageNumber: number, res: Response) {
                     canvasContext: canvasAndContext.context,
                     viewport: viewport,
                     canvasFactory: factory
-                }
+                };
                 console.log("read " + pageNumber);
 
                 page.render(renderContext).promise
-- 
cgit v1.2.3-70-g09d2


From a8664face6f19cd2373bd928c2a2b3043bb6278d Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 2 Jul 2019 19:02:51 -0400
Subject: Added arrange script to freeform views

---
 src/client/views/MainView.tsx                      |  4 +-
 src/client/views/OverlayView.tsx                   | 46 ++++++++++++++++++++++
 src/client/views/ScriptBox.tsx                     | 36 +++++++++++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 41 ++++++++++++++++++-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 ++++--
 src/client/views/nodes/KeyValueBox.tsx             | 12 ++++--
 6 files changed, 141 insertions(+), 10 deletions(-)
 create mode 100644 src/client/views/OverlayView.tsx
 create mode 100644 src/client/views/ScriptBox.tsx

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 04ecc47cf..cc39dc04c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu';
 import { InkTool } from '../../new_fields/InkField';
 import _ from "lodash";
 import KeyManager from './GlobalKeyHandler';
+import { OverlayView } from './OverlayView';
 
 @observer
 export class MainView extends React.Component {
@@ -291,7 +292,7 @@ export class MainView extends React.Component {
             } else {
                 CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
             }
-        }
+        };
         let flyout = <DocumentView
             Document={CurrentUserUtils.UserDocument}
             DataDoc={undefined}
@@ -441,6 +442,7 @@ export class MainView extends React.Component {
                 {this.miscButtons}
                 <PDFMenu />
                 <MainOverlayTextBox />
+                <OverlayView />
             </div>
         );
     }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
new file mode 100644
index 000000000..72f1068ce
--- /dev/null
+++ b/src/client/views/OverlayView.tsx
@@ -0,0 +1,46 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+
+export type OverlayDisposer = () => void;
+
+export type OverlayElementOptions = {
+    x: number;
+    y: number;
+    width?: number;
+    height?: number;
+};
+
+@observer
+export class OverlayView extends React.Component {
+    public static Instance: OverlayView;
+    @observable.shallow
+    private _elements: { ele: JSX.Element, options: OverlayElementOptions }[] = [];
+
+    constructor(props: any) {
+        super(props);
+        if (!OverlayView.Instance) {
+            OverlayView.Instance = this;
+        }
+    }
+
+    @action
+    addElement(ele: JSX.Element, options: OverlayElementOptions): OverlayDisposer {
+        const eleWithPosition = { ele, options };
+        this._elements.push(eleWithPosition);
+        return action(() => {
+            const index = this._elements.indexOf(eleWithPosition);
+            if (index !== -1) this._elements.splice(index, 1);
+        });
+    }
+
+    render() {
+        return (
+            <div>
+                {this._elements.map(({ ele, options: { x, y, width, height } }) => (
+                    <div style={{ position: "absolute", transform: `translate(${x}px, ${y}px)`, width, height }}>{ele}</div>
+                ))}
+            </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
new file mode 100644
index 000000000..aea9d52a4
--- /dev/null
+++ b/src/client/views/ScriptBox.tsx
@@ -0,0 +1,36 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+
+export interface ScriptBoxProps {
+    onSave: (text: string, onError: (error: string) => void) => void;
+    onCancel?: () => void;
+}
+
+@observer
+export class ScriptBox extends React.Component<ScriptBoxProps> {
+    @observable
+    private _scriptText: string = "";
+
+    @action
+    onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+        this._scriptText = e.target.value;
+    }
+
+    @action
+    onError = (error: string) => {
+        console.log(error);
+    }
+
+    render() {
+        return (
+            <div className="scriptBox-outerDiv">
+                <div className="scriptBox-toolbar">
+                    <button onClick={e => { this.props.onSave(this._scriptText, this.onError); e.stopPropagation(); }}>Save</button>
+                    <button onClick={e => { this.props.onCancel && this.props.onCancel(); e.stopPropagation(); }}>Cancel</button>
+                </div>
+                <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText}></textarea>
+            </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 169dbe540..23c0fdd0d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -27,6 +27,10 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { ScriptField } from "../../../../new_fields/ScriptField";
+import { OverlayView } from "../../OverlayView";
+import { ScriptBox } from "../../ScriptBox";
+import { CompileScript } from "../../../util/Scripting";
 
 
 export const panZoomSchema = createSchema({
@@ -388,6 +392,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         };
     }
 
+    getCalculatedPositions(doc: Doc, index: number, collection: Doc): { x?: number, y?: number, width?: number, height?: number } | undefined {
+        const script = Cast(this.props.Document.arrangeScript, ScriptField);
+        if (!script) {
+            return undefined;
+        }
+        const result = script.script.run({ doc, index, collection });
+        if (!result.success) {
+            return undefined;
+        }
+        return result.result;
+    }
+
     @computed.struct
     get views() {
         let curPage = FieldValue(this.Document.curPage, -1);
@@ -397,7 +413,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             if (Math.round(page) === Math.round(curPage) || page === -1) {
                 let minim = BoolCast(doc.isMinimized, false);
                 if (minim === undefined || !minim) {
-                    prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getChildDocumentViewProps(doc)} />);
+                    const pos = this.getCalculatedPositions(doc, prev.length, this.Document) || {};
+                    prev.push(<CollectionFreeFormDocumentView key={doc[Id]} x={pos.x} y={pos.y} width={pos.width} height={pos.height} {...this.getChildDocumentViewProps(doc)} />);
                 }
             }
             return prev;
@@ -438,6 +455,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 }
             }
         });
+        ContextMenu.Instance.addItem({
+            description: "Add freeform arrangement",
+            event: () => {
+                let overlayDisposer: () => void;
+                let scriptingBox = <ScriptBox onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+                    const script = CompileScript(text, {
+                        params: {
+                            doc: "Doc", index: "number", collection: "Doc"
+                        },
+                        requiredType: "{x: number, y: number, width?: number, height?: number}",
+                        typecheck: false
+                    });
+                    if (!script.compiled) {
+                        onError(script.errors.map(error => error.messageText).join("\n"));
+                        return;
+                    }
+                    this.props.Document.arrangeScript = new ScriptField(script);
+                    overlayDisposer();
+                }} />;
+                overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 });
+            }
+        });
     }
 
     private childViews = () => [
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 858959d81..8556817cc 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -9,6 +9,10 @@ import "./DocumentView.scss";
 import React = require("react");
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
+    x?: number;
+    y?: number;
+    width?: number;
+    height?: number;
 }
 
 const schema = createSchema({
@@ -23,13 +27,13 @@ const FreeformDocument = makeInterface(schema, positionSchema);
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
     @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; }
-    @computed get X() { return FieldValue(this.Document.x, 0); }
-    @computed get Y() { return FieldValue(this.Document.y, 0); }
+    @computed get X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+    @computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
+    @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
+    @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
     @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); }
     @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
     @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
-    @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.width, 0); }
-    @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.height, 0); }
 
     set width(w: number) {
         this.Document.width = w;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 0e798d291..9407d742c 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -9,7 +9,7 @@ import { KeyValuePair } from "./KeyValuePair";
 import React = require("react");
 import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
 import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
 import { SetupDrag } from "../../util/DragManager";
 import { Docs } from "../../documents/Documents";
 import { RawDataOperationParameters } from "../../northstar/model/idea/idea";
@@ -50,7 +50,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         let eq = value.startsWith("=");
         let target = eq ? doc : Doc.GetProto(doc);
         value = eq ? value.substr(1) : value;
-        let dubEq = value.startsWith(":=");
+        let dubEq = value.startsWith(":=") ? 1 : value.startsWith(";=") ? 2 : 0;
         value = dubEq ? value.substr(2) : value;
         let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
         if (dubEq) options.typecheck = false;
@@ -58,8 +58,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         if (!script.compiled) {
             return false;
         }
-        let field = new ComputedField(script);
-        if (!dubEq) {
+        let field: Field;
+        if (dubEq === 1) {
+            field = new ComputedField(script);
+        } else if (dubEq === 2) {
+            field = new ScriptField(script);
+        } else {
             let res = script.run({ this: target });
             if (!res.success) return false;
             field = res.result;
-- 
cgit v1.2.3-70-g09d2


From d059c64a7b3ce424b8c41c485c7c58c0d4bfbae8 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 2 Jul 2019 21:42:08 -0400
Subject: fixed doc decorations border for documents laid out with
 arrangeScripts

---
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8556817cc..5171f7a04 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -52,7 +52,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.zoom)
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom * NumCast(this.props.Document.width, this.width) / this.width)
 
     animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
         this.props.bringToFront(this.props.Document);
-- 
cgit v1.2.3-70-g09d2


From 5e35e1895bb54568bbf8cc3991480e20edd1de83 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 2 Jul 2019 23:28:57 -0400
Subject: fixed issue with editing applied template text boxes.

---
 src/client/views/MainOverlayTextBox.tsx                   | 7 +++++--
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++-
 src/client/views/nodes/FormattedTextBox.tsx               | 2 +-
 3 files changed, 8 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index d31319429..7d15702a2 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -30,6 +30,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     private _textBox: FormattedTextBox | undefined;
     private _tooltip?: HTMLElement;
     @observable public TextDoc?: Doc;
+    @observable public TextDataDoc?: Doc;
 
     updateTooltip = () => {
         this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip);
@@ -43,13 +44,15 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             (box?: FormattedTextBox) => {
                 this._textBox = box;
                 if (box) {
-                    this.TextDoc = box.props.DataDoc;
+                    this.TextDoc = box.props.Document;
+                    this.TextDataDoc = box.props.DataDoc;
                     let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
                     let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); };
                     this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content");
                 }
                 else {
                     this.TextDoc = undefined;
+                    this.TextDataDoc = undefined;
                     this.setTextDoc();
                 }
             });
@@ -114,7 +117,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         }
     }
     render() {
-        this.TextDoc;
+        this.TextDoc; this.TextDataDoc;
         if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) {
             let textRect = this._textTargetDiv.getBoundingClientRect();
             let s = this._textXf().Scale;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5171f7a04..3f9270597 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -47,12 +47,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             this.Document.width = this.nativeWidth / this.nativeHeight * h;
         }
     }
+    @computed get scaleToOverridingWidth() { return this.width / NumCast(this.props.Document.width, this.width); }
     contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.zoom * NumCast(this.props.Document.width, this.width) / this.width)
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom / this.scaleToOverridingWidth)
 
     animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
         this.props.bringToFront(this.props.Document);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 07cd43ce3..1eeb04755 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -211,7 +211,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
                 return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
             },
-            field => this._editorView && !this._applyingChange && this.props.Document[this.props.fieldKey] instanceof RichTextField &&
+            field => this._editorView && !this._applyingChange &&
                 this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
         );
         this.setupEditor(config, this.dataDoc, this.props.fieldKey);
-- 
cgit v1.2.3-70-g09d2


From c77dfd3fcb2df644fcf2c177db7e6e376ca4c3a9 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 3 Jul 2019 14:29:53 -0400
Subject: quick and dirty arrange transition animations

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx  | 5 ++++-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx            | 3 ++-
 2 files changed, 6 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index cb7be3f28..6b0cb5728 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
 import { action, computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
@@ -458,6 +458,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         ContextMenu.Instance.addItem({
             description: "Add freeform arrangement",
             event: () => {
+                const docs = DocListCast(this.Document[this.props.fieldKey]);
+                docs.map(d => d.transition = "transform 1s");
                 let overlayDisposer: () => void;
                 let scriptingBox = <ScriptBox onCancel={() => overlayDisposer()} onSave={(text, onError) => {
                     const script = CompileScript(text, {
@@ -473,6 +475,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     }
                     this.props.Document.arrangeScript = new ScriptField(script);
                     overlayDisposer();
+                    setTimeout(() => docs.map(d => d.transition = undefined), 1200);
                 }} />;
                 overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 });
             }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3f9270597..30cf74f3e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,7 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { BoolCast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
 import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
@@ -86,6 +86,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     backgroundColor: "transparent",
                     borderRadius: `${this.borderRounding()}px`,
                     transform: this.transform,
+                    transition: StrCast(this.props.Document.transition),
                     width: this.width,
                     height: this.height,
                     zIndex: this.Document.zIndex || 0,
-- 
cgit v1.2.3-70-g09d2


From d479f5e6bcdb7d1a0edb2f8cf366549abe2a910b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 4 Jul 2019 14:08:00 -0400
Subject: added text font color setting.   adding interactive border radius.
 moved main toolbar.

---
 src/client/documents/Documents.ts                  |  4 +--
 src/client/util/DragManager.ts                     |  1 +
 src/client/util/RichTextSchema.tsx                 | 11 ++++++++
 src/client/util/TooltipTextMenu.tsx                |  3 +-
 src/client/views/DocumentDecorations.scss          | 22 +++++++++++++++
 src/client/views/DocumentDecorations.tsx           | 32 ++++++++++++++++++++++
 src/client/views/InkingControl.tsx                 |  3 ++
 src/client/views/Main.scss                         |  2 +-
 src/client/views/MainOverlayTextBox.tsx            |  5 ++++
 src/client/views/MainView.tsx                      |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 16 ++++-------
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 15 ++++++----
 src/client/views/nodes/DocumentView.tsx            |  4 +--
 src/client/views/nodes/FormattedTextBox.tsx        | 11 +++++++-
 14 files changed, 106 insertions(+), 25 deletions(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7a976e7d7..2bddf053a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -77,7 +77,7 @@ export interface DocumentOptions {
     backgroundLayout?: string;
     curPage?: number;
     documentText?: string;
-    borderRounding?: number;
+    borderRounding?: string;
     schemaColumns?: List<string>;
     dockingConfig?: string;
     dbDoc?: Doc;
@@ -93,7 +93,7 @@ export namespace DocUtils {
         if (target === CurrentUserUtils.UserDocument) return;
 
         UndoManager.RunInBatch(() => {
-            let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 });
+            let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: "100%" });
             linkDoc.type = DocTypes.LINK;
             let linkDocProto = Doc.GetProto(linkDoc);
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 7dc48fb78..d4b1bc8f4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -315,6 +315,7 @@ export namespace DragManager {
             scaleYs.push(scaleY);
             let dragElement = ele.cloneNode(true) as HTMLElement;
             dragElement.style.opacity = "0.7";
+            dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
             dragElement.style.position = "absolute";
             dragElement.style.margin = "0";
             dragElement.style.top = "0";
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 63c879d67..2a57180d3 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -348,6 +348,17 @@ export const marks: { [index: string]: MarkSpec } = {
         }]
     },
 
+    pFontColor: {
+        attrs: {
+            color: { default: "yellow" }
+        },
+        parseDOM: [{ style: 'background: #d9dbdd' }],
+        toDOM: (node) => {
+            return ['span', {
+                style: `color: ${node.attrs.color}`
+            }];
+        }
+    },
 
     /** FONT SIZES */
     pFontSize: {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 9f8d0b2f6..e3e26d1f4 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -111,7 +111,8 @@ export class TooltipTextMenu {
         this.fontSizeToNum.set(schema.marks.p32, 32);
         this.fontSizeToNum.set(schema.marks.p48, 48);
         this.fontSizeToNum.set(schema.marks.p72, 72);
-        //this.fontSizeToNum.set(schema.marks.pFontSize,schema.marks.pFontSize.)
+        this.fontSizeToNum.set(schema.marks.pFontSize, 10);
+        this.fontSizeToNum.set(schema.marks.pFontSize, 10);
         this.fontSizes = Array.from(this.fontSizeToNum.keys());
 
         //list types
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ba9f32d7d..2d430512b 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -26,6 +26,14 @@ $linkGap : 3px;
         opacity: 0.8;
     }
 
+    .documentDecorations-radius {
+        pointer-events: auto;
+        background: black;
+        opacity: 0.8;
+        transform: translate(10px, 10px);
+        grid-row: 4;
+    }
+
     #documentDecorations-topLeftResizer,
     #documentDecorations-leftResizer,
     #documentDecorations-bottomLeftResizer {
@@ -44,11 +52,25 @@ $linkGap : 3px;
         grid-column-start: 5;
         grid-column-end: 7;
     }
+    
+    #documentDecorations-borderRadius{
+        grid-column-start: 5;
+        grid-column-end: 7;
+        border-radius: 100%;
+        .borderRadiusTooltip{
+            width:10px;
+            height:10px;
+            position:absolute;
+        }
+    }
 
     #documentDecorations-topLeftResizer,
     #documentDecorations-bottomRightResizer {
         cursor: nwse-resize;
     }
+    #documentDecorations-bottomRightResizer {
+        grid-row:4;
+    }
 
     #documentDecorations-topRightResizer,
     #documentDecorations-bottomLeftResizer {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3df520428..c7990647a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -341,6 +341,37 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         iconDoc.y = where[1] + NumCast(selView.props.Document.y);
     }
 
+    _radiusDown = [0, 0];
+    @action
+    onRadiusDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        if (e.button === 0) {
+            this._radiusDown = [e.clientX, e.clientY];
+            this._isPointerDown = true;
+            this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
+            document.removeEventListener("pointermove", this.onRadiusMove);
+            document.removeEventListener("pointerup", this.onRadiusUp);
+            document.addEventListener("pointermove", this.onRadiusMove);
+            document.addEventListener("pointerup", this.onRadiusUp);
+        }
+    }
+
+    onRadiusMove = (e: PointerEvent): void => {
+        let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1]));
+        SelectionManager.SelectedDocuments().map(dv => Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`);
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    onRadiusUp = (e: PointerEvent): void => {
+        e.stopPropagation();
+        e.preventDefault();
+        this._isPointerDown = false;
+        this._resizeUndo && this._resizeUndo.end();
+        document.removeEventListener("pointermove", this.onRadiusMove);
+        document.removeEventListener("pointerup", this.onRadiusUp);
+    }
+
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
@@ -705,6 +736,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                <div id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
                 <div className="link-button-container">
                     <div className="linkButtonWrapper">
                         <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton}  </div>
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 0461d7299..c7f7bdb66 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -10,6 +10,8 @@ import { InkTool } from "../../new_fields/InkField";
 import { Doc } from "../../new_fields/Doc";
 import { undoBatch, UndoManager } from "../util/UndoManager";
 import { StrCast } from "../../new_fields/Types";
+import { FormattedTextBox } from "./nodes/FormattedTextBox";
+import { MainOverlayTextBox } from "./MainOverlayTextBox";
 
 library.add(faPen, faHighlighter, faEraser, faBan);
 
@@ -42,6 +44,7 @@ export class InkingControl extends React.Component {
     switchColor = action((color: ColorResult): void => {
         this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
         if (InkingControl.Instance.selectedTool === InkTool.None) {
+            if (MainOverlayTextBox.Instance.SetColor(color.hex)) return;
             let selected = SelectionManager.SelectedDocuments();
             let oldColors = selected.map(view => {
                 let targetDoc = view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document);
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 44c1cb9fc..b85a8040a 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -144,7 +144,7 @@ button:hover {
 #add-nodes-menu {
     position: absolute;
     bottom: 22px;
-    left: 24px;
+    left: 250px;
 
     > label {
         background: $dark-color;
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 7d15702a2..d8aaea259 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -36,6 +36,11 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip);
     }
 
+    public SetColor(color: string) {
+        return this._textBox && this._textBox.setFontColor(color);
+    }
+
+
     constructor(props: MainOverlayTextBoxProps) {
         super(props);
         this._textProxyDiv = React.createRef();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1542fedef..61ccf4c1d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -354,7 +354,7 @@ export class MainView extends React.Component {
             [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
         ];
 
-        return < div id="add-nodes-menu" >
+        return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 5 }} >
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
             <label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label>
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 31c0fe499..4850c6218 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -44,14 +44,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     _commandExecuted = false;
 
     @action
-    cleanupInteractions = (all: boolean = false, rem_keydown: boolean = true) => {
+    cleanupInteractions = (all: boolean = false) => {
         if (all) {
             document.removeEventListener("pointerup", this.onPointerUp, true);
             document.removeEventListener("pointermove", this.onPointerMove, true);
         }
-        if (rem_keydown) {
-            document.removeEventListener("keydown", this.marqueeCommand, true);
-        }
+        document.removeEventListener("keydown", this.marqueeCommand, true);
         this._visible = false;
     }
 
@@ -191,12 +189,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
             }
             this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
-            mselect.length ? this.cleanupInteractions(true, false) : this.cleanupInteractions(true);
-        }
-        else {
-            //console.log("invisible");
-            this.cleanupInteractions(true);
         }
+        //console.log("invisible");
+        this.cleanupInteractions(true);
 
         if (e.altKey) {
             e.preventDefault();
@@ -266,13 +261,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             }
             let ink = Cast(this.props.container.props.Document.ink, InkField);
             let inkData = ink ? ink.inkData : undefined;
-            let zoomBasis = NumCast(this.props.container.props.Document.scale, 1);
             let newCollection = Docs.FreeformDocument(selected, {
                 x: bounds.left,
                 y: bounds.top,
                 panX: 0,
                 panY: 0,
-                borderRounding: e.key === "e" ? -1 : undefined,
+                borderRounding: e.key === "e" ? "100%" : undefined,
                 backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white",
                 width: bounds.width,
                 height: bounds.height,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 30cf74f3e..1c00687ed 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -70,11 +70,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
-        let br = NumCast(this.props.Document.borderRounding);
-        return br >= 0 ? br :
-            NumCast(this.props.Document.nativeWidth) === 0 ?
-                Math.min(this.props.PanelWidth(), this.props.PanelHeight())
-                : Math.min(this.Document.nativeWidth || 0, this.Document.nativeHeight || 0);
+        let br = StrCast(this.props.Document.borderRounding);
+        if (br.endsWith("%")) {
+            let percent = Number(br.substr(0, br.length - 1)) / 100;
+            let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight));
+            let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
+            return minDim;
+        }
+        return undefined;
     }
 
     render() {
@@ -84,7 +87,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     transformOrigin: "left top",
                     position: "absolute",
                     backgroundColor: "transparent",
-                    borderRadius: `${this.borderRounding()}px`,
+                    borderRadius: this.borderRounding(),
                     transform: this.transform,
                     transition: StrCast(this.props.Document.transition),
                     width: this.width,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f751a45fe..a84cac37f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -589,9 +589,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 style={{
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
-                    outlineWidth: BoolCast(this.props.Document.libraryBrush) && !NumCast(this.props.Document.borderRounding) ?
+                    outlineWidth: BoolCast(this.props.Document.libraryBrush) && !StrCast(this.props.Document.borderRounding) ?
                         `${this.props.ScreenToLocalTransform().Scale}px` : "0px",
-                    border: BoolCast(this.props.Document.libraryBrush) && NumCast(this.props.Document.borderRounding) ?
+                    border: BoolCast(this.props.Document.libraryBrush) && StrCast(this.props.Document.borderRounding) ?
                         `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined,
                     borderRadius: "inherit",
                     background: backgroundColor,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 1eeb04755..bf6f4c764 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -97,6 +97,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         return "";
     }
 
+    @undoBatch
+    public setFontColor(color: string) {
+        if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
+        let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color });
+        this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from,
+            this._editorView!.state.selection.to, colorMark));
+        return true;
+    }
+
     constructor(props: FieldViewProps) {
         super(props);
         if (this.props.outer_div) {
@@ -418,7 +427,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
     render() {
         let style = this.props.isOverlay ? "scroll" : "hidden";
-        let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : "";
+        let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
         let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
         return (
             <div className={`formattedTextBox-cont-${style}`} ref={this._ref}
-- 
cgit v1.2.3-70-g09d2


From bc4e7ceb8a738b890344c4564ab0d97e0a10293d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 9 Jul 2019 10:57:58 -0400
Subject: fixed some template issues, added text templates.

---
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  3 +-
 src/client/views/nodes/DocumentContentsView.tsx    | 27 +++++++++---
 src/client/views/nodes/DocumentView.tsx            | 15 ++++++-
 src/client/views/nodes/FormattedTextBox.tsx        | 48 +++++++++++++---------
 5 files changed, 65 insertions(+), 30 deletions(-)

(limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 48b89ff4e..d6cf793ab 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -357,7 +357,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
     onRadiusMove = (e: PointerEvent): void => {
         let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1]));
-        SelectionManager.SelectedDocuments().map(dv => Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`);
+        SelectionManager.SelectedDocuments().map(dv => dv.props.Document.borderRounding = Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`);
         e.stopPropagation();
         e.preventDefault();
     }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 1c00687ed..b09538d1a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -7,6 +7,7 @@ import { DocComponent } from "../DocComponent";
 import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     x?: number;
@@ -70,7 +71,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
-        let br = StrCast(this.props.Document.borderRounding);
+        let br = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.borderRounding : this.props.Document.borderRounding);
         if (br.endsWith("%")) {
             let percent = Number(br.substr(0, br.length - 1)) / 100;
             let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight));
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 56a14e26e..745520bc4 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -49,12 +49,11 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     hideOnLeave?: boolean
 }> {
     @computed get layout(): string {
-        let layoutDoc = this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
-        const layout = Cast(layoutDoc[this.props.layoutKey], "string");
+        const layout = Cast(this.layoutDoc[this.props.layoutKey], "string");
         if (layout === undefined) {
             return this.props.Document.data ?
                 "<FieldView {...props} fieldKey='data' />" :
-                KeyValueBox.LayoutString(layoutDoc.proto ? "proto" : "");
+                KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
         } else if (typeof layout === "string") {
             return layout;
         } else {
@@ -62,8 +61,23 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         }
     }
 
-    CreateBindings(layoutDoc?: Doc): JsxBindings {
-        return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: layoutDoc } };
+    get dataDoc() {
+        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+            // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
+            // has a template layout document, then we will render the template layout but use 
+            // this document as the data document for the layout.
+            return this.props.Document;
+        }
+        return this.props.DataDoc
+    }
+    get layoutDoc() {
+        // if this document's layout field contains a document (ie, a rendering template), then we will use that
+        // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
+        return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+    }
+
+    CreateBindings(): JsxBindings {
+        return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: this.layoutDoc, DataDoc: this.dataDoc } };
     }
 
     @computed get templates(): List<string> {
@@ -105,11 +119,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     render() {
+        let self = this;
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
             components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
-            bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
+            bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
             onError={(test: any) => { console.log(test); }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d027ed516..47ec3b5a1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -218,7 +218,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         e.stopPropagation();
     }
 
-    get dataDoc() { return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; }
+    get dataDoc() {
+        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+            // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
+            // has a template layout document, then we will render the template layout but use 
+            // this document as the data document for the layout.
+            return this.props.Document;
+        }
+        return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
+    }
     startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
         if (this._mainCont.current) {
             let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
@@ -585,20 +593,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
     @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
     @computed get contents() {
-        return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} />);
+        return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} DataDoc={this.dataDoc} />);
     }
 
     render() {
         if (this.Document.hidden) {
             return null;
         }
+        let self = this;
         let backgroundColor = this.props.Document.layout instanceof Doc ? StrCast(this.props.Document.layout.backgroundColor) : this.Document.backgroundColor;
+        let foregroundColor = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.color : this.props.Document.color);
         var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
+                    color: foregroundColor,
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
                     outlineWidth: BoolCast(this.props.Document.libraryBrush) && !StrCast(this.props.Document.borderRounding) ?
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index bf6f4c764..68a4372d5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -99,7 +99,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     @undoBatch
     public setFontColor(color: string) {
+        let self = this;
         if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
+        if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) {
+            this.props.Document.color = color;
+        }
         let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color });
         this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from,
             this._editorView!.state.selection.to, colorMark));
@@ -160,25 +164,28 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             e.stopPropagation();
         } else {
             if (de.data instanceof DragManager.DocumentDragData) {
-                let ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
-                if (!ldocs) {
-                    this.dataDoc.subBulletDocs = new List<Doc>([]);
-                }
-                ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
-                if (!ldocs) return;
-                if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
-                    ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.dataDoc.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
-                    this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
-                    this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
-                    this.props.Document.isBullet = true;
-                }
-                let stackDoc = (ldocs[0] as Doc);
-                if (de.data.moveDocument) {
-                    de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
-                        Cast(stackDoc.data, listSpec(Doc))!.push(doc);
-                        return true;
-                    });
-                }
+                this.props.Document.layout = de.data.draggedDocuments[0];
+                de.data.draggedDocuments[0].isTemplate = true;
+                e.stopPropagation();
+                // let ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
+                // if (!ldocs) {
+                //     this.dataDoc.subBulletDocs = new List<Doc>([]);
+                // }
+                // ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
+                // if (!ldocs) return;
+                // if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
+                //     ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.dataDoc.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
+                //     this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
+                //     this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
+                //     this.props.Document.isBullet = true;
+                // }
+                // let stackDoc = (ldocs[0] as Doc);
+                // if (de.data.moveDocument) {
+                //     de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
+                //         Cast(stackDoc.data, listSpec(Doc))!.push(doc);
+                //         return true;
+                //     });
+                // }
             }
         }
     }
@@ -426,6 +433,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems });
     }
     render() {
+        let self = this;
         let style = this.props.isOverlay ? "scroll" : "hidden";
         let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
         let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
@@ -435,7 +443,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     height: this.props.height ? this.props.height : undefined,
                     background: this.props.hideOnLeave ? "rgba(0,0,0,0.4)" : undefined,
                     opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || this.props.Document.libraryBrush ? 1 : 0.1) : 1,
-                    color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "initial",
+                    color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
                     pointerEvents: interactive ? "all" : "none",
                     fontSize: "13px"
                 }}
-- 
cgit v1.2.3-70-g09d2

