From 123027bf525bfb6d635cf2304d01490bccb4abff Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 18 Jul 2019 02:22:27 -0400
Subject: fixes for single column layouts not contained in a free form view.

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

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

diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index eba69b448..2603dc60b 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -78,7 +78,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
-        let self = this;
         var curPage = NumCast(this.props.Document.curPage, -1);
         Doc.GetProto(doc).page = curPage;
         if (curPage >= 0) {
@@ -146,7 +145,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const viewtype = this.collectionViewType;
         return (
             <div id="collectionBaseView"
-                style={{ boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
+                style={{ overflow: "auto", boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
                 className={this.props.className || "collectionView-cont"}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
-- 
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/CollectionBaseView.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 3442776999d1d7b7bced9bbed601ead637a43cc7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 22 Jul 2019 10:24:37 -0400
Subject: merged stacking sections branch.

---
 src/client/views/DocumentDecorations.tsx           |  10 +-
 .../views/collections/CollectionBaseView.tsx       |   6 +-
 .../views/collections/CollectionStackingView.scss  |  13 +-
 .../views/collections/CollectionStackingView.tsx   | 247 ---------------------
 src/client/views/collections/CollectionView.tsx    |  28 ++-
 src/client/views/nodes/WebBox.tsx                  |  16 --
 src/new_fields/Doc.ts                              |  33 ++-
 7 files changed, 64 insertions(+), 289 deletions(-)
 delete mode 100644 src/client/views/collections/CollectionStackingView.tsx

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fb5104915..2f7bea365 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -28,6 +28,7 @@ import { RichTextField } from '../../new_fields/RichTextField';
 import { LinkManager } from '../util/LinkManager';
 import { ObjectField } from '../../new_fields/ObjectField';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
+import { ImageBox } from './nodes/ImageBox';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -85,8 +86,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 SelectionManager.DeselectAll();
                 let fieldTemplate = fieldTemplateView.props.Document;
                 let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document;
-                let metaKey = text.slice(1, text.length);
-                Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(docTemplate));
+                let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
+                let proto = Doc.GetProto(docTemplate);
+                Doc.MakeTemplate(fieldTemplate, metaKey, proto);
+                if (text.startsWith(">>")) {
+                    proto.detailedLayout = proto.layout;
+                    proto.miniLayout = ImageBox.LayoutString(metaKey);
+                }
             }
             else {
                 if (SelectionManager.SelectedDocuments().length > 0) {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index eba69b448..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 {
@@ -78,7 +79,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
-        let self = this;
         var curPage = NumCast(this.props.Document.curPage, -1);
         Doc.GetProto(doc).page = curPage;
         if (curPage >= 0) {
@@ -146,7 +146,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const viewtype = this.collectionViewType;
         return (
             <div id="collectionBaseView"
-                style={{ boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
+                style={{ overflow: "auto", boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
                 className={this.props.className || "collectionView-cont"}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 7e886304d..7ebf5f77c 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,7 +1,9 @@
 @import "../globalCssVariables";
 .collectionStackingView {
+    height: 100%;
+    width: 100%;
+    position: absolute;
     overflow-y: auto;
-
     .collectionStackingView-docView-container {
         width: 45%;
         margin: 5% 2.5%;
@@ -71,4 +73,13 @@
         grid-column-end: span 1;
         height: 100%;
     }
+    .collectionStackingView-sectionHeader {    
+        width: 90%;
+        background: gray;
+        text-align: center;
+        margin-left: 5%;
+        margin-right: 5%;
+        color: white;
+        margin-top: 10px;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
deleted file mode 100644
index 6d9e942c9..000000000
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ /dev/null
@@ -1,247 +0,0 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, reaction, untracked, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { BoolCast, NumCast, Cast } from "../../../new_fields/Types";
-import { emptyFunction, Utils } from "../../../Utils";
-import { ContextMenu } from "../ContextMenu";
-import { CollectionSchemaPreview } from "./CollectionSchemaView";
-import "./CollectionStackingView.scss";
-import { CollectionSubView } from "./CollectionSubView";
-import { undoBatch } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
-import { DocumentType } from "../../documents/Documents";
-import { Transform } from "../../util/Transform";
-import { CursorProperty } from "csstype";
-
-@observer
-export class CollectionStackingView extends CollectionSubView(doc => doc) {
-    _masonryGridRef: HTMLDivElement | null = null;
-    _draggerRef = React.createRef<HTMLDivElement>();
-    _heightDisposer?: IReactionDisposer;
-    _gridSize = 1;
-    _docXfs: any[] = [];
-    @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); }
-    @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
-    @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); }
-    @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); }
-
-    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])],
-            () => this.singleColumn &&
-                (this.props.Document.height = 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();
-    }
-
-    @action
-    moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
-        return this.props.removeDocument(doc) && addDocument(doc);
-    }
-    createRef = (ele: HTMLDivElement | null) => {
-        this._masonryGridRef = ele;
-        this.createDropTarget(ele!);
-    }
-
-    overlays = (doc: Doc) => {
-        return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {};
-    }
-
-    getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) {
-        let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
-        let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
-        let height = () => this.getDocHeight(layoutDoc);
-        let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
-        return <CollectionSchemaPreview
-            Document={layoutDoc}
-            DataDocument={resolvedDataDoc}
-            showOverlays={this.overlays}
-            renderDepth={this.props.renderDepth}
-            width={width}
-            height={height}
-            getTransform={finalDxf}
-            CollectionView={this.props.CollectionView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            active={this.props.active}
-            whenActiveChanged={this.props.whenActiveChanged}
-            addDocTab={this.props.addDocTab}
-            setPreviewScript={emptyFunction}
-            previewScript={undefined}>
-        </CollectionSchemaPreview>;
-    }
-    getDocHeight(d: Doc) {
-        let nw = NumCast(d.nativeWidth);
-        let nh = NumCast(d.nativeHeight);
-        let aspect = nw && nh ? nh / nw : 1;
-        let wid = Math.min(d[WidthSym](), this.columnWidth);
-        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);
-        return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth);
-    }
-    getDocTransform(doc: Doc, dref: HTMLDivElement) {
-        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);
-        let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY);
-        return this.offsetTransform(doc, translate[0], translate[1]);
-    }
-
-    @computed
-    get children() {
-        this._docXfs.length = 0;
-        return this.filteredChildren.map((d, i) => {
-            let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
-            let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
-            let height = () => this.getDocHeight(layoutDoc);
-            if (this.singleColumn) {
-                let dxf = () => this.getSingleDocTransform(layoutDoc, i, width());
-                let rowHgtPcnt = height() / (this.props.Document[HeightSym]() - 2 * this.yMargin) * 100;
-                this._docXfs.push({ dxf: dxf, width: width, height: height });
-                return <div className="collectionStackingView-columnDoc" key={d[Id]} style={{ width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: `${rowHgtPcnt}%` }} >
-                    {this.getDisplayDoc(layoutDoc, d, dxf)}
-                </div>;
-            } else {
-                let dref = React.createRef<HTMLDivElement>();
-                let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
-                let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + 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)}
-                </div>;
-            }
-        });
-    }
-
-    _columnStart: number = 0;
-    columnDividerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
-        runInAction(() => this.cursor = "grabbing");
-        document.addEventListener("pointermove", this.onDividerMove);
-        document.addEventListener('pointerup', this.onDividerUp);
-        this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
-    }
-    @action
-    onDividerMove = (e: PointerEvent): void => {
-        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;
-    }
-
-    @action
-    onDividerUp = (e: PointerEvent): void => {
-        runInAction(() => this.cursor = "grab");
-        document.removeEventListener("pointermove", this.onDividerMove);
-        document.removeEventListener('pointerup', this.onDividerUp);
-    }
-
-    @computed get columnDragger() {
-        return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this.cursor, left: `${this.columnWidth + this.xMargin}px` }} >
-            <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
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        let targInd = -1;
-        let where = [de.x, de.y];
-        if (de.data instanceof DragManager.DocumentDragData) {
-            this._docXfs.map((cd, i) => {
-                let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
-                let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
-                if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
-                    targInd = i;
-                }
-            });
-        }
-        if (super.drop(e, de)) {
-            let newDoc = de.data.droppedDocuments[0];
-            let docs = this.childDocList;
-            if (docs) {
-                if (targInd === -1) targInd = docs.length;
-                else targInd = docs.indexOf(this.filteredChildren[targInd]);
-                let srcInd = docs.indexOf(newDoc);
-                docs.splice(srcInd, 1);
-                docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc);
-            }
-        }
-        return false;
-    }
-    @undoBatch
-    @action
-    onDrop = (e: React.DragEvent): void => {
-        let where = [e.clientX, e.clientY];
-        let targInd = -1;
-        this._docXfs.map((cd, i) => {
-            let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
-            let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
-            if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
-                targInd = i;
-            }
-        });
-        super.onDrop(e, {}, () => {
-            if (targInd !== -1) {
-                let newDoc = this.childDocs[this.childDocs.length - 1];
-                let docs = this.childDocList;
-                if (docs) {
-                    docs.splice(docs.length - 1, 1);
-                    docs.splice(targInd, 0, newDoc);
-                }
-            }
-        });
-    }
-    render() {
-        let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
-            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 (
-            <div className="collectionStackingView" ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
-                <div className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
-                    style={{
-                        padding: this.singleColumn ? `${this.yMargin}px ${this.xMargin}px ${this.yMargin}px ${this.xMargin}px` : `${this.yMargin}px ${this.xMargin}px`,
-                        margin: "auto",
-                        width: this.singleColumn ? undefined : `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`,
-                        height: "100%",
-                        position: "relative",
-                        gridGap: this.gridGap,
-                        gridTemplateColumns: this.singleColumn ? undefined : templatecols,
-                        gridAutoRows: this.singleColumn ? undefined : `${this._gridSize}px`
-                    }}
-                >
-                    {this.children}
-                    {this.singleColumn ? (null) : this.columnDragger}
-                </div>
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 56750668d..045c8531e 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, 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 } 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 { Docs } from '../../documents/Documents';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -16,6 +15,8 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 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);
@@ -24,6 +25,9 @@ library.add(faSquare);
 library.add(faProjectDiagram);
 library.add(faSignature);
 library.add(faThList);
