From 20a8b740cb7083d3b18272f51c823be289406bfb Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 6 Feb 2020 20:24:53 -0500
Subject: very hacky fix...

---
 src/client/views/collections/CollectionStackingView.tsx | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 91c7ca76e..01a2f1042 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -40,8 +40,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
     @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
-    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * this.gridGap); }
-    @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 2 * this.gridGap)); }
+    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 0); }// 2 * this.gridGap); }
+    @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
     @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
     @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -55,8 +55,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     children(docs: Doc[]) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
-            const width = () => Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
             const height = () => this.getDocHeight(d);
+            const width = () => (this.widthScale ? this.widthScale : 1) * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
             const dref = React.createRef<HTMLDivElement>();
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
@@ -377,6 +377,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         }
         return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
     }
+    @computed get heightScale() {
+        return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), this.props.Document[HeightSym]() / this.props.PanelHeight());
+    }
+    @computed get widthScale() {
+        return StrCast(this.props.Document.title).includes("slide") ? this.heightScale : undefined;
+    }
     render() {
         TraceMobx();
         const editableViewProps = {
@@ -391,8 +397,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     style={{
                         overflowY: this.props.active() ? "auto" : "hidden",
                         transform: `scale(${Math.min(1, this.props.PanelHeight() / this.layoutDoc[HeightSym]())})`,
-                        height: `${Math.max(100, 100 * 1 / Math.min(this.props.PanelWidth() / this.layoutDoc[WidthSym](), this.props.PanelHeight() / this.layoutDoc[HeightSym]()))}%`,
-                        transformOrigin: "top"
+                        height: `${Math.max(100, 100 * this.heightScale)}%`,
+                        width: this.widthScale ? `${Math.max(100, 100 * this.widthScale)}%` : undefined,
+                        transformOrigin: this.widthScale ? "top left" : "top",
                     }}
                     onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
                     onDrop={this.onDrop.bind(this)}
-- 
cgit v1.2.3-70-g09d2


From 9f82ece7c763ba4a054d86a715311e0280fcb79f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 7 Feb 2020 01:22:48 -0500
Subject: sped up pivot viewer by not recreating docs when pivot changes.

---
 .../views/collections/CollectionStackingView.tsx   |  6 ++--
 .../CollectionStackingViewFieldColumn.tsx          |  2 +-
 src/client/views/collections/CollectionSubView.tsx | 29 +---------------
 .../views/collections/CollectionTimeView.scss      |  5 +++
 .../views/collections/CollectionTimeView.tsx       | 39 +++++++++++++++-------
 .../CollectionFreeFormLayoutEngines.tsx            | 20 ++++++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 37 +++++++++++++++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  1 +
 8 files changed, 83 insertions(+), 56 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 01a2f1042..e04f4e8a6 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -52,11 +52,11 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
 
-    children(docs: Doc[]) {
+    children(docs: Doc[], columns?: number) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
             const height = () => this.getDocHeight(d);
-            const width = () => (this.widthScale ? this.widthScale : 1) * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+            const width = () => (this.widthScale && !columns ? this.widthScale : 1) * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
             const dref = React.createRef<HTMLDivElement>();
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
@@ -381,7 +381,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), this.props.Document[HeightSym]() / this.props.PanelHeight());
     }
     @computed get widthScale() {
-        return StrCast(this.props.Document.title).includes("slide") ? this.heightScale : undefined;
+        return StrCast(this.props.Document.title).includes("slide") || true ? this.heightScale : undefined;
     }
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index b81b1f31d..21982f1ca 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -407,7 +407,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                                     gridTemplateColumns: singleColumn ? undefined : templatecols,
                                     gridAutoRows: singleColumn ? undefined : "0px"
                                 }}>
-                                {this.props.parent.children(this.props.docList)}
+                                {this.props.parent.children(this.props.docList, uniqueHeadings.length)}
                                 {singleColumn ? (null) : this.props.parent.columnDragger}
                             </div>
                             {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a2700e75a..e0e99d635 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -111,34 +111,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         get childDocs() {
             const docs = DocListCast(this.dataField);
             const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
-            const viewedDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
-            const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []);
-            const clusters: { [key: string]: { [value: string]: string } } = {};
-            for (let i = 0; i < docFilters.length; i += 3) {
-                const [key, value, modifiers] = docFilters.slice(i, i + 3);
-                const cluster = clusters[key];
-                if (!cluster) {
-                    const child: { [value: string]: string } = {};
-                    child[value] = modifiers;
-                    clusters[key] = child;
-                } else {
-                    cluster[value] = modifiers;
-                }
-            }
-            const filteredDocs = docFilters.length ? viewedDocs.filter(d => {
-                for (const key of Object.keys(clusters)) {
-                    const cluster = clusters[key];
-                    const satisfiesFacet = Object.keys(cluster).some(inner => {
-                        const modifier = cluster[inner];
-                        return (modifier === "x") !== Doc.matchFieldValue(d, key, inner);
-                    });
-                    if (!satisfiesFacet) {
-                        return false;
-                    }
-                }
-                return true;
-            }) : viewedDocs;
-            return filteredDocs;
+            return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
         }
 
         @action
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index df5057b5b..a5ce73a92 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -5,6 +5,11 @@
     height: 100%;
     width: 100%;
     overflow: hidden;
+    .collectionTimeView-backBtn {
+        background: green;
+        display: inline;
+        margin-right: 20px;
+    }
     .collectionFreeform-customText {
         text-align: left;
     }
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index fb36a1b0c..0c1f93829 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,27 +1,27 @@
 import { faEdit } from "@fortawesome/free-solid-svg-icons";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, trace } from "mobx";
+import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Set } from "typescript-collections";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
+import { RichTextField } from "../../../new_fields/RichTextField";
 import { listSpec } from "../../../new_fields/Schema";
 import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Docs } from "../../documents/Documents";
+import { Scripting } from "../../util/Scripting";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
 import { EditableView } from "../EditableView";
 import { anchorPoints, Flyout } from "../TemplateMenu";
