From f86a5e2ad2d0a57e77bd5f43d109faff5cc50bda Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Tue, 21 Apr 2020 16:26:31 +0530
Subject: re-setup of grid view

---
 src/client/views/collections/CollectionView.tsx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 877e4355f..bedc928ad 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -46,6 +46,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
 import { Transform } from 'prosemirror-transform';
+import { CollectionGridView } from './collectionGrid/CollectionGridView';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -67,7 +68,8 @@ export enum CollectionViewType {
     Carousel = "carousel",
     Linear = "linear",
     Staff = "staff",
-    Map = "map"
+    Map = "map",
+    Grid = "grid"
 }
 
 export interface CollectionRenderProps {
@@ -174,6 +176,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
             case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); }
             case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />);
+            case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />);
             case CollectionViewType.Freeform:
             default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
         }
@@ -216,6 +219,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
             subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
             subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" });
+            subItems.push({ description: "Grid", event: () => this.props.Document._viewType = CollectionViewType.Grid, icon: "rainbow" });
             switch (this.props.Document._viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
-- 
cgit v1.2.3-70-g09d2


From 83d282a6b5e607e3c7dae4b5e5d37a8b458f81cc Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Sun, 24 May 2020 10:36:31 +0530
Subject: Added chrome to grid view and modified grid mechanism

---
 src/client/views/collections/CollectionView.tsx    |   1 +
 .../views/collections/CollectionViewChromes.scss   |  30 ++-
 .../views/collections/CollectionViewChromes.tsx    |  57 ++++-
 .../collectionGrid/CollectionGridView.tsx          | 248 ++++++++++++---------
 .../views/collections/collectionGrid/Grid.tsx      |   8 +
 .../views/nodes/ContentFittingDocumentView.tsx     |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   1 +
 7 files changed, 225 insertions(+), 122 deletions(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5b344813d..311e8d3a1 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -227,6 +227,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
         subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+        subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "border-all" });
         if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) {
             subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
         }
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index e4581eb46..9795a3a22 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -3,7 +3,7 @@
 
 .collectionViewChrome-cont {
     position: absolute;
-    width:100%;
+    width: 100%;
     opacity: 0.9;
     z-index: 9001;
     transition: top .5s;
@@ -33,7 +33,7 @@
                 outline-color: black;
             }
 
-            .collectionViewBaseChrome-button{
+            .collectionViewBaseChrome-button {
                 font-size: 75%;
                 text-transform: uppercase;
                 letter-spacing: 2px;
@@ -44,6 +44,7 @@
                 padding: 12px 10px 11px 10px;
                 margin-left: 10px;
             }
+
             .collectionViewBaseChrome-cmdPicker {
                 margin-left: 3px;
                 margin-right: 0px;
@@ -51,14 +52,16 @@
                 border: none;
                 color: grey;
             }
+
             .commandEntry-outerDiv {
                 pointer-events: all;
                 background-color: gray;
                 display: flex;
                 flex-direction: row;
+
                 .commandEntry-drop {
-                    color:white;
-                    width:25px;
+                    color: white;
+                    width: 25px;
                     margin-top: auto;
                     margin-bottom: auto;
                 }
@@ -72,15 +75,17 @@
                 pointer-events: all;
                 // margin-top: 10px;
             }
+
             .collectionViewBaseChrome-template,
             .collectionViewBaseChrome-viewModes {
                 display: grid;
                 background: rgb(238, 238, 238);
-                color:grey;
-                margin-top:auto;
-                margin-bottom:auto;
+                color: grey;
+                margin-top: auto;
+                margin-bottom: auto;
                 margin-left: 5px;
             }
+
             .collectionViewBaseChrome-viewModes {
                 margin-left: 25px;
             }
@@ -88,7 +93,7 @@
             .collectionViewBaseChrome-viewSpecs {
                 margin-left: 5px;
                 display: grid;
-                
+
                 .collectionViewBaseChrome-filterIcon {
                     position: relative;
                     display: flex;
@@ -202,7 +207,7 @@
             .collectionStackingViewChrome-pivotField,
             .collectionTreeViewChrome-pivotField {
                 color: white;
-                width:100%;
+                width: 100%;
                 min-width: 100px;
                 text-align: center;
                 background: rgb(238, 238, 238);
@@ -232,6 +237,10 @@
         .collectionTreeViewChrome-pivotField:hover {
             cursor: text;
         }
+
+        .collectionGridViewChrome-entryBox {
+            width: 50%;
+        }
     }
 }
 
@@ -292,8 +301,9 @@
     flex-direction: column;
     height: 40px;
 }
+
 .commandEntry-inputArea {
-    display:flex;
+    display: flex;
     flex-direction: row;
     width: 150px;
     margin: auto auto auto auto;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 5dc0b09ac..def20ae9b 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
-import { Doc, DocListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
 import { listSpec } from "../../../fields/Schema";
@@ -15,6 +15,8 @@ import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
 import { CollectionViewType } from "./CollectionView";
 import { CollectionView } from "./CollectionView";
 import "./CollectionViewChromes.scss";
+import { CollectionGridView } from "./collectionGrid/CollectionGridView";
+import HeightLabel from "./collectionMulticolumn/MultirowHeightLabel";
 const datepicker = require('js-datepicker');
 
 interface CollectionViewChromeProps {
@@ -205,6 +207,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
             case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
             case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
             case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
+            case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
             default: return null;
         }
     }
@@ -504,3 +507,55 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewChro
     }
 }
 
+/**
+ * Chrome for grid view.
+ */
+@observer
+export class CollectionGridViewChrome extends React.Component<CollectionViewChromeProps> {
+
+    /**
+     * Sets the value of `numCols` on the grid's Document to the value entered.
+     */
+    @action
+    onNumColsEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
+        if (e.key === "Enter" || e.key === "Tab") {
+            if (this.props.CollectionView.props.Document.numCols as number !== e.currentTarget.valueAsNumber) {
+                this.props.CollectionView.props.Document.numCols = e.currentTarget.valueAsNumber;
+                //this.props.CollectionView.forceUpdate(); // to be used if CollectionGridView is not an observer
+            }
+
+        }
+    }
+
+    /**
+     * Sets the value of `rowHeight` on the grid's Document to the value entered.
+     */
+    @action
+    onRowHeightEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
+        if (e.key === "Enter" || e.key === "Tab") {
+            if (this.props.CollectionView.props.Document.rowHeight as number !== e.currentTarget.valueAsNumber) {
+                this.props.CollectionView.props.Document.rowHeight = e.currentTarget.valueAsNumber;
+                //this.props.CollectionView.forceUpdate();
+            }
+        }
+    }
+
+    render() {
+        return (
+            <div className="collectionTreeViewChrome-cont">
+                <span className={"search-icon"}>
+                    <span className="icon-background">
+                        <FontAwesomeIcon icon="columns" size="1x" />
+                    </span>
+                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.numCols as string} onKeyDown={this.onNumColsEnter} autoFocus />
+                </span>
+                <span className={"search-icon"}>
+                    <span className="icon-background">
+                        <FontAwesomeIcon icon="text-height" size="1x" />
+                    </span>
+                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.rowHeight as string} onKeyDown={this.onRowHeightEnter} />
+                </span>
+            </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index cd1e0f7f0..ebb710af6 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,39 +1,58 @@
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
+import { computed, observable, action } from 'mobx';
 import * as React from "react";
-import { Doc, DataSym, DocListCast } from '../../../../new_fields/Doc';
-import { documentSchema } from '../../../../new_fields/documentSchemas';
-import { makeInterface } from '../../../../new_fields/Schema';
-import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../new_fields/Types';
+import { Doc, DocListCast } from '../../../../fields/Doc';
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { makeInterface, createSchema } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
 import { CollectionSubView } from '../CollectionSubView';
-import { List } from '../../../../new_fields/List';
+import { SubCollectionViewProps } from '../CollectionSubView';
+import { List } from '../../../../fields/List';
 import { returnZero } from '../../../../Utils';
-import Grid from "./Grid";
-import { Layout } from "./Grid";
+import Grid, { Layout } from "./Grid";
+import { Id } from '../../../../fields/FieldSymbols';
+import { observer } from 'mobx-react';
 
 
 type GridSchema = makeInterface<[typeof documentSchema]>;
 const GridSchema = makeInterface(documentSchema);
 
