From 5b69b2cba13f104471dc08e110148704fdc2acca Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Jul 2019 12:45:28 -0400
Subject: fixes for templating miniLayout and detailedLayouts

---
 src/client/views/collections/CollectionView.tsx | 9 ++++++---
 1 file changed, 6 insertions(+), 3 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 56750668d..377a46535 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,11 +1,10 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
+import { faProjectDiagram, faSignature, faSquare, faTh, faImage, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { Docs } from '../../documents/Documents';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -16,6 +15,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
 import { CollectionTreeView } from "./CollectionTreeView";
+import { StrCast, PromiseValue } from '../../../new_fields/Types';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
@@ -24,6 +24,7 @@ library.add(faSquare);
 library.add(faProjectDiagram);
 library.add(faSignature);
 library.add(faThList);
+library.add(faImage);
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
@@ -63,6 +64,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
                     otherdoc.height = 50;
                     Doc.GetProto(otherdoc).title = "applied(" + this.props.Document.title + ")";
                     Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
+                    Doc.GetProto(otherdoc).miniLayout = StrCast(this.props.Document.miniLayout);
+                    Doc.GetProto(otherdoc).detailedLayout = Doc.GetProto(otherdoc).layout;
                     this.props.addDocTab && this.props.addDocTab(otherdoc, undefined, "onRight");
                 }), icon: "project-diagram"
             });
-- 
cgit v1.2.3-70-g09d2


From 922c418f1207c43150c499e075a9c1be34719b58 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Jul 2019 17:36:37 -0400
Subject: fixes for stacking view and added detailedLayout

