From 513e9042ea815e964462e824d85fbd229381250f Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 27 Apr 2019 01:19:52 -0400
Subject: A bunch more stuff

---
 src/client/views/nodes/IconBox.tsx | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'src/client/views/nodes/IconBox.tsx')

diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 9c90c0a0e..f7cceb3d4 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -4,12 +4,12 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } fr
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from '../../../fields/Document';
-import { IconField } from "../../../fields/IconFIeld";
-import { KeyStore } from "../../../fields/KeyStore";
 import { SelectionManager } from "../../util/SelectionManager";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./IconBox.scss";
+import { Cast } from "../../../new_fields/Types";
+import { Doc } from "../../../new_fields/Doc";
+import { IconField } from "../../../new_fields/IconField";
 
 
 library.add(faCaretUp);
@@ -22,8 +22,8 @@ library.add(faFilm);
 export class IconBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(IconBox); }
 
-    @computed get maximized() { return this.props.Document.GetT(KeyStore.MaximizedDoc, Document); }
-    @computed get layout(): string { return this.props.Document.GetData(this.props.fieldKey, IconField, "<p>Error loading layout data</p>" as string); }
+    @computed get maximized() { return Cast(this.props.Document.maximizedDoc, Doc); }
+    @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.layout : "<p>Error loading layout data</p>"; }
     @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
 
     public static DocumentIcon(layout: string) {
@@ -33,7 +33,7 @@ export class IconBox extends React.Component<FieldViewProps> {
                     layout.indexOf("Video") !== -1 ? faFilm :
                         layout.indexOf("Collection") !== -1 ? faObjectGroup :
                             faCaretUp;
-        return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />
+        return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From c95e1789da41fb63e27f1086e30c0ebd151009df Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 30 Apr 2019 15:58:57 -0400
Subject: fixed icons

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 41 ++++++++++------------
 src/client/views/nodes/IconBox.tsx                 |  2 +-
 src/new_fields/IconField.ts                        |  6 ++--
 5 files changed, 25 insertions(+), 27 deletions(-)

(limited to 'src/client/views/nodes/IconBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ff21dd5b0..964faa8db 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -125,6 +125,7 @@ export namespace Docs {
     function CreateIconPrototype(): Doc {
         let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(),
             { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) });
+        console.log("iconpr" + iconProto.layout)
         return iconProto;
     }
     function CreateTextPrototype(): Doc {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1dc7c8394..084220f76 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -231,9 +231,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         iconDoc.nativeHeight = 0;
         iconDoc.x = NumCast(doc.x);
         iconDoc.y = NumCast(doc.y) - 24;
-        iconDoc.proto = doc;
         iconDoc.maximizedDoc = doc;
         doc.minimizedDoc = iconDoc;
+        console.log("Layout " + iconDoc.layout)
         docView.props.addDocument && docView.props.addDocument(iconDoc, false);
         return iconDoc;
     }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c8f0bca91..56c2a80fa 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -103,7 +103,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     target.width = width;
                     target.height = height;
                 }
-                (target as any).isIconAnimating = false;
+                target.isIconAnimating = false;
             }
         },
             2);
@@ -116,28 +116,25 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         if (!minimizedDocSet) return;
         let docs = minimizedDocSet.map(d => d);
         docs.push(this.props.Document);