+@observer
 export class CollectionGridView extends CollectionSubView(GridSchema) {
 
-    private layouts: Layout[] = [];
-    private layoutDocs: Doc[] = [];
-    @observable private numCols: number = 10;
-    @observable private rowHeight: number = 100;
-    @observable private isMounted: boolean = false;
+    constructor(props: Readonly<SubCollectionViewProps>) {
+        super(props);
 
-    componentDidMount() {
-        this.isMounted = true;
+        this.props.Document.numCols = this.props.Document.numCols ? this.props.Document.numCols : 10;
+        this.props.Document.rowHeight = this.props.Document.rowHeight ? this.props.Document.rowHeight : 100;
     }
 
-    componentWillUnmount() {
-        this.isMounted = false;
-        console.log("hola");
+    componentDidMount() {
+        if (!(this.props.Document.gridLayouts as List<Doc>)?.length) {
+
+            console.log("no layouts stored on doc");
+
+            this.props.Document.gridLayouts = new List<Doc>();
+
+            for (let i = 0; i < this.childLayoutPairs.length; i++) {
+
+                const layoutDoc: Doc = new Doc();
+                layoutDoc.i = layoutDoc[Id];
+                layoutDoc.x = 2 * (i % 5);
+                layoutDoc.y = 2 * Math.floor(i / 5);
+                layoutDoc.w = 2;
+                layoutDoc.h = 2;
+
+                (this.props.Document.gridLayouts as List<Doc>).push(layoutDoc);
+
+                // use childlayoutpairs length instead 
+            }
+
+        }
+
     }
 
     /**
@@ -42,18 +61,26 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      * the sum of all the resolved column widths of the
      * documents before the target. 
      */
-    private lookupIndividualTransform = (layout: Layout) => {
+    private lookupIndividualTransform = (doc: Doc) => {
 
-        const yTranslation = this.rowHeight * layout.y;// + 15 * (layout.y - 1);
-        console.log(yTranslation);
-        return this.props.ScreenToLocalTransform().translate(-this.props.PanelWidth() / this.numCols * layout.x, -yTranslation);
+        const yTranslation = (this.props.Document.rowHeight as number) * (doc.y as number) + 10 * (doc.y as number);
+        return this.props.ScreenToLocalTransform().translate(-this.props.PanelWidth() / (this.props.Document.numCols as number) * (doc.x as number), -yTranslation);
     }
 
 
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
-    @observable private width = (layout: Layout) => layout.w * this.props.PanelWidth() / this.numCols;
-    @observable private height = (layout: Layout) => layout.h * this.rowHeight;
+    /**
+     * Sets the width of the decorating box.
+     * @param Doc doc
+     */
+    @observable private width = (doc: Doc) => doc.w as number * this.props.PanelWidth() / (this.props.Document.numCols as number);
+
+    /**
+     * Sets the height of the decorating box.
+     * @param doc `Doc`
+     */
+    @observable private height = (doc: Doc) => doc.h as number * (this.props.Document.rowHeight as number);
 
     addDocTab = (doc: Doc, where: string) => {
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
@@ -81,15 +108,23 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
             parentActive={this.props.active}
-        //Display={"contents"}
+            display={"contents"}
         />;
     }
 
-
     //@action
+    /**
+     * Saves the layouts received from the Grid to the Document.
+     * @param layouts `Layout[]`
+     */
     set layout(layouts: Layout[]) {
-        this.layouts = layouts;
-        this.props.Document.gridLayouts = new List<Doc>();
+
+        console.log("setting layout in CollectionGridView");
+        console.log(layouts?.[0].w);
+        //this.props.Document.gridLayouts = new List<Doc>();
+
+        const docList: Doc[] = [];
+
         for (const layout of layouts) {
             const layoutDoc = new Doc();
             layoutDoc.i = layout.i;
@@ -98,126 +133,119 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             layoutDoc.w = layout.w;
             layoutDoc.h = layout.h;
 
-            (this.props.Document.gridLayouts as List<Doc>).push(layoutDoc);
-            console.log("gazoinks");
-
+            docList.push(layoutDoc);
         }
-        this.forceUpdate(); // better way to do this?
+
+        this.props.Document.gridLayouts = new List<Doc>(docList);
     }
 
-    get layout() {
-        //console.log(this.layouts.length === 0);
-        if (this.layouts.length === 0) {
-            if (this.props.Document.gridLayouts) {
-                //console.log(this.props.Document.gridLayouts);
-                //     for (const layout of (this.props.Document.gridLayouts as List<Doc>)) {
-                //         if (layout instanceof Doc) {
-                //             this.layouts.push(
-                //                 { i: layout.i as string, x: layout.x as number, y: layout.y as number, w: layout.w as number, h: layout.h as number }
-                //             );
-                //         }
-                //         else {
-                //             layout.then((layout: Doc) => {
-                //                 this.layouts.push(
-                //                     { i: layout.i as string, x: layout.x as number, y: layout.y as number, w: layout.w as number, h: layout.h as number }
-                //                 );
-                //                 console.log(layout.i);
-                //             });
-                //         }
-                //     }
-                // }
-                for (const layout of DocListCast(this.props.Document.gridLayouts)) {
-                    this.layouts.push(
-                        { i: layout.i as string, x: layout.x as number, y: layout.y as number, w: layout.w as number, h: layout.h as number }
-                    );
-                }
-            }
-            else {
-                for (let i = 0; i < this.childLayoutPairs.length; i++) {
-                    this.layouts.push(
-                        { i: 'wrapper' + i, x: 2 * (i % 5), y: 2 * Math.floor(i / 5), w: 2, h: 2 }
-                    );
-
-                    const layoutDoc: Doc = new Doc();
-                    layoutDoc.i = "wrapper" + i;
-                    layoutDoc.x = 2 * (i % 5);
-                    layoutDoc.y = 2 * Math.floor(i / 5);
-                    layoutDoc.w = 2;
-                    layoutDoc.h = 2;
-
-                    this.layoutDocs.push(layoutDoc);
-                    this.props.Document.highest = i;
-                }
-                this.props.Document.gridLayouts = new List<Doc>(this.layoutDocs);
-            }
-        }
+    // _.reject() on item removal?
 
-        return this.layouts;
-    }
 
+    /**
+     * @returns a list of `ContentFittingDocumentView`s inside wrapper divs.
+     * The key of the wrapper div must be the same as the `i` value of the corresponding layout.
+     */
     @computed
-    private get contents(): [JSX.Element[], Layout[]] {
+    private get contents(): JSX.Element[] {
         const { childLayoutPairs } = this;
         const collector: JSX.Element[] = [];
-        const layoutArray: Layout[] = [];
+        //const layoutArray: Layout[] = [];
 
+        const docList: Doc[] = DocListCast(this.props.Document.gridLayouts);
 
-        const previousLength = this.layout.length;
-        layoutArray.push(...this.layout);
+        const previousLength = docList.length;
+        // layoutArray.push(...this.layout);
 
-        if (!layoutArray.length) {
-            return [[], []];
-        }
-
-        if (this.childLayoutPairs.length > previousLength) {
-            layoutArray.push(
-                { i: 'wrapper' + previousLength, x: 2 * (previousLength % 5), y: 2 * Math.floor(previousLength / 5), w: 2, h: 2 }
-                // add values to document
-            );
-            // this.layout.push(
-            //     { i: 'wrapper' + previousLength, x: 2 * (previousLength % 5), y: 2 * Math.floor(previousLength / 5), w: 2, h: 2 }
-            // );
+        if (!previousLength) {
+            // console.log("early return");
+            return [];
         }
 
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const dxf = () => this.lookupIndividualTransform(layoutArray[i]);//.translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin));
-            const width = () => this.width(layoutArray[i]);  //this.lookupPixels(layout);
-            const height = () => this.height(layoutArray[i]);//PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
+            const dxf = () => this.lookupIndividualTransform(docList?.[i]);
+            const width = () => this.width(docList?.[i]);
+            const height = () => this.height(docList?.[i]);
             collector.push(
                 <div className={"document-wrapper"}
-                    key={"wrapper" + i}
+                    key={docList?.[i].i as string}
                 >
                     {this.getDisplayDoc(layout, dxf, width, height)}
                 </div>
             );
         }
 
-        return [collector, layoutArray];
+        return collector;
+    }
+
+    /**
+     * @returns a list of Layouts from a list of Docs
+     * @param docLayoutList `Doc[]`
+     */
+    toLayoutList(docLayoutList: Doc[]): Layout[] {
+
+        const layouts: Layout[] = [];
+        for (const layout of docLayoutList) {
+            layouts.push(
+                { i: layout.i as string, x: layout.x as number, y: layout.y as number, w: layout.w as number, h: layout.h as number }
+            );
+        }
+        return layouts;
+    }
+
+    /**
+     * Checks whether a new node has been added to the grid and updates the Document accordingly.
+     */
+    checkUpdate() {
+        const previousLength = (this.props.Document.gridLayouts as List<Doc>)?.length;
+        if (this.childLayoutPairs.length > previousLength) {
+            console.log("adding doc");
+            const layoutDoc: Doc = new Doc();
+            layoutDoc.i = layoutDoc[Id];
+            layoutDoc.x = 2 * (previousLength % 5);
+            layoutDoc.y = 2 * Math.floor(previousLength / 5);
+            layoutDoc.w = 2;
+            layoutDoc.h = 2;
+
+            (this.props.Document.gridLayouts as List<Doc>).push(layoutDoc);
+        }
     }
 
     render(): JSX.Element {
 
-        const contents: JSX.Element[] = this.contents?.[0];
-        const layout: Layout[] = this.contents?.[1];
-        // if (this.isMounted) {
+        this.checkUpdate();
+
+        const contents: JSX.Element[] = this.contents;
+        const layout: Layout[] = this.toLayoutList(DocListCast(this.props.Document.gridLayouts));
+
+        if (layout.length === 0) {
+            console.log("layouts not loaded");
+        }
+        else {
+            console.log("rendering with this");
+            console.log(layout[0].w);
+        }
+
+
         return (
             <div className="collectionGridView_contents"
                 style={{
                     marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
                     marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
-                }} ref={this.createDashEventsTarget}>
-
+                }}
+                ref={this.createDashEventsTarget}
+                onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+            >
                 <Grid
                     width={this.props.PanelWidth()}
                     nodeList={contents}
                     layout={layout}
                     gridView={this}
-                    numCols={this.numCols}
-                    rowHeight={this.rowHeight}
+                    numCols={this.props.Document.numCols as number}
+                    rowHeight={this.props.Document.rowHeight as number}
                 />
             </div>
         );
-        // }
     }
 }
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index ce0173e94..d400f2810 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -20,9 +20,16 @@ interface GridProps {
     rowHeight: number;
 }
 
+/**
+ * Wrapper around the actual GridLayout of `react-grid-layout`.
+ */
 @observer
 export default class Grid extends React.Component<GridProps, GridLayout.ResponsiveProps> {
 
+    /**
+     * If there has been a change in layout, calls a method in CollectionGridView to set the layouts on the Document.
+     * @param layout `Layout[]`
+     */
     onLayoutChange(layout: Layout[]) {
         this.props.gridView.layout = layout;
     }
@@ -36,6 +43,7 @@ export default class Grid extends React.Component<GridProps, GridLayout.Responsi
                 width={this.props.width}
                 compactType={null}
                 isDroppable={true}
+                margin={[10, 10]}
                 onLayoutChange={layout => this.onLayoutChange(layout)}
             >
                 {this.props.nodeList}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 39097865a..77555061f 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -83,7 +83,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
         return (<div className="contentFittingDocumentView" style={{
             width: Math.abs(this.centeringYOffset) > 0.001 ? "auto" : this.props.PanelWidth(),
             height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(),
-            display: this.props.Display /* just added for grid */
+            display: this.props.display /* just added for grid */
         }}>
             {!this.props.Document || !this.props.PanelWidth ? (null) : (
                 <div className="contentFittingDocumentView-previewDoc"
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 340fa06a8..95af52f13 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -96,6 +96,7 @@ export interface DocumentViewProps {
     dontRegisterView?: boolean;
     layoutKey?: string;
     radialMenu?: String[];
+    display?: string;
 }
 
 @observer
-- 
cgit v1.2.3-70-g09d2


From c1e7d638b92c2484f54078866c0bc69ec69c77e4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 27 May 2020 10:31:31 -0400
Subject: fixed doc decorations and sizing of grid view docs.

---
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionGrid/CollectionGridView.tsx          | 52 +++++++++++-----------
 2 files changed, 28 insertions(+), 26 deletions(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index d1fbb445d..1beddfba9 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -92,7 +92,7 @@ export interface CollectionRenderProps {
 export class CollectionView extends Touchable<FieldViewProps & CollectionViewCustomProps> {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
 
-    private _isChildActive = false;   //TODO should this be observable?
+    _isChildActive = false;   //TODO should this be observable?
     get _isLightboxOpen() { return BoolCast(this.props.Document.isLightboxOpen); }
     set _isLightboxOpen(value) { this.props.Document.isLightboxOpen = value; }
     @observable private _curLightboxImg = 0;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 4986353ba..bc554e2c2 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -63,11 +63,13 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      * documents before the target. 
      */
     private lookupIndividualTransform = (doc: Doc) => {
-
-        const yTranslation = (this.props.Document.rowHeight as number) * (doc.y as number) + 10 * (doc.y as number);
-        return this.props.ScreenToLocalTransform().translate(-this.props.PanelWidth() / (this.props.Document.numCols as number) * (doc.x as number), -yTranslation);
+        const yTranslation = this.rowHeightPlusGap * NumCast(doc.y) + 10;
+        const xTranslation = this.colWidthPlusGap * NumCast(doc.x) + 10;
+        return this.props.ScreenToLocalTransform().translate(-xTranslation, -yTranslation);
     }
 
+    @computed get colWidthPlusGap() { return (this.props.PanelWidth() - 10) / NumCast(this.props.Document.numCols); }
+    @computed get rowHeightPlusGap() { return NumCast(this.props.Document.rowHeight) + 10; }
 
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
@@ -75,13 +77,13 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      * Sets the width of the decorating box.
      * @param Doc doc
      */
-    @observable private width = (doc: Doc) => doc.w as number * this.props.PanelWidth() / (this.props.Document.numCols as number);
+    @observable private width = (doc: Doc) => NumCast(doc.w) * this.colWidthPlusGap - 10;
 
     /**
      * Sets the height of the decorating box.
      * @param doc `Doc`
      */
-    @observable private height = (doc: Doc) => doc.h as number * (this.props.Document.rowHeight as number);
+    @observable private height = (doc: Doc) => NumCast(doc.h) * this.rowHeightPlusGap - 10;
 
     addDocTab = (doc: Doc, where: string) => {
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
@@ -92,25 +94,25 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
     }
 
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
-        return <ContentFittingDocumentView
-            {...this.props}
-            Document={layout}
-            DataDoc={layout.resolvedDataDoc as Doc}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
-            addDocTab={this.addDocTab}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
-            FreezeDimensions={BoolCast(this.props.Document._freezeChildDimensions)}
-            backgroundColor={this.props.backgroundColor}
-            ContainingCollectionDoc={this.props.Document}
-            PanelWidth={width}
-            PanelHeight={height}
-            ScreenToLocalTransform={dxf}
-            onClick={this.onChildClickHandler}
-            renderDepth={this.props.renderDepth + 1}
-            parentActive={this.props.active}
-            display={"contents"}
-        />;
+        return <div style={{ display: "flex" }}>
+            <ContentFittingDocumentView
+                {...this.props}
+                Document={layout}
+                DataDoc={layout.resolvedDataDoc as Doc}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                addDocTab={this.addDocTab}
+                backgroundColor={this.props.backgroundColor}
+                ContainingCollectionDoc={this.props.Document}
+                PanelWidth={width}
+                PanelHeight={height}
+                ScreenToLocalTransform={dxf}
+                onClick={this.onChildClickHandler}
+                renderDepth={this.props.renderDepth + 1}
+                parentActive={this.props.active}
+                display={"contents"}
+            />
+        </div>;
     }
 
 
@@ -238,7 +240,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                 style={{
                     marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
                     marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin),
-                    pointerEvents: !this.props.isSelected() && !SnappingManager.GetIsDragging() ? "none" : undefined
+                    pointerEvents: !this.props.isSelected() && !this.props.ContainingCollectionView?._isChildActive && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }}
                 ref={this.createDashEventsTarget}
                 onPointerDown={e => e.stopPropagation()}
-- 
cgit v1.2.3-70-g09d2


From 4cdd53c1208cd17593e0ecdb34e89265760a6512 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 4 Jun 2020 12:46:51 +0530
Subject: added numcols buttons to chrome + fixed some chrome css + added
 rowheight slider + rowheight slider css + fixed bug on document delete in
 freeform and reenter grid

---
 package-lock.json                                  | 84 +++++++++++++---------
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../views/collections/CollectionViewChromes.scss   |  7 ++
 .../views/collections/CollectionViewChromes.tsx    | 52 +++++++++++---
 .../collectionGrid/CollectionGridView.scss         | 47 ++++++++++--
 .../collectionGrid/CollectionGridView.tsx          | 72 +++++++++----------
 .../views/collections/collectionGrid/Grid.tsx      |  9 ++-
 7 files changed, 180 insertions(+), 93 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index adf71ee58..14b279ef6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2836,7 +2836,8 @@
                         },
                         "ansi-regex": {
                             "version": "2.1.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "aproba": {
                             "version": "1.2.0",
@@ -2854,11 +2855,13 @@
                         },
                         "balanced-match": {
                             "version": "1.0.0",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "brace-expansion": {
                             "version": "1.1.11",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "balanced-match": "^1.0.0",
                                 "concat-map": "0.0.1"
@@ -2871,15 +2874,18 @@
                         },
                         "code-point-at": {
                             "version": "1.1.0",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "concat-map": {
                             "version": "0.0.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "console-control-strings": {
                             "version": "1.1.0",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "core-util-is": {
                             "version": "1.0.2",
@@ -2982,7 +2988,8 @@
                         },
                         "inherits": {
                             "version": "2.0.4",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "ini": {
                             "version": "1.3.5",
@@ -2992,6 +2999,7 @@
                         "is-fullwidth-code-point": {
                             "version": "1.0.0",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "number-is-nan": "^1.0.0"
                             }
@@ -3004,17 +3012,20 @@
                         "minimatch": {
                             "version": "3.0.4",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "brace-expansion": "^1.1.7"
                             }
                         },
                         "minimist": {
                             "version": "1.2.5",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -3031,6 +3042,7 @@
                         "mkdirp": {
                             "version": "0.5.3",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "minimist": "^1.2.5"
                             }
@@ -3086,7 +3098,8 @@
                         },
                         "npm-normalize-package-bin": {
                             "version": "1.0.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "npm-packlist": {
                             "version": "1.4.8",
@@ -3111,7 +3124,8 @@
                         },
                         "number-is-nan": {
                             "version": "1.0.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "object-assign": {
                             "version": "4.1.1",
@@ -3121,6 +3135,7 @@
                         "once": {
                             "version": "1.4.0",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "wrappy": "1"
                             }
@@ -3189,7 +3204,8 @@
                         },
                         "safe-buffer": {
                             "version": "5.1.2",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "safer-buffer": {
                             "version": "2.1.2",
@@ -3219,6 +3235,7 @@
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -3236,6 +3253,7 @@
                         "strip-ansi": {
                             "version": "3.0.1",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "ansi-regex": "^2.0.0"
                             }
@@ -3274,11 +3292,13 @@
                         },
                         "wrappy": {
                             "version": "1.0.2",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         }
                     }
                 }
@@ -9500,7 +9520,7 @@
                 },
                 "chownr": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
                 },
                 "ci-info": {
@@ -9806,7 +9826,7 @@
                 },
                 "deep-extend": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
                 },
                 "defaults": {
@@ -10305,7 +10325,7 @@
                 },
                 "glob": {
                     "version": "7.1.6",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
                     "requires": {
                         "fs.realpath": "^1.0.0",
@@ -10393,7 +10413,7 @@
                 },
                 "hosted-git-info": {
                     "version": "2.8.8",
-                    "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
                 },
                 "http-cache-semantics": {
@@ -10529,7 +10549,7 @@
                 },
                 "is-ci": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
                     "requires": {
                         "ci-info": "^1.5.0"
@@ -10605,7 +10625,7 @@
                 },
                 "is-retry-allowed": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
                 },
                 "is-stream": {
@@ -11114,7 +11134,7 @@
                 },
                 "mkdirp": {
                     "version": "0.5.4",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
                     "requires": {
                         "minimist": "^1.2.5"
@@ -11122,7 +11142,7 @@
                     "dependencies": {
                         "minimist": {
                             "version": "1.2.5",
-                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                            "resolved": false,
                             "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
                         }
                     }
@@ -11174,7 +11194,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
                     "requires": {
                         "env-paths": "^2.2.0",
@@ -11288,7 +11308,7 @@
                 },
                 "npm-packlist": {
                     "version": "1.4.8",
-                    "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
                     "requires": {
                         "ignore-walk": "^3.0.1",
@@ -11308,7 +11328,7 @@
                 },
                 "npm-profile": {
                     "version": "4.0.4",
-                    "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
                     "requires": {
                         "aproba": "^1.1.2 || 2",
@@ -11318,7 +11338,7 @@
                 },
                 "npm-registry-fetch": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==",
                     "requires": {
                         "JSONStream": "^1.3.4",
@@ -11753,7 +11773,7 @@
                 },
                 "rc": {
                     "version": "1.2.8",
-                    "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
                     "requires": {
                         "deep-extend": "^0.6.0",
@@ -11764,7 +11784,7 @@
                     "dependencies": {
                         "minimist": {
                             "version": "1.2.5",
-                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                            "resolved": false,
                             "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
                         }
                     }
@@ -11823,7 +11843,7 @@
                 },
                 "readable-stream": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
                     "requires": {
                         "inherits": "^2.0.3",
@@ -11844,7 +11864,7 @@
                 },
                 "registry-auth-token": {
                     "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
                     "requires": {
                         "rc": "^1.1.6",
@@ -11908,7 +11928,7 @@
                 },
                 "rimraf": {
                     "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
                         "glob": "^7.1.3"
@@ -12207,7 +12227,7 @@
                 },
                 "string_decoder": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
                     "requires": {
                         "safe-buffer": "~5.2.0"
@@ -12215,7 +12235,7 @@
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
                         }
                     }
@@ -12527,7 +12547,7 @@
                 },
                 "widest-line": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
                     "requires": {
                         "string-width": "^2.1.1"
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bcb190de4..df84cab56 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -232,7 +232,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
         subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
-        subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "border-all" });
+        subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
         if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) {
             subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
         }
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 42ec35853..f6027471b 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -181,9 +181,16 @@
             .grid-control {
                 align-self: center;
                 width: 30%;
+                display: flex;
+                flex-direction: row;
 
                 .grid-icon {
                     margin-right: 5px;
+                    align-self: center;
+                }
+
+                .flexLabel {
+                    margin-bottom: 0;
                 }
             }
 
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 3d712e4f5..5a76a4605 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -93,7 +93,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                 this.props.collapse?.(true);
                 break;
         }
-    })
+    });
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
@@ -569,6 +569,9 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewChro
 @observer
 export class CollectionGridViewChrome extends React.Component<CollectionViewChromeProps> {
 
+    private clicked: boolean = false;
+    private entered: boolean = false;
+
     /**
      * Sets the value of `numCols` on the grid's Document to the value entered.
      */
@@ -577,7 +580,6 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
         if (e.key === "Enter" || e.key === "Tab") {
             if (e.currentTarget.valueAsNumber > 0 && this.props.CollectionView.props.Document.numCols as number !== e.currentTarget.valueAsNumber) {
                 this.props.CollectionView.props.Document.numCols = e.currentTarget.valueAsNumber;
-                //this.props.CollectionView.forceUpdate(); // to be used if CollectionGridView is not an observer
             }
 
         }
@@ -602,27 +604,59 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
         this.props.CollectionView.props.Document.flexGrid = !this.props.CollectionView.props.Document.flexGrid;
     }
 
+    onIncrementButtonClick = () => {
+        this.clicked = true;
+        this.entered && (this.props.CollectionView.props.Document.numCols as number)--;
+        undoBatch(() => (this.props.CollectionView.props.Document.numCols as number)++)();
+        this.entered = false;
+    }
+
+    onDecrementButtonClick = () => {
+        this.clicked = true;
+        this.entered && (this.props.CollectionView.props.Document.numCols as number)++;
+        undoBatch(() => (this.props.CollectionView.props.Document.numCols as number)--)();
+        this.entered = false;
+    }
+
+    incrementValue = () => {
+        this.entered = true;
+        if (!this.clicked) {
+            (this.props.CollectionView.props.Document.numCols as number)++;
+        }
+        this.clicked = false;
+    }
+
+    decrementValue = () => {
+        this.entered = true;
+        if (!this.clicked) {
+            (this.props.CollectionView.props.Document.numCols as number)--;
+        }
+        this.clicked = false;
+    }
+
     render() {
         return (
-            <div className="collectionGridViewChrome-cont">
-                <span className={"grid-control"}>
+            <div className="collectionGridViewChrome-cont" >
+                <span className="grid-control">
                     <span className="grid-icon">
                         <FontAwesomeIcon icon="columns" size="1x" />
                     </span>
-                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.numCols as string} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => e.currentTarget.focus()} />
+                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.numCols as string} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+                    <input className="columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
+                    <input className="columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
                 </span>
-                <span className={"grid-control"}>
+                <span className="grid-control">
                     <span className="grid-icon">
                         <FontAwesomeIcon icon="text-height" size="1x" />
                     </span>
-                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => e.currentTarget.focus()} />
+                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                 </span>
-                <span className={"grid-control"}>
+                <span className="grid-control">
                     <input style={{ marginRight: 5 }} type="checkbox" onClick={this.toggleFlex} defaultChecked={this.props.CollectionView.props.Document.flexGrid as boolean} />
                     <span className="grid-icon">
                         <FontAwesomeIcon icon="arrow-up" size="1x" />
                     </span>
-                    <label>Flexible</label>
+                    <label className="flexLabel">Flexible</label>
                 </span>
             </div>
         );
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 94b0d958c..e06e8dc90 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -9,6 +9,43 @@
         height: 100%;
         overflow-y: auto;
         background-color: white;