+library.add(faColumns);
+library.add(faEllipsisV);
+library.add(faImage);
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
@@ -35,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} />);
@@ -45,6 +50,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[] = [];
@@ -54,15 +60,19 @@ 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(() => {
                     let otherdoc = new Doc();
-                    otherdoc.width = 100;
-                    otherdoc.height = 50;
-                    Doc.GetProto(otherdoc).title = "applied(" + this.props.Document.title + ")";
-                    Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
+                    otherdoc.width = this.props.Document[WidthSym]();
+                    otherdoc.height = this.props.Document[HeightSym]();
+                    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/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 59e61023f..2ad6ae5f0 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 { DocumentType } from "../client/documents/Documents";
 import { ComputedField } from "./ScriptField";
 
 export namespace Field {
@@ -317,7 +318,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)) {
@@ -345,20 +346,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 + " applied to " + dataDoc.title;
-                (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 => {
@@ -386,12 +390,12 @@ export namespace Doc {
     export function MakeDelegate(doc: Doc, id?: string): Doc;
     export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc>;
     export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc> {
-        if (!doc) {
-            return undefined;
+        if (doc) {
+            const delegate = new Doc(id, true);
+            delegate.proto = doc;
+            return delegate;
         }
-        const delegate = new Doc(id, true);
-        delegate.proto = doc;
-        return delegate;
+        return undefined;
     }
 
     export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) {
@@ -421,4 +425,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 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/CollectionBaseView.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


From 3ee8a9ebc760e3049b692efad0d6edfb877342cf Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 25 Jul 2019 22:27:26 -0400
Subject: fixed dragging annotaitons on images.  fixed border rounding of
 buttons.  fixed dropping to ignore document titles.

---
 src/client/util/DragManager.ts                      | 3 ++-
 src/client/views/collections/CollectionBaseView.tsx | 2 +-
 src/client/views/nodes/ButtonBox.scss               | 2 ++
 src/client/views/nodes/DocumentView.tsx             | 3 ++-
 src/client/views/nodes/ImageBox.tsx                 | 2 --
 5 files changed, 7 insertions(+), 5 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 323908302..5271f2f5d 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -412,7 +412,6 @@ export namespace DragManager {
         };
 
         let hideDragElements = () => {
-            SelectionManager.SetIsDragging(false);
             dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             eles.map(ele => (ele.hidden = false));
         };
@@ -426,11 +425,13 @@ export namespace DragManager {
 
         AbortDrag = () => {
             hideDragElements();
+            SelectionManager.SetIsDragging(false);
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
             hideDragElements();
             dispatchDrag(eles, e, dragData, options, finishDrag);
+            SelectionManager.SetIsDragging(false);
             endDrag();
         };
         document.addEventListener("pointermove", moveHandler, true);
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 72faf52c4..c39caec24 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -124,7 +124,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
     @action.bound
     moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
         let self = this;
-        let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
+        let targetDataDoc = this.props.Document;
         if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) {
             //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) {
             return true;
diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss
index 97cc91128..92beafa15 100644
--- a/src/client/views/nodes/ButtonBox.scss
+++ b/src/client/views/nodes/ButtonBox.scss
@@ -2,9 +2,11 @@
     width: 100%;
     height: 100%;
     pointer-events: all;
+    border-radius: inherit;
 }
 
 .buttonBox-mainButton {
     width: 100%;
     height: 100%;
+    border-radius: inherit;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b5e64ed19..4a5e20533 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -298,6 +298,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let fullScreenAlias = Doc.MakeAlias(this.props.Document);
             fullScreenAlias.templates = new List<string>();
             Doc.UseDetailLayout(fullScreenAlias);
+            fullScreenAlias.showCaption = true;
             this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
             SelectionManager.DeselectAll();
             this.props.Document.libraryBrush = false;
@@ -673,7 +674,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         {!showTitle ? (null) :
                             <div style={{
                                 position: showTextTitle ? "relative" : "absolute", top: 0, padding: "4px", textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre",
-                                pointerEvents: "all",
+                                pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
                                 overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white",
                                 transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
                             }}>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 6541007d0..ea167286b 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -104,8 +104,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                             e.stopPropagation();
                         }
                     }
-                } else if (!this.props.isSelected()) {
-                    e.stopPropagation();
                 }
             }));
             // de.data.removeDocument()  bcz: need to implement
-- 
cgit v1.2.3-70-g09d2


From 7d468c19c28a48314ea993d84e5995364e88fa86 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 26 Jul 2019 02:21:09 -0400
Subject: added a MakeBackground and FitToBox menu items.   fixed problems with
 fitToBox and frozen dimensions.  fixed issue with drop() on free forms.

---
 .../views/collections/CollectionBaseView.scss      |  2 +-
 .../views/collections/CollectionBaseView.tsx       |  6 ++--
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 32 +++++++++++++++-------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 src/client/views/nodes/DocumentView.scss           |  1 -
 src/client/views/nodes/DocumentView.tsx            |  7 +++++
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 8 files changed, 37 insertions(+), 17 deletions(-)

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

diff --git a/src/client/views/collections/CollectionBaseView.scss b/src/client/views/collections/CollectionBaseView.scss
index 34bcb705e..583e6f6ca 100644
--- a/src/client/views/collections/CollectionBaseView.scss
+++ b/src/client/views/collections/CollectionBaseView.scss
@@ -6,7 +6,7 @@
     border-radius: 0 0 $border-radius $border-radius;
     box-sizing: border-box;
     border-radius: inherit;
-    pointer-events: all;
     width:100%;
     height:100%;
+    overflow: auto;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index c39caec24..67112ae7c 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -126,7 +126,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         let self = this;
         let targetDataDoc = this.props.Document;
         if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) {
-            //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) {
             return true;
         }
         if (this.removeDocument(doc)) {
@@ -146,7 +145,10 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const viewtype = this.collectionViewType;
         return (
             <div id="collectionBaseView"
-                style={{ overflow: "auto", boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
+                style={{
+                    pointerEvents: this.props.Document.isBackground ? "none" : "all",
+                    boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
+                }}
                 className={this.props.className || "collectionView-cont"}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 2ddefb3c0..cbc9e61eb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -104,7 +104,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 } else if (de.data.moveDocument) {
                     let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
                     added = movedDocs.reduce((added: boolean, d) =>
-                        de.data.moveDocument(d, /*this.props.DataDoc ? this.props.DataDoc :*/ this.props.Document, this.props.addDocument) || added, false);
+                        de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
                 } else {
                     added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6500b3273..9c4275388 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -34,9 +34,11 @@ import { CompileScript } from "../../../util/Scripting";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye } from "@fortawesome/free-regular-svg-icons";
-import { faTable, faPaintBrush, faAsterisk } from "@fortawesome/free-solid-svg-icons";
+import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsAlt } from "@fortawesome/free-solid-svg-icons";
+import { undo } from "prosemirror-history";
+import { number } from "prop-types";
 
-library.add(faEye, faTable, faPaintBrush);
+library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt);
 
 export const panZoomSchema = createSchema({
     panX: "number",
@@ -58,24 +60,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private get _pheight() { return this.props.PanelHeight(); }
     private inkKey = "ink";
 
+    get prevScaling() {
+        return (this.props as any).ContentScaling && this.Document.nativeWidth && this.fitToBox ? (this.props as any).ContentScaling() : 1;
+    }
+
     @computed get contentBounds() {
-        let bounds = this.props.fitToBox && !NumCast(this.nativeWidth) ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined;
+        let bounds = (this.fitToBox) && !this.nativeWidth ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined;
         return {
             panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0,
             panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0,
-            scale: bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1
+            scale: (bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1) / this.prevScaling
         };
     }
 
-    @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
-    @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
+    @computed get fitToBox() { return this.props.fitToBox || this.props.Document.fitToBox; }
+    @computed get nativeWidth() { return this.fitToBox ? 0 : this.Document.nativeWidth || 0; }
+    @computed get nativeHeight() { return this.fitToBox ? 0 : this.Document.nativeHeight || 0; }
     public get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt')
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
     private panX = () => this.contentBounds.panX;
     private panY = () => this.contentBounds.panY;
     private zoomScaling = () => this.contentBounds.scale;
-    private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0;  // shift so pan position is at center of window for non-overlay collections
-    private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
+    private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 / this.prevScaling : 0;  // shift so pan position is at center of window for non-overlay collections
+    private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 / this.prevScaling : 0;// shift so pan position is at center of window for non-overlay collections
     private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
     private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
@@ -109,11 +116,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
+        let xf = this.getTransform();
         if (super.drop(e, de)) {
             if (de.data instanceof DragManager.DocumentDragData) {
                 if (de.data.droppedDocuments.length) {
-                    let dragDoc = de.data.droppedDocuments[0];
-                    let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+                    let [xp, yp] = xf.transformPoint(de.x, de.y);
                     let x = xp - de.data.xOffset;
                     let y = yp - de.data.yOffset;
                     let dropX = NumCast(de.data.droppedDocuments[0].x);
@@ -491,6 +498,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     onContextMenu = () => {
+        ContextMenu.Instance.addItem({
+            description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`,
+            event: undoBatch(async () => this.props.Document.fitToBox = !this.fitToBox),
+            icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt"
+        });
         ContextMenu.Instance.addItem({
             description: "Arrange contents in grid",
             icon: "table",
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b765517a2..d96e93aeb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -365,7 +365,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     marqueeSelect() {
         let selRect = this.Bounds;
         let selection: Doc[] = [];
-        this.props.activeDocuments().map(doc => {
+        this.props.activeDocuments().filter(doc => !doc.isBackground).map(doc => {
             var z = NumCast(doc.zoomBasis, 1);
             var x = NumCast(doc.x);
             var y = NumCast(doc.y);
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 3a4b46b7e..7c72fb6e6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,7 +4,6 @@
   position: inherit;
   top: 0;
   left:0;
-  pointer-events: all;
  // background: $light-color; //overflow: hidden;
   transform-origin: left top;
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4a5e20533..9d2dd3121 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -514,6 +514,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         proto.ignoreAspect = !BoolCast(proto.ignoreAspect, false);
     }
+    @undoBatch
+    @action
+    makeBackground = (): void => {
+        this.props.Document.isBackground = true;
+    }
 
     @undoBatch
     @action
@@ -544,6 +549,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" });
         cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" });
         cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
+        cm.addItem({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
         cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" });
         cm.addItem({
             description: "Make Portal", event: () => {
@@ -648,6 +654,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
+                    pointerEvents: this.props.Document.isBackground ? "none" : "all",
                     color: foregroundColor,
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ea167286b..a36885616 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -350,7 +350,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         if (field instanceof ImageField) paths = [this.choosePath(field.url)];
         paths.push(...altpaths);
         // }
-        let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
+        let interactive = InkingControl.Instance.selectedTool || this.props.Document.isBackground ? "" : "-interactive";
         let rotation = NumCast(this.dataDoc.rotation, 0);
         let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
         let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
-- 
cgit v1.2.3-70-g09d2


From fe9017c3cdc2481f4bcce40dcc1514a6f7af37dc Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 2 Aug 2019 18:21:23 -0400
Subject: improvements to grouping with backgrounds

---
 .../views/collections/CollectionBaseView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 68 ++++++++++++----------
 src/client/views/nodes/DocumentView.tsx            |  9 ++-
 3 files changed, 44 insertions(+), 35 deletions(-)

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

diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index c595a4c56..6801b94fd 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -147,7 +147,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             <div id="collectionBaseView"
                 style={{
                     pointerEvents: this.props.Document.isBackground ? "none" : "all",
-                    boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
+                    boxShadow: this.props.Document.isBackground ? undefined : `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
                 }}
                 className={this.props.className || "collectionView-cont"}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 232ac22c9..bc1aee159 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -108,6 +108,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
         this.props.addDocument(newBox, false);
         this.bringToFront(newBox);
+        this.updateClusters();
         return true;
     }
     private selectDocuments = (docs: Doc[]) => {
@@ -175,34 +176,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                         this.bringToFront(d);
                     });
 
-                    let sets: (Doc[])[] = []
-                    this.childDocs.map(c => {
-                        let included = []
-                        for (let i = 0; i < sets.length; i++) {
-                            for (let j = 0; j < sets[i].length; j++) {
-                                if (this.bounsdSelect(c, sets[i][j])) {
-                                    included.push(i);
-                                    break;
-                                }
-                            }
-                        }
-                        if (included.length === 0)
-                            sets.push([c]);
-                        else if (included.length === 1)
-                            sets[included[0]].push(c);
-                        else {
-                            sets[included[0]].push(c);
-                            for (let s = 1; s < included.length; s++) {
-                                sets[included[0]].push(...sets[included[s]]);
-                                sets[included[s]].length = 0;
-                            }
-                        }
-                    });
-                    for (let s = 0; s < sets.length; s++) {
-                        for (let i = 0; i < sets[s].length; i++) {
-                            Doc.GetProto(sets[s][i]).cluster = s;
-                        }
-                    }
+                    this.updateClusters();
                 }
             }
             else if (de.data instanceof DragManager.AnnotationDragData) {
@@ -223,6 +197,38 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return false;
     }
 
+    @action
+    updateClusters() {
+        let sets: (Doc[])[] = []
+        this.childDocs.map(c => {
+            let included = []
+            for (let i = 0; i < sets.length; i++) {
+                for (let j = 0; j < sets[i].length; j++) {
+                    if (this.bounsdSelect(c, sets[i][j])) {
+                        included.push(i);
+                        break;
+                    }
+                }
+            }
+            if (included.length === 0)
+                sets.push([c]);
+            else if (included.length === 1)
+                sets[included[0]].push(c);
+            else {
+                sets[included[0]].push(c);
+                for (let s = 1; s < included.length; s++) {
+                    sets[included[0]].push(...sets[included[s]]);
+                    sets[included[s]].length = 0;
+                }
+            }
+        });
+        for (let s = 0; s < sets.length; s++) {
+            for (let i = 0; i < sets[s].length; i++) {
+                Doc.GetProto(sets[s][i]).cluster = s;
+            }
+        }
+    }
+
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.button === 0 && !e.shiftKey && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) {
@@ -247,8 +253,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             let cluster = this.childDocs.reduce((cluster, cd) => {
                 let cx = NumCast(cd.x) - this._groupingBorder;
                 let cy = NumCast(cd.y) - this._groupingBorder;
-                let cw = NumCast(cd.width) + this._groupingBorder;
-                let ch = NumCast(cd.height) + this._groupingBorder;
+                let cw = NumCast(cd.width) + 2 * this._groupingBorder;
+                let ch = NumCast(cd.height) + 2 * this._groupingBorder;
                 if (this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }))
                     return NumCast(cd.cluster);
                 return cluster;
@@ -392,7 +398,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     bringToFront = (doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack) {
+        if (sendToBack || doc.isBackground) {
             doc.zIndex = 0;
             return;
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d79b58a7b..16ae5471d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -695,7 +695,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             });
         }
         let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
-        let colors = ["#da42429e", "#31ea318c", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "#000000ad"];
+        let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+        let groupCol = colors[NumCast(this.props.Document.cluster) % colors.length];
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
@@ -704,7 +705,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     color: foregroundColor,
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
-                    boxShadow: `${colors[NumCast(this.props.Document.cluster) % colors.length]} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`,
+                    boxShadow: this.layoutDoc.isBackground ?
+                        `0px 0px 50px 50px ${groupCol}` :
+                        `${groupCol} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`,
                     outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
                         `${this.props.ScreenToLocalTransform().Scale}px` : "0px",
                     marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
@@ -714,7 +717,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     border: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ?
                         `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined,
                     borderRadius: "inherit",
-                    background: backgroundColor,
+                    background: this.layoutDoc.isBackground ? groupCol : backgroundColor,
                     width: nativeWidth,
                     height: nativeHeight,
                     transform: `scale(${this.props.ContentScaling()})`,
-- 
cgit v1.2.3-70-g09d2