-        minimizedDocSet.map(async minimizedDoc => {
-            if (minimizedDoc instanceof Document) {
-                this.props.addDocument && this.props.addDocument(minimizedDoc, false);
-                let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
-                if (maximizedDoc && !(maximizedDoc as any).isIconAnimating) {
-                    (maximizedDoc as any).isIconAnimating = true;
-                    if (isMinimized === undefined) {
-                        let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
-                        isMinimized = (maximizedDocMinimizedState) ? true : false;
-                    }
-                    let minx = NumCast(minimizedDoc.x, undefined);
-                    let miny = NumCast(minimizedDoc.y, undefined);
-                    let maxx = NumCast(maximizedDoc.x, undefined);
-                    let maxy = NumCast(maximizedDoc.y, undefined);
-                    let maxw = NumCast(maximizedDoc.width, undefined);
-                    let maxh = NumCast(maximizedDoc.height, undefined);
-                    if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
-                        maxw !== undefined && maxh !== undefined) {
-                        this.animateBetweenIcon(true, [minx, miny], [maxx, maxy], maxw, maxh, Date.now(), maximizedDoc, isMinimized);
-                    }
+        docs.map(async minimizedDoc => {
+            this.props.addDocument && this.props.addDocument(minimizedDoc, false);
+            let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
+            if (maximizedDoc && !maximizedDoc.isIconAnimating) {
+                maximizedDoc.isIconAnimating = true;
+                if (isMinimized === undefined) {
+                    let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
+                    isMinimized = (maximizedDocMinimizedState) ? true : false;
+                }
+                let minx = NumCast(minimizedDoc.x, undefined);
+                let miny = NumCast(minimizedDoc.y, undefined);
+                let maxx = NumCast(maximizedDoc.x, undefined);
+                let maxy = NumCast(maximizedDoc.y, undefined);
+                let maxw = NumCast(maximizedDoc.width, undefined);
+                let maxh = NumCast(maximizedDoc.height, undefined);
+                if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
+                    maxw !== undefined && maxh !== undefined) {
+                    this.animateBetweenIcon(true, [minx, miny], [maxx, maxy], maxw, maxh, Date.now(), maximizedDoc, isMinimized);
                 }
-
             }
         })
     }
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index f7cceb3d4..7a0c49735 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -23,7 +23,7 @@ export class IconBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(IconBox); }
 
     @computed get maximized() { return Cast(this.props.Document.maximizedDoc, Doc); }