+import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import "./CollectionTimeView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { CollectionTreeView } from "./CollectionTreeView";
+import "./CollectionTimeView.scss";
 import React = require("react");
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { Scripting } from "../../util/Scripting";
-import { ViewDefResult, ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines";
+import { CollectionTreeView } from "./CollectionTreeView";
+import { ObjectField } from "../../../new_fields/ObjectField";
 
 @observer
 export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -289,7 +289,19 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
             <div className={"collectionTimeView" + (doTimeline ? "" : "-pivot")} onContextMenu={this.specificMenu}
                 style={{ height: `calc(100%  - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
                 <div className={"pivotKeyEntry"}>
-                    <EditableView {...newEditableViewProps} menuCallback={this.menuCallback} />
+                    <button className="collectionTimeView-backBtn" style={{ width: 50, height: 20, background: "green" }}
+                        onClick={action(() => {
+                            let pfilterIndex = NumCast(this.props.Document._pfilterIndex);
+                            if (pfilterIndex > 0) {
+                                this.props.Document._docFilter = ObjectField.MakeCopy(this.props.Document["_pfilter" + --pfilterIndex] as ObjectField);
+                                this.props.Document._pfilterIndex = pfilterIndex;
+                            } else {
+                                this.props.Document._docFilter = new List([]);
+                            }
+                        })}>
+                        back
+                    </button>
+                    <EditableView {...newEditableViewProps} display={"inline"} menuCallback={this.menuCallback} />
                 </div>
                 {!this.props.isSelected() || this.props.PanelHeight() < 100 ? (null) :
                     <div className="collectionTimeView-dragger" key="dragger" onPointerDown={this.onPointerDown} style={{ transform: `translate(${this._facetWidth}px, 0px)` }} >
@@ -308,6 +320,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 }
 
 Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
+    let pfilterIndex = NumCast(pivotDoc._pfilterIndex);
+    pivotDoc["_pfilter" + pfilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilter as ObjectField);
+    pivotDoc._pfilterIndex = ++pfilterIndex;
     pivotDoc._docFilter = new List();
     (bounds.payload as string[]).map(filterVal =>
         Doc.setDocFilter(pivotDoc, StrCast(pivotDoc._pivotField), filterVal, "check"));
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index e354ad0af..95f7794bb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -36,7 +36,6 @@ export interface PoolData {
     color?: string,
     transition?: string,
     highlight?: boolean,
-    state?: any
 }
 
 export interface ViewDefResult {
@@ -71,19 +70,21 @@ interface pivotColumn {
     filters: string[]
 }
 
+
 export function computePivotLayout(
     poolData: Map<string, PoolData>,
     pivotDoc: Doc,
     childDocs: Doc[],
+    filterDocs: Doc[],
     childPairs: { layout: Doc, data?: Doc }[],
     panelDim: number[],
-    viewDefsToJSX: (views: any) => ViewDefResult[]
+    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
     const fieldKey = "data";
     const pivotColumnGroups = new Map<FieldResult<Field>, pivotColumn>();
 
     const pivotFieldKey = toLabel(pivotDoc._pivotField);
-    for (const doc of childDocs) {
+    for (const doc of filterDocs) {
         const val = Field.toString(doc[pivotFieldKey] as Field);
         if (val) {
             !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val] });
@@ -179,7 +180,7 @@ export function computePivotLayout(
     const dividers = sortedPivotKeys.map((key, i) =>
         ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
     groupNames.push(...dividers);
-    return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
+    return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childDocs.filter(c => !filterDocs.includes(c)));
 }
 
 function toNumber(val: FieldResult<Field>) {
@@ -190,9 +191,10 @@ export function computeTimelineLayout(
     poolData: Map<string, PoolData>,
     pivotDoc: Doc,
     childDocs: Doc[],
+    filterDocs: Doc[],
     childPairs: { layout: Doc, data?: Doc }[],
     panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds) => ViewDefResult[]
+    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
     const fieldKey = "data";
     const pivotDateGroups = new Map<number, Doc[]>();
@@ -212,7 +214,7 @@ export function computeTimelineLayout(
 
     let minTime = Number.MAX_VALUE;
     let maxTime = -Number.MAX_VALUE;
-    childDocs.map(doc => {
+    filterDocs.map(doc => {
         const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey])));
         if (!(Number.isNaN(num) || (minTimeReq && num < minTimeReq) || (maxTimeReq && num > maxTimeReq))) {
             !pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
@@ -275,7 +277,7 @@ export function computeTimelineLayout(
     }
 
     const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1, payload: undefined };
-    return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
+    return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childDocs.filter(c => !filterDocs.includes(c)));
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
         keyDocs.forEach(doc => {
@@ -298,7 +300,8 @@ export function computeTimelineLayout(
 }
 
 function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<Doc, ViewDefBounds>,
-    poolData: Map<string, PoolData>, viewDefsToJSX: (views: ViewDefBounds) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[]) {
+    poolData: Map<string, PoolData>, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[],
+    extraDocs: Doc[]) {
 
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as ViewDefBounds);
@@ -323,6 +326,7 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
             poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos });
         }
     });
+    extraDocs.map(ed => poolData.set(ed[Id], { x: 0, y: 0, zIndex: -99 }));
 
     return {
         elements: viewDefsToJSX(extras.concat(groupNames.map(gname => ({
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ea3805b65..2518a4a55 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync, Field } f
 import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
-import { createSchema, makeInterface } from "../../../../new_fields/Schema";
+import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
 import { ScriptField } from "../../../../new_fields/ScriptField";
 import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
@@ -794,12 +794,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }.bind(this));
 
     doTimelineLayout(poolData: Map<string, any>) {
-        return computeTimelineLayout(poolData, this.props.Document, this.childDocs,
+        return computeTimelineLayout(poolData, this.props.Document, this.childDocs, this.filterDocs,
             this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
     }
 
     doPivotLayout(poolData: Map<string, any>) {
-        return computePivotLayout(poolData, this.props.Document, this.childDocs,
+        return computePivotLayout(poolData, this.props.Document, this.childDocs, this.filterDocs,
             this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
     }
 
@@ -812,7 +812,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
         this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
             const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
-            state = pos.state === undefined ? state : pos.state;
             poolData.set(pair.layout[Id], pos);
         });
         return { elements: elements };
@@ -826,6 +825,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }
         return { newPool, computedElementData: this.doFreeformLayout(newPool) };
     }
+
+    @computed get filterDocs() {
+        const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []);
+        const clusters: { [key: string]: { [value: string]: string } } = {};
+        for (let i = 0; i < docFilters.length; i += 3) {
+            const [key, value, modifiers] = docFilters.slice(i, i + 3);
+            const cluster = clusters[key];
+            if (!cluster) {
+                const child: { [value: string]: string } = {};
+                child[value] = modifiers;
+                clusters[key] = child;
+            } else {
+                cluster[value] = modifiers;
+            }
+        }
+        const filteredDocs = docFilters.length ? this.childDocs.filter(d => {
+            for (const key of Object.keys(clusters)) {
+                const cluster = clusters[key];
+                const satisfiesFacet = Object.keys(cluster).some(inner => {
+                    const modifier = cluster[inner];
+                    return (modifier === "x") !== Doc.matchFieldValue(d, key, inner);
+                });
+                if (!satisfiesFacet) {
+                    return false;
+                }
+            }
+            return true;
+        }) : this.childDocs;
+        return filteredDocs;
+    }
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         runInAction(() =>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f484b6115..3bceec45f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -88,6 +88,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 width: this.width,
                 height: this.height,
                 zIndex: this.ZInd,
+                display: this.ZInd === -99 ? "none" : undefined,
                 pointerEvents: this.props.Document.isBackground ? "none" : undefined
             }} >
 
-- 
cgit v1.2.3-70-g09d2


From d845002f60115fdb988b6fc02420fdb25b19c522 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 7 Feb 2020 13:52:13 -0500
Subject: fixed stacking layouts.  fixed templates to copy cached expanded
 templates when docs are aliased

---
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../views/collections/CollectionStackingView.tsx   | 23 ++++++++++++----------
 src/new_fields/Doc.ts                              |  2 +-
 3 files changed, 15 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2478aaecd..cb413b3e3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -469,7 +469,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         e.preventDefault();
                         e.stopPropagation();
                         const dragData = new DragManager.DocumentDragData([doc]);
-                        dragData.dropAction = doc.dropAction;
+                        dragData.dropAction = doc.dropAction === "alias" ? "alias" : doc.dropAction === "copy" ? "copy" : undefined;
                         DragManager.StartDocumentDrag([dragSpan], dragData, e.clientX, e.clientY);
                     }}>
                     <FontAwesomeIcon icon="file" size="lg" />
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e04f4e8a6..9e8600193 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,7 +11,6 @@ import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
 import { emptyFunction, Utils } from "../../../Utils";
-import { DocumentType } from "../../documents/DocumentTypes";
 import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -40,7 +39,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
     @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
-    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 0); }// 2 * this.gridGap); }
+    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * this.gridGap); }
     @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
     @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@@ -56,7 +55,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
             const height = () => this.getDocHeight(d);
-            const width = () => (this.widthScale && !columns ? this.widthScale : 1) * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+            const width = () => this.widthScale * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
             const dref = React.createRef<HTMLDivElement>();
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
@@ -377,11 +376,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         }
         return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
     }
-    @computed get heightScale() {
-        return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), this.props.Document[HeightSym]() / this.props.PanelHeight());
+    @computed get contentScale() {
+        const heightExtra = this.heightPercent > 1 ? this.heightPercent : 1;
+        return Math.max(this.props.Document[WidthSym]() / this.props.PanelWidth(), heightExtra * this.props.Document[HeightSym]() / this.props.PanelHeight());
     }
     @computed get widthScale() {
-        return StrCast(this.props.Document.title).includes("slide") || true ? this.heightScale : undefined;
+        return this.heightPercent < 1 ? Math.max(1, this.contentScale) : 1;
+    }
+    @computed get heightPercent() {
+        return this.props.PanelHeight() / this.layoutDoc[HeightSym]();
     }
     render() {
         TraceMobx();
@@ -396,10 +399,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     ref={this.createRef}
                     style={{
                         overflowY: this.props.active() ? "auto" : "hidden",
-                        transform: `scale(${Math.min(1, this.props.PanelHeight() / this.layoutDoc[HeightSym]())})`,
-                        height: `${Math.max(100, 100 * this.heightScale)}%`,
-                        width: this.widthScale ? `${Math.max(100, 100 * this.widthScale)}%` : undefined,
-                        transformOrigin: this.widthScale ? "top left" : "top",
+                        transform: `scale(${Math.min(1, this.heightPercent)})`,
+                        height: `${100 * Math.max(1, this.contentScale)}%`,
+                        width: `${100 * this.widthScale}%`,
+                        transformOrigin: "top left",
                     }}
                     onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
                     onDrop={this.onDrop.bind(this)}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 92b1fb090..1b11c0f7f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -544,7 +544,7 @@ export namespace Doc {
                 } else if (cfield instanceof ComputedField) {
                     copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
                 } else if (field instanceof ObjectField) {
-                    copy[key] = ObjectField.MakeCopy(field);
+                    copy[key] = key.includes("layout[") && doc[key] instanceof Doc && false ? Doc.MakeCopy(doc[key] as Doc, false) : ObjectField.MakeCopy(field);
                 } else if (field instanceof Promise) {
                     debugger; //This shouldn't happend...
                 } else {
-- 
cgit v1.2.3-70-g09d2


From 8d61d534eae3cb0f95a4eb39bb3ac637b362f6d7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 7 Feb 2020 15:53:40 -0500
Subject: layout fix?

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

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9e8600193..171aecc70 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -378,7 +378,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     @computed get contentScale() {
         const heightExtra = this.heightPercent > 1 ? this.heightPercent : 1;
-        return Math.max(this.props.Document[WidthSym]() / this.props.PanelWidth(), heightExtra * this.props.Document[HeightSym]() / this.props.PanelHeight());
+        return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), heightExtra * this.props.Document[HeightSym]() / this.props.PanelHeight());
     }
     @computed get widthScale() {
         return this.heightPercent < 1 ? Math.max(1, this.contentScale) : 1;
-- 
cgit v1.2.3-70-g09d2


From d3abbcfc975247839b587b37cabd4f07db73f28c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 8 Feb 2020 02:19:56 -0500
Subject: cleaned up links on images.  fixed axis sorting on pivot views. 
 fixed dragging link button

---
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 .../views/collections/CollectionSchemaView.scss    |  6 +++---
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionViewChromes.tsx    |  5 ++---
 .../CollectionFreeFormLayoutEngines.tsx            | 16 +++++++++++----
 .../collections/collectionFreeForm/MarqueeView.tsx | 23 ++--------------------
 .../views/nodes/ContentFittingDocumentView.scss    |  2 +-
 8 files changed, 23 insertions(+), 35 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 963d97d59..d6029dbe5 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -237,7 +237,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
     @computed
     get metadataButton() {
         const view0 = this.view0;
-        return !view0 ? (null) : <div title="Show metadata panel" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
+        return !view0 ? (null) : <div title="Show metadata panel" className="documentButtonBar-linkFlyout">
             <Flyout anchorPoint={anchorPoints.LEFT_TOP}
                 content={<MetadataEntryMenu docs={() => this.props.views.filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
                 <div className={"documentButtonBar-linkButton-" + "empty"} >
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 8b3d332af..a24140b48 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -39,9 +39,9 @@
         cursor: col-resize;
     }
 
-    .documentView-node:first-child {
-        background: $light-color;
-    }
+    // .documentView-node:first-child {
+    //     background: $light-color;
+    // }
 }
 
 .ReactTable {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 171aecc70..d772ace23 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -39,7 +39,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
     @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
-    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * this.gridGap); }
+    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
     @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
     @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e0e99d635..62b9e8380 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -320,7 +320,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                                     nativeWidth && (proto["data-nativeWidth"] = nativeWidth);
                                     nativeHeight && (proto["data-nativeHeight"] = nativeHeight);
                                     contentSize && (proto.contentSize = contentSize);
-                                    this.props.addDocument(doc);
+                                    this.props?.addDocument(doc);
                                 }
                             });
                         }));
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index f00af52cf..a21e78188 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -386,15 +386,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             onChange={this.viewChanged}
                             value={NumCast(this.props.CollectionView.props.Document._viewType)}>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">schema</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">Schema</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="4">Tree</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">MultiCol</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">MultiRow</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Pivo/Time</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Pivot/Time</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Carousel</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="11">Linear</option>
                         </select>
                         <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
                             <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.openViewSpecs} >
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 95f7794bb..baf09fe5b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -42,13 +42,13 @@ export interface ViewDefResult {
     ele: JSX.Element;
     bounds?: ViewDefBounds;
 }
-
 function toLabel(target: FieldResult<Field>) {
     if (target instanceof ObjectField || target instanceof RefField) {
         return target[ToString]();
     }
     return String(target);
-}/**
+}
+/**
  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
  * 
  * @param {String} text The text to be rendered.
@@ -91,9 +91,17 @@ export function computePivotLayout(
             pivotColumnGroups.get(val)!.docs.push(doc);
         }
     }
+    let nonNumbers = 0;
+    childDocs.map(doc => {
+        const num = toNumber(doc[pivotFieldKey]);
+        if (num === undefined || Number.isNaN(num)) {
+            nonNumbers++;
+        }
+    });
+    const pivotNumbers = nonNumbers / childDocs.length < .1;
     if (pivotColumnGroups.size > 10) {
         const arrayofKeys = Array.from(pivotColumnGroups.keys());
-        const sortedKeys = arrayofKeys.sort();
+        const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort();
         const clusterSize = Math.ceil(pivotColumnGroups.size / 10);
         const numClusters = Math.ceil(sortedKeys.length / clusterSize);
         for (let i = 0; i < numClusters; i++) {
@@ -135,7 +143,7 @@ export function computePivotLayout(
     const expander = 1.05;
     const gap = .15;
     let x = 0;
-    const sortedPivotKeys = Array.from(pivotColumnGroups.keys()).sort();
+    const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
     sortedPivotKeys.forEach(key => {
         const val = pivotColumnGroups.get(key)!;
         let y = 0;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1b54a92eb..e16f4011e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -301,25 +301,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     getCollection = (selected: Doc[], asTemplate: boolean) => {
         const bounds = this.Bounds;
-        const defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
-            "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
-        const colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
-        if (!colorPalette) this.props.Document.colorPalette = new List<string>(defaultPalette);
-        const palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
-        const usedPaletted = new Map<string, number>();
-        [...this.props.activeDocuments(), this.props.Document].map(child => {
-            const bg = StrCast(Doc.Layout(child).backgroundColor);
-            if (palette.indexOf(bg) !== -1) {
-                palette.splice(palette.indexOf(bg), 1);
-                if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
-                else usedPaletted.set(bg, 1);
-            }
-        });
-        usedPaletted.delete("#f1efeb");
-        usedPaletted.delete("white");
-        usedPaletted.delete("rgba(255,255,255,1)");
-        const usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
-        const chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
         // const inkData = this.ink ? this.ink.inkData : undefined;
         const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument;
         const newCollection = creator(selected, {
@@ -327,8 +308,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             y: bounds.top,
             _panX: 0,
             _panY: 0,
-            backgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
-            defaultBackgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
+            backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
+            defaultBackgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
             _width: bounds.width,
             _height: bounds.height,
             _LODdisable: true,
diff --git a/src/client/views/nodes/ContentFittingDocumentView.scss b/src/client/views/nodes/ContentFittingDocumentView.scss
index 2801af441..eb2d93b9a 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.scss
+++ b/src/client/views/nodes/ContentFittingDocumentView.scss
@@ -19,6 +19,6 @@
 
     .documentView-node:first-child {
         position: relative;
-        background: $light-color;
+        background: "#B59B66"; //$light-color;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 60a008f635177acccaa9dacb4006491725c93702 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 10 Feb 2020 15:05:30 -0500
Subject: small cleanups to adding stacking views to contentfittingboxes (like
 time/pivot).  needs to have nativeWidth/Height set.

---
 src/client/documents/Documents.ts                  |  4 +++
 src/client/util/RichTextRules.ts                   | 19 ++++++------
 src/client/util/RichTextSchema.tsx                 | 18 +++++------
 .../views/collections/CollectionStackingView.tsx   | 35 +++++++++-------------
 src/client/views/collections/CollectionSubView.tsx |  5 +++-
 .../views/collections/CollectionTimeView.tsx       |  3 +-
 src/client/views/collections/CollectionView.tsx    |  6 ++--
 .../CollectionFreeFormLayoutEngines.tsx            |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 23 +++++++-------
 .../CollectionMulticolumnView.scss                 |  2 +-
 .../CollectionMultirowView.scss                    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 20 ++++++++-----
 src/new_fields/Doc.ts                              | 17 +++++++++--
 .../authentication/models/current_user_utils.ts    |  6 ++--
 14 files changed, 87 insertions(+), 75 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6775d2302..29f253115 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -594,6 +594,10 @@ export namespace Docs {
         export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multicolumn });
         }
+        export function MultirowDocument(documents: Array<Doc>, options: DocumentOptions) {
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multirow });
+        }
+
 
         export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Masonry });
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 3b30b5b3f..1b67b4fe4 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -1,17 +1,16 @@
-import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from "prosemirror-inputrules";
-import { schema } from "./RichTextSchema";
-import { wrappingInputRule } from "./prosemirrorPatches";
+import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from "prosemirror-inputrules";
 import { NodeSelection, TextSelection } from "prosemirror-state";
-import { StrCast, Cast, NumCast } from "../../new_fields/Types";
-import { Doc, DataSym } from "../../new_fields/Doc";
-import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { Docs, DocUtils } from "../documents/Documents";
+import { DataSym, Doc } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
-import { DocServer } from "../DocServer";
+import { ComputedField } from "../../new_fields/ScriptField";
+import { Cast, NumCast } from "../../new_fields/Types";
 import { returnFalse, Utils } from "../../Utils";
+import { DocServer } from "../DocServer";
+import { Docs, DocUtils } from "../documents/Documents";
+import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
+import { wrappingInputRule } from "./prosemirrorPatches";
 import RichTextMenu from "./RichTextMenu";
-import { RichTextField } from "../../new_fields/RichTextField";
-import { ComputedField } from "../../new_fields/ScriptField";
+import { schema } from "./RichTextSchema";
 
 export const inpRules = {
     rules: [
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index c07ebe2ed..043b277de 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,28 +1,26 @@
-import { reaction, IReactionDisposer, observable, runInAction } from "mobx";
+import { IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { baseKeymap, toggleMark } from "prosemirror-commands";
 import { redo, undo } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
 import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
 import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
-import { EditorState, NodeSelection, TextSelection, Plugin } from "prosemirror-state";
+import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-state";
 import { StepMap } from "prosemirror-transform";
 import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
-import { Doc, WidthSym, HeightSym, DataSym, Field } from "../../new_fields/Doc";
+import { Doc, Field, HeightSym, WidthSym } from "../../new_fields/Doc";
+import { Id } from "../../new_fields/FieldSymbols";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { ComputedField } from "../../new_fields/ScriptField";
+import { BoolCast, NumCast, StrCast } from "../../new_fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { DocumentView } from "../views/nodes/DocumentView";
+import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 import { DocumentManager } from "./DocumentManager";
 import ParagraphNodeSpec from "./ParagraphNodeSpec";
 import { Transform } from "./Transform";
 import React = require("react");
-import { BoolCast, NumCast, StrCast } from "../../new_fields/Types";
-import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { ObjectField } from "../../new_fields/ObjectField";
-import { ComputedField } from "../../new_fields/ScriptField";
-import { observer } from "mobx-react";
-import { Id } from "../../new_fields/FieldSymbols";
-import { OnChangeHandler } from "react-color/lib/components/common/ColorWrap";
 
 const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d772ace23..21c34d047 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,25 +4,25 @@ import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Switch from 'rc-switch';
-import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc";
+import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
-import { emptyFunction, Utils } from "../../../Utils";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from "../../../new_fields/util";
+import { Utils } from "../../../Utils";
 import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
 import { EditableView } from "../EditableView";
 import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
 import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
-import { TraceMobx } from "../../../new_fields/util";
 import { CollectionViewType } from "./CollectionView";
 
 @observer
@@ -55,7 +55,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
             const height = () => this.getDocHeight(d);
-            const width = () => this.widthScale * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+            const width = () => Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
             const dref = React.createRef<HTMLDivElement>();
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
@@ -376,16 +376,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         }
         return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
     }
-    @computed get contentScale() {
-        const heightExtra = this.heightPercent > 1 ? this.heightPercent : 1;
-        return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), heightExtra * this.props.Document[HeightSym]() / this.props.PanelHeight());
-    }
-    @computed get widthScale() {
-        return this.heightPercent < 1 ? Math.max(1, this.contentScale) : 1;
-    }
-    @computed get heightPercent() {
-        return this.props.PanelHeight() / this.layoutDoc[HeightSym]();
-    }
+
+    @computed get scaling() { return !this.props.Document._nativeWidth ? 1 : this.props.PanelHeight() / NumCast(this.props.Document._nativeHeight); }
+
     render() {
         TraceMobx();
         const editableViewProps = {
@@ -399,9 +392,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     ref={this.createRef}
                     style={{
                         overflowY: this.props.active() ? "auto" : "hidden",
-                        transform: `scale(${Math.min(1, this.heightPercent)})`,
-                        height: `${100 * Math.max(1, this.contentScale)}%`,
-                        width: `${100 * this.widthScale}%`,
+                        transform: `scale(${this.scaling}`,
+                        height: `${1 / this.scaling * 100}%`,
+                        width: `${1 / this.scaling * 100}%`,
                         transformOrigin: "top left",
                     }}
                     onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 62b9e8380..d71bad647 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,7 +1,7 @@
 import { action, computed, IReactionDisposer, reaction, trace } from "mobx";
 import * as rp from 'request-promise';
 import CursorField from "../../../new_fields/CursorField";
-import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
@@ -167,6 +167,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     return true;
                 }
                 let added = false;
+                if (this.props.Document._freezeOnDrop) {
+                    de.complete.docDragData?.droppedDocuments.forEach(drop => Doc.freezeNativeDimensions(drop, drop[WidthSym](), drop[HeightSym]()));
+                }
                 if (docDragData.dropAction || docDragData.userDropAction) {
                     added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 } else if (docDragData.moveDocument) {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 6058f4e1d..db176d0bc 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -2,7 +2,7 @@ import { faEdit } from "@fortawesome/free-solid-svg-icons";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, Field } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { RichTextField } from "../../../new_fields/RichTextField";
@@ -31,6 +31,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _layoutEngine = "pivot";
 
     componentDidMount() {
+        this.props.Document._freezeOnDrop = true;
         const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked;
         if (!this.props.Document._facetCollection) {
             const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true, treeViewHideHeaderFields: true });
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2f27e5273..c081649cc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -7,7 +7,7 @@ import * as React from 'react';
 import Lightbox from 'react-image-lightbox-with-rotate';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
 import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
+import { Doc, DocListCast, DataSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { listSpec } from '../../../new_fields/Schema';
 import { BoolCast, Cast, StrCast, NumCast } from '../../../new_fields/Types';
@@ -133,7 +133,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action.bound
     addDocument(doc: Doc): boolean {
-        const targetDataDoc = Doc.GetProto(this.props.DataDoc || this.props.Document); // bcz: shouldn't this be Doc.Layout(this.props.Document)?  Right now, that causes problems with Buxton layout & adding things to a SLideView
+        const targetDataDoc = this.props.Document[DataSym];
         Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
         targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
         Doc.GetProto(doc).lastOpened = new DateField;
@@ -144,7 +144,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
     removeDocument(doc: Doc): boolean {
         const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
         docView && SelectionManager.DeselectDoc(docView);
-        const value = Cast(Doc.GetProto(this.props.DataDoc || this.props.Document)[this.props.fieldKey], listSpec(Doc), []);
+        const value = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
         let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
         index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index baf09fe5b..da4dc0270 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -186,7 +186,7 @@ export function computePivotLayout(
 
     const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols);
     const dividers = sortedPivotKeys.map((key, i) =>
-        ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
+        ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
     groupNames.push(...dividers);
     return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childDocs.filter(c => !filterDocs.includes(c)));
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c78a2a2cf..07a5a2c7b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,20 +1,22 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye } from "@fortawesome/free-regular-svg-icons";
 import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync, Field } from "../../../../new_fields/Doc";
+import { computedFn } from "mobx-utils";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
 import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
-import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
+import { InkTool } from "../../../../new_fields/InkField";
+import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
 import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
+import { TraceMobx } from "../../../../new_fields/util";
+import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
 import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
-import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnOne, Utils } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
+import { Docs } from "../../../documents/Documents";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { HistoryUtil } from "../../../util/History";
@@ -32,15 +34,12 @@ import { FormattedTextBox } from "../../nodes/FormattedTextBox";
 import { pageSchema } from "../../nodes/ImageBox";
 import PDFMenu from "../../pdf/PDFMenu";
 import { CollectionSubView } from "../CollectionSubView";
-import { computePivotLayout, ViewDefResult, computeTimelineLayout, PoolData, ViewDefBounds } from "./CollectionFreeFormLayoutEngines";
+import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { computedFn } from "mobx-utils";
-import { TraceMobx } from "../../../../new_fields/util";
-import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index 0c74b8ddb..821c8d804 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -1,8 +1,8 @@
 .collectionMulticolumnView_contents {
     display: flex;
+    overflow: hidden;
     width: 100%;
     height: 100%;
-    overflow: hidden;
 
     .document-wrapper {
         display: flex;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index 64f607680..a7e2c5707 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -1,8 +1,8 @@
 .collectionMultirowView_contents {
     display: flex;
+    overflow: hidden;
     width: 100%;
     height: 100%;
-    overflow: hidden;
     flex-direction: column;
 
     .document-wrapper {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1199ed7ee..080b01df2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -610,13 +610,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     @undoBatch
-    @action
-    freezeNativeDimensions = (): void => {
-        this.layoutDoc._autoHeight = false;
-        this.layoutDoc.ignoreAspect = !this.layoutDoc.ignoreAspect;
-        if (!this.layoutDoc.ignoreAspect && !this.layoutDoc._nativeWidth) {
-            this.layoutDoc._nativeWidth = this.props.PanelWidth();
-            this.layoutDoc._nativeHeight = this.props.PanelHeight();
+    public static unfreezeNativeDimensions = action((layoutDoc: Doc): void => {
+        layoutDoc._nativeWidth = undefined;
+        layoutDoc._nativeHeight = undefined;
+    });
+
+    toggleNativeDimensions = () => {
+        if (this.Document._nativeWidth || this.Document._nativeHeight) {
+            DocumentView.unfreezeNativeDimensions(this.layoutDoc);
+        }
+        else {
+            Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
         }
     }
 
@@ -730,7 +734,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
         layoutItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-        layoutItems.push({ description: this.Document.ignoreAspect || !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
+        layoutItems.push({ description: !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
         layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
         layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 3baab119f..a28c6f58f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -14,7 +14,7 @@ import { ComputedField, ScriptField } from "./ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
 import { intersectRect } from "../Utils";
-import { UndoManager } from "../client/util/UndoManager";
+import { UndoManager, undoBatch } from "../client/util/UndoManager";
 import { computedFn } from "mobx-utils";
 import { RichTextField } from "./RichTextField";
 import { Script } from "vm";
@@ -178,7 +178,7 @@ export class Doc extends RefField {
     private [SelfProxy]: any;
     public [WidthSym] = () => NumCast(this[SelfProxy]._width);
     public [HeightSym] = () => NumCast(this[SelfProxy]._height);
-    public get [DataSym]() { return Cast(this[SelfProxy].resolvedDataDoc, Doc, null) || this[SelfProxy]; }
+    public get [DataSym]() { return Cast(Doc.Layout(this[SelfProxy]).resolvedDataDoc, Doc, null) || this[SelfProxy]; }
     public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
     @computed get __LAYOUT__() {
         const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
@@ -842,6 +842,17 @@ export namespace Doc {
             }
         }
     }
+
+    @undoBatch
+    @action
+    export function freezeNativeDimensions(layoutDoc: Doc, width: number, height: number): void {
+        layoutDoc._autoHeight = false;
+        layoutDoc.ignoreAspect = false;
+        if (!layoutDoc.ignoreAspect && !layoutDoc._nativeWidth) {
+            layoutDoc._nativeWidth = NumCast(layoutDoc._width, width);
+            layoutDoc._nativeHeight = NumCast(layoutDoc._height, height);
+        }
+    }
 }
 
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
@@ -867,4 +878,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
     return docs.length ? new List(docs) : prevValue;
 });
 Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number) { Doc.setDocFilterRange(container, key, range); });
\ No newline at end of file
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index efee42f63..ea7a4999c 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -234,12 +234,12 @@ export class CurrentUserUtils {
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
     static setupExpandingButtons(doc: Doc) {
-        const slideTemplate = Docs.Create.StackingDocument(
+        const slideTemplate = Docs.Create.MultirowDocument(
             [
-                Docs.Create.MulticolumnDocument([], { title: "images", _height: 200, _xMargin: 10, _yMargin: 10 }),
+                Docs.Create.MulticolumnDocument([], { title: "images", _height: 200 }),
                 Docs.Create.TextDocument("", { title: "contents", _height: 100 })
             ],
-            { _width: 400, _height: 300, title: "slide", _chromeStatus: "disabled", backgroundColor: "lightGray", _autoHeight: true });
+            { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, backgroundColor: "lightGray", _autoHeight: true });
         slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
 
         const iconDoc = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("setNativeView(this)") });
-- 
cgit v1.2.3-70-g09d2


From be02a5529f472507512a127cd8de5cde300eff88 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 11 Feb 2020 14:19:56 -0500
Subject: added ability to pass a Layout as a prop.  changed presentations to
 use layout props.  fixed creating a template for just an empty collection
 representing one field.

---
 src/client/util/DropConverter.ts                   |  7 +-
 src/client/views/DocComponent.tsx                  |  5 +-
 .../views/collections/CollectionStackingView.tsx   | 20 +++--
 .../CollectionFreeFormLayoutEngines.tsx            |  9 ++-
 .../views/nodes/ContentFittingDocumentView.tsx     |  6 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/PresBox.tsx                 | 28 +------
 .../views/presentationview/PresElementBox.tsx      | 90 +++++++++++-----------
 src/new_fields/Doc.ts                              |  7 +-
 .../authentication/models/current_user_utils.ts    |  5 +-
 12 files changed, 94 insertions(+), 91 deletions(-)

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

diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index d0f1d86cb..3c7caa60b 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -8,7 +8,7 @@ import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
 import { RichTextField } from "../../new_fields/RichTextField";
 import { ImageField } from "../../new_fields/URLField";
 
-export function makeTemplate(doc: Doc): boolean {
+export function makeTemplate(doc: Doc, first: boolean = true): boolean {
     const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
     const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0];
     const fieldKey = layout.replace("fieldKey={'", "").replace(/'}$/, "");
@@ -18,9 +18,12 @@ export function makeTemplate(doc: Doc): boolean {
         if (!StrCast(d.title).startsWith("-")) {
             any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any;
         } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) {
-            any = makeTemplate(d) || any;
+            any = makeTemplate(d, false) || any;
         }
     });
+    if (!docs.length && first) {
+        any = Doc.MakeMetadataFieldTemplate(doc, Doc.GetProto(layoutDoc)) || any;
+    }
     if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
         if (!StrCast(layoutDoc.title).startsWith("-")) {
             any = Doc.MakeMetadataFieldTemplate(layoutDoc, Doc.GetProto(layoutDoc));
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index ce48e1215..0bf944f22 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,4 +1,4 @@
-import { Doc } from '../../new_fields/Doc';
+import { Doc, Opt } from '../../new_fields/Doc';
 import { Touchable } from './Touchable';
 import { computed, action, observable } from 'mobx';
 import { Cast } from '../../new_fields/Types';
@@ -11,12 +11,13 @@ import { PositionDocument } from '../../new_fields/documentSchemas';
 ///  DocComponent returns a generic React base class used by views that don't have any data extensions (e.g.,CollectionFreeFormDocumentView, DocumentView, ButtonBox)
 interface DocComponentProps {
     Document: Doc;
+    LayoutDoc?: () => Opt<Doc>;
 }
 export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
-        @computed get layoutDoc() { return PositionDocument(Doc.Layout(this.props.Document)); }
+        @computed get layoutDoc() { return PositionDocument(Doc.Layout(this.props.Document, this.props.LayoutDoc?.())); }
     }
     return Component;
 }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 21c34d047..d21ae32bc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -55,7 +55,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
             const height = () => this.getDocHeight(d);
-            const width = () => Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+            const width = () => this.getDocWidth(d);
             const dref = React.createRef<HTMLDivElement>();
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
@@ -154,11 +154,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
     getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
-        const layoutDoc = Doc.Layout(doc);
+        const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.());
         const height = () => this.getDocHeight(doc);
         return <ContentFittingDocumentView
             Document={doc}
             DataDocument={dataDoc}
+            LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
             renderDepth={this.props.renderDepth + 1}
             fitToBox={this.props.fitToBox}
@@ -178,19 +179,26 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             pinToPres={this.props.pinToPres}>
         </ContentFittingDocumentView>;
     }
+
+    getDocWidth(d?: Doc) {
+        if (!d) return 0;
+        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const nw = NumCast(layoutDoc._nativeWidth);
+        return Math.min(nw && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+    }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d);
+        let layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
         if (!layoutDoc.ignoreAspect && !layoutDoc._fitWidth && nw && nh) {
             const aspect = nw && nh ? nh / nw : 1;
-            if (!(d._nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+            if (!(!layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
             return wid * aspect;
         }
-        return layoutDoc._fitWidth ? !layoutDoc._nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin :
-            Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym]();
+        return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
+            Math.min(wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym]();
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index b86cbbdbd..050ca8347 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -43,6 +43,9 @@ export interface ViewDefResult {
     bounds?: ViewDefBounds;
 }
 function toLabel(target: FieldResult<Field>) {
+    if (typeof target === "number" || Number(target)) {
+        return Number(target).toFixed(2).toString();
+    }
     if (target instanceof ObjectField || target instanceof RefField) {
         return target[ToString]();
     }
@@ -254,10 +257,10 @@ export function computeTimelineLayout(
     let prevKey = Math.floor(minTime);
 
     if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
-        groupNames.push({ type: "text", text: prevKey.toString(), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: "text", text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
     if (!sortedKeys.length && curTime !== undefined) {
-        groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
     const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
@@ -265,7 +268,7 @@ export function computeTimelineLayout(
     let zind = 0;
     sortedKeys.forEach(key => {
         if (curTime !== undefined && curTime > prevKey && curTime <= key) {
-            groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
+            groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
         }
         const keyDocs = pivotDateGroups.get(key)!;
         x += scaling * (key - prevKey);
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index bd1b6166f..671f5b96e 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -2,7 +2,7 @@ import React = require("react");
 import { action, computed } from "mobx";
 import { observer } from "mobx-react";
 import "react-table/react-table.css";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
 import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
 import { NumCast, StrCast } from "../../../new_fields/Types";
 import { emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
@@ -18,6 +18,7 @@ import { TraceMobx } from "../../../new_fields/util";
 interface ContentFittingDocumentViewProps {
     Document?: Doc;
     DataDocument?: Doc;
+    LayoutDoc?: () => Opt<Doc>;
     LibraryPath: Doc[];
     childDocs?: Doc[];
     renderDepth: number;
@@ -42,7 +43,7 @@ interface ContentFittingDocumentViewProps {
 @observer
 export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
-    private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); }
+    private get layoutDoc() { return this.props.Document && (this.props.LayoutDoc?.() || Doc.Layout(this.props.Document)); }
     private get nativeWidth() { return NumCast(this.layoutDoc?._nativeWidth, this.props.PanelWidth()); }
     private get nativeHeight() { return NumCast(this.layoutDoc?._nativeHeight, this.props.PanelHeight()); }
     @computed get scaling() {
@@ -97,6 +98,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                     <DocumentView {...this.props}
                         Document={this.props.Document}
                         DataDoc={this.props.DataDocument}
+                        LayoutDoc={this.props.LayoutDoc}
                         LibraryPath={this.props.LibraryPath}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a274fe8aa..11a33b70a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -82,10 +82,10 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return this.props.DataDoc instanceof Promise ? undefined : this.props.DataDoc;
     }
     get layoutDoc() {
-        if (this.props.DataDoc === undefined && typeof Doc.LayoutField(this.props.Document) !== "string") {
+        if (this.props.DataDoc === undefined && (this.props.LayoutDoc || typeof Doc.LayoutField(this.props.Document) !== "string")) {
             // if there is no dataDoc (ie, we're not rendering a template layout), but this document has a layout document (not a layout string), 
             // then we render the layout document as a template and use this document as the data context for the template layout.
-            return Doc.expandTemplateLayout(Doc.Layout(this.props.Document), this.props.Document);
+            return Doc.expandTemplateLayout(this.props.LayoutDoc?.() || Doc.Layout(this.props.Document), this.props.Document);
         }
         return Doc.Layout(this.props.Document);
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e4ab3e746..36db1ce37 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -43,7 +43,6 @@ import { ScriptingRepl } from '../ScriptingRepl';
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from './FormattedTextBox';
-import { FormattedTextBoxComment } from './FormattedTextBoxComment';
 import React = require("react");
 
 
@@ -56,6 +55,7 @@ export interface DocumentViewProps {
     ContainingCollectionDoc: Opt<Doc>;
     Document: Doc;
     DataDoc?: Doc;
+    LayoutDoc?: () => Opt<Doc>;
     LibraryPath: Doc[];
     fitToBox?: boolean;
     onClick?: ScriptField;
@@ -834,6 +834,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             ContainingCollectionDoc={this.props.ContainingCollectionDoc}
             Document={this.props.Document}
             DataDoc={this.props.DataDoc}
+            LayoutDoc={this.props.LayoutDoc}
             fitToBox={this.props.fitToBox}
             LibraryPath={this.props.LibraryPath}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 7544a5b77..8250f41f3 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -43,6 +43,7 @@ export interface FieldViewProps {
     setVideoBox?: (player: VideoBox) => void;
     ContentScaling: () => number;
     ChromeHeight?: () => number;
+    childLayoutTemplate?: () => Opt<Doc>;
 }
 
 @observer
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 85017d3d4..d4a47c159 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -20,6 +20,7 @@ import { ContextMenuProps } from "../ContextMenuItem";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
+import { PrefetchProxy } from "../../../new_fields/Proxy";
 
 library.add(faArrowLeft);
 library.add(faArrowRight);
@@ -34,35 +35,14 @@ library.add(faEdit);
 export class PresBox extends React.Component<FieldViewProps> {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
     _childReaction: IReactionDisposer | undefined;
-    _slideshowReaction: IReactionDisposer | undefined;
     @observable _isChildActive = false;
-
     componentDidMount() {
-        const userDoc = CurrentUserUtils.UserDocument;
         this.props.Document._forceRenderEngine = "timeline";
         this.props.Document._replacedChrome = "replaced";
-        this._slideshowReaction = reaction(() => this.props.Document._viewType,
-            (slideshow) => {
-                if (slideshow === CollectionViewType.Stacking || slideshow === undefined) {
-                    let presTemp = Cast(userDoc.presentationTemplate, Doc);
-                    if (presTemp instanceof Promise) {
-                        presTemp.then(presTemp => this.props.Document.childLayout = presTemp);
-                    }
-                    else if (presTemp === undefined) {
-                        presTemp = userDoc.presentationTemplate = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent", _xMargin: 5, isTemplateDoc: true, isTemplateForField: "data" });
-                    }
-                    else {
-                        this.props.Document.childLayout = presTemp;
-                    }
-                } else {
-                    this.props.Document.childLayout = undefined;
-                }
-            }, { fireImmediately: true });
         this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true });
     }
     componentWillUnmount() {
         this._childReaction?.();
-        this._slideshowReaction?.();
     }
 
     @computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); }
@@ -376,7 +356,6 @@ export class PresBox extends React.Component<FieldViewProps> {
             doc.presBox = this.props.Document;
             doc.presBoxKey = this.props.fieldKey;
             doc.collapsedHeight = hgt;
-            doc._nativeWidth = doc._nativeHeight = undefined;
             const curScale = NumCast(doc.viewScale, null);
             if (curScale === undefined) {
                 doc.viewScale = 1;
@@ -402,6 +381,8 @@ export class PresBox extends React.Component<FieldViewProps> {
         this.props.Document._viewType = Number(e.target.selectedOptions[0].value);
         this.updateMinimize(e, Number(this.props.Document._viewType));
     });
+
+    childLayoutTemplate = () => this.props.Document._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined;
     render() {
         const mode = NumCast(this.props.Document._viewType, CollectionViewType.Invalid);
         this.initializeScaleViews(this.childDocs, mode);
@@ -425,8 +406,7 @@ export class PresBox extends React.Component<FieldViewProps> {
             </div>
             <div className="presBox-listCont" >
                 {mode !== CollectionViewType.Invalid ?
-                    <CollectionView {...this.props} PanelHeight={this.panelHeight}
-                        moveDocument={returnFalse}
+                    <CollectionView {...this.props} PanelHeight={this.panelHeight} moveDocument={returnFalse} childLayoutTemplate={this.childLayoutTemplate}
                         addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
                     : (null)
                 }
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 52773d466..888707ee3 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -2,19 +2,18 @@ import { library } from '@fortawesome/fontawesome-svg-core';
 import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons';
 import { faArrowDown, faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, reaction, IReactionDisposer } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from "../../../new_fields/FieldSymbols";
 import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnFalse, emptyPath } from "../../../Utils";
-import { DocumentType } from "../../documents/DocumentTypes";
+import { Cast, NumCast } from "../../../new_fields/Types";
+import { emptyFunction, emptyPath, returnFalse } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
+import { DocExtendableComponent } from '../DocComponent';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
-import { DocComponent, DocExtendableComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import "./PresElementBox.scss";
 import React = require("react");
@@ -50,15 +49,16 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
 
     _heightDisposer: IReactionDisposer | undefined;
-    @computed get indexInPres() { return NumCast(this.originalLayout?.presentationIndex); }
-    @computed get presentationDoc() { return Cast(this.originalLayout?.presBox, Doc) as Doc; }
-    @computed get originalLayout() { return this.props.Document.expandedTemplate as Doc; }
-    @computed get targetDoc() { return this.originalLayout?.presentationTargetDoc as Doc; }
-    @computed get currentIndex() { return NumCast(this.presentationDoc?._itemIndex); }
+    @computed get indexInPres() { return NumCast(this.presElementDoc?.presentationIndex); }
+    @computed get presBoxDoc() { return Cast(this.presElementDoc?.presBox, Doc) as Doc; }
+    @computed get presElementDoc() { return this.props.Document.expandedTemplate as Doc; }
+    @computed get presLayoutDoc() { return this.props.Document; }
+    @computed get targetDoc() { return this.presElementDoc?.presentationTargetDoc as Doc; }
+    @computed get currentIndex() { return NumCast(this.presBoxDoc?._itemIndex); }
 
     componentDidMount() {
-        this._heightDisposer = reaction(() => [this.originalLayout.embedOpen, this.originalLayout.collapsedHeight],
-            params => this.originalLayout._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
+        this._heightDisposer = reaction(() => [this.presElementDoc.embedOpen, this.presElementDoc.collapsedHeight],
+            params => this.presLayoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
     }
     componentWillUnmount() {
         this._heightDisposer?.();
@@ -71,13 +71,13 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
     @action
     onHideDocumentUntilPressClick = (e: React.MouseEvent) => {
         e.stopPropagation();
-        this.originalLayout.hideTillShownButton = !this.originalLayout.hideTillShownButton;
-        if (!this.originalLayout.hideTillShownButton) {
+        this.presElementDoc.hideTillShownButton = !this.presElementDoc.hideTillShownButton;
+        if (!this.presElementDoc.hideTillShownButton) {
             if (this.indexInPres >= this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 1;
             }
         } else {
-            if (this.presentationDoc.presStatus && this.indexInPres > this.currentIndex && this.targetDoc) {
+            if (this.presBoxDoc.presStatus && this.indexInPres > this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 0;
             }
         }
@@ -91,14 +91,14 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
     @action
     onHideDocumentAfterPresentedClick = (e: React.MouseEvent) => {
         e.stopPropagation();
-        this.originalLayout.hideAfterButton = !this.originalLayout.hideAfterButton;
-        if (!this.originalLayout.hideAfterButton) {
+        this.presElementDoc.hideAfterButton = !this.presElementDoc.hideAfterButton;
+        if (!this.presElementDoc.hideAfterButton) {
             if (this.indexInPres <= this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 1;
             }
         } else {
-            if (this.originalLayout.fadeButton) this.originalLayout.fadeButton = false;
-            if (this.presentationDoc.presStatus && this.indexInPres < this.currentIndex && this.targetDoc) {
+            if (this.presElementDoc.fadeButton) this.presElementDoc.fadeButton = false;
+            if (this.presBoxDoc.presStatus && this.indexInPres < this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 0;
             }
         }
@@ -112,14 +112,14 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
     @action
     onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => {
         e.stopPropagation();
-        this.originalLayout.fadeButton = !this.originalLayout.fadeButton;
-        if (!this.originalLayout.fadeButton) {
+        this.presElementDoc.fadeButton = !this.presElementDoc.fadeButton;
+        if (!this.presElementDoc.fadeButton) {
             if (this.indexInPres <= this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 1;
             }
         } else {
-            this.originalLayout.hideAfterButton = false;
-            if (this.presentationDoc.presStatus && (this.indexInPres < this.currentIndex) && this.targetDoc) {
+            this.presElementDoc.hideAfterButton = false;
+            if (this.presBoxDoc.presStatus && (this.indexInPres < this.currentIndex) && this.targetDoc) {
                 this.targetDoc.opacity = 0.5;
             }
         }
@@ -131,11 +131,11 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
     @action
     onNavigateDocumentClick = (e: React.MouseEvent) => {
         e.stopPropagation();
-        this.originalLayout.navButton = !this.originalLayout.navButton;
-        if (this.originalLayout.navButton) {
-            this.originalLayout.showButton = false;
+        this.presElementDoc.navButton = !this.presElementDoc.navButton;
+        if (this.presElementDoc.navButton) {
+            this.presElementDoc.showButton = false;
             if (this.currentIndex === this.indexInPres) {
-                this.props.focus(this.originalLayout);
+                this.props.focus(this.presElementDoc);
             }
         }
     }
@@ -147,13 +147,13 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
     onZoomDocumentClick = (e: React.MouseEvent) => {
         e.stopPropagation();
 
-        this.originalLayout.showButton = !this.originalLayout.showButton;
-        if (!this.originalLayout.showButton) {
-            this.originalLayout.viewScale = 1;
+        this.presElementDoc.showButton = !this.presElementDoc.showButton;
+        if (!this.presElementDoc.showButton) {
+            this.presElementDoc.viewScale = 1;
         } else {
-            this.originalLayout.navButton = false;
+            this.presElementDoc.navButton = false;
             if (this.currentIndex === this.indexInPres) {
-                this.props.focus(this.originalLayout);
+                this.props.focus(this.presElementDoc);
             }
         }
     }
@@ -162,14 +162,14 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
      */
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
-    embedHeight = () => this.props.PanelHeight() - NumCast(this.originalLayout.collapsedHeight);
+    embedHeight = () => this.props.PanelHeight() - NumCast(this.presElementDoc.collapsedHeight);
     embedWidth = () => this.props.PanelWidth() - 20;
     /**
      * The function that is responsible for rendering the a preview or not for this
      * presentation element.
      */
     renderEmbeddedInline = () => {
-        return !this.originalLayout.embedOpen || !this.targetDoc ? (null) :
+        return !this.presElementDoc.embedOpen || !this.targetDoc ? (null) :
             <div className="presElementBox-embedded" style={{ height: this.embedHeight() }}>
                 <ContentFittingDocumentView
                     Document={this.targetDoc}
@@ -196,24 +196,24 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
         const treecontainer = this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Tree;
         const className = "presElementBox-item" + (this.currentIndex === this.indexInPres ? " presElementBox-selected" : "");
         const pbi = "presElementBox-interaction";
-        return !this.originalLayout ? (null) : (
+        return !this.presElementDoc ? (null) : (
             <div className={className} key={this.props.Document[Id] + this.indexInPres}
                 style={{ outlineWidth: Doc.IsBrushed(this.targetDoc) ? `1px` : "0px", }}
-                onClick={e => { this.props.focus(this.originalLayout); e.stopPropagation(); }}>
+                onClick={e => { this.props.focus(this.presElementDoc); e.stopPropagation(); }}>
                 {treecontainer ? (null) : <>
                     <strong className="presElementBox-name">
                         {`${this.indexInPres + 1}. ${this.targetDoc?.title}`}
                     </strong>
-                    <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument && this.props.removeDocument(this.originalLayout)}>X</button>
+                    <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument && this.props.removeDocument(this.presElementDoc)}>X</button>
                     <br />
                 </>}
-                <button title="Zoom" className={pbi + (this.originalLayout.showButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} /></button>
-                <button title="Navigate" className={pbi + (this.originalLayout.navButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onNavigateDocumentClick}><FontAwesomeIcon icon={"location-arrow"} /></button>
-                <button title="Hide Before" className={pbi + (this.originalLayout.hideTillShownButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onHideDocumentUntilPressClick}><FontAwesomeIcon icon={fileSolid} /></button>
-                <button title="Fade After" className={pbi + (this.originalLayout.fadeButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button>
-                <button title="Hide After" className={pbi + (this.originalLayout.hideAfterButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button>
-                <button title="Group With Up" className={pbi + (this.originalLayout.groupButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); this.originalLayout.groupButton = !this.originalLayout.groupButton; }}><FontAwesomeIcon icon={"arrow-up"} /></button>
-                <button title="Expand Inline" className={pbi + (this.originalLayout.embedOpen ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); this.originalLayout.embedOpen = !this.originalLayout.embedOpen; }}><FontAwesomeIcon icon={"arrow-down"} /></button>
+                <button title="Zoom" className={pbi + (this.presElementDoc.showButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} /></button>
+                <button title="Navigate" className={pbi + (this.presElementDoc.navButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onNavigateDocumentClick}><FontAwesomeIcon icon={"location-arrow"} /></button>
+                <button title="Hide Before" className={pbi + (this.presElementDoc.hideTillShownButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onHideDocumentUntilPressClick}><FontAwesomeIcon icon={fileSolid} /></button>
+                <button title="Fade After" className={pbi + (this.presElementDoc.fadeButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button>
+                <button title="Hide After" className={pbi + (this.presElementDoc.hideAfterButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button>
+                <button title="Group With Up" className={pbi + (this.presElementDoc.groupButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); this.presElementDoc.groupButton = !this.presElementDoc.groupButton; }}><FontAwesomeIcon icon={"arrow-up"} /></button>
+                <button title="Expand Inline" className={pbi + (this.presElementDoc.embedOpen ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); this.presElementDoc.embedOpen = !this.presElementDoc.embedOpen; }}><FontAwesomeIcon icon={"arrow-down"} /></button>
 
                 <br style={{ lineHeight: 0.1 }} />
                 {this.renderEmbeddedInline()}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 1636f4ee7..447dbe3b0 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -701,8 +701,11 @@ export namespace Doc {
     }
 
     // the document containing the view layout information - will be the Document itself unless the Document has
-    // a layout field.  In that case, all layout information comes from there unless overriden by Document  
-    export function Layout(doc: Doc): Doc { return doc[LayoutSym] || doc; }
+    // a layout field or 'layout' is given.  
+    export function Layout(doc: Doc, layout?: Doc): Doc {
+        const overrideLayout = layout && Cast(doc["data-layout[" + layout[Id] + "]"], Doc, null);
+        return overrideLayout || doc[LayoutSym] || doc;
+    }
     export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
     export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
     export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 0185017ec..ce4f94d83 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -48,7 +48,7 @@ export class CurrentUserUtils {
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
     static setupCreatorButtons(doc: Doc, buttons?: string[]) {
         const notes = CurrentUserUtils.setupNoteTypes(doc);
-        const emptyPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _chromeStatus: "replaced", showTitle: "title", boxShadow: "0 0" });
+        const emptyPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", showTitle: "title", boxShadow: "0 0" });
         const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
         doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", _height: 75 });
         doc.activePen = doc;
@@ -270,7 +270,8 @@ export class CurrentUserUtils {
 
     // the initial presentation Doc to use
     static setupDefaultPresentation(doc: Doc) {
-        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _chromeStatus: "replaced", showTitle: "title", boxShadow: "0 0" });
+        doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" }));
+        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", showTitle: "title", boxShadow: "0 0" });
     }
 
     static setupMobileUploads(doc: Doc) {
-- 
cgit v1.2.3-70-g09d2


From 30f42e403bd8cca4d20fc1b0d1cf28fbfa3a1524 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 12 Feb 2020 14:54:07 -0500
Subject: moved showTitle,etc to use _showTitle,etc

---
 src/client/documents/Documents.ts                       |  8 +++++---
 src/client/views/DocumentButtonBar.tsx                  |  2 +-
 src/client/views/TemplateMenu.tsx                       |  2 +-
 src/client/views/collections/CollectionStackingView.tsx |  4 +---
 src/client/views/collections/CollectionTreeView.tsx     |  4 ++--
 src/client/views/nodes/DocumentView.tsx                 | 14 +++++++-------
 src/new_fields/documentSchemas.ts                       |  6 +++---
 src/server/authentication/models/current_user_utils.ts  |  2 +-
 8 files changed, 21 insertions(+), 21 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index cc18dc0a6..ec5116539 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -70,7 +70,9 @@ export interface DocumentOptions {
     _fitWidth?: boolean;
     _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
     _LODdisable?: boolean;
-    dropAction?: dropActionType;
+    _showTitleHover?: string; // 
+    _showTitle?: string; // which field to display in the title area.  leave empty to have no title
+    _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
     _chromeStatus?: string;
     _viewType?: number;
     _gridGap?: number; // gap between items in masonry view
@@ -82,6 +84,7 @@ export interface DocumentOptions {
     x?: number;
     y?: number;
     z?: number;
+    dropAction?: dropActionType;
     layoutKey?: string;
     type?: string;
     title?: string;
@@ -109,7 +112,6 @@ export interface DocumentOptions {
     documentText?: string;
     borderRounding?: string;
     boxShadow?: string;
-    showTitle?: string;
     sectionFilter?: string; // field key used to determine headings for sections in stacking and masonry views
     schemaColumns?: List<SchemaHeaderField>;
     dockingConfig?: string;
@@ -392,7 +394,7 @@ export namespace Docs {
         Scripting.addGlobal(Buxton);
 
         const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "_annotationOn",
-            "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "showTitle"];
+            "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover"];
 
         /**
          * This function receives the relevant document prototype and uses
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ee850922a..ff0d2718c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -301,7 +301,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
         const view0 = this.view0;
         const templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template =>
-            templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean)));
+            templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("_show" + template.Name) ? true : false, false as boolean)));
         return !view0 ? (null) : <div title="Customize layout" className="documentButtonBar-linkFlyout" ref={this._dragRef}>
             <Flyout anchorPoint={anchorPoints.LEFT_TOP}
                 content={<TemplateMenu docViews={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 64d1e96e1..595c3817e 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -64,7 +64,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @undoBatch
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        this.props.docViews.forEach(d => Doc.Layout(d.Document)["show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : "");
+        this.props.docViews.forEach(d => Doc.Layout(d.Document)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : "");
     }
 
     @action
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d21ae32bc..7d1f2c284 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -40,7 +40,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
     @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
-    @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
+    @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
     @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
     @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -369,8 +369,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
-            subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" });
-            subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
         }
     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index be2947dff..e57003f82 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -656,8 +656,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
 
                 const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
                 heroView.proto!.layout = ImageBox.LayoutString("hero");
-                heroView.showTitle = "title";
-                heroView.showTitleHover = "titlehover";
+                heroView._showTitle = "title";
+                heroView._showTitleHover = "titlehover";
 
                 Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data",
                     Docs.Create.FontIconDocument({
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 84a4e2649..48df1ea33 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -232,7 +232,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             e.preventDefault();
             if (e.key === "†" || e.key === "t") {
-                if (!StrCast(this.layoutDoc.showTitle)) this.layoutDoc.showTitle = "title";
+                if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = "title";
                 if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0);
                 else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text...
                     {
@@ -807,16 +807,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     // does Document set a layout prop 
     setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)] && this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
     // get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise.
-    getLayoutPropStr = (prop: string) => StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
+    getLayoutPropStr = (prop: string) => {
+        return StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : Cast(this.layoutDoc?.expandedTemplate, Doc, null)?.[prop] || this.layoutDoc[prop]);
+    }
     getLayoutPropNum = (prop: string) => NumCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
 
     isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
     select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
 
     chromeHeight = () => {
-        const showTitle = StrCast(this.layoutDoc.showTitle);
-        const showTitleHover = StrCast(this.layoutDoc.showTitleHover);
-        return (showTitle && !showTitleHover ? 0 : 0) + 1;
+        return 1;
     }
 
     @computed get finalLayoutKey() {
@@ -874,8 +874,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @computed get innards() {
         TraceMobx();
-        const showTitle = StrCast(this.getLayoutPropStr("showTitle"));
-        const showTitleHover = StrCast(this.getLayoutPropStr("showTitleHover"));
+        const showTitle = StrCast(this.getLayoutPropStr("_showTitle"));
+        const showTitleHover = StrCast(this.getLayoutPropStr("_showTitleHover"));
         const showCaption = this.getLayoutPropStr("showCaption");
         const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
         const searchHighlight = (!this.Document.searchFields ? (null) :
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 962ef2f4b..81f073855 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -14,6 +14,9 @@ export const documentSchema = createSchema({
     _nativeHeight: "number",    // "
     _width: "number",           // width of document in its container's coordinate system
     _height: "number",          // "
+    _showCaption: "string",      // whether editable caption text is overlayed at the bottom of the document 
+    _showTitle: "string",        // the fieldkey whose contents should be displayed at the top of the document
+    _showTitleHover: "string",   // the showTitle should be shown only on hover
     _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
     _LODdisable: "boolean",     // whether to disbale LOD switching for CollectionFreeFormViews
     _pivotField: "string",      // specifies which field should be used as the timeline/pivot axis
@@ -46,9 +49,6 @@ export const documentSchema = createSchema({
     borderRounding: "string",   // border radius rounding of document
     searchFields: "string",     // the search fields to display when this document matches a search in its metadata
     heading: "number",          // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
-    showCaption: "string",      // whether editable caption text is overlayed at the bottom of the document 
-    showTitle: "string",        // the fieldkey whose contents should be displayed at the top of the document
-    showTitleHover: "string",   // the showTitle should be shown only on hover
     isButton: "boolean",        // whether document functions as a button (overiding native interactions of its content)    
     ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
     scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index ce4f94d83..fde1c9790 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -271,7 +271,7 @@ export class CurrentUserUtils {
     // the initial presentation Doc to use
     static setupDefaultPresentation(doc: Doc) {
         doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" }));
-        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", showTitle: "title", boxShadow: "0 0" });
+        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
     }
 
     static setupMobileUploads(doc: Doc) {
-- 
cgit v1.2.3-70-g09d2


From 5aecc6d75868dcafd0b1bb560ad5a4ad8ab976fa Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 14 Feb 2020 14:25:53 -0500
Subject: cleaned up backgroundColor settings and cluster overrides.  added a
 darkScheme

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/MainView.tsx                      | 27 ++++++++++++-----
 .../views/collections/CollectionCarouselView.tsx   |  1 +
 .../views/collections/CollectionDockingView.tsx    |  3 +-
 .../views/collections/CollectionSchemaCells.tsx    |  3 +-
 .../views/collections/CollectionStackingView.tsx   |  1 +
 .../views/collections/CollectionTreeView.scss      |  9 ++++--
 .../views/collections/CollectionTreeView.tsx       | 16 ++++++----
 src/client/views/collections/CollectionView.tsx    |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 12 ++++----
 .../collections/collectionFreeForm/MarqueeView.tsx |  3 +-
 .../CollectionMulticolumnView.tsx                  |  1 +
 .../CollectionMultirowView.tsx                     |  1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  7 ++---
 .../views/nodes/ContentFittingDocumentView.tsx     |  5 ++--
 src/client/views/nodes/DocumentView.tsx            | 34 +++++-----------------
 src/client/views/nodes/FieldView.tsx               |  6 ++--
 src/client/views/nodes/FormattedTextBox.tsx        |  3 +-
 19 files changed, 72 insertions(+), 67 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index adcdf260d..53671707e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -168,7 +168,7 @@ export namespace Docs {
         const TemplateMap: TemplateMap = new Map([
             [DocumentType.TEXT, {
                 layout: { view: FormattedTextBox, dataField: data },
-                options: { _height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" }
+                options: { _height: 150 }
             }],
             [DocumentType.HIST, {
                 layout: { view: HistogramBox, dataField: data },
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ff0d2718c..2201fe710 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -301,7 +301,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
         const view0 = this.view0;
         const templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template =>
-            templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("_show" + template.Name) ? true : false, false as boolean)));
+            templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean)));
         return !view0 ? (null) : <div title="Customize layout" className="documentButtonBar-linkFlyout" ref={this._dragRef}>
             <Flyout anchorPoint={anchorPoints.LEFT_TOP}
                 content={<TemplateMenu docViews={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 192f3b8fb..ba49a2b53 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { Doc, DocListCast, Field, FieldResult, Opt } from '../../new_fields/Doc'
 import { Id } from '../../new_fields/FieldSymbols';
 import { List } from '../../new_fields/List';
 import { listSpec } from '../../new_fields/Schema';
-import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
+import { Cast, FieldValue, StrCast, BoolCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils, emptyPath } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
@@ -43,6 +43,7 @@ import SettingsManager from '../util/SettingsManager';
 import { TraceMobx } from '../../new_fields/util';
 import { RadialMenu } from './nodes/RadialMenu';
 import RichTextMenu from '../util/RichTextMenu';
+import { DocumentType } from '../documents/DocumentTypes';
 
 @observer
 export class MainView extends React.Component {
@@ -56,8 +57,9 @@ export class MainView extends React.Component {
     @observable private _panelHeight: number = 0;
     @observable private _flyoutTranslate: boolean = true;
     @observable public flyoutWidth: number = 250;
+    private get darkScheme() { return BoolCast(Cast(this.userDoc.activeWorkspace, Doc, null)?.darkScheme); }
 
-    @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
+    @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
     @computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; }
@@ -207,7 +209,6 @@ export class MainView extends React.Component {
             _width: this._panelWidth * .7,
             _height: this._panelHeight,
             title: "Collection " + workspaceCount,
-            backgroundColor: "white"
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
@@ -273,6 +274,16 @@ export class MainView extends React.Component {
     getPHeight = () => this._panelHeight;
     getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
 
+    childBackgroundColor = (doc: Doc) => {
+        if (this.darkScheme) {
+            return doc.type === DocumentType.TEXT ? "#112423" : "black";
+        }
+        return doc.type === DocumentType.TEXT ? "#f1efeb" :
+            doc.type === DocumentType.COL && doc._viewType === CollectionViewType.Tree ? "lightgray" : "white";
+    }
+    sidebarBackgroundColor = (doc: Doc) => {
+        return this.childBackgroundColor(doc);
+    }
     @computed get mainDocView() {
         return <DocumentView Document={this.mainContainer!}
             DataDoc={undefined}
@@ -281,13 +292,13 @@ export class MainView extends React.Component {
             addDocTab={this.addDocTabFunc}
             pinToPres={emptyFunction}
             onClick={undefined}
+            backgroundColor={this.childBackgroundColor}
             removeDocument={undefined}
             ScreenToLocalTransform={Transform.Identity}
             ContentScaling={returnOne}
             PanelWidth={this.getPWidth}
             PanelHeight={this.getPHeight}
             renderDepth={0}
-            backgroundColor={returnEmptyString}
             focus={emptyFunction}
             parentActive={returnTrue}
             whenActiveChanged={emptyFunction}
@@ -366,7 +377,7 @@ export class MainView extends React.Component {
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
     @computed get flyout() {
-        const sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
+        const sidebarContent = this.userDoc?.sidebarContainer;
         if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
@@ -388,7 +399,7 @@ export class MainView extends React.Component {
                     PanelHeight={this.getPHeight}
                     renderDepth={0}
                     focus={emptyFunction}
-                    backgroundColor={returnEmptyString}
+                    backgroundColor={this.sidebarBackgroundColor}
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
@@ -414,7 +425,7 @@ export class MainView extends React.Component {
                     PanelHeight={this.getContentsHeight}
                     renderDepth={0}
                     focus={emptyFunction}
-                    backgroundColor={returnEmptyString}
+                    backgroundColor={this.sidebarBackgroundColor}
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
@@ -437,7 +448,7 @@ export class MainView extends React.Component {
     @computed get mainContent() {
         const sidebar = this.userDoc && this.userDoc.sidebarContainer;
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
-            <div className="mainView-mainContent" >
+            <div className="mainView-mainContent" style={{ color: this.darkScheme ? "lightGray" : "black" }} >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
                     <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
                         style={{ backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} >
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 3c86bd799..226a1c813 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -52,6 +52,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
             <div>
                 <div className="collectionCarouselView-image">
                     <ContentFittingDocumentView {...this.props}
+                        backgroundColor={this.props.backgroundColor}
                         Document={this.childLayoutPairs[index].layout}
                         DataDocument={this.childLayoutPairs[index].data}
                         PanelHeight={this.panelHeight}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cb413b3e3..0b7dbea7c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -599,6 +599,7 @@ interface DockedFrameProps {
     dataDocumentId: FieldId;
     glContainer: any;
     libraryPath: (FieldId[]);
+    backgroundColor?: (doc:Doc) => string| undefined;
     //collectionDockingView: CollectionDockingView
 }
 @observer
@@ -756,7 +757,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             parentActive={returnTrue}
             whenActiveChanged={emptyFunction}
             focus={emptyFunction}
-            backgroundColor={returnEmptyString}
+            backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
             addDocTab={this.addDocTab}
             pinToPres={DockedFrameRenderer.PinDoc}
             ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index caffa7eb1..a3b1b5ec0 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -37,7 +37,8 @@ export interface CellProps {
     renderDepth: number;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument: (document: Doc, targetCollection: Doc | undefined, 
+    addDocument: (document: Doc) => boolean) => boolean;
     isFocused: boolean;
     changeFocusedCellByIndex: (row: number, col: number) => void;
     setIsEditing: (isEditing: boolean) => void;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7d1f2c284..055035b3e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -159,6 +159,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return <ContentFittingDocumentView
             Document={doc}
             DataDocument={dataDoc}
+            backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
             renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2fa6813d7..6ebe81545 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -63,7 +63,9 @@
     font-size: 8pt;
     margin-left: 3px;
     display: none;
-    background: lightgray;
+}
+.collectionTreeView-keyHeader:hover {
+    background: #797777;
 }
 
 .collectionTreeView-subtitle {
@@ -84,9 +86,11 @@
 .treeViewItem-openRight {
     display: none;
     height: 17px;
-    background: gray;
     width: 15px;
 }
+.treeViewItem-openRight:hover {
+    background: #797777;
+}
 
 .treeViewItem-border {
     display: inherit;
@@ -101,7 +105,6 @@
     .treeViewItem-openRight {
         display: inline-block;
         height: 17px;
-        background: #a8a7a7;
         width: 15px;
 
         // display: inline;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a160dcc62..e2b3cc425 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -56,6 +56,7 @@ export interface TreeViewProps {
     indentDocument?: () => void;
     outdentDocument?: () => void;
     ScreenToLocalTransform: () => Transform;
+    backgroundColor?: (doc: Doc) => string | undefined;
     outerXf: () => { translateX: number, translateY: number };
     treeViewId: Doc;
     parentKey: string;
@@ -289,7 +290,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
                     DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                    this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
+                    this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
                     [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick);
             } else {
@@ -332,7 +333,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 {!docs ? (null) :
                     TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
                         this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                        this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
+                        this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
                         [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick)}
             </ul >;
@@ -348,6 +349,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DataDocument={this.templateDataDoc}
                     LibraryPath={emptyPath}
                     renderDepth={this.props.renderDepth + 1}
+                    backgroundColor={this.props.backgroundColor}
                     fitToBox={this.boundsOfCollectionDocument !== undefined}
                     PanelWidth={this.docWidth}
                     PanelHeight={this.docHeight}
@@ -384,7 +386,7 @@ class TreeView extends React.Component<TreeViewProps> {
     @computed
     get renderBullet() {
         const checked = this.props.document.type === DocumentType.COL ? undefined : this.props.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
-        return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "black"), opacity: 0.4 }}>
+        return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "inherit"), opacity: 0.4 }}>
             {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
         </div>;
     }
@@ -415,7 +417,7 @@ class TreeView extends React.Component<TreeViewProps> {
         return <>
             <div className="docContainer" title="click to edit title" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
                 style={{
-                    color: this.props.document.isMinimized ? "red" : "black",
+                    color: this.props.document.isMinimized ? "red" : "inherit",
                     background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
                     fontWeight: this.props.document.searchMatch ? "bold" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
@@ -456,6 +458,7 @@ class TreeView extends React.Component<TreeViewProps> {
         dropAction: dropActionType,
         addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean,
         pinToPres: (document: Doc) => void,
+        backgroundColor: undefined | ((document: Doc) => string | undefined),
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
         active: (outsideReaction?: boolean) => boolean,
@@ -561,6 +564,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 renderDepth={renderDepth}
                 deleteDoc={remove}
                 addDocument={addDocument}
+                backgroundColor={backgroundColor}
                 panelWidth={rowWidth}
                 panelHeight={rowHeight}
                 ChromeHeight={ChromeHeight}
@@ -714,7 +718,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
         return !this.childDocs ? (null) : (
             <div className="collectionTreeView-dropTarget" id="body"
-                style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
+                style={{ background: this.props.backgroundColor?.(this.props.Document), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
                 onContextMenu={this.onContextMenu}
                 onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                 onDrop={this.onTreeDrop}
@@ -737,7 +741,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                 <ul className="no-indent" style={{ width: "max-content" }} >
                     {
                         TreeView.GetChildElements(this.childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
-                            moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
+                            moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                             this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.treeViewHideHeaderFields),
                             BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick))
                     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 39ce4d6e8..be971eda6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -38,6 +38,7 @@ import { CollectionViewBaseChrome } from './CollectionViewChromes';
 import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { List } from '../../../new_fields/List';
+import { SubCollectionViewProps } from './CollectionSubView';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
@@ -177,7 +178,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
     }
 
     private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        const props = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+        const props: SubCollectionViewProps = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
         switch (type) {
             case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
             case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e1854fc2d..969d6b3c8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -10,7 +10,7 @@ import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkTool } from "../../../../new_fields/InkField";
 import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
 import { ScriptField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, BoolCast, StrCast } from "../../../../new_fields/Types";
 import { TraceMobx } from "../../../../new_fields/util";
 import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
 import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
@@ -206,6 +206,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     @undoBatch
+    @action
     updateClusters(useClusters: boolean) {
         this.props.Document.useClusters = useClusters;
         this._clusterSets.length = 0;
@@ -243,7 +244,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
             }
             childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
-            childLayouts.map(child => Doc.GetProto(child).clusterStr = child.cluster?.toString());
         }
     }
 
@@ -279,16 +279,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     getClusterColor = (doc: Doc) => {
-        let clusterColor = "";
+        let clusterColor = this.props.backgroundColor?.(doc);
         const cluster = NumCast(doc.cluster);
         if (this.Document.useClusters) {
             if (this._clusterSets.length <= cluster) {
                 setTimeout(() => this.updateCluster(doc), 0);
             } else {
                 // choose a cluster color from a palette
-                const colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+                const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"];
                 clusterColor = colors[cluster % colors.length];
-                const set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
+                const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
                 set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
                 set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
@@ -709,6 +709,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    backgroundHalo = () => BoolCast(this.Document.useClusters);
 
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
@@ -728,6 +729,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             ContainingCollectionDoc: this.props.Document,
             focus: this.focusDocument,
             backgroundColor: this.getClusterColor,
+            backgroundHalo: this.backgroundHalo,
             parentActive: this.props.active,
             bringToFront: this.bringToFront,
             zoomToScale: this.zoomToScale,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4b0855635..85cda4ecb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -308,8 +308,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             y: bounds.top,
             _panX: 0,
             _panY: 0,
-            backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
-            defaultBackgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
+            backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : undefined,
             _width: bounds.width,
             _height: bounds.height,
             _LODdisable: true,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 7d8de0db4..db5673573 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -207,6 +207,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             {...this.props}
             Document={layout}
             DataDocument={layout.resolvedDataDoc as Doc}
+            backgroundColor={this.props.backgroundColor}
             CollectionDoc={this.props.Document}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index ec05443e5..630a178cf 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -208,6 +208,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             {...this.props}
             Document={layout}
             DataDocument={layout.resolvedDataDoc as Doc}
+            backgroundColor={this.props.backgroundColor}
             CollectionDoc={this.props.Document}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3c27ac9a1..53d17b580 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -61,15 +61,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect && !this.props.fitToBox ? this.width / this.nativeWidth : 1;
-    clusterColorFunc = (doc: Doc) => this.clusterColor;
     panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth());
     panelHeight = () => (this.dataProvider?.height || this.props.PanelHeight());
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
         .scale(1 / this.contentScaling())
 
-    @computed
-    get clusterColor() { return this.props.backgroundColor(this.props.Document); }
     focusDoc = (doc: Doc) => this.props.focus(doc, false);
     render() {
         TraceMobx();
@@ -78,7 +75,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 boxShadow:
                     this.layoutDoc.opacity === 0 ? undefined :  // if it's not visible, then no shadow
                         this.layoutDoc.z ? `#9c9396  ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                            this.clusterColor ? (`${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                            this.props.backgroundHalo?.() ? (`${this.props.backgroundColor?.(this.props.Document)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
                                 this.layoutDoc.isBackground ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
                                     StrCast(this.layoutDoc.boxShadow, ""),
                 borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
@@ -96,7 +93,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 dragDivName={"collectionFreeFormDocumentView-container"}
                 ContentScaling={this.contentScaling}
                 ScreenToLocalTransform={this.getTransform}
-                backgroundColor={this.clusterColorFunc}
+                backgroundColor={this.props.backgroundColor}
                 PanelWidth={this.panelWidth}
                 PanelHeight={this.panelHeight}
             /> : <ContentFittingDocumentView {...this.props}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 5c449026e..387da88f5 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -6,7 +6,7 @@ import { Doc, Opt } from "../../../new_fields/Doc";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { NumCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
+import { emptyFunction, returnOne } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionView } from "../collections/CollectionView";
 import '../DocumentDecorations.scss';
@@ -27,6 +27,7 @@ interface ContentFittingDocumentViewProps {
     CollectionView?: CollectionView;
     CollectionDoc?: Doc;
     onClick?: ScriptField;
+    backgroundColor?: (doc: Doc) => string | undefined;
     getTransform: () => Transform;
     addDocument?: (document: Doc) => boolean;
     moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
@@ -86,6 +87,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         LibraryPath={this.props.LibraryPath}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
+                        backgroundColor={this.props.backgroundColor}
                         addDocument={this.props.addDocument}
                         removeDocument={this.props.removeDocument}
                         moveDocument={this.props.moveDocument}
@@ -101,7 +103,6 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
                         focus={this.props.focus || emptyFunction}
-                        backgroundColor={returnEmptyString}
                         bringToFront={emptyFunction}
                         dontRegisterView={this.props.dontRegisterView}
                         zoomToScale={emptyFunction}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ef065f51d..52928e8cb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -77,7 +77,8 @@ export interface DocumentViewProps {
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     zoomToScale: (scale: number) => void;
-    backgroundColor: (doc: Doc) => string | undefined;
+    backgroundHalo?: () => boolean;
+    backgroundColor?: (doc: Doc) => string | undefined;
     getScale: () => number;
     animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
     ChromeHeight?: () => number;
@@ -295,12 +296,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                this._hitTemplateDrag = false;
-                for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
-                    if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
-                        this._hitTemplateDrag = true;
-                    }
-                }
                 if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
@@ -778,14 +773,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         });
     }
 
-    // does Document set a layout prop 
-    setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)] && this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
-    // get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise.
-    getLayoutPropStr = (prop: string) => {
-        return StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : Cast(this.layoutDoc?.expandedTemplate, Doc, null)?.[prop] || this.layoutDoc[prop]);
-    }
-    getLayoutPropNum = (prop: string) => NumCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
-
     isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
     select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
 
