From 0dd3bbfc119d48bd2c32f8e55b7051e3ddc530c3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 21 Jun 2022 14:40:04 -0400
Subject: restored experimental templates.  fixed (enabled) pointer events on
 nested column/row views when container is active.  got rid of
 freezeDimensions which wasn't needed and introduced artifacts for views that
 set it (timeView, GridView)

---
 src/client/views/collections/CollectionStackingView.tsx | 1 -
 1 file changed, 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 277fcd59c..684c919bd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -250,7 +250,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             isDocumentActive={this.isContentActive}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
             NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
-- 
cgit v1.2.3-70-g09d2


From 5bf71bb9fde28de8cc2cedaf03708315933acc30 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 22 Jun 2022 13:46:39 -0400
Subject: more cleanup in currentuserutils.  fixed so that removing items from
 a list in code will remove them from the UI

---
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                | 209 ++++++++++-----------
 .../views/collections/CollectionStackingView.tsx   |   3 +-
 3 files changed, 104 insertions(+), 114 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d0c7d2436..01cfe90da 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -708,8 +708,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
         }
 
-        export function ScriptingDocument(script: Opt<ScriptField>, options: DocumentOptions = {}, fieldKey?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script,
+        export function ScriptingDocument(script: Opt<ScriptField>|null, options: DocumentOptions = {}, fieldKey?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script: undefined,
                 { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
         }
 
@@ -829,7 +829,7 @@ export namespace Docs {
             const nwid = options._nativeWidth || undefined;
             const nhght = options._nativeHeight || undefined;
             if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width);
-            return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(url) : undefined, options);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : "http://www.bing.com/"), options);
         }
 
         export function HtmlDocument(html: string, options: DocumentOptions = {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e119828c7..52fbda9a9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -104,6 +104,7 @@ export class CurrentUserUtils {
                 }
             });
             items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item));
+            items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), "data", item));
         }
         return doc;
     }
@@ -148,9 +149,9 @@ export class CurrentUserUtils {
             return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts);
         });
 
-        const reqdOpts = {
+        const reqdOpts:DocumentOptions = {
             title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
+            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, childDocumentsActive: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
@@ -197,17 +198,12 @@ export class CurrentUserUtils {
 
     // setup templates for different document types when they are iconified from Document Decorations
     static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
-        const templateIconsDoc = DocCast(doc[field]);
+        const reqdOpts = { title: "icon templates", _height: 75, system: true };
+        const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
-            if (!templateIconsDoc?.[iconFieldName]) {
-                const template = MakeTemplate(iconTemplate(), true, iconFieldName, templateField);
-                if (templateIconsDoc) {
-                    templateIconsDoc[iconFieldName] = template;
-                }
-                return template;
-            }
+            return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate(), true, iconFieldName, templateField))) ;
         };
         const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" });
         const labelBox = (extra: object) => Docs.Create.LabelDocument({
@@ -235,68 +231,37 @@ export class CurrentUserUtils {
             makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })),
             // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
         ].filter(d => d).map(d => d!);
-
-        const reqdOpts = { title: "icon templates", _height: 75, system: true };
-        this.AssignOpts(templateIconsDoc, reqdOpts, iconTemplates) ?? (doc[field] = Docs.Create.TreeDocument(iconTemplates, reqdOpts));
+        this.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
     }
 