-    @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.layout : "<p>Error loading layout data</p>"; }
+    @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
     @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
 
     public static DocumentIcon(layout: string) {
diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts
index 46f111f8e..c79a2f79a 100644
--- a/src/new_fields/IconField.ts
+++ b/src/new_fields/IconField.ts
@@ -5,10 +5,10 @@ import { ObjectField } from "./ObjectField";
 @Deserializable("icon")
 export class IconField extends ObjectField {
     @serializable(primitive())
-    readonly layout: string;
+    readonly icon: string;
 
-    constructor(layout: string) {
+    constructor(icon: string) {
         super();
-        this.layout = layout;
+        this.icon = icon;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 26b0374d87ef1b14d947d5882f73e67b8607436e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 3 May 2019 10:58:12 -0400
Subject: changes to icon box and decorations.

---
 src/client/views/DocumentDecorations.tsx           | 19 ++++++-------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  8 ++++--
 src/client/views/nodes/DocumentView.tsx            |  1 +
 src/client/views/nodes/IconBox.scss                | 12 +++++++-
 src/client/views/nodes/IconBox.tsx                 | 32 +++++++++++++++++++---
 6 files changed, 56 insertions(+), 19 deletions(-)

(limited to 'src/client/views/nodes/IconBox.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d07adcb71..9771c6580 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -48,14 +48,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @observable private _fieldKey = "title";
     @observable private _hidden = false;
     @observable private _opacity = 1;
-    @observable private _iconifying = false;
+    @observable private _removeIcon = false;
     @observable public Interacting = false;
 
     constructor(props: Readonly<{}>) {
         super(props);
         DocumentDecorations.Instance = this;
         this.keyinput = React.createRef();
-        reaction(() => SelectionManager.SelectedDocuments().slice(), (docs) => docs.length === 0 && (this._edtingTitle = false));
+        reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this._edtingTitle = false);
     }
 
     @action titleChanged = (event: any) => { this._title = event.target.value; };
@@ -197,6 +197,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (e.button === 0) {
             this._downX = e.pageX;
             this._downY = e.pageY;
+            this._removeIcon = false;
             let selDoc = SelectionManager.SelectedDocuments()[0];
             let selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
             this._minimizedX = selDocPos[0] + 12;
@@ -222,18 +223,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             Promise.all(selectedDocs.map(selDoc => this.getIconDoc(selDoc))).then(minDocSet =>
                 this.moveIconDocs(SelectionManager.SelectedDocuments())
             );
-            this._iconifying = snapped;
+            this._removeIcon = snapped;
         }
     }
 
-
     @action createIcon = (docView: DocumentView, layoutString: string): Doc => {
         let doc = docView.props.Document;
         let iconDoc = Docs.IconDocument(layoutString);
         iconDoc.title = "ICON" + StrCast(doc.title);
+        iconDoc.labelField = this._fieldKey;
         iconDoc.isMinimized = false;
-        iconDoc.nativeWidth = Number(MINIMIZED_ICON_SIZE);
-        iconDoc.nativeHeight = Number(MINIMIZED_ICON_SIZE);
         iconDoc.width = Number(MINIMIZED_ICON_SIZE);
         iconDoc.height = Number(MINIMIZED_ICON_SIZE);
         iconDoc.x = NumCast(doc.x);
@@ -275,15 +274,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 minDocs.map(minDoc => {
                     minDoc.x = NumCast(minDocs[0].x);
                     minDoc.y = NumCast(minDocs[0].y);
-                    minDoc.linkTags = new List(minDocs.filter(d => d !== minDoc));
-                    if (this._iconifying && selectedDocs[0].props.removeDocument) {
+                    minDoc.linkedIconTags = new List(minDocs.filter(d => d !== minDoc));
+                    if (this._removeIcon && selectedDocs[0].props.removeDocument) {
                         selectedDocs[0].props.removeDocument(minDoc);
                         (minDoc.maximizedDoc as Doc).minimizedDoc = undefined;
                     }
                 });
                 runInAction(() => this._minimizedX = this._minimizedY = 0);
-                if (!this._iconifying) selectedDocs[0].props.toggleMinimized();
-                this._iconifying = false;
+                selectedDocs[0].props.toggleMinimized();
+                this._removeIcon = false;
             });
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 54b721f7b..17c25c9db 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -225,7 +225,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     onDragOver = (): void => {
     }
 
-    bringToFront(doc: Doc) {
+    bringToFront = (doc: Doc) => {
         const docs = (this.children || []);
         docs.slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
@@ -266,6 +266,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             focus: this.focusDocument,
             parentActive: this.props.active,
             whenActiveChanged: this.props.active,
+            bringToFront: this.bringToFront,
         };
     }
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 09f9c8516..53298fbdd 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -112,10 +112,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     public toggleIcon = async (): Promise<void> => {
         SelectionManager.DeselectAll();
         let isMinimized: boolean | undefined;
-        let minimizedDocSet = Cast(this.props.Document.linkTags, listSpec(Doc));
-        if (!minimizedDocSet) return;
+        let minimizedDocSet = Cast(this.props.Document.linkedIconTags, listSpec(Doc), []);
         let docs = minimizedDocSet.map(d => d);
-        docs.push(this.props.Document);
+        let minimDoc = Cast(this.props.Document.minimizedDoc, Doc);
+        if (minimDoc instanceof Doc) docs.push(minimDoc);
+        else docs.push(this.props.Document);
         docs.map(async minimizedDoc => {
             this.props.addDocument && this.props.addDocument(minimizedDoc, false);
             let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
@@ -125,6 +126,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
                     isMinimized = (maximizedDocMinimizedState) ? true : false;
                 }
+                if (isMinimized) this.props.bringToFront(maximizedDoc);
                 let minx = NumCast(minimizedDoc.x, undefined);
                 let miny = NumCast(minimizedDoc.y, undefined);
                 let maxx = NumCast(maximizedDoc.x, undefined);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8c245ab3a..892850a69 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -53,6 +53,7 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     toggleMinimized: () => void;
+    bringToFront: (doc: Doc) => void;
 }
 
 const schema = createSchema({
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
index ce0ee2e09..85bbdeb59 100644
--- a/src/client/views/nodes/IconBox.scss
+++ b/src/client/views/nodes/IconBox.scss
@@ -4,9 +4,19 @@
     position: absolute;
     left:0;
     top:0;
+    height: 100%;
+    width: max-content;
+    // overflow: hidden;
+    pointer-events: all;
     svg {
-        width: 100% !important;
+        width:  $MINIMIZED_ICON_SIZE !important;
         height: 100%;
         background: white;
     }
+    .iconBox-label {
+        position: inherit;
+        width:max-content;
+        font-size: 14px;
+        margin-top: 3px;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 7a0c49735..08301ae0d 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -2,14 +2,17 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed } from "mobx";
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { SelectionManager } from "../../util/SelectionManager";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./IconBox.scss";
-import { Cast } from "../../../new_fields/Types";
-import { Doc } from "../../../new_fields/Doc";
+import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import { IconField } from "../../../new_fields/IconField";
+import { ContextMenu } from "../ContextMenu";
+import Measure from "react-measure";
+import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
 
 
 library.add(faCaretUp);
@@ -36,10 +39,31 @@ export class IconBox extends React.Component<FieldViewProps> {
         return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
 
+    setLabelField = (e: React.MouseEvent): void => {
+        this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel);
+    }
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({
+            description: BoolCast(this.props.Document.hideLabel) ? "show label" : "hide label",
+            event: this.setLabelField
+        });
+    }
+    @observable _panelWidth: number = 0;
+    @observable _panelHeight: number = 0;
     render() {
+        let labelField = StrCast(this.props.Document.labelField);
+        let hideLabel = BoolCast(this.props.Document.hideLabel);
+        let maxDoc = Cast(this.props.Document.maximizedDoc, Doc) as Doc;
+        let label = !hideLabel && maxDoc && labelField ? maxDoc[labelField] : "";
         return (
-            <div className="iconBox-container">
+            <div className="iconBox-container" onContextMenu={this.specificContextMenu}>
                 {this.minimizedIcon}
+                <Measure onResize={(r) => runInAction(() => { if (r.entry.width || BoolCast(this.props.Document.hideLabel)) this.props.Document.nativeWidth = this.props.Document.width = (r.entry.width + Number(MINIMIZED_ICON_SIZE)); })}>
+                    {({ measureRef }) =>
+                        <span ref={measureRef} className="iconBox-label">{label}</span>
+                    }
+                </Measure>
             </div>);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From b12143cc9822e2d590212363e02a1f8771c81e3b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 3 May 2019 12:36:06 -0400
Subject: more icon fixes for multiple documents.

---
 src/client/views/DocumentDecorations.tsx           | 95 ++++++++++------------
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 35 ++++----
 src/client/views/nodes/IconBox.tsx                 |  6 +-
 3 files changed, 64 insertions(+), 72 deletions(-)

(limited to 'src/client/views/nodes/IconBox.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9771c6580..2299f4c32 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -25,6 +25,10 @@ import { faLink } from '@fortawesome/free-solid-svg-icons';
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
+import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView";
+import { CollectionView } from "./collections/CollectionView";
+import { createCipher } from "crypto";
+import { FieldView } from "./nodes/FieldView";
 
 library.add(faLink);
 
@@ -41,6 +45,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     private _linkerButton = React.createRef<HTMLDivElement>();
     private _downX = 0;
     private _downY = 0;
+    private _iconDoc?: Doc = undefined;
     @observable private _minimizedX = 0;
     @observable private _minimizedY = 0;
     @observable private _title: string = "";
@@ -194,6 +199,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     onMinimizeDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
+        this._iconDoc = undefined;
         if (e.button === 0) {
             this._downX = e.pageX;
             this._downY = e.pageY;
@@ -220,26 +226,46 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             this._minimizedX = snapped ? selDocPos[0] + 4 : e.clientX;
             this._minimizedY = snapped ? selDocPos[1] - 18 : e.clientY;
             let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
-            Promise.all(selectedDocs.map(selDoc => this.getIconDoc(selDoc))).then(minDocSet =>
-                this.moveIconDocs(SelectionManager.SelectedDocuments())
-            );
+
+            if (selectedDocs.length > 1) {
+                this._iconDoc = this._iconDoc ? this._iconDoc : this.createIcon(SelectionManager.SelectedDocuments(), CollectionView.LayoutString());
+                this.moveIconDoc(this._iconDoc);
+            } else {
+                this.getIconDoc(selectedDocs[0]).then(icon => icon && this.moveIconDoc(this._iconDoc = icon));
+            }
             this._removeIcon = snapped;
         }
     }
+    @action
+    onMinimizeUp = (e: PointerEvent): void => {
+        e.stopPropagation();
+        if (e.button === 0) {
+            document.removeEventListener("pointermove", this.onMinimizeMove);
+            document.removeEventListener("pointerup", this.onMinimizeUp);
+            let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
+            if (this._iconDoc && selectedDocs.length === 1 && this._removeIcon) {
+                selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc);
+            }
+            !this._removeIcon && selectedDocs.length === 1 && this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].props.toggleMinimized());
+            this._removeIcon = false;
+        }
+        runInAction(() => this._minimizedX = this._minimizedY = 0);
+    }
 
-    @action createIcon = (docView: DocumentView, layoutString: string): Doc => {
-        let doc = docView.props.Document;
+    @action createIcon = (selected: DocumentView[], layoutString: string): Doc => {
+        let doc = selected[0].props.Document;
         let iconDoc = Docs.IconDocument(layoutString);
-        iconDoc.title = "ICON" + StrCast(doc.title);
+        iconDoc.title = selected.length > 1 ? "ICONset" : "ICON" + StrCast(doc.title);
         iconDoc.labelField = this._fieldKey;
+        iconDoc[this._fieldKey] = selected.length > 1 ? "collection" : undefined;
         iconDoc.isMinimized = false;
         iconDoc.width = Number(MINIMIZED_ICON_SIZE);
         iconDoc.height = Number(MINIMIZED_ICON_SIZE);
         iconDoc.x = NumCast(doc.x);
         iconDoc.y = NumCast(doc.y) - 24;
-        iconDoc.maximizedDoc = doc;
+        iconDoc.maximizedDocs = new List<Doc>(selected.map(s => s.props.Document));
         doc.minimizedDoc = iconDoc;
-        docView.props.addDocument && docView.props.addDocument(iconDoc, false);
+        selected[0].props.addDocument && selected[0].props.addDocument(iconDoc, false);
         return iconDoc;
     }
     @action
@@ -247,56 +273,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let doc = docView.props.Document;
         let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc);
         if (!iconDoc) {
-            const background = StrCast(doc.backgroundLayout, undefined);
-            if (background) {
-                iconDoc = this.createIcon(docView, background);
-            } else {
-                const layout = StrCast(doc.layout, undefined);
-                if (layout) {
-                    iconDoc = this.createIcon(docView, layout);
-                }
-            }
+            const layout = StrCast(doc.backgroundLayout, StrCast(doc.layout, FieldView.LayoutString(DocumentView)));
+            iconDoc = this.createIcon([docView], layout);
         }
         if (SelectionManager.SelectedDocuments()[0].props.addDocument !== undefined) {
             SelectionManager.SelectedDocuments()[0].props.addDocument!(iconDoc!);
         }
         return iconDoc;
     }
-    @action
-    onMinimizeUp = (e: PointerEvent): void => {
-        e.stopPropagation();
-        if (e.button === 0) {
-            document.removeEventListener("pointermove", this.onMinimizeMove);
-            document.removeEventListener("pointerup", this.onMinimizeUp);
-            let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
-            Promise.all(selectedDocs.map(selDoc => this.getIconDoc(selDoc))).then(minDocSet => {
-                let minDocs = minDocSet.filter(minDoc => minDoc instanceof Doc).map(minDoc => minDoc as Doc);
-                minDocs.map(minDoc => {
-                    minDoc.x = NumCast(minDocs[0].x);
-                    minDoc.y = NumCast(minDocs[0].y);
-                    minDoc.linkedIconTags = new List(minDocs.filter(d => d !== minDoc));
-                    if (this._removeIcon && selectedDocs[0].props.removeDocument) {
-                        selectedDocs[0].props.removeDocument(minDoc);
-                        (minDoc.maximizedDoc as Doc).minimizedDoc = undefined;
-                    }
-                });
-                runInAction(() => this._minimizedX = this._minimizedY = 0);
-                selectedDocs[0].props.toggleMinimized();
-                this._removeIcon = false;
-            });
-        }
-    }
-    moveIconDocs(selViews: DocumentView[], minDocSet?: FieldResult[]) {
-        selViews.map(selDoc => {
-            let minDoc = selDoc.props.Document.minimizedDoc;
-            if (minDoc instanceof Doc) {
-                let zoom = NumCast(selDoc.props.Document.zoomBasis, 1);
-                let where = (selDoc.props.ScreenToLocalTransform()).scale(selDoc.props.ContentScaling()).scale(1 / zoom).
-                    transformPoint(this._minimizedX - 12, this._minimizedY - 12);
-                minDoc.x = where[0] + NumCast(selDoc.props.Document.x);
-                minDoc.y = where[1] + NumCast(selDoc.props.Document.y);
-            }
-        });
+    moveIconDoc(iconDoc: Doc) {
+        let selView = SelectionManager.SelectedDocuments()[0];
+        let zoom = NumCast(selView.props.Document.zoomBasis, 1);
+        let where = (selView.props.ScreenToLocalTransform()).scale(selView.props.ContentScaling()).scale(1 / zoom).
+            transformPoint(this._minimizedX - 12, this._minimizedY - 12);
+        iconDoc.x = where[0] + NumCast(selView.props.Document.x);
+        iconDoc.y = where[1] + NumCast(selView.props.Document.y);
     }
 
     @action
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 53298fbdd..6bb00dd5c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -11,6 +11,7 @@ import { OmitKeys, Utils } from "../../../Utils";
 import { SelectionManager } from "../../util/SelectionManager";
 import { matchedData } from "express-validator/filter";
 import { Doc } from "../../../new_fields/Doc";
+import { List } from "../../../new_fields/List";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
@@ -112,23 +113,23 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     public toggleIcon = async (): Promise<void> => {
         SelectionManager.DeselectAll();
         let isMinimized: boolean | undefined;
-        let minimizedDocSet = Cast(this.props.Document.linkedIconTags, listSpec(Doc), []);
-        let docs = minimizedDocSet.map(d => d);
-        let minimDoc = Cast(this.props.Document.minimizedDoc, Doc);
-        if (minimDoc instanceof Doc) docs.push(minimDoc);
-        else docs.push(this.props.Document);
-        docs.map(async minimizedDoc => {
-            this.props.addDocument && this.props.addDocument(minimizedDoc, false);
-            let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
-            if (maximizedDoc && !maximizedDoc.isIconAnimating) {
+        let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc));
+        let minimizedDoc: Doc | undefined = this.props.Document;
+        if (!maximizedDocs) {
+            minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
+            if (minimizedDoc) maximizedDocs = await Cast(minimizedDoc.maximizedDocs, listSpec(Doc));
+        }
+        if (minimizedDoc && maximizedDocs && maximizedDocs instanceof List && !maximizedDocs.some(md => BoolCast(md.isIconAnimating))) {
+            let minimizedTarget = minimizedDoc;
+            maximizedDocs.map(maximizedDoc => {
                 maximizedDoc.isIconAnimating = true;
                 if (isMinimized === undefined) {
                     let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
                     isMinimized = (maximizedDocMinimizedState) ? true : false;
                 }
                 if (isMinimized) this.props.bringToFront(maximizedDoc);
-                let minx = NumCast(minimizedDoc.x, undefined);
-                let miny = NumCast(minimizedDoc.y, undefined);
+                let minx = NumCast(minimizedTarget.x, undefined);
+                let miny = NumCast(minimizedTarget.y, undefined);
                 let maxx = NumCast(maximizedDoc.x, undefined);
                 let maxy = NumCast(maximizedDoc.y, undefined);
                 let maxw = NumCast(maximizedDoc.width, undefined);
@@ -137,8 +138,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     maxw !== undefined && maxh !== undefined) {
                     this.animateBetweenIcon(true, [minx, miny], [maxx, maxy], maxw, maxh, Date.now(), maximizedDoc, isMinimized);
                 }
-            }
-        })
+            });
+        }
     }
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
@@ -149,9 +150,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         e.stopPropagation();
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            const maxDoc = await Cast(this.props.Document.maximizedDoc, Doc);
-            if (maxDoc) {   // bcz: need a better way to associate behaviors with click events on widget-documents
-                this.props.addDocument && this.props.addDocument(maxDoc, false);
+            let maximizedDocs = await Cast(this.props.Document.maximizedDocs, listSpec(Doc));
+            if (maximizedDocs) {   // bcz: need a better way to associate behaviors with click events on widget-documents
+                this.props.addDocument && maximizedDocs.filter(d => d instanceof Doc).map(maxDoc => this.props.addDocument!(maxDoc, false));
                 this.toggleIcon();
             }
         }