@@ -848,9 +835,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @computed get innards() {
         TraceMobx();
-        const showTitle = StrCast(this.getLayoutPropStr("_showTitle"));
-        const showTitleHover = StrCast(this.getLayoutPropStr("_showTitleHover"));
-        const showCaption = this.getLayoutPropStr("_showCaption");
+        const showTitle = StrCast(this.layoutDoc._showTitle);
+        const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
+        const showCaption = StrCast(this.layoutDoc._showCaption);
         const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
         const searchHighlight = (!this.Document.searchFields ? (null) :
             <div className="documentView-searchHighlight">
@@ -921,14 +908,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     render() {
         if (!(this.props.Document instanceof Doc)) return (null);
-        const colorSet = this.setsLayoutProp("backgroundColor");
-        const clusterCol = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.clusterOverridesDefaultBackground;
-        const backgroundColor = (clusterCol && !colorSet) ?
-            this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) :
-            StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document);
-
+        const backgroundColor = StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor?.(this.Document);
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
-        const borderRounding = this.getLayoutPropStr("borderRounding");
+        const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
 
         const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
@@ -943,7 +925,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 transform: this._animate ? `scale(${this._animate})` : undefined,
                 transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
                 pointerEvents: this.ignorePointerEvents ? "none" : "all",
-                color: StrCast(this.Document.color),
+                color: StrCast(this.Document.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
                 border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
                 boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8250f41f3..00f00dd52 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -3,14 +3,13 @@ import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { DateField } from "../../../new_fields/DateField";
 import { Doc, FieldResult, Opt } from "../../../new_fields/Doc";
-import { IconField } from "../../../new_fields/IconField";
 import { List } from "../../../new_fields/List";
-import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { AudioField, VideoField } from "../../../new_fields/URLField";
 import { Transform } from "../../util/Transform";
 import { CollectionView } from "../collections/CollectionView";
 import { AudioBox } from "./AudioBox";
 import { VideoBox } from "./VideoBox";
-import { ScriptField } from "../../../new_fields/ScriptField";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -34,6 +33,7 @@ export interface FieldViewProps {
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc) => boolean;
     moveDocument?: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+    backgroundColor?: (document: Doc) => string | undefined;
     ScreenToLocalTransform: () => Transform;
     active: (outsideReaction?: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2fb61f9db..3c64b3974 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -186,7 +186,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000)));
             this._applyingChange = true;
             if (!this.props.Document._textTemplate || Doc.GetProto(this.props.Document) === this.dataDoc) {
-                this.dataDoc[this.props.fieldKey + "-lastModified"] && (this.dataDoc[this.props.fieldKey + "-backgroundColor"] = "lightGray");
                 this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                 this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()), state.doc.textBetween(0, state.doc.content.size, "\n\n"));
             }
