From a9459901be6ed608fef28d4703072aa594748f84 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 14 Jul 2021 11:19:51 -0400
Subject: fixed dropping onto filesystem view to drop document into specific
 location/folder instead of hanging.

---
 src/client/views/collections/CollectionSubView.tsx | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8d549bd56..ca45536f4 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -241,7 +241,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
 
         @undoBatch
         @action
-        protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
+        protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: (docs: Doc[]) => void) {
             if (e.ctrlKey) {
                 e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
                 return;
@@ -439,7 +439,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             }
             this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
         }
-        slowLoadDocuments = async (files: (File[] | string), options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: (() => void) | undefined, clientX: number, clientY: number, addDocument: (doc: Doc | Doc[]) => boolean) => {
+        slowLoadDocuments = async (files: (File[] | string), options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, clientX: number, clientY: number, addDocument: (doc: Doc | Doc[]) => boolean) => {
             const disposer = OverlayView.Instance.addElement(
                 <ReactLoading type={"spinningBubbles"} color={"green"} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
             if (typeof files === "string") {
@@ -448,13 +448,17 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
             }
             if (generatedDocuments.length) {
-                const set = generatedDocuments.length > 1 && generatedDocuments.map(d => DocUtils.iconify(d));
-                if (set) {
-                    addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
-                } else {
-                    generatedDocuments.forEach(addDocument);
+                const isFreeformView = this.props.Document._viewType === CollectionViewType.Freeform;
+                const set = !isFreeformView ? generatedDocuments :
+                    generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
+                if (completed) completed(set);
+                else {
+                    if (isFreeformView) {
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+                    } else {
+                        generatedDocuments.forEach(addDocument);
+                    }
                 }
-                completed?.();
             } else {
                 if (text && !text.includes("https://")) {
                     addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 }));
-- 
cgit v1.2.3-70-g09d2


From 26e9e0a554ee5aff001e2bc10b6802a4e830b63c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 26 Jul 2021 12:35:25 -0400
Subject: cleaned up pile view a bit so that it doesn't scale up huge when
 there's one document.  made the pile be round and drop-shadowed.   made the
 pile go away immediately if there are no documents.  made it work with undo.

---
 src/client/documents/Documents.ts                         |  2 --
 src/client/views/StyleProvider.tsx                        | 15 ++++++++-------
 src/client/views/collections/CollectionPileView.tsx       | 15 +++++++++------
 src/client/views/collections/CollectionSubView.tsx        |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx                   |  5 +++--
 5 files changed, 21 insertions(+), 18 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c2cdf7f72..f1db3e32c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1333,9 +1333,7 @@ export namespace DocUtils {
         newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
         newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
         newCollection._width = newCollection._height = 110;
-        //newCollection.borderRounding = "40px";
         newCollection._jitterRotation = 10;
-        newCollection._backgroundColor = "gray";
         return newCollection;
     }
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 55f6b8e3c..6b94539c9 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -107,7 +107,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY;
             return Colors.WHITE;
         case StyleProp.Hidden: return BoolCast(doc?._hidden);
-        case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"]);
+        case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], doc?._viewType === CollectionViewType.Pile ? "50%" : "");
         case StyleProp.TitleHeight: return 15;
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
         case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
@@ -136,14 +136,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.VID: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes("SliderBox")) break;
-                    docColor = docColor ? Colors.DARK_GRAY :
-                        doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
-                            (Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
+                    docColor = docColor ||
+                        (doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
+                            (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                                 isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                     doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
-                                            Doc.UserDoc().activeCollectionBackground));
+                                            Doc.UserDoc().activeCollectionBackground)));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
@@ -159,8 +159,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             switch (doc?.type) {
                 case DocumentType.COL:
                     return StrCast(doc?.boxShadow,
-                        isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
-                            `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
+                        doc?._viewType === CollectionViewType.Pile ? "4px 4px 10px 2px" :
+                            isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
+                                `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
 
                 case DocumentType.LABEL:
                     if (doc?.annotationOn !== undefined) return "black 2px 2px 1px";
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 6baf633dd..bc1407c53 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,12 +1,12 @@
-import { action, computed } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
 import { NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, returnTrue } from "../../../Utils";
+import { emptyFunction, returnTrue, setupMoveUpEvents } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
-import { UndoManager, undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import "./CollectionPileView.scss";
 import { CollectionSubView } from "./CollectionSubView";
@@ -15,6 +15,7 @@ import React = require("react");
 @observer
 export class CollectionPileView extends CollectionSubView(doc => doc) {
     _originalChrome: any = "";
+    _disposers: { [name: string]: IReactionDisposer } = {};
 
     componentDidMount() {
         if (this.layoutEngine() !== "pass" && this.layoutEngine() !== "starburst") {
@@ -22,9 +23,14 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         }
         this._originalChrome = this.layoutDoc._chromeHidden;
         this.layoutDoc._chromeHidden = true;
+
+        // pileups are designed to go away when they are empty.  
+        this._disposers.selected = reaction(() => this.childDocs.length,
+            (num) => !num && this.props.ContainingCollectionView?.removeDocument(this.props.Document));
     }
     componentWillUnmount() {
         this.layoutDoc._chromeHidden = this._originalChrome;
+        Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
     layoutEngine = () => StrCast(this.Document._pileLayoutEngine);
@@ -107,9 +113,6 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
             this._undoBatch?.end();
             this._undoBatch = undefined;
             SnappingManager.SetIsDragging(false);
-            if (!this.childDocs.length) {
-                this.props.ContainingCollectionView?.removeDocument(this.props.Document);
-            }
         }, emptyFunction, e.shiftKey && this.layoutEngine() === "pass", this.layoutEngine() === "pass" && e.shiftKey); // this sets _doubleTap
     }
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index ca45536f4..f39443ae2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -453,7 +453,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
                 if (completed) completed(set);
                 else {
-                    if (isFreeformView) {
+                    if (isFreeformView && generatedDocuments.length > 1) {
                         addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
                     } else {
                         generatedDocuments.forEach(addDocument);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index afc1babeb..37444a9dc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -126,7 +126,8 @@ export function computerStarburstLayout(
             replica: ""
         });
     });
-    return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
+    const divider = { type: "div", color: "transparent", x: -burstRadius[0] / 3, y: 0, width: 15, height: 15, payload: undefined };
+    return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
 
@@ -399,7 +400,7 @@ function normalizeResults(
 ): ViewDefResult[] {
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
-    const aggBounds = aggregateBounds(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" }))).filter(e => e.zIndex !== -99), 0, 0);
+    const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
     aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
     let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
-- 
cgit v1.2.3-70-g09d2

