From fb76a0d66fc074a458706adf6fbb02492205b6e4 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Tue, 20 Aug 2019 12:54:44 -0400
Subject: factoring out masonry code into new file

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

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

diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 74c7ef305..a3db46584 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -318,6 +318,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         gridTemplateColumns: singleColumn ? undefined : templatecols,
                         gridAutoRows: singleColumn ? undefined : "0px"
                     }}
+                // ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}
                 >
                     {this.children(this.props.docList)}
                     {singleColumn ? (null) : this.props.parent.columnDragger}
-- 
cgit v1.2.3-70-g09d2


From b895fcf2f63c4bddf413f9572964dfdfa083fd37 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Fri, 23 Aug 2019 12:59:17 -0400
Subject: masonry + stacking color, delete, collapse, drag

---
 src/client/views/EditableView.tsx                  | 30 +++++-----
 .../collections/CollectionMasonryViewFieldRow.tsx  | 32 +++++------
 .../views/collections/CollectionStackingView.tsx   | 43 --------------
 .../CollectionStackingViewFieldColumn.tsx          | 66 ++++++++++++++--------
 src/new_fields/SchemaHeaderField.ts                |  3 +-
 5 files changed, 75 insertions(+), 99 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 87af19062..757ddbad1 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -41,8 +41,10 @@ export interface EditableProps {
     editing?: boolean;
     onClick?: (e: React.MouseEvent) => boolean;
     isEditingCallback?: (isEditing: boolean) => void;
-    // HeadingObject: SchemaHeaderField | undefined;
-    // HeadingsHack: number;
+    HeadingObject?: SchemaHeaderField | undefined;
+    HeadingsHack?: number;
+    toggle?: () => void;
+    color?: string | undefined;
 }
 
 /**
@@ -53,7 +55,6 @@ export interface EditableProps {
 @observer
 export class EditableView extends React.Component<EditableProps> {
     @observable _editing: boolean = false;
-    @observable _collapsed: boolean = false;
     @observable _headingsHack: number = 1;
 
     constructor(props: EditableProps) {
@@ -71,14 +72,13 @@ export class EditableView extends React.Component<EditableProps> {
         }
     }
 
-    // collapseSection() {
-    //     if (this.props.HeadingObject) {
-    //         this._headingsHack++;
-    //         this.props.HeadingObject.setCollapsed(!this.props.HeadingObject.collapsed);
-    //         this._collapsed = !this._collapsed;
-    //         console.log("THIS IS COLLAPSE FROM EDITABLEVIEW" + this._collapsed);
-    //     }
-    // }
+    collapseSection() {
+        if (this.props.HeadingObject) {
+            this._headingsHack++;
+            this.props.HeadingObject.setCollapsed(!this.props.HeadingObject.collapsed);
+            this.props.toggle && this.props.toggle();
+        }
+    }
 
     @action
     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
@@ -107,13 +107,15 @@ export class EditableView extends React.Component<EditableProps> {
     @action
     onClick = (e: React.MouseEvent) => {
         e.nativeEvent.stopPropagation();
-        // if (e.ctrlKey) {
-        //     this.collapseSection();
-        // }
         if (!this.props.onClick || !this.props.onClick(e)) {
             this._editing = true;
             this.props.isEditingCallback && this.props.isEditingCallback(true);
         }
+        if (e.ctrlKey) {
+            this._editing = false;
+            this.props.isEditingCallback && this.props.isEditingCallback(false);
+            this.collapseSection();
+        }
         e.stopPropagation();
     }
 
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 8943fea3b..ca386f91b 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -248,7 +248,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @observable private collapsed: boolean = false;
 
+    private toggleVisibility = action(() => {
+        this.collapsed = !this.collapsed;
+    });
+
     @observable _headingsHack: number = 1;
+
     render() {
         let cols = this.props.rows();
         let 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))));
@@ -264,30 +269,23 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             SetValue: this.headingChanged,
             contents: evContents,
             oneLine: true,
-            // HeadingObject: this.props.headingObject,
-            // HeadingsHack: this._headingsHack
+            HeadingObject: this.props.headingObject,
+            HeadingsHack: this._headingsHack,
+            toggle: this.toggleVisibility,
+            color: this.props.color
         };
         let newEditableViewProps = {
             GetValue: () => "",
             SetValue: this.addDocument,
             contents: "+ NEW",
-            // HeadingObject: this.props.headingObject,
-            // HeadingsHack: this._headingsHack
+            HeadingObject: this.props.headingObject,
+            HeadingsHack: this._headingsHack,
+            toggle: this.toggleVisibility,
+            color: this.props.color
         };
-        // let headingView = this.props.headingObject ?
         let headingView =
             <div>
-                <div key={`${this.props.heading}`} className="collectionStackingView-sectionHeader" style={{ background: this.props.color }}
-                    onClick={action(() => {
-                        if (this.props.headingObject) {
-                            this._headingsHack++;
-                            this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
-                            this.collapsed = !this.collapsed;
-                            console.log("value of collapse: " + this.collapsed);
-                        }
-                    })}
-                >
-                    {this.props.heading}
+                <div className="collectionStackingView-sectionHeader">
                     <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
                         title={evContents === `NO ${key.toUpperCase()} VALUE` ?
                             `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
@@ -324,7 +322,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             >
                 {headingView}
                 {this.collapsed ? (null) :
-                    <div>
+                    < div >
                         <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
                             style={{
                                 padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 289c3a1fa..f0a8741af 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -286,49 +286,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             translate(offset[0], offset[1]).
             scale(NumCast(doc.width, 1) / this.columnWidth);
     }
-    // masonryChildren(docs: Doc[]) {
-    //     this._docXfs.length = 0;
-    //     return docs.map((d, i) => {
-    //         let dref = React.createRef<HTMLDivElement>();
-    //         let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
-    //         let width = () => (d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth);/// (uniqueHeadings.length + 1);
-    //         let height = () => this.getDocHeight(layoutDoc);
-    //         let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
-    //         let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
-    //         this._docXfs.push({ dxf: dxf, width: width, height: height });
-    //         return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
-    //             {this.getDisplayDoc(layoutDoc, d, dxf, width)}
-    //         </div>;
-    //     });
-    // }
-
-    // @observable _headingsHack: number = 1;
-    // sectionMasonry(heading: SchemaHeaderField | undefined, docList: Doc[]) {
-    //     let cols = Math.max(1, Math.min(docList.length,
-    //         Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
-    //     // let specialCols = () => this.isMasonryView ? 1 : Math.max(1, Math.min(docList.length,
-    //     //     Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
-    //     // let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
-    //     return <div ref={} key={heading ? heading.heading : "empty"} className="collectionStackingView-masonrySection">
-    //         {!heading ? (null) :
-    //             <div key={`${heading.heading}`} className="collectionStackingView-sectionHeader" style={{ background: heading.color }}
-    //                 onClick={action(() => this._headingsHack++ && heading.setCollapsed(!heading.collapsed))} >
-    //                 {heading.heading}
-    //             </div>}
-    //         {this._headingsHack && heading && heading.collapsed ? (null) :
-    //             <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
-    //                 style={{
-    //                     padding: `${this.yMargin}px ${this.xMargin}px`,
-    //                     width: `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`,
-    //                     gridGap: this.gridGap,
-    //                     gridTemplateColumns: numberRange(cols).reduce((list, i) => list + ` ${this.columnWidth}px`, ""),
-    //                 }}>
-    //                 {this.masonryChildren(docList)}
-    //                 {this.columnDragger}
-    //             </div>
-    //         }
-    //     </div>;
-    // }
 
     sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
         let key = this.sectionFilter;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index a3db46584..01bfd813b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -247,6 +247,14 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         );
     }
 
+    @observable private collapsed: boolean = false;
+
+    private toggleVisibility = action(() => {
+        this.collapsed = !this.collapsed;
+    });
+
+    @observable _headingsHack: number = 1;
+
     render() {
         let cols = this.props.cols();
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
@@ -261,12 +269,20 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             GetValue: () => evContents,
             SetValue: this.headingChanged,
             contents: evContents,
-            oneLine: true
+            oneLine: true,
+            HeadingObject: this.props.headingObject,
+            HeadingsHack: this._headingsHack,
+            toggle: this.toggleVisibility,
+            color: this._color
         };
         let newEditableViewProps = {
             GetValue: () => "",
             SetValue: this.addDocument,
-            contents: "+ NEW"
+            contents: "+ NEW",
+            HeadingObject: this.props.headingObject,
+            HeadingsHack: this._headingsHack,
+            toggle: this.toggleVisibility,
+            color: this._color
         };
         let headingView = this.props.headingObject ?
             <div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
@@ -307,27 +323,31 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
                 {headingView}
-                <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`,
-                        margin: "auto",
-                        width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
-                        height: 'max-content',
-                        position: "relative",
-                        gridGap: style.gridGap,
-                        gridTemplateColumns: singleColumn ? undefined : templatecols,
-                        gridAutoRows: singleColumn ? undefined : "0px"
-                    }}
-                // ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}
-                >
-                    {this.children(this.props.docList)}
-                    {singleColumn ? (null) : this.props.parent.columnDragger}
-                </div>
-                {(this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ?
-                    <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
-                        style={{ width: style.columnWidth / style.numGroupColumns }}>
-                        <EditableView {...newEditableViewProps} />
-                    </div> : null}
+                {this.collapsed ? (null) :
+                    <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`,
+                                margin: "auto",
+                                width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
+                                height: 'max-content',
+                                position: "relative",
+                                gridGap: style.gridGap,
+                                gridTemplateColumns: singleColumn ? undefined : templatecols,
+                                gridAutoRows: singleColumn ? undefined : "0px"
+                            }}
+                        // ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}
+                        >
+                            {this.children(this.props.docList)}
+                            {singleColumn ? (null) : this.props.parent.columnDragger}
+                        </div>
+                        {(this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ?
+                            <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
+                                style={{ width: style.columnWidth / style.numGroupColumns }}>
+                                <EditableView {...newEditableViewProps} />
+                            </div> : null}
+                    </div>
+                }
             </div>
         );
     }
diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts
index 7494c9bd1..92d0aec9a 100644
--- a/src/new_fields/SchemaHeaderField.ts
+++ b/src/new_fields/SchemaHeaderField.ts
@@ -105,5 +105,4 @@ export class SchemaHeaderField extends ObjectField {
     [ToScriptString]() {
         return `invalid`;
     }
-}
-
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 601c8d2b0b8197e324f9cbadf525fe03fac5b345 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Fri, 23 Aug 2019 13:10:59 -0400
Subject: fixed colorpicker flyout anchorPoint

---
 src/client/views/collections/CollectionMasonryViewFieldRow.tsx     | 2 +-
 src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index ca386f91b..04a94b9b9 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -297,7 +297,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                         {<EditableView {...headerEditableViewProps} />}
                         {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
                             <div className="collectionStackingView-sectionColor">
-                                <Flyout anchorPoint={anchorPoints.TOP_CENTER} content={this.renderColorPicker()}>
+                                <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
                                     <button className="collectionStackingView-sectionColorButton">
                                         <FontAwesomeIcon icon="palette" size="sm" />
                                     </button>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 01bfd813b..a99aa67a4 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -304,7 +304,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                     <EditableView {...headerEditableViewProps} />
                     {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
                         <div className="collectionStackingView-sectionColor">
-                            <Flyout anchorPoint={anchorPoints.TOP_CENTER} content={this.renderColorPicker()}>
+                            <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
                                 <button className="collectionStackingView-sectionColorButton">
                                     <FontAwesomeIcon icon="palette" size="sm" />
                                 </button>
-- 
cgit v1.2.3-70-g09d2


From 6770b483ad4ec1bfec8733ee86dd67e9146bf474 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Mon, 26 Aug 2019 14:18:57 -0400
Subject: create aliases using alt; still has orig problems

---
 .../collections/CollectionMasonryViewFieldRow.tsx     | 10 ++++++----
 .../views/collections/CollectionStackingView.tsx      |  2 +-
 .../collections/CollectionStackingViewFieldColumn.tsx | 19 +++++++++----------
 3 files changed, 16 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e93ef673e..bae2a1ff4 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -210,10 +210,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
 
-        document.removeEventListener("pointermove", this.startDrag);
-        document.addEventListener("pointermove", this.startDrag);
-        document.removeEventListener("pointerup", this.pointerUp);
-        document.addEventListener("pointerup", this.pointerUp);
+        if (e.altKey) { //release alt key before dropping alias; also, things must have existed outside of the collection first in order to be in the alias...
+            document.removeEventListener("pointermove", this.startDrag);
+            document.addEventListener("pointermove", this.startDrag);
+            document.removeEventListener("pointerup", this.pointerUp);
+            document.addEventListener("pointerup", this.pointerUp);
+        }
     }
 
     renderColorPicker = () => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 678e10dc5..9c7816c39 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -94,7 +94,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             async (args) => args[1] instanceof Doc &&
                 this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined)));
 
-        // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking views (masonry isn't yet supported).
+        // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking and masonry views -eeng.
         this._heightDisposer = reaction(() => {
             if (BoolCast(this.props.Document.autoHeight)) {
                 let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 5df58b0a0..4ec968438 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -78,19 +78,16 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         let parent = this.props.parent;
         parent._docXfs.length = 0;
         return docs.map((d, i) => {
-            const pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
-            if (!pair.layout || pair.data instanceof Promise) {
-                return (null);
-            }
+            let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
             let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, parent.columnWidth / parent.numGroupColumns);
             let height = () => parent.getDocHeight(pair.layout);
             let dref = React.createRef<HTMLDivElement>();
-            let dxf = () => this.getDocTransform(pair.layout!, dref.current!);
+            let dxf = () => this.getDocTransform(pair.layout as Doc, dref.current!);
             this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
             let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
             let style = parent.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
             return <div className={`collectionStackingView-${parent.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
-                {this.props.parent.getDisplayDoc(pair.layout, pair.data, dxf, width)}
+                {this.props.parent.getDisplayDoc(pair.layout as Doc, pair.data, dxf, width)}
             </div>;
         });
     }
@@ -214,10 +211,12 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
 
-        document.removeEventListener("pointermove", this.startDrag);
-        document.addEventListener("pointermove", this.startDrag);
-        document.removeEventListener("pointerup", this.pointerUp);
-        document.addEventListener("pointerup", this.pointerUp);
+        if (e.altKey) { //release alt key before dropping alias; also, things must have existed outside of the collection first in order to be in the alias...
+            document.removeEventListener("pointermove", this.startDrag);
+            document.addEventListener("pointermove", this.startDrag);
+            document.removeEventListener("pointerup", this.pointerUp);
+            document.addEventListener("pointerup", this.pointerUp);
+        }
     }
 
     renderColorPicker = () => {
-- 
cgit v1.2.3-70-g09d2


From 5a7ec36e558bdf5333cb7c16faad8f31bfcf314e Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Tue, 27 Aug 2019 11:53:00 -0400
Subject: fixed autoheight for masonry

---
 .../views/collections/CollectionMasonryViewFieldRow.tsx     | 13 ++++++-------
 src/client/views/collections/CollectionStackingView.tsx     |  3 +--
 .../views/collections/CollectionStackingViewFieldColumn.tsx |  1 -
 3 files changed, 7 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index bae2a1ff4..1c1e44bb5 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -33,7 +33,6 @@ interface CMVFieldRowProps {
     type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined;
     createDropTarget: (ele: HTMLDivElement) => void;
     screenToLocalTransform: () => Transform;
-    color: string | undefined;
 }
 
 @observer
@@ -274,7 +273,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             HeadingObject: this.props.headingObject,
             HeadingsHack: this._headingsHack,
             toggle: this.toggleVisibility,
-            color: this.props.color
+            color: this._color
         };
         let newEditableViewProps = {
             GetValue: () => "",
@@ -283,10 +282,10 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             HeadingObject: this.props.headingObject,
             HeadingsHack: this._headingsHack,
             toggle: this.toggleVisibility,
-            color: this.props.color
+            color: this._color
         };
-        let headingView =
-            <div className="collectionStackingView-sectionHeader">
+        let headingView = this.props.headingObject ?
+            <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
                 <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
                     title={evContents === `NO ${key.toUpperCase()} VALUE` ?
                         `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
@@ -311,11 +310,11 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                             <FontAwesomeIcon icon="trash" />
                         </button>}
                 </div>
-            </div >;
+            </div > : (null);
         return (
             <div className="collectionStackingView-masonrySection"
                 key={heading = "empty"}
-                style={{ width: this.props.parent.NodeWidth }}
+                style={{ width: this.props.parent.NodeWidth, background: this._background }}
                 ref={this.createRowDropRef}
                 onPointerEnter={this.pointerEnteredRow}
                 onPointerLeave={this.pointerLeaveRow}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9c7816c39..7ba177a7d 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -103,7 +103,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                         (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)), 0);
                 } else {
                     return this.props.ContentScaling() * sectionsList.reduce((totalHeight, s) => totalHeight +
-                        (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin) + 40, 0);
+                        (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin) + 47, 0);
                 }
             }
             return -1;
@@ -313,7 +313,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             type={type}
             createDropTarget={this.createDropTarget}
             screenToLocalTransform={this.props.ScreenToLocalTransform}
-            color={heading ? heading.color : ""}
         />;
     }
 
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 4ec968438..c0c4750eb 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -23,7 +23,6 @@ import "./CollectionStackingView.scss";
 
 library.add(faPalette);
 
-
 interface CSVFieldColumnProps {
     cols: () => number;
     headings: () => object[];
-- 
cgit v1.2.3-70-g09d2


From 2e6c8efbcea67d345023db679de15f294a792dc5 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Tue, 24 Sep 2019 18:09:31 -0400
Subject: Initial menu for header capabilities

---
 .../collections/CollectionMasonryViewFieldRow.tsx  | 11 +++----
 .../views/collections/CollectionStackingView.scss  | 38 +++++++++++++++++++++-
 .../views/collections/CollectionStackingView.tsx   |  7 ----
 .../CollectionStackingViewFieldColumn.tsx          | 31 +++++++++++++++---
 4 files changed, 68 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index b1afd09c2..38f7493de 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -327,11 +327,11 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                             </ Flyout >
                         </div>
                     }
-                    {evContents === `NO ${key.toUpperCase()} VALUE` ?
-                        (null) :
-                        <button className="collectionStackingView-sectionDelete" onClick={this.deleteRow}>
-                            <FontAwesomeIcon icon="trash" />
-                        </button>}
+                    {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
+                        <button className="collectionStackingView-sectionOptions" onClick={this.deleteRow}>
+                            <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
+                        </button>
+                    }
                 </div>
             </div > : (null);
         const background = this._background; //to account for observables in Measure
@@ -351,7 +351,6 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                             {collapsed ? (null) :
                                 < div >
                                     <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
-                                        // ref={this.getTrueHeight}
                                         ref={this._contRef}
                                         style={{
                                             padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index deb295a41..f34b3768b 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -138,7 +138,7 @@
             .editableView-input:hover,
             .editableView-container-editing:hover,
             .editableView-container-editing-oneLine:hover {
-                cursor: text
+                cursor: text;
             }
 
             .editableView-input {
@@ -188,6 +188,42 @@
             }
         }
 
+        .collectionStackingView-sectionOptions {
+            position: absolute;
+            right: 0;
+            top: 0;
+            height: 100%;
+
+            [class*="css"] {
+                max-width: 102px;
+            }
+
+            .collectionStackingView-sectionOptionButton {
+                height: 35px;
+            }
+
+            .collectionStackingView-optionPicker {
+                width: 78px;
+
+                .optionOptions {
+                    display: inline;
+                }
+
+                .optionPicker {
+                    cursor: pointer;
+                    width: 20px;
+                    height: 20px;
+                    border-radius: 10px;
+                    margin: 3px;
+
+                    &.active {
+                        border: 2px solid white;
+                        box-shadow: 0 0 0 2px lightgray;
+                    }
+                }
+            }
+        }
+
         .collectionStackingView-sectionDelete {
             position: absolute;
             right: 0;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 3fec7a85f..9c6d5c52a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -25,8 +25,6 @@ import { ContextMenuProps } from "../ContextMenuItem";
 import { ScriptBox } from "../ScriptBox";
 import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
 
-// let _height: number = 0;
-
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
     _masonryGridRef: HTMLDivElement | null = null;
@@ -103,8 +101,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                 args[1] instanceof Doc &&
                     this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined));
             });
-
-
         // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking and masonry views -eeng
         this._heightDisposer = reaction(() => {
             if (BoolCast(this.props.Document.autoHeight)) {
@@ -114,9 +110,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                         (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)), 0);
                 } else {
                     let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
-                    // let transformScale = this.props.ScreenToLocalTransform().Scale;
-                    // let trueHeight = 30 * transformScale;
-                    // sum += trueHeight;
                     sum += 30;
                     return this.props.ContentScaling() * (sum + (this.Sections.size ? 50 : 0));
                 }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index c0c4750eb..aacf8ad4b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -20,6 +20,7 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
 import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import "./CollectionStackingView.scss";
+import Measure from "react-measure";
 
 library.add(faPalette);
 
@@ -38,6 +39,9 @@ interface CSVFieldColumnProps {
 @observer
 export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
     @observable private _background = "inherit";
+    @observable private _selected: boolean = false;
+    @observable private _mouseX: number = 0;
+    @observable private _mouseY: number = 0;
 
     private _dropRef: HTMLDivElement | null = null;
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -248,6 +252,19 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         );
     }
 
+    renderMenu = () => {
+        return (
+            <div className="collectionStackingView-optionPicker">
+                <div className="optionOptions">
+                    <div className="optionPicker">Delete</div>
+                    <div className="optionPicker">Edit</div>
+                    <div className="optionPicker">Collapse</div>
+                    <div className="optionPicker">Alias</div>
+                </div>
+            </div>
+        );
+    }
+
     @observable private collapsed: boolean = false;
 
     private toggleVisibility = action(() => {
@@ -312,11 +329,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                             </ Flyout >
                         </div>
                     }
-                    {evContents === `NO ${key.toUpperCase()} VALUE` ?
-                        (null) :
-                        <button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
-                            <FontAwesomeIcon icon="trash" />
-                        </button>}
+                    {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
+                        <div className="collectionStackingView-sectionOptions">
+                            <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderMenu()}>
+                                <button className="collectionStackingView-sectionOptionButton">
+                                    <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
+                                </button>
+                            </Flyout>
+                        </div>
+                    }
                 </div>
             </div> : (null);
         for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
-- 
cgit v1.2.3-70-g09d2


From 7e4dc0b2b2c604c60ffdfbaeb3eda95fa8221a06 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Tue, 24 Sep 2019 18:19:40 -0400
Subject: delete enabled

---
 src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index aacf8ad4b..b04e1fc73 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -253,10 +253,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     }
 
     renderMenu = () => {
+        let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className="optionPicker">Delete</div>
+                    <div className="optionPicker" onClick={() => this.deleteColumn()}>Delete</div>
                     <div className="optionPicker">Edit</div>
                     <div className="optionPicker">Collapse</div>
                     <div className="optionPicker">Alias</div>
-- 
cgit v1.2.3-70-g09d2


From 16e511e35ecbe6166ced813d27884b367f6c6fbb Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Sat, 28 Sep 2019 16:53:13 -0400
Subject: delete and menu button for group header

---
 .../collections/CollectionMasonryViewFieldRow.tsx  | 26 +++++++++++++++++++---
 .../views/collections/CollectionStackingView.scss  |  2 +-
 .../CollectionStackingViewFieldColumn.tsx          | 12 +++++-----
 3 files changed, 31 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 38f7493de..ad84b7635 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -261,6 +261,17 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         );
     }
 
+    renderMenu = () => {
+        let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+        return (
+            <div className="collectionStackingView-optionPicker">
+                <div className="optionOptions">
+                    <div className="optionPicker">Create Alias</div>
+                </div>
+            </div>
+        );
+    }
+
     @observable private collapsed: boolean = false;
 
     private toggleVisibility = action(() => {
@@ -327,10 +338,19 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                             </ Flyout >
                         </div>
                     }
+                    {evContents === `NO ${key.toUpperCase()} VALUE` ?
+                        (null) :
+                        <button className="collectionStackingView-sectionDelete" onClick={this.deleteRow}>
+                            <FontAwesomeIcon icon="trash" size="lg" />
+                        </button>}
                     {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
-                        <button className="collectionStackingView-sectionOptions" onClick={this.deleteRow}>
-                            <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
-                        </button>
+                        <div className="collectionStackingView-sectionOptions">
+                            <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderMenu()}>
+                                <button className="collectionStackingView-sectionOptionButton">
+                                    <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
+                                </button>
+                            </Flyout>
+                        </div>
                     }
                 </div>
             </div > : (null);
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index f34b3768b..d3deff9f1 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -226,7 +226,7 @@
 
         .collectionStackingView-sectionDelete {
             position: absolute;
-            right: 0;
+            right: 25px;
             top: 0;
             height: 100%;
         }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index b04e1fc73..e94a7d7f6 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -257,10 +257,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className="optionPicker" onClick={() => this.deleteColumn()}>Delete</div>
-                    <div className="optionPicker">Edit</div>
-                    <div className="optionPicker">Collapse</div>
-                    <div className="optionPicker">Alias</div>
+                    <div className="optionPicker">Create Alias</div>
                 </div>
             </div>
         );
@@ -325,11 +322,16 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         <div className="collectionStackingView-sectionColor">
                             <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
                                 <button className="collectionStackingView-sectionColorButton">
-                                    <FontAwesomeIcon icon="palette" size="sm" />
+                                    <FontAwesomeIcon icon="palette" size="lg" />
                                 </button>
                             </ Flyout >
                         </div>
                     }
+                    {evContents === `NO ${key.toUpperCase()} VALUE` ?
+                        (null) :
+                        <button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
+                            <FontAwesomeIcon icon="trash" size="lg" />
+                        </button>}
                     {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
                         <div className="collectionStackingView-sectionOptions">
                             <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderMenu()}>
-- 
cgit v1.2.3-70-g09d2


From 2b26a9e8b718fb595a0f40914741e4f7d7c19d14 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Tue, 1 Oct 2019 18:52:53 -0400
Subject: UI changes: 3 buttons, bar, menu w/ alias

---
 src/client/views/EditableView.tsx                  | 13 ----------
 .../collections/CollectionMasonryViewFieldRow.tsx  | 25 +++++++++++++++----
 .../views/collections/CollectionStackingView.scss  | 10 +++++++-
 .../CollectionStackingViewFieldColumn.tsx          | 28 ++++++++++++++++------
 4 files changed, 51 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 4a77db3d6..58f5d662d 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -73,14 +73,6 @@ export class EditableView extends React.Component<EditableProps> {
         }
     }
 
-    collapseSection() {
-        if (this.props.HeadingObject) {
-            this._headingsHack++;
-            this.props.HeadingObject.setCollapsed(!this.props.HeadingObject.collapsed);
-            this.props.toggle && this.props.toggle();
-        }
-    }
-
     @action
     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
         if (e.key === "Tab") {
@@ -110,11 +102,6 @@ export class EditableView extends React.Component<EditableProps> {
             this._editing = true;
             this.props.isEditingCallback && this.props.isEditingCallback(true);
         }
-        if (e.ctrlKey) {
-            this._editing = false;
-            this.props.isEditingCallback && this.props.isEditingCallback(false);
-            this.collapseSection();
-        }
         e.stopPropagation();
     }
 
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index ad84b7635..8ba33b38d 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -40,6 +40,7 @@ interface CMVFieldRowProps {
 @observer
 export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
     @observable private _background = "inherit";
+    @observable private _createAliasSelected: boolean = false;
 
     private _dropRef: HTMLDivElement | null = null;
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -186,6 +187,15 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         }
     }
 
+    @action
+    collapseSection = () => {
+        if (this.props.headingObject) {
+            this._headingsHack++;
+            this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
+            this.toggleVisibility();
+        }
+    }
+
     startDrag = (e: PointerEvent) => {
         let [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) {
@@ -223,12 +233,13 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
 
-        if (e.altKey) { //release alt key before dropping alias; also, things must have existed outside of the collection first in order to be in the alias...
+        if (this._createAliasSelected) {
             document.removeEventListener("pointermove", this.startDrag);
             document.addEventListener("pointermove", this.startDrag);
             document.removeEventListener("pointerup", this.pointerUp);
             document.addEventListener("pointerup", this.pointerUp);
         }
+        this._createAliasSelected = false;
     }
 
     renderColorPicker = () => {
@@ -261,12 +272,17 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         );
     }
 
+    @action
+    toggleAlias = () => {
+        this._createAliasSelected = true;
+    }
+
     renderMenu = () => {
         let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className="optionPicker">Create Alias</div>
+                    <div className="optionPicker" onClick={this.toggleAlias}>Create Alias</div>
                 </div>
             </div>
         );
@@ -320,6 +336,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         };
         let headingView = this.props.headingObject ?
             <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
+                <div className="collectionStackingView-collapseBar" onClick={this.collapseSection}></div>
                 <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
                     title={evContents === `NO ${key.toUpperCase()} VALUE` ?
                         `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
@@ -333,7 +350,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                         <div className="collectionStackingView-sectionColor">
                             <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
                                 <button className="collectionStackingView-sectionColorButton">
-                                    <FontAwesomeIcon icon="palette" size="sm" />
+                                    <FontAwesomeIcon icon="palette" size="lg" />
                                 </button>
                             </ Flyout >
                         </div>
@@ -345,7 +362,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                         </button>}
                     {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
                         <div className="collectionStackingView-sectionOptions">
-                            <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderMenu()}>
+                            <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
                                 <button className="collectionStackingView-sectionOptionButton">
                                     <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
                                 </button>
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index d3deff9f1..818db1669 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -102,12 +102,20 @@
         margin: auto;
     }
 
+    .collectionStackingView-collapseBar {
+        margin-left: 2px;
+        margin-right: 2px;
+        margin-top: 2px;
+        background: $main-accent;
+        height: 5px;
+    }
+
     .collectionStackingView-sectionHeader {
         text-align: center;
         margin-left: 2px;
         margin-right: 2px;
         margin-top: 10px;
-        background: gray;
+        background: $main-accent;
         // overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
 
         .editableView-input {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index e94a7d7f6..a8015472a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -39,9 +39,7 @@ interface CSVFieldColumnProps {
 @observer
 export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
     @observable private _background = "inherit";
-    @observable private _selected: boolean = false;
-    @observable private _mouseX: number = 0;
-    @observable private _mouseY: number = 0;
+    @observable private _createAliasSelected: boolean = false;
 
     private _dropRef: HTMLDivElement | null = null;
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -177,6 +175,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         }
     }
 
+    @action
+    collapseSection = () => {
+        if (this.props.headingObject) {
+            this._headingsHack++;
+            this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
+            this.toggleVisibility();
+        }
+    }
+
     startDrag = (e: PointerEvent) => {
         let [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) {
@@ -214,12 +221,13 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
         this._startDragPosition = { x: dx, y: dy };
 
-        if (e.altKey) { //release alt key before dropping alias; also, things must have existed outside of the collection first in order to be in the alias...
+        if (this._createAliasSelected) {
             document.removeEventListener("pointermove", this.startDrag);
             document.addEventListener("pointermove", this.startDrag);
             document.removeEventListener("pointerup", this.pointerUp);
             document.addEventListener("pointerup", this.pointerUp);
         }
+        this._createAliasSelected = false;
     }
 
     renderColorPicker = () => {
@@ -252,14 +260,19 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         );
     }
 
+    @action
+    toggleAlias = () => {
+        this._createAliasSelected = true;
+    }
+
     renderMenu = () => {
         let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className="optionPicker">Create Alias</div>
+                    <div className={"optionPicker" + (selected === " active")} onClick={this.toggleAlias}>Create Alias</div>
                 </div>
-            </div>
+            </div >
         );
     }
 
@@ -307,6 +320,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         ((uniqueHeadings.length +
                             ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
                 }}>
+                <div className="collectionStackingView-collapseBar" onClick={this.collapseSection}></div>
                 {/* the default bucket (no key value) has a tooltip that describes what it is.
                     Further, it does not have a color and cannot be deleted. */}
                 <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