@@ -1075,7 +1074,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             <div className={`formattedTextBox-cont`} ref={this._ref}
                 style={{
                     height: this.layoutDoc._autoHeight ? "max-content" : undefined,
-                    background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : undefined,
+                    background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"]),
                     opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
                     color: this.props.hideOnLeave ? "white" : "inherit",
                     pointerEvents: interactive ? "none" : "all",
-- 
cgit v1.2.3-70-g09d2


From b42e1e1e2da941955d7751b6003f18fecd5f2f8d Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Sun, 16 Feb 2020 12:22:10 -0500
Subject: collection sub view and google authentication manager cleanup,
 deleteAssets route added

---
 src/client/apis/GoogleAuthenticationManager.tsx    |  46 ++--
 .../views/collections/CollectionCarouselView.tsx   |  28 +--
 .../views/collections/CollectionLinearView.tsx     |   2 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   2 +-
 .../views/collections/CollectionSchemaView.tsx     |   7 +-
 .../views/collections/CollectionStackingView.tsx   |  12 +-
 .../CollectionStackingViewFieldColumn.tsx          |   2 +-
 src/client/views/collections/CollectionSubView.tsx | 264 +++++++++++----------
 .../views/collections/CollectionTreeView.tsx       |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  10 +-
 .../CollectionMulticolumnView.tsx                  |   6 +-
 .../CollectionMultirowView.tsx                     |   6 +-
 src/server/ApiManagers/DeleteManager.ts            |  31 +--
 13 files changed, 213 insertions(+), 209 deletions(-)

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

diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index ce1277667..417dc3c3b 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -12,8 +12,8 @@ const prompt = "Paste authorization code here...";
 @observer
 export default class GoogleAuthenticationManager extends React.Component<{}> {
     public static Instance: GoogleAuthenticationManager;
-    @observable private openState = false;
     private authenticationLink: Opt<string> = undefined;
+    @observable private openState = false;
     @observable private authenticationCode: Opt<string> = undefined;
     @observable private clickedState = false;
     @observable private success: Opt<boolean> = undefined;
@@ -39,24 +39,18 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
                 const disposer = reaction(
                     () => this.authenticationCode,
                     async authenticationCode => {
-                        if (!authenticationCode) {
-                            return;
+                        if (authenticationCode) {
+                            disposer();
+                            const { access_token, avatar, name } = await Networking.PostToServer("/writeGoogleAccessToken", { authenticationCode });
+                            runInAction(() => {
+                                this.avatar = avatar;
+                                this.username = name;
+                                this.hasBeenClicked = false;
+                                this.success = false;
+                            });
+                            this.beginFadeout();
+                            resolve(access_token);
                         }
-                        const { access_token, avatar, name } = await Networking.PostToServer(
-                            "/writeGoogleAccessToken",
-                            { authenticationCode }
-                        );
-                        runInAction(() => {
-                            this.avatar = avatar;
-                            this.username = name;
-                        });
-                        this.beginFadeout();
-                        disposer();
-                        resolve(access_token);
-                        action(() => {
-                            this.hasBeenClicked = false;
-                            this.success = false;
-                        });
                     }
                 );
             });
@@ -86,26 +80,20 @@ export default class GoogleAuthenticationManager extends React.Component<{}> {
         GoogleAuthenticationManager.Instance = this;
     }
 
-    private handleClick = () => {
-        window.open(this.authenticationLink);
-        setTimeout(() => this.hasBeenClicked = true, 500);
-    }
-
-    private handlePaste = action((e: React.ChangeEvent<HTMLInputElement>) => {
-        this.authenticationCode = e.currentTarget.value;
-    });
-
     private get renderPrompt() {
         return (
             <div className={'authorize-container'}>
                 {this.displayLauncher ? <button
                     className={"dispatch"}
-                    onClick={this.handleClick}
+                    onClick={() => {
+                        window.open(this.authenticationLink);
+                        setTimeout(() => this.hasBeenClicked = true, 500);
+                    }}
                     style={{ marginBottom: this.clickedState ? 15 : 0 }}
                 >Authorize a Google account...</button> : (null)}
                 {this.clickedState ? <input
                     className={'paste-target'}
-                    onChange={this.handlePaste}
+                    onChange={action(e => this.authenticationCode = e.currentTarget.value)}
                     placeholder={prompt}
                 /> : (null)}
                 {this.avatar ? <img
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 8167416c6..a0cb1fe19 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -27,7 +27,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
     protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
         this._dropDisposer?.();
         if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
         }
     }
 
@@ -41,21 +41,21 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
     }
 
     panelHeight = () => this.props.PanelHeight() - 50;
-    @computed get content() { 
+    @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
         return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) :
-        <>
-            <div className="collectionCarouselView-image" key="image">
-                <ContentFittingDocumentView {...this.props}
-                    Document={this.childLayoutPairs[index].layout}
-                    DataDocument={this.childLayoutPairs[index].data}
-                    PanelHeight={this.panelHeight}
-                    getTransform={this.props.ScreenToLocalTransform} />
-            </div>
-            <div className="collectionCarouselView-caption" key="caption" style={{ background: this.props.backgroundColor?.(this.props.Document) }}>
-                <FormattedTextBox key={index} {...this.props} Document={this.childLayoutPairs[index].layout} DataDoc={undefined} fieldKey={"caption"}></FormattedTextBox>
-            </div>
-        </>
+            <>
+                <div className="collectionCarouselView-image" key="image">
+                    <ContentFittingDocumentView {...this.props}
+                        Document={this.childLayoutPairs[index].layout}
+                        DataDocument={this.childLayoutPairs[index].data}
+                        PanelHeight={this.panelHeight}
+                        getTransform={this.props.ScreenToLocalTransform} />
+                </div>
+                <div className="collectionCarouselView-caption" key="caption" style={{ background: this.props.backgroundColor?.(this.props.Document) }}>
+                    <FormattedTextBox key={index} {...this.props} Document={this.childLayoutPairs[index].layout} DataDoc={undefined} fieldKey={"caption"}></FormattedTextBox>
+                </div>
+            </>;
     }
     @computed get buttons() {
         return <>
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 7eb316cf0..9bbc9f1b6 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -67,7 +67,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
         this._dropDisposer && this._dropDisposer();
         if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
         }
     }
 
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e25a2f5eb..f3d512a97 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -80,7 +80,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             const key = StrCast(this.props.parent.props.Document.sectionFilter);
             const castedValue = this.getValue(this._heading);
             de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
-            this.props.parent.drop(e, de);
+            this.props.parent.onInternalDrop(e, de);
             e.stopPropagation();
         }
     });
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index fa8be5177..c422c38f1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -14,7 +14,6 @@ import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { Docs, DocumentOptions } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
 import { Gateway } from "../../northstar/manager/Gateway";
 import { CompileScript, Transformer, ts } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
@@ -175,7 +174,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             moveDocument={this.props.moveDocument}
             ScreenToLocalTransform={this.props.ScreenToLocalTransform}
             active={this.props.active}
-            onDrop={this.onDrop}
+            onDrop={this.onExternalDrop}
             addDocTab={this.props.addDocTab}
             pinToPres={this.props.pinToPres}
             isSelected={this.props.isSelected}
@@ -199,7 +198,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
 
     render() {
         return <div className="collectionSchemaView-container">
-            <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}>
+            <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.onExternalDrop(e, {})} ref={this.createTarget}>
                 {this.schemaTable}
             </div>
             {this.dividerDragger}
@@ -692,7 +691,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     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
             // ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" });
-            ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" })
+            ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
         }
     }
 
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 055035b3e..a9cefae6a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -189,7 +189,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
-        let layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
@@ -234,7 +234,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     @undoBatch
     @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         const where = [de.x, de.y];
         let targInd = -1;
         let plusOne = 0;
@@ -248,7 +248,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     plusOne = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0;
                 }
             });
