From 075b626e1eab5d2852d5157c4105e3110e4fe7e4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Sep 2020 10:16:59 -0400
Subject: fixed shift-dragging of tabs

---
 src/client/views/collections/TabDocView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 589649ad9..5ace82b3c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -367,7 +367,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         return (<div className="collectionDockingView-content" ref={ref => {
             if (this._mainCont = ref) {
                 (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
-                this.tab && DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.init(this.tab, this._document)));
+                DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
             }
         }}
             style={{
-- 
cgit v1.2.3-70-g09d2


From 51eea45ec6ee39cb83ee3b0780ad262b8b8b5dd8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Sep 2020 20:10:58 -0400
Subject: made basic progressivized slides using TreeView

---
 src/client/documents/Documents.ts                  |  2 +
 src/client/util/CurrentUserUtils.ts                |  4 ++
 src/client/views/EditableView.tsx                  | 23 ++++++----
 src/client/views/PropertiesView.tsx                |  2 +-
 .../views/collections/CollectionTreeView.scss      | 13 +++++-
 .../views/collections/CollectionTreeView.tsx       | 49 ++++++++++++++++++----
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        |  4 ++
 .../collections/collectionFreeForm/MarqueeView.tsx | 24 +++++++----
 src/client/views/nodes/DocumentView.tsx            |  3 --
 src/client/views/nodes/PresBox.tsx                 | 16 ++++---
 11 files changed, 105 insertions(+), 37 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2f7b5a449..f67887c09 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -130,6 +130,7 @@ export interface DocumentOptions {
     opacity?: number;
     defaultBackgroundColor?: string;
     _isBackground?: boolean;
+    "_isBackground-canClick"?: boolean; // a background document that you can still click on to edit its contents
     isLinkButton?: boolean;
     _columnWidth?: number;
     _fontSize?: string;
@@ -195,6 +196,7 @@ export interface DocumentOptions {
     treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
     treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
     treeViewTruncateTitleWidth?: number;
+    treeViewOutlineMode?: boolean; // whether slide should function as a text outline
     treeViewLockExpandedView?: boolean; // whether the expanded view can be changed
     treeViewDefaultExpandedView?: string; // default expanded view
     limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2f08aa928..b3567a4e8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -418,6 +418,9 @@ export class CurrentUserUtils {
             doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
+        if (doc.emptySlide === undefined) {
+            doc.emptySlide = Docs.Create.TreeDocument([], { title: "slide", treeViewOutlineMode: true, "_isBackground-canClick": true, _backgroundColor: "transparent", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
+        }
         if (doc.emptyComparison === undefined) {
             doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
@@ -453,6 +456,7 @@ export class CurrentUserUtils {
         return [
             { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
             { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
+            { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc, noviceMode: true },
             { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc },
             { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
             { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 1b4b9a2be..cbbd78a20 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -18,11 +18,12 @@ export interface EditableProps {
      * @param value - The string entered by the user to set the value to
      * @returns `true` if setting the value was successful, `false` otherwise
      *  */
-    SetValue(value: string, shiftDown?: boolean): boolean;
+    SetValue(value: string, shiftDown?: boolean, enterKey?: boolean): boolean;
 
     OnFillDown?(value: string): void;
 
     OnTab?(shift?: boolean): void;
+    OnEmpty?(): void;
 
     /**
      * The contents to render when not editing
@@ -93,13 +94,17 @@ export class EditableView extends React.Component<EditableProps> {
         switch (e.key) {
             case "Tab":
                 e.stopPropagation();
-                this.finalizeEdit(e.currentTarget.value, e.shiftKey, false);
-                this.props.OnTab && this.props.OnTab(e.shiftKey);
+                this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, false);
+                this.props.OnTab?.(e.shiftKey);
+                break;
+            case "Backspace":
+                e.stopPropagation();
+                if (!e.currentTarget.value) this.props.OnEmpty?.();
                 break;
             case "Enter":
                 e.stopPropagation();
                 if (!e.ctrlKey) {
-                    this.finalizeEdit(e.currentTarget.value, e.shiftKey, false);
+                    this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
                 } else if (this.props.OnFillDown) {
                     this.props.OnFillDown(e.currentTarget.value);
                     this._editing = false;
@@ -130,10 +135,10 @@ export class EditableView extends React.Component<EditableProps> {
     }
 
     @action
-    private finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean) {
-        if (this.props.SetValue(value, shiftDown)) {
+    private finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean, enterKey: boolean) {
+        if (this.props.SetValue(value, shiftDown, enterKey)) {
             this._editing = false;
-            this.props.isEditingCallback?.(false);
+            this.props.isEditingCallback?.(false,);
         } else {
             this._editing = false;
             this.props.isEditingCallback?.(false);
@@ -164,7 +169,7 @@ export class EditableView extends React.Component<EditableProps> {
                     className: "editableView-input",
                     onKeyDown: this.onKeyDown,
                     autoFocus: true,
-                    onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true),
+                    onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true, false),
                     onPointerDown: this.stopPropagation,
                     onClick: this.stopPropagation,
                     onPointerUp: this.stopPropagation,
@@ -178,7 +183,7 @@ export class EditableView extends React.Component<EditableProps> {
                 onKeyDown={this.onKeyDown}
                 autoFocus={true}
                 onKeyPress={e => e.stopPropagation()}
-                onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)}
+                onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
                 onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
                 style={{ display: this.props.display, fontSize: this.props.fontSize, minWidth: 20 }}
                 placeholder={this.props.placeholder}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index d70f4b332..5a9402d70 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1040,7 +1040,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>}
                     {!selectedItem ? (null) : <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
-                            onPointerDown={action(() => { this.openPresProgressivize = !this.openPresProgressivize; })}
+                            onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)}
                             style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
                             &nbsp; <FontAwesomeIcon icon={"tasks"} /> &nbsp; Progressivize
                         <div className="propertiesView-presTrails-title-icon">
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index e192f1760..3f437b799 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -30,6 +30,14 @@
         padding-left: 0;
     }
 
+    .outline-bullet {
+        position: relative;
+        width: 15px;
+        color: $intermediate-color;
+        margin-top: 3px;
+        transform: scale(0.5);
+    }
+
     .bullet {
         position: relative;
         width: 15px;
@@ -106,11 +114,14 @@
     cursor: pointer;
 }
 
+.treeViewItem-border-outline,
 .treeViewItem-border {
     display: flex;
-    border-left: dashed 1px #00000042;
     overflow: hidden;
 }
+.treeViewItem-border{
+    border-left: dashed 1px #00000042;
+}
 
 .treeViewItem-header-editing,
 .treeViewItem-header {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d2b86d3c6..a6975f517 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -181,22 +181,38 @@ class TreeView extends React.Component<TreeViewProps> {
         fontStyle={style}
         fontSize={12}
         GetValue={() => StrCast(this.doc[key])}
-        SetValue={undoBatch((value: string) => {
-            Doc.SetInPlace(this.doc, key, value, false) || true;
-            Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+        SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
+            if (this.props.treeView.doc.treeViewOutlineMode && enterKey) {
+                Doc.SetInPlace(this.doc, key, value, false);
+                const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+                Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+                Doc.SetInPlace(doc, "editTitle", "*", false);
+                this.props.addDocument(doc);
+                doc.context = this.props.treeView.Document;
+            } else {
+                Doc.SetInPlace(this.doc, key, value, false) || true;
+                Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+            }
         })}
         OnFillDown={undoBatch((value: string) => {
             Doc.SetInPlace(this.doc, key, value, false);
-            const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+            const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
             Doc.SetInPlace(this.doc, "editTitle", undefined, false);
             Doc.SetInPlace(doc, "editTitle", "*", false);
-            return this.props.addDocument(doc);
+            const added = this.props.addDocument(doc);
+            doc.context = this.props.treeView.Document;
+            return added;
         })}
         onClick={() => {
             SelectionManager.DeselectAll();
             Doc.UserDoc().activeSelection = new List([this.doc]);
             return false;
         }}
+        OnEmpty={undoBatch(() => {
+            if (this.props.treeView.doc.treeViewOutlineMode) {
+                this.props.removeDoc?.(this.doc);
+            }
+        })}
         OnTab={undoBatch((shift?: boolean) => {
             shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
             setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0);
@@ -398,13 +414,25 @@ class TreeView extends React.Component<TreeViewProps> {
         e.stopPropagation();
     }
 
+    @computed get renderOutlineBullet() {
+        TraceMobx();
+        return <div className="outline-bullet"
+            title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
+            onClick={this.bulletClick}
+            style={{ opacity: NumCast(this.doc.opacity, 1) }}>
+            {<FontAwesomeIcon icon={"circle"} />}
+        </div>;
+    }
     @computed get renderBullet() {
         TraceMobx();
         const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
         return <div className="bullet"
             title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
             onClick={this.bulletClick}
-            style={{ color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}>
+            style={{
+                color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
+                opacity: checked === "unchecked" ? undefined : 0.4
+            }}>
             {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs?.length ? "caret-square-right" : "caret-right") : (this.childDocs?.length ? "caret-square-down" : "caret-down"))} />}
         </div>;
     }
@@ -521,10 +549,10 @@ class TreeView extends React.Component<TreeViewProps> {
                             }
                         }}
                         onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        {this.renderBullet}
+                        {this.props.treeView.props.Document.treeViewOutlineMode ? this.renderOutlineBullet : this.renderBullet}
                         {this.renderTitle}
                     </div>
-                    <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+                    <div className={`treeViewItem-border${this.props.treeView.props.Document.treeViewOutlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
                         {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
                     </div>
                 </li>
@@ -617,6 +645,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         Doc.AddDocToList(docs[i - 1], fieldKey, child);
                         docs[i - 1].treeViewOpen = true;
                         remove(child);
+                        child.context = treeView.Document;
                     }
                 }
             };
@@ -627,6 +656,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
                     parentCollectionDoc.treeViewOpen = true;
                     remove(child);
+                    child.context = treeView.Document;
                 }
             };
             const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => {
@@ -729,8 +759,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true);
         if (this.doc.resolvedDataDoc instanceof Promise) {
             this.doc.resolvedDataDoc.then((resolved: any) => doAddDoc(doc));
+        } else if (relativeTo === undefined) {
+            this.props.addDocument(doc);
         } else {
             doAddDoc(doc);
+            (doc instanceof Doc ? [doc] : doc).forEach(d => d.context = this.props.Document);
         }
         return true;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6939399e6..7084aba40 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -391,7 +391,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutTemplate: this.childLayoutTemplate,
             ChildLayoutString: this.childLayoutString,
         };
-        const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
+        const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.props.Document._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
             `${CurrentUserUtils.ActiveDashboard?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.Document._isBackground ? "none" : undefined, boxShadow }}>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5ace82b3c..aa1852250 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -146,6 +146,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     CollectionDockingView.AddSplit(curPres, "right");
                 }
                 DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
+                setTimeout(() => {
+                    curPres._itemIndex = DocListCast(curPres.data).length - 1;
+                    doc.treeViewOutlineMode && PresBox.Instance.progressivizeChild(null as any);
+                }, 100);
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index deb7e68e8..ebad3bf45 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -116,15 +116,23 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 })();
                 e.stopPropagation();
             } else if (e.key === "b" && e.ctrlKey) {
+                // e.preventDefault();
+                // navigator.clipboard.readText().then(text => {
+                //     const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+                //     if (ns.length === 1 && text.startsWith("http")) {
+                //         this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
+                //     } else {
+                //         this.pasteTable(ns, x, y);
+                //     }
+                // });
+                // e.stopPropagation();
+
                 e.preventDefault();
-                navigator.clipboard.readText().then(text => {
-                    const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
-                    if (ns.length === 1 && text.startsWith("http")) {
-                        this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
-                    } else {
-                        this.pasteTable(ns, x, y);
-                    }
-                });
+                const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
+                slide.x = x;
+                slide.y = y;
+                this.props.addDocument(slide);
+                setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false));
                 e.stopPropagation();
             } else if (!e.ctrlKey && !e.metaKey) {
                 FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : "";
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7ef5eaf54..7b7c3578b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -593,9 +593,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (CurrentUserUtils.ActiveDashboard === this.props.Document) {
             alert("Can't delete the active dashboard");
         } else {
-            const selected = SelectionManager.SelectedDocuments().slice();
-            SelectionManager.DeselectAll();
-            selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
             this.props.removeDocument?.(this.props.Document);
         }
     }
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 99fb5d2ce..79f1b7ddc 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1258,7 +1258,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                     <div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                         <div className="ribbon-box">
                             {this.stringType} selected
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc.type === DocumentType.COL ? "inline-flex" : "none" }}>
                                 <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
                                 <div className="ribbon-button" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
                             </div>
@@ -1435,18 +1435,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     }
 
     @action
-    progressivizeChild = (e: React.MouseEvent) => {
-        e.stopPropagation();
+    progressivizeChild = (e?: React.MouseEvent) => {
+        e?.stopPropagation();
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
         if (!activeItem.presProgressivize) {
             targetDoc.editing = false;
             activeItem.presProgressivize = true;
             targetDoc.presProgressivize = true;
             targetDoc._currentFrame = 0;
-            docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
-            targetDoc.lastFrame = docs.length - 1;
+            let count = 0;
+            const setupProgressivize = (doc: Doc) => {
+                CollectionFreeFormDocumentView.setupKeyframes([doc], count++, true);
+                targetDoc.treeViewOutlineMode && DocListCast(doc[Doc.LayoutFieldKey(doc)]).forEach(d => setupProgressivize(d));
+            }
+            setupProgressivize(targetDoc);
+            targetDoc.lastFrame = count;
         } else {
             targetDoc.editProgressivize = false;
             activeItem.presProgressivize = false;
-- 
cgit v1.2.3-70-g09d2