+        overflow-x: hidden;
+
+        display: flex;
+        flex-direction: row;
+
+        .rowHeightSlider {
+
+            -webkit-appearance: none;
+            appearance: none;
+            width: 100%;
+            height: 15px;
+            background: #d3d3d3;
+            // border-radius: 5px;
+
+            position: absolute;
+            height: 3;
+            left: 5;
+            top: 40;
+            transform-origin: left;
+            transform: rotate(90deg);
+            outline: none;
+            opacity: 0.7;
+        }
+
+        .rowHeightSlider:hover {
+            opacity: 1;
+        }
+
+        .rowHeightSlider::-webkit-slider-thumb {
+            -webkit-appearance: none;
+            appearance: none;
+            width: 10px;
+            height: 10px;
+            border-radius: 50%;
+            background: darkgrey;
+            opacity: 1;
+        }
     }
 
     .collectionGridView-addDocumentButton {
@@ -17,7 +54,8 @@
         margin: auto;
         width: 90%;
         cursor: text;
-
+        min-height: 30px;
+        max-height: 30px;
         font-size: 75%;
         letter-spacing: 2px;
 
@@ -31,15 +69,14 @@
 
         .editableView-container-editing,
         .editableView-container-editing-oneLine {
-            min-height: 20px;
             display: flex;
             align-items: center;
             flex-direction: row;
-            height: auto;
+            height: 20px;
 
+            width: 100%;
             color: grey;
             padding: 10px;
-            width: 100%;
 
             span::before,
             span::after {
@@ -67,8 +104,6 @@
                 align-items: center;
             }
         }
-
-
     }
 
 }
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index c56d6ee53..68612c9dc 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -3,7 +3,7 @@ import * as React from "react";
 import { Doc, Opt } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
 import { makeInterface } from '../../../../fields/Schema';
-import { BoolCast, NumCast, ScriptCast } from '../../../../fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
@@ -15,9 +15,9 @@ import Grid, { Layout } from "./Grid";
 import { Id } from '../../../../fields/FieldSymbols';
 import { observer } from 'mobx-react';
 import { SnappingManager } from '../../../util/SnappingManager';
-import "./CollectionGridView.scss";
 import { Docs } from '../../../documents/Documents';
 import { EditableView, EditableProps } from '../../EditableView';
+import "./CollectionGridView.scss";
 
 
 type GridSchema = makeInterface<[typeof documentSchema]>;
@@ -37,28 +37,38 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         this.props.Document.flexGrid = BoolCast(this.props.Document.flexGrid, true);
 
         this.setLayout = this.setLayout.bind(this);
-        // this.addTextDoc = this.addTextDoc.bind(this);
+        this.onSliderChange = this.onSliderChange.bind(this);
 
         this.containerRef = React.createRef();
     }
 
     componentDidMount() {
-
         this.changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => {
 
             const layouts: Layout[] = this.parsedLayoutList;
+
+            // if grid view has been opened and then exited and a document has been deleted
+            // this deletes the layout of that document from the layouts list
+            if (!oldValue) {
+                layouts.forEach(({ i }, index) => {
+                    const targetId = i;
+                    if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) {
+                        layouts.splice(index, 1);
+                    }
+                });
+            }
+
             if (!oldValue || newValue.length > oldValue.length) {
                 // for each document that was added, add a corresponding grid layout document
                 newValue.forEach(({ layout }, i) => {
                     const targetId = layout[Id];
                     if (!layouts.find((gridLayout: Layout) => gridLayout.i === targetId)) {
-                        console.log("pushing")
                         layouts.push({
                             i: targetId,
                             w: 2,
                             h: 2,
-                            x: 2 * (i % Math.floor(this.props.Document.numCols as number / 2)),
-                            y: 2 * Math.floor(i / Math.floor(this.props.Document.numCols as number / 2))
+                            x: 2 * (i % Math.floor(NumCast(this.props.Document.numCols) / 2)),
+                            y: 2 * Math.floor(i / Math.floor(NumCast(this.props.Document.numCols) / 2))
                         });
                     }
                 });
@@ -72,9 +82,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                     }
                 });
             }
-
             this.unStringifiedLayoutList = layouts;
-            // this.props.Document.gridLayoutString = JSON.stringify(layouts);
         }, true);
     }
 
@@ -82,23 +90,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         this.changeListenerDisposer && this.changeListenerDisposer();
     }
 
-    /**
-     * Establishes the x and y properties of the @param layoutDoc, currently
-     * using the @param previousLength for the computations.
-     * 
-     * However, this could also be more of a first fit algorithm, iterating through
-     * this.toLayoutList(DocListCast(this.props.Document.gridLayouts)) and finding the
-     * first gap in the layout structure that suits the width and height. It would be
-     * easiest to see that a column is free (for a given row, if two documents' x are separated
-     * by a value greater than the ratio width of the document you're trying to insert),
-     * but you would then have to ensure that the next row at that column has a y at least
-     * as big as the ratio height of the document you're trying to insert.  
-     */
-    private findNextLayout(layoutDoc: Doc, previousLength: number) {
-        layoutDoc.x = 2 * (previousLength % Math.floor(this.props.Document.numCols as number / 2));
-        layoutDoc.y = 2 * Math.floor(previousLength / Math.floor(this.props.Document.numCols as number / 2));
-    }
-
     /**
      * @returns the transform that will correctly place
      * the document decorations box, shifted to the right by
@@ -108,8 +99,8 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
     private lookupIndividualTransform = (layout: Layout) => {
 
         const index = this.childLayoutPairs.findIndex(({ layout: layoutDoc }) => layoutDoc[Id] === layout.i);
-        const yTranslation = (this.props.Document.flexGrid ? NumCast(layout.y) : 2 * Math.floor(index / Math.floor(this.props.Document.numCols as number / 2))) * this.rowHeightPlusGap + 10 - this._scroll;
-        const xTranslation = (this.props.Document.flexGrid ? NumCast(layout.x) : 2 * (index % Math.floor(this.props.Document.numCols as number / 2))) * this.colWidthPlusGap + 10;
+        const yTranslation = (this.props.Document.flexGrid ? NumCast(layout.y) : 2 * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / 2))) * this.rowHeightPlusGap + 10 - this._scroll + 30; // 30 is the height of the add text doc bar
+        const xTranslation = (this.props.Document.flexGrid ? NumCast(layout.x) : 2 * (index % Math.floor(NumCast(this.props.Document.numCols) / 2))) * this.colWidthPlusGap + 10;
 
         return this.props.ScreenToLocalTransform().translate(-xTranslation, -yTranslation);
     }
@@ -119,7 +110,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
 
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
-    get parsedLayoutList() { return this.props.Document.gridLayoutString ? JSON.parse(this.props.Document.gridLayoutString as string) : []; }
+    get parsedLayoutList() { return this.props.Document.gridLayoutString ? JSON.parse(StrCast(this.props.Document.gridLayoutString)) : []; }
     set unStringifiedLayoutList(layouts: Layout[]) { this.props.Document.gridLayoutString = JSON.stringify(layouts); }
 
 
@@ -177,7 +168,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             let update: Opt<Layout>;
             const targetId = doc[Id];
             const gridLayout = layouts.find(gridLayout => gridLayout.i === targetId);
-            // const gridLayout = DocListCast(this.props.Document.gridLayouts).find(gridLayout => StrCast(gridLayout.i) === targetId);
             if (this.props.Document.flexGrid && gridLayout && (update = layoutArray.find(layout => layout.i === targetId))) {
                 gridLayout.x = update.x;
                 gridLayout.y = update.y;
@@ -186,7 +176,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             }
         });
 
-        // this.props.Document.gridLayoutString = JSON.stringify(layouts);
         this.unStringifiedLayoutList = layouts;
     }
 
@@ -210,7 +199,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             const width = () => this.width(gridLayout);
             const height = () => this.height(gridLayout);
             collector.push(
-                <div className={this.props.Document.flexGrid ? "document-wrapper" : "document-wrapper static"}
+                <div className={this.props.Document.flexGrid && (this.props.isSelected() ? true : false) ? "document-wrapper" : "document-wrapper static"}
                     key={gridLayout.i}
                 >
                     {this.getDisplayDoc(layout, dxf, width, height)}
@@ -231,14 +220,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         return this.props.Document.flexGrid ?
             layouts : layouts.map(({ i }, index) => ({
                 i: i,
-                x: 2 * (index % Math.floor(this.props.Document.numCols as number / 2)),
-                y: 2 * Math.floor(index / Math.floor(this.props.Document.numCols as number / 2)),
+                x: 2 * (index % Math.floor(NumCast(this.props.Document.numCols) / 2)),
+                y: 2 * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / 2)),
                 w: 2,
                 h: 2,
                 static: true
             }));
     }
 
+    onSliderChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+        this.props.Document.rowHeight = event.currentTarget.valueAsNumber;
+    }
+
     @undoBatch @action addTextDocument = (value: string) => this.props.addDocument(Docs.Create.TextDocument(value, { title: value }));
 
     /**
@@ -254,14 +247,13 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         const newEditableViewProps: EditableProps = {
             GetValue: () => "",
             SetValue: this.addTextDocument,
-            contents: "+ NEW",
+            contents: "+ ADD TEXT DOCUMENT AT END",
         };
 
         const childDocumentViews: JSX.Element[] = this.contents;
         const chromeStatus = this.props.Document._chromeStatus;
         const showChrome = (chromeStatus !== 'view-mode' && chromeStatus !== 'disabled');
 
-
         return (
             <div className="collectionGridView-contents"
                 style={{
@@ -290,18 +282,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                     ref={this.containerRef}
                     onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
                 >
+                    <input className="rowHeightSlider" type="range" defaultValue={NumCast(this.props.Document.rowHeight)} onChange={this.onSliderChange} style={{ width: this.props.PanelHeight() - 40 }} min={1} max={this.props.PanelHeight() - 40} onPointerEnter={e => e.currentTarget.focus()} />
                     <Grid
                         width={this.props.PanelWidth()}
                         nodeList={childDocumentViews.length ? childDocumentViews : null}
                         layout={childDocumentViews.length ? this.layoutList : undefined}
                         childrenDraggable={this.props.isSelected() ? true : false}
-                        numCols={this.props.Document.numCols as number}
-                        rowHeight={this.props.Document.rowHeight as number}
+                        numCols={NumCast(this.props.Document.numCols)}
+                        rowHeight={NumCast(this.props.Document.rowHeight)}
                         setLayout={this.setLayout}
-                        //setLayout={(layout: Layout[]) => this.setLayout(layout)}
                         transformScale={this.props.ScreenToLocalTransform().Scale}
-                    // flex={this.props.Document.flexGrid as boolean}
                     />
+
                 </div>
             </div >
         );
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index cae06c4e7..29bafda9b 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -36,7 +36,6 @@ export default class Grid extends React.Component<GridProps> {
      * @param layout `Layout[]`
      */
     onLayoutChange(layout: Layout[]) {
-        console.log("layout changed");
         this.props.setLayout(layout);
     }
 