-            if (super.drop(e, de)) {
+            if (super.onInternalDrop(e, de)) {
                 const newDoc = de.complete.docDragData.droppedDocuments[0];
                 const docs = this.childDocList;
                 if (docs) {
@@ -264,7 +264,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     @undoBatch
     @action
-    onDrop = async (e: React.DragEvent): Promise<void> => {
+    onExternalDrop = async (e: React.DragEvent): Promise<void> => {
         const where = [e.clientX, e.clientY];
         let targInd = -1;
         this._docXfs.map((cd, i) => {
@@ -274,7 +274,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                 targInd = i;
             }
         });
-        super.onDrop(e, {}, () => {
+        super.onExternalDrop(e, {}, () => {
             if (targInd !== -1) {
                 const newDoc = this.childDocs[this.childDocs.length - 1];
                 const docs = this.childDocList;
@@ -405,7 +405,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                         transformOrigin: "top left",
                     }}
                     onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
-                    onDrop={this.onDrop.bind(this)}
+                    onDrop={this.onExternalDrop.bind(this)}
                     onContextMenu={this.onContextMenu}
                     onWheel={e => this.props.active() && e.stopPropagation()} >
                     {this.renderedSections}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 2ff477c57..87c35679f 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -71,7 +71,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             else {
                 de.complete.docDragData.droppedDocuments.forEach(d => d[key] = undefined);
             }
-            this.props.parent.drop(e, de);
+            this.props.parent.onInternalDrop(e, de);
             e.stopPropagation();
         }
     });
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e80e1c802..042385dcd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -57,7 +57,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             this.gestureDisposer?.();
             this.multiTouchDisposer?.();
             if (ele) {
-                this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+                this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
                 this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
             }
@@ -156,7 +156,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
 
         @undoBatch
         @action
-        protected drop(e: Event, de: DragManager.DropEvent): boolean {
+        protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
             const docDragData = de.complete.docDragData;
             (this.props.Document.dropConverter instanceof ScriptField) &&
                 this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this 
@@ -195,158 +195,172 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
 
         @undoBatch
         @action
-        protected async onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
+        protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
             if (e.ctrlKey) {
                 e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
                 return;
             }
-            const html = e.dataTransfer.getData("text/html");
-            const text = e.dataTransfer.getData("text/plain");
+
+            const { dataTransfer } = e;
+            const html = dataTransfer.getData("text/html");
+            const text = dataTransfer.getData("text/plain");
 
             if (text && text.startsWith("<div")) {
                 return;
             }
+
             e.stopPropagation();
             e.preventDefault();
-
-            if (html && FormattedTextBox.IsFragment(html)) {
-                const href = FormattedTextBox.GetHref(html);
-                if (href) {
-                    const docid = FormattedTextBox.GetDocFromUrl(href);
-                    if (docid) { // prosemirror text containing link to dash document
-                        DocServer.GetRefField(docid).then(f => {
-                            if (f instanceof Doc) {
-                                if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
-                                (f instanceof Doc) && this.props.addDocument(f);
-                            }
-                        });
-                    } else {
-                        this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
-                    }
-                } else if (text) {
-                    this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
-                }
+            const { addDocument } = this.props;
+            if (!addDocument) {
+                alert("this.props.addDocument does not exist. Aborting drop operation.");
                 return;
             }
-            if (html && !html.startsWith("<a")) {
-                const tags = html.split("<");
-                if (tags[0] === "") tags.splice(0, 1);
-                const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
-                if (img) {
-                    const split = img.split("src=\"")[1].split("\"")[0];
-                    let source = split;
-                    if (split.startsWith("data:image") && split.includes("base64")) {
-                        const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
-                        source = Utils.prepend(accessPaths.agnostic.client);
+
+            if (html) {
+                if (FormattedTextBox.IsFragment(html)) {
+                    const href = FormattedTextBox.GetHref(html);
+                    if (href) {
+                        const docid = FormattedTextBox.GetDocFromUrl(href);
+                        if (docid) { // prosemirror text containing link to dash document
+                            DocServer.GetRefField(docid).then(f => {
+                                if (f instanceof Doc) {
+                                    if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
+                                    (f instanceof Doc) && addDocument(f);
+                                }
+                            });
+                        } else {
+                            addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
+                        }
+                    } else if (text) {
+                        addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
                     }
-                    const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
-                    ImageUtils.ExtractExif(doc);
-                    this.props.addDocument(doc);
                     return;
-                } else {
-                    const path = window.location.origin + "/doc/";
-                    if (text.startsWith(path)) {
-                        const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
-                        DocServer.GetRefField(docid).then(f => {
-                            if (f instanceof Doc) {
-                                if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
-                                (f instanceof Doc) && this.props.addDocument(f);
-                            }
-                        });
+                }
+                if (!html.startsWith("<a")) {
+                    const tags = html.split("<");
+                    if (tags[0] === "") tags.splice(0, 1);
+                    const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
+                    if (img) {
+                        const split = img.split("src=\"")[1].split("\"")[0];
+                        let source = split;
+                        if (split.startsWith("data:image") && split.includes("base64")) {
+                            const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
+                            source = Utils.prepend(accessPaths.agnostic.client);
+                        }
+                        const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
+                        ImageUtils.ExtractExif(doc);
+                        addDocument(doc);
+                        return;
                     } else {
-                        const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 });
-                        Doc.GetProto(htmlDoc)["data-text"] = text;
-                        this.props.addDocument(htmlDoc);
+                        const path = window.location.origin + "/doc/";
+                        if (text.startsWith(path)) {
+                            const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                            DocServer.GetRefField(docid).then(f => {
+                                if (f instanceof Doc) {
+                                    if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
+                                    (f instanceof Doc) && this.props.addDocument(f);
+                                }
+                            });
+                        } else {
+                            const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 });
+                            Doc.GetProto(htmlDoc)["data-text"] = text;
+                            this.props.addDocument(htmlDoc);
+                        }
+                        return;
                     }
-                    return;
                 }
             }
-            if (text && text.indexOf("www.youtube.com/watch") !== -1) {
-                const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
-                this.props.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5 }));
-                return;
-            }
-            let matches: RegExpExecArray | null;
-            if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
-                const newBox = Docs.Create.TextDocument("", { ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." });
-                const proto = newBox.proto!;
-                const documentId = matches[2];
-                proto[GoogleRef] = documentId;
-                proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
-                proto.backgroundColor = "#eeeeff";
-                this.props.addDocument(newBox);
-                // const parent = Docs.Create.StackingDocument([newBox], { title: `Google Doc Import (${documentId})` });
-                // CollectionDockingView.Instance.AddRightSplit(parent, undefined);
-                // proto.height = parent[HeightSym]();
-                return;
-            }
-            if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) {
-                const albums = await GooglePhotos.Transactions.ListAlbums();
-                const albumId = matches[3];
-                const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
-                console.log(mediaItems);
-                return;
+
+            if (text) {
+                if (text.includes("www.youtube.com/watch")) {
+                    const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
+                    addDocument(Docs.Create.VideoDocument(url, {
+                        ...options,
+                        title: url,
+                        _width: 400,
+                        _height: 315,
+                        _nativeWidth: 600,
+                        _nativeHeight: 472.5
+                    }));
+                    return;
+                }
+                let matches: RegExpExecArray | null;
+                if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
+                    const newBox = Docs.Create.TextDocument("", { ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." });
+                    const proto = newBox.proto!;
+                    const documentId = matches[2];
+                    proto[GoogleRef] = documentId;
+                    proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
+                    proto.backgroundColor = "#eeeeff";
+                    addDocument(newBox);
+                    return;
+                }
+                if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) {
+                    const albumId = matches[3];
+                    const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
+                    console.log(mediaItems);
+                    return;
+                }
             }
+
             const { items } = e.dataTransfer;
             const { length } = items;
             const files: File[] = [];
-            if (length) {
-                const batch = UndoManager.StartBatch("collection view drop");
-                const promises: Promise<void>[] = [];
-                // tslint:disable-next-line:prefer-for-of
-                for (let i = 0; i < length; i++) {
-                    const item = e.dataTransfer.items[i];
-                    if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
-                        let str: string;
-                        const prom = new Promise<string>(resolve => item.getAsString(resolve))
-                            .then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
-                            .then(result => {
-                                const type = result["content-type"];
-                                if (type) {
-                                    Docs.Get.DocumentFromType(type, str, options)
-                                        .then(doc => doc && this.props.addDocument(doc));
-                                }
-                            });
-                        promises.push(prom);
-                    }
-                    if (item.kind === "file") {
-                        const file = item.getAsFile();
-                        file && file.type && files.push(file);
+            const generatedDocuments: Doc[] = [];
+            if (!length) {
+                alert("No uploadable content found.");
+                return;
+            }
+
+            const batch = UndoManager.StartBatch("collection view drop");
+            for (let i = 0; i < length; i++) {
+                const item = e.dataTransfer.items[i];
+                if (item.kind === "string" && item.type.includes("uri")) {
+                    const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
+                    const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+                    if (type) {
+                        const doc = await Docs.Get.DocumentFromType(type, stringContents, options);
+                        doc && generatedDocuments.push(doc);
                     }
                 }
-                promises.push(Networking.UploadFilesToServer(files).then(responses => responses.forEach(({ source: { name, type }, result }) => {
-                    if (result instanceof Error) {
-                        alert(`Upload failed: ${result.message}`);
-                        return;
-                    }
-                    const full = { ...options, _width: 300, title: name };
-                    const pathname = Utils.prepend(result.accessPaths.agnostic.client);
-                    Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
-                        if (doc) {
-                            const proto = Doc.GetProto(doc);
-                            proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
-                            if (Upload.isImageInformation(result)) {
-                                proto["data-nativeWidth"] = result.nativeWidth;
-                                proto["data-nativeHeight"] = result.nativeHeight;
-                                proto.contentSize = result.contentSize;
-                            }
-                            this.props?.addDocument(doc);
-                        }
-                    });
-                })));
-                if (promises.length) {
-                    Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
-                } else {
-                    if (text && !text.includes("https://")) {
-                        this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
-                    }
-                    batch.end();
+                if (item.kind === "file") {
+                    const file = item.getAsFile();
+                    file && file.type && files.push(file);
+                }
+            }
+            for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
+                if (result instanceof Error) {
+                    alert(`Upload failed: ${result.message}`);
+                    return;
                 }
+                const full = { ...options, _width: 300, title: name };
+                const pathname = Utils.prepend(result.accessPaths.agnostic.client);
+                const doc = await Docs.Get.DocumentFromType(type, pathname, full);
+                if (!doc) {
+                    continue;
+                }
+                const proto = Doc.GetProto(doc);
+                proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+                if (Upload.isImageInformation(result)) {
+                    proto["data-nativeWidth"] = result.nativeWidth;
+                    proto["data-nativeHeight"] = result.nativeHeight;
+                    proto.contentSize = result.contentSize;
+                }
+                generatedDocuments.push(doc);
+            }
+            if (generatedDocuments.length) {
+                generatedDocuments.forEach(addDocument);
+                completed && completed();
             } else {
-                alert("No uploadable content found.");
+                if (text && !text.includes("https://")) {
+                    addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+                }
             }
+            batch.end();
         }
     }
+
     return CollectionSubView;
 }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8720ce002..13ab7c1a4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -210,7 +210,7 @@ class TreeView extends React.Component<TreeViewProps> {
             } else {
                 ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
                 ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
-            }    
+            }
             ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" });
             ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
             ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
@@ -594,7 +594,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this.treedropDisposer && this.treedropDisposer();
         if (this._mainEle = ele) {
-            this.treedropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+            this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
         }
     }
 