-    /// initalizes the set of default versions of most document types
+    /// initalizes the set of "empty<DocType>" versions of each document type with default fields.  e.g.,. emptyNote, emptyPresentation
     static creatorBtnDescriptors(doc: Doc): {
         title: string, toolTip: string, icon: string,  ignoreClick?: boolean, dragFactory?: Doc, 
         backgroundColor?: string,  clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string},
     }[] {
-        const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
-        const emptyThings:{key:string,  // the field name where the empty thing will be stored
-                          opts:DocumentOptions,   // the document options that are required for the empty thing
-                          funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
-                          creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
-              }[] = [
-            {key: "emptyPresentation", creator: Docs.Create.PresDocument,  opts: { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }},
-            {key: "emptyCollection", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "freeform", _width: 150, _height: 100 }},
-            {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts:  { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }},
-            {key: "emptyMath", creator: (opts) => Docs.Create.EquationDocument(opts), opts: { title: "Equation", _backgroundGridShow: true, _width: 300, _height: 35 }},
-            {key: "emptySlide", creator: (opts) => Docs.Create.TreeDocument([], opts), funcs: {title: 'self.text?.Text'}, opts: {
-                _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
-                "dragFactory-count": undefined,  allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
-            }},
-            {key: "emptyComparison", creator: Docs.Create.ComparisonDocument, opts: { title: "Comparer", _width: 300, _height: 300 }},
-            {key: "emptyScript", creator: (opts) => Docs.Create.ScriptingDocument(undefined, opts), opts:  { title: "script", _width: 200, _height: 250, }},
-            {key: "emptyScreenshot", creator: Docs.Create.ScreenshotDocument, opts: { title: "empty screenshot", _width: 400, _height: 200 }},
-            {key: "emptyWebCam", creator: (opts) => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
-            {key: "emptyAudio", creator: (opts) => Docs.Create.AudioDocument(nullAudio, opts), opts: { title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }},
-            {key: "emptyNote", creator: (opts) => Docs.Create.TextDocument("", opts), opts:  { title: "text note", _width: 200, _autoHeight: true }},
-            {key: "emptyButton", creator: Docs.Create.ButtonDocument, opts: { title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }},
-            {key: "emptyWebpage", creator: (opts) => Docs.Create.WebDocument("http://www.bing.com/", opts), opts: { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }},
-            {key: "emptyMap", creator: (opts) =>  Docs.Create.MapDocument([], opts), opts: { title: "map", _showSidebar: true, _width: 800, _height: 600, }},
-            {key: "emptyDataViz", creator: (opts) => Docs.Create.DataVizDocument(opts), opts: { title: "DataViz",                 _width: 300, _height: 300 }},
-        ];
-
-        emptyThings.forEach(thing => 
-            this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), undefined, thing.funcs))
-        
-        if (doc.emptyHeader === undefined) {
-            const json = {
-                doc: {
-                    type: "doc",
-                    content: [
-                        {
-                            type: "paragraph", attrs: {}, content: [{
-                                type: "dashField",
-                                attrs: { fieldKey: "author", docid: "", hideKey: false },
-                                marks: [{ type: "strong" }]
-                            }, {
-                                type: "dashField",
-                                attrs: { fieldKey: "creationDate", docid: "", hideKey: false },
-                                marks: [{ type: "strong" }]
-                            }]
+        const standardOps = (key:string) => ({ title : "Untitled "+ key, _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
+        const json = {
+            doc: {
+                type: "doc",
+                content: [
+                    {
+                        type: "paragraph", attrs: {}, content: [{
+                            type: "dashField",
+                            attrs: { fieldKey: "author", docid: "", hideKey: false },
+                            marks: [{ type: "strong" }]
+                        }, {
+                            type: "dashField",
+                            attrs: { fieldKey: "creationDate", docid: "", hideKey: false },
+                            marks: [{ type: "strong" }]
                         }]
-                },
-                selection: { type: "text", anchor: 1, head: 1 },
-                storedMarks: []
-            };
-            const headerBtnHgt = 10;
-            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
-                ...standardOps(), title: "text", _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,
+                    }]
+            },
+            selection: { type: "text", anchor: 1, head: 1 },
+            storedMarks: []
+        };
+        const headerBtnHgt = 10;
+        const headerTemplate = (opts:DocumentOptions) => {
+            const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "text", 
                 layout:
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
                 `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'}  height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
@@ -309,28 +274,57 @@ export class CurrentUserUtils {
             // "    <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
             // "    <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" +
             // "</div>";
-            Doc.GetProto(headerTemplate).isTemplateDoc = makeTemplate(Doc.GetProto(headerTemplate), true, "headerView");
-            doc.emptyHeader = headerTemplate;
+            Doc.GetProto(header).isTemplateDoc = makeTemplate(Doc.GetProto(header), true, "headerView");
+            Doc.GetProto(header).title = "Untitled Header";
+            return header; 
         }
+        const emptyThings:{key:string,  // the field name where the empty thing will be stored
+                          opts:DocumentOptions,   // the document options that are required for the empty thing
+                          funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
+                          creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
+              }[] = [
+            {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _autoHeight: true }},
+            {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 150, _height: 100 }},
+            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, _backgroundGridShow: true, }},
+            {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }},
+            {key: "Comparison", creator: Docs.Create.ComparisonDocument,                    opts: { _width: 300, _height: 300 }},
+            {key: "Audio",      creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
+            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _showSidebar: true,  }},
+            {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
+            {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
+            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }},
+            {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
+            {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument(opts),         opts: { _width: 300, _height: 300 }},
+            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}},
+            {key: "Presentation",creator: Docs.Create.PresDocument,                         opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _backgroundGridShow: true, }},
+            {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, 
+                                                                                                treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
+                                                                                                allowOverlayDrop: true, treeViewType: TreeViewType.outline, 
+                                                                                                backgroundColor: "white", _xMargin: 0, _yMargin: 0, _singleLine: true 
+                                                                                            }, funcs: {title: 'self.text?.Text'}},
+            ];
+
+        emptyThings.forEach(thing => 
+            this.AssignScripts(this.AssignOpts(DocCast(doc["empty"+thing.key]), {...standardOps(thing.key), ...thing.opts}) ?? (doc["empty"+thing.key] = thing.creator({...standardOps(thing.key), ...thing.opts})), undefined, thing.funcs))
+        
         
         return [
-            { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc,        scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
-            { toolTip: "Tap or drag to create a collection",      title: "Col", icon: "folder",         dragFactory: doc.emptyCollection as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, clickFactory: doc.emptyPane as Doc, },
-            { toolTip: "Tap or drag to create an equation",       title: "Math", icon: "calculator",    dragFactory: doc.emptyMath as Doc,        scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
-            { toolTip: "Tap or drag to create a webpage",         title: "Web", icon: "globe-asia",     dragFactory: doc.emptyWebpage as Doc,     scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
-            { toolTip: "Tap or drag to create a comparison box",  title: "Compare", icon: "columns",    dragFactory: doc.emptyComparison as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
-            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc,       scripts: {onClick: 'openInOverlay(copyDragFactory(this.dragFactory))',onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
-            { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc,         scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
-            { toolTip: "Tap or drag to create a custom note",     title: "Custom", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,  scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'},  },
-            { toolTip: "Tap or drag to create a progressive slide",title: "Slide", icon: "file",        dragFactory: doc.emptySlide as Doc,       scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} },
-            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreenshot as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} },
-            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
-            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs:{ hidden: 'IsNoviceMode()'} },
-            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
-            { toolTip: "Tap or drag to create a mobile view",     title: "Phone", icon: "mobile",       dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: {hidden: 'IsNoviceMode()'} },
-            { toolTip: "Tap or drag to create a data viz node",   title: "DataViz", icon: "file",       dragFactory: doc.emptyDataViz as Doc,     scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: '{ return copyDragFactory(this.dragFactory);}'} },
+            { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc, },
+            { toolTip: "Tap or drag to create a collection",      title: "Col", icon: "folder",         dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab),  scripts: { onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create an equation",       title: "Math", icon: "calculator",    dragFactory: doc.emptyEquation as Doc, },
+            { toolTip: "Tap or drag to create a webpage",         title: "Web", icon: "globe-asia",     dragFactory: doc.emptyWebpage as Doc, },
+            { toolTip: "Tap or drag to create a comparison box",  title: "Compare", icon: "columns",    dragFactory: doc.emptyComparison as Doc, },
+            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc, },
+            { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, },
+            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreengrab as Doc,  funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a data viz node",   title: "DataViz", icon: "file",       dragFactory: doc.emptyDataViz as Doc, },
+            { toolTip: "Tap or drag to create a data note",       title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,  scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'},  },
             { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
-          ];
+          ].map(tuple => ({scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, ...tuple, }))
     }
 
     /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
@@ -345,9 +339,10 @@ export class CurrentUserUtils {
             return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
-        const reqdOpts = {
+        const reqdOpts:DocumentOptions = {
             title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
+            childDocumentsActive: true
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -357,34 +352,26 @@ export class CurrentUserUtils {
     static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] {
         const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
         return [
-            { title: "Dashboards",      target: CurrentUserUtils.MyDashboards,     icon: "desktop",     scripts:{onClick: 'selectMainMenu(self)'} },
-            { title: "Search",          target: CurrentUserUtils.MySearcher,       icon: "search",      scripts:{onClick: 'selectMainMenu(self)'} },
-            { title: "Files",           target: CurrentUserUtils.MyFilesystem,     icon: "folder-open", scripts:{onClick: 'selectMainMenu(self)'} },
-            { title: "Tools",           target: CurrentUserUtils.MyTools,          icon: "wrench",      scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} },
-            { title: "Imports",         target: CurrentUserUtils.MyImports,        icon: "upload",      scripts:{onClick: 'selectMainMenu(self)'} },
-            { title: "Recently Closed", target: CurrentUserUtils.MyRecentlyClosed, icon: "archive",     scripts:{onClick: 'selectMainMenu(self)'} },
-            { title: "Shared with me",  target: CurrentUserUtils.MySharedDocs,     icon: "users",       scripts:{onClick: 'selectMainMenu(self)'}, funcs:{badgeValue:badgeValue}},
-            { title: "Trails",          target: CurrentUserUtils.MyTrails,         icon: "pres-trail",  scripts:{onClick: 'selectMainMenu(self)'} },
-            { title: "User Doc",        target: CurrentUserUtils.MyUserDocView,    icon: "address-card",scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} },
-        ];
+            { title: "Dashboards",      target: this.setupDashboards(doc, "myDashboards"),         icon: "desktop",     },
+            { title: "Search",          target: this.setupSearcher(doc, "mySearcher"),             icon: "search",      },
+            { title: "Files",           target: this.setupFilesystem(doc, "myFilesystem"),         icon: "folder-open", },
+            { title: "Tools",           target: this.setupToolsBtnPanel(doc, "myTools"),           icon: "wrench",      funcs: {hidden: "IsNoviceMode()"} },
+            { title: "Imports",         target: this.setupImportSidebar(doc, "myImports"),         icon: "upload",      },
+            { title: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive",     },
+            { title: "Shared Docs",     target: this.MySharedDocs,                                 icon: "users",       funcs:{badgeValue:badgeValue}},
+            { title: "Trails",          target: this.setupTrails(doc, "myTrails"),                 icon: "pres-trail",  },
+            { title: "User Doc View",   target: this.setupUserDocView(doc, "myUserDocView"),       icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
+        ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}}));
     }
 
-    /// setup the left sidebar container and panels that can be displayed within it
+    /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
         this.AssignOpts(DocCast(doc[field]), {}) ?? (doc[field] = ((doc:Doc) => {doc.system = true; return doc;})(new Doc()));
-        CurrentUserUtils.setupSearcher(doc, "mySearcher");
-        CurrentUserUtils.setupToolsBtnPanel(doc, "myTools");
-        CurrentUserUtils.setupImportSidebar(doc, "myImports");
-        CurrentUserUtils.setupDashboards(doc, "myDashboards");
-        CurrentUserUtils.setupTrails(doc, "myTrails");
-        CurrentUserUtils.setupFilesystem(doc, "myFilesystem");
-        CurrentUserUtils.setupRecentlyClosedDocs(doc, "myRecentlyClosed");
-        CurrentUserUtils.setupUserDocView(doc, "myUserDocView");
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
     static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") {
-        this.setupLeftSidebarPanel(doc); // the tools/panels opened up by the menu buttons
+        this.setupLeftSidebarPanel(doc);
         const myLeftSidebarMenu = DocCast(doc[field]);
         const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
@@ -520,7 +507,7 @@ export class CurrentUserUtils {
             dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
             _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,
         };
-        this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts));
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts));
     }
 
     /// Initializes the panel of draggable tools that is opened from the left sidebar.
@@ -532,7 +519,7 @@ export class CurrentUserUtils {
             title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
             _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
         };
-        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps));
+        return this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps));
     }
 
     /// initializes the left sidebar dashboard pane
@@ -595,6 +582,7 @@ export class CurrentUserUtils {
         if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
             myTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
         }
+        return myTrails;
     }
 
     /// initializes the left sidebar File system pane
@@ -624,10 +612,11 @@ export class CurrentUserUtils {
         if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
             myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
         }
+        return myFilesystem;
     }
 
     /// initializes the panel displaying docs that have been recently closed
-    static setupRecentlyClosedDocs(doc: Doc, field:string) {
+    static setupRecentlyClosed(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
             title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true,
             treeViewTruncateTitleWidth: 150, ignoreClick: true,  boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
@@ -651,6 +640,7 @@ export class CurrentUserUtils {
         if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
             recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
         }
+        return recentlyClosed;
     }
 
     /// creates a new, empty filter doc
@@ -674,7 +664,7 @@ export class CurrentUserUtils {
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 150
         };
         if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" })
-        this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts));
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts));
     }
 
     static linearButtonList = (title: string, opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
@@ -903,7 +893,7 @@ export class CurrentUserUtils {
             buttonText: "Import", icon: "upload", system: true };
         const reqdBtnScripts = { onClick: "importDocument()" };
         const newImportBtn = this.AssignOpts(DocCast(myImports.buttonMenuDoc), reqdBtnOpts) ?? (myImports.buttonMenuDoc = Docs.Create.FontIconDocument(reqdBtnOpts));
-        this.AssignScripts(newImportBtn, reqdBtnScripts);
+        return this.AssignScripts(newImportBtn, reqdBtnScripts);
     }
 
     static setupClickEditorTemplates(doc: Doc) {
@@ -1055,7 +1045,6 @@ export class CurrentUserUtils {
         }
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
-            !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
             userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
         }
         const state = CurrentUserUtils._urlState;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 684c919bd..4e8c14039 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -229,6 +229,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         }
     }
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive));
     getDisplayDoc(doc: Doc, width: () => number) {
         const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
@@ -245,7 +246,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             styleProvider={this.styleProvider}
             docViewPath={this.props.docViewPath}
             fitWidth={this.props.childFitWidth}
-            isContentActive={emptyFunction}
+            isContentActive={this.isChildContentActive}
             onKey={this.onKeyDown}
             isDocumentActive={this.isContentActive}
             LayoutTemplate={this.props.childLayoutTemplate}
-- 
cgit v1.2.3-70-g09d2