@@ -169,7 +170,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     render() {
-        let maximizedDoc = FieldValue(Cast(this.props.Document.maximizedDoc, Doc));
+        let maximizedDoc = FieldValue(Cast(this.props.Document.maximizedDocs, listSpec(Doc)));
         let zoomFade = 1;
         //var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
         let transform = this.getTransform().scale(this.contentScaling()).inverse();
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 08301ae0d..3fab10df4 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -13,6 +13,7 @@ import { IconField } from "../../../new_fields/IconField";
 import { ContextMenu } from "../ContextMenu";
 import Measure from "react-measure";
 import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
+import { listSpec } from "../../../new_fields/Schema";
 
 
 library.add(faCaretUp);
@@ -25,7 +26,6 @@ library.add(faFilm);
 export class IconBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(IconBox); }
 
-    @computed get maximized() { return Cast(this.props.Document.maximizedDoc, Doc); }
     @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
     @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
 
@@ -54,8 +54,8 @@ export class IconBox extends React.Component<FieldViewProps> {
     render() {
         let labelField = StrCast(this.props.Document.labelField);
         let hideLabel = BoolCast(this.props.Document.hideLabel);
-        let maxDoc = Cast(this.props.Document.maximizedDoc, Doc) as Doc;
-        let label = !hideLabel && maxDoc && labelField ? maxDoc[labelField] : "";
+        let maxDoc = Cast(this.props.Document.maximizedDocs, listSpec(Doc), []);
+        let label = !hideLabel && maxDoc && labelField ? (maxDoc.length === 1 ? maxDoc[0][labelField] : this.props.Document[labelField]) : "";
         return (
             <div className="iconBox-container" onContextMenu={this.specificContextMenu}>
                 {this.minimizedIcon}
-- 
cgit v1.2.3-70-g09d2