@@ -50,12 +49,12 @@ export default class Grid extends React.Component<GridProps> {
                 width={this.props.width}
                 compactType={null}
                 isDroppable={true}
-                // isDraggable={this.props.childrenDraggable}
+                isDraggable={this.props.childrenDraggable}
+                isResizable={this.props.childrenDraggable}
                 useCSSTransforms={true}
-                margin={[10, 10]}
                 onLayoutChange={this.onLayoutChange}
-                preventCollision={false} // change this to true later
-                // transformScale={2} // 1.2/scale
+                preventCollision={true}
+                transformScale={this.props.transformScale}
                 style={{ zIndex: 5 }}
             >
                 {this.props.nodeList}
-- 
cgit v1.2.3-70-g09d2


From 78f70dbd2136aa8b7b05bacafc71a8a714462d29 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 4 Jun 2020 20:03:19 +0530
Subject: slider added + wheel propagation prevented + fixed bug in
 changeListenerDisposer

---
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionGrid/CollectionGridView.tsx          | 55 ++++++++++++++++++----
 .../views/collections/collectionGrid/Grid.tsx      | 18 ++++++-
 3 files changed, 63 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index df84cab56..d6f729598 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -264,7 +264,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
             subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
             subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" });
-            subItems.push({ description: "Grid", event: () => this.props.Document._viewType = CollectionViewType.Grid, icon: "rainbow" });
+            subItems.push({ description: "Grid", event: () => this.props.Document._viewType = CollectionViewType.Grid, icon: "th-list" });
             switch (this.props.Document._viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 68612c9dc..dd4e28e33 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -28,6 +28,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
     private containerRef: React.RefObject<HTMLDivElement>;
     @observable private _scroll: number = 0;
     private changeListenerDisposer: Opt<Lambda>;
+    private rowHeight: number = 0;
 
     constructor(props: Readonly<SubCollectionViewProps>) {
         super(props);
@@ -38,7 +39,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
 
         this.setLayout = this.setLayout.bind(this);
         this.onSliderChange = this.onSliderChange.bind(this);
-
+        // this.deletePlaceholder = this.deletePlaceholder.bind(this);
         this.containerRef = React.createRef();
     }
 
@@ -49,7 +50,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
 
             // if grid view has been opened and then exited and a document has been deleted
             // this deletes the layout of that document from the layouts list
-            if (!oldValue) {
+            if (!oldValue && newValue.length) {
                 layouts.forEach(({ i }, index) => {
                     const targetId = i;
                     if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) {
@@ -88,8 +89,28 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
 
     componentWillUnmount() {
         this.changeListenerDisposer && this.changeListenerDisposer();
+        console.log("unmounted")
     }
 
+    // deletePlaceholder(placeholder: Layout, e: MouseEvent) {
+
+    //     const { left, right, top, bottom } = this.containerRef.current!.getBoundingClientRect();
+    //     if (e.clientX > right || e.clientX < left || e.clientY < top || e.clientY > bottom) {
+    //         const layouts: Layout[] = this.parsedLayoutList;
+    //         const index = layouts.findIndex((gridLayout: Layout) => gridLayout.i === placeholder.i);
+    //         index !== -1 && layouts.splice(index, 1);
+
+    //         const i = this.childLayoutPairs.findIndex(({ layout }) => placeholder.i === layout.i);
+    //         i !== -1 && this.childLayoutPairs.splice(i, 1);
+
+    //         console.log("deleting");
+
+    //         this.unStringifiedLayoutList = layouts;
+    //     }
+
+    // }
+
+
     /**
      * @returns the transform that will correctly place
      * the document decorations box, shifted to the right by
@@ -232,6 +253,16 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         this.props.Document.rowHeight = event.currentTarget.valueAsNumber;
     }
 
+    onSliderDown = () => {
+        this.rowHeight = NumCast(this.props.Document.rowHeight);
+    }
+
+    onSliderUp = () => {
+        const tempVal = this.props.Document.rowHeight;
+        this.props.Document.rowHeight = this.rowHeight;
+        undoBatch(() => this.props.Document.rowHeight = tempVal)();
+    }
+
     @undoBatch @action addTextDocument = (value: string) => this.props.addDocument(Docs.Create.TextDocument(value, { title: value }));
 
     /**
@@ -243,7 +274,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      * the setLayout event, which makes these unintended changes permanent by writing them to the likely now resolved documents.
      */
     render() {
-
         const newEditableViewProps: EditableProps = {
             GetValue: () => "",
             SetValue: this.addTextDocument,
@@ -257,8 +287,8 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         return (
             <div className="collectionGridView-contents"
                 style={{
-                    marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
-                    marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin),
+                    // marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
+                    // marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin),
                     pointerEvents: !this.props.isSelected() && this.props.renderDepth !== 0 && !this.props.ContainingCollectionView?._isChildActive && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }}
                 ref={this.createDashEventsTarget}
@@ -268,9 +298,12 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                             e.stopPropagation();
                         }
                     }
-                    if (this.props.isSelected(true)) {
-                        !((e.target as any)?.className.includes("react-resizable-handle")) && e.preventDefault();
-                    }
+                    // is the following section needed? it prevents the slider from being easily used and I'm not sure what it's preventing
+
+                    // if (this.props.isSelected(true)) {
+                    // !((e.target as any)?.className.includes("react-resizable-handle")) && e.preventDefault();
+                    // }
+
                 }} // the grid doesn't stopPropagation when its widgets are hit, so we need to otherwise the outer documents will respond
             >
                 {showChrome ?
@@ -280,9 +313,10 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                 }
                 <div className="collectionGridView-gridContainer"
                     ref={this.containerRef}
-                    onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
+                    onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+                    onWheel={e => e.stopPropagation()}
                 >
-                    <input className="rowHeightSlider" type="range" defaultValue={NumCast(this.props.Document.rowHeight)} onChange={this.onSliderChange} style={{ width: this.props.PanelHeight() - 40 }} min={1} max={this.props.PanelHeight() - 40} onPointerEnter={e => e.currentTarget.focus()} />
+                    <input className="rowHeightSlider" type="range" value={NumCast(this.props.Document.rowHeight)} onPointerDown={this.onSliderDown} onPointerUp={this.onSliderUp} onChange={this.onSliderChange} style={{ width: this.props.PanelHeight() - 40 }} min={1} max={this.props.PanelHeight() - 40} onPointerEnter={e => e.currentTarget.focus()} />
                     <Grid
                         width={this.props.PanelWidth()}
                         nodeList={childDocumentViews.length ? childDocumentViews : null}
@@ -292,6 +326,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                         rowHeight={NumCast(this.props.Document.rowHeight)}
                         setLayout={this.setLayout}
                         transformScale={this.props.ScreenToLocalTransform().Scale}
+                    // deletePlaceholder={this.deletePlaceholder}
                     />
 
                 </div>
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 29bafda9b..df550e3c2 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -19,6 +19,7 @@ interface GridProps {
     setLayout: Function;
     transformScale: number;
     childrenDraggable: boolean;
+    // deletePlaceholder: Function;
 }
 
 /**
@@ -27,9 +28,12 @@ interface GridProps {
 @observer
 export default class Grid extends React.Component<GridProps> {
 
+    // private dragging: boolean = false;
+
     constructor(props: Readonly<GridProps>) {
         super(props);
         this.onLayoutChange = this.onLayoutChange.bind(this);
+        // this.onDrag = this.onDrag.bind(this);
     }
     /**
      * If there has been a change in layout, calls a method in CollectionGridView to set the layouts on the Document.
@@ -39,8 +43,20 @@ export default class Grid extends React.Component<GridProps> {
         this.props.setLayout(layout);
     }
 
+    // onDrag(layout: Layout[],
+    //     oldItem: Layout,
+    //     newItem: Layout,
+    //     placeholder: Layout,
+    //     event: MouseEvent,
+    //     element: HTMLElement) {
+    //     this.props.deletePlaceholder(placeholder, event);
+    //     console.log("Grid -> event", event.clientX)
+
+    // }
+
     render() {
         console.log(this.props.transformScale);
+
         return (
             <GridLayout className="layout"
                 layout={this.props.layout}
@@ -54,7 +70,7 @@ export default class Grid extends React.Component<GridProps> {
                 useCSSTransforms={true}
                 onLayoutChange={this.onLayoutChange}
                 preventCollision={true}
-                transformScale={this.props.transformScale}
+                transformScale={this.props.transformScale} // still doesn't work :(
                 style={{ zIndex: 5 }}
             >
                 {this.props.nodeList}
-- 
cgit v1.2.3-70-g09d2


From af374bfbf3a218528bca426911f4de83e3466872 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 11 Jun 2020 22:25:13 -0400
Subject: code cleanup for CollectionGridView.  added double click to create
 text.

---
 package-lock.json                                  |  84 ++---
 src/client/views/collections/CollectionView.tsx    |  32 --
 .../views/collections/CollectionViewChromes.tsx    |  39 +-
 .../collectionGrid/CollectionGridView.scss         |   6 +-
 .../collectionGrid/CollectionGridView.tsx          | 407 ++++++++-------------
 .../views/collections/collectionGrid/Grid.tsx      |   2 -
 6 files changed, 207 insertions(+), 363 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index e692b266d..457104c8e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2841,8 +2841,7 @@
                         },
                         "ansi-regex": {
                             "version": "2.1.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "aproba": {
                             "version": "1.2.0",
@@ -2860,13 +2859,11 @@
                         },
                         "balanced-match": {
                             "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "brace-expansion": {
                             "version": "1.1.11",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "balanced-match": "^1.0.0",
                                 "concat-map": "0.0.1"
@@ -2879,18 +2876,15 @@
                         },
                         "code-point-at": {
                             "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "concat-map": {
                             "version": "0.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "console-control-strings": {
                             "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "core-util-is": {
                             "version": "1.0.2",
@@ -2993,8 +2987,7 @@
                         },
                         "inherits": {
                             "version": "2.0.4",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "ini": {
                             "version": "1.3.5",
@@ -3004,7 +2997,6 @@
                         "is-fullwidth-code-point": {
                             "version": "1.0.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "number-is-nan": "^1.0.0"
                             }
@@ -3017,20 +3009,17 @@
                         "minimatch": {
                             "version": "3.0.4",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "brace-expansion": "^1.1.7"
                             }
                         },
                         "minimist": {
                             "version": "1.2.5",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -3047,7 +3036,6 @@
                         "mkdirp": {
                             "version": "0.5.3",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "minimist": "^1.2.5"
                             }
@@ -3103,8 +3091,7 @@
                         },
                         "npm-normalize-package-bin": {
                             "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "npm-packlist": {
                             "version": "1.4.8",
@@ -3129,8 +3116,7 @@
                         },
                         "number-is-nan": {
                             "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "object-assign": {
                             "version": "4.1.1",
@@ -3140,7 +3126,6 @@
                         "once": {
                             "version": "1.4.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "wrappy": "1"
                             }
@@ -3209,8 +3194,7 @@
                         },
                         "safe-buffer": {
                             "version": "5.1.2",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "safer-buffer": {
                             "version": "2.1.2",
@@ -3240,7 +3224,6 @@
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -3258,7 +3241,6 @@
                         "strip-ansi": {
                             "version": "3.0.1",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "ansi-regex": "^2.0.0"
                             }
@@ -3297,13 +3279,11 @@
                         },
                         "wrappy": {
                             "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         }
                     }
                 }
@@ -9535,7 +9515,7 @@
                 },
                 "chownr": {
                     "version": "1.1.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
                     "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
                 },
                 "ci-info": {
@@ -9841,7 +9821,7 @@
                 },
                 "deep-extend": {
                     "version": "0.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
                     "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
                 },
                 "defaults": {
@@ -10340,7 +10320,7 @@
                 },
                 "glob": {
                     "version": "7.1.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
                     "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
                     "requires": {
                         "fs.realpath": "^1.0.0",
@@ -10428,7 +10408,7 @@
                 },
                 "hosted-git-info": {
                     "version": "2.8.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
                     "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
                 },
                 "http-cache-semantics": {
@@ -10564,7 +10544,7 @@
                 },
                 "is-ci": {
                     "version": "1.2.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
                     "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
                     "requires": {
                         "ci-info": "^1.5.0"
@@ -10640,7 +10620,7 @@
                 },
                 "is-retry-allowed": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
                     "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
                 },
                 "is-stream": {
@@ -11149,7 +11129,7 @@
                 },
                 "mkdirp": {
                     "version": "0.5.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
                     "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
                     "requires": {
                         "minimist": "^1.2.5"
@@ -11157,7 +11137,7 @@
                     "dependencies": {
                         "minimist": {
                             "version": "1.2.5",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
                             "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
                         }
                     }
@@ -11209,7 +11189,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz",
                     "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
                     "requires": {
                         "env-paths": "^2.2.0",
@@ -11323,7 +11303,7 @@
                 },
                 "npm-packlist": {
                     "version": "1.4.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
                     "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
                     "requires": {
                         "ignore-walk": "^3.0.1",
@@ -11343,7 +11323,7 @@
                 },
                 "npm-profile": {
                     "version": "4.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz",
                     "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
                     "requires": {
                         "aproba": "^1.1.2 || 2",
@@ -11353,7 +11333,7 @@
                 },
                 "npm-registry-fetch": {
                     "version": "4.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz",
                     "integrity": "sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==",
                     "requires": {
                         "JSONStream": "^1.3.4",
@@ -11788,7 +11768,7 @@
                 },
                 "rc": {
                     "version": "1.2.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
                     "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
                     "requires": {
                         "deep-extend": "^0.6.0",
@@ -11799,7 +11779,7 @@
                     "dependencies": {
                         "minimist": {
                             "version": "1.2.5",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
                             "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
                         }
                     }
@@ -11858,7 +11838,7 @@
                 },
                 "readable-stream": {
                     "version": "3.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
                     "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
                     "requires": {
                         "inherits": "^2.0.3",
@@ -11879,7 +11859,7 @@
                 },
                 "registry-auth-token": {
                     "version": "3.4.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
                     "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
                     "requires": {
                         "rc": "^1.1.6",
@@ -11943,7 +11923,7 @@
                 },
                 "rimraf": {
                     "version": "2.7.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
                         "glob": "^7.1.3"
@@ -12242,7 +12222,7 @@
                 },
                 "string_decoder": {
                     "version": "1.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
                     "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
                     "requires": {
                         "safe-buffer": "~5.2.0"
@@ -12250,7 +12230,7 @@
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
                             "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
                         }
                     }
@@ -12562,7 +12542,7 @@
                 },
                 "widest-line": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
                     "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
                     "requires": {
                         "string-width": "^2.1.1"
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 524997d20..215b5bce8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -244,38 +244,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            const existingVm = ContextMenu.Instance.findByDescription("View Modes...");
-            const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
-            subItems.push({ description: "Freeform", event: () => { this.props.Document._viewType = CollectionViewType.Freeform; }, icon: "signature" });
-            if (CollectionView._safeMode) {
-                ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document._viewType = CollectionViewType.Invalid, icon: "project-diagram" });
-            }
-            subItems.push({ description: "Schema", event: () => this.props.Document._viewType = CollectionViewType.Schema, icon: "th-list" });
-            subItems.push({ description: "Treeview", event: () => this.props.Document._viewType = CollectionViewType.Tree, icon: "tree" });
-            subItems.push({ description: "Stacking", event: () => this.props.Document._viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
-            subItems.push({
-                description: "Stacking (AutoHeight)", event: () => {
-                    this.props.Document._viewType = CollectionViewType.Stacking;
-                    this.props.Document._autoHeight = true;
-                }, icon: "ellipsis-v"
-            });
-            subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" });
-            subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" });
-            subItems.push({ description: "Multirow", event: () => this.props.Document._viewType = CollectionViewType.Multirow, icon: "columns" });
-            subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" });
-            subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
-            subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
-            subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" });
-            subItems.push({ description: "Grid", event: () => this.props.Document._viewType = CollectionViewType.Grid, icon: "th-list" });
-            switch (this.props.Document._viewType) {
-                case CollectionViewType.Freeform: {
-                    subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
-                    break;
-                }
-            }
-            subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
-            !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
-
             this.setupViewTypes("Add a Perspective...", vtype => {
                 const newRendition = Doc.MakeAlias(this.props.Document);
                 newRendition._viewType = vtype;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 63080d2e6..53bff0fe5 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -16,6 +16,7 @@ import { CollectionViewType } from "./CollectionView";
 import { CollectionView } from "./CollectionView";
 import "./CollectionViewChromes.scss";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { undo } from "prosemirror-history";
 const datepicker = require('js-datepicker');
 
 interface CollectionViewChromeProps {
@@ -589,14 +590,16 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
         this.resizeListenerDisposer?.();
     }
 
+    get numCols() { return NumCast(this.props.CollectionView.props.Document.gridNumCols, 10); }
+
     /**
      * Sets the value of `numCols` on the grid's Document to the value entered.
      */
     @undoBatch
     onNumColsEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
         if (e.key === "Enter" || e.key === "Tab") {
-            if (e.currentTarget.valueAsNumber > 0 && this.props.CollectionView.props.Document.numCols as number !== e.currentTarget.valueAsNumber) {
-                this.props.CollectionView.props.Document.numCols = e.currentTarget.valueAsNumber;
+            if (e.currentTarget.valueAsNumber > 0) {
+                this.props.CollectionView.props.Document.gridNumCols = e.currentTarget.valueAsNumber;
             }
 
         }
@@ -617,8 +620,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
     /**
      * Sets whether the grid is flexible or not on the grid's Document.
      */
+    @undoBatch
     toggleFlex = () => {
-        this.props.CollectionView.props.Document.flexGrid = !this.props.CollectionView.props.Document.flexGrid;
+        this.props.CollectionView.props.Document.gridFlex = !this.props.CollectionView.props.Document.gridFlex;
     }
 
     /**
@@ -626,8 +630,8 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
      */
     onIncrementButtonClick = () => {
         this.clicked = true;
-        this.entered && (this.props.CollectionView.props.Document.numCols as number)--;
-        undoBatch(() => (this.props.CollectionView.props.Document.numCols as number)++)();
+        this.entered && (this.props.CollectionView.props.Document.gridNumCols as number)--;
+        undoBatch(() => this.props.CollectionView.props.Document.gridNumCols = this.numCols + 1)();
         this.entered = false;
     }
 
@@ -637,8 +641,8 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
     onDecrementButtonClick = () => {
         this.clicked = true;
         if (!this.decrementLimitReached) {
-            this.entered && (this.props.CollectionView.props.Document.numCols as number)++;
-            undoBatch(() => (this.props.CollectionView.props.Document.numCols as number)--)();
+            this.entered && (this.props.CollectionView.props.Document.gridNumCols as number)++;
+            undoBatch(() => this.props.CollectionView.props.Document.gridNumCols = this.numCols - 1)();
         }
         this.entered = false;
     }
@@ -649,7 +653,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
     incrementValue = () => {
         this.entered = true;
         if (!this.clicked && !this.decrementLimitReached) {
-            (this.props.CollectionView.props.Document.numCols as number)++;
+            this.props.CollectionView.props.Document.gridNumCols = this.numCols + 1;
         }
         this.decrementLimitReached = false;
         this.clicked = false;
@@ -661,8 +665,8 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
     decrementValue = () => {
         this.entered = true;
         if (!this.clicked) {
-            if (this.props.CollectionView.props.Document.numCols as number !== 1) {
-                (this.props.CollectionView.props.Document.numCols as number)--;
+            if (this.numCols !== 1) {
+                this.props.CollectionView.props.Document.gridNumCols = this.numCols - 1;
             }
             else {
                 this.decrementLimitReached = true;
@@ -676,14 +680,15 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
      * Toggles the value of preventCollision
      */
     toggleCollisions = () => {
-        this.props.CollectionView.props.Document.preventCollision = !this.props.CollectionView.props.Document.preventCollision;
+        this.props.CollectionView.props.Document.gridPreventCollision = !this.props.CollectionView.props.Document.gridPreventCollision;
     }
 
     /**
      * Changes the value of the compactType
      */
+    @undoBatch
     changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => {
-        this.props.CollectionView.props.Document.compactType = e.target.selectedOptions[0].value;
+        this.props.CollectionView.props.Document.gridCompaction = e.target.selectedOptions[0].value;
     }
 
     render() {
@@ -693,7 +698,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
                     <span className="grid-icon">
                         <FontAwesomeIcon icon="columns" size="1x" />
                     </span>
-                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.numCols as string} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.numCols.toString()} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                     <input className="columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
                     <input className="columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
                 </span>
@@ -704,7 +709,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
                     <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                 </span> */}
                 <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
-                    <input type="checkbox" style={{ marginRight: 5 }} onClick={this.toggleCollisions} defaultChecked={!this.props.CollectionView.props.Document.preventCollision} />
+                    <input type="checkbox" style={{ marginRight: 5 }} onClick={this.toggleCollisions} defaultChecked={!this.props.CollectionView.props.Document.gridPreventCollision} />
                     <label className="flexLabel">{this.resize ? "Coll" : "Collisions"}</label>
                 </span>
 
@@ -712,7 +717,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
                     style={{ marginRight: 5 }}
                     onPointerDown={stopPropagation}
                     onChange={this.changeCompactType}
-                    value={StrCast(this.props.CollectionView.props.Document.compactType)}>
+                    value={StrCast(this.props.CollectionView.props.Document.gridCompaction)}>
                     >
                     {["vertical", "horizontal", "null"].map(type =>
                         <option className="collectionGridViewChrome-viewOption"
@@ -724,11 +729,11 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
                 </select>
 
                 <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
-                    <input style={{ marginRight: 5 }} type="checkbox" onClick={this.toggleFlex} defaultChecked={this.props.CollectionView.props.Document.flexGrid as boolean} />
+                    <input style={{ marginRight: 5 }} type="checkbox" onClick={this.toggleFlex} defaultChecked={BoolCast(this.props.CollectionView.props.Document.gridFlex)} />
                     <label className="flexLabel">{this.resize ? "Flex" : "Flexible"}</label>
                 </span>
 
-                <button onClick={() => this.props.CollectionView.props.Document.resetLayout = true}>
+                <button onClick={() => this.props.CollectionView.props.Document.gridResetLayout = true}>
                     {!this.resize ? "Reset" :
                         <FontAwesomeIcon icon="redo-alt" size="1x" />}
                 </button>
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index de7d62475..9c2d5cbff 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -21,6 +21,10 @@
             max-width: 100%;
         }
 
+        .react-grid-layout {
+            width : 100%;
+        }
+
         .react-grid-item>.react-resizable-handle {
             z-index: 4; // doesn't work on prezi otherwise
         }
@@ -41,7 +45,7 @@
             position: absolute;
             height: 3;
             left: 5;
-            top: 40;
+            top: 30;
             transform-origin: left;
             transform: rotate(90deg);
             outline: none;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index a820b7e0e..2e1f7a0db 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,85 +1,61 @@
-import { computed, observable, Lambda, action, reaction } from 'mobx';
+import { action, computed, Lambda, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from "react";
 import { Doc, Opt } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
+import { Id } from '../../../../fields/FieldSymbols';
 import { makeInterface } from '../../../../fields/Schema';
-import { BoolCast, NumCast, StrCast, ScriptCast } from '../../../../fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
+import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { ContextMenuProps } from '../../ContextMenuItem';
 import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { CollectionSubView } from '../CollectionSubView';
-import { SubCollectionViewProps } from '../CollectionSubView';
-import { returnZero } from '../../../../Utils';
-import Grid, { Layout } from "./Grid";
-import { Id } from '../../../../fields/FieldSymbols';
-import { observer } from 'mobx-react';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Docs } from '../../../documents/Documents';
-import { EditableView, EditableProps } from '../../EditableView';
 import "./CollectionGridView.scss";
-import { ContextMenu } from '../../ContextMenu';
-import { List } from '../../../../fields/List';
-import { ContextMenuProps } from '../../ContextMenuItem';
-
+import Grid, { Layout } from "./Grid";
 
 type GridSchema = makeInterface<[typeof documentSchema]>;
 const GridSchema = makeInterface(documentSchema);
 
 @observer
 export class CollectionGridView extends CollectionSubView(GridSchema) {
-    private containerRef: React.RefObject<HTMLDivElement>;
+    private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
+    private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
+    private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
+    @observable private _rowHeight: Opt<number>; // temporary store of row height to make change undoable
     @observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
-    private changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
-    private rowHeight: number = 0; // temporary store of row height to make change undoable
-    private mounted: boolean = false; // hack to fix the issue of not rerendering when mounting
-    private resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
-
-    constructor(props: Readonly<SubCollectionViewProps>) {
-        super(props);
-
-        this.props.Document.numCols = NumCast(this.props.Document.numCols, 10);
-        this.props.Document.rowHeight = NumCast(this.props.Document.rowHeight, 100);
-
-        // determines whether the grid is static/flexible i.e. whether can nodes be moved around and resized or not
-        this.props.Document.flexGrid = BoolCast(this.props.Document.flexGrid, true);
 
-        // determines whether nodes should remain in position, be bound to the top, or to the left
-        this.props.Document.compactType = StrCast(this.props.Document.compactType, "vertical");
-
-        // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
-        this.props.Document.preventCollision = BoolCast(this.props.Document.preventCollision, false);
+    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
 
-        // sets the default width and height of the grid nodes 
-        this.props.Document.defaultW = NumCast(this.props.Document.defaultW, 2);
-        this.props.Document.defaultH = NumCast(this.props.Document.defaultH, 2);
+    @computed get numCols() { return NumCast(this.props.Document.gridNumCols, 10); }
+    @computed get rowHeight() { return this._rowHeight === undefined ? NumCast(this.props.Document.gridRowHeight, 100) : this._rowHeight; }
+    // sets the default width and height of the grid nodes 
+    @computed get defaultW() { return NumCast(this.props.Document.gridDefaultW, 2); }
+    @computed get defaultH() { return NumCast(this.props.Document.gridDefaultH, 2); }
 
-        // sets the margin between grid nodes
-        this.props.Document.margin = NumCast(this.props.Document.margin, 10);
+    @computed get colWidthPlusGap() { return (this.props.PanelWidth() - this.margin) / this.numCols; }
+    @computed get rowHeightPlusGap() { return this.rowHeight + this.margin; }
 
-        // sets the css display type of the ContentFittingDocumentView component
-        this.props.Document.display = StrCast(this.props.Document.display, "contents");
+    @computed get margin() { return NumCast(this.props.Document.margin, 10); }  // sets the margin between grid nodes
 
-        this.setLayout = this.setLayout.bind(this);
-        this.onSliderChange = this.onSliderChange.bind(this);
-        this.onContextMenu = this.onContextMenu.bind(this);
-
-        this.containerRef = React.createRef();
-    }
+    @computed get flexGrid() { return BoolCast(this.props.Document.gridFlex, true); } // is grid static/flexible i.e. whether nodes be moved around and resized
 
     componentDidMount() {
-        this.mounted = true;
-
-        this.changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => {
-
-            const layouts: Layout[] = this.parsedLayoutList;
+        this._changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => {
+            const layouts = this.parsedLayoutList;
 
             // if grid view has been opened and then exited and a document has been deleted
             // this deletes the layout of that document from the layouts list
 
             if (!oldValue && newValue.length) {
                 layouts.forEach(({ i }, index) => {
-                    const targetId = i;
-                    if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) {
+                    if (!newValue.find(({ layout: preserved }) => preserved[Id] === i)) {
                         layouts.splice(index, 1);
                     }
                 });
@@ -88,120 +64,84 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             if (!oldValue || newValue.length > oldValue.length) {
                 // for each document that was added, add a corresponding grid layout object
                 newValue.forEach(({ layout }, i) => {
-                    const targetId = layout[Id];
-                    if (!layouts.find((gridLayout: Layout) => gridLayout.i === targetId)) {
-                        layouts.push({
-                            i: targetId,
-                            w: this.defaultW,
-                            h: this.defaultH,
-                            x: this.defaultW * (i % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW)),
-                            y: this.defaultH * Math.floor(i / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH)),
-                            static: !this.props.Document.flexGrid
-                        });
+                    if (!layouts.find(gridLayout => gridLayout.i === layout[Id])) {
+                        this.addLayoutItem(layouts, this.makeLayoutItem(layout, this.unflexedPosition(i), !this.flexGrid))
                     }
-                });
+                })
             } else {
                 // for each document that was removed, remove its corresponding grid layout object
                 oldValue.forEach(({ layout }) => {
-                    const targetId = layout[Id];
-                    if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) {
-                        const index = layouts.findIndex((gridLayout: Layout) => gridLayout.i === targetId);
+                    if (!newValue.find(({ layout: preserved }) => preserved[Id] === layout[Id])) {
+                        const index = layouts.findIndex((gridLayout: Layout) => gridLayout.i === layout[Id]);
                         index !== -1 && layouts.splice(index, 1);
                     }
                 });
             }
-            this.unStringifiedLayoutList = layouts;
+            this.setLayoutList(layouts);
         }, true);
 
         // updates the layouts if the reset button has been clicked
-        this.resetListenerDisposer = reaction(() => this.props.Document.resetLayout, () => {
-            if (this.props.Document.flexGrid) {
-                const layouts: Layout[] = this.parsedLayoutList;
-                this.setLayout(
-                    layouts.map(({ i }, index) => ({
-                        i: i,
-                        x: this.defaultW * (index % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW)),
-                        y: this.defaultH * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH)),
-                        w: this.defaultW,
-                        h: this.defaultH,
-                    })));
+        this._resetListenerDisposer = reaction(() => this.props.Document.gridResetLayout, (reset) => {
+            if (reset && this.flexGrid) {
+                this.setLayout(this.childLayoutPairs.map((pair, index) => this.makeLayoutItem(pair.layout, this.unflexedPosition(index))));
             }
-            this.props.Document.resetLayout = false;
+            this.props.Document.gridResetLayout = false;
         });
     }
 
     componentWillUnmount() {
-        this.mounted = false;
-        this.changeListenerDisposer && this.changeListenerDisposer();
-        this.resetListenerDisposer?.();
+        this._changeListenerDisposer?.();
+        this._resetListenerDisposer?.();
     }
 
-    /**
-     * @returns the transform that will correctly place the document decorations box. 
-     */
-    private lookupIndividualTransform = (layout: Layout) => {
-        const index = this.childLayoutPairs.findIndex(({ layout: layoutDoc }) => layoutDoc[Id] === layout.i);
-
-        // translations depend on whether the grid is flexible or static
-        const xTranslation = (this.props.Document.flexGrid ? NumCast(layout.x) : this.defaultW * (index % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW))) * this.colWidthPlusGap + this.margin;
-        const yTranslation = (this.props.Document.flexGrid ? NumCast(layout.y) : this.defaultH * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH))) * this.rowHeightPlusGap + this.margin - this._scroll + 30; // 30 is the height of the add text doc bar
-
-        return this.props.ScreenToLocalTransform().translate(-xTranslation, -yTranslation);
+    unflexedPosition(index: number) {
+        return {
+            x: (index % Math.floor(this.numCols / this.defaultW)) * this.defaultW,
+            y: Math.floor(index / Math.floor(this.numCols / this.defaultH)) * this.defaultH
+        };
     }
 
-    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    screenToCell(sx: number, sy: number) {
+        const pt = this.props.ScreenToLocalTransform().transformPoint(sx, sy);
+        const x = Math.floor(pt[0] / this.colWidthPlusGap);
+        const y = Math.floor((pt[1] + this._scroll) / this.rowHeight);
+        return { x, y };
+    }
 
-    addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
-            this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
-            return true;
-        }
-        return this.props.addDocTab(doc, where);
+    makeLayoutItem = (doc: Doc, pos: { x: number, y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => {
+        return ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static });
     }
 
-    @computed get colWidthPlusGap() { return (this.props.PanelWidth() - this.margin) / NumCast(this.props.Document.numCols); }
-    @computed get rowHeightPlusGap() { return NumCast(this.props.Document.rowHeight) + this.margin; }
+    addLayoutItem = (layouts: Layout[], layout: Layout) => {
+        const f = layouts.findIndex(l => l.i === layout.i);
+        f !== -1 && layouts.splice(f, 1);
+        layouts.push(layout);
+        return layouts;
+    }
+    /**
+     * @returns the transform that will correctly place the document decorations box. 
+     */
+    private lookupIndividualTransform = (layout: Layout) => {
+        const xypos = this.flexGrid ? layout : this.unflexedPosition(this.layoutList.findIndex(l => l.i === layout.i));
+        const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
 
-    @computed get margin() { return NumCast(this.props.Document.margin); }
-    @computed get defaultW() { return NumCast(this.props.Document.defaultW); }
-    @computed get defaultH() { return NumCast(this.props.Document.defaultH); }
+        return this.props.ScreenToLocalTransform().translate(-pos.x, -pos.y);
+    }
 
     /**
      * @returns the layout list converted from JSON
      */
     get parsedLayoutList() {
-        return this.props.Document.gridLayoutString ? JSON.parse(StrCast(this.props.Document.gridLayoutString)) : [];
+        return (this.props.Document.gridLayoutString ? JSON.parse(StrCast(this.props.Document.gridLayoutString)) : []) as Layout[];
     }
 
     /**
      * Stores the layout list on the Document as JSON
      */
-    set unStringifiedLayoutList(layouts: Layout[]) {
-
-        // sometimes there are issues with rendering when you switch from a different view
-        // where the nodes are all squeezed together on the left hand side of the screen
-        // until you click on the screen or close the chrome or interact with it in some way
-        // the component doesn't rerender when the component mounts
-        // this seems to fix that though it isn't very elegant
-
-        this.mounted && (this.props.Document.gridLayoutString = "");
+    setLayoutList(layouts: Layout[]) {
         this.props.Document.gridLayoutString = JSON.stringify(layouts);
-        this.mounted = false;
     }
 
-
-    /**
-     * Sets the width of the decorating box.
-     * @param layout 
-     */
-    @observable private width = (layout: Layout) => (this.props.Document.flexGrid ? layout.w : this.defaultW) * this.colWidthPlusGap - this.margin;
-
-    /**
-     * Sets the height of the decorating box.
-     * @param layout
-     */
-    @observable private height = (layout: Layout) => (this.props.Document.flexGrid ? layout.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
-
     /**
      * 
      * @param layout 
@@ -217,7 +157,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             DataDoc={layout.resolvedDataDoc as Doc}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            addDocTab={this.addDocTab}
             backgroundColor={this.props.backgroundColor}
             ContainingCollectionDoc={this.props.Document}
             PanelWidth={width}
@@ -226,7 +165,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
             parentActive={this.props.active}
-            display={StrCast(this.props.Document.display)}
+            display={StrCast(this.props.Document.display, "contents")} // sets the css display type of the ContentFittingDocumentView component
         />;
     }
 
@@ -234,27 +173,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      * Saves the layouts received from the Grid to the Document.
      * @param layouts `Layout[]`
      */
-    @undoBatch
     @action
-    setLayout(layoutArray: Layout[]) {
+    setLayout = (layoutArray: Layout[]) => {
         // for every child in the collection, check to see if there's a corresponding grid layout object and
         // updated layout object. If both exist, which they should, update the grid layout object from the updated object 
-
-        if (this.props.Document.flexGrid) {
-            const layouts: Layout[] = this.parsedLayoutList;
+        if (this.flexGrid) {
+            const layouts = this.parsedLayoutList;
             this.childLayoutPairs.forEach(({ layout: doc }) => {
-                let update: Opt<Layout>;
-                const targetId = doc[Id];
-                const gridLayout = layouts.find(gridLayout => gridLayout.i === targetId);
-                if (gridLayout && (update = layoutArray.find(layout => layout.i === targetId))) {
-                    gridLayout.x = update.x;
-                    gridLayout.y = update.y;
-                    gridLayout.w = update.w;
-                    gridLayout.h = update.h;
-                }
+                let gridLayout = layouts.find(gridLayout => gridLayout.i === doc[Id]);
+                gridLayout && Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout);
             });
 
-            this.unStringifiedLayoutList = layouts;
+            this.setLayoutList(layouts);
         }
     }
 
@@ -264,27 +194,22 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      */
     @computed
     private get contents(): JSX.Element[] {
-
-        const { childLayoutPairs } = this;
         const collector: JSX.Element[] = [];
-        const layouts: Layout[] = this.parsedLayoutList;
-        if (!layouts.length || layouts.length !== childLayoutPairs.length) {
-            return [];
-        }
-
-        for (let i = 0; i < childLayoutPairs.length; i++) {
-            const { layout } = childLayoutPairs[i];
-            const gridLayout = layouts[i];
-            const dxf = () => this.lookupIndividualTransform(gridLayout);
-            const width = () => this.width(gridLayout);
-            const height = () => this.height(gridLayout);
-            collector.push(
-                <div className={this.props.Document.flexGrid && (this.props.isSelected() ? true : false) ? "document-wrapper" : "document-wrapper static"}
-                    key={gridLayout.i}
-                >
-                    {this.getDisplayDoc(layout, dxf, width, height)}
-                </div >
-            );
+        const layouts = this.parsedLayoutList;
+        if (layouts.length !== this.childLayoutPairs.length) {
+            setTimeout(action(() => this.props.Document.gridResetLayout = true), 0);
+        } else {
+            this.layoutList.forEach((l, i) => {
+                const child = this.childLayoutPairs.find(c => c.layout[Id] === l.i);
+                const dxf = () => this.lookupIndividualTransform(layouts[i]);
+                const width = () => (this.flexGrid ? layouts[i].w : this.defaultW) * this.colWidthPlusGap - this.margin;
+                const height = () => (this.flexGrid ? layouts[i].h : this.defaultH) * this.rowHeightPlusGap - this.margin;
+                child && collector.push(
+                    <div key={child.layout[Id]} className={"document-wrapper" + (this.flexGrid && this.props.isSelected() ? "" : " static")} >
+                        {this.getDisplayDoc(child.layout, dxf, width, height)}
+                    </div >
+                );
+            });
         }
         return collector;
     }
@@ -293,56 +218,42 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      * @returns a list of `Layout` objects with attributes depending on whether the grid is flexible or static
      */
     get layoutList(): Layout[] {
-
-        const layouts: Layout[] = this.parsedLayoutList;
-
-        return this.props.Document.flexGrid ?
-            layouts.map(({ i, x, y, w, h }) => ({
-                i: i,
-                x: x + w > NumCast(this.props.Document.numCols) ? 0 : x, // handles wrapping around of nodes when numCols decreases
-                y: y,
-                w: w > NumCast(this.props.Document.numCols) ? NumCast(this.props.Document.numCols) : w, // reduces width if greater than numCols
-                h: h,
+        return this.flexGrid ?
+            this.parsedLayoutList.map(({ i, x, y, w, h }) => ({
+                i, y, h,
+                x: x + w > this.numCols ? 0 : x, // handles wrapping around of nodes when numCols decreases
+                w: Math.max(w, this.numCols), // reduces width if greater than numCols
                 static: BoolCast(this.childLayoutPairs.find(({ layout }) => layout[Id] === i)?.layout.lockedPosition, false) // checks if the lock position item has been selected in the context menu
-            }))
-            : layouts.map(({ i }, index) => ({
-                i: i,
-                x: this.defaultW * (index % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW)),
-                y: this.defaultH * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH)),
-                w: this.defaultW,
-                h: this.defaultH,
-                static: true
-            }));
+            })) :
+            this.parsedLayoutList.map((layout, index) => Object.assign(layout, this.unflexedPosition(index)));
     }
 
-    /**
-     * Handles the change in the value of the rowHeight slider.
-     */
-    onSliderChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.rowHeight = event.currentTarget.valueAsNumber;
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+        const layouts = this.parsedLayoutList;
+        const dropped = de.complete.docDragData?.droppedDocuments;
+        if (dropped && super.onInternalDrop(e, de) && layouts.length !== this.childDocs.length) {
+            dropped.forEach(doc => this.addLayoutItem(layouts, this.makeLayoutItem(doc, this.screenToCell(de.x, de.y)))); // shouldn't place all docs in the same cell;
+            this.setLayoutList(layouts);
+            return true;
+        }
+        return false;
     }
 
     /**
-     * Saves the rowHeight in a temporary variable to make it undoable later.
+     * Handles the change in the value of the rowHeight slider.
      */
-    onSliderDown = () => {
-        this.rowHeight = NumCast(this.props.Document.rowHeight);
+    @action
+    onSliderChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+        this._rowHeight = event.currentTarget.valueAsNumber;
     }
-
-    /**
-     * Uses the stored rowHeight to make the rowHeight change undoable.
-     */
-    onSliderUp = () => {
-        const tempVal = this.props.Document.rowHeight;
-        this.props.Document.rowHeight = this.rowHeight;
-        undoBatch(() => this.props.Document.rowHeight = tempVal)();
+    @action
+    onSliderDown = (e: React.PointerEvent) => {
+        this._rowHeight = this.rowHeight; // uses _rowHeight during dragging and sets doc's rowHeight when finished so that operation is undoable
+        setupMoveUpEvents(this, e, returnFalse, action(() => {
+            undoBatch(() => this.props.Document.gridRowHeight = this._rowHeight)();
+            this._rowHeight = undefined;
+        }), emptyFunction, false, false);
     }
-
-    /**
-     * Creates a text document and adds it to the grid.
-     */
-    @undoBatch @action addTextDocument = (value: string) => this.props.addDocument(Docs.Create.TextDocument(value, { title: value }));
-
     /**
      * Adds the display option to change the css display attribute of the `ContentFittingDocumentView`s
      */
@@ -350,74 +261,52 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
         const displayOptionsMenu: ContextMenuProps[] = [];
         displayOptionsMenu.push({ description: "Contents", event: () => this.props.Document.display = "contents", icon: "copy" });
         displayOptionsMenu.push({ description: "Undefined", event: () => this.props.Document.display = undefined, icon: "exclamation" });
-
         ContextMenu.Instance.addItem({ description: "Display", subitems: displayOptionsMenu, icon: "tv" });
     }
 
-    render() {
-
-        // for the add text document EditableView
-        const newEditableViewProps: EditableProps = {
-            GetValue: () => "",
-            SetValue: this.addTextDocument,
-            contents: "+ ADD TEXT DOCUMENT",
-        };
-
-        const childDocumentViews: JSX.Element[] = this.contents;
-        const chromeStatus = this.props.Document._chromeStatus;
-        const showChrome = (chromeStatus !== 'view-mode' && chromeStatus !== 'disabled');
+    onPointerDown = (e: React.PointerEvent) => {
+        this.props.isSelected(true) && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
+            if (doubleTap) {
+                const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 });
+                FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+                Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
+                this.setLayoutList(this.addLayoutItem(this.parsedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
+            }
+        }), false);
+        e.stopPropagation();
+    }
 
+    render() {
         return (
-            <div className="collectionGridView-contents"
-                style={{
-                    pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
-                }}
+            <div className="collectionGridView-contents" ref={this.createDashEventsTarget}
+                style={{ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined }}
                 onContextMenu={this.onContextMenu}
-                ref={this.createDashEventsTarget}
-                onPointerDown={e => {
-                    if (this.props.active(true)) {
-                        if (this.props.isSelected(true)) {
-                            e.stopPropagation();
-                        }
-                    }
-                    // is the following section needed? it prevents the slider from being easily used and I'm not sure what it's preventing
-
-                    // if (this.props.isSelected(true)) {
-                    //     !((e.target as any)?.className.includes("react-resizable-handle")) && e.preventDefault();
-                    // }
-
-                }} // the grid doesn't stopPropagation when its widgets are hit, so we need to otherwise the outer documents will respond
-            >
-                {showChrome ?
-                    <div className="collectionGridView-addDocumentButton">
-                        <EditableView {...newEditableViewProps} />
-                    </div> : null
-                }
-                <div className="collectionGridView-gridContainer"
-                    ref={this.containerRef}
+                onPointerDown={e => this.onPointerDown(e)} >
+                <div className="collectionGridView-gridContainer" ref={this._containerRef}
+                    onWheel={e => e.stopPropagation()}
                     onScroll={action(e => {
                         if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
                         else this._scroll = e.currentTarget.scrollTop;
-                    })}
-                    onWheel={e => e.stopPropagation()}
-                >
-                    <input className="rowHeightSlider" type="range" value={NumCast(this.props.Document.rowHeight)} onPointerDown={this.onSliderDown} onPointerUp={this.onSliderUp} onChange={this.onSliderChange} style={{ width: this.props.PanelHeight() - 40 }} min={1} max={this.props.PanelHeight() - 40} />
+                    })} >
+                    <input className="rowHeightSlider" type="range"
+                        style={{ width: this.props.PanelHeight() - 30 }}
+                        min={1} value={this.rowHeight} max={this.props.PanelHeight() - 30}
+                        onPointerDown={this.onSliderDown} onChange={this.onSliderChange} />
                     <Grid
                         width={this.props.PanelWidth()}
-                        nodeList={childDocumentViews.length ? childDocumentViews : null}
-                        layout={childDocumentViews.length ? this.layoutList : undefined}
+                        nodeList={this.contents.length ? this.contents : null}
+                        layout={this.contents.length ? this.layoutList : undefined}
                         childrenDraggable={this.props.isSelected() ? true : false}
-                        numCols={NumCast(this.props.Document.numCols)}
-                        rowHeight={NumCast(this.props.Document.rowHeight)}
+                        numCols={this.numCols}
+                        rowHeight={this.rowHeight}
                         setLayout={this.setLayout}
                         transformScale={this.props.ScreenToLocalTransform().Scale}
-                        compactType={StrCast(this.props.Document.compactType)}
-                        preventCollision={BoolCast(this.props.Document.preventCollision)}
+                        compactType={StrCast(this.props.Document.gridCompaction, "vertical")} // determines whether nodes should remain in position, be bound to the top, or to the left
+                        preventCollision={BoolCast(this.props.Document.gridPreventCollision)}// determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
                         margin={this.margin}
                     />
-
                 </div>
             </div >
         );
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 6232fca7c..66edb99d9 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -1,7 +1,6 @@
 import * as React from 'react';
 import { observer } from "mobx-react";
 
-
 import "../../../../../node_modules/react-grid-layout/css/styles.css";
 import "../../../../../node_modules/react-resizable/css/styles.css";
 
@@ -29,7 +28,6 @@ interface GridProps {
  */
 @observer
 export default class Grid extends React.Component<GridProps> {
-
     render() {
         const compactType = this.props.compactType === "vertical" || this.props.compactType === "horizontal" ? this.props.compactType : null;
         return (
-- 
cgit v1.2.3-70-g09d2


From 3927564a9ff22af07490f0e98be3e8cbe1a4d3ab Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 16 Jun 2020 23:24:30 -0400
Subject: changed Doc.MakeCopy() to ignore layout[ fields so that copying text
 documents with a layout doesn't copy their text

---
 src/client/util/SettingsManager.tsx                |  7 +++-
 .../views/collections/CollectionTreeView.tsx       |  7 ++--
 src/client/views/collections/CollectionView.tsx    | 10 +++--
 src/client/views/nodes/DocumentView.tsx            | 46 +++++++++++-----------
 src/client/views/nodes/ImageBox.tsx                |  5 ---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  4 +-
 src/fields/Doc.ts                                  |  2 +-
 8 files changed, 45 insertions(+), 38 deletions(-)

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

diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 0e15197c4..9888cce48 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -10,6 +10,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Networking } from "../Network";
 import { CurrentUserUtils } from "./CurrentUserUtils";
 import { Utils } from "../../Utils";
+import { Doc } from "../../fields/Doc";
 
 library.add(fa.faWindowClose);
 
@@ -78,6 +79,10 @@ export default class SettingsManager extends React.Component<{}> {
         this.errorText = "";
         this.successText = "";
     }
+    @action
+    noviceToggle = (event: any) => {
+        Doc.UserDoc().noviceMode = !Doc.UserDoc().noviceMode;
+    }
 
     private get settingsInterface() {
         return (
@@ -91,7 +96,7 @@ export default class SettingsManager extends React.Component<{}> {
                 <div className="settings-body">
                     <div className="settings-type">
                         <button onClick={this.onClick} value="password">reset password</button>
-                        <button onClick={this.onClick} value="data">reset data</button>
+                        <button onClick={this.noviceToggle} value="data">{`toggle ${Doc.UserDoc().noviceMode ? "developer" : "novice"} mode`}</button>
                         <button onClick={() => window.location.assign(Utils.prepend("/logout"))}>
                             {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
                         </button>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 180bcdd02..d631a492e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -83,8 +83,9 @@ class TreeView extends React.Component<TreeViewProps> {
     private _tref = React.createRef<HTMLDivElement>();
     private _docRef = React.createRef<DocumentView>();
 
+    get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
     get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
-    get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
+    get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
     @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
     set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
     @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.props.document.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
@@ -420,10 +421,10 @@ class TreeView extends React.Component<TreeViewProps> {
                 <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
                     onPointerDown={action(() => {
                         if (this.treeViewOpen) {
-                            this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
+                            this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
                                 this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
                                     this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
-                                        this.childDocs ? this.fieldKey : "fields";
+                                        this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
                         }
                         this.treeViewOpen = true;
                     })}>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 215b5bce8..e436a13ab 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -282,10 +282,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 }));
             !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
-            const more = ContextMenu.Instance.findByDescription("More...");
-            const moreItems = more && "subitems" in more ? more.subitems : [];
-            moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
-            !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+            if (!Doc.UserDoc().noviceMode) {
+                const more = ContextMenu.Instance.findByDescription("More...");
+                const moreItems = more && "subitems" in more ? more.subitems : [];
+                moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
+                !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+            }
         }
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index da5327f48..e7cfb5e9c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -759,30 +759,32 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
 
         const more = cm.findByDescription("More...");
-        const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
-        moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
-        moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+        const moreItems = more && "subitems" in more ? more.subitems : [];
+        if (!Doc.UserDoc().noviceMode) {
+            moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
+            moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+
+            if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
+                moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
+                moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
+                moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+            }
+            moreItems.push({
+                description: "Download document", icon: "download", event: async () => {
+                    const response = await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
+                        qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
+                    });
+                    console.log(response ? JSON.parse(response) : undefined);
+                }
+                // const a = document.createElement("a");
+                // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+                // a.href = url;
+                // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+                // a.click();
+            });
+        }
         moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
-
-        if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
-            moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
-            moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
-            moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
-        }
-        moreItems.push({
-            description: "Download document", icon: "download", event: async () => {
-                const response = await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
-                    qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
-                });
-                console.log(response ? JSON.parse(response) : undefined);
-            }
-            // const a = document.createElement("a");
-            // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
-            // a.href = url;
-            // a.download = `DocExport-${this.props.Document[Id]}.zip`;
-            // a.click();
-        });
         moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" });
         !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index cabf30c13..69cffb6c2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -182,11 +182,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
             !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
 
             ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-
-
-            const existingMore = ContextMenu.Instance.findByDescription("More...");
-            const mores: ContextMenuProps[] = existingMore && "subitems" in existingMore ? existingMore.subitems : [];
-            !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" });
         }
     }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ba36a1618..b43bd6d63 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -481,7 +481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }), icon: "eye"
             });
         });