@@ -334,7 +348,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         </button>}
                     {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
                         <div className="collectionStackingView-sectionOptions">
-                            <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderMenu()}>
+                            <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
                                 <button className="collectionStackingView-sectionOptionButton">
                                     <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
                                 </button>
-- 
cgit v1.2.3-70-g09d2


From c0055c5677489b4cc7896c6e0b36f4730d49b4eb Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Sun, 6 Oct 2019 14:47:22 -0400
Subject: indicator for create alias

---
 .../views/collections/CollectionMasonryViewFieldRow.tsx      | 12 ++++++++++--
 src/client/views/collections/CollectionStackingView.scss     |  3 +--
 .../views/collections/CollectionStackingViewFieldColumn.tsx  | 12 ++++++++++--
 3 files changed, 21 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 8ba33b38d..e3d7fc481 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -74,6 +74,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     @undoBatch
     @action
     rowDrop = (e: Event, de: DragManager.DropEvent) => {
+        this._createAliasSelected = false;
         if (de.data instanceof DragManager.DocumentDragData) {
             let key = StrCast(this.props.parent.props.Document.sectionFilter);
             let castedValue = this.getValue(this._heading);
@@ -130,6 +131,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
+        this._createAliasSelected = false;
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         let castedValue = this.getValue(value);
         if (castedValue) {
@@ -150,6 +152,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     changeColumnColor = (color: string) => {
+        this._createAliasSelected = false;
         if (this.props.headingObject) {
             this.props.headingObject.setColor(color);
             this._color = color;
@@ -158,6 +161,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     pointerEnteredRow = () => {
+        this._createAliasSelected = false;
         if (SelectionManager.GetIsDragging()) {
             this._background = "#b4b4b4";
         }
@@ -165,12 +169,14 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     pointerLeaveRow = () => {
+        this._createAliasSelected = false;
         this._background = "inherit";
         document.removeEventListener("pointermove", this.startDrag);
     }
 
     @action
     addDocument = (value: string, shiftDown?: boolean) => {
+        this._createAliasSelected = false;
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
         newDoc[key] = this.getValue(this.props.heading);
@@ -179,6 +185,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     deleteRow = () => {
+        this._createAliasSelected = false;
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         this.props.docList.forEach(d => d[key] = undefined);
         if (this.props.parent.sectionHeaders && this.props.headingObject) {
@@ -189,6 +196,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     collapseSection = () => {
+        this._createAliasSelected = false;
         if (this.props.headingObject) {
             this._headingsHack++;
             this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
@@ -278,11 +286,11 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
 
     renderMenu = () => {
-        let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+        let selected = this._createAliasSelected;
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className="optionPicker" onClick={this.toggleAlias}>Create Alias</div>
+                    <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
                 </div>
             </div>
         );
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 818db1669..df3f7e70d 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -225,8 +225,7 @@
                     margin: 3px;
 
                     &.active {
-                        border: 2px solid white;
-                        box-shadow: 0 0 0 2px lightgray;
+                        color: red;
                     }
                 }
             }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index a8015472a..fabe19d52 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -61,6 +61,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @undoBatch
     @action
     columnDrop = (e: Event, de: DragManager.DropEvent) => {
+        this._createAliasSelected = false;
         if (de.data instanceof DragManager.DocumentDragData) {
             let key = StrCast(this.props.parent.props.Document.sectionFilter);
             let castedValue = this.getValue(this._heading);
@@ -118,6 +119,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
+        this._createAliasSelected = false;
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         let castedValue = this.getValue(value);
         if (castedValue) {
@@ -138,6 +140,7 @@ 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;
@@ -147,18 +150,21 @@ 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) => {
+        this._createAliasSelected = false;
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value });
         newDoc[key] = this.getValue(this.props.heading);
@@ -167,6 +173,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     deleteColumn = () => {
+        this._createAliasSelected = false;
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         this.props.docList.forEach(d => d[key] = undefined);
         if (this.props.parent.sectionHeaders && this.props.headingObject) {
@@ -177,6 +184,7 @@ 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);
@@ -266,11 +274,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     }
 
     renderMenu = () => {
-        let selected = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+        let selected = this._createAliasSelected;
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className={"optionPicker" + (selected === " active")} onClick={this.toggleAlias}>Create Alias</div>
+                    <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
                 </div>
             </div >
         );
-- 
cgit v1.2.3-70-g09d2


From 9c980548765016911c0f7624ac15c9692243dfb5 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Sun, 6 Oct 2019 15:31:46 -0400
Subject: masonry alias fixed, collapse inidicator added

---
 src/client/views/collections/CollectionMasonryViewFieldRow.tsx     | 3 +--
 src/client/views/collections/CollectionStackingView.scss           | 6 ++++++
 src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 2 +-
 3 files changed, 8 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e3d7fc481..4505aeb70 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -161,7 +161,6 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     pointerEnteredRow = () => {
-        this._createAliasSelected = false;
         if (SelectionManager.GetIsDragging()) {
             this._background = "#b4b4b4";
         }
@@ -344,7 +343,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         };
         let headingView = this.props.headingObject ?
             <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
-                <div className="collectionStackingView-collapseBar" onClick={this.collapseSection}></div>
+                <div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
                 <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
                     title={evContents === `NO ${key.toUpperCase()} VALUE` ?
                         `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index df3f7e70d..cf7da069c 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -108,6 +108,12 @@
         margin-top: 2px;
         background: $main-accent;
         height: 5px;
+
+        &.active {
+            margin-left: 0;
+            margin-right: 0;
+            background: red;
+        }
     }
 
     .collectionStackingView-sectionHeader {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index fabe19d52..dccc9462a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -328,7 +328,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         ((uniqueHeadings.length +
                             ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
                 }}>
-                <div className="collectionStackingView-collapseBar" onClick={this.collapseSection}></div>
+                <div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
                 {/* the default bucket (no key value) has a tooltip that describes what it is.
                     Further, it does not have a color and cannot be deleted. */}
                 <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
-- 
cgit v1.2.3-70-g09d2


From 4d6bcd43e015a760f73508150ffef190cd6d72cf Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Sun, 6 Oct 2019 15:35:51 -0400
Subject: potato

---
 src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 1 -
 1 file changed, 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index dccc9462a..445a2a391 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -20,7 +20,6 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
 import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import "./CollectionStackingView.scss";
-import Measure from "react-measure";
 
 library.add(faPalette);
 
-- 
cgit v1.2.3-70-g09d2


From 5c2d46f7cb064f579bde1e17af91271b31440310 Mon Sep 17 00:00:00 2001
From: eeng5 <eleanor_eng@brown.edu>
Date: Sun, 6 Oct 2019 16:52:58 -0400
Subject: otatop

---
 src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 445a2a391..bda6bdfae 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -381,9 +381,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                                 gridGap: style.gridGap,
                                 gridTemplateColumns: singleColumn ? undefined : templatecols,
                                 gridAutoRows: singleColumn ? undefined : "0px"
-                            }}
-                        // ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}
-                        >
+                            }}>
                             {this.children(this.props.docList)}
                             {singleColumn ? (null) : this.props.parent.columnDragger}
                         </div>
-- 
cgit v1.2.3-70-g09d2


From 2cc7ca6b267d9dd6b1683d59b6966a021339bc18 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 9 Oct 2019 22:48:00 -0400
Subject: fixed compile errors and exceptions

---
 src/client/views/collections/CollectionMasonryViewFieldRow.tsx     | 5 ++---
 src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 5 ++---
 src/server/authentication/config/passport.ts                       | 2 +-
 src/server/index.ts                                                | 6 +++---
 4 files changed, 8 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 4505aeb70..f01a54001 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -72,8 +72,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     }
 
     @undoBatch
-    @action
-    rowDrop = (e: Event, de: DragManager.DropEvent) => {
+    rowDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createAliasSelected = false;
         if (de.data instanceof DragManager.DocumentDragData) {
             let key = StrCast(this.props.parent.props.Document.sectionFilter);
@@ -87,7 +86,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             this.props.parent.drop(e, de);
             e.stopPropagation();
         }
-    }
+    })
 
     masonryChildren(docs: Doc[]) {
         let parent = this.props.parent;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index bda6bdfae..fffa1dde5 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -58,8 +58,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     }
 
     @undoBatch
-    @action
-    columnDrop = (e: Event, de: DragManager.DropEvent) => {
+    columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createAliasSelected = false;
         if (de.data instanceof DragManager.DocumentDragData) {
             let key = StrCast(this.props.parent.props.Document.sectionFilter);
@@ -73,7 +72,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             this.props.parent.drop(e, de);
             e.stopPropagation();
         }
-    }
+    })
 
     children(docs: Doc[]) {
         let parent = this.props.parent;
diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts
index d42741410..e87b93ddf 100644
--- a/src/server/authentication/config/passport.ts
+++ b/src/server/authentication/config/passport.ts
@@ -42,7 +42,7 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) =
 export let isAuthorized = (req: Request, res: Response, next: NextFunction) => {
     const provider = req.path.split("/").slice(-1)[0];
 
-    if (_.find(req.user.tokens, { kind: provider })) {
+    if (_.find((req.user as any).tokens, { kind: provider })) {
         next();
     } else {
         res.redirect(`/auth/${provider}`);
diff --git a/src/server/index.ts b/src/server/index.ts
index eeadef239..de46ebf71 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -116,7 +116,7 @@ function addSecureRoute(method: Method,
 ) {
     let abstracted = (req: express.Request, res: express.Response) => {
         if (req.user) {
-            handler(req.user, res, req);
+            handler(req.user as any, res, req);
         } else {
             req.session!.target = req.originalUrl;
             onRejection(res, req);
@@ -813,8 +813,8 @@ const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerU
 app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
     let sector = req.params.sector;
     let action = req.params.action;
-    GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentials, token }).then(endpoint => {
-        let handler = EndpointHandlerMap.get(action);
+    GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector as any], { credentials, token }).then(endpoint => {
+        let handler = EndpointHandlerMap.get(action as any);
         if (endpoint && handler) {
             let execute = handler(endpoint, req.body).then(
                 response => res.send(response.data),
-- 
cgit v1.2.3-70-g09d2


From ee10ec0995fa67d27edb9032ba6fadf79bbdaaa1 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 10 Oct 2019 01:22:27 -0400
Subject: fixed document decorations for masonry view & scrolling.  cleaned up
 duplicated code a bit.

---
 .../collections/CollectionMasonryViewFieldRow.tsx  | 28 +-------
 .../views/collections/CollectionStackingView.scss  |  8 ++-
 .../views/collections/CollectionStackingView.tsx   | 75 +++++++++++++---------
 .../CollectionStackingViewFieldColumn.tsx          | 21 +-----
 4 files changed, 53 insertions(+), 79 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index b744ce4a5..5c1960d53 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -88,32 +88,6 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         }
     })
 
-    masonryChildren(docs: Doc[]) {
-        let parent = this.props.parent;
-        parent._docXfs.length = 0;
-        return docs.map((d, i) => {
-            let dref = React.createRef<HTMLDivElement>();
-            let layoutDoc = Doc.expandTemplateLayout(d, parent.props.DataDoc);
-            let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(d[WidthSym](), parent.columnWidth) : parent.columnWidth);/// (uniqueHeadings.length + 1);
-            let height = () => parent.getDocHeight(layoutDoc);
-            let dxf = () => parent.getDocTransform(layoutDoc as Doc, dref.current!);
-            let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
-            parent._docXfs.push({ dxf: dxf, width: width, height: height });
-            return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
-                {this.props.parent.getDisplayDoc(layoutDoc as Doc, d, dxf, width)}
-            </div>;
-        });
-    }
-
-    getDocTransform(doc: Doc, dref: HTMLDivElement) {
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
-        let outerXf = Utils.GetScreenTransform(this.props.parent._masonryGridRef!);
-        let offset = this.props.parent.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
-        return this.props.parent.props.ScreenToLocalTransform().
-            translate(offset[0], offset[1]).
-            scale(NumCast(doc.width, 1) / this.props.parent.columnWidth);
-    }
-
     getValue = (value: string): any => {
         let parsed = parseInt(value);
         if (!isNaN(parsed)) {
@@ -402,7 +376,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
                                             gridGap: this.props.parent.gridGap,
                                             gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
                                         }}>
-                                        {this.masonryChildren(this.props.docList)}
+                                        {this.props.parent.children(this.props.docList)}
                                         {this.props.parent.columnDragger}
                                     </div>
                                     {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 7bf8348ff..b31f0b8e3 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -7,12 +7,16 @@
 .collectionStackingView {
     display: flex;
 }
-
+.collectionStackingMasonry-cont {
+    position:relative;
+    height:100%;
+    width:100%;
+}
 .collectionStackingView,
 .collectionMasonryView {
     height: 100%;
     width: 100%;
-    position: relative;
+    position: absolute;
     top: 0;
     overflow-y: auto;
     flex-wrap: wrap;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index cde015152..0dfd1d9cf 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -29,12 +29,12 @@ import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
-    @observable _heightMap = new Map<string, number>();
     _heightDisposer?: IReactionDisposer;
     _sectionFilterDisposer?: IReactionDisposer;
     _docXfs: any[] = [];
     _columnStart: number = 0;
-    @observable private cursor: CursorProperty = "grab";
+    @observable _heightMap = new Map<string, number>();
+    @observable _cursor: CursorProperty = "grab";
     @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); }
@@ -49,12 +49,26 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
             this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250));
     }
-    @computed get NodeWidth() {
-        return this.props.PanelWidth();
-    }
+    @computed get NodeWidth() { return this.props.PanelWidth(); }
 
     childDocHeight(child: Doc) { return this.getDocHeight(Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, child).layout); }
 
+    children(docs: Doc[]) {
+        this._docXfs.length = 0;
+        return docs.map((d, i) => {
+            let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
+            let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+            let height = () => this.getDocHeight(pair.layout);
+            let dref = React.createRef<HTMLDivElement>();
+            let dxf = () => this.getDocTransform(pair.layout!, dref.current!);
+            this._docXfs.push({ dxf: dxf, width: width, height: height });
+            let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+            let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+            return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
+                {this.getDisplayDoc(pair.layout as Doc, pair.data, dxf, width)}
+            </div>;
+        });
+    }
     @action
     setDocHeight = (key: string, sectionHeight: number) => {
         this._heightMap.set(key, sectionHeight);
@@ -177,19 +191,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (!d) return 0;
         let nw = NumCast(d.nativeWidth);
         let nh = NumCast(d.nativeHeight);
+        let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
         if (!d.ignoreAspect && !d.fitWidth && nw && nh) {
             let aspect = nw && nh ? nh / nw : 1;
-            let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid);
             return wid * aspect;
         }
-        return d.fitWidth ? this.props.PanelHeight() - 2 * this.yMargin : d[HeightSym]();
+        return d.fitWidth ? Math.min(wid * NumCast(d.scrollHeight, NumCast(d.nativeHeight)) / NumCast(d.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : d[HeightSym]();
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
-        runInAction(() => this.cursor = "grabbing");
+        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];
@@ -204,13 +218,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     @action
     onDividerUp = (e: PointerEvent): void => {
-        runInAction(() => this.cursor = "grab");
+        runInAction(() => this._cursor = "grab");
         document.removeEventListener("pointermove", this.onDividerMove);
         document.removeEventListener('pointerup', this.onDividerUp);
     }
 
     @computed get columnDragger() {
-        return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this.cursor, left: `${this.columnWidth + this.xMargin}px` }} >
+        return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px` }} >
             <FontAwesomeIcon icon={"arrows-alt-h"} />
         </div>;
     }
@@ -375,26 +389,27 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             sections = entries.sort(this.sortFunc);
         }
         return (
-            <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
-                ref={this.createRef}
-                onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
-                onDrop={this.onDrop.bind(this)}
-                onContextMenu={this.onContextMenu}
-                onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
-                {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
-                {!this.showAddAGroup ? (null) :
-                    <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
-                        style={{ width: this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
-                        <EditableView {...editableViewProps} />
-                    </div>}
-                {this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled' ? <Switch
-                    onChange={this.onToggle}
-                    onClick={this.onToggle}
-                    defaultChecked={this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode'}
-                    checkedChildren="edit"
-                    unCheckedChildren="view"
-                /> : null}
-            </div>
+            <div className="collectionStackingMasonry-cont" >
+                <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+                    ref={this.createRef}
+                    onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
+                    onDrop={this.onDrop.bind(this)}
+                    onContextMenu={this.onContextMenu}
+                    onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
+                    {sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]))}
+                    {!this.showAddAGroup ? (null) :
+                        <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
+                            style={{ width: this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+                            <EditableView {...editableViewProps} />
+                        </div>}
+                    {this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled' ? <Switch
+                        onChange={this.onToggle}
+                        onClick={this.onToggle}
+                        defaultChecked={this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode'}
+                        checkedChildren="edit"
+                        unCheckedChildren="view"
+                    /> : null}
+                </div> </div>
         );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index cc7fe2aa1..e8627780d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -73,25 +73,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             e.stopPropagation();
         }
     })
-
-    children(docs: Doc[]) {
-        let parent = this.props.parent;
-        parent._docXfs.length = 0;
-        return docs.map((d, i) => {
-            let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
-            let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, parent.columnWidth / parent.numGroupColumns);
-            let height = () => parent.getDocHeight(pair.layout);
-            let dref = React.createRef<HTMLDivElement>();
-            let dxf = () => parent.getDocTransform(pair.layout!, dref.current!);
-            parent._docXfs.push({ dxf: dxf, width: width, height: height });
-            let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
-            let style = parent.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
-            return <div className={`collectionStackingView-${parent.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
-                {parent.getDisplayDoc(pair.layout as Doc, pair.data, dxf, width)}
-            </div>;
-        });
-    }
-
     getValue = (value: string): any => {
         let parsed = parseInt(value);
         if (!isNaN(parsed)) {
@@ -373,7 +354,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                                     gridTemplateColumns: singleColumn ? undefined : templatecols,
                                     gridAutoRows: singleColumn ? undefined : "0px"
                                 }}>
-                                {this.children(this.props.docList)}
+                                {this.props.parent.children(this.props.docList)}
                                 {singleColumn ? (null) : this.props.parent.columnDragger}
                             </div>
                             {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
-- 
cgit v1.2.3-70-g09d2


From 21aab2fbf28e9cf6d08366b57d368d120d6813bb Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 10 Oct 2019 16:42:12 -0400
Subject: got rid of all CollectionView wrappers around XxxBox's  added
 DocAnnotatableComponent

---
 src/client/documents/Documents.ts                  |   4 +-
 src/client/util/RichTextSchema.tsx                 |   4 +-
 src/client/views/DocComponent.tsx                  |  50 ++++++-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   2 +-
 .../CollectionStackingViewFieldColumn.tsx          |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  20 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |   8 +-
 src/client/views/nodes/ImageBox.scss               |   8 +-
 src/client/views/nodes/ImageBox.tsx                |  44 +++++--
 src/client/views/nodes/PDFBox.tsx                  |  24 ++--
 src/client/views/nodes/VideoBox.scss               |   8 +-
 src/client/views/nodes/VideoBox.tsx                | 143 ++++++---------------
 src/client/views/nodes/WebBox.tsx                  |  69 +++++++---
 src/client/views/pdf/Annotation.tsx                |   8 +-
 src/client/views/pdf/PDFViewer.tsx                 |  65 +++-------
 src/new_fields/Doc.ts                              |   6 +-
 18 files changed, 234 insertions(+), 234 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 22aa74634..0114f82d8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -120,11 +120,11 @@ export namespace Docs {
                 options: { height: 300, backgroundColor: "black" }
             }],
             [DocumentType.IMG, {
-                layout: { view: ImageBox, collectionView: [CollectionView, data, anno] as CollectionViewType },
+                layout: { view: ImageBox, ext: anno },
                 options: {}
             }],
             [DocumentType.WEB, {
-                layout: { view: WebBox, collectionView: [CollectionView, data, anno] as CollectionViewType },
+                layout: { view: WebBox, ext: anno },
                 options: { height: 300 }
             }],
             [DocumentType.COL, {
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index a5502577b..063686d58 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -784,8 +784,8 @@ export class DashDocView {
                     addDocTab={self._textBox.props.addDocTab}
                     pinToPres={returnFalse}
                     renderDepth={1}
-                    PanelWidth={self._dashDoc![WidthSym]}
-                    PanelHeight={self._dashDoc![HeightSym]}
+                    PanelWidth={self._dashDoc[WidthSym]}
+                    PanelHeight={self._dashDoc[HeightSym]}
                     focus={emptyFunction}
                     backgroundColor={returnEmptyString}
                     parentActive={returnFalse}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index d6562492f..93e852bef 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,6 +1,8 @@
 import * as React from 'react';
 import { Doc } from '../../new_fields/Doc';
-import { computed } from 'mobx';
+import { computed, action } from 'mobx';
+import { Cast } from '../../new_fields/Types';
+import { listSpec } from '../../new_fields/Schema';
 
 export function DocComponent<P extends { Document: Doc }, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends React.Component<P> {
@@ -11,4 +13,50 @@ export function DocComponent<P extends { Document: Doc }, T>(schemaCtor: (doc: D
         }
     }
     return Component;
+}
+
+interface DocAnnotatableProps {
+    Document: Doc;
+    DataDoc?: Doc;
+    fieldKey: string;
+    fieldExt: string;
+    whenActiveChanged: (isActive: boolean) => void;
+    isSelected: () => boolean, renderDepth: number
+}
+export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
+    class Component extends React.Component<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);
+        }
+        _isChildActive = false;
+        @action.bound
+        removeDocument(doc: Doc): boolean {
+            Doc.GetProto(doc).annotationOn = undefined;
+            let value = Cast(this.extensionDoc[this.props.fieldExt], listSpec(Doc), []);
+            let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
+            return index !== -1 && value.splice(index, 1) ? true : false;
+        }
+
+        @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
+
+        @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
+        // if the moved document is already in this overlay collection nothing needs to be done.
+        // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. 
+        @action.bound
+        moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+            return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false;
+        }
+
+        @action.bound
+        addDocument(doc: Doc): boolean {
+            Doc.GetProto(doc).annotationOn = this.props.Document;
+            return Doc.AddDocToList(this.extensionDoc, this.props.fieldExt, doc);
+        }
+        whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
+        active = () => this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
+    }
+    return Component;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 5c1960d53..6251d7114 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -86,7 +86,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             this.props.parent.drop(e, de);
             e.stopPropagation();
         }
-    })
+    });
 
     getValue = (value: string): any => {
         let parsed = parseInt(value);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index e8627780d..815b28586 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -72,7 +72,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             this.props.parent.drop(e, de);
             e.stopPropagation();
         }
-    })
+    });
     getValue = (value: string): any => {
         let parsed = parseInt(value);
         if (!isNaN(parsed)) {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 689adc375..06d048383 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -38,6 +38,7 @@ export interface CollectionViewProps extends FieldViewProps {
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
     ruleProvider: Doc | undefined;
+    children?: never | (() => JSX.Element[]) | React.ReactNode;
 }
 
 export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d2644480c..adbad5da5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye } from "@fortawesome/free-regular-svg-icons";
-import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faFileUpload } from "@fortawesome/free-solid-svg-icons";
+import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
@@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        if (!e.cancelBubble && !this.isAnnotationOverlay) {
+        if (!e.cancelBubble) {
             if (this._hitCluster && this.tryDragCluster(e)) {
                 e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                 e.preventDefault();
@@ -339,7 +339,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.props.Document.lockedPosition || this.props.Document.inOverlay || this.isAnnotationOverlay) return;
+        if (this.props.Document.lockedPosition || this.props.Document.inOverlay) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -477,7 +477,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             PanelWidth: layoutDoc[WidthSym],
             PanelHeight: layoutDoc[HeightSym],
             ContentScaling: returnOne,
-            ContainingCollectionView: this.props.CollectionView,
+            ContainingCollectionView: this.props.ContainingCollectionView,
             focus: this.focusDocument,
             backgroundColor: returnEmptyString,
             parentActive: this.props.active,
@@ -681,10 +681,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
 
-    private childViews = () => [
-        <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
-        ...this.views
-    ]
+    private childViews = () => {
+        let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
+        return [
+            <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
+            ...children,
+            ...this.views,
+        ];
+    }
     render() {
         // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
         this.props.Document.fitX = this.contentBounds && this.contentBounds.x;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index bb787106c..ecdd02b0f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -217,7 +217,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             this._downY = y;
             PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument);
         }
-    })
+    });
 
     @action
     onClick = (e: React.MouseEvent): void => {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9ca77cc8b..cbe4945af 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -218,13 +218,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
             let res = terms.map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
-            let tr = this._editorView!.state.tr;
+            let tr = this._editorView.state.tr;
             let flattened: TextSelection[] = [];
             res.map(r => r.map(h => flattened.push(h)));
             let lastSel = Math.min(flattened.length - 1, this._searchIndex);
             flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
             this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-            this._editorView!.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
+            this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
         }
     }
 
@@ -232,8 +232,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         if (this._editorView && (this._editorView as any).docView) {
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
-            let end = this._editorView!.state.doc.nodeSize - 2;
-            this._editorView!.dispatch(this._editorView!.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
+            let end = this._editorView.state.doc.nodeSize - 2;
+            this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
         }
     }
     setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 71d718b39..97d858f58 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,9 +1,9 @@
-.imageBox-cont {
+.imageBox-cont, .imageBox-cont-interactive {
     padding: 0vw;
-    position: relative;
+    position: absolute;
     text-align: center;
     width: 100%;
-    height: auto;
+    height: 100%;
     max-width: 100%;
     max-height: 100%;
     pointer-events: none;
@@ -11,8 +11,6 @@
 
 .imageBox-cont-interactive {
     pointer-events: all;
-    width: 100%;
-    height: auto;
 }
 
 .imageBox-dot {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a198a0764..9f39eccea 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -13,20 +13,21 @@ import { ComputedField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types';
 import { AudioField, ImageField } from '../../../new_fields/URLField';
 import { RouteStore } from '../../../server/RouteStore';
-import { Utils } from '../../../Utils';
+import { Utils, returnOne, emptyFunction } from '../../../Utils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../../views/ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent } from '../DocComponent';
+import { DocAnnotatableComponent } from '../DocComponent';
 import { InkingControl } from '../InkingControl';
 import { documentSchema } from './DocumentView';
 import FaceRectangles from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 var requestImageSize = require('../../util/request-image-size');
 var path = require('path');
 const { Howl } = require('howler');
@@ -54,9 +55,10 @@ type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
 const ImageDocument = makeInterface(pageSchema, documentSchema);
 
 @observer
-export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
+export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
 
-    public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
+    public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(ImageBox, "data", fieldExt); }
+    @observable static _showControls: boolean;
     private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
     private _downX: number = 0;
     private _downY: number = 0;
@@ -65,10 +67,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     private dropDisposer?: DragManager.DragDropDisposer;
     @observable private hoverActive = false;
 
-    @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
-
-    @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
-
     protected createDropTarget = (ele: HTMLDivElement) => {
         if (this.dropDisposer) {
             this.dropDisposer();
@@ -381,7 +379,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         return (null);
     }
 
-    render() {
+    @computed
+    get content() {
         // let transform = this.props.ScreenToLocalTransform().inverse();
         let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
         // var [sptX, sptY] = transform.transformPoint(0, 0);
@@ -393,7 +392,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
         // this._curSuffix = "";
         // if (w > 20) {
-        Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
         let alts = DocListCast(this.extensionDoc.Alternates);
         let altpaths: string[] = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
         let field = this.dataDoc[this.props.fieldKey];
@@ -448,4 +446,30 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                 <FaceRectangles document={this.extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
             </div>);
     }
+
+    render() {
+        Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
+        return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}>
+            <CollectionFreeFormView {...this.props}
+                PanelHeight={this.props.PanelHeight}
+                PanelWidth={this.props.PanelWidth}
+                focus={this.props.focus}
+                isSelected={this.props.isSelected}
+                select={emptyFunction}
+                active={this.active}
+                ContentScaling={returnOne}
+                whenActiveChanged={this.whenActiveChanged}
+                removeDocument={this.removeDocument}
+                moveDocument={this.moveDocument}
+                addDocument={this.addDocument}
+                CollectionView={undefined}
+                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                ruleProvider={undefined}
+                renderDepth={this.props.renderDepth + 1}
+                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                chromeCollapsed={true}>
+                {() => [this.content]}
+            </CollectionFreeFormView>
+        </div >);
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 57803be1f..9af6d7cad 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,17 +1,21 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, trace } from 'mobx';
+import { action, observable, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import 'react-image-lightbox/style.css';
-import { Doc, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Opt, WidthSym } from "../../../new_fields/Doc";
 import { makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast } from "../../../new_fields/Types";
 import { PdfField } from "../../../new_fields/URLField";
+import { Utils } from '../../../Utils';
 import { KeyCodes } from '../../northstar/utils/KeyCodes';
+import { undoBatch } from '../../util/UndoManager';
 import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
-import { DocComponent } from "../DocComponent";
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { DocAnnotatableComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
 import { PDFViewer } from "../pdf/PDFViewer";
 import { documentSchema } from "./DocumentView";
@@ -19,22 +23,17 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
-import { undoBatch } from '../../util/UndoManager';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { ContextMenu } from '../ContextMenu';
-import { Utils } from '../../../Utils';
 
 type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
 
 @observer
-export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
+export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) {
     public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(PDFBox, "data", fieldExt); }
     private _keyValue: string = "";
     private _valueValue: string = "";
     private _scriptValue: string = "";
     private _searchString: string = "";
-    private _isChildActive = false;
     private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title
     private _pdfViewer: PDFViewer | undefined;
     private _keyRef: React.RefObject<HTMLInputElement> = React.createRef();
@@ -46,9 +45,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
     @observable private _pageControls = false;
 
-    @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
-    @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
-
     componentDidMount() {
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
         if (pdfUrl instanceof PdfField) {
@@ -206,7 +202,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                     pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
                     isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
-                    fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} />
+                    fieldKey={this.props.fieldKey} extensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} />
                 {this.settingsPanel()}
             </div>);
     }
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index b3cd439aa..48623eaaf 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,8 +1,6 @@
-// .videoBox-container {
-//     .collectionfreeformview-container {
-//         mix-blend-mode: multiply;
-//     }
-// }
+.videoBox-container {
+    pointer-events: all;
+}
 
 .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen,
 .videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index feb067d8f..aa9b28118 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -11,7 +11,7 @@ import { Utils, emptyFunction, returnOne } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
-import { DocComponent } from "../DocComponent";
+import { DocAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { documentSchema } from "./DocumentView";
@@ -35,7 +35,7 @@ const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema);
 library.add(faVideo);
 
 @observer
-export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
+export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
     static _youtubeIframeCounter: number = 0;
     private _reactionDisposer?: IReactionDisposer;
     private _youtubeReactionDisposer?: IReactionDisposer;
@@ -43,16 +43,12 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     private _videoRef: HTMLVideoElement | null = null;
     private _youtubeIframeId: number = -1;
     private _youtubeContentCreated = false;
-    private _downX: number = 0;
-    private _downY: number = 0;
     private _isResetClick = 0;
-    private _isChildActive = false;
-    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
     @observable _forceCreateYouTubeIFrame = false;
-    @observable static _showControls: boolean;
     @observable _playTimer?: NodeJS.Timeout = undefined;
     @observable _fullScreen = false;
-    @observable public Playing: boolean = false;
+    @observable _playing = false;
+    @observable static _showControls: boolean;
     public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(VideoBox, "data", fieldExt); }
 
     public get player(): HTMLVideoElement | null {
@@ -72,7 +68,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @action public Play = (update: boolean = true) => {
-        this.Playing = true;
+        this._playing = true;
         update && this.player && this.player.play();
         update && this._youtubePlayer && this._youtubePlayer.playVideo();
         this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
@@ -85,7 +81,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @action public Pause = (update: boolean = true) => {
-        this.Playing = false;
+        this._playing = false;
         update && this.player && this.player.pause();
         update && this._youtubePlayer && this._youtubePlayer.pauseVideo && this._youtubePlayer.pauseVideo();
         this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
@@ -181,7 +177,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
             vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             this._reactionDisposer && this._reactionDisposer();
             this._reactionDisposer = reaction(() => this.Document.currentTimecode || 0,
-                time => !this.Playing && (vref.currentTime = time), { fireImmediately: true });
+                time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
         }
     }
 
@@ -218,7 +214,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div>Loading</div> :
-            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}
+            <video className={`${style}`} key="video" ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}
                 onPlay={() => this.Play()} onSeeked={this.updateTimecode} onPause={() => this.Pause()} onClick={e => e.preventDefault()}>
                 <source src={field.url.href} type="video/mp4" />
                 Not supported.
@@ -246,13 +242,13 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 this.Pause();
                 return;
             }
-            if (event.data === YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
-            if (event.data === YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
+            if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
+            if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
         });
         let onYoutubePlayerReady = (event: any) => {
             this._reactionDisposer && this._reactionDisposer();
             this._youtubeReactionDisposer && this._youtubeReactionDisposer();
-            this._reactionDisposer = reaction(() => this.Document.currentTimecode, () => !this.Playing && this.Seek(this.Document.currentTimecode || 0));
+            this._reactionDisposer = reaction(() => this.Document.currentTimecode, () => !this._playing && this.Seek(this.Document.currentTimecode || 0));
             this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
                 let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 iframe.style.pointerEvents = interactive ? "all" : "none";
@@ -278,22 +274,16 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         </div>,
         VideoBox._showControls ? (null) : [
             <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling})` }}>
-                <FontAwesomeIcon icon={this.Playing ? "pause" : "play"} size="lg" />
+                <FontAwesomeIcon icon={this._playing ? "pause" : "play"} size="lg" />
             </div>,
-            <div className="collecvideoBoxtionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling})` }}>
+            <div className="videoBox-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling})` }}>
                 F
             </div>
         ]]);
     }
 
     @action
-    onPlayDown = () => {
-        if (this.Playing) {
-            this.Pause();
-        } else {
-            this.Play();
-        }
-    }
+    onPlayDown = () => this._playing ? this.Pause() : this.Play()
 
     @action
     onFullDown = (e: React.PointerEvent) => {
@@ -342,97 +332,40 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let start = untracked(() => Math.round(this.Document.currentTimecode || 0));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.Document.nativeWidth || 640)} height={(this.Document.nativeHeight || 390)}
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`}
-        ></iframe>;
-    }
-
-
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => {
-        this._setPreviewCursor = func;
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
     }
 
     @action.bound
-    removeDocument(doc: Doc): boolean {
-        Doc.GetProto(doc).annotationOn = undefined;
-        //TODO This won't create the field if it doesn't already exist
-        let targetDataDoc = this.fieldExtensionDoc;
-        let targetField = this.props.fieldExt;
-        let value = Cast(targetDataDoc[targetField], 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);
-        index !== -1 && value.splice(index, 1);
-        return true;
-    }
-    // this is called with the document that was dragged and the collection to move it into.
-    // if the target collection is the same as this collection, then the move will be allowed.
-    // otherwise, the document being moved must be able to be removed from its container before
-    // moving it into the target.  
-    @action.bound
-    moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
-        if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
-            return true;
-        }
-        return this.removeDocument(doc) ? addDocument(doc) : false;
-    }
-
-    @action.bound
-    addDocument(doc: Doc): boolean {
+    addDocumentWithTimestamp(doc: Doc): boolean {
         Doc.GetProto(doc).annotationOn = this.props.Document;
         var curTime = NumCast(this.props.Document.currentTimecode, -1);
         curTime !== -1 && (doc.displayTimecode = curTime);
-        Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc);
-        return true;
-    }
-    whenActiveChanged = (isActive: boolean) => {
-        this._isChildActive = isActive;
-        this.props.whenActiveChanged(isActive);
-    }
-    active = () => {
-        return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
-    }
-    onClick = (e: React.MouseEvent) => {
-        this._setPreviewCursor &&
-            e.button === 0 &&
-            Math.abs(e.clientX - this._downX) < 3 &&
-            Math.abs(e.clientY - this._downY) < 3 &&
-            this._setPreviewCursor(e.clientX, e.clientY, false);
-    }
-
-    onPointerDown = (e: React.PointerEvent): void => {
-        this._downX = e.clientX;
-        this._downY = e.clientY;
-        if ((e.button !== 0 || e.altKey) && this.active()) {
-            this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
-        }
-    }
-    @computed get annotationLayer() {
-        return <CollectionFreeFormView {...this.props}
-            setPreviewCursor={this.setPreviewCursor}
-            PanelHeight={this.props.PanelHeight}
-            PanelWidth={this.props.PanelWidth}
-            focus={this.props.focus}
-            isSelected={this.props.isSelected}
-            select={emptyFunction}
-            active={this.active}
-            ContentScaling={returnOne}
-            whenActiveChanged={this.whenActiveChanged}
-            removeDocument={this.removeDocument}
-            moveDocument={this.moveDocument}
-            addDocument={this.addDocument}
-            CollectionView={undefined}
-            ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-            ruleProvider={undefined}
-            renderDepth={this.props.renderDepth + 1}
-            ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-            chromeCollapsed={true}>
-        </CollectionFreeFormView>
+        return Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc);
     }
 
     render() {
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
-        return (<div className={"videoBox-container"} onClick={this.onClick} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu}>
-            {this.youtubeVideoId ? this.youtubeContent : this.content}
-            {this.annotationLayer}
+        return (<div className={"videoBox-container"} onContextMenu={this.specificContextMenu}>
+            <CollectionFreeFormView {...this.props}
+                PanelHeight={this.props.PanelHeight}
+                PanelWidth={this.props.PanelWidth}
+                focus={this.props.focus}
+                isSelected={this.props.isSelected}
+                select={emptyFunction}
+                active={this.active}
+                ContentScaling={returnOne}
+                whenActiveChanged={this.whenActiveChanged}
+                removeDocument={this.removeDocument}
+                moveDocument={this.moveDocument}
+                addDocument={this.addDocumentWithTimestamp}
+                CollectionView={undefined}
+                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                ruleProvider={undefined}
+                renderDepth={this.props.renderDepth + 1}
+                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                chromeCollapsed={true}>
+                {() => [this.youtubeVideoId ? this.youtubeContent : this.content]}
+            </CollectionFreeFormView>
             {this.uIButtons}
         </div >);
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index bb4c5adc9..7c7f9fb83 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,34 +1,36 @@
+import { library } from "@fortawesome/fontawesome-svg-core";
+import { faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { FieldResult, Doc, Field } from "../../../new_fields/Doc";
+import { Doc, FieldResult } from "../../../new_fields/Doc";
 import { HtmlField } from "../../../new_fields/HtmlField";
+import { InkTool } from "../../../new_fields/InkField";
+import { makeInterface } from "../../../new_fields/Schema";
+import { Cast, NumCast } from "../../../new_fields/Types";
 import { WebField } from "../../../new_fields/URLField";
+import { emptyFunction, returnOne, Utils } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { SelectionManager } from "../../util/SelectionManager";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
+import { KeyValueBox } from "./KeyValueBox";
 import "./WebBox.scss";
 import React = require("react");
-import { InkTool } from "../../../new_fields/InkField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils } from "../../../Utils";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faStickyNote } from '@fortawesome/free-solid-svg-icons';
-import { observable, action, computed } from "mobx";
-import { listSpec } from "../../../new_fields/Schema";
-import { RefField } from "../../../new_fields/RefField";
-import { ObjectField } from "../../../new_fields/ObjectField";
-import { updateSourceFile } from "typescript";
-import { KeyValueBox } from "./KeyValueBox";
-import { setReactionScheduler } from "mobx/lib/internal";
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { SelectionManager } from "../../util/SelectionManager";
-import { Docs } from "../../documents/Documents";
+import { documentSchema } from "./DocumentView";
+import { DocAnnotatableComponent } from "../DocComponent";
 
 library.add(faStickyNote);
 
+type WebDocument = makeInterface<[typeof documentSchema]>;
+const WebDocument = makeInterface(documentSchema);
+
 @observer
-export class WebBox extends React.Component<FieldViewProps> {
+export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
 
-    public static LayoutString() { return FieldView.LayoutString(WebBox); }
+    public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(WebBox, "data", fieldExt); }
     @observable private collapsed: boolean = true;
     @observable private url: string = "";
 
@@ -162,8 +164,10 @@ export class WebBox extends React.Component<FieldViewProps> {
             e.stopPropagation();
         }
     }
-    render() {
-        let field = this.props.Document[this.props.fieldKey];
+
+    @computed
+    get content() {
+        let field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
             view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
@@ -189,4 +193,29 @@ export class WebBox extends React.Component<FieldViewProps> {
                 {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
             </>);
     }
+    render() {
+        Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
+        return (<div className={"imageBox-container"} >
+            <CollectionFreeFormView {...this.props}
+                PanelHeight={this.props.PanelHeight}
+                PanelWidth={this.props.PanelWidth}
+                focus={this.props.focus}
+                isSelected={this.props.isSelected}
+                select={emptyFunction}
+                active={this.active}
+                ContentScaling={returnOne}
+                whenActiveChanged={this.whenActiveChanged}
+                removeDocument={this.removeDocument}
+                moveDocument={this.moveDocument}
+                addDocument={this.addDocument}
+                CollectionView={undefined}
+                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                ruleProvider={undefined}
+                renderDepth={this.props.renderDepth + 1}
+                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                chromeCollapsed={true}>
+                {() => [this.content]}
+            </CollectionFreeFormView>
+        </div >);
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 5a07b88d9..ad6240c70 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;
-    fieldExtensionDoc: Doc;
+    extensionDoc: Doc;
     addDocTab: (document: Doc, dataDoc: Opt<Doc>, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     focus: (doc: Doc) => void;
@@ -29,7 +29,7 @@ interface IRegionAnnotationProps {
     y: number;
     width: number;
     height: number;
-    fieldExtensionDoc: Doc;
+    extensionDoc: Doc;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     document: Doc;
@@ -66,12 +66,12 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     }
 
     deleteAnnotation = () => {
-        let annotation = DocListCast(this.props.fieldExtensionDoc.annotations);
+        let annotation = DocListCast(this.props.extensionDoc.annotations);
         let group = FieldValue(Cast(this.props.document.group, Doc));
         if (group) {
             if (annotation.indexOf(group) !== -1) {
                 let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
-                this.props.fieldExtensionDoc.annotations = new List<Doc>(newAnnotations);
+                this.props.extensionDoc.annotations = new List<Doc>(newAnnotations);
             }
 
             DocListCast(group.annotations).forEach(anno => anno.delete = true);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 366861144..d0759d207 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -6,7 +6,7 @@ import { Dictionary } from "typescript-collections";
 import { Doc, DocListCast, FieldResult, WidthSym, Opt, HeightSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
+import { makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules } from "../../../Utils";
@@ -23,19 +23,24 @@ import Annotation from "./Annotation";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { SelectionManager } from "../../util/SelectionManager";
 import { undoBatch } from "../../util/UndoManager";
+import { DocAnnotatableComponent } from "../DocComponent";
+import { documentSchema } from "../nodes/DocumentView";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 
 pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
+type PdfDocument = makeInterface<[typeof documentSchema]>;
+const PdfDocument = makeInterface(documentSchema);
 
 interface IViewerProps {
     pdf: Pdfjs.PDFDocumentProxy;
     url: string;
-    Document: Doc;
-    DataDoc?: Doc;
-    fieldExtensionDoc: Doc;
     fieldKey: string;
     fieldExt: string;
+    Document: Doc;
+    DataDoc?: Doc;
+    ContainingCollectionView: Opt<CollectionView>;
+    extensionDoc: Doc;
     PanelWidth: () => number;
     PanelHeight: () => number;
     ContentScaling: () => number;
@@ -52,7 +57,6 @@ interface IViewerProps {
     addDocument?: (doc: Doc) => boolean;
     setPdfViewer: (view: PDFViewer) => void;
     ScreenToLocalTransform: () => Transform;
-    ContainingCollectionView: Opt<CollectionView>;
     whenActiveChanged: (isActive: boolean) => void;
 }
 
@@ -60,7 +64,7 @@ interface IViewerProps {
  * Handles rendering and virtualization of the pdf
  */
 @observer
-export class PDFViewer extends React.Component<IViewerProps> {
+export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument>(PdfDocument) {
     static _annotationStyle: any = addStyleSheet();
     @observable private _pageSizes: { width: number, height: number }[] = [];
     @observable private _annotations: Doc[] = [];
@@ -79,7 +83,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     private _pdfViewer: any;
     private _retries = 0; // number of times tried to create the PDF viewer 
-    private _isChildActive = false;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _reactionDisposer?: IReactionDisposer;
@@ -97,7 +100,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _coverPath: any;
 
     @computed get allAnnotations() {
-        return DocListCast(this.props.fieldExtensionDoc.annotations).filter(
+        return DocListCast(this.props.extensionDoc.annotations).filter(
             anno => this._script.run({ this: anno }, console.log, true).result);
     }
 
@@ -186,7 +189,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         await this.initialLoad();
 
         this._annotationReactionDisposer = reaction(
-            () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations),
+            () => this.props.extensionDoc && DocListCast(this.props.extensionDoc.annotations),
             annotations => annotations && annotations.length && this.renderAnnotations(annotations, true),
             { fireImmediately: true });
 
@@ -530,7 +533,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     highlight = (color: string) => {
         // creates annotation documents for current highlights
         let annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc);
+        annotationDoc && Doc.AddDocToList(this.props.extensionDoc, this.props.fieldExt, annotationDoc);
         return annotationDoc;
     }
 
@@ -570,40 +573,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
         DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0);
     }
 
-    // this is called with the document that was dragged and the collection to move it into.
-    // if the target collection is the same as this collection, then the move will be allowed.
-    // otherwise, the document being moved must be able to be removed from its container before
-    // moving it into the target.  
-    @action.bound
-    moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
-        if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
-            return true;
-        }
-        return this.removeDocument(doc) ? addDocument(doc) : false;
-    }
-    @action.bound
-    addDocument(doc: Doc): boolean {
-        Doc.GetProto(doc).annotationOn = this.props.Document;
-        Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc);
-        return true;
-    }
-    @action.bound
-    removeDocument(doc: Doc): boolean {
-        Doc.GetProto(doc).annotationOn = undefined;
-        let targetDataDoc = this.props.fieldExtensionDoc;
-        let targetField = this.props.fieldExt;
-        let value = Cast(targetDataDoc[targetField], 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);
-        index !== -1 && value.splice(index, 1);
-        return true;
-    }
     scrollXf = () => {
         return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform();
     }
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => {
-        this._setPreviewCursor = func;
-    }
     onClick = (e: React.MouseEvent) => {
         this._setPreviewCursor &&
             e.button === 0 &&
@@ -611,13 +583,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
             Math.abs(e.clientY - this._downY) < 3 &&
             this._setPreviewCursor(e.clientX, e.clientY, false);
     }
-    whenActiveChanged = (isActive: boolean) => {
-        this._isChildActive = isActive;
-        this.props.whenActiveChanged(isActive);
-    }
-    active = () => {
-        return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
-    }
+
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
+
 
     getCoverImage = () => {
         if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) {
@@ -632,7 +600,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
             style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />;
     }
 
-
     @action
     onZoomWheel = (e: React.WheelEvent) => {
         e.stopPropagation();
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 418863bcc..eb752f8c6 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -332,8 +332,10 @@ export namespace Doc {
         return Array.from(results);
     }
 
-    export function IndexOf(toFind: Doc, list: Doc[]) {
-        return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
+    export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) {
+        let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1);
+        index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1);
+        return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
     }
     export function RemoveDocFromList(listDoc: Doc, key: string, doc: Doc) {
         if (listDoc[key] === undefined) {
-- 
cgit v1.2.3-70-g09d2


From 3afceef2f8c2be1bddf67cecd97086a41ec6dc48 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 11 Oct 2019 16:47:42 -0400
Subject: changes to menu layouts

---
 src/client/documents/Documents.ts                  |   2 +
 src/client/views/GlobalKeyHandler.ts               |  30 +++++-
 src/client/views/Main.tsx                          |   5 -
 src/client/views/MainView.tsx                      | 108 +++++++++++++--------
 .../collections/CollectionMasonryViewFieldRow.tsx  |   2 +-
 .../CollectionStackingViewFieldColumn.tsx          |   4 +-
 src/client/views/nodes/ButtonBox.scss              |   2 +
 src/client/views/nodes/DragBox.tsx                 |   4 +-
 src/client/views/nodes/IconBox.tsx                 |   4 +-
 src/client/views/search/IconBar.tsx                |   2 +-
 src/client/views/search/IconButton.scss            |   2 +-
 .../authentication/models/current_user_utils.ts    |  73 +++++++++++---
 12 files changed, 167 insertions(+), 71 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7f6ab50d8..6b56fb443 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -74,6 +74,7 @@ export interface DocumentOptions {
     dropAction?: dropActionType;
     backgroundLayout?: string;
     chromeStatus?: string;
+    columnWidth?: number;
     fontSize?: number;
     curPage?: number;
     currentTimecode?: number;
@@ -83,6 +84,7 @@ export interface DocumentOptions {
     dockingConfig?: string;
     autoHeight?: boolean;
     dbDoc?: Doc;
+    icon?: string;
     // [key: string]: Opt<Field>;
 }
 
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 557b3e366..9d239d0bf 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -9,6 +9,7 @@ import { DictationManager } from "../util/DictationManager";
 import SharingManager from "../util/SharingManager";
 import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 import { Cast, PromiseValue } from "../../new_fields/Types";
+import { ScriptField } from "../../new_fields/ScriptField";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -162,12 +163,31 @@ export default class KeyManager {
                     }
                 }
                 break;
+            case "c":
+                if (MainView.Instance.flyoutWidth > 0) {
+                    MainView.Instance.flyoutWidth = 0;
+                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                } else {
+                    MainView.Instance.flyoutWidth = 400;
+                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                }
+                break;
+            case "l":
+                if (MainView.Instance.flyoutWidth > 0) {
+                    MainView.Instance.flyoutWidth = 0;
+                } else {
+                    MainView.Instance.flyoutWidth = 400;
+                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                }
+                break;
             case "f":
-                stopPropagation = false;
-                preventDefault = false;
-                MainView.Instance.isSearchVisible = !MainView.Instance.isSearchVisible;
-                MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0;
-                PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0)));
+                if (MainView.Instance.flyoutWidth > 0) {
+                    MainView.Instance.flyoutWidth = 0;
+                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                } else {
+                    MainView.Instance.flyoutWidth = 400;
+                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                }
                 break;
             case "o":
                 let target = SelectionManager.SelectedDocuments()[0];
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 3bd898ac0..a91a2b69e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -38,11 +38,6 @@ let swapDocs = async () => {
     if (info.id !== "__guest__") {
         // a guest will not have an id registered
         await CurrentUserUtils.loadUserDocument(info);
-        // updates old user documents to prevent chrome on tree view.
-        (await Cast(CurrentUserUtils.UserDocument.workspaces, Doc))!.chromeStatus = "disabled";
-        (await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))!.chromeStatus = "disabled";
-        (await Cast(CurrentUserUtils.UserDocument.sidebar, Doc))!.chromeStatus = "disabled";
-        CurrentUserUtils.UserDocument.chromeStatus = "disabled";
         await swapDocs();
     }
     document.getElementById('root')!.addEventListener('wheel', event => {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1ec21f638..6f60de1c4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -15,7 +15,7 @@ import { listSpec } from '../../new_fields/Schema';
 import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse, Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
 import { ClientUtils } from '../util/ClientUtils';
@@ -40,6 +40,8 @@ import { PreviewCursor } from './PreviewCursor';
 import { FilterBox } from './search/FilterBox';
 import { OverlayView } from './OverlayView';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
+import { CollectionStackingView } from './collections/CollectionStackingView';
+import { ScriptField } from '../../new_fields/ScriptField';
 
 @observer
 export class MainView extends React.Component {
@@ -459,33 +461,63 @@ export class MainView extends React.Component {
     @computed
     get flyout() {
         let sidebar: FieldResult<Field>;
-        if (!this.userDoc || !((sidebar = this.userDoc.sidebar) instanceof Doc)) {
+        if (!this.userDoc || !((sidebar = this.userDoc.sidebarContainer) instanceof Doc)) {
             return (null);
         }
-        return <DocumentView
-            Document={this.isSearchVisible ? Cast(CurrentUserUtils.UserDocument.searchBox, Doc) as Doc : sidebar}
-            DataDoc={undefined}
-            addDocument={undefined}
-            addDocTab={this.addDocTabFunc}
-            pinToPres={emptyFunction}
-            removeDocument={undefined}
-            ruleProvider={undefined}
-            onClick={undefined}
-            ScreenToLocalTransform={Transform.Identity}
-            ContentScaling={returnOne}
-            PanelWidth={this.flyoutWidthFunc}
-            PanelHeight={this.getPHeight}
-            renderDepth={0}
-            focus={emptyFunction}
-            backgroundColor={returnEmptyString}
-            parentActive={returnTrue}
-            whenActiveChanged={emptyFunction}
-            bringToFront={emptyFunction}
-            ContainingCollectionView={undefined}
-            ContainingCollectionDoc={undefined}
-            zoomToScale={emptyFunction}
-            getScale={returnOne}>
-        </DocumentView>;
+        (Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc).columnWidth = this.flyoutWidthFunc() / 3 - 30;
+        return <div style={{ display: "flex", flexDirection: "column", position: "absolute", width: "100%", height: "100%" }}>
+            <div style={{ position: "relative", height: "85px", width: "100%" }}>
+                <DocumentView
+                    Document={Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    removeDocument={undefined}
+                    ruleProvider={undefined}
+                    onClick={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
+                    ContentScaling={returnOne}
+                    PanelWidth={this.flyoutWidthFunc}
+                    PanelHeight={this.getPHeight}
+                    renderDepth={0}
+                    focus={emptyFunction}
+                    backgroundColor={returnEmptyString}
+                    parentActive={returnTrue}
+                    whenActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne}>
+                </DocumentView>
+            </div>
+            <div style={{ position: "relative", height: "calc(100% - 80px)", width: "100%" }}>
+                <DocumentView
+                    Document={this.isSearchVisible ? Cast(CurrentUserUtils.UserDocument.searchBox, Doc) as Doc : sidebar}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    removeDocument={undefined}
+                    ruleProvider={undefined}
+                    onClick={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
+                    ContentScaling={returnOne}
+                    PanelWidth={this.flyoutWidthFunc}
+                    PanelHeight={this.getPHeight}
+                    renderDepth={0}
+                    focus={emptyFunction}
+                    backgroundColor={returnEmptyString}
+                    parentActive={returnTrue}
+                    whenActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne}>
+                </DocumentView>
+            </div></div>;
     }
 
     @computed
@@ -493,7 +525,7 @@ export class MainView extends React.Component {
         if (!this.userDoc) {
             return (<div>{this.dockingContent}</div>);
         }
-        let sidebar = this.userDoc.sidebar;
+        let sidebar = this.userDoc.sidebarContainer;
         if (!(sidebar instanceof Doc)) {
             return (null);
         }
@@ -585,16 +617,16 @@ export class MainView extends React.Component {
         // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc | Promise<Doc>][] = [
-            [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
-            [React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode],
-            [React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
-            [React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
-            [React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
+            //[React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
+            //[React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode],
+            //[React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
+            //[React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
+            //[React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
             // [React.createRef<HTMLDivElement>(), "object-group", "Test Google Photos Search", googlePhotosSearch],
-            [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
+            //[React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
             //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
         ];
-        if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
+        //if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
 
         return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
 
@@ -603,7 +635,7 @@ export class MainView extends React.Component {
 
             <div id="add-options-content">
                 <ul id="add-options-list">
-                    <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li>
+                    {/* <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> */}
                     <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
                     <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
                     {btns.map(btn =>
@@ -612,7 +644,7 @@ export class MainView extends React.Component {
                                 <FontAwesomeIcon icon={btn[1]} size="sm" />
                             </button>
                         </div></li>)}
-                    <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
+                    {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
                     <li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
                         <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
                             <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
@@ -648,9 +680,7 @@ export class MainView extends React.Component {
     @observable isSearchVisible = false;
     @action.bound
     toggleSearch = () => {
-        this.isSearchVisible = !MainView.Instance.isSearchVisible;
-        MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0;
-        PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0)));
+        PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
     }
 
     @computed private get dictationOverlay() {
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 6251d7114..1709b9c99 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -353,7 +353,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             </div > : (null);
         const background = this._background; //to account for observables in Measure
         const collapsed = this.collapsed;
-        let chromeStatus = this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus;
+        let chromeStatus = this.props.parent.props.Document.chromeStatus;
         return (
             <Measure offset onResize={this.handleResize}>
                 {({ measureRef }) => {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 815b28586..7e54b0f29 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -335,11 +335,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                 </div>
             </div> : (null);
         for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
-        let chromeStatus = this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus;
+        let chromeStatus = this.props.parent.props.Document.chromeStatus;
         return (
             <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
-                {headingView}
+                {this.props.parent.Document.hideHeadings ? (null) : headingView}
                 {
                     this.collapsed ? (null) :
                         <div>
diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss
index 75a790667..e8a3d1479 100644
--- a/src/client/views/nodes/ButtonBox.scss
+++ b/src/client/views/nodes/ButtonBox.scss
@@ -13,6 +13,8 @@
     border-radius: inherit;
     text-align: center;
     display: table;
+    overflow: hidden;
+    text-overflow: ellipsis;
 }
 .buttonBox-mainButtonCenter {
     height: 100%;
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 6c3db18c4..8429382e3 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -52,7 +52,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             e.stopPropagation();
             e.preventDefault();
             let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
-            let doc = (res as Doc) ||  Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
+            let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
             DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
         }
         e.stopPropagation();
@@ -93,7 +93,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
 
     render() {
         return (<div className="dragBox-outerDiv" onContextMenu={this.onContextMenu} onPointerDown={this.onDragStart} ref={this._mainCont}>
-            <FontAwesomeIcon className="dragBox-icon" icon="folder" size="lg" color="white" />
+            <FontAwesomeIcon className="dragBox-icon" icon={this.props.Document.icon as any} size="lg" color="white" />
         </div>);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index f3adade58..4971f61b7 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -26,6 +26,8 @@ library.add(faFilm, faTag, faTextHeight);
 export class IconBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(IconBox); }
 
+    @observable _panelWidth: number = 0;
+    @observable _panelHeight: number = 0;
     @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
     @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
 
@@ -69,8 +71,6 @@ export class IconBox extends React.Component<FieldViewProps> {
             cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
         }
     }
-    @observable _panelWidth: number = 0;
-    @observable _panelHeight: number = 0;
     render() {
         let label = this.props.Document.hideLabel ? "" : this.props.Document.title;
         return (
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx
index bdeb57d5c..cff397407 100644
--- a/src/client/views/search/IconBar.tsx
+++ b/src/client/views/search/IconBar.tsx
@@ -60,7 +60,7 @@ export class IconBar extends React.Component {
         return (
             <div className="icon-bar">
                 {FilterBox.Instance._allIcons.map((type: string) =>
-                    <IconButton type={type} />
+                    <IconButton key={type.toString()} type={type} />
                 )}
             </div>
         );
diff --git a/src/client/views/search/IconButton.scss b/src/client/views/search/IconButton.scss
index d1853177e..4a3107676 100644
--- a/src/client/views/search/IconButton.scss
+++ b/src/client/views/search/IconButton.scss
@@ -35,7 +35,7 @@
         text-align: center;
         height: 15px;
         margin-top: 5px;
-        opacity: 0;
+        opacity: 1;
         -webkit-transition: all 0.2s ease-in-out;
         -moz-transition: all 0.2s ease-in-out;
         -o-transition: all 0.2s ease-in-out;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index f9e3dc479..e1bb91838 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -12,6 +12,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { Cast, StrCast, PromiseValue } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
 import { RouteStore } from "../../RouteStore";
+import { ScriptField } from "../../../new_fields/ScriptField";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -97,24 +98,70 @@ export class CurrentUserUtils {
             curPresentation.boxShadow = "0 0";
             doc.curPresentation = curPresentation;
         }
-        if (doc.searchBox === undefined) {
+
+        if (doc.Library === undefined) {
+            let Search = Docs.Create.ButtonDocument({ width: 50, height: 35, title: "Search" });
+            let Library = Docs.Create.ButtonDocument({ width: 50, height: 35, title: "Library" });
+            let Create = Docs.Create.ButtonDocument({ width: 35, height: 35, title: "Create" });
+            if (doc.sidebarContainer === undefined) {
+                doc.sidebarContainer = new Doc();
+                (doc.sidebarContainer as Doc).chromeStatus = "disabled";
+            }
+
+            const library = Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Library" });
+            library.forceActive = true;
+            library.lockedPosition = true;
+            library.gridGap = 5;
+            library.xMargin = 5;
+            library.yMargin = 5;
+            library.boxShadow = "1 1 3";
+            library.workspaceLibrary = true; // flag that this is the document that shows the Notifications button when documents are shared
+            Library.targetContainer = doc.sidebarContainer;
+            Library.library = library;
+            Library.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.library");
+
             const searchBox = Docs.Create.QueryDocument({ title: "Searching" });
             searchBox.boxShadow = "0 0";
             searchBox.ignoreClick = true;
-            doc.searchBox = searchBox;
-        }
+            Search.searchBox = searchBox;
+            Search.targetContainer = doc.sidebarContainer;
+            Search.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.searchBox");
 
-        if (doc.sidebar === undefined) {
-            const sidebar = Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
-            sidebar.forceActive = true;
-            sidebar.lockedPosition = true;
-            sidebar.gridGap = 5;
-            sidebar.xMargin = 5;
-            sidebar.yMargin = 5;
-            sidebar.boxShadow = "1 1 3";
-            sidebar.workspaceLibrary = true; // flag that this is the document that shows the Notifications button when documents are shared
-            doc.sidebar = sidebar;
+            let createCollection = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Collection", icon: "folder" });
+            let createWebPage = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Web Page", icon: "globe-asia" });
+            createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })');
+            let createCatImage = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Image", icon: "cat" });
+            createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })');
+            let createButton = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Button", icon: "bolt" });
+            createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })');
+            let createPresentation = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Presentation", icon: "tv" });
+            createPresentation.onDragStart = ScriptField.MakeFunction('Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" })');
+            let createFolderImport = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Import Folder", icon: "cloud-upload-alt" });
+            createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })');
+            const creators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, height: 50, columnWidth: 35, chromeStatus: "disabled", title: "buttons" });
+            Create.targetContainer = doc.sidebarContainer;
+            Create.creators = creators;
+            Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators");
+
+            const buttons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" });
+            buttons.sectionFilter = "title";
+            buttons.boxShadow = "0 0";
+            buttons.ignoreClick = true;
+            buttons.hideHeadings = true;
+            doc.libraryButtons = buttons;
+
+            doc.Library = Library;
+            doc.Create = Create;
+            doc.Search = Search;
+            (Library.onClick as ScriptField).script.run({ this: Library });
+            //(doc.sidebarContainer as Doc).proto = library;
         }
+        PromiseValue(Cast(doc.libraryButtons, Doc)).then(libraryButtons => {
+            if (libraryButtons) {
+                libraryButtons.backgroundColor = "lightgrey";
+            }
+        });
+
         PromiseValue(Cast(doc.sidebar, Doc)).then(sidebar => {
             if (sidebar) {
                 sidebar.backgroundColor = "lightgrey";
-- 
cgit v1.2.3-70-g09d2


From 4d02c9b581a22da777232124f2b1a96f8e342285 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 21 Oct 2019 15:45:51 -0400
Subject: initial simplification to fieldExt

---
 src/client/views/DocComponent.tsx                  | 20 +++---
 .../views/collections/CollectionBaseView.tsx       |  4 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +-
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../CollectionStackingViewFieldColumn.tsx          |  4 +-
 src/client/views/collections/CollectionSubView.tsx |  3 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../views/collections/CollectionViewChromes.tsx    | 72 ++--------------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 13 ++--
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +-
 src/client/views/nodes/AudioBox.tsx                |  1 -
 src/client/views/nodes/ColorBox.tsx                | 19 +-----
 src/client/views/nodes/DocumentView.tsx            |  2 +
 src/client/views/nodes/FormattedTextBox.tsx        |  6 --
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/nodes/VideoBox.tsx                |  5 +-
 src/client/views/nodes/WebBox.tsx                  |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  3 +-
 src/new_fields/Doc.ts                              | 69 +++++++++------------
 21 files changed, 74 insertions(+), 167 deletions(-)

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

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index ff149a9ac..1f9bdaac4 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -10,6 +10,8 @@ import { InkTool } from '../../new_fields/InkField';
 ///  DocComponents returns a generic base class for React views of document fields that are not interactive
 interface DocComponentProps {
     Document: Doc;
+    DataDoc?: Doc;
+    fieldKey: string;
 }
 export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends React.Component<P> {
@@ -18,6 +20,8 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
         get Document(): T {
             return schemaCtor(this.props.Document);
         }
+        @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc!) : Doc.GetProto(this.props.Document); }
+        @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
     }
     return Component;
 }
@@ -26,6 +30,8 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
 ///  DocStaticProps return a base class for React views of document fields that are interactive only when selected (e.g. ColorBox)
 interface DocStaticProps {
     Document: Doc;
+    DataDoc?: Doc;
+    fieldKey: string;
     isSelected: () => boolean;
     renderDepth: number;
 }
@@ -36,6 +42,8 @@ export function DocStaticComponent<P extends DocStaticProps, T>(schemaCtor: (doc
         get Document(): T {
             return schemaCtor(this.props.Document);
         }
+        @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc!) : Doc.GetProto(this.props.Document); }
+        @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
         active = () => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected() || this.props.renderDepth === 0);//  && !InkingControl.Instance.selectedTool;  // bcz: inking state shouldn't affect static tools 
     }
     return Component;
@@ -54,12 +62,15 @@ interface DocAnnotatableProps {
 }
 export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends React.Component<P> {
+        _isChildActive = false;
         //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);
         }
-        _isChildActive = false;
+        @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
+        @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
         @action.bound
         removeDocument(doc: Doc): boolean {
             Doc.GetProto(doc).annotationOn = undefined;
@@ -67,23 +78,18 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
             let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
             return index !== -1 && value.splice(index, 1) ? true : false;
         }
-
-        @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
-
-        @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
-
         // if the moved document is already in this overlay collection nothing needs to be done.
         // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. 
         @action.bound
         moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
             return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false;
         }
-
         @action.bound
         addDocument(doc: Doc): boolean {
             Doc.GetProto(doc).annotationOn = this.props.Document;
             return Doc.AddDocToList(this.extensionDoc, this.props.fieldExt, doc);
         }
+
         whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
         active = () => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) &&
             (this.props.Document.forceActive || this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0) ? true : false)
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 1ade44250..fa543cc01 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -97,9 +97,11 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     addDocument(doc: Doc): boolean {
-        let targetDataDoc = this.props.Document;
+        let targetDataDoc = Doc.GetProto(this.props.Document);
         let targetField = this.props.fieldKey;
         Doc.AddDocToList(targetDataDoc, targetField, doc);
+        let extension = Doc.fieldExtensionDoc(targetDataDoc, targetField);
+        extension && (extension.lastModified = new DateField(new Date(Date.now())));
         Doc.GetProto(doc).lastOpened = new DateField;
         return true;
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 23e070750..04bc550ea 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -158,6 +158,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             <CollectionSchemaPreview
                 Document={layoutDoc}
                 DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
+                fieldKey={this.props.fieldKey}
                 childDocs={this.childDocs}
                 renderDepth={this.props.renderDepth}
                 ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
@@ -226,7 +227,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
 
     render() {
-        Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
         return (
             <div className="collectionSchemaView-container" style={{ height: "100%", marginTop: "0", }}>
                 <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
@@ -898,6 +898,7 @@ interface CollectionSchemaPreviewProps {
     childDocs?: Doc[];
     renderDepth: number;
     fitToBox?: boolean;
+    fieldKey: string;
     PanelWidth: () => number;
     PanelHeight: () => number;
     ruleProvider: Doc | undefined;
@@ -993,6 +994,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                     <DocumentView {...this.props}
                         DataDoc={this.props.DataDocument}
                         Document={this.props.Document}
+                        fieldKey={this.props.fieldKey}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
                         ruleProvider={this.props.ruleProvider}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 1bb2d2f46..5fb0c0f9f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -170,6 +170,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return <CollectionSchemaPreview
             Document={doc}
             DataDocument={dataDoc}
+            fieldKey={this.props.fieldKey}
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
             ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
@@ -389,7 +390,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             SetValue: this.addGroup,
             contents: "+ ADD A GROUP"
         };
-        Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
         let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
         if (this.sectionFilter) {
             let entries = Array.from(this.Sections.entries());
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7e54b0f29..fec3d90b9 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -2,7 +2,7 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faPalette } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, trace } from "mobx";
+import { action, observable, trace, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
@@ -204,7 +204,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             document.removeEventListener("pointerup", this.pointerUp);
             document.addEventListener("pointerup", this.pointerUp);
         }
-        this._createAliasSelected = false;
+        runInAction(() => this._createAliasSelected = false);
     }
 
     renderColorPicker = () => {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8bd67b880..43147ed20 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -33,6 +33,7 @@ export interface CollectionViewProps extends FieldViewProps {
     VisibleHeight?: () => number;
     chromeCollapsed: boolean;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+    fieldKey: string;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
@@ -77,7 +78,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         // to its children which may be templates.
         // The name of the data field comes from fieldExt if it's an extension, or fieldKey otherwise.
         @computed get dataField() {
-            return Doc.fieldExtensionDoc(this.props.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt)[this.props.fieldExt || this.props.fieldKey];
+            return this.props.fieldExt ? this.extensionDoc[this.props.fieldExt] : this.dataDoc[this.props.fieldKey];
         }
 
         get childLayoutPairs() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c1b7f7e48..2cad41acb 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -317,6 +317,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 <CollectionSchemaPreview
                     Document={layoutDoc}
                     DataDocument={this.templateDataDoc}
+                    fieldKey={this.fieldKey}
                     renderDepth={this.props.renderDepth}
                     showOverlays={this.noOverlays}
                     ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
@@ -556,7 +557,6 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     }
 
     render() {
-        Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
         let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
         let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
         let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4ca6df034..74a388425 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -149,7 +149,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
                 {this.SubView}
             </CollectionBaseView>
 
-            {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => Cast(d.data, ImageField)!.url!.href))}
+            {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => Cast(d.data, ImageField)!.url.href))}
         </>
         );
     }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index a5b7f0181..dd5e630e4 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -480,12 +480,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
     getKeySuggestions = async (value: string): Promise<string[]> => {
         value = value.toLowerCase();
-        let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
-            = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
-        if (typeof docs === "function") {
-            docs = docs();
-        }
-        docs = await docs;
+        let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
         if (docs instanceof Doc) {
             return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
         } else {
@@ -591,19 +586,9 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
         if (textwrappedRows.length) {
             this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]);
         } else {
-            let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
-                = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
-            if (typeof docs === "function") {
-                docs = docs();
-            }
-            docs = await docs;
-            if (docs instanceof Doc) {
-                let allRows = [docs[Id]];
-                this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
-            } else {
-                let allRows = docs.map(doc => doc[Id]);
-                this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
-            }
+            let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+            let allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+            this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
         }
     }
 
@@ -638,63 +623,14 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
 
 @observer
 export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
-    @observable private _currentKey: string = "";
-    @observable private suggestions: string[] = [];
 
     @computed private get descending() { return Cast(this.props.CollectionView.props.Document.sortAscending, "boolean", null); }
-    @computed get sectionFilter() { return StrCast(this.props.CollectionView.props.Document.sectionFilter); }
-
-    getKeySuggestions = async (value: string): Promise<string[]> => {
-        value = value.toLowerCase();
-        let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
-            = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
-        if (typeof docs === "function") {
-            docs = docs();
-        }
-        docs = await docs;
-        if (docs instanceof Doc) {
-            return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
-        } else {
-            const keys = new Set<string>();
-            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-            return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
-        }
-    }
-
-    @action
-    onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
-        this._currentKey = newValue;
-    }
-
-    getSuggestionValue = (suggestion: string) => suggestion;
-
-    renderSuggestion = (suggestion: string) => {
-        return <p>{suggestion}</p>;
-    }
-
-    onSuggestionFetch = async ({ value }: { value: string }) => {
-        const sugg = await this.getKeySuggestions(value);
-        runInAction(() => {
-            this.suggestions = sugg;
-        });
-    }
-
-    @action
-    onSuggestionClear = () => {
-        this.suggestions = [];
-    }
-
-    setValue = (value: string) => {
-        this.props.CollectionView.props.Document.sectionFilter = value;
-        return true;
-    }
 
     @action toggleSort = () => {
         if (this.props.CollectionView.props.Document.sortAscending) this.props.CollectionView.props.Document.sortAscending = undefined;
         else if (this.props.CollectionView.props.Document.sortAscending === undefined) this.props.CollectionView.props.Document.sortAscending = false;
         else this.props.CollectionView.props.Document.sortAscending = true;
     }
-    @action resetValue = () => { this._currentKey = this.sectionFilter; };
 
     render() {
         return (
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 430ba582b..33d6b1358 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -113,10 +113,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
     }
 
-    @computed get fieldExtensionDoc() {
-        return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey);
-    }
-
     @action
     onDrop = (e: React.DragEvent): Promise<void> => {
         var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
@@ -309,7 +305,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
                     [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
                 }, [[minx, maxx], [miny, maxy]]);
-                let ink = Cast(this.fieldExtensionDoc.ink, InkField);
+                let ink = Cast(this.extensionDoc.ink, InkField);
                 if (ink && ink.inkData) {
                     ink.inkData.forEach((value: StrokeData, key: string) => {
                         let bounds = InkingCanvas.StrokeRect(value);
@@ -605,9 +601,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     analyzeStrokes = async () => {
-        let data = Cast(this.fieldExtensionDoc.ink, InkField);
+        let data = Cast(this.extensionDoc.ink, InkField);
         if (data) {
-            CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.fieldExtensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
+            CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.extensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
         }
     }
 
@@ -680,7 +676,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
         // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
         // otherwise, they are stored in fieldKey.  All annotations to this document are stored in the extension document
-        Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey);
         return (
             <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
                 style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
@@ -690,7 +685,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
                     <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
-                        <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
+                        <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.extensionDoc} inkFieldKey={"ink"} >
                             {this.childViews}
                         </InkingCanvas>
                         <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 743055875..4ff70daba 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -247,12 +247,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     get ink() { // ink will be stored on the extension doc for the field (fieldKey) where the container's data is stored.
         let cprops = this.props.container.props;
-        return Cast(Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink, InkField);
+        return Cast(this.props.container.extensionDoc.ink, InkField);
     }
 
     set ink(value: InkField | undefined) {
         let cprops = this.props.container.props;
-        Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink = value;
+        this.props.container.extensionDoc.ink = value;
     }
 
     @undoBatch
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 689d44a2f..3e5deb55b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -7,7 +7,6 @@ import { AudioField } from "../../../new_fields/URLField";
 import { DocStaticComponent } from "../DocComponent";
 import { makeInterface } from "../../../new_fields/Schema";
 import { documentSchema } from "./DocumentView";
-import { InkingControl } from "../InkingControl";
 
 type AudioDocument = makeInterface<[typeof documentSchema]>;
 const AudioDocument = makeInterface(documentSchema);
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 30554ea36..fdcedb3a5 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -11,7 +11,6 @@ import { trace, reaction, observable, action, IReactionDisposer } from "mobx";
 import { SelectionManager } from "../../util/SelectionManager";
 import { StrCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { Doc } from "../../../new_fields/Doc";
 
 type ColorDocument = makeInterface<[typeof documentSchema]>;
 const ColorDocument = makeInterface(documentSchema);
@@ -19,8 +18,11 @@ const ColorDocument = makeInterface(documentSchema);
 @observer
 export class ColorBox extends DocStaticComponent<FieldViewProps, ColorDocument>(ColorDocument) {
     public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
+
     _selectedDisposer: IReactionDisposer | undefined;
     _penDisposer: IReactionDisposer | undefined;
+    @observable _startupColor = "black";
+
     componentDidMount() {
         this._selectedDisposer = reaction(() => SelectionManager.SelectedDocuments(),
             action(() => this._startupColor = SelectionManager.SelectedDocuments().length ? StrCast(SelectionManager.SelectedDocuments()[0].Document.backgroundColor, "black") : "black"),
@@ -28,27 +30,12 @@ export class ColorBox extends DocStaticComponent<FieldViewProps, ColorDocument>(
         this._penDisposer = reaction(() => CurrentUserUtils.ActivePen,
             action(() => this._startupColor = CurrentUserUtils.ActivePen ? StrCast(CurrentUserUtils.ActivePen.backgroundColor, "black") : "black"),
             { fireImmediately: true });
-
-        // compare to this reaction that used to be in Selection Manager
-        // reaction(() => manager.SelectedDocuments, sel => {
-        //     let targetColor = "#FFFFFF";
-        //     if (sel.length > 0) {
-        //         let firstView = sel[0];
-        //         let doc = firstView.props.Document;
-        //         let targetDoc = doc.isTemplateField ? doc : Doc.GetProto(doc);
-        //         let stored = StrCast(targetDoc.backgroundColor);
-        //         stored.length > 0 && (targetColor = stored);
-        //     }
-        //     InkingControl.Instance.updateSelectedColor(targetColor);
-        // }, { fireImmediately: true });
     }
     componentWillUnmount() {
         this._penDisposer && this._penDisposer();
         this._selectedDisposer && this._selectedDisposer();
     }
 
-    @observable _startupColor = "black";
-
     render() {
         return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
             onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a41a37e66..089ec77ba 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -67,6 +67,7 @@ library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCom
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    fieldKey: string;
     Document: Doc;
     DataDoc?: Doc;
     fitToBox?: boolean;
@@ -598,6 +599,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
             ContainingCollectionDoc={this.props.ContainingCollectionDoc}
             Document={this.props.Document}
+            fieldKey={this.props.fieldKey}
             fitToBox={this.props.fitToBox}
             addDocument={this.props.addDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f866e6f7e..020442bf3 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -44,7 +44,6 @@ import React = require("react");
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ContextMenu } from '../ContextMenu';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { siteVerification } from 'googleapis/build/src/apis/siteVerification';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -143,10 +142,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
 
-    @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
-
-    @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); }
-
     // 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
     @computed get layoutDoc(): Doc { return Doc.Layout(this.props.Document); }
@@ -1028,7 +1023,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground
             ? "none" : "all";
-        Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
         if (this.props.isSelected()) {
             FormattedTextBox._toolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
         }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 426b9cbb4..d80e222c2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -326,11 +326,11 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     }
 
     render() {
-        if (!Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey)) return (null);
         return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}>
             <CollectionFreeFormView {...this.props}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
+                fieldExt={"annotations"}
                 isAnnotationOverlay={true}
                 focus={this.props.focus}
                 isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 5795d6f12..78858731f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -229,7 +229,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
                     renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
                     Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
                     addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} focus={this.props.focus}
-                    pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
+                    pinToPres={this.props.pinToPres} addDocument={this.addDocument}
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
                     isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
                     fieldKey={this.props.fieldKey} extensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} />
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 20383bab1..5e8154233 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -322,7 +322,6 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
         InkingControl.Instance.switchTool(InkTool.None);
         this._isResetClick < 10 && (this.Document.currentTimecode = 0);
     }
-    @computed get fieldExtensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
     @computed get dataDoc() { return this.props.DataDoc && this.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
 
     @computed get youtubeContent() {
@@ -337,14 +336,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
 
     @action.bound
     addDocumentWithTimestamp(doc: Doc): boolean {
-        Doc.GetProto(doc).annotationOn = this.props.Document;
         var curTime = (this.Document.currentTimecode || -1);
         curTime !== -1 && (doc.displayTimecode = curTime);
-        return Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc);
+        return this.addDocument(doc);
     }
 
     render() {
-        if (!Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey)) return (null);
         return (<div className={"videoBox-container"} onContextMenu={this.specificContextMenu}>
             <CollectionFreeFormView {...this.props}
                 PanelHeight={this.props.PanelHeight}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d5a1f4e73..907e71627 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -195,11 +195,11 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
             </>);
     }
     render() {
-        Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
         return (<div className={"imageBox-container"} >
             <CollectionFreeFormView {...this.props}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
+                fieldExt={"annotations"}
                 focus={this.props.focus}
                 isSelected={this.props.isSelected}
                 isAnnotationOverlay={true}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f3aaca471..361e58c65 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -46,7 +46,6 @@ interface IViewerProps {
     pdf: Pdfjs.PDFDocumentProxy;
     url: string;
     fieldKey: string;
-    fieldExt: string;
     Document: Doc;
     DataDoc?: Doc;
     ContainingCollectionView: Opt<CollectionView>;
@@ -550,7 +549,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     highlight = (color: string) => {
         // creates annotation documents for current highlights
         let annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && Doc.AddDocToList(this.props.extensionDoc, this.props.fieldExt, annotationDoc);
+        annotationDoc && this.props.addDocument && this.props.addDocument(annotationDoc);
         return annotationDoc;
     }
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 71b64d7c3..f60a2e720 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -398,44 +398,6 @@ export namespace Doc {
         return bounds;
     }
 
-    //
-    // Resolves a reference to a field by returning 'doc' if no field extension is specified,
-    // otherwise, it returns the extension document stored in doc.<fieldKey>_ext.
-    // This mechanism allows any fields to be extended with an extension document that can
-    // be used to capture field-specific metadata.  For example, an image field can be extended
-    // to store annotations, ink, and other data.
-    //
-    export function fieldExtensionDoc(doc: Doc, fieldKey: string, fieldExt: string = "yes") {
-        return fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc;
-    }
-
-    export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
-        let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
-        docExtensionForField.title = fieldKey + ".ext";
-        docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
-        docExtensionForField.type = DocumentType.EXTENSION;
-        let proto: Doc | undefined = doc;
-        while (proto && !Doc.IsPrototype(proto) && proto.proto) {
-            proto = proto.proto;
-        }
-        (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
-        return docExtensionForField;
-    }
-
-    export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string, immediate: boolean = false) {
-        let docExtensionForField = doc[fieldKey + "_ext"] as Doc;
-        if (docExtensionForField === undefined) {
-            if (immediate) {
-                CreateDocumentExtensionForField(doc, fieldKey);
-                return true;
-            }
-            else {
-                setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
-                return false;
-            }
-        }
-        return true;
-    }
     export function MakeTitled(title: string) {
         let doc = new Doc();
         doc.title = title;
@@ -490,13 +452,38 @@ export namespace Doc {
         let layoutDoc: Doc | undefined = childDocLayout;
         let resolvedDataDoc = !doc.isTemplateField && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
         if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
-            Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey);
-            let fieldExtensionDoc = Doc.fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy");
-            layoutDoc = Doc.expandTemplateLayout(childDocLayout, fieldExtensionDoc !== resolvedDataDoc ? fieldExtensionDoc : undefined);
+            let extensionDoc = fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)));
+            layoutDoc = Doc.expandTemplateLayout(childDocLayout, extensionDoc !== resolvedDataDoc ? extensionDoc : undefined);
         } else layoutDoc = childDocLayout;
         return { layout: layoutDoc, data: resolvedDataDoc };
     }
 
+    //
+    // Resolves a reference to a field by returning 'doc' if no field extension is specified,
+    // otherwise, it returns the extension document stored in doc.<fieldKey>_ext.
+    // This mechanism allows any fields to be extended with an extension document that can
+    // be used to capture field-specific metadata.  For example, an image field can be extended
+    // to store annotations, ink, and other data.
+    //
+    export function fieldExtensionDoc(doc: Doc, fieldKey: string) {
+        let extension = doc[fieldKey + "_ext"] as Doc;
+        (extension === undefined) && setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
+        return extension ? extension : doc;
+    }
+
+    export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
+        let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
+        docExtensionForField.title = fieldKey + ".ext";
+        docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
+        docExtensionForField.type = DocumentType.EXTENSION;
+        let proto: Doc | undefined = doc;
+        while (proto && !Doc.IsPrototype(proto) && proto.proto) {
+            proto = proto.proto;
+        }
+        (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
+        return docExtensionForField;
+    }
+
     export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
         Object.keys(doc).forEach(key => {
             const field = ProxyField.WithoutProxy(() => doc[key]);
-- 
cgit v1.2.3-70-g09d2


From 2786a2e4b33ff874f320697b89fecec3105df201 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 24 Oct 2019 22:55:57 -0400
Subject: fixed some stacking view layout.  added a miniHeader layout for
 stacks

---
 .../collections/CollectionMasonryViewFieldRow.tsx  | 74 ++++++++++++----------
 .../views/collections/CollectionSchemaView.scss    |  1 -
 .../views/collections/CollectionStackingView.scss  | 33 +++++++++-
 .../views/collections/CollectionStackingView.tsx   |  7 +-
 .../CollectionStackingViewFieldColumn.tsx          |  2 +-
 5 files changed, 77 insertions(+), 40 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index d885f558e..52ebfafd3 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -76,6 +76,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     rowDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createAliasSelected = false;
         if (de.data instanceof DragManager.DocumentDragData) {
+            (this.props.parent.Document.dropConverter instanceof ScriptField) &&
+                this.props.parent.Document.dropConverter.script.run({ dragData: de.data });
             let key = StrCast(this.props.parent.props.Document.sectionFilter);
             let castedValue = this.getValue(this._heading);
             de.data.droppedDocuments.forEach(d => d[key] = castedValue);
@@ -282,40 +284,44 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             toggle: this.toggleVisibility,
             color: this._color
         };
-        let headingView = this.props.headingObject ?
-            <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
-                <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
-                    title={evContents === `NO ${key.toUpperCase()} VALUE` ?
-                        `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
-                    style={{
-                        width: "100%",
-                        background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey",
-                        color: "grey"
-                    }}>
-                    {<EditableView {...headerEditableViewProps} />}
-                    {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
-                        <div className="collectionStackingView-sectionColor">
-                            <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
-                                <button className="collectionStackingView-sectionColorButton">
-                                    <FontAwesomeIcon icon="palette" size="lg" />
-                                </button>
-                            </ Flyout >
-                        </div>
-                    }
-                    <button className="collectionStackingView-sectionDelete" onClick={this.collapseSection}>
-                        <FontAwesomeIcon icon={this._collapsed ? "chevron-down" : "chevron-up"} size="lg" />
-                    </button>
-                    {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
-                        <div className="collectionStackingView-sectionOptions">
-                            <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
-                                <button className="collectionStackingView-sectionOptionButton">
-                                    <FontAwesomeIcon icon="ellipsis-v" size="lg" />
-                                </button>
-                            </Flyout>
-                        </div>
-                    }
-                </div>
-            </div > : (null);
+        let headingView = this.props.parent.props.Document.miniHeaders ?
+            <div className="collectionStackingView-miniHeader" style={{ width: "100%" }}>
+                {<EditableView {...headerEditableViewProps} />}
+            </div> :
+            this.props.headingObject ?
+                <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
+                    <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
+                        title={evContents === `NO ${key.toUpperCase()} VALUE` ?
+                            `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
+                        style={{
+                            width: "100%",
+                            background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey",
+                            color: "grey"
+                        }}>
+                        {<EditableView {...headerEditableViewProps} />}
+                        {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+                            <div className="collectionStackingView-sectionColor">
+                                <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
+                                    <button className="collectionStackingView-sectionColorButton">
+                                        <FontAwesomeIcon icon="palette" size="lg" />
+                                    </button>
+                                </ Flyout >
+                            </div>
+                        }
+                        <button className="collectionStackingView-sectionDelete" onClick={this.collapseSection}>
+                            <FontAwesomeIcon icon={this._collapsed ? "chevron-down" : "chevron-up"} size="lg" />
+                        </button>
+                        {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
+                            <div className="collectionStackingView-sectionOptions">
+                                <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
+                                    <button className="collectionStackingView-sectionOptionButton">
+                                        <FontAwesomeIcon icon="ellipsis-v" size="lg" />
+                                    </button>
+                                </Flyout>
+                            </div>
+                        }
+                    </div>
+                </div > : (null);
         const background = this._background; //to account for observables in Measure
         const collapsed = this._collapsed;
         let chromeStatus = this.props.parent.props.Document.chromeStatus;
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index e0cedc210..6a9392253 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -40,7 +40,6 @@
 
 .collectionSchemaView-previewRegion {
     position: relative;
-    background: $light-color;
     height: auto !important;
 
     .collectionSchemaView-previewDoc {
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index f5530ab26..29178b909 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -124,7 +124,38 @@
             background: red;
         }
     }
-
+    .collectionStackingView-miniHeader {
+        width: 100%;
+        .editableView-container-editing-oneLine {
+            min-height: 20px;
+            display: flex;
+            align-items: center;
+            flex-direction: row;
+        }
+        span::before , span::after{
+            content: "";
+            width: 50%;
+            border-top: dashed gray 1px;
+            position: relative;
+            display: inline-block;
+        }
+        span::before {
+            margin-right: 10px;
+        }
+        span::after{
+            margin-left: 10px;
+        }
+        span {
+            position: relative;
+            text-align: center;
+            white-space: nowrap;
+            overflow: visible;
+            width: 100%;
+            display: flex;
+            color:gray;
+            align-items: center;
+        }
+    }
     .collectionStackingView-sectionHeader {
         text-align: center;
         margin-left: 2px;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 96940acbc..7f0165ad4 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -44,7 +44,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @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; }
-    @computed get showAddAGroup() { return (this.sectionFilter && this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled')); }
+    @computed get showAddAGroup() { return (this.sectionFilter && (this.props.Document.chromeStatus !== 'view-mode' && this.props.Document.chromeStatus !== 'disabled')); }
     @computed get columnWidth() {
         return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
             this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250));
@@ -121,7 +121,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     return res;
                 } else {
                     let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
-                    return this.props.ContentScaling() * (sum + (this.Sections.size ? 85 : -15));
+                    return this.props.ContentScaling() * (sum + (this.Sections.size ? (this.props.Document.miniHeaders ? 20 : 85) : -15));
                 }
             }
             return -1;
@@ -363,7 +363,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     onToggle = (checked: Boolean) => {
-        this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus = checked ? "collapsed" : "view-mode");
+        this.props.Document.chromeStatus = checked ? "collapsed" : "view-mode";
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
@@ -393,6 +393,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             let entries = Array.from(this.Sections.entries());
             sections = entries.sort(this.sortFunc);
         }
+        console.log("NUM = " + this.numGroupColumns);
         return (
             <div className="collectionStackingMasonry-cont" >
                 <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index fec3d90b9..b9d334b10 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -295,7 +295,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                 style={{
                     width: (style.columnWidth) /
                         ((uniqueHeadings.length +
-                            ((this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
+                            ((this.props.parent.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
                 }}>
                 <div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
                 {/* the default bucket (no key value) has a tooltip that describes what it is.
-- 
cgit v1.2.3-70-g09d2