---
 src/client/views/collections/CollectionStackingView.scss |  5 +++--
 src/client/views/collections/CollectionStackingView.tsx  |  8 ++++----
 src/client/views/collections/CollectionView.tsx          |  3 ++-
 src/client/views/nodes/DocumentView.tsx                  | 13 ++++---------
 src/new_fields/Doc.ts                                    | 11 +++++++++--
 5 files changed, 22 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index b6ad47813..7ebf5f77c 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,8 +1,9 @@
 @import "../globalCssVariables";
 .collectionStackingView {
-    overflow-y: auto;
     height: 100%;
-
+    width: 100%;
+    position: absolute;
+    overflow-y: auto;
     .collectionStackingView-docView-container {
         width: 45%;
         margin: 5% 2.5%;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index c42a423c1..4424cffe1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -15,6 +15,7 @@ import { DragManager } from "../../util/DragManager";
 import { DocumentType } from "../../documents/Documents";
 import { Transform } from "../../util/Transform";
 import { CursorProperty } from "csstype";
+import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss";
 import { string } from "prop-types";
 
 @observer
@@ -107,6 +108,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
         return this.offsetTransform(doc, translateX, translateY);
     }
+
     getSingleDocTransform(doc: Doc, ind: number, width: number) {
         let localY = this.filteredChildren.reduce((height, d, i) =>
             height + (i < ind ? this.getDocHeight(Doc.expandTemplateLayout(d, this.props.DataDoc)) + this.gridGap : 0), this.yMargin);
@@ -234,7 +236,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
         let templatecols = "";
         for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `;
-        return <>
+        return <div key={heading}>
             {heading ? <div key={`${heading}`} className="collectionStackingView-sectionHeader">{heading}</div> : (null)}
             <div key={`${heading}-stack`} className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
                 style={{
@@ -250,7 +252,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             >
                 {this.children(docList)}
                 {this.singleColumn ? (null) : this.columnDragger}
-            </div></>;
+            </div></div>;
     }
     render() {
         let sectionFilter = StrCast(this.props.Document.sectionFilter);
@@ -261,8 +263,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         });
         return (
             <div className="collectionStackingView"
-
-                style={{ height: this.props.Document[HeightSym]() }}
                 ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
                 {/* {sectionFilter as boolean ? [
                     ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())],
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 377a46535..1b85a0cdb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -46,6 +46,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
 
     get isAnnotationOverlay() { return this.props.fieldExt ? true : false; }
 
+    static _applyCount: number = 0;
     onContextMenu = (e: React.MouseEvent): void => {
         if (!this.isAnnotationOverlay && !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
             let subItems: ContextMenuProps[] = [];
@@ -62,7 +63,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
                     let otherdoc = new Doc();
                     otherdoc.width = 100;
                     otherdoc.height = 50;
-                    Doc.GetProto(otherdoc).title = "applied(" + this.props.Document.title + ")";
+                    Doc.GetProto(otherdoc).title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied"
                     Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
                     Doc.GetProto(otherdoc).miniLayout = StrCast(this.props.Document.miniLayout);
                     Doc.GetProto(otherdoc).detailedLayout = Doc.GetProto(otherdoc).layout;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 899c47cb3..76b8658a5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -291,7 +291,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this._doubleTap && this.props.renderDepth) {
             let fullScreenAlias = Doc.MakeAlias(this.props.Document);
             fullScreenAlias.templates = new List<string>();
-            if (this.props.Document.layout === this.props.Document.miniLayout) fullScreenAlias.layout = this.props.Document.detailedLayout instanceof Doc ? this.props.Document.detailedLayout : StrCast(this.props.Document.detailedLayout);
+            if (this.props.Document.layout === this.props.Document.miniLayout) {
+                Doc.ToggleDetailLayout(fullScreenAlias);
+            }
             this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
             SelectionManager.DeselectAll();
             this.props.Document.libraryBrush = undefined;
@@ -557,14 +559,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }, icon: "search"
         });
         if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
-            cm.addItem({
-                description: "Toggle detail", event: async () => {
-                    let d = this.props.Document;
-                    let miniLayout = await PromiseValue(d.miniLayout);
-                    let detailLayout = await PromiseValue(d.detailedLayout);
-                    d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
-                }, icon: "image"
-            });
+            cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
         }
         cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
         cm.addItem({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 7180564ea..9a4b817d3 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -316,7 +316,7 @@ export namespace Doc {
         if (extensionDoc === undefined) {
             setTimeout(() => {
                 let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
-                docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey;
+                docExtensionForField.title = doc.title + ":" + fieldKey + ".ext";
                 docExtensionForField.extendsDoc = doc;
                 let proto: Doc | undefined = doc;
                 while (proto && !Doc.IsPrototype(proto)) {
@@ -351,7 +351,7 @@ export namespace Doc {
         if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) {
             setTimeout(() => {
                 templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
-                (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + " applied to " + dataDoc.title;
+                (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]";  // previously: "applied to"
                 (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).isExpandedTemplate = templateLayoutDoc;
             }, 0);
         }
@@ -420,4 +420,11 @@ export namespace Doc {
         fieldTemplate.showTitle = "title";
         setTimeout(() => fieldTemplate.proto = proto);
     }
+
+    export async function ToggleDetailLayout(d: Doc) {
+        let miniLayout = await PromiseValue(d.miniLayout);
+        let detailLayout = await PromiseValue(d.detailedLayout);
+        d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
+        if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined;
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 2b98b5484b942361ab5e271fe9e09d09dc83fa60 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 19 Jul 2019 00:51:15 -0400
Subject: more cleanup for templates

---
 src/client/views/collections/CollectionView.tsx          |  6 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  3 +--
 src/client/views/nodes/WebBox.tsx                        | 16 ----------------
 src/new_fields/Doc.ts                                    | 14 +++++++++-----
 4 files changed, 13 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 1b85a0cdb..b6b690e26 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -2,7 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core';
 import { faProjectDiagram, faSignature, faSquare, faTh, faImage, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
+import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 import { undoBatch } from '../../util/UndoManager';
@@ -61,8 +61,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
             ContextMenu.Instance.addItem({
                 description: "Apply Template", event: undoBatch(() => {
                     let otherdoc = new Doc();
-                    otherdoc.width = 100;
-                    otherdoc.height = 50;
+                    otherdoc.width = this.props.Document[WidthSym]();
+                    otherdoc.height = this.props.Document[HeightSym]();
                     Doc.GetProto(otherdoc).title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied"
                     Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
                     Doc.GetProto(otherdoc).miniLayout = StrCast(this.props.Document.miniLayout);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 58218e641..1997a6a4d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
@@ -516,7 +516,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     ]
     render() {
         const easing = () => this.props.Document.panTransformType === "Ease";
-
         Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
         return (
             <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f0a9ec6d8..162ac1d98 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -9,22 +9,6 @@ import React = require("react");
 import { InkTool } from "../../../new_fields/InkField";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 
-export function onYouTubeIframeAPIReady() {
-    console.log("player");
-    return;
-    let player = new YT.Player('player', {
-        events: {
-            'onReady': onPlayerReady
-        }
-    });
-}
-// must cast as any to set property on window
-const _global = (window /* browser */ || global /* node */) as any;
-_global.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
-
-function onPlayerReady(event: any) {
-    event.target.playVideo();
-}
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 9a4b817d3..7bf1dec53 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -10,6 +10,7 @@ import { RefField, FieldId } from "./RefField";
 import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols";
 import { scriptingGlobal } from "../client/util/Scripting";
 import { List } from "./List";
+import { string } from "prop-types";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -344,20 +345,23 @@ export namespace Doc {
         // ... which means we change the layout to be an expanded view of the template layout.  
         // This allows the view override the template's properties and be referenceable as its own document.
 
-        let expandedTemplateLayout = templateLayoutDoc["_expanded_" + dataDoc[Id]];
+        let expandedTemplateLayout = dataDoc[templateLayoutDoc[Id]];
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
         if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) {
             setTimeout(() => {
-                templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
-                (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]";  // previously: "applied to"
-                (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).isExpandedTemplate = templateLayoutDoc;
+                let expandedDoc = Doc.MakeDelegate(templateLayoutDoc);
+                expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]";
+                expandedDoc.isExpandedTemplate = templateLayoutDoc;
+                dataDoc[templateLayoutDoc[Id]] = expandedDoc;
             }, 0);
         }
-        return templateLayoutDoc;
+        return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.
     }
 
+    let _pendingExpansions: Map<string, boolean> = new Map();
+
     export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
         const copy = new Doc;
         Object.keys(doc).forEach(key => {
-- 
cgit v1.2.3-70-g09d2


From 64de59fc4250eff5284380ca2743030aa453fce7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 19 Jul 2019 14:51:36 -0400
Subject: gave templates a type ... but is it the right one?

---
 src/client/views/collections/CollectionView.tsx | 10 ++++++----
 src/new_fields/Doc.ts                           |  1 -
 2 files changed, 6 insertions(+), 5 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 b6b690e26..7e9d35d3d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -16,6 +16,7 @@ import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import { StrCast, PromiseValue } from '../../../new_fields/Types';
+import { DocumentType } from '../../documents/Documents';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
@@ -63,10 +64,11 @@ export class CollectionView extends React.Component<FieldViewProps> {
                     let otherdoc = new Doc();
                     otherdoc.width = this.props.Document[WidthSym]();
                     otherdoc.height = this.props.Document[HeightSym]();
-                    Doc.GetProto(otherdoc).title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied"
-                    Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
-                    Doc.GetProto(otherdoc).miniLayout = StrCast(this.props.Document.miniLayout);
-                    Doc.GetProto(otherdoc).detailedLayout = Doc.GetProto(otherdoc).layout;
+                    otherdoc.title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied"
+                    otherdoc.layout = Doc.MakeDelegate(this.props.Document);
+                    otherdoc.miniLayout = StrCast(this.props.Document.miniLayout);
+                    otherdoc.detailedLayout = otherdoc.layout;
+                    otherdoc.type = DocumentType.TEMPLATE;
                     this.props.addDocTab && this.props.addDocTab(otherdoc, undefined, "onRight");
                 }), icon: "project-diagram"
             });
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index c6b364d8e..2ad6ae5f0 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -423,7 +423,6 @@ export namespace Doc {
         fieldTemplate.nativeHeight = nh;
         fieldTemplate.isTemplate = true;
         fieldTemplate.showTitle = "title";
-        fieldTemplate.type = DocumentType.TEMPLATE;
         setTimeout(() => fieldTemplate.proto = proto);
     }
 
-- 
cgit v1.2.3-70-g09d2


From 5e9bcf2e35415fd0ab4dec4f0141511cd4d312d0 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 19 Jul 2019 16:03:09 -0400
Subject: split masonry collection view into its own view type.

---
 src/client/documents/Documents.ts                  |  4 ++
 .../views/collections/CollectionBaseView.tsx       |  3 +-
 .../views/collections/CollectionStackingView.tsx   | 58 +++++++---------------
 src/client/views/collections/CollectionView.tsx    | 10 ++--
 4 files changed, 32 insertions(+), 43 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 11df6c152..47ed33adf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -410,6 +410,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking });
         }
 
+        export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Masonry });
+        }
+
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 2603dc60b..72faf52c4 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -18,7 +18,8 @@ export enum CollectionViewType {
     Schema,
     Docking,
     Tree,
-    Stacking
+    Stacking,
+    Masonry
 }
 
 export interface CollectionRenderProps {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 039bd7b3a..0e5f9a321 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -6,7 +6,6 @@ import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { BoolCast, NumCast, Cast, StrCast } from "../../../new_fields/Types";
 import { emptyFunction, Utils } from "../../../Utils";
-import { ContextMenu } from "../ContextMenu";
 import { CollectionSchemaPreview } from "./CollectionSchemaView";
 import "./CollectionStackingView.scss";
 import { CollectionSubView } from "./CollectionSubView";
@@ -15,16 +14,14 @@ import { DragManager } from "../../util/DragManager";
 import { DocumentType } from "../../documents/Documents";
 import { Transform } from "../../util/Transform";
 import { CursorProperty } from "csstype";
-import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss";
-import { string } from "prop-types";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     _heightDisposer?: IReactionDisposer;
-    _gridSize = 1;
     _docXfs: any[] = [];
+    _columnStart: number = 0;
     @observable private cursor: CursorProperty = "grab";
     @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
     @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); }
@@ -33,24 +30,25 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); }
     @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
 
+    @computed get Sections() {
+        let sectionFilter = StrCast(this.props.Document.sectionFilter);
+        let fields = new Map<object, Doc[]>();
+        sectionFilter && this.filteredChildren.map(d => {
+            let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object;
+            if (!fields.has(sectionValue)) fields.set(sectionValue, [d]);
+            else fields.get(sectionValue)!.push(d);
+        });
+        return fields;
+    }
     componentDidMount() {
         this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])],
-            () => {
-                if (this.singleColumn) {
-                    let sectionFilter = StrCast(this.props.Document.sectionFilter);
-                    let fields = new Map<object, Doc[]>();
-                    sectionFilter && this.filteredChildren.map(d => {
-                        if (!fields.has(d[sectionFilter] as object)) fields.set(d[sectionFilter] as object, [d]);
-                        else fields.get(d[sectionFilter] as object)!.push(d);
-                    });
-                    (this.props.Document.height = fields.size * 50 + this.filteredChildren.reduce((height, d, i) =>
-                        height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin));
-                }
-            }
+            () => this.singleColumn &&
+                (this.props.Document.height = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) =>
+                    height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin))
             , { fireImmediately: true });
     }
     componentWillUnmount() {
-        if (this._heightDisposer) this._heightDisposer();
+        this._heightDisposer && this._heightDisposer();
     }
 
     @action
@@ -98,7 +96,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return (nw && nh) ? wid * aspect : d[HeightSym]();
     }
 
-
     offsetTransform(doc: Doc, translateX: number, translateY: number) {
         let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
         let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
@@ -133,7 +130,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             } else {
                 let dref = React.createRef<HTMLDivElement>();
                 let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
-                let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap));
+                let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
                 this._docXfs.push({ dxf: dxf, width: width, height: height });
                 return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
                     {this.getDisplayDoc(layoutDoc, d, dxf)}
@@ -142,7 +139,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         });
     }
 
-    _columnStart: number = 0;
     columnDividerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
@@ -156,7 +152,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
         let delta = dragPos - this._columnStart;
         this._columnStart = dragPos;
-
         this.props.Document.columnWidth = this.columnWidth + delta;
     }
 
@@ -172,14 +167,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             <FontAwesomeIcon icon={"arrows-alt-h"} />
         </div>;
     }
-    onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({
-                description: "Toggle multi-column",
-                event: () => this.props.Document.singleColumn = !BoolCast(this.props.Document.singleColumn, true), icon: "file-pdf"
-            });
-        }
-    }
 
     @undoBatch
     @action
@@ -247,7 +234,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     position: "relative",
                     gridGap: this.gridGap,
                     gridTemplateColumns: this.singleColumn ? undefined : templatecols,
-                    gridAutoRows: this.singleColumn ? undefined : `${this._gridSize}px`
+                    gridAutoRows: this.singleColumn ? undefined : "0px"
                 }}
             >
                 {this.children(docList)}
@@ -255,21 +242,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             </div></div>;
     }
     render() {
-        let sectionFilter = StrCast(this.props.Document.sectionFilter);
-        let fields = new Map<object, Doc[]>();
-        sectionFilter && this.filteredChildren.map(d => {
-            let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object;
-            if (!fields.has(sectionValue)) fields.set(sectionValue, [d]);
-            else fields.get(sectionValue)!.push(d);
-        });
         return (
             <div className="collectionStackingView"
-                ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
+                ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
                 {/* {sectionFilter as boolean ? [
                     ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())],
                     ["width = height", this.filteredChildren.filter(f => Math.abs(f[WidthSym]() - f[HeightSym]()) < 1)],
                     ["height > width", this.filteredChildren.filter(f => f[WidthSym]() + 1 <= f[HeightSym]())]]. */}
-                {sectionFilter ? Array.from(fields.entries()).
+                {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).
                     map(section => this.section(section[0].toString(), section[1] as Doc[])) :
                     this.section("", this.filteredChildren)}
             </div>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7e9d35d3d..045c8531e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,5 +1,5 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faProjectDiagram, faSignature, faSquare, faTh, faImage, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
+import { faProjectDiagram, faSignature, faColumns, faSquare, faTh, faImage, faThList, faTree, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
 import { observer } from "mobx-react";
 import * as React from 'react';
 import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc';
@@ -25,6 +25,8 @@ library.add(faSquare);
 library.add(faProjectDiagram);
 library.add(faSignature);
 library.add(faThList);
+library.add(faColumns);
+library.add(faEllipsisV);
 library.add(faImage);
 
 @observer
@@ -37,7 +39,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
             case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />);
             case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />);
             case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />);
-            case CollectionViewType.Stacking: return (<CollectionStackingView {...props} CollectionView={this} />);
+            case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView {...props} CollectionView={this} />); }
+            case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView {...props} CollectionView={this} />); }
             case CollectionViewType.Freeform:
             default:
                 return (<CollectionFreeFormView {...props} CollectionView={this} />);
@@ -57,7 +60,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
             }
             subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" });
             subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
-            subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "th-list" });
+            subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "ellipsis-v" });
+            subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" });
             ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems });
             ContextMenu.Instance.addItem({
                 description: "Apply Template", event: undoBatch(() => {
-- 
cgit v1.2.3-70-g09d2


From 3b740e1c1ac74e7aac45c136ec9df1b37e2dbc16 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 23 Jul 2019 16:32:43 -0400
Subject: pretty ui for changing section fitler and added base for colletion
 chromes

---
 src/client/views/EditableView.scss                 |  10 +-
 src/client/views/EditableView.tsx                  |  30 +++-
 .../views/collections/CollectionBaseView.tsx       |   2 +-
 src/client/views/collections/CollectionView.tsx    |  34 ++++-
 .../views/collections/CollectionViewChromes.scss   |  54 ++++++++
 .../views/collections/CollectionViewChromes.tsx    | 152 +++++++++++++++++++++
 6 files changed, 268 insertions(+), 14 deletions(-)
 create mode 100644 src/client/views/collections/CollectionViewChromes.scss
 create mode 100644 src/client/views/collections/CollectionViewChromes.tsx

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

diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
index a5150cd66..19512362e 100644
--- a/src/client/views/EditableView.scss
+++ b/src/client/views/EditableView.scss
@@ -1,20 +1,24 @@
-.editableView-container-editing, .editableView-container-editing-oneLine {
+.editableView-container-editing,
+.editableView-container-editing-oneLine {
     overflow-wrap: break-word;
     word-wrap: break-word;
     hyphens: auto;
     overflow: hidden;
 }
+
 .editableView-container-editing-oneLine {
     span {
         white-space: nowrap;
         overflow: hidden;
         text-overflow: ellipsis;
-        display:block;
+        display: block;
     }
+
     input {
-        display:block;
+        display: block;
     }
 }
+
 .editableView-input {
     width: 100%;
     background: inherit;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index f2cdffd38..a5bb40243 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -2,6 +2,7 @@ import React = require('react');
 import { observer } from 'mobx-react';
 import { observable, action, trace } from 'mobx';
 import "./EditableView.scss";
+import * as Autosuggest from 'react-autosuggest';
 
 export interface EditableProps {
     /**
@@ -28,6 +29,13 @@ export interface EditableProps {
     fontSize?: number;
     height?: number;
     display?: string;
+    autosuggestProps?: {
+        resetValue: () => void;
+        value: string,
+        onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void,
+        autosuggestProps: Autosuggest.AutosuggestProps<string>
+
+    };
     oneLine?: boolean;
     editing?: boolean;
     onClick?: (e: React.MouseEvent) => boolean;
@@ -85,10 +93,26 @@ export class EditableView extends React.Component<EditableProps> {
 
     render() {
         if (this._editing) {
-            return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
-                onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
-                style={{ display: this.props.display, fontSize: this.props.fontSize }} />;
+            return this.props.autosuggestProps
+                ? <Autosuggest
+                    {...this.props.autosuggestProps.autosuggestProps}
+                    inputProps={{
+                        className: "editableView-input",
+                        onKeyDown: this.onKeyDown,
+                        autoFocus: true,
+                        onBlur: action(() => this._editing = false),
+                        onPointerDown: this.stopPropagation,
+                        onClick: this.stopPropagation,
+                        onPointerUp: this.stopPropagation,
+                        value: this.props.autosuggestProps.value,
+                        onChange: this.props.autosuggestProps.onChange
+                    }}
+                />
+                : <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
+                    onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
+                    style={{ display: this.props.display, fontSize: this.props.fontSize }} />;
         } else {
+            if (this.props.autosuggestProps) this.props.autosuggestProps.resetValue();
             return (
                 <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
                     style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 72faf52c4..4cdbd8554 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -32,7 +32,7 @@ export interface CollectionRenderProps {
 
 export interface CollectionViewProps extends FieldViewProps {
     onContextMenu?: (e: React.MouseEvent) => void;
-    children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null;
+    children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null | (JSX.Element | null)[];
     className?: string;
     contentRef?: React.Ref<HTMLDivElement>;
 }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 045c8531e..ba9780c24 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -17,6 +17,7 @@ import { CollectionStackingView } from './CollectionStackingView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import { StrCast, PromiseValue } from '../../../new_fields/Types';
 import { DocumentType } from '../../documents/Documents';
+import { CollectionStackingViewChrome } from './CollectionViewChromes';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
@@ -33,21 +34,40 @@ library.add(faImage);
 export class CollectionView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
 
-    private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
+    private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
         let props = { ...this.props, ...renderProps };
         switch (this.isAnnotationOverlay ? CollectionViewType.Freeform : type) {
-            case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />);
-            case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />);
-            case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />);
-            case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView {...props} CollectionView={this} />); }
-            case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView {...props} CollectionView={this} />); }
+            case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} CollectionView={this} />);
+            case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} CollectionView={this} />);
+            case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} CollectionView={this} />);
+            case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} CollectionView={this} />); }
+            case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} CollectionView={this} />); }
             case CollectionViewType.Freeform:
             default:
-                return (<CollectionFreeFormView {...props} CollectionView={this} />);
+                return (<CollectionFreeFormView key="collview" {...props} CollectionView={this} />);
         }
         return (null);
     }
 
+    private Chrome = (type: CollectionViewType) => {
+        if (this.isAnnotationOverlay || this.props.Document === CurrentUserUtils.UserDocument.sidebar) {
+            return (null);
+        }
+
+        switch (type) {
+            case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this} />);
+            default:
+                return null;
+        }
+    }
+
+    private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
+        return [
+            this.Chrome(type),
+            this.SubViewHelper(type, renderProps)
+        ]
+    }
+
     get isAnnotationOverlay() { return this.props.fieldExt ? true : false; }
 
     static _applyCount: number = 0;
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
new file mode 100644
index 000000000..e5cb1b546
--- /dev/null
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -0,0 +1,54 @@
+@import "../globalCssVariables";
+
+.collectionStackingViewChrome {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    padding-bottom: 10px;
+    border-bottom: .5px solid lightgrey;
+    margin: 10px;
+
+    .collectionStackingViewChrome-sectionFilter-cont {
+        justify-self: right;
+        display: flex;
+        font-size: 75%;
+        text-transform: uppercase;
+        letter-spacing: 2px;
+
+        .collectionStackingViewChrome-sectionFilter-label {
+            vertical-align: center;
+            padding: 10px;
+        }
+
+        .collectionStackingViewChrome-sectionFilter {
+            color: white;
+            width: fit-content;
+            text-align: center;
+            background: rgb(238, 238, 238);
+            height: 37px;
+
+            .editable-view-input,
+            input,
+            .editableView-container-editing-oneLine,
+            .editableView-container-editing {
+                padding: 12px 10px 11px 10px;
+                border: 0px;
+                color: grey;
+                text-align: center;
+                text-transform: uppercase;
+                letter-spacing: 2px;
+                outline-color: black;
+                height: 100%;
+            }
+
+            .react-autosuggest__container {
+                margin: 0;
+                color: grey;
+                padding: 0px;
+            }
+        }
+    }
+
+    .collectionStackingViewChrome-sectionFilter:hover {
+        cursor: text;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
new file mode 100644
index 000000000..ca1480ff2
--- /dev/null
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -0,0 +1,152 @@
+import * as React from "react";
+import { CollectionView } from "./CollectionView";
+import "./CollectionViewChromes.scss";
+import { CollectionViewType } from "./CollectionBaseView";
+import { undoBatch } from "../../util/UndoManager";
+import { action, observable, runInAction, computed } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { DocLike } from "../MetadataEntryMenu";
+const higflyout = require("@hig/flyout");
+export const Flyout = higflyout.default;
+import * as Autosuggest from 'react-autosuggest';
+import { EditableView } from "../EditableView";
+import { StrCast } from "../../../new_fields/Types";
+
+interface CollectionViewChromeProps {
+    CollectionView: CollectionView;
+}
+
+class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> {
+    @undoBatch
+    viewChanged = (e: React.ChangeEvent) => {
+        //@ts-ignore
+        switch (e.target.selectedOptions[0].value) {
+            case "freeform":
+                this.props.CollectionView.props.Document.viewType = CollectionViewType.Freeform;
+                break;
+            case "schema":
+                this.props.CollectionView.props.Document.viewType = CollectionViewType.Schema;
+                break;
+            case "treeview":
+                this.props.CollectionView.props.Document.viewType = CollectionViewType.Tree;
+                break;
+            case "stacking":
+                this.props.CollectionView.props.Document.viewType = CollectionViewType.Stacking;
+                break;
+            case "masonry":
+                this.props.CollectionView.props.Document.viewType = CollectionViewType.Masonry;
+                break;
+            default:
+                break;
+        }
+    }
+
+    render() {
+        return (
+            <div className="collectionViewBaseChrome">
+                <select onChange={this.viewChanged}>
+                    <option value="freeform" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Freeform}>Freeform View</option>
+                    <option value="schema" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Schema}>Schema View</option>
+                    <option value="treeview" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Tree}>Tree View</option>
+                    <option value="stacking" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Stacking}>Stacking View</option>
+                    <option value="masonry" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Masonry}>Masonry View</option>
+                </select>
+            </div>
+        )
+    }
+}
+
+@observer
+export class CollectionStackingViewChrome extends React.Component<CollectionViewChromeProps> {
+    @observable private _currentKey: string = "";
+    @observable private suggestions: string[] = [];
+
+    @computed get sectionFilter() { return StrCast(this.props.CollectionView.props.Document.sectionFilter); }
+
+    getKeySuggestions = async (value: string): Promise<string[]> => {
+        value = value.toLowerCase();
+        let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
+            = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
+        if (typeof docs === "function") {
+            docs = docs();
+        }
+        docs = await docs;
+        if (docs instanceof Doc) {
+            return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
+        } else {
+            const keys = new Set<string>();
+            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+            return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
+        }
+    }
+
+    @action
+    onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
+        this._currentKey = newValue;
+    }
+
+    getSuggestionValue = (suggestion: string) => suggestion;
+
+    renderSuggestion = (suggestion: string) => {
+        return <p>{suggestion}</p>;
+    }
+
+    onSuggestionFetch = async ({ value }: { value: string }) => {
+        const sugg = await this.getKeySuggestions(value);
+        runInAction(() => {
+            this.suggestions = sugg;
+        });
+    }
+
+    @action
+    onSuggestionClear = () => {
+        this.suggestions = [];
+    }
+
+    setValue = (value: string) => {
+        this.props.CollectionView.props.Document.sectionFilter = value;
+        return true;
+    }
+
+    @action resetValue = () => { this._currentKey = this.sectionFilter; }
+
+    render() {
+        return (
+            <div className="collectionStackingViewChrome">
+                <CollectionViewBaseChrome CollectionView={this.props.CollectionView} />
+                <div className="collectionStackingViewChrome-sectionFilter-cont">
+                    <div className="collectionStackingViewChrome-sectionFilter-label">
+                        Group items by:
+                    </div>
+                    <div className="collectionStackingViewChrome-sectionFilter">
+                        <EditableView
+                            GetValue={() => this.sectionFilter}
+                            autosuggestProps={
+                                {
+                                    resetValue: this.resetValue,
+                                    value: this._currentKey,
+                                    onChange: this.onKeyChange,
+                                    autosuggestProps: {
+                                        inputProps:
+                                        {
+                                            value: this._currentKey,
+                                            onChange: this.onKeyChange
+                                        },
+                                        getSuggestionValue: this.getSuggestionValue,
+                                        suggestions: this.suggestions,
+                                        alwaysRenderSuggestions: true,
+                                        renderSuggestion: this.renderSuggestion,
+                                        onSuggestionsFetchRequested: this.onSuggestionFetch,
+                                        onSuggestionsClearRequested: this.onSuggestionClear
+                                    }
+                                }}
+                            SetValue={this.setValue}
+                            contents={this.sectionFilter ? this.sectionFilter : "N/A"}
+                        />
+                    </div>
+                </div>
+            </div>
+        )
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