-        changeItems.push({ description: "FreeForm", event: undoBatch(() => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" });
+        changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
         !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
     }
 
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 114288a9d..0a4c52ef9 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
 import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc } from "../../../../fields/Doc";
+import { Doc, DataSym } from "../../../../fields/Doc";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { Id } from "../../../../fields/FieldSymbols";
 import { Docs } from "../../../documents/Documents";
@@ -142,6 +142,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
         if (force || props.Document._singleLine) {
             const layoutKey = StrCast(originalDoc.layoutKey);
             const newDoc = Doc.MakeCopy(originalDoc, true);
+            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
             newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
             if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
                 newDoc[layoutKey] = originalDoc[layoutKey];
@@ -166,6 +167,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
         if (originalDoc instanceof Doc) {
             const layoutKey = StrCast(originalDoc.layoutKey);
             const newDoc = Doc.MakeCopy(originalDoc, true);
+            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
             newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
             if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
                 newDoc[layoutKey] = originalDoc[layoutKey];
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 70aa2d60b..96587af44 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -670,7 +670,7 @@ export namespace Doc {
                     copy[key] = cfield[Copy]();// ComputedField.MakeFunction(cfield.script.originalScript);
                 } else if (field instanceof ObjectField) {
                     copy[key] = doc[key] instanceof Doc ?
-                        key.includes("layout[") ? Doc.MakeCopy(doc[key] as Doc, false) : doc[key] : // reference documents except copy documents that are expanded teplate fields 
+                        key.includes("layout[") ? undefined : doc[key] : // reference documents except remove documents that are expanded teplate fields 
                         ObjectField.MakeCopy(field);
                 } else if (field instanceof Promise) {
                     debugger; //This shouldn't happend...
-- 
cgit v1.2.3-70-g09d2


From 375786198596ce8dcab06c39c5b4ffcca4a78b88 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Jun 2020 10:59:54 -0400
Subject: fixed move document to undo correctly when ACLs of target don't allow
 a drop.

---
 src/client/util/UndoManager.ts                     | 22 ++++++++++++++++++++--
 src/client/views/RecommendationsBox.tsx            |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    | 15 +++++++++++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 --
 src/client/views/nodes/DocumentView.tsx            |  3 ---
 6 files changed, 35 insertions(+), 11 deletions(-)

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

diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 314b52bf3..c7b7bb215 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -78,10 +78,12 @@ export namespace UndoManager {
     let currentBatch: UndoBatch | undefined;
     let batchCounter = 0;
     let undoing = false;
+    let tempEvents: UndoEvent[] | undefined = undefined;
 
     export function AddEvent(event: UndoEvent): void {
         if (currentBatch && batchCounter && !undoing) {
             currentBatch.push(event);
+            tempEvents?.push(event);
         }
     }
 
@@ -135,7 +137,7 @@ export namespace UndoManager {
 
     const EndBatch = action((cancel: boolean = false) => {
         batchCounter--;
-        if (batchCounter === 0 && currentBatch && currentBatch.length) {
+        if (batchCounter === 0 && currentBatch?.length) {
             if (!cancel) {
                 undoStack.push(currentBatch);
             }
@@ -144,6 +146,13 @@ export namespace UndoManager {
         }
     });
 
+    export function ClearTempBatch() {
+        tempEvents = undefined;
+    }
+    export function RunInTempBatch<T>(fn: () => T) {
+        tempEvents = [];
+        return runInAction(fn);
+    }
     //TODO Make this return the return value
     export function RunInBatch<T>(fn: () => T, batchName: string) {
         const batch = StartBatch(batchName);
@@ -153,7 +162,16 @@ export namespace UndoManager {
             batch.end();
         }
     }
-
+    export const UndoTempBatch = action(() => {
+        if (tempEvents) {
+            undoing = true;
+            for (let i = tempEvents.length - 1; i >= 0; i--) {
+                tempEvents[i].undo();
+            }
+            undoing = false;
+        }
+        tempEvents = undefined;
+    });
     export const Undo = action(() => {
         if (undoStack.length === 0) {
             return;
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index 8ca81c070..c98363214 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -56,7 +56,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
         }
         const returnXDimension = () => 150;
         const returnYDimension = () => 150;
-        const scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+        const scale = () => returnXDimension() / NumCast(renderDoc._nativeWidth, returnXDimension());
         //let scale = () => 1;
         const newRenderDoc = Doc.MakeAlias(renderDoc); ///   newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
         newRenderDoc.height = NumCast(this.props.Document.documentIconHeight);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d631a492e..be0339345 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -250,7 +250,7 @@ class TreeView extends React.Component<TreeViewProps> {
             const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
             if (aspect) return this.docWidth() * aspect;
             if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
-            return layoutDoc._fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection._height) :
+            return layoutDoc._fitWidth ? (!this.props.document._nativeHeight ? NumCast(this.props.containingCollection._height) :
                 Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
                     NumCast(this.props.containingCollection._height)))) :
                 NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e436a13ab..be79dbce1 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -45,6 +45,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
 import './CollectionView.scss';
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
+import { UndoManager } from '../../util/UndoManager';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -166,7 +167,17 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             return true;
         }
         const first = doc instanceof Doc ? doc : doc[0];
-        return !first?.stayInCollection && addDocument !== returnFalse && this.removeDocument(doc) ? addDocument(doc) : false;
+        if (!first?.stayInCollection && addDocument !== returnFalse) {
+            if (UndoManager.RunInTempBatch(() => this.removeDocument(doc))) {
+                const added = addDocument(doc);
+                if (!added) UndoManager.UndoTempBatch();
+                else UndoManager.ClearTempBatch();
+
+                return added;
+            }
+            UndoManager.ClearTempBatch();
+        }
+        return false;
     }
 
     showIsTagged = () => {
@@ -431,7 +442,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     @computed get filterView() {
         const facetCollection = this.props.Document;
         const flyout = (
-            <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
+            <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => fmovede.stopPropagation()}>
                 {this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
                     <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey + "-filter"]).some(d => d.title === facet)} />
                     <span className="checkmark" />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 736c5fd06..72cb2d218 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -258,8 +258,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp);
         } else if (de.complete.docDragData?.droppedDocuments.length && this.internalDocDrop(e, de, de.complete.docDragData, xp, yp)) {
             return true;
-        } else {
-            UndoManager.Undo();
         }
         return false;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4752ddd9e..ffadc6edf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -17,10 +17,8 @@ import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { ClientRecommender } from '../../ClientRecommender';
-import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
-import { ClientUtils } from '../../util/ClientUtils';
 import { DocumentManager } from "../../util/DocumentManager";
 import { SnappingManager } from '../../util/SnappingManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
@@ -137,7 +135,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.removeEndListeners();
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
-        console.log(SelectionManager.SelectedDocuments());
         if (RadialMenu.Instance._display === false) {
             this.addHoldMoveListeners();
             this.addHoldEndListeners();
-- 
cgit v1.2.3-70-g09d2


From 2e6a3e1aa3fb5b82587a9c84aba8bccef96963d3 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Jun 2020 13:38:07 -0400
Subject: fixed docdecorations on collecction with side filter bar expanded. 
 cleaned up warnings/error messages a bit.

---
 src/client/util/DictationManager.ts                                  | 2 +-
 src/client/util/InteractionUtils.tsx                                 | 4 ++--
 src/client/views/collections/CollectionView.tsx                      | 5 +++--
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx  | 3 +--
 src/client/views/collections/collectionGrid/CollectionGridView.tsx   | 2 +-
 src/client/views/nodes/DocumentContentsView.tsx                      | 2 +-
 src/client/views/nodes/formattedText/SummaryView.tsx                 | 2 +-
 src/fields/Doc.ts                                                    | 2 +-
 8 files changed, 11 insertions(+), 11 deletions(-)

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

diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index e46225b4a..d8a5657c3 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -144,7 +144,7 @@ export namespace DictationManager {
             recognizer.start();
 
             return new Promise<string>((resolve, reject) => {
-                recognizer.onerror = (e: SpeechRecognitionError) => {
+                recognizer.onerror = (e: any) => { // e is SpeechRecognitionError but where is that defined? 
                     if (!(indefinite && e.error === "no-speech")) {
                         recognizer.stop();
                         reject(e);
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index aeb0f670d..df792c9c0 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -100,9 +100,9 @@ export namespace InteractionUtils {
             const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
 
             const bezierCurves = fitCurve(newPoints, parseInt(bezier));
-            for (var i = 0; i < bezierCurves.length; i++) {
+            for (const curve of bezierCurves) {
                 for (var t = 0; t < 1; t += 0.01) {
-                    const point = beziercurve(t, bezierCurves[i]);
+                    const point = beziercurve(t, curve);
                     pts.push({ X: point[0], Y: point[1] });
                 }
             }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index be79dbce1..2f38fb4b4 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -191,8 +191,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
     }
 
+    screenToLocalTransform = () => this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
     private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        const props: SubCollectionViewProps = { ...this.props, ...renderProps, CollectionView: this, annotationsKey: "" };
+        const props: SubCollectionViewProps = { ...this.props, ...renderProps, ScreenToLocalTransform: this.screenToLocalTransform, CollectionView: this, annotationsKey: "" };
         switch (type) {
             case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
             case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
@@ -442,7 +443,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     @computed get filterView() {
         const facetCollection = this.props.Document;
         const flyout = (
-            <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => fmovede.stopPropagation()}>
+            <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
                 {this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
                     <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey + "-filter"]).some(d => d.title === facet)} />
                     <span className="checkmark" />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 72cb2d218..571b5bf5c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -158,8 +158,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         if (retVal) {
             const newBoxes = (newBox instanceof Doc) ? [newBox] : newBox;
-            for (let i = 0; i < newBoxes.length; i++) {
-                const newBox = newBoxes[i];
+            for (const newBox of newBoxes) {
                 if (newBox.activeFrame !== undefined) {
                     const x = newBox.x;
                     const y = newBox.y;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index a5d355abc..2015ca930 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -163,7 +163,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             const savedLayouts = this.savedLayoutList;
             this.childLayoutPairs.forEach(({ layout: doc }) => {
                 const gridLayout = savedLayouts.find(gridLayout => gridLayout.i === doc[Id]);
-                gridLayout && Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout);
+                if (gridLayout) Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout);
             });
 
             if (this.props.Document.gridStartCompaction) {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 126e9ac14..f1438fd54 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import { Doc, Opt, Field, AclSym, AclPrivate } from "../../../fields/Doc";
 import { Cast, StrCast, NumCast } from "../../../fields/Types";
 import { OmitKeys, Without, emptyPath } from "../../../Utils";
-import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
+import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionSchemaView } from "../collections/CollectionSchemaView";
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 922285bd0..c017db034 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -14,7 +14,7 @@ export class SummaryView {
         const self = this;
         this._fieldWrapper = document.createElement("span");
         this._fieldWrapper.className = this.className(node.attrs.visibility);
-        this._fieldWrapper.onpointerdown = function (e: any) { self.onPointerDown(e, node, view, getPos); }
+        this._fieldWrapper.onpointerdown = function (e: any) { self.onPointerDown(e, node, view, getPos); };
         this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 96587af44..8c8720179 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -134,7 +134,7 @@ export class Doc extends RefField {
             has: (target, key) => target[AclSym] !== AclPrivate && key in target.__fields,
             ownKeys: target => {
                 const obj = {} as any;
-                (target[AclSym] !== AclPrivate) && Object.assign(obj, target.___fields);
+                if (target[AclSym] !== AclPrivate) Object.assign(obj, target.___fields);
                 runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
                 return Object.keys(obj);
             },
-- 
cgit v1.2.3-70-g09d2


From 1a03c645e5801fe0cfe71e1b1744313a41523de6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Jun 2020 20:07:44 -0400
Subject: made docFilters a prop on everything

---
 src/Utils.ts                                                     | 2 ++
 src/client/views/GestureOverlay.tsx                              | 3 ++-
 src/client/views/GlobalKeyHandler.ts                             | 4 ++--
 src/client/views/MainView.tsx                                    | 6 +++++-
 src/client/views/OverlayView.tsx                                 | 3 ++-
 src/client/views/Palette.tsx                                     | 3 ++-
 src/client/views/RecommendationsBox.tsx                          | 3 ++-
 src/client/views/TemplateMenu.tsx                                | 3 ++-
 src/client/views/collections/CollectionDockingView.tsx           | 4 ++--
 src/client/views/collections/CollectionLinearView.tsx            | 1 +
 src/client/views/collections/CollectionSchemaCells.tsx           | 3 ++-
 src/client/views/collections/CollectionSchemaView.tsx            | 1 +
 src/client/views/collections/CollectionStackingView.tsx          | 1 +
 src/client/views/collections/CollectionSubView.tsx               | 9 +++++++--
 src/client/views/collections/CollectionTreeView.tsx              | 6 ++++--
 src/client/views/collections/CollectionView.tsx                  | 3 ++-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx    | 1 +
 .../collectionMulticolumn/CollectionMulticolumnView.tsx          | 1 +
 .../collections/collectionMulticolumn/CollectionMultirowView.tsx | 1 +
 src/client/views/nodes/DocHolderBox.tsx                          | 2 ++
 src/client/views/nodes/DocumentView.tsx                          | 2 ++
 src/client/views/nodes/FieldView.tsx                             | 1 +
 src/client/views/nodes/ImageBox.tsx                              | 1 +
 src/client/views/nodes/KeyValuePair.tsx                          | 3 ++-
 src/client/views/nodes/PDFBox.tsx                                | 2 +-
 src/client/views/nodes/VideoBox.tsx                              | 1 +
 src/client/views/nodes/WebBox.tsx                                | 1 +
 src/client/views/nodes/formattedText/DashDocView.tsx             | 3 ++-
 src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx | 3 ++-
 src/client/views/nodes/formattedText/RichTextSchema.tsx          | 1 +
 src/client/views/pdf/PDFViewer.tsx                               | 1 +
 src/client/views/presentationview/PresElementBox.tsx             | 1 +
 src/client/views/search/SearchItem.tsx                           | 3 ++-
 src/mobile/MobileInterface.tsx                                   | 5 ++++-
 34 files changed, 66 insertions(+), 22 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index e527634fd..dba802f98 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -395,6 +395,8 @@ export function returnZero() { return 0; }
 
 export function returnEmptyString() { return ""; }
 
+export function returnEmptyFilter() { return [] as string[]; }
+
 export let emptyPath = [];
 
 export function emptyFunction() { }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 79d407c7a..aeac1d4a9 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -8,7 +8,7 @@ import MobileInkOverlay from "../../mobile/MobileInkOverlay";
 import MobileInterface from "../../mobile/MobileInterface";
 import { GestureUtils } from "../../pen-gestures/GestureUtils";
 import { MobileInkOverlayContent } from "../../server/Message";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from "../../Utils";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
 import { DocServer } from "../DocServer";
 import { DocUtils } from "../documents/Documents";
@@ -845,6 +845,7 @@ export default class GestureOverlay extends Touchable {
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
             />;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index fead46301..f5adeeb00 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -267,8 +267,8 @@ export default class KeyManager {
                     const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.SelectedDocuments().map(dv => dv.Document[Id]).join(":");
                     SelectionManager.SelectedDocuments().length && navigator.clipboard.writeText(text);
                     stopPropagation = false;
-                    preventDefault = false;
-                } else preventDefault = false
+                }
+                preventDefault = false;
                 break;
         }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2f68004f9..4a09f9ff3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -20,7 +20,7 @@ import { listSpec } from '../../fields/Schema';
 import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils, returnEmptyFilter } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
@@ -310,6 +310,7 @@ export class MainView extends React.Component {
             parentActive={returnTrue}
             whenActiveChanged={emptyFunction}
             bringToFront={emptyFunction}
+            docFilters={returnEmptyFilter}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined}
         />;
@@ -412,6 +413,7 @@ export class MainView extends React.Component {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
             </div>
@@ -438,6 +440,7 @@ export class MainView extends React.Component {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
                 <button className="mainView-settings" key="settings" onClick={() => SettingsManager.Instance.open()}>
@@ -529,6 +532,7 @@ export class MainView extends React.Component {
                     renderDepth={0}
                     focus={emptyFunction}
                     whenActiveChanged={emptyFunction}
+                    docFilters={returnEmptyFilter}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
             </div>;
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index f6e5e1705..37d8dd23b 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
 import { Doc, DocListCast, Opt } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { NumCast, Cast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
 import { DocumentView } from "./nodes/DocumentView";
@@ -202,6 +202,7 @@ export class OverlayView extends React.Component {
                     backgroundColor={returnEmptyString}
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
+                    docFilters={returnEmptyFilter}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
             </div>;
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 108eb83d6..0a4334302 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc } from "../../fields/Doc";
 import { NumCast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { DocumentView } from "./nodes/DocumentView";
 import "./Palette.scss";
@@ -59,6 +59,7 @@ export default class Palette extends React.Component<PaletteProps> {
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
+                            docFilters={returnEmptyFilter}
                             ContainingCollectionView={undefined}
                             ContainingCollectionDoc={undefined} />
                         <div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index c98363214..cdde32c21 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -6,7 +6,7 @@ import "./RecommendationsBox.scss";
 import { Doc, DocListCast, WidthSym, HeightSym } from "../../fields/Doc";
 import { DocumentIcon } from "./nodes/DocumentIcon";
 import { StrCast, NumCast } from "../../fields/Types";
-import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath, returnZero } from "../../Utils";
+import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath, returnZero, returnEmptyFilter } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { ObjectField } from "../../fields/ObjectField";
 import { DocumentView } from "./nodes/DocumentView";
@@ -82,6 +82,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
                 parentActive={returnFalse}
                 whenActiveChanged={returnFalse}
                 bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
                 ContentScaling={scale}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index f135823a8..916e631d0 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -10,7 +10,7 @@ import { Doc, DocListCast } from "../../fields/Doc";
 import { Docs, DocUtils, } from "../documents/Documents";
 import { StrCast, Cast } from "../../fields/Types";
 import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero } from "../../Utils";
+import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { ScriptField, ComputedField } from "../../fields/ScriptField";
 import { Scripting } from "../util/Scripting";
@@ -140,6 +140,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 CollectionView={undefined}
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
+                docFilters={returnEmptyFilter}
                 rootSelected={returnFalse}
                 onCheckedClick={this.scriptField!}
                 onChildClick={this.scriptField!}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1c0b60b2b..a969e302d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,11 +8,10 @@ import * as GoldenLayout from "../../../client/goldenLayout";
 import { DateField } from '../../../fields/DateField';
 import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
-import { List } from '../../../fields/List';
 import { FieldId } from "../../../fields/RefField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils";
+import { emptyFunction, returnOne, returnTrue, Utils, returnZero, returnEmptyFilter } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -843,6 +842,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
             addDocTab={this.addDocTab}
             pinToPres={DockedFrameRenderer.PinDoc}
+            docFilters={returnEmptyFilter}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined} />;
     }
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index f1002044a..f38eeaf41 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -124,6 +124,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 parentActive={returnTrue}
                                 whenActiveChanged={emptyFunction}
                                 bringToFront={emptyFunction}
+                                docFilters={this.props.docFilters}
                                 ContainingCollectionView={undefined}
                                 ContainingCollectionDoc={undefined} />
                         </div>;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index baf9d4156..2b8110e27 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -3,7 +3,7 @@ import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { CellInfo } from "react-table";
 import "react-table/react-table.css";
-import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter } from "../../../Utils";
 import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { KeyCodes } from "../../util/KeyCodes";
@@ -159,6 +159,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             bringToFront: emptyFunction,
             rootSelected: returnFalse,
             fieldKey: this.props.rowProps.column.id as string,
+            docFilters: returnEmptyFilter,
             ContainingCollectionView: this.props.CollectionView,
             ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
             isSelected: returnFalse,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 6dbee217a..56a2a517c 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -133,6 +133,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     PanelWidth={this.previewWidth}
                     PanelHeight={this.previewHeight}
                     ScreenToLocalTransform={this.getPreviewTransform}
+                    docFilters={this.docFilters}
                     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                     ContainingCollectionView={this.props.CollectionView}
                     moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9289c4cf6..4aab43b08 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -217,6 +217,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             ScreenToLocalTransform={dxf}
             opacity={opacity}
             focus={this.focusDocument}
+            docFilters={this.docFilters}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 93d20c475..00d6d59c8 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,7 +10,7 @@ import { WebField } from "../../../fields/URLField";
 import { Cast, ScriptCast, NumCast } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
 import { Upload } from "../../../server/SharedMediaTypes";
-import { Utils, returnFalse } from "../../../Utils";
+import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Networking } from "../../Network";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
@@ -101,8 +101,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
+        docFilters = () => {
+            return this.props.ignoreFields?.includes("_docFilters") ? [] :
+                this.props.docFilters !== returnEmptyFilter ? this.props.docFilters() :
+                    Cast(this.props.Document._docFilters, listSpec("string"), []);
+        }
         @computed get childDocs() {
-            const docFilters = this.props.ignoreFields?.includes("_docFilters") ? [] : Cast(this.props.Document._docFilters, listSpec("string"), []);
+            const docFilters = this.docFilters();
             const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
             const filterFacets: { [key: string]: { [value: string]: string } } = {};  // maps each filter key to an object with value=>modifier fields
             for (let i = 0; i < docFilters.length; i += 3) {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d75aed0cd..747eb36a1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -8,7 +8,7 @@ import { PrefetchProxy } from '../../../fields/Proxy';
 import { Document, listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
@@ -355,6 +355,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     PanelHeight={panelHeight}
                     focus={returnFalse}
                     ScreenToLocalTransform={this.docTransform}
+                    docFilters={returnEmptyFilter}
                     ContainingCollectionDoc={this.props.containingCollection}
                     ContainingCollectionView={undefined}
                     addDocument={returnFalse}
@@ -472,6 +473,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
                         dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildViews)}
+                        docFilters={returnEmptyFilter}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={this.props.containingCollection}
                     />}
@@ -490,7 +492,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 <div className="treeViewItem-header" ref={this._header} onClick={e => {
                     if (this.props.active(true)) {
                         e.stopPropagation();
-                        e.preventDefault()
+                        e.preventDefault();
                         SelectionManager.DeselectAll();
                     }
                 }}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2f38fb4b4..33615cc68 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -17,7 +17,7 @@ import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnEmptyFilter } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -467,6 +467,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         DataDoc={facetCollection}
                         fieldKey={`${this.props.fieldKey}-filter`}
                         CollectionView={this}
+                        docFilters={returnEmptyFilter}
                         ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                         ContainingCollectionView={this.props.ContainingCollectionView}
                         PanelWidth={this.facetWidth}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 571b5bf5c..00260745d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -958,6 +958,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
             ContainingCollectionDoc: this.props.Document,
+            docFilters: this.docFilters,
             focus: this.focusDocument,
             backgroundColor: this.getClusterColor,
             backgroundHalo: this.backgroundHalo,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index c0e1a0232..776266ce6 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -234,6 +234,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
+            docFilters={this.docFilters}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 602246d07..1703ff4dc 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -233,6 +233,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
+            docFilters={this.docFilters}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index a4c4663dd..0cf5505cc 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -119,6 +119,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     Document={containedDoc}
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
+                    docFilters={this.props.docFilters}
                     ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                     ContainingCollectionDoc={undefined}
                     fitToBox={true}
@@ -147,6 +148,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     Document={containedDoc}
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
+                    docFilters={this.props.docFilters}
                     ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                     ContainingCollectionDoc={undefined}
                     fitToBox={true}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ffadc6edf..3205e3050 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -51,6 +51,7 @@ export type DocFocusFunc = () => boolean;
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    docFilters: () => string[];
     FreezeDimensions?: boolean;
     NativeWidth: () => number;
     NativeHeight: () => number;
@@ -996,6 +997,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         TraceMobx();
         return (<>
             <DocumentContentsView key={1}
+                docFilters={this.props.docFilters}
                 ContainingCollectionView={this.props.ContainingCollectionView}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                 NativeWidth={this.NativeWidth}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index e9dc43bd8..305c04a90 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -27,6 +27,7 @@ export interface FieldViewProps {
     LibraryPath: Doc[];
     onClick?: ScriptField;
     dropAction: dropActionType;
+    docFilters: () => string[];
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (isCtrlPressed: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 69cffb6c2..84d49681c 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -473,6 +473,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 CollectionView={undefined}
                 ScreenToLocalTransform={this.screenToLocalTransform}
                 renderDepth={this.props.renderDepth + 1}
+                docFilters={this.props.docFilters}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                 {this.contentFunc}
             </CollectionFreeFormView>
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 3cbe3e494..4568a6b16 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import { Doc, Field, Opt } from '../../../fields/Doc';
-import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
@@ -56,6 +56,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             Document: this.props.doc,
             DataDoc: this.props.doc,
             LibraryPath: [],
+            docFilters:returnEmptyFilter,
             ContainingCollectionView: undefined,
             ContainingCollectionDoc: undefined,
             fieldKey: this.props.keyName,
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fae216f17..aabaac39e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -234,7 +234,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
             <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
                 setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
                 renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
-                addDocTab={this.props.addDocTab} focus={this.props.focus}
+                addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters}
                 pinToPres={this.props.pinToPres} addDocument={this.addDocument}
                 Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
                 ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e4dbceca6..71556bfd3 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -383,6 +383,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     CollectionView={undefined}
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform}
                     renderDepth={this.props.renderDepth + 1}
+                    docFilters={this.props.docFilters}
                     ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                     {this.contentFunc}
                 </CollectionFreeFormView>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index fb9e57b51..b726a6df9 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -488,6 +488,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                         CollectionView={undefined}
                         ScreenToLocalTransform={this.scrollXf}
                         renderDepth={this.props.renderDepth + 1}
+                        docFilters={this.props.docFilters}
                         ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                     </CollectionFreeFormView>
                 </div>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 55b3f6f1e..5c3f3dcc9 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,7 +5,7 @@ import { Id } from "../../../../fields/FieldSymbols";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { ComputedField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from "../../../documents/Documents";
 import { DocumentView } from "../DocumentView";
@@ -254,6 +254,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                             whenActiveChanged={returnFalse}
                             bringToFront={emptyFunction}
                             dontRegisterView={false}
+                            docFilters={this.props.tbox?.props.docFilters||returnEmptyFilter}
                             ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                             ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                             ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 000b3c2e5..7c697033c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
 import { Doc, DocCastAsync } from "../../../../fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { schema } from "./schema_rts";
@@ -222,6 +222,7 @@ export class FormattedTextBoxComment {
                                     addDocTab={returnFalse}
                                     pinToPres={returnFalse}
                                     dontRegisterView={true}
+                                    docFilters={returnEmptyFilter}
                                     ContainingCollectionDoc={undefined}
                                     ContainingCollectionView={undefined}
                                     renderDepth={1}
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 05557e22a..a989abd6a 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -152,6 +152,7 @@ export class DashDocView {
                     whenActiveChanged={returnFalse}
                     bringToFront={emptyFunction}
                     dontRegisterView={false}
+                    docFilters={this._textBox.props.docFilters}
                     ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                     ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                     ContentScaling={this.contentScaling}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c02dd6786..516774f44 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -51,6 +51,7 @@ interface IViewerProps {
     fieldKey: string;
     Document: Doc;
     DataDoc?: Doc;
+    docFilters: () => string[];
     ContainingCollectionView: Opt<CollectionView>;
     PanelWidth: () => number;
     PanelHeight: () => number;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index caee06d8f..6b59a0563 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -194,6 +194,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     whenActiveChanged={returnFalse}
                     bringToFront={returnFalse}
                     opacity={returnOne}
+                    docFilters={this.props.docFilters}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     ContentScaling={returnOne}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 24d6e9d6f..74262d81a 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -7,7 +7,7 @@ import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero, returnEmptyString, returnEmptyFilter } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, SetupDrag } from "../../util/DragManager";
@@ -164,6 +164,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     removeDocument={returnFalse}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
+                    docFilters={returnEmptyFilter}
                     ContainingCollectionDoc={undefined}
                     ContainingCollectionView={undefined}
                     ScreenToLocalTransform={Transform.Identity}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index da14ffc88..a50ec103e 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -22,7 +22,7 @@ import { listSpec } from '../fields/Schema';
 import { Cast, FieldValue } from '../fields/Types';
 import { WebField } from "../fields/URLField";
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils';
 import "./MobileInterface.scss";
 import { CollectionView } from '../client/views/collections/CollectionView';
 import { InkingStroke } from '../client/views/InkingStroke';
@@ -126,6 +126,7 @@ export default class MobileInterface extends React.Component {
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />;
         }
@@ -205,6 +206,7 @@ export default class MobileInterface extends React.Component {
                         whenActiveChanged={returnFalse}
                         ScreenToLocalTransform={Transform.Identity}
                         renderDepth={0}
+                        docFilters={returnEmptyFilter}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={undefined}
                         rootSelected={returnTrue}>
@@ -293,6 +295,7 @@ export default class MobileInterface extends React.Component {
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
+                        docFilters={returnEmptyFilter}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={undefined} />
                 </div>
-- 
cgit v1.2.3-70-g09d2


From d8feecd0542670233e6ea87006a89eb00d10a17e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 18 Jun 2020 17:59:48 -0400
Subject: fixed keyvalue pane inputting new key values.  made collection
 chromes shrink a little when window shrinks.

---
 src/client/views/MainView.tsx                      |  8 ++--
 src/client/views/collections/CollectionView.tsx    | 11 ++---
 .../views/collections/CollectionViewChromes.scss   |  2 +-
 .../views/collections/CollectionViewChromes.tsx    | 10 ++---
 src/client/views/nodes/KeyValueBox.tsx             | 51 +++++++++-------------
 5 files changed, 34 insertions(+), 48 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4a09f9ff3..fcaa349cd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -201,9 +201,9 @@ export class MainView extends React.Component {
 
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const cloneWorkspace = ScriptField.MakeScript(`cloneWorkspace()`);
-        workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneWorkspace!]);
-        workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Workspace Layout"]);
+        const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`);
+        workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyWorkspace!]);
+        workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Workspace"]);
 
         Doc.AddDocToList(workspaces, "data", workspaceDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
@@ -582,7 +582,7 @@ export class MainView extends React.Component {
 }
 Scripting.addGlobal(function freezeSidebar() { MainView.expandFlyout(); });
 Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
-Scripting.addGlobal(function cloneWorkspace() {
+Scripting.addGlobal(function copyWorkspace() {
     const copiedWorkspace = Doc.MakeCopy(Cast(Doc.UserDoc().activeWorkspace, Doc, null), true);
     const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc, null);
     Doc.AddDocToList(workspaces, "data", copiedWorkspace);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 33615cc68..508b9e5e7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -516,13 +516,10 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutTemplate: this.childLayoutTemplate,
             ChildLayoutString: this.childLayoutString,
         };
-        return (<div className={"collectionView"}
-            style={{
-                pointerEvents: this.props.Document.isBackground ? "none" : undefined,
-                boxShadow: Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
-                    `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31)" : "#9c9396"} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
-            }}
-            onContextMenu={this.onContextMenu}>
+        const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
+            `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+        return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
+            style={{ pointerEvents: this.props.Document.isBackground ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
             <div className="collectionView-facetCont" style={{ width: `calc(100% - ${this.facetWidth()}px)` }}>
                 {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index f85cbfee2..77a12ed37 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -15,7 +15,7 @@
         padding-bottom: 1px;
         height: 32px;
         border-bottom: .5px solid rgb(180, 180, 180);
-        overflow: hidden;
+        overflow: visible;
 
         .collectionViewBaseChrome {
             display: flex;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 48810f1e9..52fb63386 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -259,10 +259,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                     <FontAwesomeIcon icon="bullseye" size="2x" />
                 </div>
                 <select
-                    className="collectionViewBaseChrome-cmdPicker"
-                    onPointerDown={stopPropagation}
-                    onChange={this.commandChanged}
-                    value={this._currentKey}>
+                    className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}
+                    style={{ width: this.props.PanelWidth() < 300 ? 15 : undefined }}>
                     <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""} />
                     {this._buttonizableCommands.map(cmd =>
                         <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
@@ -280,7 +278,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                     <FontAwesomeIcon icon="bullseye" size="2x" />
                 </div>
                 <select
-                    className="collectionViewBaseChrome-viewPicker"
+                    className="collectionViewBaseChrome-viewPicker" style={{ width: this.props.PanelWidth() < 300 ? 15 : undefined }}
                     onPointerDown={stopPropagation}
                     onChange={this.viewChanged}
                     value={StrCast(this.props.CollectionView.props.Document._viewType)}>
@@ -713,7 +711,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewChro
                 </span>
 
                 <select className="collectionGridViewChrome-viewPicker"
-                    style={{ marginRight: 5 }}
+                    style={{ marginRight: 5, width: this.props.PanelWidth() < 300 ? 25 : undefined }}
                     onPointerDown={stopPropagation}
                     onChange={this.changeCompactType}
                     value={StrCast(this.props.CollectionView.props.Document.gridStartCompaction, StrCast(this.props.CollectionView.props.Document.gridCompaction))}>
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index e983852ea..4442ee2eb 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -18,6 +18,7 @@ import { KeyValuePair } from "./KeyValuePair";
 import React = require("react");
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
+import e = require("express");
 
 export type KVPScript = {
     script: CompiledScript;
@@ -31,10 +32,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     private _mainCont = React.createRef<HTMLDivElement>();
     private _keyHeader = React.createRef<HTMLTableHeaderCellElement>();
+    private _keyInput = React.createRef<HTMLInputElement>();
+    private _valInput = React.createRef<HTMLInputElement>();
 
     @observable private rows: KeyValuePair[] = [];
-    @observable private _keyInput: string = "";
-    @observable private _valueInput: string = "";
+
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
     get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
 
@@ -42,10 +44,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     onEnterKey = (e: React.KeyboardEvent): void => {
         if (e.key === 'Enter') {
             e.stopPropagation();
-            if (this._keyInput && this._valueInput && this.fieldDocToLayout) {
-                if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput, this._valueInput)) {
-                    this._keyInput = "";
-                    this._valueInput = "";
+            if (this._keyInput.current?.value && this._valInput.current?.value && this.fieldDocToLayout) {
+                if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput.current.value, this._valInput.current.value)) {
+                    this._keyInput.current.value = "";
+                    this._valInput.current.value = "";
+                    document.body.focus();
                 }
             }
         }
@@ -103,7 +106,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     rowHeight = () => 30;
 
-    createTable = () => {
+    @computed get createTable() {
         const doc = this.fieldDocToLayout;
         if (!doc) {
             return <tr><td>Loading...</td></tr>;
@@ -136,29 +139,17 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         }
         return rows;
     }
-
-    @action
-    keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this._keyInput = e.currentTarget.value;
+    @computed get newKeyValue() {
+        return <tr className="keyValueBox-valueRow">
+            <td className="keyValueBox-td-key" onClick={(e) => { this._keyInput.current!.select(); e.stopPropagation(); }} style={{ width: `${100 - this.splitPercentage}%` }}>
+                <input style={{ width: "100%" }} ref={this._keyInput} type="text" placeholder="Key" />
+            </td>
+            <td className="keyValueBox-td-value" onClick={(e) => { this._valInput.current!.select(); e.stopPropagation(); }} style={{ width: `${this.splitPercentage}%` }}>
+                <input style={{ width: "100%" }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} />
+            </td>
+        </tr>
     }
 
-    @action
-    valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this._valueInput = e.currentTarget.value;
-    }
-
-    newKeyValue = () =>
-        (
-            <tr className="keyValueBox-valueRow">
-                <td className="keyValueBox-td-key" style={{ width: `${100 - this.splitPercentage}%` }}>
-                    <input style={{ width: "100%" }} type="text" value={this._keyInput} placeholder="Key" onChange={this.keyChanged} />
-                </td>
-                <td className="keyValueBox-td-value" style={{ width: `${this.splitPercentage}%` }}>
-                    <input style={{ width: "100%" }} type="text" value={this._valueInput} placeholder="Value" onChange={this.valueChanged} onKeyDown={this.onEnterKey} />
-                </td>
-            </tr>
-        )
-
     @action
     onDividerMove = (e: PointerEvent): void => {
         const nativeWidth = this._mainCont.current!.getBoundingClientRect();
@@ -260,8 +251,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                         >Key</th>
                         <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>Fields</th>
                     </tr>
-                    {this.createTable()}
-                    {this.newKeyValue()}
+                    {this.createTable}
+                    {this.newKeyValue}
                 </tbody>
             </table>
             {dividerDragger}
-- 
cgit v1.2.3-70-g09d2