@@ -702,7 +702,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
     }
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
-    onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
+    onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {});
 
     @computed get renderClearButton() {
         return <div id="toolbar" key="toolbar">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 969d6b3c8..bdc5e03e3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -117,20 +117,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     @action
-    onDrop = (e: React.DragEvent): Promise<void> => {
+    onExternalDrop = (e: React.DragEvent): Promise<void> => {
         const pt = this.getTransform().transformPoint(e.pageX, e.pageY);
-        return super.onDrop(e, { x: pt[0], y: pt[1] });
+        return super.onExternalDrop(e, { x: pt[0], y: pt[1] });
     }
 
     @undoBatch
     @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (this.props.Document.isBackground) return false;
         const xf = this.getTransform();
         const xfo = this.getTransformOverlay();
         const [xp, yp] = xf.transformPoint(de.x, de.y);
         const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
-        if (super.drop(e, de)) {
+        if (super.onInternalDrop(e, de)) {
             if (de.complete.docDragData) {
                 if (de.complete.docDragData.droppedDocuments.length) {
                     const firstDoc = de.complete.docDragData.droppedDocuments[0];
@@ -1079,7 +1079,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return <div className={"collectionfreeformview-container"}
             ref={this.createDashEventsTarget}
             onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
-            onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}
+            onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
                 transform: this.contentScaling ? `scale(${this.contentScaling})` : "",
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index db5673573..785f90212 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -189,8 +189,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
 
     @undoBatch
     @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        if (super.drop(e, de)) {
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+        if (super.onInternalDrop(e, de)) {
             de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
                 d.dimUnit = "*";
                 d.dimMagnitude = 1;
@@ -214,7 +214,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             getTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
-        />
+        />;
     }
     /**
      * @returns the resolved list of rendered child documents, displayed
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 630a178cf..5e59f8237 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -190,8 +190,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
 
     @undoBatch
     @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        if (super.drop(e, de)) {
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+        if (super.onInternalDrop(e, de)) {
             de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
                 d.dimUnit = "*";
                 d.dimMagnitude = 1;
@@ -215,7 +215,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             getTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
-        />
+        />;
     }
     /**
      * @returns the resolved list of rendered child documents, displayed
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index be452c0ff..9e70af2eb 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -2,6 +2,11 @@ import ApiManager, { Registration } from "./ApiManager";
 import { Method, _permission_denied, PublicHandler } from "../RouteManager";
 import { WebSocket } from "../Websocket/Websocket";
 import { Database } from "../database";
+import rimraf = require("rimraf");
+import { pathToDirectory, Directory } from "./UploadManager";
+import { filesDirectory } from "..";
+import { DashUploadUtils } from "../DashUploadUtils";
+import { mkdirSync } from "fs";
 
 export default class DeleteManager extends ApiManager {
 
@@ -31,21 +36,19 @@ export default class DeleteManager extends ApiManager {
             }
         });
 
-        const hi: PublicHandler = async ({ res, isRelease }) => {
-            if (isRelease) {
-                return _permission_denied(res, deletionPermissionError);
+        register({
+            method: Method.GET,
+            subscription: "/deleteAssets",
+            secureHandler: async ({ res, isRelease }) => {
+                if (isRelease) {
+                    return _permission_denied(res, deletionPermissionError);
+                }
+                rimraf.sync(filesDirectory);
+                mkdirSync(filesDirectory);
+                await DashUploadUtils.buildFileDirectories();
+                res.redirect("/delete");
             }
-            await Database.Instance.deleteAll('users');
-            res.redirect("/home");
-        };
-
-        // register({
-        //     method: Method.GET,
-        //     subscription: "/deleteUsers",
-        //     onValidation: hi,
-        //     onUnauthenticated: hi
-        // });
-
+        });
 
         register({
             method: Method.GET,
-- 
cgit v1.2.3-70-g09d2


From 2ae91501a3195da9dcbc02e180b108983cd1d8af Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 16 Feb 2020 21:47:28 -0500
Subject: got rid of unneeded ignoreAspect field.   other cleanup.

---
 src/client/util/RichTextSchema.tsx                      |  2 +-
 src/client/views/DocumentDecorations.tsx                |  9 ++-------
 src/client/views/collections/CollectionDockingView.tsx  |  8 ++++----
 .../views/collections/CollectionMasonryViewFieldRow.tsx |  3 ++-
 src/client/views/collections/CollectionStackingView.tsx |  6 +++---
 src/client/views/collections/CollectionView.tsx         | 17 -----------------
 .../views/nodes/CollectionFreeFormDocumentView.tsx      |  2 +-
 src/client/views/nodes/DocumentView.tsx                 | 15 +++++----------
 src/client/views/nodes/ImageBox.tsx                     |  2 +-
 src/client/views/nodes/PDFBox.tsx                       |  4 ++--
 src/new_fields/Doc.ts                                   |  3 +--
 src/new_fields/documentSchemas.ts                       |  1 -
 12 files changed, 22 insertions(+), 50 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3a3bcd1ad..656215368 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -723,7 +723,7 @@ export class DashDocView {
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer);
         return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
     }
-    contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+    contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
     outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document);  // ideally, this would scroll to show the focus target
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
         this._textBox = tbox;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 5fc14e716..cc7388a61 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -283,17 +283,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 const actualdH = Math.max(height + (dH * scale), 20);
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
-                const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
-                if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) {
-                    layoutDoc.ignoreAspect = false;
-                    layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
-                    layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
-                }
+                const fixedAspect = e.ctrlKey || (nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
                     layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
                     layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
                 }
-                if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) {
+                if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
                             layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6abfb59c3..bf0333d55 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -678,8 +678,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : this._panelWidth;
     panelHeight = () => this._panelHeight;
 
-    nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
-    nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
+    nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
+    nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
 
     contentScaling = () => {
         if (this.layoutDoc!.type === DocumentType.PDF) {
@@ -705,8 +705,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         }
         return Transform.Identity();
     }
-    get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
-    get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
+    get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+    get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
 
     addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index f3dd7c733..c9b7ca221 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -259,7 +259,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @computed get contentLayout() {
         const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
-        const style = this.props.parent; const collapsed = this._collapsed;
+        const style = this.props.parent;
+        const collapsed = this._collapsed;
         const chromeStatus = this.props.parent.props.Document._chromeStatus;
         const newEditableViewProps = {
             GetValue: () => "",
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a9cefae6a..ad9dc8f7c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -185,7 +185,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (!d) return 0;
         const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
-        return Math.min(nw && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+        return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
@@ -193,9 +193,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
-        if (!layoutDoc.ignoreAspect && !layoutDoc._fitWidth && nw && nh) {
+        if (!layoutDoc._fitWidth && nw && nh) {
             const aspect = nw && nh ? nh / nw : 1;
-            if (!(!layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+            if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
             return wid * aspect;
         }
         return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 48294f9c2..52a18fb99 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -90,11 +90,9 @@ export interface CollectionRenderProps {
 export class CollectionView extends Touchable<FieldViewProps> {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
 
-    private _reactionDisposer: IReactionDisposer | undefined;
     private _isChildActive = false;   //TODO should this be observable?
     @observable private _isLightboxOpen = false;
     @observable private _curLightboxImg = 0;
-    @observable private _collapsed = true;
     @observable private static _safeMode = false;
     public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
 
@@ -111,20 +109,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
         return viewField;
     }
 
-    componentDidMount = () => {
-        this._reactionDisposer = reaction(() => StrCast(this.props.Document._chromeStatus),
-            () => {
-                // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
-                // chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar.
-                const chromeStatus = this.props.Document._chromeStatus;
-                if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed" || chromeStatus === "replaced")) {
-                    runInAction(() => this._collapsed = true);
-                }
-            });
-    }
-
-    componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer();
-
     // bcz: Argh?  What's the height of the collection chromes??  
     chromeHeight = () => (this.props.Document._chromeStatus === "enabled" ? -60 : 0);
 
@@ -198,7 +182,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action
     private collapse = (value: boolean) => {
-        this._collapsed = value;
         this.props.Document._chromeStatus = value ? "collapsed" : "enabled";
     }
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 53d17b580..eaab4086c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -60,7 +60,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return undefined;
     }
 
-    contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect && !this.props.fitToBox ? this.width / this.nativeWidth : 1;
+    contentScaling = () => this.nativeWidth > 0 && !this.props.fitToBox ? this.width / this.nativeWidth : 1;
     panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth());
     panelHeight = () => (this.dataProvider?.height || this.props.PanelHeight());
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b4ec09aee..330410a92 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -364,18 +364,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 const actualdH = Math.max(height + (dH * scale), 20);
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
-                const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight);
-                if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) {
-                    layoutDoc.ignoreAspect = false;
-
-                    layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
-                    layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
-                }
+                const fixedAspect = e.ctrlKey || (nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
                     layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
                     layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
                 }
-                if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) {
+                if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
                             layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0);
@@ -546,10 +540,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     @undoBatch
-    public static unfreezeNativeDimensions = action((layoutDoc: Doc): void => {
+    @action
+    public static unfreezeNativeDimensions(layoutDoc: Doc) {
         layoutDoc._nativeWidth = undefined;
         layoutDoc._nativeHeight = undefined;
-    });
+    }
 
     toggleNativeDimensions = () => {
         if (this.Document._nativeWidth || this.Document._nativeHeight) {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 364bce7a8..5b5768d54 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -375,7 +375,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         const aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
         const shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
 
-        !this.Document.ignoreAspect && this.resize(srcpath);
+        this.resize(srcpath);
 
         return <div className="imageBox-cont" key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
             <div className="imageBox-fader" >
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index e1c5fd27f..a2aec699f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -52,7 +52,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
         this._initialScale = this.props.ScreenToLocalTransform().Scale;
         const nw = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 927));
         const nh = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, 1200));
-        !this.Document._fitWidth && !this.Document.ignoreAspect && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
 
         const backup = "oldPath";
         const { Document } = this.props;
@@ -93,7 +93,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
         this.dataDoc[this.props.fieldKey + "-numPages"] = np;
         this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = nw * 96 / 72;
         this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = nh * 96 / 72;
-        !this.Document._fitWidth && !this.Document.ignoreAspect && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
     }
 
     public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index a722f552e..48890ac8d 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -840,8 +840,7 @@ export namespace Doc {
 
     export function freezeNativeDimensions(layoutDoc: Doc, width: number, height: number): void {
         layoutDoc._autoHeight = false;
-        layoutDoc.ignoreAspect = false;
-        if (!layoutDoc.ignoreAspect && !layoutDoc._nativeWidth) {
+        if (!layoutDoc._nativeWidth) {
             layoutDoc._nativeWidth = NumCast(layoutDoc._width, width);
             layoutDoc._nativeHeight = NumCast(layoutDoc._height, height);
         }
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 3cc05d3d5..f3726cb5a 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -34,7 +34,6 @@ export const documentSchema = createSchema({
     onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
     dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
     removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
-    ignoreAspect: "boolean",    // whether aspect ratio should be ignored when laying out or manipulating the document
     isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field
     isBackground: "boolean",    // whether document is a background element and ignores input events (can only selet with marquee)
     treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
-- 
cgit v1.2.3-70-g09d2


From d53ae2d90e7d7de8135ff19e18535cccd7a90ed0 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 19 Feb 2020 00:45:23 -0500
Subject: getting rid of data docs from many places

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/DictationManager.ts                |  4 +-
 src/client/util/DocumentManager.ts                 |  6 +--
 src/client/util/RichTextSchema.tsx                 |  2 +-
 src/client/views/DocComponent.tsx                  |  4 +-
 src/client/views/DocumentButtonBar.tsx             |  8 +--
 src/client/views/GlobalKeyHandler.ts               |  2 +-
 src/client/views/MainView.tsx                      |  6 +--
 src/client/views/MainViewNotifs.tsx                |  2 +-
 .../views/collections/CollectionDockingView.tsx    | 60 +++++++++-------------
 .../views/collections/CollectionSchemaCells.tsx    |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  6 +--
 .../views/collections/CollectionTreeView.tsx       | 12 ++---
 src/client/views/collections/CollectionView.tsx    |  4 +-
 .../views/collections/ParentDocumentSelector.tsx   |  4 +-
 src/client/views/linking/LinkFollowBox.tsx         | 16 +++---
 src/client/views/linking/LinkMenu.tsx              |  2 +-
 src/client/views/linking/LinkMenuGroup.tsx         |  2 +-
 src/client/views/linking/LinkMenuItem.tsx          |  2 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  2 +-
 src/client/views/nodes/DocuLinkBox.tsx             |  2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 22 ++++----
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/FontIconBox.tsx             |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 src/client/views/nodes/FormattedTextBoxComment.tsx |  4 +-
 src/client/views/nodes/KeyValuePair.tsx            |  4 +-
 src/client/views/nodes/PresBox.tsx                 |  4 +-
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 src/client/views/pdf/Annotation.tsx                |  6 +--
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 .../views/presentationview/PresElementBox.tsx      |  3 +-
 src/client/views/search/SearchItem.tsx             |  6 +--
 src/new_fields/Doc.ts                              |  4 +-
 36 files changed, 105 insertions(+), 112 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 669aa97b5..650538e93 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -654,7 +654,7 @@ export namespace Docs {
                     {
                         type: type,
                         content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth, config.path))
+                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth, config.path))
                         ]
                     }
                 ]
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 3394cb93d..569c1ef6d 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -326,7 +326,7 @@ export namespace DictationManager {
             ["open fields", {
                 action: (target: DocumentView) => {
                     const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
-                    target.props.addDocTab(kvp, target.props.DataDoc, "onRight");
+                    target.props.addDocTab(kvp, "onRight");
                 }
             }],
 
@@ -340,7 +340,7 @@ export namespace DictationManager {
                     const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
                     proto.data = new RichTextField(proseMirrorState);
                     proto.backgroundColor = "#eeffff";
-                    target.props.addDocTab(newBox, proto, "onRight");
+                    target.props.addDocTab(newBox, "onRight");
                 }
             }]
 
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index bfda8e75b..c639f07f5 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -148,7 +148,7 @@ export class DocumentManager {
             const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc);
 
             if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
-                (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined);
+                (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)));
                 highlight();
             } else {
                 const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext);
@@ -164,12 +164,12 @@ export class DocumentManager {
                             retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context
                         } else {
                             if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document);
-                            targetDoc.layout && (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); // otherwise create a new view of the target
+                            targetDoc.layout && (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc))); // otherwise create a new view of the target
                         }
                         highlight();
                     }, 0);
                 } else {  // there's no context view so we need to create one first and try again
-                    (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext, undefined);
+                    (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext);
                     setTimeout(() => {
                         const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
                         const finalDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 23b0a56a8..cfbae5dca 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -613,7 +613,7 @@ export class ImageResizeView {
                 DocServer.GetRefField(node.attrs.docid).then(async linkDoc =>
                     (linkDoc instanceof Doc) &&
                     DocumentManager.Instance.FollowLink(linkDoc, view.state.schema.Document,
-                        document => addDocTab(document, undefined, node.attrs.location ? node.attrs.location : "inTab"), false));
+                        document => addDocTab(document, node.attrs.location ? node.attrs.location : "inTab"), false));
             }
         };
         this._handle.onpointerdown = function (e: any) {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 0bf944f22..61b1f9e0f 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,4 +1,4 @@
-import { Doc, Opt } from '../../new_fields/Doc';
+import { Doc, Opt, DataSym } from '../../new_fields/Doc';
 import { Touchable } from './Touchable';
 import { computed, action, observable } from 'mobx';
 import { Cast } from '../../new_fields/Types';
@@ -58,7 +58,7 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
-        @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Cast(this.props.Document.resolvedDataDoc, Doc, null) || Doc.GetProto(this.props.Document)) as Doc; }
+        @computed get dataDoc() { return this.props.Document[DataSym]; }
 
         _annotationKey: string = "annotations";
         public set annotationKey(val: string) { this._annotationKey = val; }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index b530d8a42..c8ad78f47 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -251,10 +251,10 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
 
     @computed
     get contextButton() {
-        return !this.view0 ? (null) : <ParentDocSelector Views={this.props.views.filter(v => v).map(v => v as DocumentView)} Document={this.view0.props.Document} addDocTab={(doc, data, where) => {
-            where === "onRight" ? CollectionDockingView.AddRightSplit(doc, data) :
-                this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc, data) :
-                    this.view0?.props.addDocTab(doc, data, "onRight");
+        return !this.view0 ? (null) : <ParentDocSelector Views={this.props.views.filter(v => v).map(v => v as DocumentView)} Document={this.view0.props.Document} addDocTab={(doc, where) => {
+            where === "onRight" ? CollectionDockingView.AddRightSplit(doc) :
+                this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc) :
+                    this.view0?.props.addDocTab(doc, "onRight");
             return true;
         }} />;
     }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 979687ffb..d0900251d 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -156,7 +156,7 @@ export default class KeyManager {
                         return { stopPropagation: false, preventDefault: false };
                     }
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform, undefined);
+                MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform);
                 break;
             case "arrowleft":
                 if (document.activeElement) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7948da3c5..1849ec250 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -185,7 +185,7 @@ export class MainView extends React.Component {
                 reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
                     initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
                         if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
-                            CollectionDockingView.AddRightSplit(docField, undefined);
+                            CollectionDockingView.AddRightSplit(docField);
                         }
                     }),
                 );
@@ -379,10 +379,10 @@ export class MainView extends React.Component {
         document.removeEventListener("pointerup", this.onPointerUp);
     }
     flyoutWidthFunc = () => this.flyoutWidth;
-    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]): boolean => {
+    addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
         return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
             doc.dockingConfig ? this.openWorkspace(doc) :
-                CollectionDockingView.AddRightSplit(doc, undefined, libraryPath);
+                CollectionDockingView.AddRightSplit(doc, libraryPath);
     }
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
index 09fa1cb0c..82e07c449 100644
--- a/src/client/views/MainViewNotifs.tsx
+++ b/src/client/views/MainViewNotifs.tsx
@@ -15,7 +15,7 @@ export class MainViewNotifs extends React.Component {
     @observable static NotifsCol: Opt<Doc>;
     openNotifsCol = () => {
         if (MainViewNotifs.NotifsCol) {
-            CollectionDockingView.AddRightSplit(MainViewNotifs.NotifsCol, undefined);
+            CollectionDockingView.AddRightSplit(MainViewNotifs.NotifsCol);
         }
     }
     render() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 26b65c898..6e1d522fc 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import * as ReactDOM from 'react-dom';
 import Measure from "react-measure";
 import * as GoldenLayout from "../../../client/goldenLayout";
 import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field, Opt, DataSym } from "../../../new_fields/Doc";
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
 import { FieldId } from "../../../new_fields/RefField";
@@ -38,7 +38,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
     @observable public static Instances: CollectionDockingView[] = [];
     @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
-    public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number, libraryPath?: Doc[]) {
+    public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -46,8 +46,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             width: width,
             props: {
                 documentId: document[Id],
-                dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : "",
-                libraryPath: libraryPath ? libraryPath.map(d => d[Id]) : []
+                libraryPath: libraryPath?.map(d => d[Id])
                 //collectionDockingView: CollectionDockingView.Instance
             }
         };
@@ -76,12 +75,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     public StartOtherDrag(e: any, dragDocs: Doc[]) {
         let config: any;
         if (dragDocs.length === 1) {
-            config = CollectionDockingView.makeDocumentConfig(dragDocs[0], undefined);
+            config = CollectionDockingView.makeDocumentConfig(dragDocs[0]);
         } else {
             config = {
                 type: 'row',
                 content: dragDocs.map((doc, i) => {
-                    CollectionDockingView.makeDocumentConfig(doc, undefined);
+                    CollectionDockingView.makeDocumentConfig(doc);
                 })
             };
         }
@@ -97,10 +96,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
         const document = Doc.MakeAlias(docView.props.Document);
-        const dataDoc = docView.props.DataDoc;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
         };
         const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
         this._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -172,7 +170,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     @undoBatch
     @action
-    public static ReplaceRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[], addToSplit?: boolean): boolean {
+    public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
         if (!CollectionDockingView.Instance) return false;
         const instance = CollectionDockingView.Instance;
         let retVal = false;
@@ -180,7 +178,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
                 if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
                     DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) {
-                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath);
+                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
                     child.addChild(newItemStackConfig, undefined);
                     !addToSplit && child.contentItems[0].remove();
                     instance.layoutChanged(document);
@@ -188,7 +186,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 }
                 return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
                     if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) {
-                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath);
+                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
                         child.addChild(newItemStackConfig, undefined);
                         !addToSplit && child.contentItems[j].remove();
                         instance.layoutChanged(document);
@@ -210,12 +208,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     @undoBatch
     @action
-    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[]) {
+    public static AddRightSplit(document: Doc, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
         };
 
         const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -245,18 +243,18 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     @undoBatch
     @action
-    public static UseRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[], shiftKey?: boolean) {
+    public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
         document.isDisplayPanel = true;
-        if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, dataDoc, libraryPath, shiftKey)) {
-            CollectionDockingView.AddRightSplit(document, dataDoc, libraryPath);
+        if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
+            CollectionDockingView.AddRightSplit(document, libraryPath);
         }
     }
 
     @undoBatch
     @action
-    public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined, libraryPath?: Doc[]) => {
+    public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
         Doc.GetProto(document).lastOpened = new DateField;
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument, undefined, libraryPath);
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
         if (stack === undefined) {
             let stack: any = this._goldenLayout.root;
             while (!stack.isStack) {
@@ -427,7 +425,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             }
 
             const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
-            const dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
             if (doc instanceof Doc) {
                 //tab.titleElement[0].outerHTML = `<input class='lm_title' style="background:black" value='${doc.title}' />`;
                 tab.titleElement[0].onclick = (e: any) => tab.titleElement[0].focus();
@@ -507,7 +504,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         stack.header.element[0].style.backgroundColor = DocServer.Control.isReadOnly() ? "#228540" : undefined;
         stack.header.element.on('mousedown', (e: any) => {
             if (e.target === stack.header.element[0] && e.button === 1) {
-                this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }), undefined);
+                this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }));
             }
         });
 
@@ -580,7 +577,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
 interface DockedFrameProps {
     documentId: FieldId;
-    dataDocumentId: FieldId;
     glContainer: any;
     libraryPath: (FieldId[]);
     backgroundColor?: (doc: Doc) => string | undefined;
@@ -593,7 +589,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Doc>;
-    @observable private _dataDoc: Opt<Doc>;
     @observable private _isActive: boolean = false;
 
     get _stack(): any {
@@ -601,12 +596,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
     constructor(props: any) {
         super(props);
-        DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => {
-            this._document = f as Doc;
-            if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) {
-                DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
-            }
-        }));
+        DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
         this.props.libraryPath && this.setupLibraryPath();
     }
 
@@ -630,7 +620,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             pinDoc.presentationTargetDoc = doc;
             Doc.AddDocToList(curPres, "data", pinDoc);
             if (!DocumentManager.Instance.getDocumentView(curPres)) {
-                CollectionDockingView.AddRightSplit(curPres, undefined);
+                CollectionDockingView.AddRightSplit(curPres);
             }
         }
     }
@@ -708,16 +698,16 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
 
-    addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string, libraryPath?: Doc[]) => {
+    addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc.dockingConfig) {
             return MainView.Instance.openWorkspace(doc);
         } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc, dataDoc, libraryPath);
+            return CollectionDockingView.AddRightSplit(doc, libraryPath);
         } else if (location === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         } else {
-            return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc, libraryPath);
+            return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
         }
     }
 
@@ -725,7 +715,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         TraceMobx();
         if (!this._document) return (null);
         const document = this._document;
-        const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
+        const resolvedDataDoc = this._document[DataSym] !== this._document && this._document[DataSym];// document.layout instanceof Doc ? document : this._dataDoc;
         return <DocumentView key={document[Id]}
             LibraryPath={this._libraryPath}
             Document={document}
@@ -762,5 +752,5 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             </div >);
     }
 }
-Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc, undefined); });
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, undefined, shiftKey); });
+Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); });
+Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); });
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index a3b1b5ec0..851fded71 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -35,7 +35,7 @@ export interface CellProps {
     Document: Doc;
     fieldKey: string;
     renderDepth: number;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     moveDocument: (document: Doc, targetCollection: Doc | undefined, 
     addDocument: (document: Doc) => boolean) => boolean;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index c422c38f1..7dee7f18f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -224,7 +224,7 @@ export interface SchemaTableProps {
     ScreenToLocalTransform: () => Transform;
     active: (outsideReaction: boolean) => boolean;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     isSelected: (outsideReaction?: boolean) => boolean;
     isFocused: (document: Doc) => boolean;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index ad9dc8f7c..0d5d3e449 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,7 +4,7 @@ import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Switch from 'rc-switch';
-import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
@@ -62,7 +62,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
             const style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
             return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
-                {this.getDisplayDoc(d, Cast(d.resolvedDataDoc, Doc, null) || this.props.DataDoc, dxf, width)}
+                {this.getDisplayDoc(d, this.props.DataDoc, dxf, width)}
             </div>;
         });
     }
@@ -158,7 +158,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         const height = () => this.getDocHeight(doc);
         return <ContentFittingDocumentView
             Document={doc}
-            DataDocument={dataDoc}
+            DataDocument={doc[DataSym] !== doc && doc[DataSym]}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 13ab7c1a4..dd622bf9d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -47,7 +47,7 @@ export interface TreeViewProps {
     deleteDoc: (doc: Doc) => boolean;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
+    addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     panelWidth: () => number;
     panelHeight: () => number;
@@ -128,7 +128,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
-    @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
+    @undoBatch openRight = () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath);
     @undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
     @undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
@@ -201,8 +201,8 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
             } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
                 ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
-                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab", this.props.libraryPath), icon: "folder" });
-                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath), icon: "caret-square-right" });
+                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab", this.props.libraryPath), icon: "folder" });
+                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath), icon: "caret-square-right" });
                 if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
                     ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" });
                 }
@@ -212,7 +212,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
             }
             ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" });
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, "onRight"); }, icon: "layer-group" });
             ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
             ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
             e.stopPropagation();
@@ -457,7 +457,7 @@ class TreeView extends React.Component<TreeViewProps> {
         remove: ((doc: Doc) => boolean),
         move: DragManager.MoveFunction,
         dropAction: dropActionType,
-        addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean,
+        addDocTab: (doc: Doc, where: string) => boolean,
         pinToPres: (document: Doc) => void,
         backgroundColor: undefined | ((document: Doc) => string | undefined),
         screenToLocalXf: () => Transform,
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 79fd7d792..95f9fa62f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -229,10 +229,10 @@ export class CollectionView extends Touchable<FieldViewProps> {
             const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
             if (this.props.Document.childLayout instanceof Doc) {
-                layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, undefined, "onRight"), icon: "project-diagram" });
+                layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
             }
             if (this.props.Document.childDetailed instanceof Doc) {
-                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, undefined, "onRight"), icon: "project-diagram" });
+                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, "onRight"), icon: "project-diagram" });
             }
             !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
 
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 027ac5a0e..43ba5c614 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -24,7 +24,7 @@ type SelectorProps = {
     Document: Doc,
     Views: DocumentView[],
     Stack?: any,
-    addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void
+    addDocTab(doc: Doc, location: string): void
 };
 
 @observer
@@ -60,7 +60,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
                 col._panX = newPanX;
                 col._panY = newPanY;
             }
-            this.props.addDocTab(col, undefined, "inTab"); // bcz: dataDoc?
+            this.props.addDocTab(col, "inTab"); // bcz: dataDoc?
         };
     }
 
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index 325c92413..b47464517 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -183,9 +183,9 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     }
 
-    static _addDocTab: (undefined | ((doc: Doc, dataDoc: Opt<Doc>, where: string) => boolean));
+    static _addDocTab: (undefined | ((doc: Doc, where: string) => boolean));
 
-    static setAddDocTab = (addFunc: (doc: Doc, dataDoc: Opt<Doc>, where: string) => boolean) => {
+    static setAddDocTab = (addFunc: (doc: Doc, where: string) => boolean) => {
         LinkFollowBox._addDocTab = addFunc;
     }
 
@@ -199,7 +199,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 options.context._panX = newPanX;
                 options.context._panY = newPanY;
             }
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, undefined, "onRight");
+            (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, "onRight");
 
             if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
 
@@ -212,7 +212,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     openLinkRight = () => {
         if (LinkFollowBox.destinationDoc) {
             const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(alias, undefined, "onRight");
+            (LinkFollowBox._addDocTab || this.props.addDocTab)(alias, "onRight");
             this.highlightDoc();
             SelectionManager.DeselectAll();
         }
@@ -222,8 +222,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     @undoBatch
     jumpToLink = async (options: { shouldZoom: boolean }) => {
         if (LinkFollowBox.sourceDoc && LinkFollowBox.linkDoc) {
-            const focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
-            //let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation));
+            const focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, "inTab"); SelectionManager.DeselectAll(); };
+            //let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, maxLocation));
 
             DocumentManager.Instance.FollowLink(LinkFollowBox.linkDoc, LinkFollowBox.sourceDoc, focus, options && options.shouldZoom, false, undefined);
         }
@@ -234,7 +234,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         if (LinkFollowBox.destinationDoc) {
             const fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
             // this.prosp.addDocTab is empty -- use the link source's addDocTab 
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(fullScreenAlias, undefined, "inTab");
+            (LinkFollowBox._addDocTab || this.props.addDocTab)(fullScreenAlias, "inTab");
 
             this.highlightDoc();
             SelectionManager.DeselectAll();
@@ -251,7 +251,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                 options.context._panX = newPanX;
                 options.context._panY = newPanY;
             }
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, undefined, "inTab");
+            (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, "inTab");
             if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
 
             this.highlightDoc();
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 1a40f0c55..a8aa01306 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -16,7 +16,7 @@ library.add(faTrash);
 interface Props {
     docView: DocumentView;
     changeFlyout: () => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
 }
 
 @observer
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 0c38ff45c..78c77e106 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -17,7 +17,7 @@ interface LinkMenuGroupProps {
     group: Doc[];
     groupType: string;
     showEditor: (linkDoc: Doc) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     docView: DocumentView;
 }
 
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index b7d27ee30..54f5636e4 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -20,7 +20,7 @@ interface LinkMenuItemProps {
     sourceDoc: Doc;
     destinationDoc: Doc;
     showEditor: (linkDoc: Doc) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
 }
 
 @observer
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 73fe4fb5d..36233a7e6 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -34,7 +34,7 @@ interface ContentFittingDocumentViewProps {
     removeDocument?: (document: Doc) => boolean;
     active: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     dontRegisterView?: boolean;
 }
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index 156b16ec8..a0b5cd8ec 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -61,7 +61,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
     onClick = (e: React.MouseEvent) => {
         if (!this.props.Document.onClick) {
             if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) {
-                DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false);
+                DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, "inTab"), false);
             }
             e.stopPropagation();
         }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 11a33b70a..c0f603171 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -82,7 +82,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return this.props.DataDoc instanceof Promise ? undefined : this.props.DataDoc;
     }
     get layoutDoc() {
-        if (this.props.DataDoc === undefined && (this.props.LayoutDoc || typeof Doc.LayoutField(this.props.Document) !== "string")) {
+        if (this.props.LayoutDoc || (this.props.DataDoc === undefined && typeof Doc.LayoutField(this.props.Document) !== "string")) {
             // if there is no dataDoc (ie, we're not rendering a template layout), but this document has a layout document (not a layout string), 
             // then we render the layout document as a template and use this document as the data context for the template layout.
             return Doc.expandTemplateLayout(this.props.LayoutDoc?.() || Doc.Layout(this.props.Document), this.props.Document);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 566c0d5de..dea08600a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -71,7 +71,7 @@ export interface DocumentViewProps {
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
+    addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     zoomToScale: (scale: number) => void;
     backgroundHalo?: () => boolean;
@@ -152,9 +152,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     //     RadialMenu.Instance.openMenu();
 
-    //     RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 });
+    //     RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"), icon: "layer-group", selected: -1 });
     //     RadialMenu.Instance.addItem({ description: "Delete this document", event: () => this.props.ContainingCollectionView?.removeDocument(this.props.Document), icon: "trash", selected: -1 });
-    //     RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, undefined, "onRight"), icon: "folder", selected: -1 });
+    //     RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "folder", selected: -1 });
     //     RadialMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
 
     //     RadialMenu.Instance.displayMenu(pt.pageX - 15, pt.pageY - 15);
@@ -253,7 +253,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
                     fullScreenAlias.layoutKey = "layout_fullScreen";
                 }
-                UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, undefined, "inTab"), "double tap");
+                UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap");
                 SelectionManager.DeselectAll();
                 Doc.UnBrushDoc(this.props.Document);
             } else if (this.onClickHandler && this.onClickHandler.script) {
@@ -277,7 +277,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (linkDocs.length) {
             DocumentManager.Instance.FollowLink(undefined, this.props.Document,
                 // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
-                (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
+                (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, maxLocation)),
                 ctrlKey, altKey, this.props.ContainingCollectionDoc);
         }
     }
@@ -644,12 +644,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const open = ContextMenu.Instance.findByDescription("Open...");
         const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
         openItems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" });
-        openItems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab", this.props.LibraryPath), icon: "folder" });
-        openItems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
-        openItems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
-        openItems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
-        openItems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), undefined, "onRight"), icon: "layer-group" });
-        templateDoc && openItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, undefined, "onRight"), icon: "eye" });
+        openItems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, "inTab", this.props.LibraryPath), icon: "folder" });
+        openItems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
+        openItems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" });
+        openItems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" });
+        openItems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
+        templateDoc && openItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
         openItems.push({ description: "Open Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
         !open && cm.addItem({ description: "Open...", subitems: openItems, icon: "external-link-alt" });
 
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 00f00dd52..f5c85e143 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -29,7 +29,7 @@ export interface FieldViewProps {
     select: (isCtrlPressed: boolean) => void;
     renderDepth: number;
     addDocument?: (document: Doc) => boolean;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc) => boolean;
     moveDocument?: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index a191ac4f4..d4da21239 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -36,7 +36,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
     showTemplate = (): void => {
         const dragFactory = Cast(this.props.Document.dragFactory, Doc, null);
-        dragFactory && this.props.addDocTab(dragFactory, undefined, "onRight");
+        dragFactory && this.props.addDocTab(dragFactory, "onRight");
     }
 
     specificContextMenu = (): void => {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index fd288f720..842ccdd7b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -916,7 +916,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         //             if (linkClicked) {
         //                 DocServer.GetRefField(linkClicked).then(async linkDoc => {
         //                     (linkDoc instanceof Doc) &&
-        //                         DocumentManager.Instance.FollowLink(linkDoc, this.props.Document, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), false);
+        //                         DocumentManager.Instance.FollowLink(linkDoc, this.props.Document, document => this.props.addDocTab(document, location ? location : "inTab"), false);
         //                 });
         //             }
         //         } else {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index fda3e3285..1174110e7 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -84,9 +84,9 @@ export class FormattedTextBoxComment {
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
                     DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
-                        (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
+                        (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"));
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
-                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), undefined, "onRight");
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), "onRight");
                 }
                 keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index e6b512adf..93bda6d02 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -22,7 +22,7 @@ export interface KeyValuePairProps {
     keyWidth: number;
     PanelHeight: () => number;
     PanelWidth: () => number;
-    addDocTab: (doc: Doc, data: Opt<Doc>, where: string) => boolean;
+    addDocTab: (doc: Doc, where: string) => boolean;
 }
 @observer
 export class KeyValuePair extends React.Component<KeyValuePairProps> {
@@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
         if (value instanceof Doc) {
             e.stopPropagation();
             e.preventDefault();
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), undefined, "onRight"), icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
             ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
         }
     }
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index d4a47c159..8d20bbe59 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -327,12 +327,12 @@ export class PresBox extends React.Component<FieldViewProps> {
         if (toggle) {
             if (this.props.Document.inOverlay) {
                 Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document);
-                CollectionDockingView.AddRightSplit(this.props.Document, this.props.DataDoc);
+                CollectionDockingView.AddRightSplit(this.props.Document);
                 this.props.Document.inOverlay = false;
             } else {
                 this.props.Document.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
                 this.props.Document.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
-                this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close");
+                this.props.addDocTab?.(this.props.Document, "close");
                 Doc.AddDocToList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document);
             }
         }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 6695e04c3..69c6f2617 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -90,7 +90,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     @action public FullScreen() {
         this._fullScreen = true;
         this.player && this.player.requestFullscreen();
-        this._youtubePlayer && this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab");
+        this._youtubePlayer && this.props.addDocTab(this.props.Document, "inTab");
     }
 
     choosePath(url: string) {
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index d8b340db6..58823c93d 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -11,7 +11,7 @@ import "./Annotation.scss";
 
 interface IAnnotationProps {
     anno: Doc;
-    addDocTab: (document: Doc, dataDoc: Opt<Doc>, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     focus: (doc: Doc) => void;
     dataDoc: Doc;
@@ -30,7 +30,7 @@ interface IRegionAnnotationProps {
     y: number;
     width: number;
     height: number;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     document: Doc;
     dataDoc: Doc;
@@ -98,7 +98,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             const annoGroup = await Cast(this.props.document.group, Doc);
             if (annoGroup) {
                 DocumentManager.Instance.FollowLink(undefined, annoGroup,
-                    (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"),
+                    (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"),
                     false, false, undefined);
                 e.stopPropagation();
             }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a9b8c6bbe..198aeb856 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -65,7 +65,7 @@ interface IViewerProps {
     loaded: (nw: number, nh: number, np: number) => void;
     active: (outsideReaction?: boolean) => boolean;
     isChildActive: (outsideReaction?: boolean) => boolean;
-    addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     addDocument?: (doc: Doc) => boolean;
     setPdfViewer: (view: PDFViewer) => void;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 888707ee3..8d62c34c5 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -4,7 +4,7 @@ import { faArrowDown, faArrowUp, faFile as fileSolid, faFileDownload, faLocation
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DataSym } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from "../../../new_fields/FieldSymbols";
 import { createSchema, makeInterface } from '../../../new_fields/Schema';
@@ -173,6 +173,7 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
             <div className="presElementBox-embedded" style={{ height: this.embedHeight() }}>
                 <ContentFittingDocumentView
                     Document={this.targetDoc}
+                    DataDocument={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     LibraryPath={emptyPath}
                     fitToBox={true}
                     addDocument={returnFalse}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 827cb80c6..2cbb24da7 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -74,7 +74,7 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
                 col._panX = newPanX;
                 col._panY = newPanY;
             }
-            CollectionDockingView.AddRightSplit(col, undefined);
+            CollectionDockingView.AddRightSplit(col);
         };
     }
     render() {
@@ -108,7 +108,7 @@ export class LinkContextMenu extends React.Component<LinkMenuProps> {
 
     unHighlightDoc = (doc: Doc) => () => Doc.UnBrushDoc(doc);
 
-    getOnClick = (col: Doc) => () => CollectionDockingView.AddRightSplit(col, undefined);
+    getOnClick = (col: Doc) => () => CollectionDockingView.AddRightSplit(col);
 
     render() {
         return (
@@ -272,7 +272,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
 
     @computed
     get contextButton() {
-        return <ParentDocSelector Views={DocumentManager.Instance.DocumentViews} Document={this.props.doc} addDocTab={(doc, data, where) => CollectionDockingView.AddRightSplit(doc, data)} />;
+        return <ParentDocSelector Views={DocumentManager.Instance.DocumentViews} Document={this.props.doc} addDocTab={(doc, where) => CollectionDockingView.AddRightSplit(doc)} />;
     }
 
     render() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 43bbea623..1f2dd1c8f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -17,6 +17,7 @@ import { listSpec } from "./Schema";
 import { ComputedField } from "./ScriptField";
 import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
+import { DatasetAbout } from "../client/northstar/model/idea/idea";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -495,7 +496,8 @@ export namespace Doc {
     // if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
     // otherwise, it just returns the childDoc
     export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
-        const resolvedDataDoc = containerDataDoc === containerDoc || !containerDataDoc || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc;
+        const existingResolvedDataDoc = childDoc[DataSym] !== Doc.GetProto(childDoc)[DataSym] && childDoc[DataSym];
+        const resolvedDataDoc = existingResolvedDataDoc || (containerDataDoc === containerDoc || !containerDataDoc || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc);
         return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc), data: resolvedDataDoc };
     }
     export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
-- 
cgit v1.2.3-70-g09d2


From 1c73798b14bc91e59ab95225b341008271d0159a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 22 Feb 2020 01:04:55 -0500
Subject: removed LinkFollow box.

---
 src/client/documents/DocumentTypes.ts              |   1 -
 src/client/documents/Documents.ts                  |   8 -
 src/client/views/OverlayView.tsx                   |   2 +-
 .../views/collections/CollectionLinearView.tsx     |   2 +-
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 .../views/collections/CollectionTreeView.tsx       |   1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   6 +-
 src/client/views/linking/LinkFollowBox.scss        |  93 ----
 src/client/views/linking/LinkFollowBox.tsx         | 571 ---------------------
 src/client/views/linking/LinkMenuItem.tsx          |   1 -
 src/client/views/nodes/DocumentContentsView.tsx    |   3 +-
 .../authentication/models/current_user_utils.ts    |   2 -
 12 files changed, 7 insertions(+), 685 deletions(-)
 delete mode 100644 src/client/views/linking/LinkFollowBox.scss
 delete mode 100644 src/client/views/linking/LinkFollowBox.tsx

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

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 0e6b59b33..06b15d78c 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -19,7 +19,6 @@ export enum DocumentType {
     WEBCAM = "webcam",
     FONTICON = "fonticonbox",
     PRES = "presentation",
-    LINKFOLLOW = "linkfollow",
     PRESELEMENT = "preselement",
     QUERY = "search",
     COLOR = "color",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index aed14e4f6..ff64489bb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -40,7 +40,6 @@ import { PresBox } from "../views/nodes/PresBox";
 import { ComputedField, ScriptField } from "../../new_fields/ScriptField";
 import { ProxyField } from "../../new_fields/Proxy";
 import { DocumentType } from "./DocumentTypes";
-import { LinkFollowBox } from "../views/linking/LinkFollowBox";
 import { PresElementBox } from "../views/presentationview/PresElementBox";
 import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { QueryBox } from "../views/nodes/QueryBox";
@@ -250,9 +249,6 @@ export namespace Docs {
                 layout: { view: FontIconBox, dataField: data },
                 options: { _width: 40, _height: 40, borderRounding: "100%" },
             }],
-            [DocumentType.LINKFOLLOW, {
-                layout: { view: LinkFollowBox, dataField: data }
-            }],
             [DocumentType.WEBCAM, {
                 layout: { view: DashWebRTCVideo, dataField: data }
             }],
@@ -658,10 +654,6 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
         }
 
-        export function LinkFollowBoxDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.LINKFOLLOW), undefined, { ...(options || {}) });
-        }
-
         export function PresElementBoxDocument(options?: DocumentOptions) {
             return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) });
         }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7a99bf0ae..220efd4a8 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -169,7 +169,7 @@ export class OverlayView extends React.Component {
                 document.addEventListener("pointermove", onPointerMove);
                 document.addEventListener("pointerup", onPointerUp);
             };
-            return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)`, display: d.isMinimized ? "none" : "" }}>
+            return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)` }}>
                 <DocumentView
                     Document={d}
                     LibraryPath={emptyPath}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 9bbc9f1b6..4a14a30cd 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -71,7 +71,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
         }
     }
 
-    public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+    public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
 
     dimension = () => NumCast(this.props.Document._height); // 2 * the padding
     getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0d5d3e449..a1cc21319 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -38,7 +38,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @observable _scroll = 0; // used to force the document decoration to update when scrolling
     @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
     @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
-    @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
+    @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
     @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
     @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ed1d674c6..7eeeb6ff1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -421,7 +421,6 @@ class TreeView extends React.Component<TreeViewProps> {
         return <>
             <div className="docContainer" title="click to edit title" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
                 style={{
-                    color: this.props.document.isMinimized ? "red" : "inherit",
                     background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
                     fontWeight: this.props.document.searchMatch ? "bold" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5902cc22e..4458c7dcf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -111,7 +111,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         SelectionManager.DeselectAll();
         docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
     }
-    public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+    public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
 
     public getActiveDocuments = () => {
         return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
@@ -433,7 +433,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
         let x = this.Document._panX || 0;
         let y = this.Document._panY || 0;
-        const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout);
+        const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout);
         const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
         if (!this.isAnnotationOverlay && docs.length && this.childDataProvider(docs[0])) {
             PDFMenu.Instance.fadeOut(true);
@@ -869,7 +869,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             }));
         this._cachedPool.clear();
         Array.from(newPool.keys()).forEach(k => this._cachedPool.set(k, newPool.get(k)));
-        const elements:ViewDefResult[] = computedElementData.slice();
+        const elements: ViewDefResult[] = computedElementData.slice();
         this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair =>
             elements.push({
                 ele: <CollectionFreeFormDocumentView key={pair.layout[Id]}  {...this.getChildDocumentViewProps(pair.layout, pair.data)}
diff --git a/src/client/views/linking/LinkFollowBox.scss b/src/client/views/linking/LinkFollowBox.scss
deleted file mode 100644
index 9eeed1cc8..000000000
--- a/src/client/views/linking/LinkFollowBox.scss
+++ /dev/null
@@ -1,93 +0,0 @@
-@import "../globalCssVariables";
-
-.linkFollowBox-main {
-    position: absolute;
-    background: whitesmoke;
-    color: grey;
-    border-radius: 15px;
-    box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw;
-    border: solid #BBBBBBBB 5px;
-    pointer-events: all;
-
-    .linkFollowBox-header {
-        height: 50px;
-        text-align: center;
-        text-transform: uppercase;
-        letter-spacing: 2px;
-        font-size: 16px;
-        width: 100%;
-    }
-
-    .direction-indicator {
-        font-size: 12px;
-    }
-
-    .closeDocument {
-        position: relative;
-        max-width: 30px;
-        top: -20px;
-        left: 460px;
-        color: $darker-alt-accent
-    }
-
-    .closeDocument:hover {
-        color: $main-accent;
-    }
-
-    .topHeader {
-        width: 100%;
-        height: 25px;
-    }
-
-    .linkFollowBox-footer {
-        height: 50px;
-        text-align: center;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-
-        button {
-            background-color: $darker-alt-accent;
-            width: 30%;
-        }
-    }
-
-    .linkFollowBox-content {
-        display: grid;
-        grid-template-columns: 1fr 1fr 1fr;
-        grid-column-gap: 5px;
-        margin-left: 5px;
-        margin-right: 5px;
-
-        .linkFollowBox-item {
-            background-color: $light-color;
-            width: 100%;
-            height: 100%;
-
-            .linkFollowBox-itemContent {
-                padding: 5px;
-                font-size: 12px;
-                overflow: scroll;
-
-                input[type=radio] {
-                    border: 0px;
-                    margin-right: 5px;
-                }
-            }
-
-            .title {
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                text-transform: uppercase;
-                color: $light-color;
-                background-color: $lighter-alt-accent;
-                width: 100%;
-                height: 30px;
-                border-bottom: solid $darker-alt-accent 5px;
-                font-size: 12px;
-                text-align: center;
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
deleted file mode 100644
index b47464517..000000000
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ /dev/null
@@ -1,571 +0,0 @@
-import { observable, computed, action, runInAction, reaction, IReactionDisposer } from "mobx";
-import React = require("react");
-import { observer } from "mobx-react";
-import { FieldViewProps, FieldView } from "../nodes/FieldView";
-import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { undoBatch } from "../../util/UndoManager";
-import { NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
-import { CollectionViewType } from "../collections/CollectionView";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DocumentView } from "../nodes/DocumentView";
-import "./LinkFollowBox.scss";
-import { SearchUtil } from "../../util/SearchUtil";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { listSpec } from "../../../new_fields/Schema";
-import { DocServer } from "../../DocServer";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faTimes } from '@fortawesome/free-solid-svg-icons';
-import { docs_v1 } from "googleapis";
-import { Utils } from "../../../Utils";
-import { Link } from "@react-pdf/renderer";
-
-enum FollowModes {
-    OPENTAB = "Open in Tab",
-    OPENRIGHT = "Open in Right Split",
-    OPENFULL = "Open Full Screen",
-    PAN = "Pan to Document",
-    INPLACE = "Open In Place"
-}
-
-enum FollowOptions {
-    ZOOM = "Zoom",
-    NOZOOM = "No Zoom",
-}
-
-@observer
-export class LinkFollowBox extends React.Component<FieldViewProps> {
-
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkFollowBox, fieldKey); }
-    public static Instance: LinkFollowBox | undefined;
-    @observable static linkDoc: Doc | undefined = undefined;
-    @observable static destinationDoc: Doc | undefined = undefined;
-    @observable static sourceDoc: Doc | undefined = undefined;
-    @observable selectedMode: string = "";
-    @observable selectedContext: Doc | undefined = undefined;
-    @observable selectedContextAliases: Doc[] | undefined = undefined;
-    @observable selectedOption: string = "";
-    @observable selectedContextString: string = "";
-    @observable sourceView: DocumentView | undefined = undefined;
-    @observable canPan: boolean = false;
-    @observable shouldUseOnlyParentContext = false;
-    _contextDisposer?: IReactionDisposer;
-
-    @observable private _docs: { col: Doc, target: Doc }[] = [];
-    @observable private _otherDocs: { col: Doc, target: Doc }[] = [];
-
-    constructor(props: FieldViewProps) {
-        super(props);
-        LinkFollowBox.Instance = this;
-        this.resetVars();
-        this.props.Document.isBackground = true;
-    }
-
-    componentDidMount = () => {
-        this.resetVars();
-
-        this._contextDisposer = reaction(
-            () => this.selectedContextString,
-            async () => {
-                const ref = await DocServer.GetRefField(this.selectedContextString);
-                runInAction(() => {
-                    if (ref instanceof Doc) {
-                        this.selectedContext = ref;
-                    }
-                });
-                if (this.selectedContext instanceof Doc) {
-                    const aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext);
-                    runInAction(() => { this.selectedContextAliases = aliases; });
-                }
-            }
-        );
-    }
-
-    componentWillUnmount = () => {
-        this._contextDisposer && this._contextDisposer();
-    }
-
-    async resetPan() {
-        if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
-            runInAction(() => this.canPan = false);
-            if (this.sourceView.props.ContainingCollectionDoc._viewType === CollectionViewType.Freeform) {
-                const docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []);
-                const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
-
-                aliases.forEach(alias => {
-                    if (docs.filter(doc => doc === alias).length > 0) {
-                        runInAction(() => { this.canPan = true; });
-                    }
-                });
-            }
-        }
-    }
-
-    @action
-    resetVars = () => {
-        this.selectedContext = undefined;
-        this.selectedContextString = "";
-        this.selectedMode = "";
-        this.selectedOption = "";
-        LinkFollowBox.linkDoc = undefined;
-        LinkFollowBox.sourceDoc = undefined;
-        LinkFollowBox.destinationDoc = undefined;
-        this.sourceView = undefined;
-        this.canPan = false;
-        this.shouldUseOnlyParentContext = false;
-    }
-
-    async fetchDocuments() {
-        if (LinkFollowBox.destinationDoc) {
-            const dest: Doc = LinkFollowBox.destinationDoc;
-            const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest));
-            const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${dest[Id]}"` });
-            const map: Map<Doc, Doc> = new Map;
-            const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
-            allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index])));
-            docs.forEach(doc => map.delete(doc));
-            runInAction(async () => {
-                this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest }));
-                this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target }));
-                const tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc;
-                runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest }));
-            });
-        }
-    }
-
-    @action
-    setLinkDocs = (linkDoc: Doc, source: Doc, dest: Doc) => {
-        this.resetVars();
-
-        LinkFollowBox.linkDoc = linkDoc;
-        LinkFollowBox.sourceDoc = source;
-        LinkFollowBox.destinationDoc = dest;
-        this.fetchDocuments();
-
-        SelectionManager.SelectedDocuments().forEach(dv => {
-            if (dv.props.Document === LinkFollowBox.sourceDoc) {
-                this.sourceView = dv;
-            }
-        });
-
-        this.resetPan();
-    }
-
-    highlightDoc = () => LinkFollowBox.destinationDoc && Doc.linkFollowHighlight(LinkFollowBox.destinationDoc);
-
-    @undoBatch
-    openFullScreen = () => {
-        if (LinkFollowBox.destinationDoc) {
-            const view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
-            view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
-        }
-    }
-
-    @undoBatch
-    openColFullScreen = (options: { context: Doc }) => {
-        if (LinkFollowBox.destinationDoc) {
-            if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
-                const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2;
-                const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2;
-                options.context._panX = newPanX;
-                options.context._panY = newPanY;
-            }
-            const view = DocumentManager.Instance.getDocumentView(options.context);
-            view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
-            this.highlightDoc();
-        }
-    }
-
-    // should container be a doc or documentview or what? This one needs work and is more long term
-    @undoBatch
-    openInContainer = (options: { container: Doc }) => {
-
-    }
-
-    static _addDocTab: (undefined | ((doc: Doc, where: string) => boolean));
-
-    static setAddDocTab = (addFunc: (doc: Doc, where: string) => boolean) => {
-        LinkFollowBox._addDocTab = addFunc;
-    }
-
-    @undoBatch
-    openLinkColRight = (options: { context: Doc, shouldZoom: boolean }) => {
-        if (LinkFollowBox.destinationDoc) {
-            options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context;
-            if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
-                const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2;
-                const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2;
-                options.context._panX = newPanX;
-                options.context._panY = newPanY;
-            }
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, "onRight");
-
-            if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
-
-            this.highlightDoc();
-            SelectionManager.DeselectAll();
-        }
-    }
-
-    @undoBatch
-    openLinkRight = () => {
-        if (LinkFollowBox.destinationDoc) {
-            const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(alias, "onRight");
-            this.highlightDoc();
-            SelectionManager.DeselectAll();
-        }
-
-    }
-
-    @undoBatch
-    jumpToLink = async (options: { shouldZoom: boolean }) => {
-        if (LinkFollowBox.sourceDoc && LinkFollowBox.linkDoc) {
-            const focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, "inTab"); SelectionManager.DeselectAll(); };
-            //let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, maxLocation));
-
-            DocumentManager.Instance.FollowLink(LinkFollowBox.linkDoc, LinkFollowBox.sourceDoc, focus, options && options.shouldZoom, false, undefined);
-        }
-    }
-
-    @undoBatch
-    openLinkTab = () => {
-        if (LinkFollowBox.destinationDoc) {
-            const fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-            // this.prosp.addDocTab is empty -- use the link source's addDocTab 
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(fullScreenAlias, "inTab");
-
-            this.highlightDoc();
-            SelectionManager.DeselectAll();
-        }
-    }
-
-    @undoBatch
-    openLinkColTab = (options: { context: Doc, shouldZoom: boolean }) => {
-        if (LinkFollowBox.destinationDoc) {
-            options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context;
-            if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
-                const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2;
-                const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2;
-                options.context._panX = newPanX;
-                options.context._panY = newPanY;
-            }
-            (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, "inTab");
-            if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
-
-            this.highlightDoc();
-            SelectionManager.DeselectAll();
-        }
-    }
-
-    @undoBatch
-    openLinkInPlace = (options: { shouldZoom: boolean }) => {
-
-        if (LinkFollowBox.destinationDoc && LinkFollowBox.sourceDoc) {
-            if (this.sourceView && this.sourceView.props.addDocument) {
-                const destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc);
-                if (!destViews.find(dv => dv.props.ContainingCollectionView === this.sourceView!.props.ContainingCollectionView)) {
-                    const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
-                    const y = NumCast(LinkFollowBox.sourceDoc.y);
-                    const x = NumCast(LinkFollowBox.sourceDoc.x);
-
-                    const width = NumCast(LinkFollowBox.sourceDoc._width);
-                    const height = NumCast(LinkFollowBox.sourceDoc._height);
-
-                    alias.x = x + width + 30;
-                    alias.y = y;
-                    alias._width = width;
-                    alias._height = height;
-
-                    this.sourceView.props.addDocument(alias);
-                }
-            }
-
-            this.jumpToLink({ shouldZoom: options.shouldZoom });
-
-            this.highlightDoc();
-            SelectionManager.DeselectAll();
-        }
-    }
-
-    //set this to be the default link behavior, can be any of the above
-    public defaultLinkBehavior: (options?: any) => void = this.jumpToLink;
-
-    @action
-    currentLinkBehavior = () => {
-        // this.resetPan();
-        if (LinkFollowBox.destinationDoc) {
-            if (this.selectedContextString === "") {
-                this.selectedContextString = "self";
-                this.selectedContext = LinkFollowBox.destinationDoc;
-            }
-            if (this.selectedOption === "") this.selectedOption = FollowOptions.NOZOOM;
-            const shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true;
-            const notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id];
-
-            if (this.selectedMode === FollowModes.INPLACE) {
-                if (shouldZoom !== undefined) this.openLinkInPlace({ shouldZoom: shouldZoom });
-            }
-            else if (this.selectedMode === FollowModes.OPENFULL) {
-                if (notOpenInContext) this.openFullScreen();
-                else this.selectedContext && this.openColFullScreen({ context: this.selectedContext });
-            }
-            else if (this.selectedMode === FollowModes.OPENRIGHT) {
-                if (notOpenInContext) this.openLinkRight();
-                else this.selectedContext && this.openLinkColRight({ context: this.selectedContext, shouldZoom: shouldZoom });
-            }
-            else if (this.selectedMode === FollowModes.OPENTAB) {
-                if (notOpenInContext) this.openLinkTab();
-                else this.selectedContext && this.openLinkColTab({ context: this.selectedContext, shouldZoom: shouldZoom });
-            }
-            else if (this.selectedMode === FollowModes.PAN) {
-                this.jumpToLink({ shouldZoom: shouldZoom });
-            }
-            else return;
-        }
-    }
-
-    @action
-    handleModeChange = (e: React.ChangeEvent) => {
-        const target = e.target as HTMLInputElement;
-        this.selectedMode = target.value;
-        this.selectedContext = undefined;
-        this.selectedContextString = "";
-
-        this.shouldUseOnlyParentContext = (this.selectedMode === FollowModes.INPLACE || this.selectedMode === FollowModes.PAN);
-
-        if (this.shouldUseOnlyParentContext) {
-            if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
-                this.selectedContext = this.sourceView.props.ContainingCollectionDoc;
-                this.selectedContextString = (StrCast(this.sourceView.props.ContainingCollectionDoc.title));
-            }
-        }
-    }
-
-    @action
-    handleOptionChange = (e: React.ChangeEvent) => {
-        const target = e.target as HTMLInputElement;
-        this.selectedOption = target.value;
-    }
-
-    @action
-    handleContextChange = (e: React.ChangeEvent) => {
-        const target = e.target as HTMLInputElement;
-        this.selectedContextString = target.value;
-        // selectedContext is updated in reaction
-        this.selectedOption = "";
-    }
-
-    @computed
-    get canOpenInPlace() {
-        if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
-            const colDoc = this.sourceView.props.ContainingCollectionDoc;
-            if (colDoc._viewType === CollectionViewType.Freeform) return true;
-        }
-        return false;
-    }
-
-    @computed
-    get availableModes() {
-        return (
-            <div>
-                <label><input
-                    type="radio"
-                    name="mode"
-                    value={FollowModes.OPENRIGHT}
-                    checked={this.selectedMode === FollowModes.OPENRIGHT}
-                    onChange={this.handleModeChange}
-                    disabled={false} />
-                    {FollowModes.OPENRIGHT}
-                </label><br />
-                <label><input
-                    type="radio"
-                    name="mode"
-                    value={FollowModes.OPENTAB}
-                    checked={this.selectedMode === FollowModes.OPENTAB}
-                    onChange={this.handleModeChange}
-                    disabled={false} />
-                    {FollowModes.OPENTAB}
-                </label><br />
-                <label><input
-                    type="radio"
-                    name="mode"
-                    value={FollowModes.OPENFULL}
-                    checked={this.selectedMode === FollowModes.OPENFULL}
-                    onChange={this.handleModeChange}
-                    disabled={false} />
-                    {FollowModes.OPENFULL}
-                </label><br />
-                <label><input
-                    type="radio"
-                    name="mode"
-                    value={FollowModes.PAN}
-                    checked={this.selectedMode === FollowModes.PAN}
-                    onChange={this.handleModeChange}
-                    disabled={!this.canPan} />
-                    {FollowModes.PAN}
-                </label><br />
-                <label><input
-                    type="radio"
-                    name="mode"
-                    value={FollowModes.INPLACE}
-                    checked={this.selectedMode === FollowModes.INPLACE}
-                    onChange={this.handleModeChange}
-                    disabled={!this.canOpenInPlace} />
-                    {FollowModes.INPLACE}
-                </label><br />
-            </div>
-        );
-    }
-
-    @computed
-    get parentName() {
-        if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
-            return this.sourceView.props.ContainingCollectionDoc.title;
-        }
-    }
-
-    @computed
-    get parentID(): string {
-        if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
-            return StrCast(this.sourceView.props.ContainingCollectionDoc[Id]);
-        }
-        return "col";
-    }
-
-    @computed
-    get availableContexts() {
-        return (
-            this.shouldUseOnlyParentContext ?
-                <label><input
-                    type="radio" disabled={true}
-                    name="context"
-                    value={this.parentID}
-                    checked={true} />
-                    {this.parentName} (Parent Collection)
-                </label>
-                :
-                <div>
-                    <label><input
-                        type="radio" disabled={LinkFollowBox.linkDoc ? false : true}
-                        name="context"
-                        value={LinkFollowBox.destinationDoc ? StrCast(LinkFollowBox.destinationDoc[Id]) : "self"}
-                        checked={LinkFollowBox.destinationDoc ? this.selectedContextString === StrCast(LinkFollowBox.destinationDoc[Id]) || this.selectedContextString === "self" : true}
-                        onChange={this.handleContextChange} />
-                        Open Self
-                </label><br />
-                    {[...this._docs, ...this._otherDocs].map(doc => {
-                        if (doc && doc.target && doc.col.title !== "Recently Closed") {
-                            return <div key={doc.col[Id] + doc.target[Id]}><label key={doc.col[Id] + doc.target[Id]}>
-                                <input
-                                    type="radio" disabled={LinkFollowBox.linkDoc ? false : true}
-                                    name="context"
-                                    value={StrCast(doc.col[Id])}
-                                    checked={this.selectedContextString === StrCast(doc.col[Id])}
-                                    onChange={this.handleContextChange} />
-                                {doc.col.title}
-                            </label><br /></div>;
-                        }
-                    })}
-                </div>
-        );
-    }
-
-    @computed
-    get shouldShowZoom(): boolean {
-        if (this.selectedMode === FollowModes.OPENFULL) return false;
-        if (this.shouldUseOnlyParentContext) return true;
-        if (LinkFollowBox.destinationDoc ? this.selectedContextString === LinkFollowBox.destinationDoc[Id] : "self") return false;
-
-        let contextMatch: boolean = false;
-        if (this.selectedContextAliases) {
-            this.selectedContextAliases.forEach(alias => {
-                if (alias._viewType === CollectionViewType.Freeform) contextMatch = true;
-            });
-        }
-        if (contextMatch) return true;
-
-        return false;
-    }
-
-    @computed
-    get availableOptions() {
-        if (LinkFollowBox.destinationDoc) {
-            return (
-                this.shouldShowZoom ?
-                    <div>
-                        <label><input
-                            type="radio"
-                            name="option"
-                            value={FollowOptions.ZOOM}
-                            checked={this.selectedOption === FollowOptions.ZOOM}
-                            onChange={this.handleOptionChange}
-                            disabled={false} />
-                            {FollowOptions.ZOOM}
-                        </label><br />
-                        <label><input
-                            type="radio"
-                            name="option"
-                            value={FollowOptions.NOZOOM}
-                            checked={this.selectedOption === FollowOptions.NOZOOM}
-                            onChange={this.handleOptionChange}
-                            disabled={false} />
-                            {FollowOptions.NOZOOM}
-                        </label><br />
-                    </div>
-                    :
-                    <div>No Available Options</div>
-            );
-        }
-        return null;
-    }
-
-    render() {
-        return (
-            <div className="linkFollowBox-main" style={{ height: NumCast(this.props.Document._height), width: NumCast(this.props.Document._width) }}>
-                <div className="linkFollowBox-header">
-                    <div className="topHeader">
-                        {LinkFollowBox.linkDoc ? "Link Title: " + StrCast(LinkFollowBox.linkDoc.title) : "No Link Selected"}
-                        <div onClick={() => this.props.Document.isMinimized = true} className="closeDocument"><FontAwesomeIcon icon={faTimes} size="lg" /></div>
-                    </div>
-                    <div className=" direction-indicator">{LinkFollowBox.linkDoc ?
-                        LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc ? "Source: " + StrCast(LinkFollowBox.sourceDoc.title) + ", Destination: " + StrCast(LinkFollowBox.destinationDoc.title)
-                            : "" : ""}</div>
-                </div>
-                <div className="linkFollowBox-content" style={{ height: NumCast(this.props.Document._height) - 110 }}>
-                    <div className="linkFollowBox-item">
-                        <div className="linkFollowBox-item title">Mode</div>
-                        <div className="linkFollowBox-itemContent">
-                            {LinkFollowBox.linkDoc ? this.availableModes : "Please select a link to view modes"}
-                        </div>
-                    </div>
-                    <div className="linkFollowBox-item">
-                        <div className="linkFollowBox-item title">Context</div>
-                        <div className="linkFollowBox-itemContent">
-                            {this.selectedMode !== "" ? this.availableContexts : "Please select a mode to view contexts"}
-                        </div>
-                    </div>
-                    <div className="linkFollowBox-item">
-                        <div className="linkFollowBox-item title">Options</div>
-                        <div className="linkFollowBox-itemContent">
-                            {this.selectedContextString !== "" ? this.availableOptions : "Please select a context to view options"}
-                        </div>
-                    </div>
-                </div>
-                <div className="linkFollowBox-footer">
-                    <button
-                        onClick={this.resetVars}>
-                        Clear Link
-                    </button>
-                    <div style={{ width: 20 }}></div>
-                    <button
-                        onClick={this.currentLinkBehavior}
-                        disabled={(LinkFollowBox.linkDoc) ? false : true}>
-                        Follow Link
-                    </button>
-                </div>
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 376afa64b..5fd6e4630 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -8,7 +8,6 @@ import { Cast, StrCast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
 import { ContextMenu } from '../ContextMenu';
-import { LinkFollowBox } from './LinkFollowBox';
 import './LinkMenuItem.scss';
 import React = require("react");
 import { DocumentManager } from '../../util/DocumentManager';
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 73a53f5cc..bfda13eb3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -9,7 +9,6 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionSchemaView } from "../collections/CollectionSchemaView";
 import { CollectionView } from "../collections/CollectionView";
-import { LinkFollowBox } from "../linking/LinkFollowBox";
 import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
 import { AudioBox } from "./AudioBox";
 import { ButtonBox } from "./ButtonBox";
@@ -108,7 +107,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                 components={{
                     FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, ButtonBox, SliderBox, FieldView,
                     CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                    PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox,
+                    PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
                     ColorBox, DashWebRTCVideo, DocuLinkBox, InkingStroke, DocumentBox, LinkBox
                 }}
                 bindings={this.CreateBindings()}
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 32439757c..8fe2257fc 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -265,8 +265,6 @@ export class CurrentUserUtils {
     // sets up the default set of documents to be shown in the Overlay layer
     static setupOverlays(doc: Doc) {
         doc.overlays = Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" });
-        doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, _width: 500, _height: 370, title: "Link Follower" });
-        Doc.AddDocToList(doc.overlays as Doc, "data", doc.linkFollowBox as Doc);
     }
 
     // the initial presentation Doc to use
-- 
cgit v1.2.3-70-g09d2


From 8190b6557065828e3a5e7a6505c0963a433e27d1 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 27 Feb 2020 17:05:39 -0500
Subject: adding colors to schema view will update the enumerated colors used
 by text boxes in freeformview.

---
 .../views/collections/CollectionStackingView.tsx     |  5 ++++-
 .../CollectionStackingViewFieldColumn.tsx            |  4 ++--
 src/client/views/nodes/PresBox.tsx                   |  2 --
 src/client/views/pdf/Annotation.tsx                  |  2 +-
 src/new_fields/Doc.ts                                | 20 ++++++++++++++++----
 5 files changed, 23 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a1cc21319..4495e8248 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -24,6 +24,7 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
+import { Docs } from "../../documents/Documents";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -348,7 +349,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @action
     addGroup = (value: string) => {
         if (value && this.sectionHeaders) {
-            this.sectionHeaders.push(new SchemaHeaderField(value));
+            const schemaHdrField = new SchemaHeaderField(value);
+            this.sectionHeaders.push(schemaHdrField);
+            Doc.addEnumerationToTextField(undefined, this.sectionFilter, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]);
             return true;
         }
         return false;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 8c23ecd49..058733cc9 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -68,10 +68,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             const key = StrCast(this.props.parent.props.Document.sectionFilter);
             const castedValue = this.getValue(this._heading);
             if (castedValue) {
-                de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
+                de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, false));
             }
             else {
-                de.complete.docDragData.droppedDocuments.forEach(d => d[key] = undefined);
+                de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, undefined, false));
             }
             this.props.parent.onInternalDrop(e, de);
             e.stopPropagation();
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 791ed5ef1..4180ee255 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -10,7 +10,6 @@ import { listSpec } from "../../../new_fields/Schema";
 import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { returnFalse } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -20,7 +19,6 @@ import { ContextMenuProps } from "../ContextMenuItem";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
-import { PrefetchProxy } from "../../../new_fields/Proxy";
 
 library.add(faArrowLeft);
 library.add(faArrowRight);
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 58823c93d..d23c81065 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym, Opt, DocListCastAsync } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 8ea347ec3..d364bfe28 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -858,16 +858,28 @@ export namespace Doc {
         Doc.addEnumerationToTextField(doc, enumeratedFieldKey, enumeratedDocs);
     }
 
-    export function addEnumerationToTextField(doc: Doc, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
+    export async function getEnumerationTextField(enumeratedFieldKey: string) {
+        return (await DocServer.GetRefField(enumeratedFieldKey)) as Doc;
+    }
+
+    export function addEnumerationToTextField(doc: Opt<Doc>, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
         DocServer.GetRefField(enumeratedFieldKey).then(optionsCollection => {
             if (!(optionsCollection instanceof Doc)) {
                 optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
                 Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
             }
             const options = optionsCollection as Doc;
-            doc.backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options });
-            doc.color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options });
-            enumeratedDocs.map(enumeratedDoc => !DocListCast(options.data).find(d => d.title === enumeratedDoc.title) && Doc.AddDocToList(options, "data", enumeratedDoc));
+            doc && (doc.backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
+            doc && (doc.color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
+            enumeratedDocs.map(enumeratedDoc => {
+                const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
+                if (found) {
+                    found._backgroundColor = enumeratedDoc._backgroundColor || found._backgroundColor;
+                    found._color = enumeratedDoc._color || found._color;
+                } else {
+                    Doc.AddDocToList(options, "data", enumeratedDoc);
+                }
+            });
         });
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 44a521a2b333913ee83bd325b84d1dbd03339398 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 27 Feb 2020 22:05:12 -0500
Subject: schema view & Stacking view code cleanup and minor ui fixes

---
 .../views/collections/CollectionSchemaView.tsx     | 43 ++---------
 .../views/collections/CollectionStackingView.scss  | 18 ++++-
 .../views/collections/CollectionStackingView.tsx   | 30 +++-----
 .../CollectionStackingViewFieldColumn.tsx          | 89 ++++++----------------
 src/new_fields/Doc.ts                              |  4 +-
 5 files changed, 58 insertions(+), 126 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9486d195a..6eeceb552 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -28,6 +28,7 @@ import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionView } from "./CollectionView";
 import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
 
 library.add(faCog, faPlus, faSortUp, faSortDown);
 library.add(faTable);
@@ -43,8 +44,8 @@ export enum ColumnType {
 // this map should be used for keys that should have a const type of value
 const columnTypes: Map<string, ColumnType> = new Map([
     ["title", ColumnType.String],
-    ["x", ColumnType.Number], ["y", ColumnType.Number], ["width", ColumnType.Number], ["height", ColumnType.Number],
-    ["nativeWidth", ColumnType.Number], ["nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
+    ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
+    ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
     ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
 ]);
 
@@ -54,9 +55,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     private _startPreviewWidth = 0;
     private DIVIDER_WIDTH = 4;
 
-    @observable previewScript: string = "";
     @observable previewDoc: Doc | undefined = undefined;
-    @observable private _node: HTMLDivElement | null = null;
     @observable private _focusedTable: Doc = this.props.Document;
 
     @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
@@ -75,9 +74,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
 
     @action setPreviewDoc = (doc: Doc) => this.previewDoc = doc;
 
-    @undoBatch
-    @action setPreviewScript = (script: string) => this.previewScript = script
-
     //toggles preview side-panel of schema
     @action
     toggleExpander = () => {
@@ -86,27 +82,17 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
 
     onDividerDown = (e: React.PointerEvent) => {
         this._startPreviewWidth = this.previewWidth();
-        e.stopPropagation();
-        e.preventDefault();
-        document.addEventListener("pointermove", this.onDividerMove);
-        document.addEventListener('pointerup', this.onDividerUp);
+        setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, action(() => this.toggleExpander()));
     }
     @action
-    onDividerMove = (e: PointerEvent): void => {
+    onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const nativeWidth = this._mainCont!.getBoundingClientRect();
         const minWidth = 40;
         const maxWidth = 1000;
         const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
         this.props.Document.schemaPreviewWidth = width;
-    }
-    @action
-    onDividerUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onDividerMove);
-        document.removeEventListener('pointerup', this.onDividerUp);
-        if (this._startPreviewWidth === this.previewWidth()) {
-            this.toggleExpander();
-        }
+        return false;
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
@@ -119,9 +105,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     @computed
-    get previewDocument(): Doc | undefined {
-        return this.previewDoc ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(this.previewDoc[this.previewScript], Doc)) : this.previewDoc) : undefined;
-    }
+    get previewDocument(): Doc | undefined { return this.previewDoc; }
 
     getPreviewTransform = (): Transform => {
         return this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth);
@@ -477,8 +461,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @undoBatch
     createRow = () => {
-        const newDoc = Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 });
-        this.props.addDocument(newDoc);
+        this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
     }
 
     @undoBatch
@@ -559,16 +542,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             columns[index] = columnField;
             this.columns = columns;
         }
-
-        // const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc));
-        // if (!typesDoc) {
-        //     let newTypesDoc = new Doc();
-        //     newTypesDoc[key] = type;
-        //     this.props.Document.schemaColumnTypes = newTypesDoc;
-        //     return;
-        // } else {
-        //     typesDoc[key] = type;
-        // }
     }
 
     @undoBatch
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 293dc5414..8e3919532 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -160,9 +160,7 @@
     }
     .collectionStackingView-sectionHeader {
         text-align: center;
-        margin-left: 2px;
-        margin-right: 2px;
-        margin-top: 10px;
+        margin: auto;
         background: $main-accent;
         // overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
 
@@ -214,6 +212,7 @@
             left: 0;
             top: 0;
             height: 100%;
+            display: none;
 
             [class*="css"] {
                 max-width: 102px;
@@ -251,6 +250,7 @@
             right: 0;
             top: 0;
             height: 100%;
+            display: none;
 
             [class*="css"] {
                 max-width: 102px;
@@ -285,6 +285,18 @@
             right: 25px;
             top: 0;
             height: 100%;
+            display: none;
+        }
+    }
+    .collectionStackingView-sectionHeader:hover {
+        .collectionStackingView-sectionColor {
+            display:unset;
+        }
+        .collectionStackingView-sectionOptions {
+            display:unset;
+        }
+        .collectionStackingView-sectionDelete {
+            display:unset;
         }
     }
 
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4495e8248..d1f45af90 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { Utils } from "../../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -61,7 +61,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             const dxf = () => this.getDocTransform(d, dref.current!);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
             const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
-            const style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+            const style = this.isStackingView ? { width: width(), marginTop: this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
             return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
                 {this.getDisplayDoc(d, this.props.DataDoc, dxf, width)}
             </div>;
@@ -79,8 +79,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0);
             return new Map<SchemaHeaderField, Doc[]>();
         }
-        const sectionHeaders = this.sectionHeaders;
+        const sectionHeaders: SchemaHeaderField[] = Array.from(this.sectionHeaders);
         const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+        let changed = false;
         this.filteredChildren.map(d => {
             const sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
             // the next five lines ensures that floating point rounding errors don't create more than one section -syip
@@ -96,8 +97,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                 const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
                 sectionHeaders.push(newSchemaHeader);
+                changed = true;
             }
         });
+        changed && setTimeout(action(() => { if (this.sectionHeaders) { this.sectionHeaders.length = 0; this.sectionHeaders.push(...sectionHeaders); } }), 0);
         return fields;
     }
 
@@ -204,26 +207,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
         runInAction(() => this._cursor = "grabbing");
-        document.addEventListener("pointermove", this.onDividerMove);
-        document.addEventListener('pointerup', this.onDividerUp);
-        this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+        setupMoveUpEvents(this, e, this.onDividerMove, action(() => this._cursor = "grab"), emptyFunction);
     }
     @action
-    onDividerMove = (e: PointerEvent): void => {
-        const dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
-        const delta = dragPos - this._columnStart;
-        this._columnStart = dragPos;
-        this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta);
-    }
-
-    @action
-    onDividerUp = (e: PointerEvent): void => {
-        runInAction(() => this._cursor = "grab");
-        document.removeEventListener("pointermove", this.onDividerMove);
-        document.removeEventListener('pointerup', this.onDividerUp);
+    onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+        this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta[0]);
+        return false;
     }
 
     @computed get columnDragger() {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 058733cc9..17011698f 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -20,7 +20,10 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
+import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import "./CollectionStackingView.scss";
+import { listSpec } from "../../../new_fields/Schema";
+import { Schema } from "prosemirror-model";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -42,20 +45,15 @@ interface CSVFieldColumnProps {
 @observer
 export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
     @observable private _background = "inherit";
-    @observable private _createAliasSelected: boolean = false;
 
-    private _dropRef: HTMLDivElement | null = null;
     private dropDisposer?: DragManager.DragDropDisposer;
     private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
-    private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
-    private _sensitivity: number = 16;
 
     @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
     @observable _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
 
     createColumnDropRef = (ele: HTMLDivElement | null) => {
-        this._dropRef = ele;
-        this.dropDisposer && this.dropDisposer();
+        this.dropDisposer?.();
         if (ele) {
             this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
         }
@@ -63,16 +61,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @undoBatch
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
-        this._createAliasSelected = false;
         if (de.complete.docDragData) {
             const key = StrCast(this.props.parent.props.Document.sectionFilter);
             const castedValue = this.getValue(this._heading);
-            if (castedValue) {
-                de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, false));
-            }
-            else {
-                de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, undefined, false));
-            }
+            de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, false));
             this.props.parent.onInternalDrop(e, de);
             e.stopPropagation();
         }
@@ -93,7 +85,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
-        this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document.sectionFilter);
         const castedValue = this.getValue(value);
         if (castedValue) {
@@ -114,7 +105,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     changeColumnColor = (color: string) => {
-        this._createAliasSelected = false;
         if (this.props.headingObject) {
             this.props.headingObject.setColor(color);
             this._color = color;
@@ -124,22 +114,18 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @action
     pointerEntered = () => {
         if (SelectionManager.GetIsDragging()) {
-            this._createAliasSelected = false;
             this._background = "#b4b4b4";
         }
     }
 
     @action
     pointerLeave = () => {
-        this._createAliasSelected = false;
         this._background = "inherit";
-        document.removeEventListener("pointermove", this.startDrag);
     }
 
     @action
     addDocument = (value: string, shiftDown?: boolean) => {
         if (!value) return false;
-        this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document.sectionFilter);
         const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true });
         newDoc[key] = this.getValue(this.props.heading);
@@ -151,7 +137,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     deleteColumn = () => {
-        this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document.sectionFilter);
         this.props.docList.forEach(d => d[key] = undefined);
         if (this.props.parent.sectionHeaders && this.props.headingObject) {
@@ -162,7 +147,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     collapseSection = () => {
-        this._createAliasSelected = false;
         if (this.props.headingObject) {
             this._headingsHack++;
             this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
@@ -170,46 +154,23 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         }
     }
 
-    startDrag = (e: PointerEvent) => {
-        const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
-        if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
-            const alias = Doc.MakeAlias(this.props.parent.props.Document);
-            const key = StrCast(this.props.parent.props.Document.sectionFilter);
-            let value = this.getValue(this._heading);
-            value = typeof value === "string" ? `"${value}"` : value;
-            alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name });
-            if (alias.viewSpecScript) {
-                DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
-            }
-
-            e.stopPropagation();
-            document.removeEventListener("pointermove", this.startDrag);
-            document.removeEventListener("pointerup", this.pointerUp);
-        }
-    }
-
-    pointerUp = (e: PointerEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
-
-        document.removeEventListener("pointermove", this.startDrag);
-        document.removeEventListener("pointerup", this.pointerUp);
-    }
-
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
-        e.stopPropagation();
-        e.preventDefault();
-
-        const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
-        this._startDragPosition = { x: dx, y: dy };
+        setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
+    }
 
-        if (this._createAliasSelected) {
-            document.removeEventListener("pointermove", this.startDrag);
-            document.addEventListener("pointermove", this.startDrag);
-            document.removeEventListener("pointerup", this.pointerUp);
-            document.addEventListener("pointerup", this.pointerUp);
+    startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
+        const alias = Doc.MakeAlias(this.props.parent.props.Document);
+        alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.props.Document.schemaColumns, listSpec(SchemaHeaderField))?.length || 1);
+        alias.sectionFilter = undefined;
+        const key = StrCast(this.props.parent.props.Document.sectionFilter);
+        let value = this.getValue(this._heading);
+        value = typeof value === "string" ? `"${value}"` : value;
+        alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name });
+        if (alias.viewSpecScript) {
+            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+            return true;
         }
-        runInAction(() => this._createAliasSelected = false);
+        return false;
     }
 
     renderColorPicker = () => {
@@ -242,17 +203,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         );
     }
 
-    @action
-    toggleAlias = () => {
-        this._createAliasSelected = true;
-    }
-
     renderMenu = () => {
-        const selected = this._createAliasSelected;
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
+                    <div className={"optionPicker" + (true ? " active" : "")} onClick={action(() => { })}>Add options here</div>
                 </div>
             </div >
         );
@@ -328,6 +283,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         const heading = this._heading;
         const style = this.props.parent;
         const singleColumn = style.isStackingView;
+        const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin);
         const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
         const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
         const headerEditableViewProps = {
@@ -352,6 +308,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         const headingView = this.props.headingObject ?
             <div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
                 style={{
+                    marginTop: NumCast(this.props.parent.props.Document._yMargin),
                     width: (style.columnWidth) /
                         ((uniqueHeadings.length +
                             ((this.props.parent.props.Document._chromeStatus !== 'view-mode' && this.props.parent.props.Document._chromeStatus !== 'disabled') ? 1 : 0)) || 1)
@@ -404,7 +361,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         <div>
                             <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
                                 style={{
-                                    padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`,
+                                    padding: singleColumn ? `${columnYMargin}px ${0}px ${style.yMargin}px ${0}px` : `${columnYMargin}px ${0}px`,
                                     margin: "auto",
                                     width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
                                     height: 'max-content',
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d364bfe28..ba8e21100 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -869,8 +869,8 @@ export namespace Doc {
                 Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
             }
             const options = optionsCollection as Doc;
-            doc && (doc.backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
-            doc && (doc.color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
+            doc && (Doc.GetProto(doc).backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
+            doc && (Doc.GetProto(doc).color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
             enumeratedDocs.map(enumeratedDoc => {
                 const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
                 if (found) {
-- 
cgit v1.2.3-70-g09d2

