From 5b7d51d47a967e679d17560771efe71522ada13a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 19 Aug 2020 22:30:57 -0400
Subject: more cleanup of SearchBox.  made searchMatch be a tri-state for
 forward/backward/nomatch

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

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f23fa8eb6..0f6274663 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -469,7 +469,7 @@ class TreeView extends React.Component<TreeViewProps> {
         return <>
             <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
                 style={{
-                    fontWeight: this.doc.searchMatch ? "bold" : undefined,
+                    fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined,
                     pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
-- 
cgit v1.2.3-70-g09d2


From 36630b9aa2e1c4710a69a4fdf4ec98c3f5bca92c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 21 Aug 2020 16:05:52 -0400
Subject: trying scenes instead of workspaces

---
 src/client/util/CurrentUserUtils.ts                | 40 ++++++-------
 src/client/util/History.ts                         |  2 +-
 src/client/util/SettingsManager.tsx                |  2 +-
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/MainView.scss                     |  2 +-
 src/client/views/MainView.tsx                      | 50 ++++++++--------
 .../views/collections/CollectionDockingView.tsx    | 12 ++--
 .../views/collections/CollectionTreeView.tsx       |  8 +--
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 src/client/views/nodes/DocumentView.tsx            | 10 ++--
 .../views/nodes/formattedText/DashDocView.tsx      |  4 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |  4 +-
 src/mobile/MobileInterface.tsx                     | 70 +++++++++++-----------
 15 files changed, 105 insertions(+), 106 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 8dd7b033b..4b1c48bd3 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -36,7 +36,7 @@ export class CurrentUserUtils {
     @computed public static get UserDocument() { return Doc.UserDoc(); }
 
     @observable public static GuestTarget: Doc | undefined;
-    @observable public static GuestWorkspace: Doc | undefined;
+    @observable public static GuestScene: Doc | undefined;
     @observable public static GuestMobile: Doc | undefined;
 
     @observable public static propertiesWidth: number = 0;
@@ -511,7 +511,7 @@ export class CurrentUserUtils {
         this.setupSharingSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
         return [
             { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
-            { title: "Workspace", target: Cast(doc["sidebar-workspaces"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+            { title: "Scenes", target: Cast(doc["sidebar-scenes"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
             { title: "Archive", target: Cast(doc["sidebar-recentlyClosed"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
@@ -592,7 +592,7 @@ export class CurrentUserUtils {
     // SEts up mobile buttons for inside mobile menu
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
-            { title: "WORKSPACES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " },
+            { title: "SCENES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Scenes from your mobile, and navigate through all of your documents. " },
             { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
             { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
             { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
@@ -689,7 +689,7 @@ export class CurrentUserUtils {
     }
 
     static setupLibrary(userDoc: Doc) {
-        return CurrentUserUtils.setupWorkspaces(userDoc);
+        return CurrentUserUtils.setupScenes(userDoc);
     }
 
     // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker. 
@@ -724,28 +724,28 @@ export class CurrentUserUtils {
         }
     }
 
-    static async setupWorkspaces(doc: Doc) {
-        // setup workspaces library item
-        await doc.myWorkspaces;
-        if (doc.myWorkspaces === undefined) {
-            doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
+    static async setupScenes(doc: Doc) {
+        // setup scenes library item
+        await doc.myScenes;
+        if (doc.myScenes === undefined) {
+            doc.myScenes = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "SCENES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
             }));
         }
-        if (doc["sidebar-workspaces"] === undefined) {
-            const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
-            (doc.myWorkspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
-            (doc.myWorkspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
+        if (doc["sidebar-scenes"] === undefined) {
+            const newScene = ScriptField.MakeScript(`createNewScene()`);
+            (doc.myScenes as Doc).contextMenuScripts = new List<ScriptField>([newScene!]);
+            (doc.myScenes as Doc).contextMenuLabels = new List<string>(["Create New Scene"]);
 
-            const workspaces = doc.myWorkspaces as Doc;
+            const scenes = doc.myScenes as Doc;
 
-            doc["sidebar-workspaces"] = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], {
+            doc["sidebar-scenes"] = new PrefetchProxy(Docs.Create.TreeDocument([scenes], {
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             })) as any as Doc;
         }
-        return doc.myWorkspaces as any as Doc;
+        return doc.myScenes as any as Doc;
     }
 
     static setupCatalog(doc: Doc) {
@@ -821,7 +821,7 @@ export class CurrentUserUtils {
     static async setupSidebarButtons(doc: Doc) {
         CurrentUserUtils.setupSidebarContainer(doc);
         await CurrentUserUtils.setupToolsBtnPanel(doc);
-        CurrentUserUtils.setupWorkspaces(doc);
+        CurrentUserUtils.setupScenes(doc);
         CurrentUserUtils.setupCatalog(doc);
         CurrentUserUtils.setupRecentlyClosed(doc);
         CurrentUserUtils.setupUserDoc(doc);
@@ -1002,8 +1002,8 @@ export class CurrentUserUtils {
     }
 }
 
-Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); },
-    "creates a new workspace when called");
+Scripting.addGlobal(function createNewScene() { return MainView.Instance.createNewScene(); },
+    "creates a new scene when called");
 
 Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
     "returns all the links to the document or its annotations", "(doc: any)");
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 7b7d4b835..aed887055 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            MainView.Instance.openWorkspace(field, true);
+            MainView.Instance.openScene(field, true);
         }
     }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 5642c5a42..b2131c9b2 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -158,7 +158,7 @@ export default class SettingsManager extends React.Component<{}> {
                 <div className="settings-title">Settings</div>
                 <div className="settings-username">{Doc.CurrentUserEmail}</div>
                 <button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
-                    {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
+                    {CurrentUserUtils.GuestScene ? "Exit" : "Log Out"}
                 </button>
                 <div className="close-button" onClick={this.close}>
                     <FontAwesomeIcon icon={fa.faTimes} color="black" size={"lg"} />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0cc492ee9..fdce8bf71 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -596,7 +596,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
     render() {
-        const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;
+        const darkScheme = Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index a05a2b858..fb80bfc0d 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -313,7 +313,7 @@
 
 }
 
-.mainView-workspace {
+.mainView-scene {
     height: 200px;
     position: relative;
     display: flex;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6bbe09974..08ac69a38 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -77,10 +77,10 @@ export class MainView extends React.Component {
     @observable private _panelHeight: number = 0;
     @observable private _flyoutTranslate: boolean = false;
     @observable public flyoutWidth: number = 0;
-    private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); }
+    private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeScene, Doc, null)?.darkScheme); }
 
     @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
+    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeScene, Doc)) : CurrentUserUtils.GuestScene; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
     @computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; }
 
@@ -224,12 +224,12 @@ export class MainView extends React.Component {
     }
 
     initAuthenticationRouters = async () => {
-        // Load the user's active workspace, or create a new one if initial session after signup
+        // Load the user's active scene, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
             reaction(
                 () => CurrentUserUtils.GuestTarget,
-                target => target && this.createNewWorkspace(),
+                target => target && this.createNewScene(),
                 { fireImmediately: true }
             );
         } else {
@@ -242,21 +242,21 @@ export class MainView extends React.Component {
                     }),
                 );
             }
-            const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
+            const doc = this.userDoc && await Cast(this.userDoc.activeScene, Doc);
             if (doc) {
-                this.openWorkspace(doc);
+                this.openScene(doc);
             } else {
-                this.createNewWorkspace();
+                this.createNewScene();
             }
         }
     }
 
     @action
-    createNewWorkspace = async (id?: string) => {
+    createNewScene = async (id?: string) => {
         const myCatalog = Doc.UserDoc().myCatalog as Doc;
         const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
-        const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
-        const workspaceCount = DocListCast(workspaces.data).length + 1;
+        const scenes = Cast(this.userDoc.myScenes, Doc) as Doc;
+        const sceneCount = DocListCast(scenes.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -265,28 +265,28 @@ export class MainView extends React.Component {
             title: "Untitled Collection",
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+        const sceneDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Scene ${sceneCount}` }, id, "row");
         Doc.AddDocToList(myCatalog, "data", freeformDoc);
         Doc.AddDocToList(myCatalog, "data", presentation);
         Doc.UserDoc().activePresentation = presentation;
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`);
-        workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyWorkspace!]);
-        workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Workspace"]);
+        const copyScene = ScriptField.MakeScript(`copyScene()`);
+        sceneDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyScene!]);
+        sceneDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Scene"]);
 
-        Doc.AddDocToList(workspaces, "data", workspaceDoc);
+        Doc.AddDocToList(scenes, "data", sceneDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => this.openWorkspace(workspaceDoc), 0);
+        setTimeout(() => this.openScene(sceneDoc), 0);
     }
 
     @action
-    openWorkspace = (doc: Doc, fromHistory = false) => {
+    openScene = (doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
 
-        if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest workspace
+        if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest scene
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
-            this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
+            this.userDoc ? (this.userDoc.activeScene = doc) : (CurrentUserUtils.GuestScene = doc);
         }
         const state = this._urlState;
         if (state.sharing === true && !this.userDoc) {
@@ -438,7 +438,7 @@ export class MainView extends React.Component {
     flyoutWidthFunc = () => this.flyoutWidth;
     addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
         return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
-            doc.dockingConfig ? this.openWorkspace(doc) :
+            doc.dockingConfig ? this.openScene(doc) :
                 CollectionDockingView.AddRightSplit(doc, libraryPath);
     }
     sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
@@ -977,12 +977,12 @@ export class MainView extends React.Component {
 }
 Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
 Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
-Scripting.addGlobal(function copyWorkspace() {
-    const copiedWorkspace = Doc.MakeCopy(Cast(Doc.UserDoc().activeWorkspace, Doc, null), true);
-    const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc, null);
-    Doc.AddDocToList(workspaces, "data", copiedWorkspace);
+Scripting.addGlobal(function copyScene() {
+    const copiedScene = Doc.MakeCopy(Cast(Doc.UserDoc().activeScene, Doc, null), true);
+    const scenes = Cast(Doc.UserDoc().myScenes, Doc, null);
+    Doc.AddDocToList(scenes, "data", copiedScene);
     // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-    setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0);
+    setTimeout(() => MainView.Instance.openScene(copiedScene), 0);
 });
 Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); },
     "imports files from device directly into the import sidebar");
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 43da0d3cf..6338e69a4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -96,7 +96,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
         if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
-            return MainView.Instance.openWorkspace(docView.props.Document);
+            return MainView.Instance.openScene(docView.props.Document);
         }
         const document = Doc.MakeAlias(docView.props.Document);
         const newItemStackConfig = {
@@ -410,8 +410,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         // Because this is in a set timeout, if this component unmounts right after mounting,
                         // we will leak a GoldenLayout, because we try to destroy it before we ever create it
                         setTimeout(() => this.setupGoldenLayout(), 1);
-                        DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false);
-                        this.props.Document.workspaceBrush = true;
+                        DocListCast((Doc.UserDoc().myScenes as Doc).data).map(d => d.sceneBrush = false);
+                        this.props.Document.sceneBrush = true;
                     }
                     this._ignoreStateChange = "";
                 }, { fireImmediately: true });
@@ -421,7 +421,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     componentWillUnmount: () => void = () => {
         try {
-            this.props.Document.workspaceBrush = false;
+            this.props.Document.sceneBrush = false;
             this._goldenLayout.unbind('itemDropped', this.itemDropped);
             this._goldenLayout.unbind('tabCreated', this.tabCreated);
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
@@ -668,7 +668,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     render() {
         if (this.props.renderDepth > 0) {
-            return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>;
+            return <div style={{ width: "100%", height: "100%" }}>Nested scenes can't be rendered</div>;
         }
         return <div className="collectiondockingview-container" id="menuContainer"
             onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />;
@@ -834,7 +834,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
-            return MainView.Instance.openWorkspace(doc);
+            return MainView.Instance.openScene(doc);
         } else if (location === "onRight") {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
         } else if (location === "close") {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 0f6274663..6eead6e6d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -471,7 +471,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 style={{
                     fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined,
+                    outline: BoolCast(this.doc.sceneBrush) ? "dashed 1px #06123232" : undefined,
                     pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
                 }} >
                 {view}
@@ -725,9 +725,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myWorkspaces) {
-            ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
-            ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.doc), icon: "minus" });
+        if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myScenes) {
+            ContextMenu.Instance.addItem({ description: "Create Scene", event: () => MainView.Instance.createNewScene(), icon: "plus" });
+            ContextMenu.Instance.addItem({ description: "Delete Scene", event: () => this.remove(this.doc), icon: "minus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0aaceb7f4..c75179252 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -588,7 +588,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutString: this.childLayoutString,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+            `${Cast(Doc.UserDoc().activeScene, Doc, null)?.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 }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index b00074cc6..6a320dfde 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -359,7 +359,7 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: "div", color: Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f6bb375fc..e66e469eb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -641,7 +641,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-        // panning a workspace
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             const pt = myTouches[0];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5b512c617..8d9d1f729 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -564,8 +564,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     deleteClicked = (): void => {
-        if (Doc.UserDoc().activeWorkspace === this.props.Document) {
-            alert("Can't delete the active workspace");
+        if (Doc.UserDoc().activeScene === this.props.Document) {
+            alert("Can't delete the active scene");
         } else {
             const selected = SelectionManager.SelectedDocuments().slice();
             SelectionManager.DeselectAll();
@@ -601,7 +601,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
-        if (this.props.Document === Doc.UserDoc().activeWorkspace) {
+        if (this.props.Document === Doc.UserDoc().activeScene) {
             alert("linking to document tabs not yet supported.  Drop link on document content.");
             return;
         }
@@ -713,7 +713,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
 
         const cm = ContextMenu.Instance;
-        if (!cm || (e?.nativeEvent as any)?.SchemaHandled) return;
+        if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
         Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
@@ -1011,7 +1011,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
-        const highlightColors = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ?
+        const highlightColors = Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 145ee8c2e..0e965ba9a 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -175,7 +175,7 @@ export class DashDocView extends React.Component<IDashDocView> {
         const outerStyle = {
             position: "relative" as "relative",
             textIndent: "0",
-            border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+            border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray")),
             width: this.props.node.props.width,
             height: this.props.node.props.height,
             display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +202,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                 ({ dim, color }) => {
                     spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
                     spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
-                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray"));
                 }, { fireImmediately: true });
 
             if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 33a080fe4..78c76efcb 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -43,7 +43,7 @@ export class DashDocView {
         this._outer = document.createElement("span");
         this._outer.style.position = "relative";
         this._outer.style.textIndent = "0";
-        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray"));
         this._outer.style.width = node.attrs.width;
         this._outer.style.height = node.attrs.height;
         this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +126,7 @@ export class DashDocView {
             this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
                 this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
                 this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
-                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray"));
             }, { fireImmediately: true });
 
             const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c5e395d2f..fc8c45c25 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -58,7 +58,7 @@ export class MobileInterface extends React.Component {
     @observable private _menuListView: boolean = false; //to switch between menu view (list / icon)
     @observable private _ink: boolean = false; //toggle whether ink is being dispalyed
     @observable private _homeMenu: boolean = true; // to determine whether currently at home menu
-    @observable private _child: Doc | null = null; // currently selected document
+    @observable private scenes: Doc | null = null; // currently selected document
     @observable private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu)
     @observable private _homeDoc: Doc = this._mainDoc; // home menu as a document
     @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
@@ -136,19 +136,19 @@ export class MobileInterface extends React.Component {
     back = () => {
         const header = document.getElementById("header") as HTMLElement;
         const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document
-        // Case 1: Parent document is 'workspaces'
+        // Case 1: Parent document is 'scenes'
         if (doc === Cast(this._library, Doc) as Doc) {
-            this._child = null;
+            this.scenes = null;
             this._library.then(library => this.switchCurrentView(library));
             // Case 2: Parent document is the 'home' menu (root node)
         } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
             this._homeMenu = true;
             this._parents = [];
-            this._child = null;
+            this.scenes = null;
             this.switchCurrentView(this._homeDoc);
             // Case 3: Parent document is any document
         } else if (doc) {
-            this._child = doc;
+            this.scenes = doc;
             this.switchCurrentView(doc);
             this._homeMenu = false;
             header.textContent = String(doc.title);
@@ -164,7 +164,7 @@ export class MobileInterface extends React.Component {
         if (!this._homeMenu || this._sidebarActive) {
             this._homeMenu = true;
             this._parents = [];
-            this._child = null;
+            this.scenes = null;
             this.switchCurrentView(this._homeDoc);
         }
         if (this._sidebarActive) {
@@ -173,14 +173,14 @@ export class MobileInterface extends React.Component {
     }
 
     /**
-     * Return to primary Workspace in library (Workspaces Doc)
+     * Return to primary Scene in library (Scenes Doc)
      */
     @action
     returnMain = () => {
         this._parents = [this._homeDoc];
         this._library.then(library => this.switchCurrentView(library));
         this._homeMenu = false;
-        this._child = null;
+        this.scenes = null;
     }
 
     /**
@@ -194,7 +194,7 @@ export class MobileInterface extends React.Component {
     /**
      * DocumentView for graphic display of all documents
      */
-    @computed get displayWorkspaces() {
+    @computed get displayScenes() {
         return !this.mainContainer ? (null) :
             <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
                 <DocumentView
@@ -243,7 +243,7 @@ export class MobileInterface extends React.Component {
                 this._parents.push(this._activeDoc);
                 this.switchCurrentView(doc);
                 this._homeMenu = false;
-                this._child = doc;
+                this.scenes = doc;
             }
         });
     }
@@ -258,7 +258,7 @@ export class MobileInterface extends React.Component {
         this._parents.push(this._activeDoc);
         this.switchCurrentView(doc);
         this._homeMenu = false;
-        this._child = doc;
+        this.scenes = doc;
         this.toggleSidebar();
     }
 
@@ -290,13 +290,13 @@ export class MobileInterface extends React.Component {
     handlePathClick = async (doc: Doc, index: number) => {
         const library = await this._library;
         if (doc === library) {
-            this._child = null;
+            this.scenes = null;
             this.switchCurrentView(doc);
             this._parents.length = index;
         } else if (doc === this._homeDoc) {
             this.returnHome();
         } else {
-            this._child = doc;
+            this.scenes = doc;
             this.switchCurrentView(doc);
             this._parents.length = index;
         }
@@ -321,13 +321,13 @@ export class MobileInterface extends React.Component {
                 </div>
             );
         }
-        // stores workspace documents as 'workspaces' variable
-        let workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc) as Doc;
-        if (this._child) {
-            workspaces = this._child;
+        // stores scenes documents as 'scenes' variable
+        let scenes = Cast(Doc.UserDoc().myScenes, Doc) as Doc;
+        if (this.scenes) {
+            scenes = this.scenes;
         }
         // returns a list of navbar buttons as 'buttons'
-        const buttons = DocListCast(workspaces.data).map((doc: Doc, index: any) => {
+        const buttons = DocListCast(scenes.data).map((doc: Doc, index: any) => {
             if (doc.type !== "ink") {
                 return (
                     <div
@@ -357,7 +357,7 @@ export class MobileInterface extends React.Component {
                 {this.renderPathbar()}
                 <div className={`sidebar ${this._sidebarActive ? "active" : ""}`}>
                     <div className="sidebarButtons">
-                        {this._child ?
+                        {this.scenes ?
                             <>
                                 {buttons}
                                 <div
@@ -365,7 +365,7 @@ export class MobileInterface extends React.Component {
                                     onClick={this.returnMain}
                                     style={{ opacity: 0.7 }}>
                                     <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
-                                    <div className="item-type">Return to workspaces</div>
+                                    <div className="item-type">Return to scenes</div>
                                 </div>
                             </> :
                             <>
@@ -373,9 +373,9 @@ export class MobileInterface extends React.Component {
                                 <div
                                     className="item"
                                     style={{ opacity: 0.7 }}
-                                    onClick={() => this.createNewWorkspace()}>
+                                    onClick={() => this.createNewScene()}>
                                     <FontAwesomeIcon className="right" icon="plus" size="lg" />
-                                    <div className="item-type">Create New Workspace</div>
+                                    <div className="item-type">Create New Scene</div>
                                 </div>
                             </>
                         }
@@ -388,27 +388,27 @@ export class MobileInterface extends React.Component {
     }
 
     /**
-     * Handles the 'Create New Workspace' button in the menu (taken from MainView.tsx)
+     * Handles the 'Create New Scene' button in the menu (taken from MainView.tsx)
      */
     @action
-    createNewWorkspace = async (id?: string) => {
-        const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc) as Doc;
-        const workspaceCount = DocListCast(workspaces.data).length + 1;
+    createNewScene = async (id?: string) => {
+        const scens = Cast(Doc.UserDoc().myScenes, Doc) as Doc;
+        const sceneCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
-            title: "Collection " + workspaceCount,
+            title: "Collection " + sceneCount,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+        const sceneDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Scene ${sceneCount}` }, id, "row");
 
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const cloneWorkspace = ScriptField.MakeScript(`cloneWorkspace()`);
-        workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneWorkspace!]);
-        workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Workspace Layout"]);
+        const cloneScene = ScriptField.MakeScript(`cloneScene()`);
+        sceneDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneScene!]);
+        sceneDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Scene Layout"]);
 
-        Doc.AddDocToList(workspaces, "data", workspaceDoc);
+        Doc.AddDocToList(scens, "data", sceneDoc);
     }
 
     // Button for switching between pen and ink mode
@@ -612,7 +612,7 @@ export class MobileInterface extends React.Component {
     }
 
     // Radial menu can only be used if it is a colleciton and it is not a homeDoc 
-    // (and cannot be used on Workspace to avoid pin to presentation opening on right)
+    // (and cannot be used on Scene to avoid pin to presentation opening on right)
     @computed get displayRadialMenu() {
         return this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
             this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : (null);
@@ -657,7 +657,7 @@ export class MobileInterface extends React.Component {
                         {this.drawInk}
                         {this.uploadImageButton}
                     </div>
-                    {this.displayWorkspaces}
+                    {this.displayScenes}
                     {this.renderDefaultContent}
                 </GestureOverlay>
                 {this.displayRadialMenu}
@@ -669,7 +669,7 @@ export class MobileInterface extends React.Component {
 
 //Global functions for mobile menu
 Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
-    "opens the library to navigate through workspaces on Dash Mobile");
+    "opens the library to navigate through scenes on Dash Mobile");
 Scripting.addGlobal(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
     "opens the upload files menu for Dash Mobile");
 Scripting.addGlobal(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
-- 
cgit v1.2.3-70-g09d2


From ca9cf672bdfd8ebf11cb20264474bcb9d9729944 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 21 Aug 2020 18:46:58 -0400
Subject: switched from "scenes" to "dashboards"

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 40 ++++++-------
 src/client/util/History.ts                         |  2 +-
 src/client/util/SettingsManager.tsx                |  2 +-
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/MainView.scss                     |  2 +-
 src/client/views/MainView.tsx                      | 50 ++++++++--------
 .../views/collections/CollectionDockingView.tsx    | 35 ++++++-----
 .../views/collections/CollectionTreeView.tsx       |  8 +--
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  8 +--
 .../views/nodes/formattedText/DashDocView.tsx      |  4 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |  4 +-
 src/mobile/MobileInterface.tsx                     | 70 +++++++++++-----------
 15 files changed, 120 insertions(+), 115 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7d114d417..697ff858f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -820,7 +820,9 @@ export namespace Docs {
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
-            Doc.GetProto(inst).data = new List<Doc>(documents);
+            const tabs = TreeDocument(documents, { title: "Active Tabs" });
+            const all = TreeDocument([], { title: "Other Tabs" });
+            Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
             return inst;
         }
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4b1c48bd3..b439b8b43 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -36,7 +36,7 @@ export class CurrentUserUtils {
     @computed public static get UserDocument() { return Doc.UserDoc(); }
 
     @observable public static GuestTarget: Doc | undefined;
-    @observable public static GuestScene: Doc | undefined;
+    @observable public static GuestDashboard: Doc | undefined;
     @observable public static GuestMobile: Doc | undefined;
 
     @observable public static propertiesWidth: number = 0;
@@ -510,11 +510,11 @@ export class CurrentUserUtils {
     }[] {
         this.setupSharingSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
         return [
-            { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
-            { title: "Scenes", target: Cast(doc["sidebar-scenes"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+            { title: "Dashboards", target: Cast(doc["sidebar-dashboards"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
             { title: "Archive", target: Cast(doc["sidebar-recentlyClosed"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+            { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
             { title: "Tools", target: Cast(doc["sidebar-tools"], Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
             { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
             { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
@@ -592,7 +592,7 @@ export class CurrentUserUtils {
     // SEts up mobile buttons for inside mobile menu
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
-            { title: "SCENES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Scenes from your mobile, and navigate through all of your documents. " },
+            { title: "SCENES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
             { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
             { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
             { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
@@ -689,7 +689,7 @@ export class CurrentUserUtils {
     }
 
     static setupLibrary(userDoc: Doc) {
-        return CurrentUserUtils.setupScenes(userDoc);
+        return CurrentUserUtils.setupDashboards(userDoc);
     }
 
     // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker. 
@@ -724,28 +724,28 @@ export class CurrentUserUtils {
         }
     }
 
-    static async setupScenes(doc: Doc) {
-        // setup scenes library item
-        await doc.myScenes;
-        if (doc.myScenes === undefined) {
-            doc.myScenes = new PrefetchProxy(Docs.Create.TreeDocument([], {
+    static async setupDashboards(doc: Doc) {
+        // setup dashboards library item
+        await doc.myDashboards;
+        if (doc.myDashboards === undefined) {
+            doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "SCENES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
             }));
         }
-        if (doc["sidebar-scenes"] === undefined) {
-            const newScene = ScriptField.MakeScript(`createNewScene()`);
-            (doc.myScenes as Doc).contextMenuScripts = new List<ScriptField>([newScene!]);
-            (doc.myScenes as Doc).contextMenuLabels = new List<string>(["Create New Scene"]);
+        if (doc["sidebar-dashboards"] === undefined) {
+            const newDashboard = ScriptField.MakeScript(`createNewDashboard()`);
+            (doc.myDashboards as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
+            (doc.myDashboards as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
 
-            const scenes = doc.myScenes as Doc;
+            const dashboards = doc.myDashboards as Doc;
 
-            doc["sidebar-scenes"] = new PrefetchProxy(Docs.Create.TreeDocument([scenes], {
+            doc["sidebar-dashboards"] = new PrefetchProxy(Docs.Create.TreeDocument([dashboards], {
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             })) as any as Doc;
         }
-        return doc.myScenes as any as Doc;
+        return doc.myDashboards as any as Doc;
     }
 
     static setupCatalog(doc: Doc) {
@@ -821,7 +821,7 @@ export class CurrentUserUtils {
     static async setupSidebarButtons(doc: Doc) {
         CurrentUserUtils.setupSidebarContainer(doc);
         await CurrentUserUtils.setupToolsBtnPanel(doc);
-        CurrentUserUtils.setupScenes(doc);
+        CurrentUserUtils.setupDashboards(doc);
         CurrentUserUtils.setupCatalog(doc);
         CurrentUserUtils.setupRecentlyClosed(doc);
         CurrentUserUtils.setupUserDoc(doc);
@@ -1002,8 +1002,8 @@ export class CurrentUserUtils {
     }
 }
 
-Scripting.addGlobal(function createNewScene() { return MainView.Instance.createNewScene(); },
-    "creates a new scene when called");
+Scripting.addGlobal(function createNewDashboard() { return MainView.Instance.createNewDashboard(); },
+    "creates a new dashboard when called");
 
 Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
     "returns all the links to the document or its annotations", "(doc: any)");
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index aed887055..cab682ac7 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            MainView.Instance.openScene(field, true);
+            MainView.Instance.openDashboard(field, true);
         }
     }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index b2131c9b2..accb9c346 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -158,7 +158,7 @@ export default class SettingsManager extends React.Component<{}> {
                 <div className="settings-title">Settings</div>
                 <div className="settings-username">{Doc.CurrentUserEmail}</div>
                 <button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
-                    {CurrentUserUtils.GuestScene ? "Exit" : "Log Out"}
+                    {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
                 </button>
                 <div className="close-button" onClick={this.close}>
                     <FontAwesomeIcon icon={fa.faTimes} color="black" size={"lg"} />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fdce8bf71..c44c2968c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -596,7 +596,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
     render() {
-        const darkScheme = Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ? "dimgray" : undefined;
+        const darkScheme = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index fb80bfc0d..2d742f4ba 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -313,7 +313,7 @@
 
 }
 
-.mainView-scene {
+.mainView-dashboard {
     height: 200px;
     position: relative;
     display: flex;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 08ac69a38..64efc5ee5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -77,10 +77,10 @@ export class MainView extends React.Component {
     @observable private _panelHeight: number = 0;
     @observable private _flyoutTranslate: boolean = false;
     @observable public flyoutWidth: number = 0;
-    private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeScene, Doc, null)?.darkScheme); }
+    private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeDashboard, Doc, null)?.darkScheme); }
 
     @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeScene, Doc)) : CurrentUserUtils.GuestScene; }
+    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
     @computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; }
 
@@ -224,12 +224,12 @@ export class MainView extends React.Component {
     }
 
     initAuthenticationRouters = async () => {
-        // Load the user's active scene, or create a new one if initial session after signup
+        // Load the user's active dashboard, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
             reaction(
                 () => CurrentUserUtils.GuestTarget,
-                target => target && this.createNewScene(),
+                target => target && this.createNewDashboard(),
                 { fireImmediately: true }
             );
         } else {
@@ -242,21 +242,21 @@ export class MainView extends React.Component {
                     }),
                 );
             }
-            const doc = this.userDoc && await Cast(this.userDoc.activeScene, Doc);
+            const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc);
             if (doc) {
-                this.openScene(doc);
+                this.openDashboard(doc);
             } else {
-                this.createNewScene();
+                this.createNewDashboard();
             }
         }
     }
 
     @action
-    createNewScene = async (id?: string) => {
+    createNewDashboard = async (id?: string) => {
         const myCatalog = Doc.UserDoc().myCatalog as Doc;
         const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
-        const scenes = Cast(this.userDoc.myScenes, Doc) as Doc;
-        const sceneCount = DocListCast(scenes.data).length + 1;
+        const dashboards = Cast(this.userDoc.myDashboards, Doc) as Doc;
+        const dashboardCount = DocListCast(dashboards.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -265,28 +265,28 @@ export class MainView extends React.Component {
             title: "Untitled Collection",
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const sceneDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Scene ${sceneCount}` }, id, "row");
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
         Doc.AddDocToList(myCatalog, "data", freeformDoc);
         Doc.AddDocToList(myCatalog, "data", presentation);
         Doc.UserDoc().activePresentation = presentation;
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const copyScene = ScriptField.MakeScript(`copyScene()`);
-        sceneDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyScene!]);
-        sceneDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Scene"]);
+        const copyDashboard = ScriptField.MakeScript(`copyDashboard()`);
+        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyDashboard!]);
+        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard"]);
 
-        Doc.AddDocToList(scenes, "data", sceneDoc);
+        Doc.AddDocToList(dashboards, "data", dashboardDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => this.openScene(sceneDoc), 0);
+        setTimeout(() => this.openDashboard(dashboardDoc), 0);
     }
 
     @action
-    openScene = (doc: Doc, fromHistory = false) => {
+    openDashboard = (doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
 
-        if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest scene
+        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" })]));
-            this.userDoc ? (this.userDoc.activeScene = doc) : (CurrentUserUtils.GuestScene = doc);
+            this.userDoc ? (this.userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
         }
         const state = this._urlState;
         if (state.sharing === true && !this.userDoc) {
@@ -438,7 +438,7 @@ export class MainView extends React.Component {
     flyoutWidthFunc = () => this.flyoutWidth;
     addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
         return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
-            doc.dockingConfig ? this.openScene(doc) :
+            doc.dockingConfig ? this.openDashboard(doc) :
                 CollectionDockingView.AddRightSplit(doc, libraryPath);
     }
     sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
@@ -977,12 +977,12 @@ export class MainView extends React.Component {
 }
 Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
 Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
-Scripting.addGlobal(function copyScene() {
-    const copiedScene = Doc.MakeCopy(Cast(Doc.UserDoc().activeScene, Doc, null), true);
-    const scenes = Cast(Doc.UserDoc().myScenes, Doc, null);
-    Doc.AddDocToList(scenes, "data", copiedScene);
+Scripting.addGlobal(function copyDashboard() {
+    const copiedDashboard = Doc.MakeCopy(Cast(Doc.UserDoc().activeDashboard, Doc, null), true);
+    const dashboards = Cast(Doc.UserDoc().myDashboards, Doc, null);
+    Doc.AddDocToList(dashboards, "data", copiedDashboard);
     // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-    setTimeout(() => MainView.Instance.openScene(copiedScene), 0);
+    setTimeout(() => MainView.Instance.openDashboard(copiedDashboard), 0);
 });
 Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); },
     "imports files from device directly into the import sidebar");
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6338e69a4..2f84065ec 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -31,9 +31,10 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { listSpec } from '../../../fields/Schema';
 import { clamp } from 'lodash';
 import { PresBox } from '../nodes/PresBox';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { lstat } from 'fs';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -79,9 +80,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         } else {
             config = {
                 type: 'row',
-                content: dragDocs.map((doc, i) => {
-                    CollectionDockingView.makeDocumentConfig(doc);
-                })
+                content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc))
             };
         }
         const div = document.createElement("div");
@@ -96,7 +95,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
         if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
-            return MainView.Instance.openScene(docView.props.Document);
+            return MainView.Instance.openDashboard(docView.props.Document);
         }
         const document = Doc.MakeAlias(docView.props.Document);
         const newItemStackConfig = {
@@ -154,7 +153,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
         this._goldenLayout.emit('stateChanged');
         this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
-        if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
         this.stateChanged();
     }
     @undoBatch
@@ -410,8 +408,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         // Because this is in a set timeout, if this component unmounts right after mounting,
                         // we will leak a GoldenLayout, because we try to destroy it before we ever create it
                         setTimeout(() => this.setupGoldenLayout(), 1);
-                        DocListCast((Doc.UserDoc().myScenes as Doc).data).map(d => d.sceneBrush = false);
-                        this.props.Document.sceneBrush = true;
+                        DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false);
+                        this.props.Document.dashboardBrush = true;
                     }
                     this._ignoreStateChange = "";
                 }, { fireImmediately: true });
@@ -421,7 +419,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     componentWillUnmount: () => void = () => {
         try {
-            this.props.Document.sceneBrush = false;
+            this.props.Document.dashboardBrush = false;
             this._goldenLayout.unbind('itemDropped', this.itemDropped);
             this._goldenLayout.unbind('tabCreated', this.tabCreated);
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
@@ -481,8 +479,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
         if (docids) {
             const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
-            docs.map(doc => Doc.AddDocToList(Doc.GetProto(this.props.Document), this.props.fieldKey, doc));
-            // Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
+            const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
+            const tabs = Cast(sublists[0], Doc, null);
+            const other = Cast(sublists[1], Doc, null);
+            const tabdocs = DocListCast(tabs.data);
+            const otherdocs = DocListCast(other.data);
+            Doc.GetProto(tabs).data = new List<Doc>(docs);
+            const otherSet = new Set<Doc>();
+            otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+            tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+            Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
         }
     }
 
@@ -586,7 +592,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
                 if (doc instanceof Doc) {
                     const theDoc = doc;
-                    CollectionDockingView.Instance._removedDocs.push(theDoc);
 
                     const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc);
                     if (recent) {
@@ -607,7 +612,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             }
         }
     }
-    _removedDocs: Doc[] = [];
 
     stackCreated = (stack: any) => {
         //stack.header.controlsContainer.find('.lm_popout').hide();
@@ -652,7 +656,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                         }
                         const theDoc = doc;
-                        CollectionDockingView.Instance._removedDocs.push(theDoc);
                     }
                 });
                 //}
@@ -668,7 +671,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     render() {
         if (this.props.renderDepth > 0) {
-            return <div style={{ width: "100%", height: "100%" }}>Nested scenes can't be rendered</div>;
+            return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>;
         }
         return <div className="collectiondockingview-container" id="menuContainer"
             onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />;
@@ -834,7 +837,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
-            return MainView.Instance.openScene(doc);
+            return MainView.Instance.openDashboard(doc);
         } else if (location === "onRight") {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
         } else if (location === "close") {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6eead6e6d..b78678bc3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -471,7 +471,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 style={{
                     fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: BoolCast(this.doc.sceneBrush) ? "dashed 1px #06123232" : undefined,
+                    outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
                     pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
                 }} >
                 {view}
@@ -725,9 +725,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myScenes) {
-            ContextMenu.Instance.addItem({ description: "Create Scene", event: () => MainView.Instance.createNewScene(), icon: "plus" });
-            ContextMenu.Instance.addItem({ description: "Delete Scene", event: () => this.remove(this.doc), icon: "minus" });
+        if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) {
+            ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => MainView.Instance.createNewDashboard(), icon: "plus" });
+            ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c75179252..12d2152b1 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -588,7 +588,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutString: this.childLayoutString,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            `${Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+            `${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.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 }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 6a320dfde..646ae94fb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -359,7 +359,7 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8d9d1f729..91f12f004 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -564,8 +564,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     deleteClicked = (): void => {
-        if (Doc.UserDoc().activeScene === this.props.Document) {
-            alert("Can't delete the active scene");
+        if (Doc.UserDoc().activeDashboard === this.props.Document) {
+            alert("Can't delete the active dashboard");
         } else {
             const selected = SelectionManager.SelectedDocuments().slice();
             SelectionManager.DeselectAll();
@@ -601,7 +601,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
-        if (this.props.Document === Doc.UserDoc().activeScene) {
+        if (this.props.Document === Doc.UserDoc().activeDashboard) {
             alert("linking to document tabs not yet supported.  Drop link on document content.");
             return;
         }
@@ -1011,7 +1011,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
-        const highlightColors = Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ?
+        const highlightColors = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 0e965ba9a..31dd92b59 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -175,7 +175,7 @@ export class DashDocView extends React.Component<IDashDocView> {
         const outerStyle = {
             position: "relative" as "relative",
             textIndent: "0",
-            border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+            border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")),
             width: this.props.node.props.width,
             height: this.props.node.props.height,
             display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +202,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                 ({ dim, color }) => {
                     spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
                     spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
-                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
                 }, { fireImmediately: true });
 
             if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 78c76efcb..a85dacc7e 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -43,7 +43,7 @@ export class DashDocView {
         this._outer = document.createElement("span");
         this._outer.style.position = "relative";
         this._outer.style.textIndent = "0";
-        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
         this._outer.style.width = node.attrs.width;
         this._outer.style.height = node.attrs.height;
         this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +126,7 @@ export class DashDocView {
             this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
                 this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
                 this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
-                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeScene, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
             }, { fireImmediately: true });
 
             const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index fc8c45c25..41b997285 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -58,7 +58,7 @@ export class MobileInterface extends React.Component {
     @observable private _menuListView: boolean = false; //to switch between menu view (list / icon)
     @observable private _ink: boolean = false; //toggle whether ink is being dispalyed
     @observable private _homeMenu: boolean = true; // to determine whether currently at home menu
-    @observable private scenes: Doc | null = null; // currently selected document
+    @observable private dashboards: Doc | null = null; // currently selected document
     @observable private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu)
     @observable private _homeDoc: Doc = this._mainDoc; // home menu as a document
     @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
@@ -136,19 +136,19 @@ export class MobileInterface extends React.Component {
     back = () => {
         const header = document.getElementById("header") as HTMLElement;
         const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document
-        // Case 1: Parent document is 'scenes'
+        // Case 1: Parent document is 'dashboards'
         if (doc === Cast(this._library, Doc) as Doc) {
-            this.scenes = null;
+            this.dashboards = null;
             this._library.then(library => this.switchCurrentView(library));
             // Case 2: Parent document is the 'home' menu (root node)
         } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
             this._homeMenu = true;
             this._parents = [];
-            this.scenes = null;
+            this.dashboards = null;
             this.switchCurrentView(this._homeDoc);
             // Case 3: Parent document is any document
         } else if (doc) {
-            this.scenes = doc;
+            this.dashboards = doc;
             this.switchCurrentView(doc);
             this._homeMenu = false;
             header.textContent = String(doc.title);
@@ -164,7 +164,7 @@ export class MobileInterface extends React.Component {
         if (!this._homeMenu || this._sidebarActive) {
             this._homeMenu = true;
             this._parents = [];
-            this.scenes = null;
+            this.dashboards = null;
             this.switchCurrentView(this._homeDoc);
         }
         if (this._sidebarActive) {
@@ -173,14 +173,14 @@ export class MobileInterface extends React.Component {
     }
 
     /**
-     * Return to primary Scene in library (Scenes Doc)
+     * Return to primary Dashboard in library (Dashboards Doc)
      */
     @action
     returnMain = () => {
         this._parents = [this._homeDoc];
         this._library.then(library => this.switchCurrentView(library));
         this._homeMenu = false;
-        this.scenes = null;
+        this.dashboards = null;
     }
 
     /**
@@ -194,7 +194,7 @@ export class MobileInterface extends React.Component {
     /**
      * DocumentView for graphic display of all documents
      */
-    @computed get displayScenes() {
+    @computed get displayDashboards() {
         return !this.mainContainer ? (null) :
             <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
                 <DocumentView
@@ -243,7 +243,7 @@ export class MobileInterface extends React.Component {
                 this._parents.push(this._activeDoc);
                 this.switchCurrentView(doc);
                 this._homeMenu = false;
-                this.scenes = doc;
+                this.dashboards = doc;
             }
         });
     }
@@ -258,7 +258,7 @@ export class MobileInterface extends React.Component {
         this._parents.push(this._activeDoc);
         this.switchCurrentView(doc);
         this._homeMenu = false;
-        this.scenes = doc;
+        this.dashboards = doc;
         this.toggleSidebar();
     }
 
@@ -290,13 +290,13 @@ export class MobileInterface extends React.Component {
     handlePathClick = async (doc: Doc, index: number) => {
         const library = await this._library;
         if (doc === library) {
-            this.scenes = null;
+            this.dashboards = null;
             this.switchCurrentView(doc);
             this._parents.length = index;
         } else if (doc === this._homeDoc) {
             this.returnHome();
         } else {
-            this.scenes = doc;
+            this.dashboards = doc;
             this.switchCurrentView(doc);
             this._parents.length = index;
         }
@@ -321,13 +321,13 @@ export class MobileInterface extends React.Component {
                 </div>
             );
         }
-        // stores scenes documents as 'scenes' variable
-        let scenes = Cast(Doc.UserDoc().myScenes, Doc) as Doc;
-        if (this.scenes) {
-            scenes = this.scenes;
+        // stores dashboards documents as 'dashboards' variable
+        let dashboards = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+        if (this.dashboards) {
+            dashboards = this.dashboards;
         }
         // returns a list of navbar buttons as 'buttons'
-        const buttons = DocListCast(scenes.data).map((doc: Doc, index: any) => {
+        const buttons = DocListCast(dashboards.data).map((doc: Doc, index: any) => {
             if (doc.type !== "ink") {
                 return (
                     <div
@@ -357,7 +357,7 @@ export class MobileInterface extends React.Component {
                 {this.renderPathbar()}
                 <div className={`sidebar ${this._sidebarActive ? "active" : ""}`}>
                     <div className="sidebarButtons">
-                        {this.scenes ?
+                        {this.dashboards ?
                             <>
                                 {buttons}
                                 <div
@@ -365,7 +365,7 @@ export class MobileInterface extends React.Component {
                                     onClick={this.returnMain}
                                     style={{ opacity: 0.7 }}>
                                     <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
-                                    <div className="item-type">Return to scenes</div>
+                                    <div className="item-type">Return to dashboards</div>
                                 </div>
                             </> :
                             <>
@@ -373,9 +373,9 @@ export class MobileInterface extends React.Component {
                                 <div
                                     className="item"
                                     style={{ opacity: 0.7 }}
-                                    onClick={() => this.createNewScene()}>
+                                    onClick={() => this.createNewDashboard()}>
                                     <FontAwesomeIcon className="right" icon="plus" size="lg" />
-                                    <div className="item-type">Create New Scene</div>
+                                    <div className="item-type">Create New Dashboard</div>
                                 </div>
                             </>
                         }
@@ -388,27 +388,27 @@ export class MobileInterface extends React.Component {
     }
 
     /**
-     * Handles the 'Create New Scene' button in the menu (taken from MainView.tsx)
+     * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx)
      */
     @action
-    createNewScene = async (id?: string) => {
-        const scens = Cast(Doc.UserDoc().myScenes, Doc) as Doc;
-        const sceneCount = DocListCast(scens.data).length + 1;
+    createNewDashboard = async (id?: string) => {
+        const scens = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+        const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
-            title: "Collection " + sceneCount,
+            title: "Collection " + dashboardCount,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const sceneDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Scene ${sceneCount}` }, id, "row");
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
 
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const cloneScene = ScriptField.MakeScript(`cloneScene()`);
-        sceneDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneScene!]);
-        sceneDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Scene Layout"]);
+        const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
+        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneDashboard!]);
+        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Dashboard Layout"]);
 
-        Doc.AddDocToList(scens, "data", sceneDoc);
+        Doc.AddDocToList(scens, "data", dashboardDoc);
     }
 
     // Button for switching between pen and ink mode
@@ -612,7 +612,7 @@ export class MobileInterface extends React.Component {
     }
 
     // Radial menu can only be used if it is a colleciton and it is not a homeDoc 
-    // (and cannot be used on Scene to avoid pin to presentation opening on right)
+    // (and cannot be used on Dashboard to avoid pin to presentation opening on right)
     @computed get displayRadialMenu() {
         return this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
             this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : (null);
@@ -657,7 +657,7 @@ export class MobileInterface extends React.Component {
                         {this.drawInk}
                         {this.uploadImageButton}
                     </div>
-                    {this.displayScenes}
+                    {this.displayDashboards}
                     {this.renderDefaultContent}
                 </GestureOverlay>
                 {this.displayRadialMenu}
@@ -669,7 +669,7 @@ export class MobileInterface extends React.Component {
 
 //Global functions for mobile menu
 Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
-    "opens the library to navigate through scenes on Dash Mobile");
+    "opens the library to navigate through dashboards on Dash Mobile");
 Scripting.addGlobal(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
     "opens the upload files menu for Dash Mobile");
 Scripting.addGlobal(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
-- 
cgit v1.2.3-70-g09d2


From c57a215fcca0928522f7d765fc2d8d8120b7a33e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 21 Aug 2020 19:55:31 -0400
Subject: added a locking option for treeViewExpandedView field.

---
 src/client/documents/Documents.ts                   |  8 +++++---
 src/client/util/CurrentUserUtils.ts                 |  8 +++++---
 src/client/views/collections/CollectionTreeView.tsx | 17 ++++++++++-------
 src/fields/documentSchemas.ts                       |  2 ++
 4 files changed, 22 insertions(+), 13 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 697ff858f..f0cae8f0e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -192,6 +192,8 @@ 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;
+    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
     // [key: string]: Opt<Field>;
     pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
@@ -819,9 +821,9 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
-            const tabs = TreeDocument(documents, { title: "Active Tabs" });
-            const all = TreeDocument([], { title: "Other Tabs" });
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            const tabs = TreeDocument(documents, { title: "Active Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
+            const all = TreeDocument([], { title: "Other Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
             Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
             return inst;
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b439b8b43..fbcc562da 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -592,7 +592,7 @@ export class CurrentUserUtils {
     // SEts up mobile buttons for inside mobile menu
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
-            { title: "SCENES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
+            { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
             { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
             { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
             { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
@@ -729,7 +729,8 @@ export class CurrentUserUtils {
         await doc.myDashboards;
         if (doc.myDashboards === undefined) {
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "SCENES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
+                title: "DASHBOARDS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true,
+                treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
             }));
         }
         if (doc["sidebar-dashboards"] === undefined) {
@@ -773,7 +774,8 @@ export class CurrentUserUtils {
         doc.myRecentlyClosed === undefined;
         if (doc.myRecentlyClosed === undefined) {
             doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true
+                title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true,
+                treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
             }));
         }
         // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b78678bc3..cde86b127 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -88,14 +88,16 @@ class TreeView extends React.Component<TreeViewProps> {
     get doc() { return this.props.document; }
     get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
     get displayName() { return "TreeView(" + this.doc.title + ")"; }  // this makes mobx trace() statements more descriptive
-    get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+    get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
+    get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+    get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs.length ? this.fieldKey : this.defaultExpandedView); }
     @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
     set treeViewOpen(c: boolean) {
         if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
         else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
     }
     @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
-    @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); }
+    @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
     @computed get dataDoc() { return this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
@@ -422,11 +424,12 @@ class TreeView extends React.Component<TreeViewProps> {
                 <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
                     onPointerDown={action(() => {
                         if (this.treeViewOpen) {
-                            this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
-                                this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
-                                    this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
-                                        (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
-                                            this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+                            this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
+                                this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
+                                    this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
+                                        this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
+                                            (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
+                                                this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
                         }
                         this.treeViewOpen = true;
                     })}>
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 848a648e1..4b75b1a32 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -83,6 +83,8 @@ export const documentSchema = createSchema({
     textTransform: "string",
     treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
     treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
+    treeViewLockExpandedView: "boolean", // whether the expanded view can be changed
+    treeViewDefaultExpandedView: "string", // name of field whose contents are displayed by default
     treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
 
     // interaction and linking properties
-- 
cgit v1.2.3-70-g09d2


From 4e715d6261487602a32e7182d2bc89817bf76527 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 21 Aug 2020 22:29:25 -0400
Subject: fixed stacking view to use autoHeight to avoid having scroll bars. 
 changed some names (inactive instead of recentlyClosed). switched to untitled
 Tabs from Untitled Collection

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                | 32 +++++++++++-----------
 src/client/views/DocComponent.tsx                  |  2 +-
 src/client/views/MainView.scss                     |  2 +-
 src/client/views/MainView.tsx                      |  4 +--
 .../views/collections/CollectionDockingView.tsx    |  6 ++--
 .../views/collections/CollectionStackingView.tsx   |  4 +--
 .../views/collections/CollectionTreeView.tsx       |  4 +--
 src/client/views/collections/CollectionView.tsx    |  2 +-
 9 files changed, 29 insertions(+), 29 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f0cae8f0e..4544e0e3e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -823,7 +823,7 @@ export namespace Docs {
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
             const tabs = TreeDocument(documents, { title: "Active Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
-            const all = TreeDocument([], { title: "Other Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
+            const all = TreeDocument([], { title: "Inactive Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
             Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
             return inst;
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index fbcc562da..b935e28d4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -401,7 +401,7 @@ export class CurrentUserUtils {
                 { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.emptyPane === undefined) {
-            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Collection", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Tab", 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"]) });
@@ -512,7 +512,7 @@ export class CurrentUserUtils {
         return [
             { title: "Dashboards", target: Cast(doc["sidebar-dashboards"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
-            { title: "Archive", target: Cast(doc["sidebar-recentlyClosed"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+            { title: "Inactive", target: Cast(doc["sidebar-inactiveDocs"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
             { title: "Tools", target: Cast(doc["sidebar-tools"], Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
@@ -769,26 +769,26 @@ export class CurrentUserUtils {
             })) as any as Doc;
         }
     }
-    static setupRecentlyClosed(doc: Doc) {
+    static setupInactiveDocs(doc: Doc) {
         // setup Recently Closed library item
-        doc.myRecentlyClosed === undefined;
-        if (doc.myRecentlyClosed === undefined) {
-            doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true,
+        doc.myInactiveDocs === undefined;
+        if (doc.myInactiveDocs === undefined) {
+            doc.myInactiveDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "CLOSED DOCS", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true,
                 treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
             }));
         }
-        // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
-        PromiseValue(Cast(doc.myRecentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
-        if (doc["sidebar-recentlyClosed"] === undefined) {
+        // this is equivalent to using PrefetchProxies to make sure the inactiveDocs doc is ready
+        PromiseValue(Cast(doc.myInactiveDocs, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
+        if (doc["sidebar-inactiveDocs"] === undefined) {
             const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
-            (doc.myRecentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.myRecentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+            (doc.myInactiveDocs as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+            (doc.myInactiveDocs as Doc).contextMenuLabels = new List<string>(["Clear All"]);
 
-            const recentlyClosed = doc.myRecentlyClosed as Doc;
+            const inactiveDocs = doc.myInactiveDocs as Doc;
 
-            doc["sidebar-recentlyClosed"] = new PrefetchProxy(Docs.Create.TreeDocument([recentlyClosed], {
-                title: "sidebar-recentlyClosed",
+            doc["sidebar-inactiveDocs"] = new PrefetchProxy(Docs.Create.TreeDocument([inactiveDocs], {
+                title: "sidebar-inactiveDocs",
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
@@ -825,7 +825,7 @@ export class CurrentUserUtils {
         await CurrentUserUtils.setupToolsBtnPanel(doc);
         CurrentUserUtils.setupDashboards(doc);
         CurrentUserUtils.setupCatalog(doc);
-        CurrentUserUtils.setupRecentlyClosed(doc);
+        CurrentUserUtils.setupInactiveDocs(doc);
         CurrentUserUtils.setupUserDoc(doc);
     }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 23ce71c4f..d621a792b 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                 const toRemove = value.filter(v => docs.includes(v));
 
                 if (toRemove.length !== 0) {
-                    const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+                    const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc;
                     toRemove.forEach(doc => {
                         Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc);
                         recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 2d742f4ba..aebb7a19a 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -325,7 +325,7 @@
     display: flex;
 }
 
-.mainView-recentlyClosed {
+.mainView-inactiveDocs {
     height: 25%;
     position: relative;
     display: flex;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0dee9291d..bc5483644 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -262,7 +262,7 @@ export class MainView extends React.Component {
             y: 400,
             _width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
             _height: this._panelHeight,
-            title: "Untitled Collection",
+            title: "Untitled Tab",
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
@@ -339,7 +339,7 @@ export class MainView extends React.Component {
 
         if (doc?.type === DocumentType.COL) {
             if (doc.title === "Basic Item Creators" || doc.title === "sidebar-tools"
-                || doc.title === "sidebar-recentlyClosed" || doc.title === "sidebar-catalog"
+                || doc.title === "sidebar-inactiveDocs" || doc.title === "sidebar-catalog"
                 || doc.title === "Mobile Uploads" || doc.title === "COLLECTION_PROTO"
                 || doc.title === "Advanced Item Prototypes" || doc.title === "all Creators") {
                 return "lightgrey";
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c499a4176..faa9d4901 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -613,7 +613,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 if (doc instanceof Doc) {
                     const theDoc = doc;
 
-                    const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc);
+                    const recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc);
                     if (recent) {
                         Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                     }
@@ -637,7 +637,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         //stack.header.controlsContainer.find('.lm_popout').hide();
         stack.header.element.on('mousedown', (e: any) => {
             if (e.target === stack.header.element[0] && e.button === 1) {
-                this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }));
+                this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Tab" }));
             }
         });
 
@@ -672,7 +672,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
                     if (doc instanceof Doc) {
                         let recent: Doc | undefined;
-                        if (recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc)) {
+                        if (recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc)) {
                             Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                         }
                         const theDoc = doc;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 241c64f9a..fdb843e60 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -484,12 +484,12 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     style={{
                         overflowY: this.props.active() ? "auto" : "hidden",
                         transform: `scale(${this.scaling}`,
-                        height: `${1 / this.scaling * 100}%`,
+                        height: this.layoutDoc._autoHeight ? "auto" : `${1 / this.scaling * 100}%`,
                         width: `${1 / this.scaling * 100}%`,
                         transformOrigin: "top left",
                     }}
                     onScroll={action(e => {
-                        if (!this.props.isSelected() && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
+                        if (!this.props.isSelected(true) && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
                         else this._scroll = e.currentTarget.scrollTop;
                     })}
                     onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index cde86b127..8cf8000f7 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -734,8 +734,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) {
-            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
+        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myInactiveDocs) {
+            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myInactiveDocs = new List<Doc>(), icon: "plus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 12d2152b1..ca3b67610 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -204,7 +204,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             const value = DocListCast(targetDataDoc[this.props.fieldKey]);
             const toRemove = value.filter(v => docs.includes(v));
             if (toRemove.length !== 0) {
-                const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+                const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc;
                 toRemove.forEach(doc => {
                     Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc);
                     recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-- 
cgit v1.2.3-70-g09d2


From cbedd8e1e651843e50f8f27163bf8e2a1a574009 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 22 Aug 2020 00:49:22 -0400
Subject: fixed key value for empty lists to show [], fixed tree view to allow
 'data' field .to display for empty lists.   Gave untitled Tabs a default
 height

---
 src/client/util/CurrentUserUtils.ts                 |  2 +-
 src/client/views/collections/CollectionTreeView.tsx | 16 ++++++++--------
 src/client/views/nodes/FieldView.tsx                |  2 +-
 src/fields/Doc.ts                                   |  3 +++
 4 files changed, 13 insertions(+), 10 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b935e28d4..b907a8e4c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -401,7 +401,7 @@ export class CurrentUserUtils {
                 { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.emptyPane === undefined) {
-            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", 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"]) });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8cf8000f7..4503b0fb3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { PrefetchProxy } from '../../../fields/Proxy';
@@ -90,7 +90,7 @@ class TreeView extends React.Component<TreeViewProps> {
     get displayName() { return "TreeView(" + this.doc.title + ")"; }  // this makes mobx trace() statements more descriptive
     get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
     get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); }
-    get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs.length ? this.fieldKey : this.defaultExpandedView); }
+    get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
     @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
     set treeViewOpen(c: boolean) {
         if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
@@ -104,9 +104,9 @@ class TreeView extends React.Component<TreeViewProps> {
     @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
     childDocList(field: string) {
         const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
-        return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
-            (layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
-            DocListCast(this.doc[field])); // otherwise use the document's data field
+        return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+            (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
+            DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
     }
     @computed get childDocs() { return this.childDocList(this.fieldKey); }
     @computed get childLinks() { return this.childDocList("links"); }
@@ -429,7 +429,7 @@ class TreeView extends React.Component<TreeViewProps> {
                                     this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
                                         this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
                                             (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
-                                                this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+                                                this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
                         }
                         this.treeViewOpen = true;
                     })}>
@@ -734,8 +734,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myInactiveDocs) {
-            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myInactiveDocs = new List<Doc>(), icon: "plus" });
+        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) {
+            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9d61ec6d1..d95951f3a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -133,7 +133,7 @@ export class FieldView extends React.Component<FieldViewProps> {
             // );
         }
         else if (field instanceof List) {
-            return <div> {field.map(f => Field.toString(f)).join(", ")}  </div>;
+            return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : "[]"}  </div>;
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
         else if (field instanceof WebField) {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index c4a49962b..c78b01def 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -91,6 +91,9 @@ export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> {
 export function DocListCast(field: FieldResult): Doc[] {
     return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[];
 }
+export function DocListCastOrNull(field: FieldResult) {
+    return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined;
+}
 
 export const WidthSym = Symbol("Width");
 export const HeightSym = Symbol("Height");
-- 
cgit v1.2.3-70-g09d2


From 4ed30f48bd0c87f0a5ccb484d78bf4601ab6ff74 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 22 Aug 2020 09:49:56 -0400
Subject: switched search from writiing searchMatch to matched docs to instead
 store in a run-time observableMap.  This allows simultaneous searches by
 different users.

---
 .../views/collections/CollectionSchemaCells.tsx    | 26 ++++++---------
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/nodes/FieldView.tsx               |  3 --
 .../views/nodes/formattedText/FormattedTextBox.tsx | 39 ++++++++++------------
 src/client/views/pdf/PDFViewer.tsx                 |  4 +--
 src/client/views/search/SearchBox.tsx              | 34 ++++++++-----------
 src/fields/Doc.ts                                  | 25 ++++++++++++++
 7 files changed, 69 insertions(+), 64 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index ea786800c..4bd69041b 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -894,26 +894,22 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
 export class CollectionSchemaButtons extends CollectionSchemaCell {
     render() {
         const doc = this.props.rowProps.original;
-        const searchMatch = (backward: boolean = true) => { doc.searchMatch = undefined; setTimeout(() => doc.searchMatch = backward, 0); };
+        const searchMatch = (backward: boolean = true) => Doc.SearchMatchNext(doc, backward);
         // const reference = React.createRef<HTMLDivElement>();
         // const onItemDown = (e: React.PointerEvent) => {
         //     (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
         //         SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
         // };
         return !BoolCast(this.props.Document._searchDoc) ? <></>
-            : StrCast(doc.type) === DocumentType.PDF ?
-                <button style={{ position: "relative", height: 30, width: 28, left: 1, }} onClick={() => searchMatch(false)}>
-                    <FontAwesomeIcon icon="arrow-down" size="sm" />
-                </button>
-                : StrCast(doc.type) === DocumentType.RTF ?
-                    <div style={{ paddingTop: 8, paddingLeft: 3, }} >
-                        <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}>
-                            <FontAwesomeIcon icon="arrow-up" size="sm" />
-                        </button>
-                        <button style={{ padding: 2 }} onClick={() => searchMatch(false)} >
-                            <FontAwesomeIcon icon="arrow-down" size="sm" />
-                        </button>
-                    </div> :
-                    <></>;
+            : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ?
+                <div style={{ paddingTop: 8, paddingLeft: 3, }} >
+                    <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}>
+                        <FontAwesomeIcon icon="arrow-up" size="sm" />
+                    </button>
+                    <button style={{ padding: 2 }} onClick={() => searchMatch(false)} >
+                        <FontAwesomeIcon icon="arrow-down" size="sm" />
+                    </button>
+                </div> :
+                <></>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4503b0fb3..8440d3e9b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -472,7 +472,7 @@ class TreeView extends React.Component<TreeViewProps> {
         return <>
             <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
                 style={{
-                    fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined,
+                    fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
                     pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d95951f3a..3726f1d5f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -58,9 +58,6 @@ export interface FieldViewProps {
 
     ChromeHeight?: () => number;
     childLayoutTemplate?: () => Opt<Doc>;
-    highlighting?: string[];
-    lines?: string[];
-    doc?: Doc;
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     height?: number;
     width?: number;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 6b4115e53..5250a8f58 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -376,8 +376,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     public highlightSearchTerms = (terms: string[], backward: boolean) => {
         if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
-
-
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
             const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
@@ -385,24 +383,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             let tr = this._editorView.state.tr;
             const flattened: TextSelection[] = [];
             res.map(r => r.map(h => flattened.push(h)));
-            if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) {
-                this._searchIndex = 0;
-                Doc.GetProto(this.dataDoc).resetSearch = undefined;
-            }
-            else {
-                this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-                if (backward === true) {
-                    if (this._searchIndex > 1) {
-                        this._searchIndex += -2;
-                    }
-                    else if (this._searchIndex === 1) {
-                        this._searchIndex = length - 1;
-                    }
-                    else if (this._searchIndex === 0 && length !== 1) {
-                        this._searchIndex = length - 2;
-                    }
-
+            console.log("Search:" + this.rootDoc.title + " " + this._searchIndex + " => " + (this._searchIndex + 1 > flattened.length - 1 ? 0 : this._searchIndex + 1));
+            this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+            if (backward === true) {
+                if (this._searchIndex > 1) {
+                    this._searchIndex += -2;
+                }
+                else if (this._searchIndex === 1) {
+                    this._searchIndex = length - 1;
                 }
+                else if (this._searchIndex === 0 && length !== 1) {
+                    this._searchIndex = length - 2;
+                }
+
             }
 
             const lastSel = Math.min(flattened.length - 1, this._searchIndex);
@@ -909,9 +902,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
         this.setupEditor(this.config, this.props.fieldKey);
 
-        this._disposers.search = reaction(() => this.rootDoc.searchMatch,
-            search => search !== undefined ? this.highlightSearchTerms([Doc.SearchQuery()], BoolCast(search)) : this.unhighlightSearchTerms(),
-            { fireImmediately: this.rootDoc.searchMatch !== undefined ? true : false });
+        this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
+            search => {
+                search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms();
+            },
+            { fireImmediately: Doc.IsSearchMatch(this.rootDoc) ? true : false });
 
         this._disposers.record = reaction(() => this._recording,
             () => {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0916e8b0c..f2aa9fa3f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -150,9 +150,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         runInAction(() => this._showWaiting = this._showCover = true);
         this.props.startupLive && this.setupPdfJsViewer();
         this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0);
-        this._searchReactionDisposer = reaction(() => this.Document.searchMatch,
+        this._searchReactionDisposer = reaction(() => Doc.IsSearchMatch(this.rootDoc),
             m => {
-                if (m !== undefined) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true);
+                if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0);
                 else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200);
             }, { fireImmediately: true });
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d7c065a75..c571adefe 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -84,21 +84,17 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
-    @action
-    getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc)
-
     @action.bound
     onChange(e: React.ChangeEvent<HTMLInputElement>) {
         this.layoutDoc._searchString = e.target.value;
         this.newsearchstring = e.target.value;
         if (e.target.value === "") {
             if (this.currentSelectedCollection) {
-                this.doLoop(this.currentSelectedCollection, undefined);
+                this.setSearchDocsRecursive(this.currentSelectedCollection, undefined);
             }
             this.closeSearch(false);
 
             if (this.currentSelectedCollection !== undefined) {
-                this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
                 this.currentSelectedCollection = undefined;
                 this.props.Document.selectedDoc = undefined;
             }
@@ -242,14 +238,12 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             while (docs.length > 0) {
                 newarray = [];
                 docs.forEach((d) => {
-                    if (d.data !== undefined) {
-                        newarray.push(...DocListCast(d.data));
-                    }
-                    const hlights: string[] = [];
+                    d.data && newarray.push(...DocListCast(d.data));
+                    const hlights = new Set<string>();
                     this.documentKeys(d).forEach(key =>
-                        Field.toString(d[key] as Field).toLowerCase().includes(query) && !hlights.includes(key) && hlights.push(key));
-                    if (hlights.length > 0) {
-                        found.push([d, hlights, []]);
+                        Field.toString(d[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+                    if (Array.from(hlights.keys()).length > 0) {
+                        found.push([d, Array.from(hlights.keys()), []]);
                         docsforFilter.push(d);
                     }
                 });
@@ -259,7 +253,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             this.docsforfilter = docsforFilter;
             if (this.filter === true) {
                 selectedCollection.props.Document._searchDocs = new List<Doc>(docsforFilter);
-                this.doLoop(selectedCollection, docsforFilter);
+                this.setSearchDocsRecursive(selectedCollection, docsforFilter);
             }
             this._numTotalResults = found.length;
             this.realTotalResults = found.length;
@@ -285,7 +279,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     @action
     submitSearch = async (reset?: boolean) => {
         if (this.currentSelectedCollection !== undefined) {
-            this.doLoop(this.currentSelectedCollection, undefined);
+            this.setSearchDocsRecursive(this.currentSelectedCollection, undefined);
         }
         if (reset) {
             this.layoutDoc._searchString = "";
@@ -434,7 +428,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     closeSearch = (closesearchbar = true) => {
         this._results.forEach(result => {
             Doc.UnBrushDoc(result[0]);
-            result[0].searchMatch = undefined;
+            Doc.ClearSearchMatches();
         });
         closesearchbar && (this._searchbarOpen = false);
     }
@@ -479,9 +473,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                 const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
                 const lines = new List<string>(result[2]);
                 highlights.forEach((item) => headers.add(item));
-                result[0].lines = lines;
-                result[0].highlighting = highlights.join(", ");
-                result[0].searchMatch = true;
+                Doc.SetSearchMatch(result[0], { searchMatch: 1 });
                 if (i < this._visibleDocuments.length) {
                     this._visibleDocuments[i] = result[0];
                     Doc.BrushDoc(result[0]);
@@ -530,7 +522,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         this._searchFullDB = scope;
         this.dataDoc[this.fieldKey] = new List<Doc>([]);
         if (this.currentSelectedCollection !== undefined) {
-            this.doLoop(this.currentSelectedCollection, undefined);
+            this.setSearchDocsRecursive(this.currentSelectedCollection, undefined);
         }
         this.submitSearch();
     }
@@ -566,7 +558,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         </div>;
     }
 
-    doLoop = (collectionView: DocumentView, filter: Doc[] | undefined) => {
+    setSearchDocsRecursive = (collectionView: DocumentView, filter: Doc[] | undefined) => {
         let docs = DocListCast(collectionView.dataDoc[Doc.LayoutFieldKey(collectionView.dataDoc)]);
         let newarray: Doc[] = [];
         while (docs.length > 0) {
@@ -614,7 +606,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                                             onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
                                             onClick={action(() => {
                                                 this.filter = !this.filter && !this._searchFullDB;
-                                                this.currentSelectedCollection && this.doLoop(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined);
+                                                this.currentSelectedCollection && this.setSearchDocsRecursive(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined);
                                             })} />
                                     </div>
                                 </Tooltip>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index c78b01def..dc274a063 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -23,6 +23,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u
 import { LinkManager } from "../client/util/LinkManager";
 import JSZip = require("jszip");
 import { saveAs } from "file-saver";
+import { result } from "lodash";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -881,6 +882,7 @@ export namespace Doc {
 
     export class DocBrush {
         BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+        SearchMatchDoc: ObservableMap<Doc, { searchMatch: number }> = new ObservableMap();
     }
     const brushManager = new DocBrush();
 
@@ -906,6 +908,29 @@ export namespace Doc {
     export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
     export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
     export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
+
+    export function IsSearchMatch(doc: Doc) {
+        return computedFn(function IsSearchMatch(doc: Doc) {
+            return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
+                brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+        })(doc);
+    }
+    export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) {
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
+        brushManager.SearchMatchDoc.set(doc, results);
+        return doc;
+    }
+    export function SearchMatchNext(doc: Doc, backward: boolean) {
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
+        const result = brushManager.SearchMatchDoc.get(doc);
+        const num = Math.abs(result?.searchMatch || 0) + 1;
+        runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num }));
+        return doc;
+    }
+    export function ClearSearchMatches() {
+        brushManager.SearchMatchDoc.clear();
+    }
+
     export function IsBrushed(doc: Doc) {
         return computedFn(function IsBrushed(doc: Doc) {
             return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
-- 
cgit v1.2.3-70-g09d2


From 88b06945c8a1fcb7bc518de878a4894de70dda94 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 22 Aug 2020 14:21:41 -0400
Subject: major overhaul of search to avoid writing anything to filtered
 documents and to make the UI work properly.

---
 src/Utils.ts                                       |   2 +
 src/client/documents/Documents.ts                  |   5 +-
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/views/GestureOverlay.tsx                |   3 +-
 src/client/views/MainView.tsx                      |  11 +-
 src/client/views/OverlayView.tsx                   |   3 +-
 src/client/views/Palette.tsx                       |   3 +-
 src/client/views/TemplateMenu.tsx                  |   3 +-
 .../views/collections/CollectionDockingView.tsx    |   4 +-
 .../views/collections/CollectionLinearView.tsx     |   1 +
 src/client/views/collections/CollectionMenu.tsx    |  14 ++-
 .../views/collections/CollectionSchemaCells.tsx    |   6 +-
 .../views/collections/CollectionSchemaHeaders.tsx  |  26 ++---
 .../views/collections/CollectionSchemaView.scss    |   3 +
 .../views/collections/CollectionSchemaView.tsx     |  17 +--
 .../views/collections/CollectionStackingView.tsx   |   1 +
 src/client/views/collections/CollectionSubView.tsx |  26 ++---
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 src/client/views/collections/CollectionView.tsx    |   3 +-
 src/client/views/collections/SchemaTable.tsx       |   3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 +
 .../collectionFreeForm/PropertiesView.tsx          |   3 +-
 .../CollectionMulticolumnView.tsx                  |   1 +
 .../CollectionMultirowView.tsx                     |   1 +
 src/client/views/nodes/DocHolderBox.tsx            |   2 +
 src/client/views/nodes/DocumentView.tsx            |   2 +
 src/client/views/nodes/FieldView.tsx               |   1 +
 src/client/views/nodes/ImageBox.tsx                |   1 +
 src/client/views/nodes/LinkDocPreview.tsx          |   3 +-
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 src/client/views/nodes/VideoBox.tsx                |   1 +
 src/client/views/nodes/WebBox.tsx                  |   1 +
 .../views/nodes/formattedText/DashDocView.tsx      |   3 +-
 .../formattedText/FormattedTextBoxComment.tsx      |   3 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |   1 +
 src/client/views/pdf/PDFViewer.tsx                 |   1 +
 .../views/presentationview/PresElementBox.tsx      |   1 +
 src/client/views/search/SearchBox.tsx              | 128 ++++++++-------------
 src/fields/Doc.ts                                  |  15 +--
 src/fields/List.ts                                 |  25 ++--
 src/mobile/AudioUpload.tsx                         |   3 +-
 src/mobile/MobileInterface.tsx                     |   3 +-
 42 files changed, 173 insertions(+), 169 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index d9a5353e8..3ff05104f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -422,6 +422,8 @@ export function returnEmptyString() { return ""; }
 
 export function returnEmptyFilter() { return [] as string[]; }
 
+export function returnEmptyDoclist() { return [] as Doc[]; }
+
 export let emptyPath = [];
 
 export function emptyFunction() { }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4544e0e3e..49da4f9eb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -72,6 +72,7 @@ export interface DocumentOptions {
     _scrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
     _chromeStatus?: string;
+    _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
     _viewType?: string; // sub type of a collection
     _gridGap?: number; // gap between items in masonry view
     _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts
@@ -822,8 +823,8 @@ export namespace Docs {
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
-            const tabs = TreeDocument(documents, { title: "Active Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
-            const all = TreeDocument([], { title: "Inactive Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
+            const tabs = TreeDocument(documents, { title: "On-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
+            const all = TreeDocument([], { title: "Off-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" });
             Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
             return inst;
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b907a8e4c..c09a227cf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -525,7 +525,7 @@ export class CurrentUserUtils {
     static setupSearchPanel(doc: Doc) {
         if (doc["search-panel"] === undefined) {
             doc["search-panel"] = new PrefetchProxy(Docs.Create.SearchDocument({
-                _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true,
+                _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
                 childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, _chromeStatus: "disabled", title: "sidebar search stack", system: true
             })) as any as Doc;
         }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 2d41343b3..260cffc90 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -7,7 +7,7 @@ import { Cast, FieldValue, NumCast } from "../../fields/Types";
 import MobileInkOverlay from "../../mobile/MobileInkOverlay";
 import { GestureUtils } from "../../pen-gestures/GestureUtils";
 import { MobileInkOverlayContent } from "../../server/Message";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents, returnEmptyDoclist } from "../../Utils";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
 import { DocUtils } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
@@ -935,6 +935,7 @@ export default class GestureOverlay extends Touchable {
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
             />;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bc5483644..979f803bf 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -16,7 +16,7 @@ import { listSpec } from '../../fields/Schema';
 import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils, returnEmptyDoclist } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
@@ -206,7 +206,7 @@ export class MainView extends React.Component {
                 }
             });
             if (check === false) {
-                SearchBox.Instance.closeSearch();
+                SearchBox.Instance.resetSearch(true);
             }
         }
 
@@ -394,6 +394,7 @@ export class MainView extends React.Component {
             whenActiveChanged={emptyFunction}
             bringToFront={emptyFunction}
             docFilters={returnEmptyFilter}
+            searchFilterDocs={returnEmptyDoclist}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined}
             renderDepth={-1}
@@ -476,6 +477,7 @@ export class MainView extends React.Component {
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     relative={true}
@@ -510,6 +512,7 @@ export class MainView extends React.Component {
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
                 relative={true}
@@ -703,6 +706,7 @@ export class MainView extends React.Component {
                     focus={emptyFunction}
                     whenActiveChanged={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
             </div>;
@@ -775,6 +779,7 @@ export class MainView extends React.Component {
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
             /></div>
@@ -808,6 +813,7 @@ export class MainView extends React.Component {
                     NativeWidth={() => 800}
                     ContentScaling={returnOne}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                 />
             </div>;
     }
@@ -878,6 +884,7 @@ export class MainView extends React.Component {
                             NativeWidth={() => 800}
                             ContentScaling={returnOne}
                             docFilters={returnEmptyFilter}
+                            searchFilterDocs={returnEmptyDoclist}
                         />
                     </div>;
                 </span>, ele);
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 49580cde4..001135340 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
 import { Doc, DocListCast, Opt } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { NumCast, Cast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
 import { DocumentView } from "./nodes/DocumentView";
@@ -205,6 +205,7 @@ export class OverlayView extends React.Component {
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
             </div>;
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 0a4334302..92c3f09b4 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc } from "../../fields/Doc";
 import { NumCast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { DocumentView } from "./nodes/DocumentView";
 import "./Palette.scss";
@@ -60,6 +60,7 @@ export default class Palette extends React.Component<PaletteProps> {
                             whenActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
                             docFilters={returnEmptyFilter}
+                            searchFilterDocs={returnEmptyDoclist}
                             ContainingCollectionView={undefined}
                             ContainingCollectionDoc={undefined} />
                         <div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index eb20fc257..e4ba45648 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -10,7 +10,7 @@ import { Doc, DocListCast } from "../../fields/Doc";
 import { Docs, DocUtils, } from "../documents/Documents";
 import { StrCast, Cast } from "../../fields/Types";
 import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter } from "../../Utils";
+import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { ScriptField, ComputedField } from "../../fields/ScriptField";
 import { Scripting } from "../util/Scripting";
@@ -133,6 +133,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 rootSelected={returnFalse}
                 onCheckedClick={this.scriptField}
                 onChildClick={this.scriptField}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 83321d6e0..74b1e5714 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -14,7 +14,7 @@ import { FieldId } from "../../../fields/RefField";
 import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -938,6 +938,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 addDocTab={this.addDocTab}
                 pinToPres={DockedFrameRenderer.PinDoc}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 fitToBox={true}
             />
             <div className="miniOverlay" onPointerDown={this.miniDown} >
@@ -979,6 +980,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 addDocTab={this.addDocTab}
                 pinToPres={DockedFrameRenderer.PinDoc}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
             {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index e1b07077e..0fd18034f 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -166,6 +166,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 whenActiveChanged={emptyFunction}
                                 bringToFront={emptyFunction}
                                 docFilters={this.props.docFilters}
+                                searchFilterDocs={this.props.searchFilterDocs}
                                 ContainingCollectionView={undefined}
                                 ContainingCollectionDoc={undefined} />
                         </div>;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 388eda2b3..5580c32f2 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -131,7 +131,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     _contentCommand = {
         params: ["target", "source"], title: "set content",
         script: "getProto(self.target).data = copyField(self.source);",
-        immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)), // Doc.aliasDocs(source),
+        immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)),
         initialize: emptyFunction,
     };
     _onClickCommand = {
@@ -180,12 +180,16 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     };
     _saveFilterCommand = {
         params: ["target"], title: "save filter",
-        script: "self.target._docFilters = copyField(self['target-docFilters']);",
-        immediate: undoBatch((source: Doc[]) => this.target._docFilters = undefined),
-        initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; },
+        script: `self.target._docFilters = copyField(self['target-docFilters']); 
+                 self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
+        immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
+        initialize: (button: Doc) => {
+            button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : undefined;
+            button['target-searchFilterDocs'] = this.target._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(this.target._searchFilterDocs as any as ObjectField) : undefined;
+        },
     };
 
-    @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+    @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
     @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
     @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
     @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 4bd69041b..2f1f7a90f 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -229,7 +229,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
 
         const onItemDown = async (e: React.PointerEvent) => {
-            if (this.props.Document._searchDoc !== undefined) {
+            if (this.props.Document._searchDoc) {
                 const doc = Doc.GetProto(this.props.rowProps.original);
                 const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
                 let targetContext = undefined;
@@ -315,7 +315,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             }
         }
         let search = false;
-        if (this.props.Document._searchDoc !== undefined) {
+        if (this.props.Document._searchDoc) {
             search = true;
         }
 
@@ -900,7 +900,7 @@ export class CollectionSchemaButtons extends CollectionSchemaCell {
         //     (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
         //         SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
         // };
-        return !BoolCast(this.props.Document._searchDoc) ? <></>
+        return !this.props.Document._searchDoc ? <></>
             : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ?
                 <div style={{ paddingTop: 8, paddingLeft: 3, }} >
                     <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}>
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 34a8bfa24..a90d78a87 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -287,12 +287,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
                 const colpos = this._searchTerm.indexOf(":");
                 const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
                 if (temp === "") {
-                    console.log("here we are first");
                     Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
                     this.updateFilter();
                 }
                 else {
-                    console.log("here we are first");
                     Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
                     this.tempfilter = temp;
                     Doc.setDocFilter(this.props.Document, this._key, temp, "check");
@@ -304,8 +302,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
                 Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
                 this.updateFilter();
                 let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-                const blockedkeys = ["system", "ACL-Public", "_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
-                keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
+                const blockedkeys = ["system", "customTitle", "limitHeight", "proto", "x", "y", "zIndex", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
+                keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
                 if (keyOptions.length) {
                     this.onSelect(keyOptions[0]);
                 } else if (this._searchTerm !== "" && this.props.canAddNew) {
@@ -338,8 +336,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
             this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
 
-        const blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
-        keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
+        const blockedkeys = ["proto", "x", "y", "zIndex", "_timeStampOnEnter", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
+        keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
 
         const options = keyOptions.map(key => {
             return <div key={key} className="key-option" style={{
@@ -498,11 +496,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
             }
         });
 
-        keyOptions.forEach(key => {
-            Doc.setDocFilter(this.props.Document, this._key, key, undefined);
-        }
-        );
-        Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
+        Doc.setDocFilter(this.props.Document, this._key, "", "remove");
         this.props.col.setColor("rgb(241, 239, 235)");
         this.closeResultsVisibility = "none";
     }
@@ -518,13 +512,9 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
                 <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
                     <input className="keys-search" style={{ width: "100%" }}
                         ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
-                        onChange={e => {
-                            this.onChange(e.target.value);
-                        }}
-                        onClick={(e) => {
-                            //this._inputRef.current!.select();
-                            e.stopPropagation();
-                        }} onFocus={this.onFocus} ></input>
+                        onChange={e => this.onChange(e.target.value)}
+                        onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
+                        onFocus={this.onFocus} ></input>
                     <div style={{ display: this.closeResultsVisibility }}>
                         <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
                             style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index fc5dffaec..49d9a5c68 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -99,6 +99,9 @@
         direction: ltr;
         overflow: visible;
     }
+    .rt-noData {
+        display: none;
+    }
 
     .rt-thead {
         width: 100%;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 257099783..a78061da6 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -9,7 +9,7 @@ import { Resize } from "react-table";
 import "react-table/react-table.css";
 import { Doc, Opt } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
+import { listSpec, makeInterface, emptySchema } from "../../../fields/Schema";
 import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { Cast, NumCast, BoolCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
@@ -73,7 +73,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     @computed get menuCoordinates() {
         let searchx = 0;
         let searchy = 0;
-        if (this.props.Document._searchDoc !== undefined) {
+        if (this.props.Document._searchDoc) {
             const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
             if (el !== undefined) {
                 const rect = el.getBoundingClientRect();
@@ -305,17 +305,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     this.columns = columns;
                     if (filter) {
                         Doc.setDocFilter(this.props.Document, newKey, filter, "match");
-                        if (this.props.Document.selectedDoc !== undefined) {
-                            const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
-                            Doc.setDocFilter(doc, newKey, filter, "match");
-                        }
                     }
                     else {
                         this.props.Document._docFilters = undefined;
-                        if (this.props.Document.selectedDoc !== undefined) {
-                            const doc = Cast(this.props.Document.selectedDoc, Doc, null);
-                            doc._docFilters = undefined;
-                        }
                     }
                 }
             }
@@ -454,6 +446,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     PanelHeight={this.previewHeight}
                     ScreenToLocalTransform={this.getPreviewTransform}
                     docFilters={this.docFilters}
+                    searchFilterDocs={this.searchFilterDocs}
                     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                     ContainingCollectionView={this.props.CollectionView}
                     moveDocument={this.props.moveDocument}
@@ -592,7 +585,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     }
     render() {
         let name = "collectionSchemaView-container";
-        if (this.props.Document._searchDoc !== undefined) {
+        if (this.props.Document._searchDoc) {
             name = "collectionSchemaView-searchContainer";
         }
         if (!this.props.active()) setTimeout(() => this.closeHeader(), 0);
@@ -615,7 +608,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
         return <div className={name}
             style={{
                 overflow: this.props.overflow === true ? "scroll" : undefined, backgroundColor: "white",
-                pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
+                pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
                 width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
             }}  >
             <div className="collectionSchemaView-tableContainer"
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a9437c11f..a50dab54d 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -217,6 +217,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             opacity={opacity}
             focus={this.focusDocument}
             docFilters={this.docFilters}
+            searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a1dd905c2..c73159084 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -111,6 +111,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             return this.props.ignoreFields?.includes("_docFilters") ? [] :
                 [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
         }
+        searchFilterDocs = () => {
+            return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
+        }
         @computed get childDocs() {
             let rawdocs: (Doc | Promise<Doc>)[] = [];
             if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
@@ -128,34 +131,31 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
             const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
 
-            let searchDocs = DocListCast(this.props.Document._searchDocs);
-
+            let searchDocs = this.searchFilterDocs();
 
             let docsforFilter: Doc[] = childDocs;
 
-            if (searchDocs !== undefined && searchDocs.length > 0) {
+            if (searchDocs.length > 0) {
                 docsforFilter = [];
                 const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
-                console.log(searchDocs);
                 searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript);
                 childDocs.forEach((d) => {
+                    let notFiltered = searchDocs.includes(d) || d.z;
                     if (d.data !== undefined) {
-                        let newdocs = DocListCast(d.data);
-                        if (newdocs.length > 0) {
-                            let displaycheck = false;
+                        let subDocs = DocListCast(d.data);
+                        if (subDocs.length > 0) {
                             let newarray: Doc[] = [];
-                            while (newdocs.length > 0) {
+                            while (subDocs.length > 0 && !notFiltered) {
                                 newarray = [];
-                                newdocs.forEach((t) => {
+                                subDocs.forEach((t) => {
+                                    notFiltered = notFiltered || searchDocs.includes(t);
                                     DocListCast(t.data).forEach((newdoc) => newarray.push(newdoc));
-                                    displaycheck = displaycheck || searchDocs.includes(t);
                                 });
-                                newdocs = newarray;
+                                subDocs = newarray;
                             }
-                            displaycheck && docsforFilter.push(d);
                         }
                     }
-                    searchDocs.includes(d) && docsforFilter.push(d);
+                    notFiltered && docsforFilter.push(d);
                 });
                 return docsforFilter;
             }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8440d3e9b..9460095e4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -8,7 +8,7 @@ import { PrefetchProxy } from '../../../fields/Proxy';
 import { Document, listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
@@ -359,6 +359,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     focus={returnFalse}
                     ScreenToLocalTransform={this.docTransform}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.containingCollection}
                     ContainingCollectionView={undefined}
                     addDocument={returnFalse}
@@ -466,6 +467,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 bringToFront={emptyFunction}
                 dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.containingCollection}
             />;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ca3b67610..5230fb533 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -18,7 +18,7 @@ import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx, GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -537,6 +537,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     fieldKey={`${this.props.fieldKey}-filter`}
                     CollectionView={this}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     PanelWidth={this.facetWidth}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 6ec9783e2..e1c81aeab 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -12,7 +12,7 @@ import { listSpec } from "../../../fields/Schema";
 import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
 import { Docs, DocumentOptions } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CompileScript, Transformer, ts } from "../../util/Scripting";
@@ -627,6 +627,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         PanelHeight={() => 150}
                         ScreenToLocalTransform={this.getPreviewTransform}
                         docFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                         ContainingCollectionView={this.props.CollectionView}
                         moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e66e469eb..0c0f6ca58 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -974,6 +974,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             ContainingCollectionView: this.props.CollectionView,
             ContainingCollectionDoc: this.props.Document,
             docFilters: this.docFilters,
+            searchFilterDocs: this.searchFilterDocs,
             focus: this.focusDocument,
             backgroundColor: this.getClusterColor,
             backgroundHalo: this.backgroundHalo,
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index fb138ecc0..cbce6448f 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { EditableView } from "../../EditableView";
 import { KeyValueBox } from "../../nodes/KeyValueBox";
 import { Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView";
-import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils";
+import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils, returnEmptyDoclist } from "../../../../Utils";
 import { Id } from "../../../../fields/FieldSymbols";
 import { Transform } from "../../../util/Transform";
 import { PropertiesButtons } from "../../PropertiesButtons";
@@ -284,6 +284,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     focus={returnFalse}
                     ScreenToLocalTransform={this.getTransform}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionDoc={undefined}
                     ContainingCollectionView={undefined}
                     addDocument={returnFalse}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 21d283547..0afcab5a3 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -234,6 +234,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
             docFilters={this.docFilters}
+            searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index d02088a6c..53825eece 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -234,6 +234,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
             docFilters={this.docFilters}
+            searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 88eb48f51..b3b7cc4f3 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -120,6 +120,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     docFilters={this.props.docFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                     ContainingCollectionDoc={undefined}
                     fitToBox={true}
@@ -149,6 +150,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     docFilters={this.props.docFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                     ContainingCollectionDoc={undefined}
                     fitToBox={true}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 91f12f004..bf42f30fe 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -45,6 +45,7 @@ export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     docFilters: () => string[];
+    searchFilterDocs: () => Doc[];
     FreezeDimensions?: boolean;
     NativeWidth: () => number;
     NativeHeight: () => number;
@@ -829,6 +830,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return (<div className="documentView-contentsView" style={{ borderRadius: "inherit", width: "100%", height: "100%" }}>
             <DocumentContentsView key={1}
                 docFilters={this.props.docFilters}
+                searchFilterDocs={this.props.searchFilterDocs}
                 ContainingCollectionView={this.props.ContainingCollectionView}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                 NativeWidth={this.NativeWidth}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 3726f1d5f..fceeced36 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -29,6 +29,7 @@ export interface FieldViewProps {
     dropAction: dropActionType;
     backgroundHalo?: () => boolean;
     docFilters: () => string[];
+    searchFilterDocs: () => Doc[];
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (isCtrlPressed: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 216c5f39e..dd3914df7 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -489,6 +489,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 ScreenToLocalTransform={this.screenToLocalTransform}
                 renderDepth={this.props.renderDepth + 1}
                 docFilters={this.props.docFilters}
+                searchFilterDocs={this.props.searchFilterDocs}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                 {this.contentFunc}
             </CollectionFreeFormView>
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c4481b213..f154c5f56 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import wiki from "wikijs";
 import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { Transform } from "../../util/Transform";
@@ -101,6 +101,7 @@ export class LinkDocPreview extends React.Component<Props> {
                 pinToPres={returnFalse}
                 dontRegisterView={true}
                 docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 renderDepth={0}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 255a1b2d0..890263a61 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -246,7 +246,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
             <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
                 setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
                 renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
-                addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters}
+                addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters} searchFilterDocs={this.props.searchFilterDocs}
                 pinToPres={this.props.pinToPres} addDocument={this.addDocument}
                 Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
                 ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ee92e517c..dd76503d5 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -384,6 +384,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform}
                     renderDepth={this.props.renderDepth + 1}
                     docFilters={this.props.docFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                     {this.contentFunc}
                 </CollectionFreeFormView>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1393e7868..7d7426e31 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -733,6 +733,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             ScreenToLocalTransform={this.scrollXf}
                             renderDepth={this.props.renderDepth + 1}
                             docFilters={this.props.docFilters}
+                            searchFilterDocs={this.props.searchFilterDocs}
                             ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                         </CollectionFreeFormView>
                     </div>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 31dd92b59..08bab8d12 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,7 +5,7 @@ import { Id } from "../../../../fields/FieldSymbols";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { ComputedField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from "../../../documents/Documents";
 import { DocumentView } from "../DocumentView";
@@ -255,6 +255,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                             bringToFront={emptyFunction}
                             dontRegisterView={false}
                             docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
+                            searchFilterDocs={this.props.tbox?.props.searchFilterDocs || returnEmptyDoclist}
                             ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                             ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                             ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 6f3984f39..1e1bcd42c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
 import { Doc, DocCastAsync, Opt } from "../../../../fields/Doc";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { schema } from "./schema_rts";
@@ -302,6 +302,7 @@ export class FormattedTextBoxComment {
                                             pinToPres={returnFalse}
                                             dontRegisterView={true}
                                             docFilters={returnEmptyFilter}
+                                            searchFilterDocs={returnEmptyDoclist}
                                             ContainingCollectionDoc={undefined}
                                             ContainingCollectionView={undefined}
                                             renderDepth={0}
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index a85dacc7e..b76e520b7 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -155,6 +155,7 @@ export class DashDocView {
                     bringToFront={emptyFunction}
                     dontRegisterView={false}
                     docFilters={this._textBox.props.docFilters}
+                    searchFilterDocs={this._textBox.props.searchFilterDocs}
                     ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                     ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                     ContentScaling={this.contentScaling}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f2aa9fa3f..67d435194 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -56,6 +56,7 @@ interface IViewerProps {
     Document: Doc;
     DataDoc?: Doc;
     docFilters: () => string[];
+    searchFilterDocs: () => Doc[];
     ContainingCollectionView: Opt<CollectionView>;
     PanelWidth: () => number;
     PanelHeight: () => number;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index a25a8ee33..dfcf575f1 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -102,6 +102,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     bringToFront={returnFalse}
                     opacity={returnOne}
                     docFilters={this.props.docFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     ContentScaling={returnOne}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index c571adefe..9c9900bfb 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
@@ -17,8 +17,8 @@ import { SetupDrag } from '../../util/DragManager';
 import { SearchUtil } from '../../util/SearchUtil';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
-import { ColumnType } from "../collections/CollectionSchemaView";
-import { CollectionView, CollectionViewType } from '../collections/CollectionView';
+import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
+import { CollectionViewType } from '../collections/CollectionView';
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -46,11 +46,11 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
 
-    private currentSelectedCollection: DocumentView | undefined = undefined;
-    private docsforfilter: Doc[] = [];
+    private docsforfilter: Doc[] | undefined = [];
     private realTotalResults: number = 0;
     private collectionRef = React.createRef<HTMLSpanElement>();
 
+    @observable _currentSelectedCollection: DocumentView | undefined = undefined;
     @observable _icons: string[] = this._allIcons;
     @observable _results: [Doc, string[], string[]][] = [];
     @observable _visibleElements: JSX.Element[] = [];
@@ -64,9 +64,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     @observable open = false;
     @observable children = 0;
     @observable newsearchstring = "";
-    @observable headercount: number = 0;
-    @observable headerscale: number = 0;
-    @observable filter = false;
+    @computed get filter() { return this._results?.length && (this._currentSelectedCollection?.props.Document._searchFilterDocs || this._currentSelectedCollection?.props.Document._docFilters); }
 
     constructor(props: any) {
         super(props);
@@ -78,6 +76,8 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             this._inputRef.current.focus();
             this._searchbarOpen = true;
         }
+        this._disposers.filters = reaction(() => this.props.Document._docFilters,
+            (filters: any) => this.setSearchFilter(this._currentSelectedCollection, this.filter ? undefined : this.docsforfilter));
     });
 
     componentWillUnmount() {
@@ -89,14 +89,12 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         this.layoutDoc._searchString = e.target.value;
         this.newsearchstring = e.target.value;
         if (e.target.value === "") {
-            if (this.currentSelectedCollection) {
-                this.setSearchDocsRecursive(this.currentSelectedCollection, undefined);
-            }
-            this.closeSearch(false);
+            this.docsforfilter = undefined;
+            this.setSearchFilter(this._currentSelectedCollection, undefined);
+            this.resetSearch(false);
 
-            if (this.currentSelectedCollection !== undefined) {
-                this.currentSelectedCollection = undefined;
-                this.props.Document.selectedDoc = undefined;
+            if (this._currentSelectedCollection !== undefined) {
+                this._currentSelectedCollection = undefined;
             }
             this.open = false;
             this._results = [];
@@ -226,10 +224,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         query = query.toLowerCase();
 
         if (selectedCollection !== undefined) {
-            this.currentSelectedCollection = selectedCollection;
-            if (this.filter === true) {
-                this.props.Document.selectedDoc = selectedCollection.props.Document;
-            }
+            this._currentSelectedCollection = selectedCollection;
             let docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
             const found: [Doc, string[], string[]][] = [];
             const docsforFilter: Doc[] = [];
@@ -251,10 +246,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             }
             this._results = found;
             this.docsforfilter = docsforFilter;
-            if (this.filter === true) {
-                selectedCollection.props.Document._searchDocs = new List<Doc>(docsforFilter);
-                this.setSearchDocsRecursive(selectedCollection, docsforFilter);
-            }
+            this.setSearchFilter(selectedCollection, this.filter && found.length ? docsforFilter : undefined);
             this._numTotalResults = found.length;
             this.realTotalResults = found.length;
         }
@@ -277,23 +269,17 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     }
 
     @action
-    submitSearch = async (reset?: boolean) => {
-        if (this.currentSelectedCollection !== undefined) {
-            this.setSearchDocsRecursive(this.currentSelectedCollection, undefined);
-        }
-        if (reset) {
-            this.layoutDoc._searchString = "";
-        }
+    submitSearch = async () => {
+        this.resetSearch(false);
+
         //this.props.Document._docFilters = new List();
         this._noResults = "";
 
         this.dataDoc[this.fieldKey] = new List<Doc>([]);
-        this.headercount = 0;
         this.children = 0;
         let query = StrCast(this.layoutDoc._searchString);
         Doc.SetSearchQuery(query);
-        this._searchFullDB ? query = this.getFinalQuery(query) : console.log("local");
-        this.closeSearch(false);
+        this._searchFullDB && (query = this.getFinalQuery(query));
         this._results = [];
         this._resultsSet.clear();
         this._visibleElements = [];
@@ -425,12 +411,14 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     }
 
     @action.bound
-    closeSearch = (closesearchbar = true) => {
+    resetSearch = (close: boolean) => {
         this._results.forEach(result => {
             Doc.UnBrushDoc(result[0]);
             Doc.ClearSearchMatches();
         });
-        closesearchbar && (this._searchbarOpen = false);
+        if (close) {
+            this._searchbarOpen = false;
+        }
     }
 
     @action.bound
@@ -482,7 +470,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                 }
             }
         }
-        this.headerscale = headers.size;
         const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
         if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
             const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
@@ -491,7 +478,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                     newSchemaHeaders.push(new SchemaHeaderField(header, "#f1efeb"));
                 }
             });
-            this.headercount = newSchemaHeaders.length;
             this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
         } else if (this.props.Document._schemaHeaders === undefined) {
             this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
@@ -518,12 +504,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     @action
     changeSearchScope = (scope: string) => {
-        scope && (this.filter = false);
+        this.docsforfilter = undefined;
+        this.setSearchFilter(this._currentSelectedCollection, undefined);
         this._searchFullDB = scope;
         this.dataDoc[this.fieldKey] = new List<Doc>([]);
-        if (this.currentSelectedCollection !== undefined) {
-            this.setSearchDocsRecursive(this.currentSelectedCollection, undefined);
-        }
         this.submitSearch();
     }
 
@@ -558,28 +542,14 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         </div>;
     }
 
-    setSearchDocsRecursive = (collectionView: DocumentView, filter: Doc[] | undefined) => {
-        let docs = DocListCast(collectionView.dataDoc[Doc.LayoutFieldKey(collectionView.dataDoc)]);
-        let newarray: Doc[] = [];
-        while (docs.length > 0) {
-            newarray = [];
-            docs.forEach(d => {
-                const subDocs = DocListCast(d.data);
-                if (subDocs.length) {
-                    d._searchDocs = filter ? new List<Doc>(filter) : undefined;
-                    DocListCast(d.data).forEach(newdoc => newarray.push(newdoc));
-                }
-            });
-            docs = newarray;
+    setSearchFilter = (collectionView: DocumentView | undefined, docsForFilter: Doc[] | undefined) => {
+        if (collectionView) {
+            const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null);
+            collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
+            collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined;
         }
-        collectionView.props.Document._searchDocs = filter ? new List<Doc>(filter) : undefined;
-        this.props.Document.selectedDoc = filter ? collectionView.props.Document : undefined;
     }
-
     render() {
-        this.props.Document._chromeStatus === "disabled";
-        this.props.Document._searchDoc = true;
-        const rows = this.children;
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
                 <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>{`${Doc.CurrentUserEmail}/${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.title}`}</div>
@@ -604,34 +574,34 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                                         <FontAwesomeIcon icon={"filter"} size="lg"
                                             style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
                                             onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
-                                            onClick={action(() => {
-                                                this.filter = !this.filter && !this._searchFullDB;
-                                                this.currentSelectedCollection && this.setSearchDocsRecursive(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined);
-                                            })} />
+                                            onClick={action(() => this.setSearchFilter(this._currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
                                     </div>
                                 </Tooltip>
                             </div>
                             {this.scopeButtons}
                         </div>
-
                     </div >
                 </div >
-                {!this._searchbarOpen ? (null) : <div style={{ zIndex: 20000, color: "black" }}>
-                    <div style={{ display: "flex", justifyContent: "center", }}>
-                        <div style={{ display: this.open ? "flex" : "none", overflow: "auto", }}>
-                            <CollectionView {...this.props}
-                                Document={this.props.Document}
-                                moveDocument={returnFalse}
-                                removeDocument={returnFalse}
-                                PanelHeight={this.open ? this.returnHeight : returnZero}
-                                PanelWidth={this.open ? this.returnLength : returnZero}
-                                overflow={length > window.innerWidth || rows > 6 ? true : false}
-                                focus={this.selectElement}
-                                ScreenToLocalTransform={Transform.Identity}
-                            />
+                {!this._searchbarOpen ? (null) :
+                    <div style={{ zIndex: 20000, color: "black" }} ref={(r) => r?.focus()}>
+                        <div style={{ display: "flex", justifyContent: "center", }}>
+                            <div style={{ display: this.open ? "flex" : "none", overflow: "auto", }}>
+                                <CollectionSchemaView {...this.props}
+                                    CollectionView={undefined}
+                                    annotationsKey={""}
+                                    addDocument={returnFalse}
+                                    Document={this.props.Document}
+                                    moveDocument={returnFalse}
+                                    removeDocument={returnFalse}
+                                    PanelHeight={this.open ? this.returnHeight : returnZero}
+                                    PanelWidth={this.open ? this.returnLength : returnZero}
+                                    overflow={length > window.innerWidth || this.children > 6 ? true : false}
+                                    focus={this.selectElement}
+                                    ScreenToLocalTransform={Transform.Identity}
+                                />
+                            </div>
                         </div>
                     </div>
-                </div>
                 }
             </div >
         );
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index dc274a063..272b70293 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1024,10 +1024,10 @@ export namespace Doc {
         const fieldVal = doc[key];
         if (Cast(fieldVal, listSpec("string"), []).length) {
             const vals = Cast(fieldVal, listSpec("string"), []);
-            return vals.some(v => v === value);
+            return vals.some(v => v.includes(value));  // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
         }
         const fieldStr = Field.toString(fieldVal as Field);
-        return fieldStr === value;
+        return fieldStr.includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
     }
 
     export function deiconifyView(doc: any) {
@@ -1056,18 +1056,14 @@ export namespace Doc {
         }
     }
 
-    export function aliasDocs(field: any) {
-        return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d)));
-    }
-
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded 
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) {
+    export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) {
         const docFilters = Cast(container._docFilters, listSpec("string"), []);
         runInAction(() => {
             for (let i = 0; i < docFilters.length; i += 3) {
-                if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) {
+                if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match" || modifiers === "remove")) {
                     if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return;
                     docFilters.splice(i, 3);
                     container._docFilters = new List<string>(docFilters);
@@ -1077,7 +1073,7 @@ export namespace Doc {
             if (typeof modifiers === "string") {
                 if (!docFilters.length && modifiers === "match" && value === undefined) {
                     container._docFilters = undefined;
-                } else {
+                } else if (modifiers !== "remove") {
                     docFilters.push(key);
                     docFilters.push(value);
                     docFilters.push(modifiers);
@@ -1274,7 +1270,6 @@ Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTempla
 Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
 Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
 Scripting.addGlobal(function copyField(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; });
-Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); });
 Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
 Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
 Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
diff --git a/src/fields/List.ts b/src/fields/List.ts
index a9da75abb..1c2b89b01 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,14 +1,16 @@
-import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper";
+import { action, observable, runInAction } from "mobx";
+import { alias, list, serializable } from "serializr";
+import { DocServer } from "../client/DocServer";
+import { Scripting } from "../client/util/Scripting";
+import { afterDocDeserialize, autoObject, Deserializable } from "../client/util/SerializationHelper";
 import { Field } from "./Doc";
-import { setter, getter, deleteProperty, updateFunction } from "./util";
-import { serializable, alias, list } from "serializr";
-import { observable, action, runInAction } from "mobx";
+import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols";
 import { ObjectField } from "./ObjectField";
-import { RefField } from "./RefField";
 import { ProxyField } from "./Proxy";
-import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
-import { DocServer } from "../client/DocServer";
+import { RefField } from "./RefField";
+import { listSpec } from "./Schema";
+import { Cast } from "./Types";
+import { deleteProperty, getter, setter, updateFunction } from "./util";
 
 const listHandlers: any = {
     /// Mutator methods
@@ -328,4 +330,9 @@ class ListImpl<T extends Field> extends ObjectField {
 export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
 export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
 
-Scripting.addGlobal("List", List);
\ No newline at end of file
+Scripting.addGlobal("List", List);
+Scripting.addGlobal(function compareLists(l1: any, l2: any) {
+    const L1 = Cast(l1, listSpec("string"), []);
+    const L2 = Cast(l2, listSpec("string"), []);
+    return L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
+}, "compare two lists");
\ No newline at end of file
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 8ae504f1b..738de09c6 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -3,7 +3,7 @@ import "./ImageUpload.scss";
 import React = require('react');
 import { observer } from 'mobx-react';
 import { observable, action, computed } from 'mobx';
-import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue, returnEmptyFilter } from '../Utils';
+import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue, returnEmptyFilter, returnEmptyDoclist } from '../Utils';
 import { Doc, Opt } from '../fields/Doc';
 import { Cast, FieldValue } from '../fields/Types';
 import { listSpec } from '../fields/Schema';
@@ -88,6 +88,7 @@ export class AudioUpload extends React.Component {
                         rootSelected={returnTrue}
                         removeDocument={undefined}
                         docFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
                         onClick={undefined}
                         ScreenToLocalTransform={Transform.Identity}
                         ContentScaling={returnOne}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 41b997285..05a695147 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -14,7 +14,7 @@ import { action, computed, observable, reaction, trace, runInAction } from 'mobx
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../fields/Doc';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, returnEmptyDoclist } from '../Utils';
 import { Docs, DocumentOptions } from '../client/documents/Documents';
 import { Scripting } from '../client/util/Scripting';
 import { DocumentView } from '../client/views/nodes/DocumentView';
@@ -220,6 +220,7 @@ export class MobileInterface extends React.Component {
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                 />
-- 
cgit v1.2.3-70-g09d2


From a6728f81c8192c910ed1f2b70091c39634aca05c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 24 Aug 2020 11:48:00 -0400
Subject: fixed presentation trails being added to myPresentations. fixed
 properties pane to set title "in place".

---
 src/client/views/MainView.tsx                                      | 2 ++
 src/client/views/collections/CollectionDockingView.tsx             | 3 ---
 src/client/views/collections/CollectionTreeView.tsx                | 4 ++--
 src/client/views/collections/CollectionView.tsx                    | 5 -----
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx    | 3 ---
 src/client/views/collections/collectionFreeForm/PropertiesView.tsx | 2 +-
 src/client/views/nodes/PresBox.tsx                                 | 3 +++
 7 files changed, 8 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9d6bffa60..3958b2a8c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -274,6 +274,7 @@ export class MainView extends React.Component {
     @action
     createNewDashboard = async (id?: string) => {
         const myCatalog = Doc.UserDoc().myCatalog as Doc;
+        const myPresentations = Doc.UserDoc().myPresentations as Doc;
         const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
         const dashboards = Cast(this.userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
@@ -290,6 +291,7 @@ export class MainView extends React.Component {
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
         Doc.AddDocToList(myCatalog, "data", freeformDoc);
         Doc.AddDocToList(myCatalog, "data", presentation);
+        Doc.AddDocToList(myPresentations, "data", presentation);
         Doc.UserDoc().activePresentation = presentation;
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cc2220f62..4d4bc2a97 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -757,9 +757,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     CollectionDockingView.AddRightSplit(curPres);
                 }
                 DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
-                const myPresentations = Doc.UserDoc().myPresentations as Doc;
-                const presData = DocListCast(myPresentations.data);
-                if (!presData.includes(curPres)) Doc.AddDocToList(myPresentations, "data", curPres);
             }
         }
     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 9460095e4..c43349059 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -736,8 +736,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) {
-            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
+        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myInactiveDocs) {
+            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myInactiveDocs = new List<Doc>(), icon: "plus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 97336798b..1ad63c34e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -179,11 +179,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         doc.context = this.props.Document;
                     });
                     added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
-                    const myPresentations = Doc.UserDoc().myPresentations as Doc;
-                    added.map(add => {
-                        if (add.type === DocumentType.PRES) Doc.AddDocToList(myPresentations, "data", add);
-                    });
-                    // targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
                     (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
                     targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                     const lastModified = "lastModified";
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d161f49f7..b8019e37b 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -400,9 +400,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             if (!DocumentManager.Instance.getDocumentView(curPres)) {
                 CollectionDockingView.AddRightSplit(curPres);
             }
-            const myPresentations = Doc.UserDoc().myPresentations as Doc;
-            const presData = DocListCast(myPresentations.data);
-            if (!presData.includes(curPres)) Doc.AddDocToList(myPresentations, "data", curPres);
             if (e instanceof KeyboardEvent ? e.key === "c" : true) {
                 const x = this.Bounds.left + this.Bounds.width / 2;
                 const y = this.Bounds.top + this.Bounds.height / 2;
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index 1bfc6bdce..c4f8f4d44 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -436,7 +436,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @action
     setTitle = (value: string) => {
         if (this.dataDoc) {
-            this.selectedDoc && (this.selectedDoc.title = value);
+            this.selectedDoc && Doc.SetInPlace(this.selectedDoc, "title", value, true);
             KeyValueBox.SetField(this.dataDoc, "title", value, true);
             return true;
         }
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 1228a285e..9070bf2ae 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -107,6 +107,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.rootDoc._replacedChrome = "replaced";
         this.layoutDoc.presStatus = "edit";
         this.layoutDoc._gridGap = 5;
+        if (!DocListCast((Doc.UserDoc().myPresentations as Doc).data).includes(this.rootDoc)) {
+            Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc);
+        }
     }
 
     updateCurrentPresentation = () => {
-- 
cgit v1.2.3-70-g09d2


From 9f9390859fd974135de4fd2ff1fecd9f51d569dc Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 24 Aug 2020 12:45:57 -0400
Subject: added alternate doc button bar to settings.  changed context menu
 icon to bars from cog.

---
 src/client/util/SettingsManager.tsx                 | 7 ++++++-
 src/client/views/DocumentButtonBar.tsx              | 4 ++--
 src/client/views/DocumentDecorations.tsx            | 4 ++--
 src/client/views/collections/CollectionTreeView.tsx | 2 +-
 src/client/views/nodes/DocumentView.tsx             | 2 +-
 5 files changed, 12 insertions(+), 7 deletions(-)

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

diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index e9befc465..23ede2e0a 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -85,7 +85,7 @@ export class SettingsManager extends React.Component<{}> {
 
         return <div className="preferences-content">
             <div className="preferences-color">
-                <div className="preferences-color-text">Background Color</div>
+                <div className="preferences-color-text">Back. Color</div>
                 {colorFlyout}
             </div>
             <div className="preferences-font">
@@ -100,6 +100,11 @@ export class SettingsManager extends React.Component<{}> {
                     Show title
                     <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = !Doc.UserDoc().showTitle} checked={BoolCast(Doc.UserDoc().showTitle)} />
                 </div>
+                <div>
+                    Alt Btns
+                    <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"]}
+                        checked={BoolCast(Doc.UserDoc()["documentLinksButton-hideEnd"])} />
+                </div>
             </div>
         </div>;
     }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 659adebbc..ed5ba9e71 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -310,9 +310,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
-            {/* <div className="documentButtonBar-button">
+            {!Doc.UserDoc()["documentLinksButton-hideEnd"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
-            </div> */}
+            </div>}
             <div className="documentButtonBar-button">
                 {this.moreButton}
             </div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7b665ccd3..19b9f20d9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -587,7 +587,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         const maximizeIcon = minimal ? (
             <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top">
                 <div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}>
-                    <FontAwesomeIcon size="lg" icon="cog" />
+                    <FontAwesomeIcon size="lg" icon="bars" />
                 </div></Tooltip>) : canDelete ? (
                     <Tooltip title={<><div className="dash-tooltip">Close</div></>} placement="top">
                         <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
@@ -614,7 +614,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             </> :
             <>
                 {minimal ? (null) : <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
-                    <FontAwesomeIcon size="lg" icon="cog" />
+                    <FontAwesomeIcon size="lg" icon="bars" />
                 </div></Tooltip>}
                 <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
                     <span style={{ width: "100%", display: "inline-block", cursor: "move" }}>{`${this.selectionTitle}`}</span>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c43349059..2f536a464 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -421,7 +421,7 @@ class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const headerElements = this.props.treeViewHideHeaderFields() ? (null) :
             <>
-                <FontAwesomeIcon icon="cog" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
+                <FontAwesomeIcon icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
                 <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
                     onPointerDown={action(() => {
                         if (this.treeViewOpen) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 80d83c3cb..767a4504d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -771,7 +771,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
             }
             moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
-            Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc()["sidebar-userDoc"], Doc, null)) && moreItems.push({ description: "Toggle Always Show Link End", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
+            Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc()["sidebar-userDoc"], Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
         }
 
         const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
-- 
cgit v1.2.3-70-g09d2


From 385fc8bbcb36b5f062f0202a4fb74bcfba00b150 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 24 Aug 2020 17:46:23 -0400
Subject: simplified sidebar document structure.  changed treeViewHideTitle to
 treeViewHideTopDoc along with semantics to match.  fixed linkBox  to work
 with updates.

---
 src/client/documents/Documents.ts                  |  10 +-
 src/client/util/CurrentUserUtils.ts                | 134 +++++++--------------
 src/client/util/GroupManager.tsx                   |   2 +-
 src/client/util/SharingManager.tsx                 |   4 +-
 src/client/views/MainView.tsx                      |  21 +---
 src/client/views/OverlayView.tsx                   |   4 +-
 .../views/collections/CollectionDockingView.tsx    |   3 +-
 src/client/views/collections/CollectionMenu.tsx    |   9 +-
 .../views/collections/CollectionTreeView.tsx       |  93 +++++++-------
 src/client/views/collections/CollectionView.tsx    |   3 +-
 .../collectionFreeForm/PropertiesView.tsx          |  26 ++--
 src/client/views/nodes/DocumentView.tsx            |  19 ++-
 src/client/views/nodes/LinkBox.tsx                 |   2 +
 src/client/views/nodes/PresBox.tsx                 |   6 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   2 +-
 src/mobile/AudioUpload.tsx                         |   2 +-
 src/mobile/MobileInterface.tsx                     |  10 +-
 17 files changed, 156 insertions(+), 194 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e4c704ce7..db32dbaec 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -187,7 +187,7 @@ export interface DocumentOptions {
     cloneFieldFilter?: List<string>; // fields not to copy when the document is cloned
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
     treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
-    treeViewHideTitle?: boolean; // whether to hide the title of a tree view
+    treeViewHideTopLevel?: boolean; // whether to hide the top document of a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewOpen?: boolean; // whether this document is expanded in a tree view
     treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
@@ -682,17 +682,19 @@ export namespace Docs {
 
         export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
             const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, {
-                isLinkButton: true, treeViewHideTitle: true, treeViewOpen: false, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area
-                removeDropProperties: new List(["isBackground", "isLinkButton"]), ...options
+                dontRegisterChildViews: true,
+                isLinkButton: true, treeViewHideTopLevel: true, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area
+                treeViewExpandedView: "fields", removeDropProperties: new List(["isBackground", "isLinkButton"]), ...options
             }, id);
             const linkDocProto = Doc.GetProto(doc);
+            linkDocProto.treeViewOpen = true;// setting this in the instance creator would set it on the view document. 
             linkDocProto.anchor1 = source.doc;
             linkDocProto.anchor2 = target.doc;
             linkDocProto.anchor1_timecode = source.doc._currentTimecode || source.doc.displayTimecode;
             linkDocProto.anchor2_timecode = target.doc._currentTimecode || target.doc.displayTimecode;
 
             if (linkDocProto.linkBoxExcludedKeys === undefined) {
-                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
+                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTopDoc", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
                 Cast(linkDocProto.proto, Doc, null).layoutKey = undefined;
             }
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index be584c3cf..a4cfd67fb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -218,7 +218,7 @@ export class CurrentUserUtils {
 
             const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
             const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
-            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true };
+            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTopDoc: true, _pivotField: "title", system: true };
 
             const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
             descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
@@ -516,22 +516,22 @@ export class CurrentUserUtils {
     }[] {
         this.setupSharingSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
         return [
-            { title: "Dashboards", target: Cast(doc["sidebar-dashboards"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+            { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+            { title: "Inactive", target: Cast(doc.myInactiveDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+            { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+            { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
+            { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
+            { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
-            { title: "Inactive", target: Cast(doc["sidebar-inactiveDocs"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
-            { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
-            { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
-            { title: "Tools", target: Cast(doc["sidebar-tools"], Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
-            { title: "Pres. Trails", target: Cast(doc["sidebar-presentations"], Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
             { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
-            { title: "User Doc", target: Cast(doc["sidebar-userDoc"], Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
+            { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
         ];
     }
 
     static setupSearchPanel(doc: Doc) {
-        if (doc["search-panel"] === undefined) {
-            doc["search-panel"] = new PrefetchProxy(Docs.Create.SearchDocument({
+        if (doc.mySearchPanelDoc === undefined) {
+            doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({
                 _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
                 childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, _chromeStatus: "disabled", title: "sidebar search stack", system: true
             })) as any as Doc;
@@ -722,12 +722,12 @@ export class CurrentUserUtils {
             doc.myColorPicker = new PrefetchProxy(color);
         }
 
-        if (doc["sidebar-tools"] === undefined) {
+        if (doc.myTools === undefined) {
             const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], {
-                title: "sidebar-tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, forceActive: true, system: true
+                title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, forceActive: true, system: true
             })) as any as Doc;
 
-            doc["sidebar-tools"] = toolsStack;
+            doc.myTools = toolsStack;
         }
     }
 
@@ -736,22 +736,14 @@ export class CurrentUserUtils {
         await doc.myDashboards;
         if (doc.myDashboards === undefined) {
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "DASHBOARDS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true,
-                treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
-            }));
-        }
-        if (doc["sidebar-dashboards"] === undefined) {
-            const newDashboard = ScriptField.MakeScript(`createNewDashboard()`);
-            (doc.myDashboards as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
-            (doc.myDashboards as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
-
-            const dashboards = doc.myDashboards as Doc;
-
-            doc["sidebar-dashboards"] = new PrefetchProxy(Docs.Create.TreeDocument([dashboards], {
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+                title: "My Dashboards", _height: 400,
+                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
-            })) as any as Doc;
+            }));
+            const newDashboard = ScriptField.MakeScript(`createNewDashboard()`);
+            (doc.myDashboards as any as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
+            (doc.myDashboards as any as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
         }
         return doc.myDashboards as any as Doc;
     }
@@ -760,79 +752,42 @@ export class CurrentUserUtils {
         await doc.myPresentations;
         if (doc.myPresentations === undefined) {
             doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "PRESENTATION TRAILS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
-            }));
-        }
-
-        if (doc["sidebar-presentations"] === undefined) {
-            const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
-            (doc.myPresentations as Doc).contextMenuScripts = new List<ScriptField>([newPresentations!]);
-            (doc.myPresentations as Doc).contextMenuLabels = new List<string>(["Create New Presentation"]);
-            const presentations = doc.myPresentations as Doc;
-
-            doc["sidebar-presentations"] = new PrefetchProxy(Docs.Create.TreeDocument([presentations], {
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+                title: "My Presentations", _height: 100,
+                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
-            })) as any as Doc;
+            }));
+            const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
+            (doc.myPresentations as any as Doc).contextMenuScripts = new List<ScriptField>([newPresentations!]);
+            (doc.myPresentations as any as Doc).contextMenuLabels = new List<string>(["Create New Presentation"]);
+            const presentations = doc.myPresentations as any as Doc;
         }
         return doc.myPresentations as any as Doc;
     }
 
-    static setupCatalog(doc: Doc) {
-        doc.myCatalog === undefined;
-        if (doc.myCatalog === undefined) {
-            doc.myCatalog = new PrefetchProxy(Docs.Create.SchemaDocument([], [], {
-                title: "CATALOG", _height: 1000, _fitWidth: true, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false,
-                childDropAction: "alias", targetDropAction: "same", _stayInCollection: true, treeViewOpen: true, system: true
-            }));
-        }
-
-        if (doc["sidebar-catalog"] === undefined) {
-            const catalog = doc.myCatalog as Doc;
-
-            doc["sidebar-catalog"] = new PrefetchProxy(Docs.Create.TreeDocument([catalog], {
-                title: "sidebar-catalog",
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
-                lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
-            })) as any as Doc;
-        }
-    }
     static setupInactiveDocs(doc: Doc) {
         // setup Recently Closed library item
         doc.myInactiveDocs === undefined;
         if (doc.myInactiveDocs === undefined) {
             doc.myInactiveDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "CLOSED DOCS", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true,
-                treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data",
-            }));
-        }
-        // this is equivalent to using PrefetchProxies to make sure the inactiveDocs doc is ready
-        PromiseValue(Cast(doc.myInactiveDocs, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
-        if (doc["sidebar-inactiveDocs"] === undefined) {
-            const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
-            (doc.myInactiveDocs as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.myInactiveDocs as Doc).contextMenuLabels = new List<string>(["Clear All"]);
-
-            const inactiveDocs = doc.myInactiveDocs as Doc;
-
-            doc["sidebar-inactiveDocs"] = new PrefetchProxy(Docs.Create.TreeDocument([inactiveDocs], {
-                title: "sidebar-inactiveDocs",
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+                title: "Inactive", _height: 500,
+                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
-            })) as any as Doc;
+            }));
+            const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+            (doc.myInactiveDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+            (doc.myInactiveDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
         }
     }
 
 
     static setupUserDoc(doc: Doc) {
-        if (doc["sidebar-userDoc"] === undefined) {
+        if (doc.myUserDoc === undefined) {
             doc.treeViewOpen = true;
             doc.treeViewExpandedView = "fields";
-            doc["sidebar-userDoc"] = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "sidebar-userDoc",
+            doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
+                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             })) as any as Doc;
@@ -854,7 +809,6 @@ export class CurrentUserUtils {
         CurrentUserUtils.setupSidebarContainer(doc);
         await CurrentUserUtils.setupToolsBtnPanel(doc);
         CurrentUserUtils.setupDashboards(doc);
-        CurrentUserUtils.setupCatalog(doc);
         CurrentUserUtils.setupPresentations(doc);
         CurrentUserUtils.setupInactiveDocs(doc);
         CurrentUserUtils.setupUserDoc(doc);
@@ -884,8 +838,8 @@ export class CurrentUserUtils {
     }
     // sets up the default set of documents to be shown in the Overlay layer
     static setupOverlays(doc: Doc) {
-        if (doc.myOverlayDocuments === undefined) {
-            doc.myOverlayDocuments = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true }));
+        if (doc.myOverlayDocs === undefined) {
+            doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true }));
         }
     }
 
@@ -900,20 +854,20 @@ export class CurrentUserUtils {
 
     // Sharing sidebar is where shared documents are contained
     static setupSharingSidebar(doc: Doc) {
-        if (doc["sidebar-sharing"] === undefined) {
-            doc["sidebar-sharing"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Shared Documents", childDropAction: "alias", system: true, _yMargin: 30, _showTitle: "title", ignoreClick: true, lockedPosition: true }));
+        if (doc.mySharedDocs === undefined) {
+            doc.mySharedDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, _yMargin: 30, _showTitle: "title", ignoreClick: true, lockedPosition: true }));
         }
     }
 
     // Import sidebar is where shared documents are contained
     static setupImportSidebar(doc: Doc) {
-        if (doc["sidebar-import-documents"] === undefined) {
-            doc["sidebar-import-documents"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Imported Documents", forceActive: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 30, lockedPosition: true, _chromeStatus: "disabled", system: true }));
+        if (doc.myImportDocs === undefined) {
+            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My ImportDocuments", forceActive: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 30, lockedPosition: true, _chromeStatus: "disabled", system: true }));
         }
-        if (doc["sidebar-import"] === undefined) {
-            const uploads = Cast(doc["sidebar-import-documents"], Doc, null);
+        if (doc.myImportPanel === undefined) {
+            const uploads = Cast(doc.myImportDocs, Doc, null);
             const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _backgroundColor: "black", title: "Import", icon: "upload", system: true });
-            doc["sidebar-import"] = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "Imported Documents", _yMargin: 20, ignoreClick: true, lockedPosition: true, system: true }));
+            doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, lockedPosition: true, system: true }));
         }
     }
 
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 612ca7be7..314640012 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -66,7 +66,7 @@ export class GroupManager extends React.Component<{}> {
             const evaluating = raw.map(async user => {
                 const userDocument = await DocServer.GetRefField(user.userDocumentId);
                 if (userDocument instanceof Doc) {
-                    const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
+                    const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
                     runInAction(() => {
                         if (notificationDoc instanceof Doc) {
                             this.users.push(user.email);
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a73cb63d0..538c01780 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -115,7 +115,7 @@ export class SharingManager extends React.Component<{}> {
     }
 
     /**
-     * Populates the list of validated users (this.users) by adding registered users which have a sidebar-sharing.
+     * Populates the list of validated users (this.users) by adding registered users which have a mySharedDocs.
      */
     populateUsers = async () => {
         if (!this.populating) {
@@ -128,7 +128,7 @@ export class SharingManager extends React.Component<{}> {
                 if (isCandidate) {
                     const userDocument = await DocServer.GetRefField(user.userDocumentId);
                     if (userDocument instanceof Doc) {
-                        const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
+                        const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
                         runInAction(() => {
                             if (notificationDoc instanceof Doc) {
                                 this.users.push({ user, notificationDoc });
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 22dc023bb..b036adcd4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -83,7 +83,7 @@ export class MainView extends React.Component {
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
-    @computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; }
+    @computed public get searchDoc() { return Cast(this.userDoc.mySearchPanelDoc, Doc) as Doc; }
 
     @observable public sidebarContent: any = this.userDoc?.sidebar;
     @observable public panelContent: string = "none";
@@ -273,7 +273,6 @@ export class MainView extends React.Component {
 
     @action
     createNewDashboard = async (id?: string) => {
-        const myCatalog = Doc.UserDoc().myCatalog as Doc;
         const myPresentations = Doc.UserDoc().myPresentations as Doc;
         const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
         const dashboards = Cast(this.userDoc.myDashboards, Doc) as Doc;
@@ -288,9 +287,7 @@ export class MainView extends React.Component {
             title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
-        Doc.AddDocToList(myCatalog, "data", freeformDoc);
-        Doc.AddDocToList(myCatalog, "data", presentation);
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
         Doc.AddDocToList(myPresentations, "data", presentation);
         Doc.UserDoc().activePresentation = presentation;
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
@@ -359,16 +356,9 @@ export class MainView extends React.Component {
     getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
 
     defaultBackgroundColors = (doc: Opt<Doc>, renderDepth: number) => {
-        if (this.panelContent === doc?.title) return "lightgrey";
-
         if (doc?.type === DocumentType.COL) {
-            if (doc.title === "Basic Item Creators" || doc.title === "sidebar-tools"
-                || doc.title === "sidebar-inactiveDocs" || doc.title === "sidebar-catalog"
-                || doc.title === "Mobile Uploads" || doc.title === "COLLECTION_PROTO"
-                || doc.title === "Advanced Item Prototypes" || doc.title === "all Creators") {
-                return "lightgrey";
-            }
-            return StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
+            const system = Object.getOwnPropertyNames(doc).indexOf("system") !== -1;
+            return system ? "lightgrey" : StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
         }
         if (this.darkScheme) {
             switch (doc?.type) {
@@ -573,7 +563,6 @@ export class MainView extends React.Component {
                     SearchBox.Instance.newsearchstring = "";
                     SearchBox.Instance.enter(undefined);
                     break;
-                // panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break;
                 default:
                     panelDoc = button.target as any; break;
             }
@@ -932,7 +921,7 @@ export class MainView extends React.Component {
     }
 
     importDocument = () => {
-        const sidebar = Cast(Doc.UserDoc()["sidebar-import-documents"], Doc, null);
+        const sidebar = Cast(Doc.UserDoc().myImportDocs, Doc, null);
         const sidebarDocView = DocumentManager.Instance.getDocumentView(sidebar);
         const input = document.createElement("input");
         input.type = "file";
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 001135340..5188ab9a8 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -145,7 +145,7 @@ export class OverlayView extends React.Component {
 
 
     @computed get overlayDocs() {
-        const userDocOverlays = Doc.UserDoc().myOverlayDocuments;
+        const userDocOverlays = Doc.UserDoc().myOverlayDocs;
         if (!userDocOverlays) {
             return null;
         }
@@ -165,7 +165,7 @@ export class OverlayView extends React.Component {
                     dragData.dropAction = "move";
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
                         const docs = (doc instanceof Doc) ? [doc] : doc;
-                        docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null), "data", d));
+                        docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocs, Doc, null), "data", d));
                         return true;
                     };
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cae7d0ca1..9ed3247ae 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -188,6 +188,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @action
     public static AddRightSplit(document: Doc, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
+        if (document._viewType === CollectionViewType.Docking) return MainView.Instance.openDashboard(document);
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
@@ -854,7 +855,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
-        if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
+        if (doc._viewType === CollectionViewType.Docking) {
             return MainView.Instance.openDashboard(doc);
         } else if (location === "onRight") {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 4137b6c27..3004176a0 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -186,8 +186,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
         initialize: (button: Doc) => {
-            button['target-docFilters'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters as any as ObjectField) : undefined;
-            button['target-searchFilterDocs'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs as any as ObjectField) : undefined;
+            button['target-docFilters'] = Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters as any as ObjectField) : undefined;
+            button['target-searchFilterDocs'] = Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._searchFilterDocs as any as ObjectField) : undefined;
         },
     };
 
@@ -424,10 +424,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     get aliasButton() {
         const targetDoc = this.selectedDoc;
         return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
-            <button className="antidmodeMenu-button"
-                ref={this._dragRef}
-                onPointerDown={this.onAliasButtonDown}
-                onClick={this.onAlias}>
+            <button className="antimodeMenu-button" ref={this._dragRef} onPointerDown={this.onAliasButtonDown} onClick={this.onAlias}>
                 <FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" />
             </button>
         </Tooltip>;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2f536a464..c5ea02a2f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -64,6 +64,8 @@ export interface TreeViewProps {
     onCheckedClick?: () => ScriptField;
     onChildClick?: () => ScriptField;
     ignoreFields?: string[];
+    firstLevel: boolean;
+    forceOpen: boolean;
 }
 
 @observer
@@ -77,6 +79,7 @@ export interface TreeViewProps {
  */
 class TreeView extends React.Component<TreeViewProps> {
     private _editTitleScript: (() => ScriptField) | undefined;
+    private _openScript: (() => ScriptField) | undefined;
     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
     private _treedropDisposer?: DragManager.DragDropDisposer;
     private _dref = React.createRef<HTMLDivElement>();
@@ -96,7 +99,7 @@ class TreeView extends React.Component<TreeViewProps> {
         if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
         else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
     }
-    @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
+    @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen || this.props.forceOpen; }
     @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
     @computed get dataDoc() { return this.doc[DataSym]; }
@@ -130,8 +133,10 @@ class TreeView extends React.Component<TreeViewProps> {
 
     constructor(props: any) {
         super(props);
-        const script = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
-        this._editTitleScript = script && (() => script);
+        const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
+        const openScript = ScriptField.MakeScript(`openOnRight(self)`);
+        this._editTitleScript = titleScript && (() => titleScript);
+        this._openScript = openScript && (() => openScript);
         if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
     }
 
@@ -280,7 +285,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                    [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields);
+                    [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, false);
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -322,7 +327,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
             const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
-            return <ul key={expandKey + "more"} onClick={(e) => {
+            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTopDoc ? "no-indent" : ""} onClick={(e) => {
                 this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
                 e.stopPropagation();
             }}>
@@ -331,7 +336,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                        [...this.props.renderedIds, this.doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
+                        [...this.props.renderedIds, this.doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, false)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
@@ -409,10 +414,11 @@ class TreeView extends React.Component<TreeViewProps> {
         this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
     }
     focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
-    contextMenuItems = () => [{ script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
+    contextMenuItems = () => [{ script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }, { script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }];
     truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
     showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.editTitleScript));
+    onChildDoubleClick = () => this._openScript?.() || ScriptCast(this.doc.editTitleScript);
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -449,6 +455,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 rootSelected={returnTrue}
                 pinToPres={emptyFunction}
                 onClick={this.onChildClick}
+                onDoubleClick={this.onChildDoubleClick}
                 dropAction={this.props.dropAction}
                 moveDocument={this.move}
                 removeDocument={this.removeDoc}
@@ -482,9 +489,6 @@ class TreeView extends React.Component<TreeViewProps> {
                 {view}
             </div >
             {headerElements}
-            <div className="treeViewItem-openRight" onClick={this.openRight}>
-                <FontAwesomeIcon title="open in a new pane" icon="external-link-alt" size="sm" />
-            </div>
         </>;
     }
 
@@ -502,30 +506,31 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         } else this._editMaxWidth = "";
-        return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
-            <li className="collection-child">
-                <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
-                    if (this.props.active(true)) {
-                        e.stopPropagation();
-                        e.preventDefault();
-                        SelectionManager.DeselectAll();
-                    }
-                }}
-                    onPointerDown={e => {
+        return this.doc.treeViewHideTopDoc && this.props.firstLevel ? !this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
+            <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
+                <li className="collection-child">
+                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
                         if (this.props.active(true)) {
                             e.stopPropagation();
                             e.preventDefault();
+                            SelectionManager.DeselectAll();
                         }
                     }}
-                    onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                    {this.renderBullet}
-                    {this.renderTitle}
-                </div>
-                <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
-                    {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
-                </div>
-            </li>
-        </div>;
+                        onPointerDown={e => {
+                            if (this.props.active(true)) {
+                                e.stopPropagation();
+                                e.preventDefault();
+                            }
+                        }}
+                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                        {this.renderBullet}
+                        {this.renderTitle}
+                    </div>
+                    <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+                        {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
+                    </div>
+                </li>
+            </div>;
     }
     public static GetChildElements(
         childDocs: Doc[],
@@ -554,7 +559,9 @@ class TreeView extends React.Component<TreeViewProps> {
         libraryPath: Doc[] | undefined,
         onCheckedClick: undefined | (() => ScriptField),
         onChildClick: undefined | (() => ScriptField),
-        ignoreFields: string[] | undefined
+        ignoreFields: string[] | undefined,
+        firstLevel: boolean,
+        forceOpen: boolean
     ) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -663,13 +670,15 @@ class TreeView extends React.Component<TreeViewProps> {
                 treeViewHideHeaderFields={treeViewHideHeaderFields}
                 treeViewPreventOpen={treeViewPreventOpen}
                 renderedIds={renderedIds}
-                ignoreFields={ignoreFields} />;
+                ignoreFields={ignoreFields}
+                firstLevel={firstLevel}
+                forceOpen={forceOpen} />;
         });
     }
 }
 
 export type collectionTreeViewProps = {
-    treeViewHideTitle?: boolean;
+    treeViewHideTopDoc?: boolean;
     treeViewHideHeaderFields?: boolean;
     onCheckedClick?: () => ScriptField;
     onChildClick?: () => ScriptField;
@@ -745,7 +754,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
+            layoutItems.push({ description: (this.doc.treeViewHideTopDoc ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTopDoc = !this.doc.treeViewHideTopDoc, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
         }
@@ -817,6 +826,12 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
         const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
+        const childElements = childDocs && TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
+            moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+            this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
+            BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
+            this.onChildClick, this.props.ignoreFields, true, BoolCast(this.props.Document.treeViewOpen));
+        const hideTitle = this.props.treeViewHideTopDoc || this.doc.treeViewHideTopDoc;
         return !childDocs ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget" id="body"
@@ -830,7 +845,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                     onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                     onDrop={this.onTreeDrop}
                     ref={this.createTreeDropTarget}>
-                    {this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : <EditableView
+                    {hideTitle ? (null) : <EditableView
                         contents={this.dataDoc.title}
                         editing={false}
                         display={"block"}
@@ -845,15 +860,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                             this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
                         })} />}
                     {this.doc.allowClear ? this.renderClearButton : (null)}
-                    <ul className="no-indent" style={{ width: "max-content" }} >
-                        {
-                            TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
-                                moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
-                                this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
-                                BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
-                                this.onChildClick, this.props.ignoreFields)
-                        }
-                    </ul>
+                    <ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul>
                 </div >
             </div>
         );
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1ad63c34e..38809e1e8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -178,7 +178,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         doc._stayInCollection = undefined;
                         doc.context = this.props.Document;
                     });
-                    added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
                     (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
                     targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                     const lastModified = "lastModified";
@@ -550,7 +549,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     bringToFront={emptyFunction}
                     active={this.props.active}
                     whenActiveChanged={returnFalse}
-                    treeViewHideTitle={true}
+                    treeViewHideTopDoc={true}
                     ContentScaling={returnOne}
                     focus={returnFalse}
                     treeViewHideHeaderFields={true}
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index 44101e958..c5d396859 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Checkbox, Tooltip } from "@material-ui/core";
-import { action, computed, observable, runInAction } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { ColorState, SketchPicker } from "react-color";
 import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, Field, HeightSym, WidthSym } from "../../../../fields/Doc";
@@ -843,10 +843,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <div className="propertiesView-name">
                         {this.editableTitle}
                     </div>
-                    <div className="propertiesView-settings" onPointerEnter={() => runInAction(() => { this.inOptions = true; })}
+                    <div className="propertiesView-settings" onPointerEnter={action(() => this.inOptions = true)}
                         onPointerLeave={action(() => this.inOptions = false)}>
                         <div className="propertiesView-settings-title"
-                            onPointerDown={() => runInAction(() => { this.openOptions = !this.openOptions; })}
+                            onPointerDown={action(() => this.openOptions = !this.openOptions)}
                             style={{ backgroundColor: this.openOptions ? "black" : "" }}>
                             Options
                         <div className="propertiesView-settings-title-icon">
@@ -860,7 +860,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>
                     <div className="propertiesView-sharing">
                         <div className="propertiesView-sharing-title"
-                            onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}
+                            onPointerDown={action(() => this.openSharing = !this.openSharing)}
                             style={{ backgroundColor: this.openSharing ? "black" : "" }}>
                             Sharing {"&"} Permissions
                         <div className="propertiesView-sharing-title-icon">
@@ -914,7 +914,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     {!this.isInk ? (null) :
                         <div className="propertiesView-appearance">
                             <div className="propertiesView-appearance-title"
-                                onPointerDown={() => runInAction(() => { this.openAppearance = !this.openAppearance; })}
+                                onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
                                 style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
                                 Appearance
                             <div className="propertiesView-appearance-title-icon">
@@ -929,7 +929,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
                     {this.isInk ? <div className="propertiesView-transform">
                         <div className="propertiesView-transform-title"
-                            onPointerDown={() => runInAction(() => { this.openTransform = !this.openTransform; })}
+                            onPointerDown={action(() => this.openTransform = !this.openTransform)}
                             style={{ backgroundColor: this.openTransform ? "black" : "" }}>
                             Transform
                         <div className="propertiesView-transform-title-icon">
@@ -943,7 +943,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
                     <div className="propertiesView-fields">
                         <div className="propertiesView-fields-title"
-                            onPointerDown={() => runInAction(() => { this.openFields = !this.openFields; })}
+                            onPointerDown={action(() => this.openFields = !this.openFields)}
                             style={{ backgroundColor: this.openFields ? "black" : "" }}>
                             Fields {"&"} Tags
                             <div className="propertiesView-fields-title-icon">
@@ -961,10 +961,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>
                     <div className="propertiesView-layout">
                         <div className="propertiesView-layout-title"
-                            onPointerDown={() => runInAction(() => { this.openLayout = !this.openLayout; })}
+                            onPointerDown={action(() => this.openLayout = !this.openLayout)}
                             style={{ backgroundColor: this.openLayout ? "black" : "" }}>
                             Layout
-                        <div className="propertiesView-layout-title-icon" onPointerDown={() => runInAction(() => { this.openLayout = !this.openLayout; })}>
+                        <div className="propertiesView-layout-title-icon">
                                 <FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
                             </div>
                         </div>
@@ -1002,7 +1002,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>}
                     {!selectedItem ? (null) : <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
-                            onPointerDown={() => runInAction(() => { 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">
@@ -1015,7 +1015,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>}
                     {!selectedItem ? (null) : <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
-                            onPointerDown={() => runInAction(() => { this.openSlideOptions = !this.openSlideOptions; })}
+                            onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })}
                             style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>
                             &nbsp; <FontAwesomeIcon icon={"cog"} /> &nbsp; {PresBox.Instance.stringType} options
                         <div className="propertiesView-presTrails-title-icon">
@@ -1028,7 +1028,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>}
                     <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
-                            onPointerDown={() => runInAction(() => { this.openAddSlide = !this.openAddSlide; })}
+                            onPointerDown={action(() => { this.openAddSlide = !this.openAddSlide; })}
                             style={{ backgroundColor: this.openAddSlide ? "black" : "" }}>
                             &nbsp; <FontAwesomeIcon icon={"plus"} /> &nbsp; Add new slide
                         <div className="propertiesView-presTrails-title-icon">
@@ -1041,7 +1041,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>
                     {/* <div className="propertiesView-sharing">
                         <div className="propertiesView-sharing-title"
-                            onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}
+                            onPointerDown={acition(() => { this.openSharing = !this.openSharing; })}
                             style={{ backgroundColor: this.openSharing ? "black" : "" }}>
                             Sharing {"&"} Permissions
                         <div className="propertiesView-sharing-title-icon">
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 767a4504d..9ab2889e1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -286,6 +286,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
+    _timeout: NodeJS.Timeout | undefined;
+
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
         if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
@@ -293,6 +295,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let preventDefault = true;
             !this.props.Document.isBackground && this.props.bringToFront(this.props.Document);
             if (this._doubleTap && this.props.renderDepth && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
+                if (this._timeout) {
+                    clearTimeout(this._timeout);
+                    this._timeout = undefined;
+                }
                 if (!(e.nativeEvent as any).formattedHandled) {
                     if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
                         const func = () => this.onDoubleClickHandler.script.run({
@@ -324,9 +330,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     documentView: this,
                     shiftKey: e.shiftKey
                 }, console.log);
-                if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
-                    UndoManager.RunInBatch(func, "on click");
-                } else func();
+                const clickFunc = () => {
+                    if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
+                        UndoManager.RunInBatch(func, "on click");
+                    } else func();
+                };
+                if (this.onDoubleClickHandler) {
+                    this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500);
+                } else clickFunc();
             } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
                 this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight");
             } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
@@ -771,7 +782,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
             }
             moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
-            Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc()["sidebar-userDoc"], Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
+            Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc().myUserDoc, Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
         }
 
         const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 532e7dc15..a067f23af 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -26,6 +26,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
                 NativeWidth={returnZero}
                 ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
                 annotationsKey={""}
+                dontRegisterView={true}
+                renderDepth={this.props.renderDepth + 1}
                 CollectionView={undefined}
                 addDocument={returnFalse}
                 removeDocument={returnFalse}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 6bde94a76..5e20b6f9f 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -436,7 +436,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
         if (this.layoutDoc.inOverlay) {
             this.layoutDoc.presStatus = 'edit';
-            Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+            Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
             CollectionDockingView.AddRightSplit(this.rootDoc);
             this.layoutDoc.inOverlay = false;
         } else if (this.layoutDoc.context && docView) {
@@ -447,7 +447,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             this.rootDoc._height = 35;
             this.rootDoc._width = 250;
             docView.props.removeDocument?.(this.layoutDoc);
-            Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+            Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
         } else {
             this.layoutDoc.presStatus = 'manual';
             const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
@@ -456,7 +456,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             this.rootDoc._height = 35;
             this.rootDoc._width = 250;
             this.props.addDocTab?.(this.rootDoc, "close");
-            Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+            Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
         }
     }
 
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 866e41ee0..5d51c420b 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -73,7 +73,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
                             const spt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
                             imageSummary.x = spt[0];
                             imageSummary.y = spt[1];
-                            Cast(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
+                            Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
                         } else {
                             this.props.addDocument?.(imageSummary);
                         }
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index c412059dd..5e86246c5 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -53,7 +53,7 @@ export class AudioUpload extends React.Component {
      * Pushing the audio doc onto Dash Web through the right side bar
      */
     uploadAudio = () => {
-        const audioRightSidebar = Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
+        const audioRightSidebar = Cast(Doc.UserDoc().mySharedDocs, Doc) as Doc;
         const audioDoc = this._audioCol;
         const data = Cast(audioRightSidebar.data, listSpec(Doc));
         for (let i = 1; i < 8; i++) {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 8ca67f9ee..841862f49 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -402,7 +402,7 @@ export class MobileInterface extends React.Component {
             title: "Collection " + dashboardCount,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Dashboard ${dashboardCount}` }, id, "row");
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
 
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
@@ -440,7 +440,7 @@ export class MobileInterface extends React.Component {
     // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
     @computed get undo() {
         if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.UserDoc()["sidebar-sharing"] && this._activeDoc.title !== "WORKSPACES") {
+            this._activeDoc !== Doc.UserDoc().mySharedDocs && this._activeDoc.title !== "WORKSPACES") {
             return (
                 <div className="docButton"
                     style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
@@ -458,7 +458,7 @@ export class MobileInterface extends React.Component {
     // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
     @computed get redo() {
         if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.UserDoc()["sidebar-sharing"] && this._activeDoc.title !== "WORKSPACES") {
+            this._activeDoc !== Doc.UserDoc().mySharedDocs && this._activeDoc.title !== "WORKSPACES") {
             return (
                 <div className="docButton"
                     style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
@@ -609,7 +609,7 @@ export class MobileInterface extends React.Component {
 
     // Returns the image upload pop up
     @computed get uploadImage() {
-        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
+        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc().mySharedDocs, Doc) as Doc;
         return <Uploader Document={doc} />;
     }
 
@@ -632,7 +632,7 @@ export class MobileInterface extends React.Component {
      */
     @action
     switchToMobileUploads = () => {
-        const mobileUpload = Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
+        const mobileUpload = Cast(Doc.UserDoc().mySharedDocs, Doc) as Doc;
         this.switchCurrentView(mobileUpload);
         this._homeMenu = false;
     }
-- 
cgit v1.2.3-70-g09d2


From 69f94b19baa37a0a9114bc21710d2e09b49b638d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 24 Aug 2020 18:37:46 -0400
Subject: removed treeViewOpen from sidebar panels since they don't need them
 now that the top level is hidden.

---
 src/client/documents/Documents.ts                   |  2 +-
 src/client/util/CurrentUserUtils.ts                 |  8 ++++----
 src/client/views/MainView.tsx                       |  6 ++++--
 src/client/views/collections/CollectionTreeView.tsx | 10 +++++-----
 src/client/views/collections/CollectionView.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx             |  3 ++-
 6 files changed, 17 insertions(+), 14 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 759310241..cef7c5d4d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -694,7 +694,7 @@ export namespace Docs {
             linkDocProto.anchor2_timecode = target.doc._currentTimecode || target.doc.displayTimecode;
 
             if (linkDocProto.linkBoxExcludedKeys === undefined) {
-                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTopDoc", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
+                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTopLevel", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
                 Cast(linkDocProto.proto, Doc, null).layoutKey = undefined;
             }
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index a4cfd67fb..997807ce4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -218,7 +218,7 @@ export class CurrentUserUtils {
 
             const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
             const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
-            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTopDoc: true, _pivotField: "title", system: true };
+            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTopLevel: true, _pivotField: "title", system: true };
 
             const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
             descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
@@ -738,7 +738,7 @@ export class CurrentUserUtils {
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Dashboards", _height: 400,
                 treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const newDashboard = ScriptField.MakeScript(`createNewDashboard()`);
@@ -754,7 +754,7 @@ export class CurrentUserUtils {
             doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Presentations", _height: 100,
                 treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
@@ -772,7 +772,7 @@ export class CurrentUserUtils {
             doc.myInactiveDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "Inactive", _height: 500,
                 treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9189c1951..67d1de2ec 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -293,8 +293,9 @@ export class MainView extends React.Component {
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
         const copyDashboard = ScriptField.MakeScript(`copyDashboard()`);
-        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard"]);
+        const createDashboard = ScriptField.MakeScript(`createDashb oard()`);
+        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyDashboard!, createDashboard!]);
+        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
@@ -1005,5 +1006,6 @@ Scripting.addGlobal(function copyDashboard() {
         setTimeout(() => MainView.Instance.openDashboard(copy), 0);
     });
 });
+Scripting.addGlobal(function createDashboard() { MainView.Instance.createNewDashboard(); });
 Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); },
     "imports files from device directly into the import sidebar");
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c5ea02a2f..585d1f775 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -327,7 +327,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
             const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
-            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTopDoc ? "no-indent" : ""} onClick={(e) => {
+            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTopLevel ? "no-indent" : ""} onClick={(e) => {
                 this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
                 e.stopPropagation();
             }}>
@@ -506,7 +506,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         } else this._editMaxWidth = "";
-        return this.doc.treeViewHideTopDoc && this.props.firstLevel ? !this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
+        return this.doc.treeViewHideTopLevel && this.props.firstLevel ? !this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
             <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
                 <li className="collection-child">
                     <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
@@ -678,7 +678,7 @@ class TreeView extends React.Component<TreeViewProps> {
 }
 
 export type collectionTreeViewProps = {
-    treeViewHideTopDoc?: boolean;
+    treeViewHideTopLevel?: boolean;
     treeViewHideHeaderFields?: boolean;
     onCheckedClick?: () => ScriptField;
     onChildClick?: () => ScriptField;
@@ -754,7 +754,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideTopDoc ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTopDoc = !this.doc.treeViewHideTopDoc, icon: "paint-brush" });
+            layoutItems.push({ description: (this.doc.treeViewHideTopLevel ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTopLevel = !this.doc.treeViewHideTopLevel, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
         }
@@ -831,7 +831,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
             this.onChildClick, this.props.ignoreFields, true, BoolCast(this.props.Document.treeViewOpen));
-        const hideTitle = this.props.treeViewHideTopDoc || this.doc.treeViewHideTopDoc;
+        const hideTitle = this.props.treeViewHideTopLevel || this.doc.treeViewHideTopLevel;
         return !childDocs ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget" id="body"
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 38809e1e8..828cdd282 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -549,7 +549,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     bringToFront={emptyFunction}
                     active={this.props.active}
                     whenActiveChanged={returnFalse}
-                    treeViewHideTopDoc={true}
+                    treeViewHideTopLevel={true}
                     ContentScaling={returnOne}
                     focus={returnFalse}
                     treeViewHideHeaderFields={true}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9ab2889e1..c8ac59bcd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -322,13 +322,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     }
                 }
             } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+                const shiftKey = e.shiftKey;
                 const func = () => this.onClickHandler.script.run({
                     this: this.layoutDoc,
                     self: this.rootDoc,
                     scriptContext: this.props.scriptContext,
                     thisContainer: this.props.ContainingCollectionDoc,
                     documentView: this,
-                    shiftKey: e.shiftKey
+                    shiftKey
                 }, console.log);
                 const clickFunc = () => {
                     if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
-- 
cgit v1.2.3-70-g09d2


From ec89542cbd31aaff9d4f58e9e9bf045e934a8235 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 25 Aug 2020 18:13:39 -0400
Subject: changed system documents to act like dividers in tree views.  removed
 some menu items in cases they don't apply. fixed ink selection to not use
 boudning box.

---
 src/client/util/CurrentUserUtils.ts                | 19 +++--------
 src/client/util/InteractionUtils.tsx               |  2 +-
 src/client/views/ContextMenu.tsx                   |  2 +-
 src/client/views/InkingStroke.tsx                  |  7 ++--
 .../views/collections/CollectionTreeView.scss      |  9 ++++++
 .../views/collections/CollectionTreeView.tsx       | 11 ++++---
 src/client/views/nodes/DocumentView.tsx            | 37 +++++++++++-----------
 7 files changed, 43 insertions(+), 44 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4a51daa9f..e91cf118f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -394,7 +394,7 @@ export class CurrentUserUtils {
     }[] {
         if (doc.emptyPresentation === undefined) {
             doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
+                { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyCollection === undefined) {
@@ -444,30 +444,18 @@ export class CurrentUserUtils {
             { 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 },
-            //  { title: "Drag a webcam", title: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
             { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
             { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc, noviceMode: true },
-
-            { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
+            { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
             { toolTip: "Tap to create a search box in a new pane, drag for a search box", title: "Query", icon: "search", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySearch as Doc },
             { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
-            // { title: "Drag an import folder", title: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
             { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
-            // { title: "Drag an instance of the device collection", title: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
-            // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen,  this)`, activeInkPen: doc },
-            // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
-            // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
-            // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "pink", activeInkPen: doc },
-            // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activeInkPen = this;', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "white", activeInkPen: doc },
             { toolTip: "Tap to create a document previewer in a new pane, drag for a document previewer", title: "Prev", icon: "expand", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyDocHolder as Doc },
             { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
-            { toolTip: "Connect a Google Account", title: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
         ];
 
     }
 
-
-
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
     static async setupCreatorButtons(doc: Doc) {
         let alreadyCreatedButtons: string[] = [];
@@ -496,7 +484,8 @@ export class CurrentUserUtils {
             dragFactory,
             clickFactory,
             userDoc: noviceMode ? undefined as any : doc,
-            hidden: noviceMode ? undefined as any : ComputedField.MakeFunction("self.userDoc.noviceMode"), system: true
+            hidden: noviceMode ? undefined as any : ComputedField.MakeFunction("self.userDoc.noviceMode"),
+            system: true
         }));
 
         if (dragCreatorSet === undefined) {
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index bb58423c8..d1e656e3f 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -210,7 +210,7 @@ export namespace InteractionUtils {
                 points={strpts}
                 style={{
                     filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
-                    fill: fill ? fill : "transparent",
+                    fill: fill ? fill : "none",
                     opacity: strokeWidth !== width ? 0.5 : undefined,
                     pointerEvents: pevents as any,
                     stroke: color ?? "rgb(0, 0, 0)",
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index cb34b8873..349fd077c 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -95,7 +95,7 @@ export class ContextMenu extends React.Component {
     }
     @action
     moveAfter(item: ContextMenuProps, after: ContextMenuProps) {
-        if (this.findByDescription(after.description)) {
+        if (after && this.findByDescription(after.description)) {
             const curInd = this._items.findIndex((i) => i.description === item.description);
             this._items.splice(curInd, 1);
             const afterInd = this._items.findIndex((i) => i.description === after.description);
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index b972d4e41..d47d0d90c 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -113,18 +113,18 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const strokeColor = StrCast(this.layoutDoc.color, "");
 
         const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
-            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
             StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
             StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false);
 
         const hpoints = InteractionUtils.CreatePolyline(data, left, top,
             this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
-            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
             "none", "none", undefined, scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
 
         //points for adding
         const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth,
-            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
             StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
             StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
 
@@ -208,6 +208,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
                     if (cm) {
                         !Doc.UserDoc().noviceMode && cm.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
                         cm.addItem({ description: "Make Mask", event: this.makeMask, icon: "paint-brush" });
+                        cm.addItem({ description: "Edit Points", event: action(() => FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn), icon: "paint-brush" });
                         //cm.addItem({ description: "Format Shape...", event: this.formatShape, icon: "paint-brush" });
                     }
                 }}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index c9bf82406..f96a5c4f0 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -85,6 +85,15 @@
     white-space: pre-wrap;
     min-width: 10px;
 }
+.docContainer-system {
+    font-variant: all-small-caps;
+    border-radius: 5px;
+    background: grey;
+    color: white;
+    padding-left: 3px;
+    padding-right: 3px;
+    padding-bottom: 2px;
+}
 
 .treeViewItem-openRight {
     display: none;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 585d1f775..83f212336 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -135,8 +135,9 @@ class TreeView extends React.Component<TreeViewProps> {
         super(props);
         const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
         const openScript = ScriptField.MakeScript(`openOnRight(self)`);
-        this._editTitleScript = titleScript && (() => titleScript);
-        this._openScript = openScript && (() => openScript);
+        const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
+        this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
+        this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
         if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
     }
 
@@ -414,7 +415,7 @@ class TreeView extends React.Component<TreeViewProps> {
         this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
     }
     focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
-    contextMenuItems = () => [{ script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }, { script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }];
+    contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
     truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
     showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.editTitleScript));
@@ -479,7 +480,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionDoc={this.props.containingCollection}
             />;
         return <>
-            <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
+            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
@@ -488,7 +489,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }} >
                 {view}
             </div >
-            {headerElements}
+            {Doc.IsSystem(this.doc) ? (null) : headerElements}
         </>;
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c8ac59bcd..56791fc81 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -105,17 +105,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _lastTap: number = 0;
     private _doubleTap = false;
     private _mainCont = React.createRef<HTMLDivElement>();
-    private _dropDisposer?: DragManager.DragDropDisposer;
-    private _showKPQuery: boolean = false;
-    private _queries: string = "";
     private _titleRef = React.createRef<EditableView>();
+    private _dropDisposer?: DragManager.DragDropDisposer;
     private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
     private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
+    private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
-    private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
     @computed get topMost() { return this.props.renderDepth === 0; }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@@ -308,7 +306,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                             shiftKey: e.shiftKey
                         }, console.log);
                         func();
-                    } else {
+                    } else if (!Doc.IsSystem(this.props.Document)) {
                         UndoManager.RunInBatch(() => {
                             let fullScreenDoc = this.props.Document;
                             if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
@@ -732,7 +730,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
             cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
         this.props.contextMenuItems?.().forEach(item =>
-            cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+            item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
 
         const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
         const appearance = cm.findByDescription("UI Controls...");
@@ -743,21 +741,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const options = cm.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-        optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+        !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
 
-
-        const existingOnClick = cm.findByDescription("OnClick...");
-        const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-        onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-        onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
-        onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
-        onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
-        !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" });
-        onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
-        onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
-        onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
-        !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+        if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+            const existingOnClick = cm.findByDescription("OnClick...");
+            const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+            onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+            onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
+            onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+            onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
+            !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" });
+            onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
+            onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
+            onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
+            !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+        }
 
         const funcs: ContextMenuProps[] = [];
         if (this.layoutDoc.onDragStart) {
-- 
cgit v1.2.3-70-g09d2


From 0e4da0ffd9ff7eaf571451d8b267c7e708e152d7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 25 Aug 2020 22:24:27 -0400
Subject: changed treeViewHideTopLevel back to treeViewHideTitle.  cleaned up
 some stuff.

---
 src/client/documents/Documents.ts                  |  9 ++--
 src/client/util/CurrentUserUtils.ts                | 10 ++--
 src/client/util/SelectionManager.ts                |  1 -
 src/client/views/EditableView.tsx                  |  2 +-
 src/client/views/MainView.tsx                      |  2 +-
 .../views/collections/CollectionTreeView.tsx       | 53 +++++++++++-----------
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  4 +-
 src/fields/documentSchemas.ts                      |  1 +
 10 files changed, 44 insertions(+), 42 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index cef7c5d4d..b8cac2beb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -187,7 +187,7 @@ export interface DocumentOptions {
     cloneFieldFilter?: List<string>; // fields not to copy when the document is cloned
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
     treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
-    treeViewHideTopLevel?: boolean; // whether to hide the top document of a tree view
+    treeViewHideTitle?: boolean; // whether to hide the top document of a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewOpen?: boolean; // whether this document is expanded in a tree view
     treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
@@ -333,7 +333,7 @@ export namespace Docs {
             }],
             [DocumentType.INK, {
                 layout: { view: InkingStroke, dataField: defaultDataKey },
-                options: { backgroundColor: "transparent" }
+                options: { _fontFamily: "cursive", backgroundColor: "transparent" }
             }],
             [DocumentType.SCREENSHOT, {
                 layout: { view: ScreenshotBox, dataField: defaultDataKey },
@@ -683,7 +683,7 @@ export namespace Docs {
         export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
             const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, {
                 dontRegisterChildViews: true,
-                isLinkButton: true, treeViewHideTopLevel: true, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area
+                isLinkButton: true, treeViewHideTitle: true, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area
                 treeViewExpandedView: "fields", removeDropProperties: new List(["isBackground", "isLinkButton"]), ...options
             }, id);
             const linkDocProto = Doc.GetProto(doc);
@@ -694,7 +694,7 @@ export namespace Docs {
             linkDocProto.anchor2_timecode = target.doc._currentTimecode || target.doc.displayTimecode;
 
             if (linkDocProto.linkBoxExcludedKeys === undefined) {
-                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTopLevel", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
+                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
                 Cast(linkDocProto.proto, Doc, null).layoutKey = undefined;
             }
 
@@ -723,6 +723,7 @@ export namespace Docs {
             I._backgroundColor = "transparent";
             I._width = options._width;
             I._height = options._height;
+            I._fontFamily = "cursive";
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
             I.data = new InkField(points);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e91cf118f..435944a74 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -218,7 +218,7 @@ export class CurrentUserUtils {
 
             const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
             const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
-            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTopLevel: true, _pivotField: "title", system: true };
+            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true };
 
             const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
             descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
@@ -726,7 +726,7 @@ export class CurrentUserUtils {
         if (doc.myDashboards === undefined) {
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Dashboards", _height: 400,
-                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
@@ -742,7 +742,7 @@ export class CurrentUserUtils {
         if (doc.myPresentations === undefined) {
             doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Presentations", _height: 100,
-                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
@@ -760,7 +760,7 @@ export class CurrentUserUtils {
         if (doc.myInactiveDocs === undefined) {
             doc.myInactiveDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "Inactive", _height: 500,
-                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
@@ -776,7 +776,7 @@ export class CurrentUserUtils {
             doc.treeViewOpen = true;
             doc.treeViewExpandedView = "fields";
             doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
-                treeViewHideTopLevel: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc",
+                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             })) as any as Doc;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 35b82cc30..6d4f2c190 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -21,7 +21,6 @@ export namespace SelectionManager {
         }
         @action
         SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
-
             // if doc is not in SelectedDocuments, add it
             if (!manager.SelectedDocuments.get(docView)) {
                 if (!ctrlPressed) {
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 8b2fa9472..1b4b9a2be 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -151,7 +151,7 @@ export class EditableView extends React.Component<EditableProps> {
     @action
     setIsFocused = (value: boolean) => {
         const wasFocused = this._editing;
-        //this._editing = value;
+        this._editing = value;
         return wasFocused !== this._editing;
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 33e316dbe..cfa2534c9 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -293,7 +293,7 @@ export class MainView extends React.Component {
         const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
         const copyDashboard = ScriptField.MakeScript(`copyDashboard()`);
-        const createDashboard = ScriptField.MakeScript(`createDashb oard()`);
+        const createDashboard = ScriptField.MakeScript(`createDashboard()`);
         dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyDashboard!, createDashboard!]);
         dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 83f212336..5a80541be 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -37,7 +37,6 @@ import { TraceMobx } from '../../../fields/util';
 export interface TreeViewProps {
     document: Doc;
     dataDoc?: Doc;
-    libraryPath: Doc[] | undefined;
     containingCollection: Doc;
     prevSibling?: Doc;
     renderDepth: number;
@@ -65,7 +64,6 @@ export interface TreeViewProps {
     onChildClick?: () => ScriptField;
     ignoreFields?: string[];
     firstLevel: boolean;
-    forceOpen: boolean;
 }
 
 @observer
@@ -99,7 +97,7 @@ class TreeView extends React.Component<TreeViewProps> {
         if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
         else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
     }
-    @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen || this.props.forceOpen; }
+    @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
     @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
     @computed get dataDoc() { return this.doc[DataSym]; }
@@ -119,7 +117,7 @@ class TreeView extends React.Component<TreeViewProps> {
             Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
     }
 
-    @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight", this.props.libraryPath);
+    @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight");
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         return this.doc !== target && this.props.deleteDoc(doc) && addDoc(doc);
     }
@@ -137,7 +135,7 @@ class TreeView extends React.Component<TreeViewProps> {
         const openScript = ScriptField.MakeScript(`openOnRight(self)`);
         const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
         this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
-        this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
+        this._openScript = !Doc.IsSystem(this.props.document) && !this.props.treeViewDoc.treeViewOutlineMode ? openScript && (() => openScript) : undefined;
         if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
     }
 
@@ -201,7 +199,7 @@ class TreeView extends React.Component<TreeViewProps> {
         }}
         OnTab={undoBatch((shift?: boolean) => {
             shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
-            setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 0);
+            setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 250);
         })}
     />)
 
@@ -286,7 +284,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                    [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, false);
+                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false);
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -328,7 +326,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
             const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
-            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTopLevel ? "no-indent" : ""} onClick={(e) => {
+            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
                 this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
                 e.stopPropagation();
             }}>
@@ -337,7 +335,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                        [...this.props.renderedIds, this.doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, false)}
+                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
@@ -418,8 +416,8 @@ class TreeView extends React.Component<TreeViewProps> {
     contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
     truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
     showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
-    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.editTitleScript));
-    onChildDoubleClick = () => this._openScript?.() || ScriptCast(this.doc.editTitleScript);
+    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+    onChildDoubleClick = () => this._openScript?.() || ScriptCast(this.doc.treeChildDoubleClick);
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -428,7 +426,7 @@ class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const headerElements = this.props.treeViewHideHeaderFields() ? (null) :
             <>
-                <FontAwesomeIcon icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
+                <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
                 <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
                     onPointerDown={action(() => {
                         if (this.treeViewOpen) {
@@ -450,7 +448,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 Document={this.doc}
                 DataDoc={undefined}
                 treeViewDoc={this.props.treeViewDoc}
-                LibraryPath={this.props.libraryPath || emptyPath}
+                LibraryPath={emptyPath}
                 addDocument={undefined}
                 addDocTab={this.props.addDocTab}
                 rootSelected={returnTrue}
@@ -479,13 +477,15 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.containingCollection}
             />;
+        const activeSel = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+        const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && activeSel?.props.treeViewDoc !== this.props.treeViewDoc ? "none" : undefined;
         return <>
             <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
+                    pointerEvents
                 }} >
                 {view}
             </div >
@@ -507,7 +507,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         } else this._editMaxWidth = "";
-        return this.doc.treeViewHideTopLevel && this.props.firstLevel ? !this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
+        return this.doc.treeViewHideTitle && this.props.firstLevel ? this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
             <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
                 <li className="collection-child">
                     <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
@@ -557,12 +557,10 @@ class TreeView extends React.Component<TreeViewProps> {
         treeViewHideHeaderFields: () => boolean,
         treeViewPreventOpen: boolean,
         renderedIds: string[],
-        libraryPath: Doc[] | undefined,
         onCheckedClick: undefined | (() => ScriptField),
         onChildClick: undefined | (() => ScriptField),
         ignoreFields: string[] | undefined,
         firstLevel: boolean,
-        forceOpen: boolean
     ) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -644,7 +642,6 @@ class TreeView extends React.Component<TreeViewProps> {
             return !(child instanceof Doc) ? (null) : <TreeView
                 document={pair.layout}
                 dataDoc={pair.data}
-                libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined}
                 containingCollection={containingCollection}
                 prevSibling={docs[i]}
                 treeViewDoc={treeViewDoc}
@@ -672,14 +669,13 @@ class TreeView extends React.Component<TreeViewProps> {
                 treeViewPreventOpen={treeViewPreventOpen}
                 renderedIds={renderedIds}
                 ignoreFields={ignoreFields}
-                firstLevel={firstLevel}
-                forceOpen={forceOpen} />;
+                firstLevel={firstLevel} />;
         });
     }
 }
 
 export type collectionTreeViewProps = {
-    treeViewHideTopLevel?: boolean;
+    treeViewHideTitle?: boolean;
     treeViewHideHeaderFields?: boolean;
     onCheckedClick?: () => ScriptField;
     onChildClick?: () => ScriptField;
@@ -755,7 +751,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideTopLevel ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTopLevel = !this.doc.treeViewHideTopLevel, icon: "paint-brush" });
+            layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
         }
@@ -803,6 +799,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         onClicks.push({
             description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
         });
+        onClicks.push({
+            description: `${this.props.Document.treeViewOutlineMode ? "Delay " : "Immediate "} Title Editing`, event: () => UndoManager.RunInBatch(() => this.props.Document.treeViewOutlineMode = !this.props.Document.treeViewOutlineMode, "edit onCheckedClick"), icon: "edit"
+        });
         !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
     }
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
@@ -830,9 +829,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const childElements = childDocs && TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
             this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
-            BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
-            this.onChildClick, this.props.ignoreFields, true, BoolCast(this.props.Document.treeViewOpen));
-        const hideTitle = this.props.treeViewHideTopLevel || this.doc.treeViewHideTopLevel;
+            BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
+            this.onChildClick, this.props.ignoreFields, true);
+        const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
+        const activeSel = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+        const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && activeSel?.props.treeViewDoc !== this.props.Document ? "none" : undefined;
         return !childDocs ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget" id="body"
@@ -841,7 +842,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                         paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
                         paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
                         paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
-                        pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+                        pointerEvents
                     }}
                     onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                     onDrop={this.onTreeDrop}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bf81e4509..a7361bc9c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -550,7 +550,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     bringToFront={emptyFunction}
                     active={this.props.active}
                     whenActiveChanged={returnFalse}
-                    treeViewHideTopLevel={true}
+                    treeViewHideTitle={true}
                     ContentScaling={returnOne}
                     focus={returnFalse}
                     treeViewHideHeaderFields={true}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 491c7e7e0..8bd454249 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1257,7 +1257,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode &&
             optionItems.push({ description: (this.showTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this.showTimeline = !this.showTimeline), icon: "eye" });
         this.props.ContainingCollectionView &&
-            optionItems.push({ description: "Undo Collection", event: this.promoteCollection, icon: "table" });
+            optionItems.push({ description: "Move Items Out of Collection", event: this.promoteCollection, icon: "table" });
         optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
         optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
         if (!Doc.UserDoc().noviceMode) {
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 0eb675b4e..a65eea50f 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -143,7 +143,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
             const layoutKey = StrCast(originalDoc.layoutKey);
             const newDoc = Doc.MakeCopy(originalDoc, true);
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
-            newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
+            newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
             if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
                 newDoc[layoutKey] = originalDoc[layoutKey];
             }
@@ -168,7 +168,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
             const layoutKey = StrCast(originalDoc.layoutKey);
             const newDoc = Doc.MakeCopy(originalDoc, true);
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
-            newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
+            newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
             if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
                 newDoc[layoutKey] = originalDoc[layoutKey];
             }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index c669b1185..a83fec4ab 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -87,6 +87,7 @@ export const documentSchema = createSchema({
     treeViewLockExpandedView: "boolean", // whether the expanded view can be changed
     treeViewDefaultExpandedView: "string", // name of field whose contents are displayed by default
     treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+    treeViewOutlineMode: "boolean", // whether tree view is an outline and clicks edit document titles immediately since double-click opening is turned off
 
     // interaction and linking properties
     ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
-- 
cgit v1.2.3-70-g09d2


From a2fb0c0483b77730a43f91e8a967f216bde51144 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Aug 2020 00:07:20 -0400
Subject: fixed immediate Title Editing of tree views

---
 src/client/views/collections/CollectionTreeView.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5a80541be..a7c414bcc 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -135,7 +135,7 @@ class TreeView extends React.Component<TreeViewProps> {
         const openScript = ScriptField.MakeScript(`openOnRight(self)`);
         const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
         this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
-        this._openScript = !Doc.IsSystem(this.props.document) && !this.props.treeViewDoc.treeViewOutlineMode ? openScript && (() => openScript) : undefined;
+        this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
         if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
     }
 
@@ -199,7 +199,7 @@ class TreeView extends React.Component<TreeViewProps> {
         }}
         OnTab={undoBatch((shift?: boolean) => {
             shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
-            setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 250);
+            setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 0);
         })}
     />)
 
@@ -417,7 +417,7 @@ class TreeView extends React.Component<TreeViewProps> {
     truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
     showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
-    onChildDoubleClick = () => this._openScript?.() || ScriptCast(this.doc.treeChildDoubleClick);
+    onChildDoubleClick = () => (!this.props.treeViewDoc.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
     /**
      * Renders the EditableView title element for placement into the tree.
      */
-- 
cgit v1.2.3-70-g09d2


From 8b995364cf2a1fab24f72827daf6c7500024dfa4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Aug 2020 00:50:33 -0400
Subject: more efficient way to update tree view active() flag

---
 .../views/collections/CollectionTreeView.tsx       | 29 +++++++++++++---------
 1 file changed, 17 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a7c414bcc..5b2957654 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -64,6 +64,7 @@ export interface TreeViewProps {
     onChildClick?: () => ScriptField;
     ignoreFields?: string[];
     firstLevel: boolean;
+    whenActiveChanged: (isActive: boolean) => void;
 }
 
 @observer
@@ -284,7 +285,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false);
+                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -335,7 +336,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false)}
+                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
@@ -469,7 +470,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 renderDepth={1}
                 focus={returnTrue}
                 parentActive={returnTrue}
-                whenActiveChanged={emptyFunction}
+                whenActiveChanged={this.props.whenActiveChanged}
                 bringToFront={emptyFunction}
                 dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)}
                 docFilters={returnEmptyFilter}
@@ -477,15 +478,13 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.containingCollection}
             />;
-        const activeSel = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
-        const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && activeSel?.props.treeViewDoc !== this.props.treeViewDoc ? "none" : undefined;
         return <>
             <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
-                    pointerEvents
+                    pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }} >
                 {view}
             </div >
@@ -561,6 +560,7 @@ class TreeView extends React.Component<TreeViewProps> {
         onChildClick: undefined | (() => ScriptField),
         ignoreFields: string[] | undefined,
         firstLevel: boolean,
+        whenActiveChanged: (isActive: boolean) => void
     ) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -669,7 +669,8 @@ class TreeView extends React.Component<TreeViewProps> {
                 treeViewPreventOpen={treeViewPreventOpen}
                 renderedIds={renderedIds}
                 ignoreFields={ignoreFields}
-                firstLevel={firstLevel} />;
+                firstLevel={firstLevel}
+                whenActiveChanged={whenActiveChanged} />;
         });
     }
 }
@@ -819,6 +820,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     onChildClick = () => {
         return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
     }
+    _isChildActive = false;
+    whenActiveChanged = (isActive: boolean) => {
+        this.props.whenActiveChanged(this._isChildActive = isActive);
+    }
+    active = (outsideReaction: boolean | undefined) => { return this.props.active(outsideReaction) || this._isChildActive; }
     render() {
         TraceMobx();
         if (!(this.doc instanceof Doc)) return (null);
@@ -828,12 +834,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
         const childElements = childDocs && TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
-            this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
+            this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
-            this.onChildClick, this.props.ignoreFields, true);
+            this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged);
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
-        const activeSel = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
-        const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && activeSel?.props.treeViewDoc !== this.props.Document ? "none" : undefined;
+
         return !childDocs ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget" id="body"
@@ -842,7 +847,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                         paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
                         paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
                         paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
-                        pointerEvents
+                        pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined,
                     }}
                     onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                     onDrop={this.onTreeDrop}
-- 
cgit v1.2.3-70-g09d2


From d2e29bc56a8c0bc8bee0d1ceb28726b3caabdb36 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Aug 2020 01:50:05 -0400
Subject: fix so that tabbing tree view items works when treview aliases (eg
 minimap) are displayed.  just doesn't work if the document is open more than
 once in the same treeview

---
 .../views/collections/CollectionTreeView.tsx       | 37 +++++++++++-----------
 1 file changed, 19 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5b2957654..918377052 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -11,7 +11,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentManager } from '../../util/DocumentManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Scripting } from '../../util/Scripting';
@@ -54,7 +53,7 @@ export interface TreeViewProps {
     ScreenToLocalTransform: () => Transform;
     backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
     outerXf: () => { translateX: number, translateY: number };
-    treeViewDoc: Doc;
+    treeView: CollectionTreeView;
     parentKey: string;
     active: (outsideReaction?: boolean) => boolean;
     treeViewHideHeaderFields: () => boolean;
@@ -200,13 +199,13 @@ class TreeView extends React.Component<TreeViewProps> {
         }}
         OnTab={undoBatch((shift?: boolean) => {
             shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
-            setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 0);
+            setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0);
         })}
     />)
 
     preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
-        dragData && (dragData.dropAction = this.props.treeViewDoc === dragData.treeViewDoc ? "same" : dragData.dropAction);
+        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
     }
 
     @undoBatch
@@ -282,7 +281,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
                     (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
-                    DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+                    DocListCast(contents), this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
                     [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
@@ -332,7 +331,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 e.stopPropagation();
             }}>
                 {!docs ? (null) :
-                    TreeView.GetChildElements(docs, this.props.treeViewDoc, this.layoutDoc,
+                    TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
                         this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
@@ -391,7 +390,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
                 heading: this.props.containingCollection.title,
                 checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
-                containingTreeView: this.props.treeViewDoc,
+                containingTreeView: this.props.treeView,
             }, console.log);
         } else {
             this.treeViewOpen = !this.treeViewOpen;
@@ -413,12 +412,13 @@ class TreeView extends React.Component<TreeViewProps> {
     showContextMenu = (e: React.MouseEvent) => {
         this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
     }
-    focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
     contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
-    truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
-    showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
+    truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
+    @computed get showTitleEdit() {
+        return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
+    }
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
-    onChildDoubleClick = () => (!this.props.treeViewDoc.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+    onChildDoubleClick = () => (!this.props.treeView.props.Document.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -443,12 +443,12 @@ class TreeView extends React.Component<TreeViewProps> {
                     {this.treeViewExpandedView}
                 </span>
             </>;
-        const view = this.showTitleEdit() ? this.editableView("title") :
+        const view = this.showTitleEdit ? this.editableView("title") :
             <DocumentView
                 ref={this._docRef}
                 Document={this.doc}
                 DataDoc={undefined}
-                treeViewDoc={this.props.treeViewDoc}
+                treeViewDoc={this.props.treeView.props.Document}
                 LibraryPath={emptyPath}
                 addDocument={undefined}
                 addDocTab={this.props.addDocTab}
@@ -472,7 +472,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 parentActive={returnTrue}
                 whenActiveChanged={this.props.whenActiveChanged}
                 bringToFront={emptyFunction}
-                dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)}
+                dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
                 docFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
@@ -495,7 +495,7 @@ class TreeView extends React.Component<TreeViewProps> {
     render() {
         TraceMobx();
         const sorting = this.doc[`${this.fieldKey}-sortAscending`];
-        if (this.showTitleEdit()) { // find  containing CollectionTreeView and set our maximum width so  the containing tree view won't have to scroll
+        if (this.showTitleEdit) { // find  containing CollectionTreeView and set our maximum width so  the containing tree view won't have to scroll
             let par: any = this._header?.current;
             if (par) {
                 while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
@@ -534,7 +534,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
     public static GetChildElements(
         childDocs: Doc[],
-        treeViewDoc: Doc,
+        treeView: CollectionTreeView,
         containingCollection: Doc,
         dataDoc: Doc | undefined,
         key: string,
@@ -644,7 +644,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 dataDoc={pair.data}
                 containingCollection={containingCollection}
                 prevSibling={docs[i]}
-                treeViewDoc={treeViewDoc}
+                treeView={treeView}
                 key={child[Id]}
                 indentDocument={indent}
                 outdentDocument={outdent}
@@ -687,6 +687,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     private treedropDisposer?: DragManager.DragDropDisposer;
     private _mainEle?: HTMLDivElement;
 
+    public _uniqueId = Utils.GenerateGuid();
     @computed get doc() { return this.props.Document; }
     @computed get dataDoc() { return this.props.DataDoc || this.doc; }
 
@@ -832,7 +833,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
         const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
-        const childElements = childDocs && TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
+        const childElements = childDocs && TreeView.GetChildElements(childDocs, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
             this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
-- 
cgit v1.2.3-70-g09d2


From 6471ccff7dd145d3725492ccccaecc6035e851f2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Aug 2020 02:30:44 -0400
Subject: fixed warnings.  fixed url / annoation updating when navigating web
 pages.

---
 src/client/views/collections/CollectionMenu.tsx        | 6 +++---
 src/client/views/collections/CollectionSchemaCells.tsx | 6 +++---
 src/client/views/collections/CollectionTreeView.tsx    | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 2f257d00b..5f40ac59c 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -725,6 +725,8 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
         if (this.selectedDoc) {
             Doc.GetProto(this.selectedDoc).data = new WebField(value);
             Doc.SetInPlace(this.selectedDoc, "title", value, true);
+            const annots = Doc.GetProto(this.selectedDoc)["data-annotations-" + this.urlHash(value)];
+            Doc.GetProto(this.selectedDoc)["data-annotations"] = annots instanceof ObjectField ? ObjectField.MakeCopy(annots) : new List<Doc>([]);
         }
     }
 
@@ -751,9 +753,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
                         }
                         future && (future.length = 0);
                     }
-                    Doc.GetProto(selectedDoc).data = new WebField(URLy);
-                    const annots = Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(url)];
-                    Doc.GetProto(selectedDoc)["data-annotations"] = annots instanceof ObjectField ? ObjectField.MakeCopy(annots) : new List<Doc>([]);
+                    this._url = url;
                 }
             }
         } catch (e) {
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 95f425524..18ca29aaa 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -169,7 +169,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
             DocumentManager.Instance.jumpToDocument(this._rowDoc, false, () => undefined, targetContext);
         }
-    };
+    }
     renderCellWithType(type: string | undefined) {
         const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
@@ -330,7 +330,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
 
     componentWillUnmount() { this.onBlur(); }
 
-    onBlur = () => { this._overlayDisposer?.(); }
+    onBlur = () => { this._overlayDisposer?.(); };
     onFocus = () => {
         this.onBlur();
         this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
@@ -443,7 +443,7 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
     }
 
     render() {
-        let link = false;
+        const link = false;
         const reference = React.createRef<HTMLDivElement>();
 
         if (this._optionsList?.length) {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 918377052..5099f0022 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -825,7 +825,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     whenActiveChanged = (isActive: boolean) => {
         this.props.whenActiveChanged(this._isChildActive = isActive);
     }
-    active = (outsideReaction: boolean | undefined) => { return this.props.active(outsideReaction) || this._isChildActive; }
+    active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
     render() {
         TraceMobx();
         if (!(this.doc instanceof Doc)) return (null);
-- 
cgit v1.2.3-70-g09d2


From 022e36458815b9fd4ce79bea3ac1ae158a0c9463 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Aug 2020 10:25:37 -0400
Subject: added dropdown dashboards input at top left + new & snapshot

---
 src/client/util/CurrentUserUtils.ts                | 89 +++++++++++++++++++++-
 src/client/util/History.ts                         |  4 +-
 src/client/views/MainView.tsx                      | 89 ++--------------------
 .../views/collections/CollectionDockingView.tsx    |  8 +-
 .../views/collections/CollectionTreeView.tsx       |  4 +-
 src/client/views/search/SearchBox.scss             | 50 +++++++++++-
 src/client/views/search/SearchBox.tsx              | 28 +++++--
 7 files changed, 171 insertions(+), 101 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 435944a74..a76aa0dee 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,4 +1,4 @@
-import { computed, observable, reaction } from "mobx";
+import { computed, observable, reaction, action } from "mobx";
 import * as rp from 'request-promise';
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
@@ -24,6 +24,8 @@ import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMu
 import { LabelBox } from "../views/nodes/LabelBox";
 import { LinkManager } from "./LinkManager";
 import { Id } from "../../fields/FieldSymbols";
+import { HistoryUtil } from "./History";
+import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -730,7 +732,7 @@ export class CurrentUserUtils {
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
-            const newDashboard = ScriptField.MakeScript(`createNewDashboard()`);
+            const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
             (doc.myDashboards as any as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
             (doc.myDashboards as any as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
         }
@@ -976,9 +978,90 @@ export class CurrentUserUtils {
             }
         });
     }
+
+    public static _urlState: HistoryUtil.DocUrl;
+
+    public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
+        CurrentUserUtils.MainDocId = doc[Id];
+
+        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 ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
+        }
+        const state = CurrentUserUtils._urlState;
+        if (state.sharing === true && !userDoc) {
+            DocServer.Control.makeReadOnly();
+        } else {
+            fromHistory || HistoryUtil.pushState({
+                type: "doc",
+                docId: doc[Id],
+                readonly: state.readonly,
+                nro: state.nro,
+                sharing: false,
+            });
+            if (state.readonly === true || state.readonly === null) {
+                DocServer.Control.makeReadOnly();
+            } else if (state.safe) {
+                if (!state.nro) {
+                    DocServer.Control.makeReadOnly();
+                }
+                CollectionView.SetSafeMode(true);
+            } else if (state.nro || state.nro === null || state.readonly === false) {
+            } else if (doc.readOnly) {
+                DocServer.Control.makeReadOnly();
+            } else {
+                DocServer.Control.makeEditable();
+            }
+        }
+
+        return true;
+    }
+
+    public static snapshotDashboard = (userDoc: Doc) => {
+        const activeDashboard = Cast(userDoc.activeDashboard, Doc, null);
+        CollectionDockingView.Copy(activeDashboard).then(copy => {
+            Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
+            // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+            setTimeout(() => CurrentUserUtils.openDashboard(userDoc, copy), 0);
+        });
+    }
+
+    public static createNewDashboard = async (userDoc: Doc, id?: string) => {
+        const myPresentations = userDoc.myPresentations as Doc;
+        const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
+        const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
+        const dashboardCount = DocListCast(dashboards.data).length + 1;
+        const emptyPane = Cast(userDoc.emptyPane, Doc, null);
+        emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+        const freeformOptions: DocumentOptions = {
+            x: 0,
+            y: 400,
+            _width: 1500,
+            _height: 1000,
+            title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
+        };
+        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
+        Doc.AddDocToList(myPresentations, "data", presentation);
+        userDoc.activePresentation = presentation;
+        const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
+        const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+        const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
+        const createDashboard = ScriptField.MakeScript(`createNewDashboard()`);
+        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!]);
+        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
+
+        Doc.AddDocToList(dashboards, "data", dashboardDoc);
+        // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+        setTimeout(() => {
+            CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+        }, 0);
+    }
 }
 
-Scripting.addGlobal(function createNewDashboard() { return MainView.Instance.createNewDashboard(); },
+Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
+    "creates a snapshot copy of a dashboard");
+Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
     "creates a new dashboard when called");
 Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
     "creates a new presentation when called");
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index cab682ac7..cbe36b401 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,8 +1,8 @@
 import { Doc } from "../../fields/Doc";
 import { DocServer } from "../DocServer";
-import { MainView } from "../views/MainView";
 import * as qs from 'query-string';
 import { Utils, OmitKeys } from "../../Utils";
+import { CurrentUserUtils } from "./CurrentUserUtils";
 
 export namespace HistoryUtil {
     export interface DocInitializerList {
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            MainView.Instance.openDashboard(field, true);
+            CurrentUserUtils.openDashboard(Doc.UserDoc(), field, true);
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cfa2534c9..d107b74f0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -70,7 +70,6 @@ import "./MainView.scss";
 export class MainView extends React.Component {
     public static Instance: MainView;
     private _buttonBarHeight = 36;
-    private _urlState: HistoryUtil.DocUrl;
     private _docBtnRef = React.createRef<HTMLDivElement>();
     private _mainViewRef = React.createRef<HTMLDivElement>();
 
@@ -145,7 +144,7 @@ export class MainView extends React.Component {
         super(props);
         MainView.Instance = this;
         this.sidebarContent.proto = undefined;
-        this._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
+        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
         // causes errors to be generated when modifying an observable outside of an action
 
         CurrentUserUtils.propertiesWidth = 0;
@@ -233,11 +232,11 @@ export class MainView extends React.Component {
         if (received && !this.userDoc) {
             reaction(
                 () => CurrentUserUtils.GuestTarget,
-                target => target && this.createNewDashboard(),
+                target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()),
                 { fireImmediately: true }
             );
         } else {
-            if (received && this._urlState.sharing) {
+            if (received && CurrentUserUtils._urlState.sharing) {
                 reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
                     initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
                         if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
@@ -248,9 +247,9 @@ export class MainView extends React.Component {
             }
             const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc);
             if (doc) {
-                this.openDashboard(doc);
+                CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
             } else {
-                this.createNewDashboard();
+                CurrentUserUtils.createNewDashboard(Doc.UserDoc());
             }
         }
     }
@@ -271,73 +270,6 @@ export class MainView extends React.Component {
         Doc.AddDocToList(myPresentations, "data", pres);
     }
 
-    @action
-    createNewDashboard = async (id?: string) => {
-        const myPresentations = Doc.UserDoc().myPresentations as Doc;
-        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
-        const dashboards = Cast(this.userDoc.myDashboards, Doc) as Doc;
-        const dashboardCount = DocListCast(dashboards.data).length + 1;
-        const emptyPane = Cast(this.userDoc.emptyPane, Doc, null);
-        emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-        const freeformOptions: DocumentOptions = {
-            x: 0,
-            y: 400,
-            _width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
-            _height: this._panelHeight,
-            title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
-        };
-        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
-        Doc.AddDocToList(myPresentations, "data", presentation);
-        Doc.UserDoc().activePresentation = presentation;
-        const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
-        const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const copyDashboard = ScriptField.MakeScript(`copyDashboard()`);
-        const createDashboard = ScriptField.MakeScript(`createDashboard()`);
-        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyDashboard!, createDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
-
-        Doc.AddDocToList(dashboards, "data", dashboardDoc);
-        // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => this.openDashboard(dashboardDoc), 0);
-    }
-
-    @action
-    openDashboard = (doc: Doc, fromHistory = false) => {
-        CurrentUserUtils.MainDocId = doc[Id];
-
-        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" })]));
-            this.userDoc ? (this.userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
-        }
-        const state = this._urlState;
-        if (state.sharing === true && !this.userDoc) {
-            DocServer.Control.makeReadOnly();
-        } else {
-            fromHistory || HistoryUtil.pushState({
-                type: "doc",
-                docId: doc[Id],
-                readonly: state.readonly,
-                nro: state.nro,
-                sharing: false,
-            });
-            if (state.readonly === true || state.readonly === null) {
-                DocServer.Control.makeReadOnly();
-            } else if (state.safe) {
-                if (!state.nro) {
-                    DocServer.Control.makeReadOnly();
-                }
-                CollectionView.SetSafeMode(true);
-            } else if (state.nro || state.nro === null || state.readonly === false) {
-            } else if (doc.readOnly) {
-                DocServer.Control.makeReadOnly();
-            } else {
-                DocServer.Control.makeEditable();
-            }
-        }
-
-        return true;
-    }
 
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
         e.preventDefault();
@@ -454,7 +386,7 @@ export class MainView extends React.Component {
     flyoutWidthFunc = () => this.flyoutWidth;
     addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
         return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
-            doc.dockingConfig ? this.openDashboard(doc) :
+            doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) :
                 CollectionDockingView.AddRightSplit(doc, libraryPath);
     }
     sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
@@ -998,14 +930,5 @@ export class MainView extends React.Component {
 }
 Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
 Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
-Scripting.addGlobal(function copyDashboard() {
-    const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null);
-    CollectionDockingView.Copy(activeDashboard).then(copy => {
-        Doc.AddDocToList(Cast(Doc.UserDoc().myDashboards, Doc, null), "data", copy);
-        // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => MainView.Instance.openDashboard(copy), 0);
-    });
-});
-Scripting.addGlobal(function createDashboard() { MainView.Instance.createNewDashboard(); });
 Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); },
     "imports files from device directly into the import sidebar");
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e7f54ff86..dfefd645a 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -25,7 +25,6 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from "../../util/UndoManager";
-import { MainView } from '../MainView';
 import { DocumentView } from "../nodes/DocumentView";
 import { PresBox } from '../nodes/PresBox';
 import "./CollectionDockingView.scss";
@@ -34,6 +33,7 @@ import { SubCollectionViewProps, CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from './CollectionView';
 import { DockingViewButtonSelector } from './ParentDocumentSelector';
 import React = require("react");
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -87,7 +87,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @action
     public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
         if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
-            return MainView.Instance.openDashboard(docView.props.Document);
+            return CurrentUserUtils.openDashboard(Doc.UserDoc(), docView.props.Document);
         }
         const document = Doc.MakeAlias(docView.props.Document);
         const newItemStackConfig = {
@@ -188,7 +188,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @action
     public static AddRightSplit(document: Doc, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
-        if (document._viewType === CollectionViewType.Docking) return MainView.Instance.openDashboard(document);
+        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
@@ -864,7 +864,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc._viewType === CollectionViewType.Docking) {
-            return MainView.Instance.openDashboard(doc);
+            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
         } else if (location === "onRight") {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
         } else if (location === "close") {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5099f0022..d7f0f8e14 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -20,7 +20,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from "../EditableView";
-import { MainView } from '../MainView';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import { DocumentView } from '../nodes/DocumentView';
 import { ImageBox } from '../nodes/ImageBox';
@@ -32,6 +31,7 @@ import { CollectionViewType } from './CollectionView';
 import React = require("react");
 import { makeTemplate } from '../../util/DropConverter';
 import { TraceMobx } from '../../../fields/util';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 
 export interface TreeViewProps {
     document: Doc;
@@ -739,7 +739,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) {
-            ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => MainView.Instance.createNewDashboard(), icon: "plus" });
+            ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
             e.stopPropagation();
             e.preventDefault();
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index b5557e0d9..aa5180c96 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -6,23 +6,69 @@
     flex-direction: column;
     width: 100%;
     height: 100%;
-    position: absolute;
+    position: relative;
     font-size: 10px;
     line-height: 1;
     overflow-y: auto;
     overflow-x: visible;
     background: lightgrey;
+    overflow: visible;
+    z-index: 10000;
 
+    .searchBox-lozenge-user,
+    .searchBox-lozenge-dashboard,
     .searchBox-lozenge {
         background-color: #313131;
         border-radius: 5px;
-        height: 15px;
+        height: 18px;
         padding: 4px;
         box-shadow: lightgrey 0.15em 0.15em 0.1em;
         margin: 2px;
         margin-bottom: 4px;
         border-top: dimgrey 1px solid;
         border-left: dimgrey 1px solid;
+        display: flex;
+        .searchBox-logoff,
+        .searchBox-dashboards {
+            border-radius: 3px;
+            background: olivedrab;
+            color: white;
+            position: relative;
+            display: none;
+            margin-left: 3px;
+            padding-left: 2px;
+            padding-right: 2px;
+            padding-bottom: 11px;
+            cursor: default;
+        }
+        .searchBox-logoff {
+            background: red;
+        }
+
+        .searchBox-dashSelect{
+            background-color: black;
+            color: white;
+            font-size: 9;
+            margin-right: 6;
+            border-radius: 5px;
+            position: relative;
+            height: 15px;
+            transform: translate(0,-3px);
+
+            &:hover {
+                cursor: pointer;
+            }
+        }
+    }
+    .searchBox-lozenge-user:hover {
+        .searchBox-logoff {
+            display:inline-block;
+        }
+    }
+    .searchBox-lozenge-dashboard:hover {
+        .searchBox-dashboards {
+            display:inline-block;
+        }
     }
 }
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 5739c2a67..7e233ecbb 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -5,14 +5,15 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
 import { documentSchema } from "../../../fields/documentSchemas";
-import { Id, Copy } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { returnFalse, returnZero, setupMoveUpEvents, emptyFunction } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { SetupDrag } from '../../util/DragManager';
 import { SearchUtil } from '../../util/SearchUtil';
 import { Transform } from '../../util/Transform';
@@ -22,6 +23,7 @@ import { CollectionViewType } from '../collections/CollectionView';
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import "./SearchBox.scss";
+import { undoBatch } from "../../util/UndoManager";
 
 export const searchSchema = createSchema({ Document: Doc });
 
@@ -485,19 +487,35 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
             collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined;
         }
+    }
+    showLogout = () => {
+
     }
     render() {
+        const myDashboards = DocListCast(Cast(Doc.UserDoc().myDashboards, Doc, null).data);
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
                 <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>
-                    <div className="searchBox-lozenge">
+                    <div className="searchBox-lozenge-user">
                         {`${Doc.CurrentUserEmail}`}
+                        <div className="searchBox-logoff" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
+                            Logoff
+                        </div>
                     </div>
                     <div className="searchBox-lozenge">
                         {`UI project`}
                     </div>
-                    <div className="searchBox-lozenge"  >
-                        {`➱ ${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.title}`}
+                    <div className="searchBox-lozenge-dashboard"  >
+                        <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
+                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null)!)}>
+                            {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
+                        </select>
+                        <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
+                            New
+                        </div>
+                        <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}>
+                            Snapshot
+                        </div>
                     </div>
                 </div>
                 <div className="searchBox-bar">
-- 
cgit v1.2.3-70-g09d2


From 3eac88792e7b11f731aac9270a918b31c58ec83e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Aug 2020 12:51:45 -0400
Subject: switched back to recentlyClosed from inactive.  fixed adding tag to
 multiple document seleciton.

---
 src/client/util/CurrentUserUtils.ts                       | 12 ++++++------
 src/client/views/DocComponent.tsx                         |  2 +-
 src/client/views/collections/CollectionDockingView.tsx    |  4 ++--
 src/client/views/collections/CollectionTreeView.tsx       |  4 ++--
 src/client/views/collections/CollectionView.tsx           |  2 +-
 .../collections/collectionFreeForm/PropertiesView.tsx     |  6 +++---
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 15 ---------------
 7 files changed, 15 insertions(+), 30 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index a76aa0dee..ef7beb030 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -508,7 +508,7 @@ export class CurrentUserUtils {
         this.setupSharingSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
         return [
             { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
-            { title: "Inactive", target: Cast(doc.myInactiveDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+            { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
             { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
@@ -758,17 +758,17 @@ export class CurrentUserUtils {
 
     static setupInactiveDocs(doc: Doc) {
         // setup Recently Closed library item
-        doc.myInactiveDocs === undefined;
-        if (doc.myInactiveDocs === undefined) {
-            doc.myInactiveDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        doc.myRecentlyClosedDocs === undefined;
+        if (doc.myRecentlyClosedDocs === undefined) {
+            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "Inactive", _height: 500,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
-            (doc.myInactiveDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.myInactiveDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+            (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+            (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
         }
     }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index d621a792b..1cea94064 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                 const toRemove = value.filter(v => docs.includes(v));
 
                 if (toRemove.length !== 0) {
-                    const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc;
+                    const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
                     toRemove.forEach(doc => {
                         Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc);
                         recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index dfefd645a..003f0c514 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -611,7 +611,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 if (doc instanceof Doc) {
                     const theDoc = doc;
 
-                    const recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc);
+                    const recent = await Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc);
                     if (recent) {
                         Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                     }
@@ -682,7 +682,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
                     if (doc instanceof Doc) {
                         let recent: Doc | undefined;
-                        if (recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc)) {
+                        if (recent = await Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc)) {
                             Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                         }
                         const theDoc = doc;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d7f0f8e14..66bdcf130 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -744,8 +744,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myInactiveDocs) {
-            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myInactiveDocs = new List<Doc>(), icon: "plus" });
+        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosedDocs) {
+            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosedDocs = new List<Doc>(), icon: "plus" });
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a7361bc9c..f028b797c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -198,7 +198,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             const value = DocListCast(targetDataDoc[this.props.fieldKey]);
             const toRemove = value.filter(v => docs.includes(v));
             if (toRemove.length !== 0) {
-                const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc;
+                const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
                 toRemove.forEach(doc => {
                     Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc);
                     recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index 2627afbae..f9ffb4260 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -233,8 +233,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     setKeyValue = (value: string) => {
-        if (this.selectedDoc && this.dataDoc) {
-            const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc;
+        const docs = SelectionManager.SelectedDocuments().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc);
+        docs.forEach(doc => {
             if (value.indexOf(":") !== -1) {
                 const newVal = value[0].toUpperCase() + value.substring(1, value.length);
                 KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true);
@@ -244,7 +244,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true);
                 return true;
             }
-        }
+        });
         return false;
     }
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 816f5028b..48f10ba26 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -58,21 +58,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
     @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
 
-    @computed get renderScriptDim() {
-        if (this.Document.renderScript) {
-            const someView = Cast(this.props.Document.someView, Doc);
-            const minimap = Cast(this.props.Document.minimap, Doc);
-            if (someView instanceof Doc && minimap instanceof Doc) {
-                const x = (NumCast(someView._panX) - NumCast(someView._width) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap._width) - NumCast(minimap._width) / 2;
-                const y = (NumCast(someView._panY) - NumCast(someView._height) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap._height) - NumCast(minimap._height) / 2;
-                const w = NumCast(someView._width) / NumCast(someView._viewScale) / NumCast(minimap.fitW) * NumCast(minimap.width);
-                const h = NumCast(someView._height) / NumCast(someView._viewScale) / NumCast(minimap.fitH) * NumCast(minimap.height);
-                return { x: x, y: y, width: w, height: h };
-            }
-        }
-        return undefined;
-    }
-
     public static getValues(doc: Doc, time: number) {
         const timecode = Math.round(time);
         return ({
-- 
cgit v1.2.3-70-g09d2


From 1536f8d6e5f44fd14954550e9bd670561727b417 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 28 Aug 2020 15:45:07 -0400
Subject: fixed undo for collection tabs. fixed treeview to support
 freezeChildren to prevent children from being removed from the tree.  Made
 _isBackground a layoutfield.  fixed formattedTextBox exceptions on undo from
 a tab.

---
 src/client/documents/Documents.ts                  |  16 +-
 src/client/util/CurrentUserUtils.ts                |  19 +-
 src/client/views/DocComponent.tsx                  |   6 +-
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/animationtimeline/Keyframe.tsx    |   2 +-
 .../views/collections/CollectionDockingView.tsx    | 316 ++++++++-------------
 .../views/collections/CollectionTreeView.tsx       |  18 +-
 src/client/views/collections/CollectionView.tsx    |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  16 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   8 +-
 .../collectionFreeForm/PropertiesView.tsx          |   2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   6 +-
 src/client/views/nodes/DocumentView.tsx            |  18 +-
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |   2 +-
 src/client/views/nodes/PresBox.tsx                 |  12 +-
 src/client/views/nodes/VideoBox.tsx                |   3 +-
 src/client/views/nodes/WebBox.tsx                  |   8 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  16 +-
 .../views/presentationview/PresElementBox.tsx      |   2 +-
 src/client/views/search/SearchBox.tsx              |   2 +-
 src/fields/Doc.ts                                  |  22 +-
 src/fields/documentSchemas.ts                      |   2 +-
 23 files changed, 231 insertions(+), 275 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b8cac2beb..18ff993fe 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -33,7 +33,6 @@ import { ColorBox } from "../views/nodes/ColorBox";
 import { ComparisonBox } from "../views/nodes/ComparisonBox";
 import { DocHolderBox } from "../views/nodes/DocHolderBox";
 import { FontIconBox } from "../views/nodes/FontIconBox";
-import { MenuIconBox } from "../views/nodes/MenuIconBox";
 import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
 import { ImageBox } from "../views/nodes/ImageBox";
 import { KeyValueBox } from "../views/nodes/KeyValueBox";
@@ -129,7 +128,7 @@ export interface DocumentOptions {
     isAnnotating?: boolean; // whether we web document is annotation mode where links can't be clicked to allow annotations to be created
     opacity?: number;
     defaultBackgroundColor?: string;
-    isBackground?: boolean;
+    _isBackground?: boolean;
     isLinkButton?: boolean;
     _columnWidth?: number;
     _fontSize?: string;
@@ -186,6 +185,7 @@ export interface DocumentOptions {
     strokeWidth?: number;
     cloneFieldFilter?: List<string>; // fields not to copy when the document is cloned
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
+    freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
     treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
     treeViewHideTitle?: boolean; // whether to hide the top document of a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
@@ -535,7 +535,7 @@ export namespace Docs {
 
         Scripting.addGlobal(Buxton);
 
-        const delegateKeys = ["x", "y", "system", "layoutKey", "dropAction", "lockedPosiiton", "childDropAction", "isLinkButton", "isBackground", "removeDropProperties", "treeViewOpen"];
+        const delegateKeys = ["x", "y", "system", "layoutKey", "dropAction", "lockedPosiiton", "childDropAction", "isLinkButton", "removeDropProperties", "treeViewOpen"];
 
         /**
          * This function receives the relevant document prototype and uses
@@ -684,7 +684,7 @@ export namespace Docs {
             const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, {
                 dontRegisterChildViews: true,
                 isLinkButton: true, treeViewHideTitle: true, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area
-                treeViewExpandedView: "fields", removeDropProperties: new List(["isBackground", "isLinkButton"]), ...options
+                treeViewExpandedView: "fields", removeDropProperties: new List(["_isBackground", "isLinkButton"]), ...options
             }, id);
             const linkDocProto = Doc.GetProto(doc);
             linkDocProto.treeViewOpen = true;// setting this in the instance creator would set it on the view document. 
@@ -694,7 +694,7 @@ export namespace Docs {
             linkDocProto.anchor2_timecode = target.doc._currentTimecode || target.doc.displayTimecode;
 
             if (linkDocProto.linkBoxExcludedKeys === undefined) {
-                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
+                Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "creationDate", "author"]);
                 Cast(linkDocProto.proto, Doc, null).layoutKey = undefined;
             }
 
@@ -825,9 +825,9 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
-            const tabs = TreeDocument(documents, { title: "On-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true });
-            const all = TreeDocument([], { title: "Off-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true });
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            const tabs = TreeDocument(documents, { title: "On-Screen Tabs", freezeChildren: "remove|add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true });
+            const all = TreeDocument([], { title: "Off-Screen Tabs", freezeChildren: "add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true });
             Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
             return inst;
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2c9c870aa..c60403701 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -26,6 +26,8 @@ import { LinkManager } from "./LinkManager";
 import { Id } from "../../fields/FieldSymbols";
 import { HistoryUtil } from "./History";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
+import { SelectionManager } from "./SelectionManager";
+import { DocumentManager } from "./DocumentManager";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -1018,12 +1020,9 @@ export class CurrentUserUtils {
     }
 
     public static snapshotDashboard = (userDoc: Doc) => {
-        const activeDashboard = Cast(userDoc.activeDashboard, Doc, null);
-        CollectionDockingView.Copy(activeDashboard).then(copy => {
-            Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
-            // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-            setTimeout(() => CurrentUserUtils.openDashboard(userDoc, copy), 0);
-        });
+        const copy = CollectionDockingView.Copy(Cast(userDoc.activeDashboard, Doc, null));
+        Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
+        CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
@@ -1059,6 +1058,14 @@ export class CurrentUserUtils {
     }
 }
 
+Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
+    const copy = Doc.copyDragFactory(dragFactory);
+    if (copy) {
+        CollectionDockingView.AddRightSplit(copy);
+        const view = DocumentManager.Instance.getFirstDocumentView(copy);
+        view && SelectionManager.SelectDoc(view, false);
+    }
+});
 Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
     "creates a snapshot copy of a dashboard");
 Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 1cea94064..e9469d7bb 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -58,7 +58,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor:
 
         lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.ContainingCollectionDoc }).result;
 
-        active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);//  && !Doc.SelectedTool();  // bcz: inking state shouldn't affect static tools 
+        active = (outsideReaction?: boolean) => !this.props.Document._isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);//  && !Doc.SelectedTool();  // bcz: inking state shouldn't affect static tools 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
@@ -187,9 +187,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         }
 
         whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
-        active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.props.Document.isBackground) &&
+        active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.props.Document._) &&
             (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0 || BoolCast((this.layoutDoc as any).forceActive)) ? true : false)
-        annotationsActive = (outsideReaction?: boolean) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.Document.isBackground && this.props.active()) ||
+        annotationsActive = (outsideReaction?: boolean) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.Document._isBackground && this.props.active()) ||
             (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
     }
     return Component;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9371728ce..3afda13c2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -386,7 +386,7 @@ export class MainView extends React.Component {
     addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
         return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
             doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) :
-                CollectionDockingView.AddRightSplit(doc, libraryPath);
+                CollectionDockingView.AddRightSplit(doc);
     }
     sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0);
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index 1b81c544a..4fb362ab1 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -331,7 +331,7 @@ export class Keyframe extends React.Component<IProps> {
         }),
             TimelineMenu.Instance.addItem("button", "Show Data", action(() => {
                 const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 });
-                CollectionDockingView.AddRightSplit(kvp, emptyPath);
+                CollectionDockingView.AddRightSplit(kvp);
             })),
             TimelineMenu.Instance.addItem("button", "Delete", action(() => {
                 (this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3d8ac2a23..86d27acd5 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -24,7 +24,7 @@ import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import { PresBox } from '../nodes/PresBox';
 import "./CollectionDockingView.scss";
@@ -59,7 +59,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
 
     @observable private _goldenLayout: any = null;
     private _containerRef = React.createRef<HTMLDivElement>();
-    private _flush: boolean = false;
+    private _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
     private _isPointerDown = false;
     private _maximizedSrc: Opt<DocumentView>;
@@ -73,6 +73,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         DragManager.StartWindowDrag = this.StartOtherDrag;
     }
     public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
+        console.log("START drag batch");
+        !this._flush && (this._flush = UndoManager.StartBatch("golden layout drag"));
         const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
             {
                 type: 'row',
@@ -104,6 +106,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         SelectionManager.DeselectAll();
     }
 
+    @undoBatch
     public CloseFullScreen = () => {
         const target = this._goldenLayout._maximisedItem;
         if (target !== null && this._maximizedSrc) {
@@ -121,25 +124,24 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @undoBatch
     @action
     public static CloseRightSplit(document: Opt<Doc>): boolean {
-        const instance = CollectionDockingView.Instance;
         const tryClose = (childItem: any) => {
             if (childItem.config?.component === "DocumentFrameRenderer") {
                 const docView = DocumentManager.Instance.getDocumentViewById(childItem.config.props.documentId);
                 if (docView && ((!document && docView.Document.isDisplayPanel) || (document && Doc.AreProtosEqual(docView.props.Document, document)))) {
                     childItem.remove();
-                    instance.layoutChanged(document);
                     return true;
                 }
             }
             return false;
         };
-        const retVal = !instance?._goldenLayout.root.contentItems[0].isRow ? false :
-            Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose));
+        const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false :
+            Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose));
 
-        retVal && instance.stateChanged();
+        retVal && CollectionDockingView.Instance.layoutChanged(document);
         return retVal;
     }
 
+    @undoBatch
     @action
     layoutChanged(removed?: Doc) {
         this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
@@ -147,20 +149,17 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         this.stateChanged();
     }
-    @undoBatch
-    @action
+
     public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
         if (!CollectionDockingView.Instance) return false;
         const instance = CollectionDockingView.Instance;
-        let retVal = false;
-        if (instance._goldenLayout.root.contentItems[0].isRow) {
-            retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
+        const retVal = !instance._goldenLayout.root.contentItems[0].isRow ? false :
+            Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
                 if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
                     DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) {
                     const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
                     child.addChild(newItemStackConfig, undefined);
                     !addToSplit && child.contentItems[0].remove();
-                    instance.layoutChanged(document);
                     return true;
                 }
                 return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
@@ -168,16 +167,12 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                         const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
                         child.addChild(newItemStackConfig, undefined);
                         !addToSplit && child.contentItems[j].remove();
-                        instance.layoutChanged(document);
                         return true;
                     }
                     return false;
                 });
             });
-        }
-        if (retVal) {
-            instance.stateChanged();
-        }
+        retVal && instance.layoutChanged(document);
         return retVal;
     }
 
@@ -186,34 +181,51 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     //
     @undoBatch
     @action
-    public static AddRightSplit(document: Doc, libraryPath?: Doc[]) {
+    public static AddRightSplit(document: Doc, dontSelect: boolean = false, isDisplayPanel: Opt<boolean> = undefined) {
         if (!CollectionDockingView.Instance) return false;
-        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
-        const instance = CollectionDockingView.Instance;
-        const newItemStackConfig = {
-            type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
-        };
 
-        const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-
-        if (instance._goldenLayout.root.contentItems.length === 0) {
-            instance._goldenLayout.root.addChild(newContentItem);
-        } else if (instance._goldenLayout.root.contentItems[0].isRow) {
-            instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
+        const ind = CollectionDockingView.Instance._tabMap.findIndex((val) => val.doc === document);
+        if (ind !== -1) {
+            const tab = CollectionDockingView.Instance._tabMap[ind].tab;
+            const activeContentItem = tab.header.parent.getActiveContentItem();
+            if (tab.contentItem !== activeContentItem) {
+                tab.header.parent.setActiveContentItem(tab.contentItem);
+            }
+            tab.setActive(true);
         } else {
-            const collayout = instance._goldenLayout.root.contentItems[0];
-            const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
-            collayout.parent.replaceChild(collayout, newRow);
+            document.isDisplayPanel = isDisplayPanel;
 
-            newRow.addChild(newContentItem, undefined, true);
-            newRow.addChild(collayout, 0, true);
+            if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
+            const instance = CollectionDockingView.Instance;
+            const newItemStackConfig = {
+                type: 'stack',
+                content: [CollectionDockingView.makeDocumentConfig(document, undefined, [])]
+            };
+
+            const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
 
-            collayout.config.width = 50;
-            newContentItem.config.width = 50;
+            if (instance._goldenLayout.root.contentItems.length === 0) {
+                instance._goldenLayout.root.addChild(newContentItem);
+            } else if (instance._goldenLayout.root.contentItems[0].isRow) {
+                instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
+            } else {
+                const collayout = instance._goldenLayout.root.contentItems[0];
+                const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+                collayout.parent.replaceChild(collayout, newRow);
+
+                newRow.addChild(newContentItem, undefined, true);
+                newRow.addChild(collayout, 0, true);
+
+                collayout.config.width = 50;
+                newContentItem.config.width = 50;
+            }
+            newContentItem.callDownwards('_$init');
+            instance.layoutChanged();
+        }
+        if (!dontSelect) {
+            const view = DocumentManager.Instance.getFirstDocumentView(document);
+            view && SelectionManager.SelectDoc(view, false);
         }
-        newContentItem.callDownwards('_$init');
-        instance.layoutChanged();
         return true;
     }
 
@@ -221,7 +233,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     //
     //  Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side
     //
-    @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
@@ -289,19 +300,13 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
-    @undoBatch
-    @action
     public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
-        document.isDisplayPanel = true;
         if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
-            CollectionDockingView.AddRightSplit(document, libraryPath);
+            CollectionDockingView.AddRightSplit(document, false, true);
         }
     }
 
-    @undoBatch
-    @action
     public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
-        Doc.GetProto(document).lastOpened = new DateField;
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
         if (stack === undefined) {
             let stack: any = this._goldenLayout.root;
@@ -324,10 +329,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         return true;
     }
 
-    @undoBatch
-    @action
     public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
-        Doc.GetProto(document).lastOpened = new DateField;
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
         if (stack === undefined) {
             let stack: any = this._goldenLayout.root;
@@ -350,9 +352,15 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         return true;
     }
 
-    setupGoldenLayout() {
+    _tabMap: { tab: any, doc: Doc }[] = [];
+
+    async setupGoldenLayout() {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
+            const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+            const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? [];
+            await Promise.all(docids.map(id => DocServer.GetRefField(id)));
+
             if (!this._goldenLayout) {
                 runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
             }
@@ -361,7 +369,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     return;
                 }
                 try {
-                    this._goldenLayout.unbind('itemDropped', this.itemDropped);
                     this._goldenLayout.unbind('tabCreated', this.tabCreated);
                     this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
                     this._goldenLayout.unbind('stackCreated', this.stackCreated);
@@ -369,12 +376,12 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 this._goldenLayout.destroy();
                 runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
             }
-            this._goldenLayout.on('itemDropped', this.itemDropped);
             this._goldenLayout.on('tabCreated', this.tabCreated);
             this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
             this._goldenLayout.on('stackCreated', this.stackCreated);
             this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer);
             this._goldenLayout.container = this._containerRef.current;
+            //this._goldenLayout.on("stateChanged", () => console.log("STATE CHANGED"));
             if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
                 try {
                     this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
@@ -388,46 +395,39 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     reactionDisposer?: Lambda;
     componentDidMount: () => void = () => {
         if (this._containerRef.current) {
-            const observer = new _global.ResizeObserver(action((entries: any) => {
-                for (const entry of entries) {
-                    this.onResize(null as any);
-                }
-            }));
+            const observer = new _global.ResizeObserver(this.onResize);
             observer.observe(this._containerRef.current);
             this.reactionDisposer = reaction(
-                () => this.props.Document.dockingConfig,
-                () => {
-                    if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
-                        // Because this is in a set timeout, if this component unmounts right after mounting,
-                        // we will leak a GoldenLayout, because we try to destroy it before we ever create it
-                        setTimeout(() => this.setupGoldenLayout(), 1);
+                () => StrCast(this.props.Document.dockingConfig),
+                config => {
+                    if (!this._goldenLayout || this._ignoreStateChange !== config) {
+                        this.setupGoldenLayout();
                         DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false);
                         this.props.Document.dashboardBrush = true;
                     }
                     this._ignoreStateChange = "";
-                }, { fireImmediately: true });
-
+                });
+            setTimeout(() => this.setupGoldenLayout(), 0);
             window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
     componentWillUnmount: () => void = () => {
         try {
             this.props.Document.dashboardBrush = false;
-            this._goldenLayout.unbind('itemDropped', this.itemDropped);
             this._goldenLayout.unbind('tabCreated', this.tabCreated);
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
             this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
         } catch (e) {
 
         }
-        this._goldenLayout && this._goldenLayout.destroy();
+        this._goldenLayout?.destroy();
         runInAction(() => {
             CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1);
             this._goldenLayout = null;
         });
         window.removeEventListener('resize', this.onResize);
 
-        this.reactionDisposer && this.reactionDisposer();
+        this.reactionDisposer?.();
     }
     @action
     onResize = (event: any) => {
@@ -437,26 +437,26 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     }
 
     @action
-    onPointerUp = (e: React.PointerEvent): void => {
+    onPointerUp = (e: MouseEvent): void => {
+        window.removeEventListener("pointerup", this.onPointerUp);
+        this._isPointerDown = false;
         if (this._flush) {
-            this._flush = false;
             setTimeout(() => {
                 CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
                 this.stateChanged();
+                console.log("END BATCH Up");
+                this._flush!.end();
+                this._flush = undefined;
             }, 10);
         }
     }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        this._isPointerDown = true;
-        const onPointerUp = action(() => {
-            window.removeEventListener("pointerup", onPointerUp);
-            this._isPointerDown = false;
-        });
-        window.addEventListener("pointerup", onPointerUp);
-        const className = (e.target as any).className;
-        if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
-            this._flush = true;
+        window.addEventListener("mouseup", this.onPointerUp);
+
+        if (!(e.target as HTMLElement).closest("*.lm_content") && ((e.target as HTMLElement).closest("*.lm_tab") || (e.target as HTMLElement).closest("*.lm_stack"))) {
+            console.log("START BATCH dwn");
+            this._flush = UndoManager.StartBatch("golden layout edit");
         }
         if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
             return;
@@ -464,29 +464,11 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             e.stopPropagation();
         }
     }
-
-    updateDataField = async (json: string) => {
-        const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
-        const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
-
-        const docs = !docids ? [] : (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
-        const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
-        const tabs = Cast(sublists[0], Doc, null);
-        const other = Cast(sublists[1], Doc, null);
-        const tabdocs = DocListCast(tabs.data);
-        const otherdocs = DocListCast(other.data);
-        Doc.GetProto(tabs).data = new List<Doc>(docs);
-        const otherSet = new Set<Doc>();
-        otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
-        tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
-        Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
-    }
-
-    public static async Copy(doc: Doc) {
+    public static Copy(doc: Doc) {
         let json = StrCast(doc.dockingConfig);
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
         const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || [];
-        const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
+        const docs = docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
         const newtabs = docs.map(doc => {
             const copy = Doc.MakeAlias(doc);
             json = json.replace(doc[Id], copy[Id]);
@@ -503,34 +485,36 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         return copy;
     }
 
-    @undoBatch
+    @action
     stateChanged = () => {
         const json = JSON.stringify(this._goldenLayout.toConfig());
-        this.props.Document.dockingConfig = json;
-        this.updateDataField(json);
-    }
-
-    itemDropped = () => {
-        CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-        this.stateChanged();
-    }
+        const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+        const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
+        const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
 
-    htmlToElement(html: string) {
-        const template = document.createElement('template');
-        html = html.trim(); // Never return a text node of whitespace as the result
-        template.innerHTML = html;
-        return template.content.firstChild;
+        this.props.Document.dockingConfig = json;
+        const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
+        const tabs = Cast(sublists[0], Doc, null);
+        const other = Cast(sublists[1], Doc, null);
+        const tabdocs = DocListCast(tabs.data);
+        const otherdocs = DocListCast(other.data);
+        Doc.GetProto(tabs).data = new List<Doc>(docs);
+        const otherSet = new Set<Doc>();
+        otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+        tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+        Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
     }
 
-    tabCreated = async (tab: any) => {
+    tabCreated = (tab: any) => {
         tab.titleElement[0].Tab = tab;
         if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
             if (tab.contentItem.config.fixed) {
                 tab.contentItem.parent.config.fixed = true;
             }
 
-            const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
+            const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId) as Doc;
             if (doc instanceof Doc) {
+                this._tabMap.push({ tab: tab, doc: doc });
                 tab.titleElement[0].onclick = (e: any) => {
                     if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select();
                     tab.titleElement[0].lastClick = Date.now();
@@ -550,8 +534,10 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 gearSpan.style.paddingRight = "12px";
                 const stack = tab.contentItem.parent;
                 tab.element[0].onpointerdown = (e: any) => {
-                    const view = DocumentManager.Instance.getDocumentView(doc);
-                    view && SelectionManager.SelectDoc(view, false);
+                    if (e.target.className !== "lm_close_tab") {
+                        const view = DocumentManager.Instance.getDocumentView(doc);
+                        view && SelectionManager.SelectDoc(view, false);
+                    }
                 };
                 // shifts the focus to this tab when another tab is dragged over it
                 tab.element[0].onmouseenter = (e: any) => {
@@ -594,27 +580,23 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 tab.reactComponents = [gearSpan];
                 tab.element.append(gearSpan);
                 tab.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
-                    tab.titleElement[0].textContent = title, { fireImmediately: true };
+                    tab.titleElement[0].value = title;
                     tab.titleElement[0].style.padding = degree ? 0 : 2;
                     tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
-                });
+                }, { fireImmediately: true });
                 //TODO why can't this just be doc instead of the id?
                 tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
             }
         }
         tab.closeElement.off('click') //unbind the current click handler
-            .click(async function () {
+            .click(function () {
                 tab.selectionDisposer?.();
                 tab.reactionDisposer?.();
                 tab.buttonDisposer?.();
-                const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
+                const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId);
                 if (doc instanceof Doc) {
-                    const theDoc = doc;
-
-                    const recent = await Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc);
-                    if (recent) {
-                        Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-                    }
+                    const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null);
+                    recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                     SelectionManager.DeselectAll();
                 }
                 CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
@@ -624,6 +606,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     }
 
     tabDestroyed = (tab: any) => {
+        const ind = this._tabMap.findIndex((val) => val.tab === tab);
+        ind !== -1 && this._tabMap.splice(ind, 1);
         if (tab.reactComponents) {
             for (const ele of tab.reactComponents) {
                 ReactDOM.unmountComponentAtNode(ele);
@@ -631,15 +615,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         }
     }
 
-    stackActiveChanged = () => {
-        try {
-            CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-            this.stateChanged();
-        } catch (e) { } // catch exception thrown when config has not been initialzed yet
-    }
 
     stackCreated = (stack: any) => {
-        stack.layoutManager.on("activeContentItemChanged", this.stackActiveChanged);
         //stack.header.controlsContainer.find('.lm_popout').hide();
         stack.header.element.on('mousedown', (e: any) => {
             if (e.target === stack.header.element[0] && e.button === 2) {
@@ -651,58 +628,28 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             }
         });
 
-        // starter code for bezel to add new pane
-        // stack.element.on("touchstart", (e: TouchEvent) => {
-        // if (e.targetTouches.length === 2) {
-        //     let pt1 = e.targetTouches.item(0);
-        //     let pt2 = e.targetTouches.item(1);
-        //     let threshold = 40 * window.devicePixelRatio;
-        //     if (pt1 && pt2 && InteractionUtils.TwoPointEuclidist(pt1, pt2) < threshold) {
-        //         let edgeThreshold = 30 * window.devicePixelRatio;
-        //         let center = InteractionUtils.CenterPoint([pt1, pt2]);
-        //         let stackRect: DOMRect = stack.element.getBoundingClientRect();
-        //         let nearLeft = center.X - stackRect.x < edgeThreshold;
-        //         let nearTop = center.Y - stackRect.y < edgeThreshold;
-        //         let nearRight = stackRect.right - center.X < edgeThreshold;
-        //         let nearBottom = stackRect.bottom - center.Y < edgeThreshold;
-        //         let ns = [nearLeft, nearTop, nearRight, nearBottom].filter(n => n);
-        //         if (ns.length === 1) {
-
-        //         }
-        //     }
-        // }
-        // });
         stack.header.controlsContainer.find('.lm_close') //get the close icon
             .off('click') //unbind the current click handler
-            .click(action(async () => {
+            .click(action(() => {
                 //if (confirm('really close this?')) {
-
                 stack.remove();
-                stack.contentItems.forEach(async (contentItem: any) => {
-                    const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
+                stack.contentItems.forEach((contentItem: any) => {
+                    const doc = Cast(DocServer.GetCachedRefField(contentItem.config.props.documentId), Doc, null);
                     if (doc instanceof Doc) {
-                        let recent: Doc | undefined;
-                        if (recent = await Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc)) {
-                            Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-                        }
-                        const theDoc = doc;
+                        const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null);
+                        recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
                     }
                 });
-                //}
             }));
         stack.header.controlsContainer.find('.lm_popout') //get the close icon
             .off('click') //unbind the current click handler
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
-
                 const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null);
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
                 this.AddTab(stack, Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 }));
-
-                // const url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
-                // let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
             }));
     }
 
@@ -710,10 +657,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         if (this.props.renderDepth > 0) {
             return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>;
         }
-        return <div className="collectiondockingview-container" id="menuContainer"
-            onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />;
+        return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />;
     }
-
 }
 
 interface DockedFrameProps {
@@ -725,7 +670,6 @@ interface DockedFrameProps {
 @observer
 export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     _mainCont: HTMLDivElement | null = null;
-    @observable private _libraryPath: Doc[] = [];
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Doc>;
@@ -741,17 +685,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
     constructor(props: any) {
         super(props);
-        DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
-        this.props.libraryPath && this.setupLibraryPath();
-    }
-
-    async setupLibraryPath() {
-        Promise.all(this.props.libraryPath.map(async docid => {
-            const d = await DocServer.GetRefField(docid);
-            return d instanceof Doc ? d : undefined;
-        })).then(action((list: (Doc | undefined)[]) => this._libraryPath = list.filter(d => d).map(d => d as Doc)));
+        this._document = Cast(DocServer.GetCachedRefField(this.props.documentId), Doc, null);
     }
-
     /**
      * Adds a document to the presentation view
      **/
@@ -792,7 +727,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     componentDidMount() {
         const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white");
-        const selected = () => SelectionManager.SelectedDocuments().some(v => Doc.AreProtosEqual(v.props.Document, this._document));
+        const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document);
         const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : "");
         const observer = new _global.ResizeObserver(action((entries: any) => {
             for (const entry of entries) {
@@ -820,6 +755,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document);
                 dv && SelectionManager.SelectDoc(dv, false);
             });
+            (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged();
             !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
         }
     }
@@ -873,7 +809,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         if (doc._viewType === CollectionViewType.Docking) {
             return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
         } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc, libraryPath);
+            return CollectionDockingView.AddRightSplit(doc);
         } else if (location === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         } else if (location === "replace") {
@@ -968,7 +904,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined;
         return <>
             <DocumentView key={document[Id]}
-                LibraryPath={this._libraryPath}
+                LibraryPath={emptyPath}
                 Document={document}
                 DataDoc={resolvedDataDoc}
                 bringToFront={emptyFunction}
@@ -1008,6 +944,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             </div >);
     }
 }
-Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); },
-    "opens up the inputted document on the right side of the screen", "(doc: any)");
+Scripting.addGlobal(function openOnRight(doc: any, dontSelect: boolean = false) { CollectionDockingView.AddRightSplit(doc, dontSelect); },
+    "opens up the inputted document on the right side of the screen", "(doc: any, dontSelect: boolean)");
 Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 66bdcf130..cdfa67769 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -39,7 +39,7 @@ export interface TreeViewProps {
     containingCollection: Doc;
     prevSibling?: Doc;
     renderDepth: number;
-    deleteDoc: (doc: Doc | Doc[]) => boolean;
+    removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
     moveDocument: DragManager.MoveFunction;
     dropAction: dropActionType;
     addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
@@ -119,14 +119,14 @@ class TreeView extends React.Component<TreeViewProps> {
 
     @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight");
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-        return this.doc !== target && this.props.deleteDoc(doc) && addDoc(doc);
+        return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
     }
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
         return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
     }
     @undoBatch @action removeDoc = (doc: Doc | Doc[]) => {
         return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
-            flg && Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), doc), true);
+            flg && Doc.RemoveDocFromList(this.doc, Doc.LayoutFieldKey(this.doc), doc), true);
     }
 
     constructor(props: any) {
@@ -280,8 +280,8 @@ class TreeView extends React.Component<TreeViewProps> {
                 const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
                 const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
                     (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
-                contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
-                    DocListCast(contents), this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+                contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
+                    this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
                     [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
@@ -458,7 +458,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 onDoubleClick={this.onChildDoubleClick}
                 dropAction={this.props.dropAction}
                 moveDocument={this.move}
-                removeDocument={this.removeDoc}
+                removeDocument={this.props.removeDoc}
                 ScreenToLocalTransform={this.getTransform}
                 ContentScaling={returnOne}
                 PanelWidth={this.truncateTitleWidth}
@@ -612,7 +612,7 @@ class TreeView extends React.Component<TreeViewProps> {
             }
 
             const indent = i === 0 ? undefined : () => {
-                if (StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+                if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
                     const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
                     const fieldKey = fieldKeysub.split("\'")[1];
                     if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
@@ -623,7 +623,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             const outdent = !parentCollectionDoc ? undefined : () => {
-                if (StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+                if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
                     const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
                     const fieldKey = fieldKeysub.split("\'")[1];
                     Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
@@ -651,7 +651,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 onCheckedClick={onCheckedClick}
                 onChildClick={onChildClick}
                 renderDepth={renderDepth}
-                deleteDoc={remove}
+                removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
                 addDocument={addDocument}
                 backgroundColor={backgroundColor}
                 panelWidth={rowWidth}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f028b797c..1da0e601a 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -582,10 +582,10 @@ 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._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
             `${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.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 }}>
+            style={{ pointerEvents: this.props.Document._isBackground ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
             <div className="collectionView-facetCont" style={{ display: this.props.PanelPosition === "absolute" ? "flex" : "", justifyContent: this.props.PanelPosition === "absolute" ? "center" : "", width: `calc(100% - ${this.facetWidth()}px)` }}>
                 {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7ba8694ed..b55dc467a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -217,7 +217,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            d._isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -256,7 +256,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
-        // if (this.props.Document.isBackground) return false;
+        // if (this.props.Document._isBackground) return false;
         const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
         if (this.isAnnotationOverlay !== true && de.complete.linkDragData) {
             return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
@@ -384,8 +384,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 clusterColor = colors[cluster % colors.length];
                 const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
-                set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
+                set && set.filter(s => !s._isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
+                set && set.filter(s => s._isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
             }
         }
         return clusterColor;
@@ -839,7 +839,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     bringToFront = action((doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || doc.isBackground) {
+        if (sendToBack || doc._isBackground) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
@@ -935,7 +935,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
-    @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
+    @computed get backgroundActive() { return this.layoutDoc._isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     backgroundHalo = () => BoolCast(this.Document._useClusters);
@@ -1328,7 +1328,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         };
         const snappableDocs: Doc[] = [];  // the set of documents in the visible viewport that we will try to snap to;
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-        this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        this.getActiveDocuments().filter(doc => !doc._isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
         !snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => isDocInView(doc, selRect)); // if not, see if there are background docs to snap to
         !snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => isDocInView(doc, otherBounds)); // if not, then why not snap to floating docs
 
@@ -1422,7 +1422,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
     }
-    @computed get backgroundEvents() { return this.layoutDoc.isBackground && SnappingManager.GetIsDragging(); }
+    @computed get backgroundEvents() { return this.layoutDoc._isBackground && SnappingManager.GetIsDragging(); }
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 864c45ca2..759d142ea 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -355,7 +355,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => {
+    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, _isBackground?: boolean) => {
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = "nested freeform";
@@ -363,8 +363,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
-        newCollection.isBackground = isBackground;
-        newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined;
+        newCollection._isBackground = _isBackground;
+        newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : _isBackground ? "cyan" : undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection.x = this.Bounds.left;
@@ -682,7 +682,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     marqueeSelect(selectBackgrounds: boolean = true) {
         const selRect = this.Bounds;
         const selection: Doc[] = [];
-        this.props.activeDocuments().filter(doc => !doc.isBackground && !doc.z).map(doc => {
+        this.props.activeDocuments().filter(doc => !doc._isBackground && !doc.z).map(doc => {
             const layoutDoc = Doc.Layout(doc);
             const x = NumCast(doc.x);
             const y = NumCast(doc.y);
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index f9ffb4260..c316e1aad 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -148,7 +148,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     &nbsp;
                 </div>);
                 } else {
-                    let contentElement = <EditableView key="editableView"
+                    const contentElement = <EditableView key="editableView"
                         contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
                         height={13}
                         fontSize={10}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 48f10ba26..16b2f7c2a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -252,8 +252,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 boxShadow:
                     this.Opacity === 0 ? undefined :  // if it's not visible, then no shadow
                         this.layoutDoc.z ? `#9c9396  ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                            this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
-                                this.layoutDoc.isBackground ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
+                            this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc._isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                                this.layoutDoc._isBackground ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
                                     StrCast(this.layoutDoc.boxShadow, ""),
                 borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
                 outline: this.Highlight ? "orange solid 2px" : "",
@@ -264,7 +264,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 zIndex: this.ZInd,
                 mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document.isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
+                pointerEvents: this.props.Document._isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
             }} >
 
             {Doc.UserDoc().renderStyle !== "comic" ? (null) :
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 30416b280..eb6988362 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -292,7 +292,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !this.props.Document.isBackground && this.props.bringToFront(this.props.Document);
+            !this.props.Document._isBackground && this.props.bringToFront(this.props.Document);
             if (this._doubleTap && this.props.renderDepth && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
@@ -694,9 +694,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     toggleBackground = () => {
-        this.Document.isBackground = (this.Document.isBackground ? undefined : true);
-        this.Document._overflow = this.Document.isBackground ? "visible" : undefined;
-        if (this.Document.isBackground) {
+        this.Document._isBackground = (this.Document._isBackground ? undefined : true);
+        this.Document._overflow = this.Document._isBackground ? "visible" : undefined;
+        if (this.Document._isBackground) {
             this.props.bringToFront(this.props.Document, true);
             this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = this.Document[WidthSym]();
             this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeHeight"] = this.Document[HeightSym]();
@@ -795,7 +795,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
 
         const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
-        if (collectionAcl === AclAdmin || collectionAcl === AclEdit) moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+        if ((collectionAcl === AclAdmin || collectionAcl === AclEdit) && this.props.removeDocument) {
+            moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+        }
 
         !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
         cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
@@ -991,7 +993,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get ignorePointerEvents() {
         return this.props.pointerEvents === false ||
-            (this.Document.isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
+            (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
             (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None);
     }
     @undoBatch
@@ -1013,11 +1015,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     });
 
     renderLock() {
-        return (this.Document.isBackground !== undefined || this.isSelected(false)) &&
+        return (this.Document._isBackground !== undefined || this.isSelected(false)) &&
             ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) &&
             this.props.renderDepth > 0 && !this.props.treeViewDoc ?
             <div className="documentView-lock" onClick={this.toggleBackground}>
-                <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" />
+                <FontAwesomeIcon icon={this.Document._isBackground ? "unlock" : "lock"} style={{ color: this.Document._isBackground ? "red" : undefined }} size="lg" />
             </div>
             : (null);
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 855160524..6cce5fb87 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -145,7 +145,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
         if (field) {
             const funcs: ContextMenuProps[] = [];
             funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
-            funcs.push({ description: "Make Background", event: () => { this.layoutDoc.isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
+            funcs.push({ description: "Make Background", event: () => { this.layoutDoc._isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
             if (!Doc.UserDoc().noviceMode) {
                 funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
                 funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
@@ -452,7 +452,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
                 width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
                 height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
-                pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
+                pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
                 borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
             }} >
             <CollectionFreeFormView {...this.props}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 50b2af0d7..1ef25c80d 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -92,7 +92,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias.isLinkButton = undefined;
-        alias.isBackground = undefined;
+        alias._isBackground = undefined;
         alias.layoutKey = "layout";
         this.props.addDocTab(alias, "onRight");
     }
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 02c62be95..f60c521ad 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -262,10 +262,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
             const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
             bestTarget && runInAction(() => {
-                bestTarget!._viewTransition = "all 1s";
-                bestTarget!._panX = activeItem.presPinViewX;
-                bestTarget!._panY = activeItem.presPinViewY;
-                bestTarget!._viewScale = activeItem.presPinViewScale;
+                bestTarget._viewTransition = "all 1s";
+                bestTarget._panX = activeItem.presPinViewX;
+                bestTarget._panY = activeItem.presPinViewY;
+                bestTarget._viewScale = activeItem.presPinViewScale;
             });
             //setTimeout(() => targetDoc._viewTransition = undefined, 1010);
         }
@@ -548,7 +548,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     removeDocument = (doc: Doc) => { Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); };
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground) &&
+    active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
@@ -674,7 +674,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             }
             handled = true;
         }
-    })
+    });
 
     /**
      * 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 88c3e6730..85d010aaa 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -185,8 +185,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         this._videoRef = vref;
         if (vref) {
             this._videoRef!.ontimeupdate = this.updateTimecode;
+            // @ts-ignore
             vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
-            this._reactionDisposer && this._reactionDisposer();
+            this._reactionDisposer?.();
             this._reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
                 time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
         }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f0e3a2b54..7758b1815 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -476,7 +476,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 {view}
             </div>;
             {!frozen ? (null) :
-                <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
+                <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc._isBackground ? undefined : "all" }}
                     onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
                     <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
                         <div className="indicator" ref={this._iframeIndicatorRef}></div>
@@ -682,7 +682,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     transform: `scale(${this.props.ContentScaling()})`,
                     width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
                     height: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
-                    pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+                    pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
                 }}
                 onContextMenu={this.specificContextMenu}>
                 <base target="_blank" />
@@ -690,7 +690,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 <div className={"webBox-outerContent"} ref={this._outerRef}
                     style={{
                         width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%",
-                        pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none"
+                        pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc._isBackground ? "all" : "none"
                     }}
                     onWheel={e => e.stopPropagation()}
                     onPointerDown={this.onMarqueeDown}
@@ -709,7 +709,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     }}>
                     <div className={"webBox-innerContent"} style={{
                         height: NumCast(this.layoutDoc.scrollHeight),
-                        pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+                        pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
                     }}>
                         <CollectionFreeFormView {...this.props}
                             PanelHeight={this.props.PanelHeight}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 063cdb0cc..356e36b76 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -94,7 +94,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     private _linkTime: number | null = null;
     private _pause: boolean = false;
 
-    @computed get _recording() { return this.dataDoc.audioState === "recording"; }
+    @computed get _recording() { return this.dataDoc?.audioState === "recording"; }
     set _recording(value) {
         this.dataDoc.audioState = value ? "recording" : undefined;
     }
@@ -818,6 +818,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         );
         this._disposers.editorState = reaction(
             () => {
+                if (!this.dataDoc || !this.layoutDoc) return undefined;
                 if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey + "-textTemplate"]) {
                     return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data;
                 }
@@ -853,13 +854,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         );
         this._disposers.autoHeight = reaction(
-            () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight],
-            () => setTimeout(() => this.tryUpdateHeight(), 0)
+            () => ({
+                width: NumCast(this.layoutDoc._width),
+                autoHeight: this.layoutDoc?._autoHeight
+            }),
+            ({ width, autoHeight }) => width !== undefined && setTimeout(() => this.tryUpdateHeight(), 0)
         );
         this._disposers.height = reaction(
-            () => this.layoutDoc[HeightSym](),
+            () => NumCast(this.layoutDoc._height),
             action(height => {
-                if (height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
+                if (height !== undefined && height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
                     this.layoutDoc._delayAutoHeight = height;
                 }
             })
@@ -1490,7 +1494,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         TraceMobx();
         const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
+        const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground;
         this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
         if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
             setTimeout(() => FormattedTextBoxComment.Hide(), 0);
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 7ff21975b..c33637d0f 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -214,7 +214,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
             PresBox.Instance._selectedArray.splice(PresBox.Instance._selectedArray.indexOf(this.rootDoc), 1);
         }
         e.stopPropagation();
-    })
+    });
 
     render() {
         const className = "presElementBox-item" + (PresBox.Instance._selectedArray.includes(this.rootDoc) ? " presElementBox-active" : "");
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 7e233ecbb..a24761195 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -507,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                     </div>
                     <div className="searchBox-lozenge-dashboard"  >
                         <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
-                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null)!)}>
+                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null))}>
                             {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
                         </select>
                         <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e569dabc1..cb4ca9a25 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -939,16 +939,19 @@ export namespace Doc {
     }
 
     export function IsBrushed(doc: Doc) {
+        return false;
         return computedFn(function IsBrushed(doc: Doc) {
             return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
         })(doc);
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
+        return 0;
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0;
         return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
     }
     export function IsBrushedDegree(doc: Doc) {
+        return 0;
         return computedFn(function IsBrushDegree(doc: Doc) {
             return Doc.IsBrushedDegreeUnmemoized(doc);
         })(doc);
@@ -977,6 +980,7 @@ export namespace Doc {
 
     let _lastDate = 0;
     export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
+        return;
         linkFollowUnhighlight();
         Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
@@ -1128,6 +1132,15 @@ export namespace Doc {
         return false;
     }
 
+    export function copyDragFactory(dragFactory: Doc) {
+        const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
+        if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
+            dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
+            Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
+        }
+        return ndoc;
+    }
+
 
     export namespace Get {
 
@@ -1276,14 +1289,7 @@ Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
 Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
 Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
 Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
-Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) {
-    const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-    if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
-        dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
-        Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
-    }
-    return ndoc;
-});
+Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); });
 Scripting.addGlobal(function copyField(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; });
 Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
 Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index a83fec4ab..76b26a9aa 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -101,7 +101,7 @@ export const documentSchema = createSchema({
     linkDisplay: "boolean",     // whether a link connection should be shown between link anchor endpoints.
     isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
     isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
-    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
+    _isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
     lockedPosition: "boolean",  // whether the document can be moved (dragged)
     _lockedTransform: "boolean",// whether a freeformview can pan/zoom
 
-- 
cgit v1.2.3-70-g09d2


From 7b8924d203c6367edbf7e6ffbe2e8f2e6f24b859 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 29 Aug 2020 00:53:11 -0400
Subject: added some link follow options to link Editor.  Big overhaul of
 dockingView to make things undoable and cleaner

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/CurrentUserUtils.ts                |  12 +-
 src/client/util/DragManager.ts                     |   3 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 .../views/collections/CollectionDockingView.tsx    | 487 ++++++++-------------
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 src/client/views/linking/LinkEditor.tsx            |   8 +
 src/client/views/search/SearchBox.tsx              |   2 +-
 src/fields/Doc.ts                                  |   4 -
 src/mobile/MobileInterface.tsx                     |   4 +-
 10 files changed, 215 insertions(+), 313 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 18ff993fe..9186cea87 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -848,7 +848,7 @@ export namespace Docs {
                     {
                         type: type,
                         content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth, config.path))
+                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth))
                         ]
                     }
                 ]
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c60403701..7937072da 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1025,7 +1025,7 @@ export class CurrentUserUtils {
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
-    public static createNewDashboard = async (userDoc: Doc, id?: string) => {
+    public static createNewDashboard = (userDoc: Doc, id?: string) => {
         const myPresentations = userDoc.myPresentations as Doc;
         const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
         const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
@@ -1051,11 +1051,13 @@ export class CurrentUserUtils {
         dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-        // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => {
-            CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
-        }, 0);
+        CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
+
+    public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
+    public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
+    public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
+    public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
 }
 
 Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0cca61841..8bf6faf03 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -117,10 +117,11 @@ export namespace DragManager {
     }
 
     export class DocumentDragData {
-        constructor(dragDoc: Doc[]) {
+        constructor(dragDoc: Doc[], dropAction?: dropActionType) {
             this.draggedDocuments = dragDoc;
             this.droppedDocuments = [];
             this.offset = [0, 0];
+            this.dropAction = dropAction;
         }
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 84b3d64fd..1cbbfd67b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -99,7 +99,7 @@ export class KeyManager {
                 if (main.isPointerDown) {
                     DragManager.AbortDrag();
                 } else {
-                    if (CollectionDockingView.Instance.HasFullScreen()) {
+                    if (CollectionDockingView.Instance.HasFullScreen) {
                         CollectionDockingView.Instance.CloseFullScreen();
                     } else {
                         doDeselect = !ContextMenu.Instance.closeMenu();
@@ -253,7 +253,7 @@ export class KeyManager {
                 break;
             case "o":
                 const target = SelectionManager.SelectedDocuments()[0];
-                target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target);
+                target && CollectionDockingView.OpenFullScreen(target.props.Document);
                 break;
             case "r":
                 preventDefault = false;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 816f784ff..201cada09 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -37,9 +37,8 @@ const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
 export class CollectionDockingView extends CollectionSubView(doc => doc) {
-    @observable public static Instances: CollectionDockingView[] = [];
-    @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
-    public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
+    @observable public static Instance: CollectionDockingView;
+    public static makeDocumentConfig(document: Doc, width?: number) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -47,79 +46,46 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             width: width,
             props: {
                 documentId: document[Id],
-                libraryPath: libraryPath?.map(d => d[Id])
             }
         };
     }
 
-    @computed public get initialized() {
-        return this._goldenLayout !== null;
-    }
-
-    @observable private _goldenLayout: any = null;
+    private _reactionDisposer?: IReactionDisposer;
     private _containerRef = React.createRef<HTMLDivElement>();
     private _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
-    private _isPointerDown = false;
-    private _maximizedSrc: Opt<DocumentView>;
+    public tabMap: Set<any> = new Set();
+    public get initialized() { return this._goldenLayout !== null; }
+    public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
+    @observable private _goldenLayout: any = null;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
-        runInAction(() => !CollectionDockingView.Instances ? CollectionDockingView.Instances = [this] : CollectionDockingView.Instances.push(this));
+        runInAction(() => CollectionDockingView.Instance = this);
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
         DragManager.StartWindowDrag = this.StartOtherDrag;
     }
+
     public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
-        console.log("START drag batch");
         !this._flush && (this._flush = UndoManager.StartBatch("golden layout drag"));
         const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
-            {
-                type: 'row',
-                content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc))
-            };
+            { type: 'row', content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) };
         const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
-        dragSource._dragListener.on("dragStop", () => dragSource.destroy());
+        dragSource._dragListener.on("dragStop", dragSource.destroy);
         dragSource._dragListener.onMouseDown(e);
     }
 
-    @undoBatch
-    @action
-    public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
-        if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), docView.props.Document);
-        }
-        const document = Doc.MakeAlias(docView.props.Document);
-        const newItemStackConfig = {
-            type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
-        };
-        const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
-        this._goldenLayout.root.contentItems[0].addChild(docconfig);
-        docconfig.callDownwards('_$init');
-        this._goldenLayout._$maximiseItem(docconfig);
-        this._maximizedSrc = docView;
-        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
-        this.stateChanged();
-        SelectionManager.DeselectAll();
-    }
-
     @undoBatch
     public CloseFullScreen = () => {
         const target = this._goldenLayout._maximisedItem;
-        if (target !== null && this._maximizedSrc) {
-            this._goldenLayout._maximisedItem.remove();
-            SelectionManager.SelectDoc(this._maximizedSrc, false);
-            this._maximizedSrc = undefined;
+        if (target) {
+            target.remove();
             this.stateChanged();
         }
     }
 
-    public HasFullScreen = () => {
-        return this._goldenLayout._maximisedItem !== null;
-    }
-
     @undoBatch
     @action
     public static CloseRightSplit(document: Opt<Doc>): boolean {
@@ -136,17 +102,29 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false :
             Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose));
 
-        retVal && CollectionDockingView.Instance.layoutChanged(document);
+        retVal && document && CollectionDockingView.Instance.layoutChanged();
         return retVal;
     }
 
     @undoBatch
     @action
-    layoutChanged(removed?: Doc) {
-        this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
-        this._goldenLayout.emit('stateChanged');
-        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
-        this.stateChanged();
+    public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) {
+        const instance = CollectionDockingView.Instance;
+        if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
+            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        }
+        const newItemStackConfig = {
+            type: 'stack',
+            content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc), undefined)]
+        };
+        const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+        instance._goldenLayout.root.contentItems[0].addChild(docconfig);
+        docconfig.callDownwards('_$init');
+        instance._goldenLayout._$maximiseItem(docconfig);
+        instance._goldenLayout.emit('stateChanged');
+        instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
+        instance.stateChanged();
+        return true;
     }
 
     public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
@@ -156,14 +134,16 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
                 if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
                     DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) {
-                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
+                    runInAction(() => document.isDisplayPanel = true);
                     child.addChild(newItemStackConfig, undefined);
                     !addToSplit && child.contentItems[0].remove();
                     return true;
                 }
                 return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
                     if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) {
-                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
+                        runInAction(() => document.isDisplayPanel = true);
                         child.addChild(newItemStackConfig, undefined);
                         !addToSplit && child.contentItems[j].remove();
                         return true;
@@ -171,7 +151,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     return false;
                 });
             });
-        retVal && instance.layoutChanged(document);
+        retVal && instance.layoutChanged();
         return retVal;
     }
 
@@ -183,9 +163,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     public static AddRightSplit(document: Doc, dontSelect: boolean = false, isDisplayPanel: Opt<boolean> = undefined) {
         if (!CollectionDockingView.Instance) return false;
 
-        const ind = CollectionDockingView.Instance._tabMap.findIndex((val) => val.doc === document);
+        const ind = Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === document);
         if (ind !== -1) {
-            const tab = CollectionDockingView.Instance._tabMap[ind].tab;
+            const tab = Array.from(CollectionDockingView.Instance.tabMap.keys())[ind];
             const activeContentItem = tab.header.parent.getActiveContentItem();
             if (tab.contentItem !== activeContentItem) {
                 tab.header.parent.setActiveContentItem(tab.contentItem);
@@ -198,7 +178,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             const instance = CollectionDockingView.Instance;
             const newItemStackConfig = {
                 type: 'stack',
-                content: [CollectionDockingView.makeDocumentConfig(document, undefined, [])]
+                content: [CollectionDockingView.makeDocumentConfig(document, undefined)]
             };
 
             const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -238,7 +218,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
+            content: [CollectionDockingView.makeDocumentConfig(document, undefined)]
         };
 
         const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -299,16 +279,20 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
+    @undoBatch
     public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
         if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
-            CollectionDockingView.AddRightSplit(document, false, true);
+            return CollectionDockingView.AddRightSplit(document, false, true);
         }
+        return false;
     }
 
-    public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+    @undoBatch
+    public static AddTab(stack: any, document: Doc, libraryPath?: Doc[]) {
+        const instance = CollectionDockingView.Instance;
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
         if (stack === undefined) {
-            let stack: any = this._goldenLayout.root;
+            stack = instance._goldenLayout.root;
             while (!stack.isStack) {
                 if (stack.contentItems.length) {
                     stack = stack.contentItems[0];
@@ -318,41 +302,21 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     break;
                 }
             }
-            if (stack) {
-                stack.addChild(docContentConfig);
-            }
-        } else {
-            stack.addChild(docContentConfig, undefined);
         }
-        this.layoutChanged();
+        stack?.addChild(docContentConfig, undefined);
+        instance.layoutChanged();
         return true;
     }
 
-    public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
-        if (stack === undefined) {
-            let stack: any = this._goldenLayout.root;
-            while (!stack.isStack) {
-                if (stack.contentItems.length) {
-                    stack = stack.contentItems[0];
-                } else {
-                    stack.addChild({ type: 'stack', content: [docContentConfig] });
-                    stack = undefined;
-                    break;
-                }
-            }
-            if (stack) {
-                stack.addChild(docContentConfig);
-            }
-        } else {
-            stack.addChild(docContentConfig, undefined);
-        }
-        this.layoutChanged();
-        return true;
+    @undoBatch
+    @action
+    layoutChanged() {
+        this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
+        this._goldenLayout.emit('stateChanged');
+        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
+        this.stateChanged();
     }
 
-    _tabMap: { tab: any, doc: Doc }[] = [];
-
     async setupGoldenLayout() {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
@@ -360,27 +324,23 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? [];
             await Promise.all(docids.map(id => DocServer.GetRefField(id)));
 
-            if (!this._goldenLayout) {
-                runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
-            }
-            else {
+            if (this._goldenLayout) {
                 if (config === JSON.stringify(this._goldenLayout.toConfig())) {
                     return;
+                } else {
+                    try {
+                        this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
+                        this._goldenLayout.unbind('stackCreated', this.stackCreated);
+                    } catch (e) { }
                 }
-                try {
-                    this._goldenLayout.unbind('tabCreated', this.tabCreated);
-                    this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
-                    this._goldenLayout.unbind('stackCreated', this.stackCreated);
-                } catch (e) { }
-                this._goldenLayout.destroy();
-                runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
             }
-            this._goldenLayout.on('tabCreated', this.tabCreated);
+            this.tabMap.clear();
+            this._goldenLayout?.destroy();
+            runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
             this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
             this._goldenLayout.on('stackCreated', this.stackCreated);
             this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer);
             this._goldenLayout.container = this._containerRef.current;
-            //this._goldenLayout.on("stateChanged", () => console.log("STATE CHANGED"));
             if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
                 try {
                     this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
@@ -391,17 +351,15 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             this._goldenLayout.init();
         }
     }
-    reactionDisposer?: Lambda;
+
     componentDidMount: () => void = () => {
         if (this._containerRef.current) {
-            const observer = new _global.ResizeObserver(this.onResize);
-            observer.observe(this._containerRef.current);
-            this.reactionDisposer = reaction(
-                () => StrCast(this.props.Document.dockingConfig),
+            new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
+            this._reactionDisposer = reaction(() => StrCast(this.props.Document.dockingConfig),
                 config => {
                     if (!this._goldenLayout || this._ignoreStateChange !== config) {
                         this.setupGoldenLayout();
-                        DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false);
+                        DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false);
                         this.props.Document.dashboardBrush = true;
                     }
                     this._ignoreStateChange = "";
@@ -410,24 +368,19 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
+
     componentWillUnmount: () => void = () => {
         try {
             this.props.Document.dashboardBrush = false;
-            this._goldenLayout.unbind('tabCreated', this.tabCreated);
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
             this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
-        } catch (e) {
-
-        }
+        } catch (e) { }
         this._goldenLayout?.destroy();
-        runInAction(() => {
-            CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1);
-            this._goldenLayout = null;
-        });
         window.removeEventListener('resize', this.onResize);
 
-        this.reactionDisposer?.();
+        this._reactionDisposer?.();
     }
+
     @action
     onResize = (event: any) => {
         const cur = this._containerRef.current;
@@ -438,31 +391,29 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @action
     onPointerUp = (e: MouseEvent): void => {
         window.removeEventListener("pointerup", this.onPointerUp);
-        this._isPointerDown = false;
         if (this._flush) {
             setTimeout(() => {
                 CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
                 this.stateChanged();
-                console.log("END BATCH Up");
                 this._flush!.end();
                 this._flush = undefined;
             }, 10);
         }
     }
+
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         window.addEventListener("mouseup", this.onPointerUp);
 
         if (!(e.target as HTMLElement).closest("*.lm_content") && ((e.target as HTMLElement).closest("*.lm_tab") || (e.target as HTMLElement).closest("*.lm_stack"))) {
-            console.log("START BATCH dwn");
             this._flush = UndoManager.StartBatch("golden layout edit");
         }
-        if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
-            return;
-        } else {
+        if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
+            Doc.GetSelectedTool() !== InkTool.Highlighter && Doc.GetSelectedTool() !== InkTool.Pen) {
             e.stopPropagation();
         }
     }
+
     public static Copy(doc: Doc) {
         let json = StrCast(doc.dockingConfig);
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -504,124 +455,18 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
     }
 
-    tabCreated = (tab: any) => {
-        tab.titleElement[0].Tab = tab;
-        if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
-            if (tab.contentItem.config.fixed) {
-                tab.contentItem.parent.config.fixed = true;
-            }
-
-            const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId) as Doc;
-            if (doc instanceof Doc) {
-                this._tabMap.push({ tab: tab, doc: doc });
-                tab.titleElement[0].onclick = (e: any) => {
-                    if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select();
-                    tab.titleElement[0].lastClick = Date.now();
-                    tab.titleElement[0].focus();
-                };
-                tab.titleElement[0].onchange = (e: any) => {
-                    tab.titleElement[0].size = e.currentTarget.value.length + 1;
-                    Doc.GetProto(doc).title = e.currentTarget.value, true;
-                };
-                tab.titleElement[0].size = StrCast(doc.title).length + 1;
-                tab.titleElement[0].value = doc.title;
-                tab.titleElement[0].style["max-width"] = "100px";
-                const gearSpan = document.createElement("span");
-                gearSpan.className = "collectionDockingView-gear";
-                gearSpan.style.position = "relative";
-                gearSpan.style.paddingLeft = "0px";
-                gearSpan.style.paddingRight = "12px";
-                const stack = tab.contentItem.parent;
-                tab.element[0].onpointerdown = (e: any) => {
-                    if (e.target.className !== "lm_close_tab") {
-                        const view = DocumentManager.Instance.getDocumentView(doc);
-                        view && SelectionManager.SelectDoc(view, false);
-                    }
-                };
-                // shifts the focus to this tab when another tab is dragged over it
-                tab.element[0].onmouseenter = (e: any) => {
-                    if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return;
-                    const activeContentItem = tab.header.parent.getActiveContentItem();
-                    if (tab.contentItem !== activeContentItem) {
-                        tab.header.parent.setActiveContentItem(tab.contentItem);
-                    }
-                    tab.setActive(true);
-                };
-                const onDown = (e: React.PointerEvent) => {
-                    setupMoveUpEvents(this, e, (e) => {
-                        if (!(e as any).defaultPrevented) {
-                            const dragData = new DragManager.DocumentDragData([doc]);
-                            dragData.dropAction = doc.dropAction as dropActionType;
-                            DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
-                            return true;
-                        }
-                        return false;
-                    }, returnFalse, emptyFunction);
-                };
-
-                tab.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
-                    (sel) => {
-                        const selected = sel.some(v => v.props.Document === doc);
-                        selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem);
-                    }
-                );
-                tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)),
-                    (views) => {
-                        if (views.length) {
-                            ReactDOM.render(<span title="Drag as document" className="collectionDockingView-dragAsDocument" onPointerDown={onDown} >
-                                <DockingViewButtonSelector views={() => views} Stack={stack} />
-                            </span>,
-                                gearSpan);
-                            tab.buttonDisposer?.();
-                        }
-                    }, { fireImmediately: true });
-
-                tab.reactComponents = [gearSpan];
-                tab.element.append(gearSpan);
-                tab.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
-                    tab.titleElement[0].value = title;
-                    tab.titleElement[0].style.padding = degree ? 0 : 2;
-                    tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
-                }, { fireImmediately: true });
-                //TODO why can't this just be doc instead of the id?
-                tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
-            }
-        }
-        tab.closeElement.off('click') //unbind the current click handler
-            .click(function () {
-                tab.selectionDisposer?.();
-                tab.reactionDisposer?.();
-                tab.buttonDisposer?.();
-                const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId);
-                if (doc instanceof Doc) {
-                    const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null);
-                    recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-                    SelectionManager.DeselectAll();
-                }
-                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-                tab.contentItem.remove();
-                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-            });
-    }
-
     tabDestroyed = (tab: any) => {
-        const ind = this._tabMap.findIndex((val) => val.tab === tab);
-        ind !== -1 && this._tabMap.splice(ind, 1);
-        if (tab.reactComponents) {
-            for (const ele of tab.reactComponents) {
-                ReactDOM.unmountComponentAtNode(ele);
-            }
-        }
+        this.tabMap.delete(tab);
+        Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+        tab.reactComponents && Array.from(tab.reactComponents).forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
     }
 
-
     stackCreated = (stack: any) => {
-        //stack.header.controlsContainer.find('.lm_popout').hide();
         stack.header.element.on('mousedown', (e: any) => {
             if (e.target === stack.header.element[0] && e.button === 2) {
-                const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null);
+                const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-                this.AddTab(stack, Docs.Create.FreeformDocument([], {
+                CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 }));
             }
@@ -632,59 +477,125 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             .click(action(() => {
                 //if (confirm('really close this?')) {
                 stack.remove();
-                stack.contentItems.forEach((contentItem: any) => {
-                    const doc = Cast(DocServer.GetCachedRefField(contentItem.config.props.documentId), Doc, null);
-                    if (doc instanceof Doc) {
-                        const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null);
-                        recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-                    }
-                });
+                stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true));
             }));
         stack.header.controlsContainer.find('.lm_popout') //get the close icon
             .off('click') //unbind the current click handler
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
-                const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null);
+                const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-                this.AddTab(stack, Docs.Create.FreeformDocument([], {
+                CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 }));
             }));
     }
 
     render() {
-        if (this.props.renderDepth > 0) {
-            return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>;
-        }
-        return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />;
+        return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef}>
+            {this.props.renderDepth > 0 ? "Nested dashboards can't be rendered" : (null)}
+        </div>;
     }
 }
 
 interface DockedFrameProps {
     documentId: FieldId;
     glContainer: any;
-    libraryPath: (FieldId[]);
-    //collectionDockingView: CollectionDockingView
 }
 @observer
 export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     _mainCont: HTMLDivElement | null = null;
+    _tabReaction: IReactionDisposer | undefined;
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
-    @observable private _document: Opt<Doc>;
     @observable private _isActive: boolean = false;
-    _tabReaction: IReactionDisposer | undefined;
 
-    get _stack(): any {
-        return (this.props as any).glContainer.parent.parent;
-    }
-    get _tab(): any {
-        const tab = (this.props as any).glContainer.tab?.element[0] as HTMLElement;
-        return tab?.getElementsByClassName("lm_title")?.[0];
-    }
+    get stack(): any { return (this.props as any).glContainer.parent.parent; }
+    get tab() { return (this.props as any).glContainer.tab; }
+    get tabTitle(): any { return (this.tab?.element[0] as HTMLElement).getElementsByClassName("lm_title")?.[0]; }
+    get view() { return this._document && DocumentManager.Instance.getDocumentView(this._document); }
+    @observable _document: Doc | undefined;
+
     constructor(props: any) {
         super(props);
-        this._document = Cast(DocServer.GetCachedRefField(this.props.documentId), Doc, null);
+        setTimeout(() => DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(doc => this.init(doc as Doc)), 0);
+    }
+
+    @action
+    init = (doc: Doc) => {
+        const tab = this.tab;
+        tab._disposers = {} as { [name: string]: IReactionDisposer };
+        tab.DashDoc = this._document = doc;
+        if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+            tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
+
+            CollectionDockingView.Instance.tabMap.add(tab);
+            tab.titleElement[0].onclick = (e: any) => {
+                if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select();
+                tab.titleElement[0].lastClick = Date.now();
+                tab.titleElement[0].focus();
+            };
+            tab.titleElement[0].onchange = (e: any) => {
+                tab.titleElement[0].size = e.currentTarget.value.length + 1;
+                Doc.GetProto(doc).title = e.currentTarget.value;
+            };
+            tab.titleElement[0].size = StrCast(this._document.title).length + 1;
+            tab.titleElement[0].value = this._document.title;
+            tab.titleElement[0].style["max-width"] = "100px";
+            const gearSpan = document.createElement("span");
+            gearSpan.className = "collectionDockingView-gear";
+            gearSpan.style.position = "relative";
+            gearSpan.style.paddingLeft = "0px";
+            gearSpan.style.paddingRight = "12px";
+            const stack = tab.contentItem.parent;
+            tab.element[0].onpointerdown = (e: any) => {
+                e.target.className !== "lm_close_tab" && this.view && SelectionManager.SelectDoc(this.view, false);
+            };
+            // shifts the focus to this tab when another tab is dragged over it
+            tab.element[0].onmouseenter = (e: MouseEvent) => {
+                if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
+                    tab.header.parent.setActiveContentItem(tab.contentItem);
+                }
+                tab.setActive(true);
+            };
+            const onDown = (e: React.PointerEvent) => {
+                setupMoveUpEvents(this, e, (e) => {
+                    !e.defaultPrevented && DragManager.StartDocumentDrag([gearSpan], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY);
+                    return !e.defaultPrevented;
+                }, returnFalse, emptyFunction);
+            };
+
+            tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => v.props.Document === doc),
+                (selected) => {
+                    selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem);
+                }
+            );
+            tab._disposers.buttonDisposer = reaction(() => this.view,
+                (view) => {
+                    if (view) {
+                        ReactDOM.render(<span title="Drag as document" className="collectionDockingView-dragAsDocument" onPointerDown={onDown} >
+                            <DockingViewButtonSelector views={() => [view]} Stack={stack} />
+                        </span>,
+                            gearSpan);
+                        tab._disposers.buttonDisposer?.();
+                    }
+                }, { fireImmediately: true });
+
+            tab.reactComponents = [gearSpan];
+            tab.element.append(gearSpan);
+            tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
+                tab.titleElement[0].value = title;
+                tab.titleElement[0].style.padding = degree ? 0 : 2;
+                tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
+            }, { fireImmediately: true });
+        }
+        tab.closeElement.off('click') //unbind the current click handler
+            .click(function () {
+                Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+                Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
+                SelectionManager.DeselectAll();
+                tab.contentItem.remove();
+            });
     }
     /**
      * Adds a document to the presentation view
@@ -695,7 +606,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         if (unpin) DockedFrameRenderer.UnpinDoc(doc);
         else {
             //add this new doc to props.Document
-            const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+            const curPres = CurrentUserUtils.ActivePresentation;
             if (curPres) {
                 const pinDoc = Doc.MakeAlias(doc);
                 pinDoc.presentationTargetDoc = doc;
@@ -717,7 +628,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @action
     public static UnpinDoc(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const curPres = CurrentUserUtils.ActivePresentation;
         if (curPres) {
             const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
             ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]);
@@ -727,7 +638,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     componentDidMount() {
         const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white");
         const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document);
-        const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : "");
+        const updateTabColor = () => this.tabTitle && (this.tabTitle.style.backgroundColor = selected() ? color() : "");
         const observer = new _global.ResizeObserver(action((entries: any) => {
             for (const entry of entries) {
                 this._panelWidth = entry.contentRect.width;
@@ -750,10 +661,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     private onActiveContentItemChanged() {
         if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) {
             this._isActive = this.props.glContainer.tab.isActive;
-            this._isActive && setTimeout(() => {
-                const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document);
-                dv && SelectionManager.SelectDoc(dv, false);
-            });
+            this._isActive && setTimeout(() => this.view && SelectionManager.SelectDoc(this.view, false), 0);
             (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged();
             !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
         }
@@ -764,7 +672,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
         (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
     panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
-
     nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
     nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
 
@@ -778,17 +685,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth))) {
             scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
         } else if (nativeW && nativeH) {
-            // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) {
-            //     if ((this.layoutDoc?._fitWidth) ||
-            //         this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
-            //         return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
-            //     } else {
-            //         return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
-            //     }
-            // }
             const wscale = this.panelWidth() / nativeW;
             scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
-        } else scaling = 1;
+        }
         return scaling;
     }
 
@@ -805,17 +704,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
-        if (doc._viewType === CollectionViewType.Docking) {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-        } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc);
-        } else if (location === "close") {
-            return CollectionDockingView.CloseRightSplit(doc);
-        } else if (location === "replace") {
-            CollectionDockingView.UseRightSplit(doc);
-            return true;
-        } else {// if (location === "inPlace") {
-            return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
+        if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        switch (location) {
+            case "onRight": return CollectionDockingView.AddRightSplit(doc);
+            case "close": return CollectionDockingView.CloseRightSplit(doc);
+            case "replace": return CollectionDockingView.UseRightSplit(doc);
+            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+            case "inPlace":
+            default: return CollectionDockingView.AddTab(this.stack, doc);
         }
     }
 
@@ -833,7 +729,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null);
     returnMiniSize = () => NumCast(this._document?._miniMapSize, 150);
     miniDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+        this._document && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
             this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim);
             this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim);
             return false;
@@ -899,12 +795,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @computed get docView() {
         TraceMobx();
         if (!this._document) return (null);
-        const document = this._document;
         const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined;
         return <>
-            <DocumentView key={document[Id]}
+            <DocumentView key={this._document[Id]}
                 LibraryPath={emptyPath}
-                Document={document}
+                Document={this._document}
                 DataDoc={resolvedDataDoc}
                 bringToFront={emptyFunction}
                 rootSelected={returnTrue}
@@ -927,7 +822,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
-            {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
+            {this._document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
         </>;
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index cdfa67769..fffbe65a3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -738,7 +738,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) {
+        if (!e.isPropagationStopped() && this.doc === CurrentUserUtils.MyDashboards) {
             ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
             e.stopPropagation();
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 11a905fb6..8e391f995 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -379,6 +379,14 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                         onPointerDown={() => this.changeFollowBehavior("onRight")}>
                         Always open in a new pane
                         </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("replace")}>
+                        Always replace right tab
+                        </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
+                        Always open full screen
+                        </div>
                     <div className="linkEditor-followingDropdown-option"
                         onPointerDown={() => this.changeFollowBehavior("inTab")}>
                         Always open in a new tab
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index a24761195..d7a8ed404 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -492,7 +492,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     }
     render() {
-        const myDashboards = DocListCast(Cast(Doc.UserDoc().myDashboards, Doc, null).data);
+        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
                 <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index cb4ca9a25..1401dd6a1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -939,19 +939,16 @@ export namespace Doc {
     }
 
     export function IsBrushed(doc: Doc) {
-        return false;
         return computedFn(function IsBrushed(doc: Doc) {
             return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
         })(doc);
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        return 0;
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0;
         return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
     }
     export function IsBrushedDegree(doc: Doc) {
-        return 0;
         return computedFn(function IsBrushDegree(doc: Doc) {
             return Doc.IsBrushedDegreeUnmemoized(doc);
         })(doc);
@@ -980,7 +977,6 @@ export namespace Doc {
 
     let _lastDate = 0;
     export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
-        return;
         linkFollowUnhighlight();
         Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 841862f49..d4eb76ecd 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -324,7 +324,7 @@ export class MobileInterface extends React.Component {
             );
         }
         // stores dashboards documents as 'dashboards' variable
-        let dashboards = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+        let dashboards = CurrentUserUtils.MyDashboards;
         if (this.dashboards) {
             dashboards = this.dashboards;
         }
@@ -394,7 +394,7 @@ export class MobileInterface extends React.Component {
      */
     @action
     createNewDashboard = async (id?: string) => {
-        const scens = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+        const scens = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
-- 
cgit v1.2.3-70-g09d2


From 4f3c8cfab12f1c5fff27faa0998a286d210148d2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 29 Aug 2020 16:39:17 -0400
Subject: major restructure of how addDocTabs works.  now you can add/replace
 with options for left/right/top/bottom and specific panel names

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/util/DictationManager.ts                |   4 +-
 src/client/util/DocumentManager.ts                 |   4 +-
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 src/client/views/MainView.tsx                      |   8 +-
 src/client/views/PropertiesButtons.tsx             |   4 +-
 src/client/views/PropertiesDocContextSelector.tsx  |   2 +-
 src/client/views/PropertiesView.tsx                |   2 +-
 src/client/views/animationtimeline/Keyframe.tsx    |   2 +-
 .../views/collections/CollectionDockingView.tsx    | 298 ++++++++-------------
 .../views/collections/CollectionSchemaCells.tsx    |   2 +-
 .../CollectionSchemaMovableTableHOC.tsx            |   2 +-
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 src/client/views/collections/CollectionView.tsx    |   8 +-
 src/client/views/collections/SchemaTable.tsx       |   2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   4 +-
 src/client/views/linking/LinkEditor.tsx            |  20 +-
 src/client/views/linking/LinkMenuItem.tsx          |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  16 +-
 src/client/views/nodes/FontIconBox.tsx             |   2 +-
 src/client/views/nodes/KeyValueBox.tsx             |   2 +-
 src/client/views/nodes/KeyValuePair.tsx            |   2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |   6 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   6 +-
 src/client/views/nodes/PresBox.tsx                 |   6 +-
 src/client/views/nodes/VideoBox.tsx                |   2 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  12 +-
 .../formattedText/FormattedTextBoxComment.tsx      |  16 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |   2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |   4 +-
 .../views/nodes/formattedText/RichTextRules.ts     |   2 +-
 src/client/views/nodes/formattedText/nodes_rts.ts  |   4 +-
 src/client/views/pdf/Annotation.tsx                |   2 +-
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/fields/RichTextUtils.ts                        |   2 +-
 39 files changed, 202 insertions(+), 268 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2ca1b95d7..262086735 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -847,7 +847,7 @@ export namespace Docs {
                     {
                         type: type,
                         content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, false, config.initialWidth))
+                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))
                         ]
                     }
                 ]
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7937072da..20f8fe9c1 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1063,7 +1063,7 @@ export class CurrentUserUtils {
 Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
     const copy = Doc.copyDragFactory(dragFactory);
     if (copy) {
-        CollectionDockingView.AddRightSplit(copy);
+        CollectionDockingView.AddSplit(copy, "right");
         const view = DocumentManager.Instance.getFirstDocumentView(copy);
         view && SelectionManager.SelectDoc(view, false);
     }
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index e66c15fcb..231e1fa8d 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -323,7 +323,7 @@ export namespace DictationManager {
             ["open fields", {
                 action: (target: DocumentView) => {
                     const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
-                    target.props.addDocTab(kvp, "onRight");
+                    target.props.addDocTab(kvp, "add:right");
                 }
             }],
 
@@ -337,7 +337,7 @@ export namespace DictationManager {
                     const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
                     proto.data = new RichTextField(proseMirrorState);
                     proto.backgroundColor = "#eeffff";
-                    target.props.addDocTab(newBox, "onRight");
+                    target.props.addDocTab(newBox, "add:right");
                 }
             }]
 
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fb54fbefc..93c37dbd1 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -124,7 +124,7 @@ export class DocumentManager {
     }
 
     static addRightSplit = (doc: Doc, finished?: () => void) => {
-        CollectionDockingView.AddRightSplit(doc);
+        CollectionDockingView.AddSplit(doc, "right");
         finished?.();
     }
     public jumpToDocument = async (
@@ -231,7 +231,7 @@ export class DocumentManager {
                     containerDoc._currentTimecode = targetTimecode;
                     const targetContext = await target?.context as Doc;
                     const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
-                    DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished);
+                    DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
                 } else {
                     finished?.();
                 }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index dd223390a..764a2547c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -156,7 +156,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                             dataDoc.googleDoc = googleDoc;
                         }
-                        CollectionDockingView.AddRightSplit(googleDoc);
+                        CollectionDockingView.AddSplit(googleDoc, "right");
                     } else if (e.altKey) {
                         e.preventDefault();
                         window.open(googleDocUrl);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6c64a3714..c7a24133c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -188,7 +188,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
                 alias.context = undefined;
                 //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
-                CollectionDockingView.AddRightSplit(alias);
+                CollectionDockingView.AddSplit(alias, "right");
             }
         }
         SelectionManager.DeselectAll();
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 2794a9f4e..db3e3d0dd 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -210,7 +210,7 @@ export class KeyManager {
                         return { stopPropagation: false, preventDefault: false };
                     }
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform);
+                MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right");
                 break;
             case "arrowleft":
                 if (document.activeElement) {
@@ -218,7 +218,7 @@ export class KeyManager {
                         return { stopPropagation: false, preventDefault: false };
                     }
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.CloseRightSplit(MainView.Instance.mainFreeform);
+                MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform);
                 break;
             case "backspace":
                 if (document.activeElement) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6a960f4d5..7b0c6fa9f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -236,7 +236,7 @@ export class MainView extends React.Component {
                 reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
                     initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
                         if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
-                            CollectionDockingView.AddRightSplit(docField);
+                            CollectionDockingView.AddSplit(docField, "right");
                         }
                     }),
                 );
@@ -260,7 +260,7 @@ export class MainView extends React.Component {
         }
         const pres = Docs.Create.PresDocument(new List<Doc>(),
             { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
-        CollectionDockingView.AddRightSplit(pres);
+        CollectionDockingView.AddSplit(pres, "right");
         Doc.UserDoc().activePresentation = pres;
         const myPresentations = Doc.UserDoc().myPresentations as Doc;
         Doc.AddDocToList(myPresentations, "data", pres);
@@ -382,9 +382,9 @@ export class MainView extends React.Component {
     @computed get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); }
     flyoutWidthFunc = () => this.flyoutWidth;
     addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
-        return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
+        return where === "close" ? CollectionDockingView.CloseSplit(doc) :
             doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) :
-                CollectionDockingView.AddRightSplit(doc);
+                CollectionDockingView.AddSplit(doc, "right");
     }
     sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0);
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index e95b28d6c..6ac6571d3 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -160,7 +160,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                                 googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                                 dataDoc.googleDoc = googleDoc;
                             }
-                            CollectionDockingView.AddRightSplit(googleDoc);
+                            CollectionDockingView.AddRight(googleDoc, "right");
                         } else if (e.altKey) {
                             e.preventDefault();
                             window.open(googleDocUrl);
@@ -343,7 +343,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 dv.toggleFollowLink("inPlace", true, false);
             } else if (value === "linkOnRight") {
                 dv.noOnClick();
-                dv.toggleFollowLink("onRight", false, false);
+                dv.toggleFollowLink("add:right", false, false);
             }
         });
     }
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index b382298f3..427748fe7 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -44,7 +44,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
             col._panX = NumCast(target.x) + NumCast(target._width) / 2;
             col._panY = NumCast(target.y) + NumCast(target._height) / 2;
         }
-        this.props.addDocTab(col, "inTab");
+        this.props.addDocTab(col, "add:right");
     }
 
     render() {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index fef0a3e00..d9ab5d029 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -258,7 +258,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get contexts() {
-        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddRightSplit(doc)} />;
+        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />;
     }
 
     previewBackground = () => "lightgrey";
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index 4fb362ab1..e84022366 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -331,7 +331,7 @@ export class Keyframe extends React.Component<IProps> {
         }),
             TimelineMenu.Instance.addItem("button", "Show Data", action(() => {
                 const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 });
-                CollectionDockingView.AddRightSplit(kvp);
+                CollectionDockingView.AddSplit(kvp, "right");
             })),
             TimelineMenu.Instance.addItem("button", "Delete", action(() => {
                 (this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8301d3df8..62c865436 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,6 +1,6 @@
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { clamp } from 'lodash';
+import { clamp, pull } from 'lodash';
 import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
@@ -38,7 +38,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 @observer
 export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @observable public static Instance: CollectionDockingView;
-    public static makeDocumentConfig(document: Doc, isDisplayPanel?: boolean, width?: number) {
+    public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -46,7 +46,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             width: width,
             props: {
                 documentId: document[Id],
-                isDisplayPanel   // flag for whether a tab should be considered a placeholder that has its contents replaced with new content
+                panelName   // name of tab that can be used to close or replace its contents
             }
         };
     }
@@ -85,26 +85,20 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     }
 
     @undoBatch
-    @action
-    public static CloseRightSplit(document: Opt<Doc>): boolean {
-        const tryClose = (childItem: any) => {
-            if (childItem.config?.component === "DocumentFrameRenderer") {
-                if ((!document && childItem.config.props.isDisplayPanel) || (document && childItem.config.props.documentId === document[Id])) {
-                    childItem.remove();
-                    return true;
-                }
+    public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
+        const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document);
+        if (tab) {
+            const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
+            if (j !== -1) {
+                tab.header.parent.contentItems[j].remove();
+                return CollectionDockingView.Instance.layoutChanged();
             }
-            return false;
-        };
-        const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false :
-            Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose));
+        }
 
-        retVal && document && CollectionDockingView.Instance.layoutChanged();
-        return retVal;
+        return false;
     }
 
     @undoBatch
-    @action
     public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) {
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
@@ -124,176 +118,94 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         return true;
     }
 
-    public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
-        if (!CollectionDockingView.Instance) return false;
-        const instance = CollectionDockingView.Instance;
-        const retVal = !instance._goldenLayout.root.contentItems[0].isRow ? false :
-            Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
-                if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
-                    child.contentItems[0].config.isDisplayPanel) {
-                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, true);
-                    child.addChild(newItemStackConfig, undefined);
-                    !addToSplit && child.contentItems[0].remove();
-                    return true;
-                }
-                return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
-                    if (tab.config.props.isDisplayPanel) {
-                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, true);
-                        child.addChild(newItemStackConfig, undefined);
-                        !addToSplit && child.contentItems[j].remove();
-                        return true;
-                    }
-                    return false;
-                });
-            });
-        retVal && instance.layoutChanged();
-        return retVal;
-    }
-
-    //
-    //  Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split
-    //
     @undoBatch
-    @action
-    public static AddRightSplit(document: Doc, isDisplayPanel: Opt<boolean> = undefined) {
-        if (!CollectionDockingView.Instance) return false;
-
-        const ind = Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === document);
-        if (ind !== -1) {
-            const tab = Array.from(CollectionDockingView.Instance.tabMap.keys())[ind];
-            const activeContentItem = tab.header.parent.getActiveContentItem();
-            if (tab.contentItem !== activeContentItem) {
-                tab.header.parent.setActiveContentItem(tab.contentItem);
-            }
-            tab.setActive(true);
-        } else {
-            if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
-            const instance = CollectionDockingView.Instance;
-            const newItemStackConfig = {
-                type: 'stack',
-                content: [CollectionDockingView.makeDocumentConfig(document, isDisplayPanel)]
-            };
-
-            const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-
-            if (instance._goldenLayout.root.contentItems.length === 0) {
-                instance._goldenLayout.root.addChild(newContentItem);
-            } else if (instance._goldenLayout.root.contentItems[0].isRow) {
-                instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
-            } else {
-                const collayout = instance._goldenLayout.root.contentItems[0];
-                const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
-                collayout.parent.replaceChild(collayout, newRow);
-
-                newRow.addChild(newContentItem, undefined, true);
-                newRow.addChild(collayout, 0, true);
-
-                collayout.config.width = 50;
-                newContentItem.config.width = 50;
-            }
-            newContentItem.callDownwards('_$init');
-            instance.layoutChanged();
+    public static ReplaceTab(document: Doc, panelName: string, stack: any, addToSplit?: boolean): boolean {
+        const instance = CollectionDockingView.Instance;
+        if (!instance) return false;
+        const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
+        if (!panelName && stack) {
+            const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config);
+            const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout);
+            stack.addChild(newContentItem.contentItems[0], undefined);
+            stack.contentItems[activeContentItemIndex].remove();
+            return CollectionDockingView.Instance.layoutChanged();
         }
-        return true;
+        const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => tab.contentItem.config.props.panelName === panelName);
+        if (tab) {
+            tab.header.parent.addChild(newConfig, undefined);
+            const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
+            !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove();
+            return CollectionDockingView.Instance.layoutChanged();
+        }
+        return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
     }
 
-
     //
     //  Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side
     //
-    @action
-    public static AddSplit(document: Doc, pullSide: string, libraryPath?: Doc[]) {
-        if (!CollectionDockingView.Instance) return false;
+    @undoBatch
+    public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
         const instance = CollectionDockingView.Instance;
-        const newItemStackConfig = {
-            type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, undefined)]
-        };
+        if (!instance) return false;
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
-        const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-
-        if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
-            instance._goldenLayout.root.addChild(newContentItem);
-        } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
-            if (pullSide === "left") {
-                instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
-            } else if (pullSide === "right") {
-                instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
-            } else if (pullSide === "top" || pullSide === "bottom") {
-                // if not going in a row layout, must add already existing content into column
-                const rowlayout = instance._goldenLayout.root.contentItems[0];
-                const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
-                rowlayout.parent.replaceChild(rowlayout, newColumn);
-                if (pullSide === "top") {
-                    newColumn.addChild(rowlayout, undefined, true);
-                    newColumn.addChild(newContentItem, 0, true);
-                } else if (pullSide === "bottom") {
-                    newColumn.addChild(newContentItem, undefined, true);
-                    newColumn.addChild(rowlayout, 0, true);
+        if (!pullSide && stack) {
+            stack.addChild(docContentConfig, undefined);
+        } else {
+            const newItemStackConfig = { type: 'stack', content: [docContentConfig] };
+            const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+            if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
+                instance._goldenLayout.root.addChild(newContentItem);
+            } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
+                switch (pullSide) {
+                    default:
+                    case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+                    case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+                    case "top":
+                    case "bottom":
+                        // if not going in a row layout, must add already existing content into column
+                        const rowlayout = instance._goldenLayout.root.contentItems[0];
+                        const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
+                        rowlayout.parent.replaceChild(rowlayout, newColumn);
+                        if (pullSide === "top") {
+                            newColumn.addChild(rowlayout, undefined, true);
+                            newColumn.addChild(newContentItem, 0, true);
+                        } else if (pullSide === "bottom") {
+                            newColumn.addChild(newContentItem, undefined, true);
+                            newColumn.addChild(rowlayout, 0, true);
+                        }
+
+                        rowlayout.config.height = 50;
+                        newContentItem.config.height = 50;
                 }
-
-                rowlayout.config.height = 50;
-                newContentItem.config.height = 50;
-            }
-        } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
-            if (pullSide === "top") {
-                instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
-            } else if (pullSide === "bottom") {
-                instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
-            } else if (pullSide === "left" || pullSide === "right") {
-                // if not going in a row layout, must add already existing content into column
-                const collayout = instance._goldenLayout.root.contentItems[0];
-                const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
-                collayout.parent.replaceChild(collayout, newRow);
-
-                if (pullSide === "left") {
-                    newRow.addChild(collayout, undefined, true);
-                    newRow.addChild(newContentItem, 0, true);
-                } else if (pullSide === "right") {
-                    newRow.addChild(newContentItem, undefined, true);
-                    newRow.addChild(collayout, 0, true);
+            } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
+                switch (pullSide) {
+                    case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+                    case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+                    case "left":
+                    case "right":
+                    default:
+                        // if not going in a row layout, must add already existing content into column
+                        const collayout = instance._goldenLayout.root.contentItems[0];
+                        const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+                        collayout.parent.replaceChild(collayout, newRow);
+
+                        if (pullSide === "left") {
+                            newRow.addChild(collayout, undefined, true);
+                            newRow.addChild(newContentItem, 0, true);
+                        } else {
+                            newRow.addChild(newContentItem, undefined, true);
+                            newRow.addChild(collayout, 0, true);
+                        }
+
+                        collayout.config.width = 50;
+                        newContentItem.config.width = 50;
                 }
-
-                collayout.config.width = 50;
-                newContentItem.config.width = 50;
             }
+            newContentItem.callDownwards('_$init');
         }
 
-        newContentItem.callDownwards('_$init');
-        instance.layoutChanged();
-        return true;
-    }
-
-    //
-    //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
-    //
-    @undoBatch
-    public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
-        if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
-            return CollectionDockingView.AddRightSplit(document, true);
-        }
-        return false;
-    }
-
-    @undoBatch
-    public static AddTab(stack: any, document: Doc, libraryPath?: Doc[]) {
-        const instance = CollectionDockingView.Instance;
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
-        if (stack === undefined) {
-            stack = instance._goldenLayout.root;
-            while (!stack.isStack) {
-                if (stack.contentItems.length) {
-                    stack = stack.contentItems[0];
-                } else {
-                    stack.addChild({ type: 'stack', content: [docContentConfig] });
-                    stack = undefined;
-                    break;
-                }
-            }
-        }
-        stack?.addChild(docContentConfig, undefined);
-        instance.layoutChanged();
-        return true;
+        return instance.layoutChanged();
     }
 
     @undoBatch
@@ -303,6 +215,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         this._goldenLayout.emit('stateChanged');
         this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         this.stateChanged();
+        return true;
     }
 
     async setupGoldenLayout() {
@@ -450,7 +363,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
     }
     tabCreated = (tab: any) => {
-        tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab(tab);  // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content)
+        tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab);  // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content)
     }
 
     stackCreated = (stack: any) => {
@@ -458,9 +371,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             if (e.target === stack.header.element[0] && e.button === 2) {
                 const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-                CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], {
+                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
-                }));
+                }), "", stack);
             }
         });
 
@@ -477,9 +390,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
                 const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-                CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], {
+                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
-                }));
+                }), "", stack);
             }));
     }
 
@@ -601,7 +514,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 Doc.AddDocToList(curPres, "data", pinDoc);
                 if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
                 if (!DocumentManager.Instance.getDocumentView(curPres)) {
-                    CollectionDockingView.AddRightSplit(curPres);
+                    CollectionDockingView.AddSplit(curPres, "right");
                 }
                 DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
             }
@@ -688,17 +601,26 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; }
 
+    // adds a tab to the layout based on the locaiton parameter which can be:
+    //  close[:{left,right,top,bottom}]  - e.g., "close" will close the tab, "close:left" will close the left tab, 
+    //  add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right
+    //  replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents, 
+    //                                  "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, 
+    //                                   "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
+    //  inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-        switch (location) {
-            case "onRight": return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
-                CollectionDockingView.CloseRightSplit(doc) : CollectionDockingView.AddRightSplit(doc);
-            case "close": return CollectionDockingView.CloseRightSplit(doc);
-            case "replace": return CollectionDockingView.UseRightSplit(doc);
+        const locationFields = location.split(":");
+        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+        switch (locationFields[0]) {
+            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+            case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
             case "inPlace":
-            default: return CollectionDockingView.AddTab(this.stack, doc);
+            case "add":
+            default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
+                CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack);
         }
     }
 
@@ -830,6 +752,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             </div >);
     }
 }
-Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); },
+Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddSplit(doc, "right"); },
     "opens up the inputted document on the right side of the screen", "(doc: any)");
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); });
+Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 18ca29aaa..0d3a951cf 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -370,7 +370,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
                         })}
                     />
                 </div >
-                <div onClick={() => this._doc && this.props.addDocTab(this._doc, "onRight")} className="collectionSchemaView-cellContents-docButton">
+                <div onClick={() => this._doc && this.props.addDocTab(this._doc, "add:right")} className="collectionSchemaView-cellContents-docButton">
                     <FontAwesomeIcon icon="external-link-alt" size="lg" />
                 </div>
             </div>;
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index a3475b481..383a9312f 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -246,7 +246,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
                         <div className="row-dragger">
                             <div className="row-option" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
                             <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
-                            <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "onRight")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+                            <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
                         </div>
                         {children}
                     </ReactTableDefaults.TrComponent>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index fffbe65a3..80a6afba7 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -117,7 +117,7 @@ class TreeView extends React.Component<TreeViewProps> {
             Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
     }
 
-    @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight");
+    @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1da0e601a..64ad3abbf 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -305,7 +305,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             this.setupViewTypes("UI Controls...", vtype => {
                 const newRendition = Doc.MakeAlias(this.props.Document);
                 newRendition._viewType = vtype;
-                this.props.addDocTab(newRendition, "onRight");
+                this.props.addDocTab(newRendition, "add:right");
                 return newRendition;
             }, false);
 
@@ -313,10 +313,10 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             const optionItems = options && "subitems" in options ? options.subitems : [];
             !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null;
             if (this.props.Document.childLayout instanceof Doc) {
-                optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
+                optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "add:right"), icon: "project-diagram" });
             }
             if (this.props.Document.childClickedOpenTemplateView instanceof Doc) {
-                optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" });
+                optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
             }
             !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
@@ -331,7 +331,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => {
                     const alias = Doc.MakeAlias(this.props.Document);
                     DocUtils.makeCustomViewClicked(alias, undefined, func.key);
-                    this.props.addDocTab(alias, "onRight");
+                    this.props.addDocTab(alias, "add:right");
                 }
             }));
             DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick =>
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 2fe00a245..1fb7aa04a 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -545,7 +545,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     }
 
     onOpenClick = () => {
-        this._showDoc && this.props.addDocTab(this._showDoc, "onRight");
+        this._showDoc && this.props.addDocTab(this._showDoc, "add:right");
     }
 
     getPreviewTransform = (): Transform => {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 759d142ea..1aa30fc02 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -76,7 +76,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
         if (e.key === "?") {
             cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
-                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "onRight"));
+                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "add:right"));
 
             cm.displayMenu(this._downX, this._downY);
             e.stopPropagation();
@@ -400,7 +400,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             Doc.AddDocToList(curPres, "data", pinDoc);
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
             if (!DocumentManager.Instance.getDocumentView(curPres)) {
-                CollectionDockingView.AddRightSplit(curPres);
+                CollectionDockingView.AddSplit(curPres, "right");
             }
             if (e instanceof KeyboardEvent ? e.key === "c" : true) {
                 const x = this.Bounds.left + this.Bounds.width / 2;
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 8e391f995..c9c2e657b 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -376,21 +376,33 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                         Default
                         </div>
                     <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("onRight")}>
-                        Always open in a new pane
+                        onPointerDown={() => this.changeFollowBehavior("add:left")}>
+                        Always open in new left pane
                         </div>
                     <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace")}>
+                        onPointerDown={() => this.changeFollowBehavior("add:right")}>
+                        Always open in new right pane
+                        </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("replace:right")}>
                         Always replace right tab
                         </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("replace:left")}>
+                        Always replace left tab
+                        </div>
                     <div className="linkEditor-followingDropdown-option"
                         onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
                         Always open full screen
                         </div>
                     <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("inTab")}>
+                        onPointerDown={() => this.changeFollowBehavior("add")}>
                         Always open in a new tab
                         </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("replace")}>
+                        Replace Tab
+                        </div>
                     {this.props.linkDoc.linksToAnnotation ?
                         <div className="linkEditor-followingDropdown-option"
                             onPointerDown={() => this.changeFollowBehavior("openExternal")}>
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index a77122456..05b6b584d 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -167,7 +167,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                 });
             }
         } else {
-            DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false);
+            DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "add:right"), false);
         }
 
         linkDoc.linksToAnnotation && Hypothesis.scrollToAnnotation(StrCast(this.props.linkDoc.annotationId), this.props.destinationDoc);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2d03c5154..48a1688a6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -180,10 +180,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
         RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15);
 
-        // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
+        // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 });
         const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
         (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
-        // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
+        // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
         RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
         RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
 
@@ -323,7 +323,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                                     fullScreenDoc = Doc.MakeAlias(this.props.Document);
                                     fullScreenDoc.layoutKey = "layout_fullScreen";
                                 }
-                                this.props.addDocTab(fullScreenDoc, "inTab");
+                                this.props.addDocTab(fullScreenDoc, "add");
                             }, "double tap");
                             SelectionManager.DeselectAll();
                         }
@@ -351,7 +351,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500);
                 } else clickFunc();
             } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
-                this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight");
+                this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right");
             } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
                 this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
             } else {
@@ -748,7 +748,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
         const appearance = cm.findByDescription("UI Controls...");
         const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
-        templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
+        templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
         //DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
         !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
@@ -766,7 +766,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
             onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
             onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
-            !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" });
+            !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
             onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
             onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
             onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
@@ -810,8 +810,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const help = cm.findByDescription("Help...");
         const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
-        !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
-        helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
+        !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+        helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
         helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
         cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
 
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index bc5abb0a4..87142babd 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -39,7 +39,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
     showTemplate = (): void => {
         const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
-        dragFactory && this.props.addDocTab(dragFactory, "onRight");
+        dragFactory && this.props.addDocTab(dragFactory, "add:right");
     }
     dragAsTemplate = (): void => {
         this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index b732f5f83..c5ff42a1a 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -230,7 +230,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         openItems.push({
             description: "Default Perspective", event: () => {
                 this.props.addDocTab(this.props.Document, "close");
-                this.props.addDocTab(this.fieldDocToLayout, "onRight");
+                this.props.addDocTab(this.fieldDocToLayout, "add:right");
             }, icon: "image"
         });
         !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 6dd85b7f5..74d10d087 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
         if (value instanceof Doc) {
             e.stopPropagation();
             e.preventDefault();
-            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
             ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
         }
     }
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 8e5ee09ff..10b6aa02e 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -73,7 +73,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
             anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
             if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
                 this._timeout = setTimeout(action(() => {
-                    DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, e.altKey ? "inTab" : "onRight")), false);
+                    DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "add:right")), false);
                     this._editing = false;
                 }), 300 - (Date.now() - this._lastTap));
             }
@@ -87,14 +87,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
     }
 
     openLinkDocOnRight = (e: React.MouseEvent) => {
-        this.props.addDocTab(this.rootDoc, "onRight");
+        this.props.addDocTab(this.rootDoc, "add:right");
     }
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias.isLinkButton = undefined;
         alias._isBackground = undefined;
         alias.layoutKey = "layout";
-        this.props.addDocTab(alias, "onRight");
+        this.props.addDocTab(alias, "add:right");
     }
     @action
     openLinkEditor = action((e: React.MouseEvent) => {
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index f154c5f56..e174a95af 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -40,7 +40,7 @@ export class LinkDocPreview extends React.Component<Props> {
     async followDefault() {
         DocumentLinksButton.EditLink = undefined;
         LinkDocPreview.LinkInfo = undefined;
-        this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "onRight"), false) : null;
+        this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "add:right"), false) : null;
     }
 
     componentDidUpdate() { this.updatePreview(); }
@@ -69,9 +69,9 @@ export class LinkDocPreview extends React.Component<Props> {
     pointerDown = (e: React.PointerEvent) => {
         if (this.props.linkDoc && this.props.linkSrc) {
             DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
-                (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
         } else if (this.props.href) {
-            this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight");
+            this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "add:right");
         }
     }
     width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index f60c521ad..a03c45a53 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -238,7 +238,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
 
         // If openDocument is selected then it should open the document for the user
         if (activeItem.openDocument) {
-            this.props.addDocTab(activeItem, "replace");
+            this.props.addDocTab(activeItem, "replace:right");
         } else
             //docToJump stayed same meaning, it was not in the group or was the last element in the group
             if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') {
@@ -443,7 +443,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         if (this.layoutDoc.inOverlay) {
             this.layoutDoc.presStatus = 'edit';
             Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
-            CollectionDockingView.AddRightSplit(this.rootDoc);
+            CollectionDockingView.AddSplit(this.rootDoc, "right");
             this.layoutDoc.inOverlay = false;
         } else if (this.layoutDoc.context && docView) {
             this.layoutDoc.presStatus = 'manual';
@@ -1115,7 +1115,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 DockedFrameRenderer.PinDoc(doc, false);
                 this.gotoDocument(this.childDocs.length, this.itemIndex);
             } else {
-                this.props.addDocTab(doc, "onRight");
+                this.props.addDocTab(doc, "add:right");
             }
         }
     }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 578061344..d50a10bdd 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -82,7 +82,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     @action public FullScreen() {
         this._fullScreen = true;
         this.player && this.player.requestFullscreen();
-        this._youtubePlayer && this.props.addDocTab(this.rootDoc, "inTab");
+        this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
     }
 
     choosePath(url: string) {
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 924079096..f2658e77e 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -169,7 +169,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     onPointerDownEnumerables = async (e: any) => {
         e.stopPropagation();
         const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
-        collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight");
+        collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right");
     }
 
 
@@ -191,7 +191,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
             list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
             alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
-            this.props.tbox.props.addDocTab(alias, "onRight");
+            this.props.tbox.props.addDocTab(alias, "add:right");
         }
     }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 356e36b76..c49e38f72 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -222,7 +222,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
                     const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
                     const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
-                    const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value });
+                    const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value });
                     const mval = this._editorView.state.schema.marks.metadataVal.create();
                     const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
                     tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -465,7 +465,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const linkDoc = data.linkDocument!;
         const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-";
         const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : "";
-        this.makeLinkToSelection(linkDoc[Id], anchor1Title, "onRight", anchor1Id);
+        this.makeLinkToSelection(linkDoc[Id], anchor1Title, "add:right", anchor1Id);
     }
 
     getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
@@ -811,7 +811,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 if (linkDoc) {
                     const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
                     const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : "";
-                    this.makeLinkToSelection(linkDoc[Id], anchor2Title, "onRight", anchor2Id);
+                    this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id);
                 }
             },
             { fireImmediately: true }
@@ -1096,7 +1096,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const marks = [...node.marks];
             const linkIndex = marks.findIndex(mark => mark.type.name === "link");
             const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
-            const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true });
+            const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
             marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
             return node.mark(marks);
         }
@@ -1243,10 +1243,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         FormattedTextBoxComment.Hide();
         if (FormattedTextBoxComment.linkDoc) {
             if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
-                this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+                this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
             } else {
                 DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document,
-                    (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                    (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
             }
         }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 55d225adc..f41f03620 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -103,7 +103,7 @@ export class FormattedTextBoxComment {
                             this.deleteLink();
                         } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) {
                             if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
-                                textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+                                textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
                             } else {
                                 const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)), textBox.dataDoc) ?
                                     Cast(FormattedTextBoxComment.linkDoc.anchor2, Doc) : (Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc))
@@ -112,28 +112,28 @@ export class FormattedTextBoxComment {
 
                                 if (FormattedTextBoxComment.linkDoc.follow) {
                                     if (FormattedTextBoxComment.linkDoc.follow === "Default") {
-                                        DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
+                                        DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false);
                                     } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in right tab") {
-                                        if (target) { textBox.props.addDocTab(target, "onRight"); }
+                                        if (target) { textBox.props.addDocTab(target, "add:right"); }
                                     } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in new tab") {
-                                        if (target) { textBox.props.addDocTab(target, "inTab"); }
+                                        if (target) { textBox.props.addDocTab(target, "add"); }
                                     }
                                 } else {
-                                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
+                                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false);
                                 }
                             }
                         } else {
                             if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
-                                textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+                                textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
                             } else {
                                 DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
-                                    (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                                    (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
                             }
                         }
 
                     }
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
-                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight");
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "add:right");
                 }
                 keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index a65eea50f..c6bacc1a8 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -104,7 +104,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     //Command to create a new Tab with a PDF of all the command shortcuts
     bind("Mod-/", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _fitWidth: true, _width: 300, _height: 300 });
-        props.addDocTab(newDoc, "onRight");
+        props.addDocTab(newDoc, "add:right");
     });
 
     //Commands to modify BlockType
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 399f72608..307238ea1 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -810,7 +810,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             <div className="dropdown link-menu">
                 <p>Linked to:</p>
                 <input value={link} placeholder="Enter URL" onChange={onLinkChange} />
-                <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "onRight")}>Apply hyperlink</button>
+                <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "add:right")}>Apply hyperlink</button>
                 <div className="divider"></div>
                 <button className="remove-button" onPointerDown={e => this.deleteLink()}>Remove link</button>
             </div>;
@@ -857,7 +857,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     // TODO: should check for valid URL
     @undoBatch
     makeLinkToURL = (target: string, lcoation: string) => {
-        ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target);
+        ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRadd:rightight", "", target);
     }
 
     @undoBatch
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 7e632a0ee..3fadfe842 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -279,7 +279,7 @@ export class RichTextRules {
                                 DocUtils.Publish(target, docid, returnFalse, returnFalse);
                                 DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
                             });
-                            const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
+                            const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "add:right", title: docid, targetId: docid });
                             return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
                         }
                         return state.tr;
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 1616500f6..64f7d27e5 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -148,7 +148,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             alt: { default: null },
             title: { default: null },
             float: { default: "left" },
-            location: { default: "onRight" },
+            location: { default: "add:right" },
             docid: { default: "" }
         },
         group: "inline",
@@ -177,7 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             height: { default: 100 },
             title: { default: null },
             float: { default: "right" },
-            location: { default: "onRight" },
+            location: { default: "add:right" },
             hidden: { default: false },
             fieldKey: { default: "" },
             docid: { default: "" },
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 98638ecc2..a33068a18 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -99,7 +99,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         else if (e.button === 0) {
             const annoGroup = await Cast(this.props.document.group, Doc);
             if (annoGroup) {
-                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined);
+                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined);
                 e.stopPropagation();
             }
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ed33ac822..d89e66ec5 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -604,7 +604,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                     if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
                         const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
                         annotationDoc.isLinkButton = true;
-                        if (link) link.followLinkLocation = "onRight";
+                        if (link) link.followLinkLocation = "add:right";
                     }
                 }
             });
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index a590c88c4..0b5f14d74 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -278,7 +278,7 @@ export namespace RichTextUtils {
             } else {
                 docid = backingDocId;
             }
-            return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" });
+            return schema.node("image", { src, agnostic, width, docid, float: null, location: "add:right" });
         };
 
         const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
-- 
cgit v1.2.3-70-g09d2


From 442572a66aaa6b50d8d01d46d79b7d6cf2f247a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 29 Aug 2020 17:42:48 -0400
Subject: fixed maximize docdecoration to toggle target w/o making an alias.
 use ctrlKey to make an alias.  removed dashboardBrush stuff.  cleaned up
 DocDecoartions css slightly

---
 src/client/util/CurrentUserUtils.ts                |  9 ++-
 src/client/views/DocumentDecorations.scss          | 84 ++++++++++++----------
 src/client/views/DocumentDecorations.tsx           | 82 +++++++++------------
 src/client/views/MainView.tsx                      | 17 +++--
 .../views/collections/CollectionDockingView.tsx    | 13 ++--
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  3 +-
 .../collectionGrid/CollectionGridView.scss         | 11 ---
 src/client/views/nodes/DocumentView.tsx            |  7 +-
 .../views/nodes/formattedText/DashDocView.tsx      |  5 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |  5 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 13 files changed, 117 insertions(+), 125 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 20f8fe9c1..2ca395164 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1020,7 +1020,7 @@ export class CurrentUserUtils {
     }
 
     public static snapshotDashboard = (userDoc: Doc) => {
-        const copy = CollectionDockingView.Copy(Cast(userDoc.activeDashboard, Doc, null));
+        const copy = CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
         Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
@@ -1031,7 +1031,11 @@ export class CurrentUserUtils {
         const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
         const emptyPane = Cast(userDoc.emptyPane, Doc, null);
-        emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+        try {
+            emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+        } catch (e) {
+            console.log(e)
+        }
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -1054,6 +1058,7 @@ export class CurrentUserUtils {
         CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
 
+    public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
     public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
     public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
     public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index b5c32ca1d..8291d7212 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -16,21 +16,11 @@ $linkGap : 3px;
     grid-template-columns: 8px 16px 1fr 8px 8px;
     pointer-events: none;
 
-    #documentDecorations-centerCont {
+    .documentDecorations-centerCont {
         grid-column: 3;
         background: none;
     }
 
-    .documentDecorations-resizer {
-        pointer-events: auto;
-        background: $alt-accent;
-        opacity: 0.1;
-    }
-
-    .documentDecorations-resizer:hover {
-        opacity: 1;
-    }
-
     .documentDecorations-selector {
         pointer-events: auto;
         height: 15px;
@@ -49,27 +39,43 @@ $linkGap : 3px;
         grid-row: 4;
     }
 
-    #documentDecorations-topLeftResizer,
-    #documentDecorations-leftResizer,
-    #documentDecorations-bottomLeftResizer {
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-topRightResizer,
+    .documentDecorations-bottomLeftResizer,
+    .documentDecorations-bottomRightResizer,
+    .documentDecorations-leftResizer,
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer,
+    .documentDecorations-rightResizer {
+        pointer-events: auto;
+        background: $alt-accent;
+        opacity: 0.1;
+        &:hover {
+            opacity: 1;
+        }
+    }
+
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-leftResizer,
+    .documentDecorations-bottomLeftResizer {
         grid-column: 1
     }
 
-    #documentDecorations-topResizer,
-    #documentDecorations-bottomResizer {
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer {
         grid-column-start: 2;
         grid-column-end: 5;
     }
 
-    #documentDecorations-bottomRightResizer,
-    #documentDecorations-topRightResizer,
-    #documentDecorations-rightResizer {
+    .documentDecorations-bottomRightResizer,
+    .documentDecorations-topRightResizer,
+    .documentDecorations-rightResizer {
         grid-column-start: 5;
         grid-column-end: 7;
     }
 
-    #documentDecorations-rotation,
-    #documentDecorations-borderRadius {
+    .documentDecorations-rotation,
+    .documentDecorations-borderRadius {
         grid-column: 5;
         grid-row: 4;
         border-radius: 100%;
@@ -87,69 +93,69 @@ $linkGap : 3px;
             position: absolute;
         }
     }
-    #documentDecorations-rotation {
+    .documentDecorations-rotation {
         background: transparent;
         right: -15;
     }
 
 
-    #documentDecorations-topLeftResizer,
-    #documentDecorations-bottomRightResizer {
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-bottomRightResizer {
         cursor: nwse-resize;
         background: unset;
         opacity: 1;
     }
 
-    #documentDecorations-topLeftResizer {
+    .documentDecorations-topLeftResizer {
         border-left: 2px solid;
         border-top: solid 2px;
     }
 
-    #documentDecorations-bottomRightResizer {
+    .documentDecorations-bottomRightResizer {
         border-right: 2px solid;
         border-bottom: solid 2px;
     }
 
-    #documentDecorations-topLeftResizer:hover,
-    #documentDecorations-bottomRightResizer:hover {
+    .documentDecorations-topLeftResizer:hover,
+    .documentDecorations-bottomRightResizer:hover {
         opacity: 1;
     }
 
-    #documentDecorations-bottomRightResizer {
+    .documentDecorations-bottomRightResizer {
         grid-row: 4;
     }
 
-    #documentDecorations-topRightResizer,
-    #documentDecorations-bottomLeftResizer {
+    .documentDecorations-topRightResizer,
+    .documentDecorations-bottomLeftResizer {
         cursor: nesw-resize;
         background: unset;
         opacity: 1;
     }
 
-    #documentDecorations-topRightResizer {
+    .documentDecorations-topRightResizer {
         border-right: 2px solid;
         border-top: 2px solid;
     }
 
-    #documentDecorations-bottomLeftResizer {
+    .documentDecorations-bottomLeftResizer {
         border-left: 2px solid;
         border-bottom: 2px solid;
     }
 
-    #documentDecorations-topRightResizer:hover,
-    #documentDecorations-bottomLeftResizer:hover {
+    .documentDecorations-topRightResizer:hover,
+    .documentDecorations-bottomLeftResizer:hover {
         cursor: nesw-resize;
         background: dimGray;
         opacity: 1;
     }
 
-    #documentDecorations-topResizer,
-    #documentDecorations-bottomResizer {
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer {
         cursor: ns-resize;
     }
 
-    #documentDecorations-leftResizer,
-    #documentDecorations-rightResizer {
+    .documentDecorations-leftResizer,
+    .documentDecorations-rightResizer {
         cursor: ew-resize;
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c7a24133c..6db5186ba 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,6 +24,7 @@ import { FormatShapePane } from './FormatShapePane';
 import { DocumentView } from "./nodes/DocumentView";
 import React = require("react");
 import e = require('express');
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
 
 @observer
 export class DocumentDecorations extends React.Component<{}, { value: string }> {
@@ -177,7 +178,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
     @action
     onMaximizeDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick);
+        if (e.ctrlKey) {
+            const selectedDocs = SelectionManager.SelectedDocuments();
+            const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+            alias.context = undefined;
+            //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
+            CollectionDockingView.AddSplit(alias, "right");
+            e.stopPropagation();
+            e.preventDefault();
+        } else {
+            setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick);
+        }
     }
     @undoBatch
     @action
@@ -185,10 +196,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (e.button === 0) {
             const selectedDocs = SelectionManager.SelectedDocuments();
             if (selectedDocs.length) {
-                const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                alias.context = undefined;
-                //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
-                CollectionDockingView.AddSplit(alias, "right");
+                CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
             }
         }
         SelectionManager.DeselectAll();
@@ -333,7 +341,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
         if (e.button === 0) {
-            this._resizeHdlId = e.currentTarget.id;
+            this._resizeHdlId = e.currentTarget.className;
             const bounds = e.currentTarget.getBoundingClientRect();
             this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
             this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
@@ -575,7 +583,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
     render() {
-        const darkScheme = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimgray" : undefined;
+        const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
@@ -587,35 +595,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         });
         const minimal = bounds.r - bounds.x < 100 ? true : false;
         const maximizeIcon = minimal ? (
-            <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top">
+            <Tooltip title={<div className="dash-tooltip">Show context menu</div>} placement="top">
                 <div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}>
                     <FontAwesomeIcon size="lg" icon="bars" />
                 </div></Tooltip>) : canDelete ? (
-                    <Tooltip title={<><div className="dash-tooltip">Close</div></>} placement="top">
+                    <Tooltip title={<div className="dash-tooltip">Close</div>} placement="top">
                         <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
                             {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
                             <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" />
                         </div></Tooltip>) : (null);
 
         const titleArea = this._edtingTitle ?
+            <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
+                onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> :
             <>
-                <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
-                    onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} />
-                {minimal ? (null) : <div className="publishBox" // title="make document referenceable by its title"
-                // onPointerDown={action(e => {
-                //     if (!seldoc.props.Document["title-custom"]) {
-                //         seldoc.props.Document["title-custom"] = true;
-                //         StrCast(Doc.GetProto(seldoc.props.Document).title).startsWith("-") && (Doc.GetProto(seldoc.props.Document).title = StrCast(seldoc.props.Document.title).substring(1));
-                //         this._accumulatedTitle = StrCast(seldoc.props.Document.title);
-                //     }
-                //     DocUtils.Publish(seldoc.props.Document, this._accumulatedTitle, seldoc.props.addDocument, seldoc.props.removeDocument);
-                // })}
-                >
-                    {/* <FontAwesomeIcon size="lg" color={SelectionManager.SelectedDocuments()[0].props.Document.title === SelectionManager.SelectedDocuments()[0].props.Document[Id] ? "green" : undefined} icon="sticky-note"></FontAwesomeIcon> */}
-                </div>}
-            </> :
-            <>
-                {minimal ? (null) : <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
+                {minimal ? (null) : <Tooltip title={<div className="dash-tooltip">Show context menu</div>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
                     <FontAwesomeIcon size="lg" icon="bars" />
                 </div></Tooltip>}
                 <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
@@ -656,37 +650,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     {maximizeIcon}
                     {titleArea}
                     {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
-                        <Tooltip title={<><div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div></>} placement="top">
+                        <Tooltip title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
                             <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
                                 <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
                             </div></Tooltip>}
-                    <Tooltip title={<><div className="dash-tooltip">Open In a New Pane</div></>} placement="top"><div className="documentDecorations-openInTab" onPointerDown={this.onMaximizeDown}>
+                    <Tooltip title={<div className="dash-tooltip">Open In a New Pane</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
                         {SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."}
                     </div></Tooltip>
-                    <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-centerCont"></div>
-                    <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-centerCont"></div>
+                    <div className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
                     {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
-                        <Tooltip title={<><div className="dash-tooltip">tap to select containing document</div></>} placement="top">
-                            <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
+                        <Tooltip title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
+                            <div className="documentDecorations-levelSelector"
                                 onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
                                 <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
                             </div></Tooltip>}
-                    <div id={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+                    <div className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
                         onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>{useRotation && "⟲"}</div>
 
                 </div >
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7b0c6fa9f..83d9a3498 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -75,10 +75,10 @@ export class MainView extends React.Component {
     @observable private _panelHeight: number = 0;
     @observable private _flyoutTranslate: boolean = false;
     @observable public flyoutWidth: number = 0;
-    private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeDashboard, Doc, null)?.darkScheme); }
+    private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
 
     @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; }
+    @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
     @computed public get searchDoc() { return Cast(this.userDoc.mySearchPanelDoc, Doc) as Doc; }
 
@@ -226,8 +226,7 @@ export class MainView extends React.Component {
         // Load the user's active dashboard, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(
-                () => CurrentUserUtils.GuestTarget,
+            reaction(() => CurrentUserUtils.GuestTarget,
                 target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()),
                 { fireImmediately: true }
             );
@@ -241,11 +240,11 @@ export class MainView extends React.Component {
                     }),
                 );
             }
-            const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc);
-            if (doc) {
-                CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            const activeDash = PromiseValue(this.userDoc.activeDashboard);
+            if (activeDash) {
+                activeDash.then(dash => dash instanceof Doc && CurrentUserUtils.openDashboard(this.userDoc, dash));
             } else {
-                CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+                CurrentUserUtils.createNewDashboard(this.userDoc);
             }
         }
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 62c865436..93e77e0a2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -140,6 +140,13 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
     }
 
+
+    @undoBatch
+    public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) {
+        return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
+            CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName);
+    }
+
     //
     //  Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side
     //
@@ -262,8 +269,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 config => {
                     if (!this._goldenLayout || this._ignoreStateChange !== config) {
                         this.setupGoldenLayout();
-                        DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false);
-                        this.props.Document.dashboardBrush = true;
                     }
                     this._ignoreStateChange = "";
                 });
@@ -274,7 +279,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
 
     componentWillUnmount: () => void = () => {
         try {
-            this.props.Document.dashboardBrush = false;
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
             this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
         } catch (e) { }
@@ -619,8 +623,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
             case "inPlace":
             case "add":
-            default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
-                CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack);
+            default: return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
         }
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 80a6afba7..84fd4cbe8 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -483,7 +483,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
+                    outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
                     pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }} >
                 {view}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 64ad3abbf..81403de46 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -583,7 +583,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutString: this.childLayoutString,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            `${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+            `${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 }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 646ae94fb..bc2cb2d20 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -10,6 +10,7 @@ import { Id, ToString } from "../../../../fields/FieldSymbols";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RefField } from "../../../../fields/RefField";
 import { listSpec } from "../../../../fields/Schema";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 export interface ViewDefBounds {
     type: string;
@@ -359,7 +360,7 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 4d8473be9..60ec02f47 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -134,17 +134,6 @@
 
 }
 
-// .documentDecorations-container .documentDecorations-resizer {
-//     pointer-events: none;
-// }
-
-// #documentDecorations-bottomRightResizer,
-// #documentDecorations-bottomLeftResizer,
-// #documentDecorations-topRightResizer,
-// #documentDecorations-topLeftResizer {
-//     visibility: collapse;
-// }
-
 
 /* Chrome, Safari, Edge, Opera */
 input::-webkit-outer-spin-button,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 48a1688a6..f4eb71145 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -39,6 +39,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { RadialMenu } from './RadialMenu';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import React = require("react");
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 
 export type DocFocusFunc = () => boolean;
 
@@ -588,7 +589,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     deleteClicked = (): void => {
-        if (Doc.UserDoc().activeDashboard === this.props.Document) {
+        if (CurrentUserUtils.ActiveDashboard === this.props.Document) {
             alert("Can't delete the active dashboard");
         } else {
             const selected = SelectionManager.SelectedDocuments().slice();
@@ -625,7 +626,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
-        if (this.props.Document === Doc.UserDoc().activeDashboard) {
+        if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
             alert("linking to document tabs not yet supported.  Drop link on document content.");
             return;
         }
@@ -1041,7 +1042,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
-        const highlightColors = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ?
+        const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 08bab8d12..3b77735a7 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { DocumentView } from "../DocumentView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { Transform } from "../../../util/Transform";
 import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 interface IDashDocView {
     node: any;
@@ -175,7 +176,7 @@ export class DashDocView extends React.Component<IDashDocView> {
         const outerStyle = {
             position: "relative" as "relative",
             textIndent: "0",
-            border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+            border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")),
             width: this.props.node.props.width,
             height: this.props.node.props.height,
             display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +203,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                 ({ dim, color }) => {
                     spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
                     spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
-                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
                 }, { fireImmediately: true });
 
             if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index b76e520b7..f0bacb735 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform";
 import { DocumentView } from "../DocumentView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 
 export class DashDocView {
@@ -43,7 +44,7 @@ export class DashDocView {
         this._outer = document.createElement("span");
         this._outer.style.position = "relative";
         this._outer.style.textIndent = "0";
-        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
         this._outer.style.width = node.attrs.width;
         this._outer.style.height = node.attrs.height;
         this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +127,7 @@ export class DashDocView {
             this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
                 this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
                 this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
-                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
             }, { fireImmediately: true });
 
             const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d7a8ed404..3926208ae 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -507,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                     </div>
                     <div className="searchBox-lozenge-dashboard"  >
                         <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
-                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null))}>
+                            value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}>
                             {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
                         </select>
                         <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
-- 
cgit v1.2.3-70-g09d2


From 4bdb3b64c476ffdc5b5c53c9c296c50ce68a8fc5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 1 Sep 2020 16:57:49 -0400
Subject: fixed facet filters operations and whitelisting of known fields

---
 src/client/documents/Documents.ts                            |  2 +-
 src/client/util/CurrentUserUtils.ts                          |  2 +-
 src/client/util/HypothesisUtils.ts                           |  2 +-
 src/client/views/DocComponent.tsx                            |  2 --
 src/client/views/DocumentButtonBar.tsx                       |  2 +-
 src/client/views/GlobalKeyHandler.ts                         |  2 --
 src/client/views/PreviewCursor.tsx                           |  2 +-
 src/client/views/PropertiesButtons.tsx                       |  2 +-
 src/client/views/PropertiesView.tsx                          |  2 +-
 src/client/views/collections/CollectionMenu.tsx              |  9 ++++-----
 src/client/views/collections/CollectionStackingView.tsx      | 12 +++++++-----
 .../views/collections/CollectionStackingViewFieldColumn.tsx  |  2 +-
 src/client/views/collections/CollectionSubView.tsx           |  2 +-
 src/client/views/collections/CollectionTreeView.tsx          |  2 +-
 src/client/views/collections/CollectionView.tsx              | 11 +++++++++--
 .../views/collections/collectionFreeForm/MarqueeView.tsx     |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx                    |  2 +-
 src/client/views/nodes/WebBox.tsx                            |  4 ++--
 .../views/nodes/formattedText/FormattedTextBoxComment.tsx    |  2 +-
 src/fields/Doc.ts                                            |  1 -
 src/fields/util.ts                                           |  2 +-
 21 files changed, 36 insertions(+), 33 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9a24a8052..feb84843d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -178,7 +178,7 @@ export interface DocumentOptions {
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     clipboard?: Doc;
-    UseCors?: boolean;
+    useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
     sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1b0747e9c..734050b05 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -439,7 +439,7 @@ export class CurrentUserUtils {
                 { _width: 250, _height: 250, title: "container", system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.emptyWebpage === undefined) {
-            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.activeMobileMenu === undefined) {
             this.setupActiveMobileMenu(doc);
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index ddd2b89d1..4a5b52e1e 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -21,7 +21,7 @@ export namespace Hypothesis {
     export const getSourceWebDoc = async (uri: string) => {
         const result = await findWebDoc(uri);
         console.log(result ? "existing doc found" : "existing doc NOT found");
-        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true }); // create and return a new Web doc with given uri if no matching docs are found
+        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
     };
 
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index e9469d7bb..aa888b34a 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -178,8 +178,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                         added.map(doc => doc.context = this.props.Document);
                         (targetDataDoc[this.annotationKey] as List<Doc>).push(...added);
                         targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
-                        const lastModified = "lastModified";
-                        targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
                     }
                 }
             }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 32434490b..cf2bd5176 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -153,7 +153,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         e.preventDefault();
                         let googleDoc = await Cast(dataDoc.googleDoc, Doc);
                         if (!googleDoc) {
-                            const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false };
+                            const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, useCors: false };
                             googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                             dataDoc.googleDoc = googleDoc;
                         }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index a52eb649d..83c02b09b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -271,8 +271,6 @@ export class KeyManager {
                             undoBatch(() => {
                                 targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
                                 targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
-                                const lastModified = "lastModified";
-                                targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
                             })();
                         }
                     }
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 37d837f59..1cadba18a 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -52,7 +52,7 @@ export class PreviewCursor extends React.Component<{}> {
                 else if (re.test(plain)) {
                     const url = plain;
                     undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
-                        title: url, _width: 500, _height: 300, UseCors: true, x: newPoint[0], y: newPoint[1]
+                        title: url, _width: 500, _height: 300, useCors: true, x: newPoint[0], y: newPoint[1]
                     })))();
                 }
                 else if (plain.startsWith("__DashDocId(") || plain.startsWith("__DashCloneId(")) {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 921822539..d66cba710 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -153,7 +153,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                             e.preventDefault();
                             let googleDoc = await Cast(dataDoc.googleDoc, Doc);
                             if (!googleDoc) {
-                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false };
+                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, useCors: false };
                                 googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                                 dataDoc.googleDoc = googleDoc;
                             }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 1768b5c41..b9d7bd18d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -180,7 +180,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const rows: JSX.Element[] = [];
             const noviceReqFields = ["author", "creationDate"];
             const noviceLayoutFields = ["_curPage"];
-            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL") && key !== "UseCors")),
+            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL"))),
             ...noviceReqFields, ...noviceLayoutFields];
             for (const key of noviceKeys.sort()) {
                 const docvals = new Set<any>();
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 556e96d50..b04c9c2eb 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -901,15 +901,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
             if (docs instanceof Doc) {
                 const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                     key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
-                    (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_"));
+                    (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key[0] !== "_"));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
             } else {
                 const keys = new Set<string>();
                 docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-                const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 ||
-                    key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 ||
-                    key.indexOf("lastModified") >= 0 || (key[0]?.toUpperCase() === key[0] &&
-                        key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_"));
+                const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
+                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
+                    (key[0]?.toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key[0] !== "_"));
                 return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
         }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 04c464b73..e4bb8302f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -344,9 +344,11 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
     sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
         const key = this.pivotField;
         let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
-        const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
-        if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
-            type = types[0];
+        if (this.pivotField) {
+            const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
+            if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+                type = types[0];
+            }
         }
         const cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
             Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
@@ -364,10 +366,10 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.observer.observe(ref);
                 }
             }}
-            key={heading ? heading.heading : ""}
+            key={heading?.heading ?? ""}
             cols={cols}
             headings={this.headings}
-            heading={heading ? heading.heading : ""}
+            heading={heading?.heading ?? ""}
             headingObject={heading}
             docList={docList}
             parent={this}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 12b7d742b..fd2ae03d8 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -287,7 +287,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         const singleColumn = style.isStackingView;
         const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin, 5);
         const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
-        const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+        const evContents = heading ? heading : this.props?.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
         const headerEditableViewProps = {
             GetValue: () => evContents,
             SetValue: this.headingChanged,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1d15fbfcb..54e7f790f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -402,7 +402,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                         _height: 315,
                         _nativeWidth: 850,
                         _nativeHeight: 962,
-                        UseCors: true
+                        useCors: true
                     });
                     newDoc.data = new WebField(uriList.split("#annotations:")[0]); // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
                     this.addDocument(newDoc);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 84fd4cbe8..52c3b2793 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -390,7 +390,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
                 heading: this.props.containingCollection.title,
                 checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
-                containingTreeView: this.props.treeView,
+                containingTreeView: this.props.treeView.props.Document,
             }, console.log);
         } else {
             this.treeViewOpen = !this.treeViewOpen;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 81403de46..cb053e85c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -400,12 +400,19 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         const viewSpecScript = ScriptCast(this.props.Document.viewSpecScript);
         return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
     }
+
     @computed get _allFacets() {
         TraceMobx();
-        const facets = new Set<string>(["type", "text", "data", "author", "ACL"]);
+        return ["author", "creationDate", "type", "text", "context"];
+        const noviceReqFields = ["author", "creationDate", "type", "text", "context"];
+        const noviceLayoutFields: string[] = [];//["_curPage"];
+        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
+
+        const facets = new Set<string>([...noviceReqFields, ...noviceLayoutFields]);
         this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
         Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
-        return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort();
+
+        return Array.from(facets).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort();
     }
 
     /**
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index f928e3fb8..d8e1bcc9c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -76,7 +76,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
         if (e.key === "?") {
             cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
-                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "add:right"));
+                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", useCors: true }), "add:right"));
 
             cm.displayMenu(this._downX, this._downY);
             e.stopPropagation();
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index e174a95af..ce8df5195 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -71,7 +71,7 @@ export class LinkDocPreview extends React.Component<Props> {
             DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
                 (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
         } else if (this.props.href) {
-            this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "add:right");
+            this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
         }
     }
     width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 876bc2bc7..4dded50b0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -374,7 +374,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: (this.layoutDoc.UseCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.UseCors = !this.layoutDoc.UseCors, icon: "snowflake" });
+        funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
         cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
 
     }
@@ -389,7 +389,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         if (field instanceof HtmlField) {
             view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
         } else if (field instanceof WebField) {
-            const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
+            const url = this.layoutDoc.useCors ? Utils.CorsProxy(field.url.href) : field.url.href;
             view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded}
                 // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
                 sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index ef0222a4c..b4f648273 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -133,7 +133,7 @@ export class FormattedTextBoxComment {
 
                     }
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
-                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "add:right");
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, useCors: true }), "add:right");
                 }
                 keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1401dd6a1..a4fcfce5e 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -23,7 +23,6 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u
 import { LinkManager } from "../client/util/LinkManager";
 import JSZip = require("jszip");
 import { saveAs } from "file-saver";
-import { result } from "lodash";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index d0c722ddc..82525f92b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -293,7 +293,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
 export function getter(target: any, in_prop: string | symbol | number, receiver: any): any {
     let prop = in_prop;
 
-    if (in_prop === ToString || in_prop === ToScriptString || in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop];
+    if (in_prop === "toString" || in_prop === ToString || in_prop === ToScriptString || in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop];
     if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym];
     if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return prop === HeightSym || prop === WidthSym ? returnZero : undefined;
     if (prop === LayoutSym) {
-- 
cgit v1.2.3-70-g09d2


From ea5bb0221411596461ae7206b6901ccbb79a1c2f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 2 Sep 2020 20:07:09 -0400
Subject: fixes to sidebar filter.  still need some more work.

---
 src/client/documents/Documents.ts                  |   5 +-
 src/client/util/CurrentUserUtils.ts                |  16 +-
 src/client/views/MainView.tsx                      |   3 +-
 .../views/collections/CollectionPileView.tsx       |   2 -
 .../views/collections/CollectionSchemaHeaders.tsx  |   8 +-
 .../views/collections/CollectionTreeView.tsx       |  25 +--
 src/client/views/collections/CollectionView.tsx    | 174 +--------------------
 src/client/views/nodes/FilterBox.scss              |  76 +++++----
 src/client/views/nodes/FilterBox.tsx               | 114 ++++++++------
 src/client/views/search/SearchBox.tsx              |  43 ++---
 10 files changed, 153 insertions(+), 313 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fbcecbec6..e00160a70 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -108,7 +108,6 @@ export interface DocumentOptions {
     layout?: string | Doc; // default layout string for a document
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
-    hideFilterView?: boolean; // whether to hide the filter popout on collections
     hideLinkButton?: boolean; // whether the blue link counter button should be hidden
     hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
     _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
@@ -762,7 +761,7 @@ export namespace Docs {
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", hideFilterView: true, forceActive: true, ...options, _viewType: CollectionViewType.Pile }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", forceActive: true, ...options, _viewType: CollectionViewType.Pile }, id);
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -835,7 +834,7 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
             const tabs = TreeDocument(documents, { title: "On-Screen Tabs", freezeChildren: "remove|add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true });
             const all = TreeDocument([], { title: "Off-Screen Tabs", freezeChildren: "add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true });
             Doc.GetProto(inst).data = new List<Doc>([tabs, all]);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 164c3ab67..5a01e9cf9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -55,7 +55,7 @@ export class CurrentUserUtils {
                     Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, _chromeStatus: "disabled", lockedPosition: true, title: "query", _height: 200, system: true }),
                     Docs.Create.FreeformDocument([], { title: "data", _height: 100, system: true })
                 ],
-                { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true }
+                { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, system: true }
             );
             queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
             doc["template-button-query"] = CurrentUserUtils.ficon({
@@ -90,7 +90,7 @@ export class CurrentUserUtils {
                     Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
                     Docs.Create.TextDocument("", { title: "text", _height: 100, system: true })
                 ],
-                { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true }
+                { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, system: true }
             );
             slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
             doc["template-button-slides"] = CurrentUserUtils.ficon({
@@ -720,7 +720,7 @@ export class CurrentUserUtils {
 
         if (doc.myTools === undefined) {
             const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], {
-                title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, forceActive: true, system: true
+                title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true
             })) as any as Doc;
 
             doc.myTools = toolsStack;
@@ -734,7 +734,7 @@ export class CurrentUserUtils {
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Dashboards", _height: 400,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
+                treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
@@ -750,7 +750,7 @@ export class CurrentUserUtils {
             doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Presentations", _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
+                treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
@@ -768,7 +768,7 @@ export class CurrentUserUtils {
             doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "Recently Closed", _height: 500,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
+                treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
@@ -783,7 +783,7 @@ export class CurrentUserUtils {
             doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({
                 title: "FilterDoc", _height: 500,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
+                treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
             const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
@@ -799,7 +799,7 @@ export class CurrentUserUtils {
             doc.treeViewExpandedView = "fields";
             doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc",
-                treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
+                treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             })) as any as Doc;
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e6e80d9fb..a87a07b62 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -83,6 +83,7 @@ export class MainView extends React.Component {
 
     componentDidMount() {
         new InkStrokeProperties();
+        this._sidebarContent.proto = undefined;
         DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
 
         DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
@@ -527,7 +528,7 @@ export class MainView extends React.Component {
         TraceMobx();
         return <div className="mainView-searchPanel">
             <SearchBox Document={CurrentUserUtils.MySearchPanelDoc}
-                DataDoc={undefined}
+                DataDoc={CurrentUserUtils.MySearchPanelDoc}
                 fieldKey="data"
                 dropAction="move"
                 isSelected={returnTrue}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 2e4055256..522f93b88 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -28,10 +28,8 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         }
         this._originalChrome = StrCast(this.layoutDoc._chromeStatus);
         this.layoutDoc._chromeStatus = "disabled";
-        this.layoutDoc.hideFilterView = true;
     }
     componentWillUnmount() {
-        this.layoutDoc.hideFilterView = false;
         this.layoutDoc._chromeStatus = this._originalChrome;
     }
 
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 94f9d4f92..d2a1234ed 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -380,7 +380,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
 
     @action
     renderFilterOptions = (): JSX.Element[] | JSX.Element => {
-        if (!this._isOpen) {
+        if (!this._isOpen || !this.props.dataDoc) {
             this.defaultMenuHeight = 0;
             return <></>;
         }
@@ -388,7 +388,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         const colpos = this._searchTerm.indexOf(":");
         const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
         if (this.docSafe.length === 0) {
-            this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]);
+            this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
         }
         const docs = this.docSafe;
         docs.forEach((doc) => {
@@ -476,8 +476,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
 
     removeFilters = (e: React.PointerEvent): void => {
         const keyOptions: string[] = [];
-        if (this.docSafe.length === 0) {
-            this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]);
+        if (this.docSafe.length === 0 && this.props.dataDoc) {
+            this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
         }
         const docs = this.docSafe;
         docs.forEach((doc) => {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 52c3b2793..f13fee776 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -32,6 +32,7 @@ import React = require("react");
 import { makeTemplate } from '../../util/DropConverter';
 import { TraceMobx } from '../../../fields/util';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionDockingView } from './CollectionDockingView';
 
 export interface TreeViewProps {
     document: Doc;
@@ -488,7 +489,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }} >
                 {view}
             </div >
-            {Doc.IsSystem(this.doc) ? (null) : headerElements}
+            {Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode ? (null) : headerElements}
         </>;
     }
 
@@ -875,28 +876,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
 }
 
-Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) {
-    const allCollectionDocs = DocListCast(dataDoc[dataKey]);
-    const facetValues = Array.from(allCollectionDocs.reduce((set, child) =>
-        set.add(Field.toString(child[facetHeader] as Field)), new Set<string>()));
-
-    let nonNumbers = 0;
-    facetValues.map(val => {
-        const num = Number(val);
-        if (Number.isNaN(num)) {
-            nonNumbers++;
-        }
-    });
-    const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
-        const doc = new Doc();
-        doc.system = true;
-        doc.title = facetValue.toString();
-        doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
-        return doc;
-    });
-    return new List<Doc>(facetValueDocSet);
-});
-
 Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
     const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
     for (let i = 0; i < docFilters.length; i += 3) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index cb053e85c..ba8e23447 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -271,7 +271,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         return this.SubViewHelper(type, renderProps);
     }
 
-
     setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
 
         const subItems: ContextMenuProps[] = [];
@@ -367,6 +366,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
     }
+
     get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
     set _facetWidth(value) { this.props.Document._facetWidth = value; }
 
@@ -390,9 +390,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, DataDoc, doc)).filter(pair => pair.layout);
         return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
     }
+
     get childDocList() {
         return Cast(this.dataField, listSpec(Doc));
     }
+
     get childDocs() {
         const dfield = this.dataField;
         const rawdocs = (dfield instanceof Doc) ? [dfield] : Cast(dfield, listSpec(Doc), Cast(this.props.Document.rootDocument, Doc, null) ? [Cast(this.props.Document.rootDocument, Doc, null)] : []);
@@ -401,99 +403,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
     }
 
-    @computed get _allFacets() {
-        TraceMobx();
-        return ["author", "creationDate", "type", "text", "context"];
-        const noviceReqFields = ["author", "creationDate", "type", "text", "context"];
-        const noviceLayoutFields: string[] = [];//["_curPage"];
-        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
-
-        const facets = new Set<string>([...noviceReqFields, ...noviceLayoutFields]);
-        this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
-        Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
-
-        return Array.from(facets).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort();
-    }
-
-    /**
-     * Responds to clicking the check box in the flyout menu
-     */
-    facetClick = (facetHeader: string) => {
-        const facetCollection = this.props.Document;
-        const found = DocListCast(facetCollection[this.props.fieldKey + "-filter"]).findIndex(doc => doc.title === facetHeader);
-        if (found !== -1) {
-            (facetCollection[this.props.fieldKey + "-filter"] as List<Doc>).splice(found, 1);
-            const docFilter = Cast(this.props.Document._docFilters, listSpec("string"));
-            if (docFilter) {
-                let index: number;
-                while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) {
-                    docFilter.splice(index, 3);
-                }
-            }
-            const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"));
-            if (docRangeFilters) {
-                let index: number;
-                while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) {
-                    docRangeFilters.splice(index, 3);
-                }
-            }
-        } else {
-            const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
-            var rtfields = 0;
-            const facetValues = Array.from(allCollectionDocs.reduce((set, child) => {
-                const field = child[facetHeader] as Field;
-                const fieldStr = Field.toString(field);
-                if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++;
-                return set.add(fieldStr);
-            }, new Set<string>()));
-
-            let nonNumbers = 0;
-            let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
-            facetValues.map(val => {
-                const num = Number(val);
-                if (Number.isNaN(num)) {
-                    nonNumbers++;
-                } else {
-                    minVal = Math.min(num, minVal);
-                    maxVal = Math.max(num, maxVal);
-                }
-            });
-            let newFacet: Opt<Doc>;
-            if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) {
-                newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
-                Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                newFacet.target = this.props.Document;
-                newFacet._textBoxPadding = 4;
-                const scriptText = `setDocFilter(this.target, "${facetHeader}", text, "match")`;
-                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
-            } else if (nonNumbers / facetValues.length < .1) {
-                newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true });
-                const newFacetField = Doc.LayoutFieldKey(newFacet);
-                const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader);
-                Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
-                const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
-                newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
-                newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
-                Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
-                Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
-                newFacet.target = this.props.Document;
-                const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
-                newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
-                Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
-            } else {
-                newFacet = new Doc();
-                newFacet.sytem = true;
-                newFacet.title = facetHeader;
-                newFacet.treeViewOpen = true;
-                newFacet.type = DocumentType.COL;
-                const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
-                newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables);
-            }
-            newFacet && Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
-        }
-    }
-
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
             this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
@@ -501,78 +410,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false);
     }
 
-    filterBackground = () => "rgba(105, 105, 105, 0.432)";
-    get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
-    @computed get scriptField() {
-        const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
-        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
-        return script ? () => script : undefined;
-    }
-    @computed get filterView() {
-        TraceMobx();
-        const facetCollection = this.props.Document;
-        const flyout = (
-            <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
-                {this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
-                    <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey + "-filter"]).some(d => d.title === facet)} />
-                    <span className="checkmark" />
-                    {facet}
-                </label>)}
-            </div>
-        );
-
-        return !this._facetWidth || this.props.dontRegisterView ? (null) : <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}>
-            <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
-                    <div className="collectionTimeView-button">
-                        <FontAwesomeIcon icon={"edit"} size={"lg"} />
-                        <span className="collectionTimeView-span">Facet Filters</span>
-                    </div>
-                </Flyout>
-            </div>
-            <div className="collectionTimeView-tree" key="tree">
-                <CollectionTreeView
-                    PanelPosition={""}
-                    Document={facetCollection}
-                    DataDoc={facetCollection}
-                    fieldKey={`${this.props.fieldKey}-filter`}
-                    CollectionView={this}
-                    docFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                    ContainingCollectionView={this.props.ContainingCollectionView}
-                    PanelWidth={this.facetWidth}
-                    PanelHeight={this.props.PanelHeight}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
-                    LibraryPath={emptyPath}
-                    rootSelected={this.props.rootSelected}
-                    renderDepth={1}
-                    dropAction={this.props.dropAction}
-                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                    addDocTab={returnFalse}
-                    pinToPres={returnFalse}
-                    isSelected={returnFalse}
-                    select={returnFalse}
-                    bringToFront={emptyFunction}
-                    active={this.props.active}
-                    whenActiveChanged={returnFalse}
-                    treeViewHideTitle={true}
-                    ContentScaling={returnOne}
-                    focus={returnFalse}
-                    treeViewHideHeaderFields={true}
-                    onCheckedClick={this.scriptField}
-                    ignoreFields={this.ignoreFields}
-                    annotationsKey={""}
-                    dontRegisterView={true}
-                    backgroundColor={this.filterBackground}
-                    moveDocument={returnFalse}
-                    removeDocument={returnFalse}
-                    addDocument={returnFalse} />
-            </div>
-        </div>;
-    }
-
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     childLayoutString = this.props.childLayoutString || StrCast(this.props.Document.childLayoutString);
 
@@ -603,11 +440,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
                     :
                     ""))}
-            {(Doc.UserDoc()?.noviceMode || !this.props.isSelected() && !this.props.Document.forceActive) || this.props.Document.hideFilterView ? (null) :
-                <div className="collectionView-filterDragger" title="library View Dragger" onPointerDown={this.onPointerDown}
-                    style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "60%" }} />
-            }
-            {Doc.UserDoc()?.noviceMode || this.facetWidth() < 10 ? (null) : this.filterView}
         </div>);
     }
 }
diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss
index c5d6e2505..b39793f01 100644
--- a/src/client/views/nodes/FilterBox.scss
+++ b/src/client/views/nodes/FilterBox.scss
@@ -1,44 +1,54 @@
 
 
-    .collectionTimeView-treeView {
-        display: flex;
-        flex-direction: column;
-        width: 200px;
-        height: 100%;
-        position: absolute;
-        right: 0;
-        top: 0;
-        border-left: solid 1px;
-        z-index: 1;
-
-        .collectionTimeView-addfacet {
-            display: inline-block;
-            width: 200px;
-            height: 30px;
-            background: darkGray;
+.filterBox-flyout {
+    width: 400px;
+    display: block;
+    text-align: left;
+    .filterBox-flyout-facet {
+            background-color: lightgray;
             text-align: left;
+            display: inline-block;
+            position: relative;
+            width: 100%;
+    }
+}
+.filterBox-treeView {
+    display: flex;
+    flex-direction: column;
+    width: 200px;
+    height: 100%;
+    position: absolute;
+    right: 0;
+    top: 0;
+    border-left: solid 1px;
+    z-index: 1;
 
-            .collectionTimeView-button {
-                align-items: center;
-                display: flex;
-                width: 100%;
-                height: 100%;
+    .filterBox-addfacet {
+        display: inline-block;
+        width: 200px;
+        height: 30px;
+        background: darkGray;
+        text-align: left;
 
-                .collectionTimeView-span {
-                    margin: auto;
-                }
-            }
+        .filterBox-addFacetButton {
+            display: flex;
+            margin: auto;
 
-            >div,
-            >div>div {
-                width: 100%;
-                height: 100%;
+            .filterBox-span {
+                margin-right: 15px;
             }
         }
 
-        .collectionTimeView-tree {
-            display: inline-block;
+        >div,
+        >div>div {
             width: 100%;
-            height: calc(100% - 30px);
+            height: 100%;
         }
-    }
\ No newline at end of file
+    }
+
+    .filterBox-tree {
+        display: inline-block;
+        width: 100%;
+        height: calc(100% - 30px);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 7bffc9184..7fcbce9e3 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -1,27 +1,24 @@
 import React = require("react");
-import { action, computed } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { ColorState, SketchPicker } from 'react-color';
-import { Doc, Opt, DocListCast, Field, DataSym } from "../../../fields/Doc";
-import { Utils, returnEmptyFilter, returnEmptyDoclist, returnZero, emptyPath, returnFalse, emptyFunction, returnOne } from "../../../Utils";
+import { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
-import { InkTool } from "../../../fields/InkField";
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { StrCast, Cast } from "../../../fields/Types";
-import { SelectionManager } from "../../util/SelectionManager";
-import { undoBatch } from "../../util/UndoManager";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import "./ColorBox.scss";
-import { FieldView, FieldViewProps } from './FieldView';
+import { List } from "../../../fields/List";
+import { RichTextField } from "../../../fields/RichTextField";
+import { listSpec, makeInterface } from "../../../fields/Schema";
+import { ComputedField, ScriptField } from "../../../fields/ScriptField";
+import { Cast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionTreeView } from "../collections/CollectionTreeView";
-import { ScriptField, ComputedField } from "../../../fields/ScriptField";
-import { Docs } from "../../documents/Documents";
-import { RichTextField } from "../../../fields/RichTextField";
-import { List } from "../../../fields/List";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { SearchBox } from "../search/SearchBox";
+import { FieldView, FieldViewProps } from './FieldView';
 import './FilterBox.scss';
-import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { Scripting } from "../../util/Scripting";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -33,17 +30,24 @@ const FilterBoxDocument = makeInterface(documentSchema);
 export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
 
-    @computed get _allFacets() {
-        return ["author", "creationDate", "type", "text", "context"];
-        // const noviceReqFields = ["author", "creationDate", "type", "text", "context"];
-        // const noviceLayoutFields: string[] = [];//["_curPage"];
-        // const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
+    @computed get allDocs() {
+        const allDocs = new Set<Doc>();
+        if (CollectionDockingView.Instance) {
+            const activeTabs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+            SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allDocs.add(doc));
+            setTimeout(() => CollectionDockingView.Instance.props.Document.allDocuments = new List<Doc>(Array.from(allDocs)));
+        }
+        return allDocs;
+    }
 
-        // const facets = new Set<string>([...noviceReqFields, ...noviceLayoutFields]);
-        // this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
-        // Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
+    @computed get _allFacets() {
+        const noviceReqFields = ["author", "tags", "text", "type"];
+        const noviceLayoutFields: string[] = [];//["_curPage"];
+        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
-        // return Array.from(facets).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort();
+        const keys = new Set<string>(noviceFields);
+        this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
+        return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort();
     }
     /**
      * Responds to clicking the check box in the flyout menu
@@ -95,7 +99,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                 newFacet._textBoxPadding = 4;
                 const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
                 newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
-            } else if (nonNumbers / facetValues.length < .1) {
+            } else if (facetHeader !== "tags" && nonNumbers / facetValues.length < .1) {
                 newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true });
                 const newFacetField = Doc.LayoutFieldKey(newFacet);
                 const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
@@ -115,7 +119,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                 newFacet.treeViewOpen = true;
                 newFacet.type = DocumentType.COL;
                 const capturedVariables = { layoutDoc: targetDoc, dataDoc: (targetDoc.data as any)[0][DataSym] };
-                newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables);
+                newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, "${facetHeader}")`, {}, capturedVariables);
             }
             newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
         }
@@ -128,28 +132,26 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
         return script ? () => script : undefined;
     }
 
-    @computed get filterView() {
+    render() {
         const facetCollection = this.props.Document.proto as Doc;
-        const flyout = (
-            <div className="collectionTimeView-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
-                {this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
-                    <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} />
-                    <span className="checkmark" />
-                    {facet}
-                </label>)}
-            </div>
-        );
+        const flyout = <div className="filterBox-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
+            {this._allFacets.map(facet => <label className="filterBox-flyout-face" key={`${facet}`} onClick={e => this.facetClick(facet)}>
+                <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} />
+                <span className="checkmark" />
+                {facet}
+            </label>)}
+        </div>;
 
-        return this.props.dontRegisterView ? (null) : <div className="collectionTimeView-treeView" style={{ width: "100%" }}>
-            <div className="collectionTimeView-addFacet" style={{ width: "100%" }} onPointerDown={e => e.stopPropagation()}>
+        return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}>
+            <div className="filterBox-addFacet" style={{ width: "100%" }} onPointerDown={e => e.stopPropagation()}>
                 <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
-                    <div className="collectionTimeView-button">
+                    <div className="filterBox-addFacetButton">
                         <FontAwesomeIcon icon={"edit"} size={"lg"} />
-                        <span className="collectionTimeView-span">Facet Filters</span>
+                        <span className="filterBox-span">Choose Facets</span>
                     </div>
                 </Flyout>
             </div>
-            <div className="collectionTimeView-tree" key="tree">
+            <div className="filterBox-tree" key="tree">
                 <CollectionTreeView
                     PanelPosition={""}
                     Document={facetCollection}
@@ -191,7 +193,23 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
             </div>
         </div>;
     }
-    render() {
-        return this.filterView;
-    }
-}
\ No newline at end of file
+}
+
+Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
+    const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments);
+    const set = new Set<string>();
+    if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+    else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
+    let facetValues = Array.from(set).filter(v => v);
+
+    let nonNumbers = 0;
+    facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
+    const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+        const doc = new Doc();
+        doc.system = true;
+        doc.title = facetValue.toString();
+        doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
+        return doc;
+    });
+    return new List<Doc>(facetValueDocSet);
+});
\ No newline at end of file
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3907d2d5c..b37ae02c3 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -179,6 +179,21 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         return finalDocs;
     }
 
+    static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
+        let newarray: Doc[] = [];
+        while (docs.length > 0) {
+            newarray = [];
+            docs.forEach(d => {
+                const fieldKey = Doc.LayoutFieldKey(d);
+                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                data && newarray.push(...DocListCast(data));
+                func(d);
+            });
+            docs = newarray;
+        }
+    }
+
     @action
     searchCollection(query: string) {
         const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
@@ -186,26 +201,14 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
         if (selectedCollection !== undefined) {
             // this._currentSelectedCollection = selectedCollection;
-            let docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
+            const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
             const found: [Doc, string[], string[]][] = [];
-            let newarray: Doc[] = [];
-
-            while (docs.length > 0) {
-                newarray = [];
-                docs.forEach(d => {
-                    const fieldKey = Doc.LayoutFieldKey(d);
-                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                    const data = d[annos ? fieldKey + "-annotations" : fieldKey];
-                    data && newarray.push(...DocListCast(data));
-                    const hlights = new Set<string>();
-                    this.documentKeys(d).forEach(key =>
-                        Field.toString(d[key] as Field).toLowerCase().includes(query) && hlights.add(key));
-                    if (Array.from(hlights.keys()).length > 0) {
-                        found.push([d, Array.from(hlights.keys()), []]);
-                    }
-                });
-                docs = newarray;
-            }
+            SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
+                const hlights = new Set<string>();
+                SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+                Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
+            });
+
             this._results = found;
             this.docsforfilter = this._results.map(r => r[0]);
             this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined);
@@ -218,7 +221,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     }
 
-    documentKeys(doc: Doc) {
+    static documentKeys(doc: Doc) {
         const keys: { [key: string]: boolean } = {};
         // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
         //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
-- 
cgit v1.2.3-70-g09d2


From 483a4d35ba2c522b889c7bd9b839a0af131ec0bb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Sep 2020 19:26:24 -0400
Subject: updated the way NativeWidth/Height props work so that, when
 specified, they override locally specified values

---
 src/Utils.ts                                       |  2 ++
 src/client/views/DocumentDecorations.tsx           |  6 +++---
 src/client/views/GestureOverlay.tsx                |  2 --
 src/client/views/MainView.tsx                      | 10 ---------
 src/client/views/OverlayView.tsx                   |  2 --
 src/client/views/Palette.tsx                       |  2 --
 src/client/views/PropertiesView.tsx                |  4 ++--
 src/client/views/TemplateMenu.tsx                  |  2 --
 .../views/collections/CollectionCarouselView.tsx   |  4 ++--
 .../views/collections/CollectionLinearView.tsx     |  2 --
 .../views/collections/CollectionSchemaView.tsx     |  2 --
 .../views/collections/CollectionStackingView.tsx   |  8 +++----
 src/client/views/collections/CollectionSubView.tsx |  4 ++--
 .../views/collections/CollectionTreeView.tsx       |  6 ++----
 src/client/views/collections/SchemaTable.tsx       |  2 --
 src/client/views/collections/TabDocView.tsx        |  2 --
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 14 ++++++------
 .../collectionGrid/CollectionGridView.tsx          |  2 --
 .../CollectionMulticolumnView.tsx                  |  8 ++++---
 .../CollectionMultirowView.tsx                     |  2 --
 src/client/views/nodes/AudioBox.tsx                |  4 ----
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  6 +++---
 .../views/nodes/ContentFittingDocumentView.tsx     | 10 ++++-----
 src/client/views/nodes/DocHolderBox.tsx            |  4 ----
 src/client/views/nodes/DocumentView.tsx            | 10 ++++-----
 src/client/views/nodes/FieldView.tsx               |  4 ++--
 src/client/views/nodes/FilterBox.tsx               |  2 --
 src/client/views/nodes/ImageBox.tsx                | 25 ++++++----------------
 src/client/views/nodes/KeyValuePair.tsx            |  2 --
 src/client/views/nodes/LinkBox.tsx                 |  2 --
 src/client/views/nodes/LinkDocPreview.tsx          |  2 --
 src/client/views/nodes/ScreenshotBox.tsx           |  2 --
 src/client/views/nodes/VideoBox.tsx                |  2 --
 src/client/views/nodes/WebBox.tsx                  |  4 +---
 .../views/nodes/formattedText/DashDocView.tsx      |  2 --
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +---
 .../formattedText/FormattedTextBoxComment.tsx      |  4 ++--
 .../views/nodes/formattedText/RichTextSchema.tsx   |  2 --
 src/client/views/pdf/PDFViewer.tsx                 |  2 --
 .../views/presentationview/PresElementBox.tsx      |  2 --
 src/mobile/AudioUpload.tsx                         |  2 --
 src/mobile/MobileInterface.tsx                     |  2 --
 42 files changed, 52 insertions(+), 133 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 6582e43ef..7dff1ac55 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -414,6 +414,8 @@ export function returnTrue() { return true; }
 
 export function returnFalse() { return false; }
 
+export function returnVal(val1?: number, val2?: number) { return val1 !== undefined ? val1 : val2 !== undefined ? val2 : 0; }
+
 export function returnOne() { return 1; }
 
 export function returnZero() { return 0; }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 81c1676b0..e5c6f0aa9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -10,7 +10,7 @@ import { InkField } from "../../fields/InkField";
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast } from "../../fields/Types";
 import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from "../../Utils";
+import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick, returnVal } from "../../Utils";
 import { DocUtils, Docs } from "../documents/Documents";
 import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from "../util/DragManager";
@@ -453,8 +453,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (e.ctrlKey && !element.props.Document._nativeHeight) element.toggleNativeDimensions();
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 const doc = Document(element.rootDoc);
-                let nwidth = doc._nativeWidth || 0;
-                let nheight = doc._nativeHeight || 0;
+                let nwidth = returnVal(element.NativeWidth?.(), doc._nativeWidth);
+                let nheight = returnVal(element.NativeHeight?.(), doc._nativeHeight);
                 const width = (doc._width || 0);
                 let height = (doc._height || (nheight / nwidth * width));
                 height = !height || isNaN(height) ? 20 : height;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 63711a3cb..6af3a40cf 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -926,8 +926,6 @@ export class GestureOverlay extends Touchable {
                 ContentScaling={returnOne}
                 PanelWidth={this.return300}
                 PanelHeight={this.return300}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 renderDepth={0}
                 backgroundColor={returnEmptyString}
                 focus={emptyFunction}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 318d0368c..d017de457 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -261,8 +261,6 @@ export class MainView extends React.Component {
             removeDocument={undefined}
             ScreenToLocalTransform={Transform.Identity}
             ContentScaling={returnOne}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
             PanelWidth={this.getPWidth}
             PanelHeight={this.getPHeight}
             focus={emptyFunction}
@@ -318,8 +316,6 @@ export class MainView extends React.Component {
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     rootSelected={returnTrue}
                     removeDocument={returnFalse}
                     onClick={undefined}
@@ -354,8 +350,6 @@ export class MainView extends React.Component {
                 addDocument={undefined}
                 addDocTab={this.addDocTabFunc}
                 pinToPres={emptyFunction}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
                 onClick={undefined}
@@ -487,8 +481,6 @@ export class MainView extends React.Component {
                     onClick={undefined}
                     ScreenToLocalTransform={this.buttonBarXf}
                     ContentScaling={returnOne}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     PanelWidth={this.flyoutWidthFunc}
                     PanelHeight={this.getContentsHeight}
                     renderDepth={0}
@@ -553,8 +545,6 @@ export class MainView extends React.Component {
                 removeDocument={undefined}
                 ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={returnOne}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 PanelWidth={this.getPWidth}
                 PanelHeight={this.getPHeight}
                 renderDepth={0}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index cc0dd0443..4b8049e14 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -193,8 +193,6 @@ export class OverlayView extends React.Component {
                     addDocument={undefined}
                     removeDocument={undefined}
                     ContentScaling={returnOne}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     PanelWidth={returnOne}
                     PanelHeight={returnOne}
                     ScreenToLocalTransform={Transform.Identity}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 92c3f09b4..9f08a03e1 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -49,8 +49,6 @@ export default class Palette extends React.Component<PaletteProps> {
                             onClick={undefined}
                             ScreenToLocalTransform={Transform.Identity}
                             ContentScaling={returnOne}
-                            NativeHeight={returnZero}
-                            NativeWidth={returnZero}
                             PanelWidth={() => window.screen.width}
                             PanelHeight={() => window.screen.height}
                             renderDepth={0}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 041eec2b4..7da56721a 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -287,9 +287,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     fitToBox={true}
                     FreezeDimensions={true}
                     NativeWidth={layoutDoc.type ===
-                        StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : returnZero}
+                        StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : undefined}
                     NativeHeight={layoutDoc.type ===
-                        StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : returnZero}
+                        StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : undefined}
                     PanelWidth={panelWidth}
                     PanelHeight={panelHeight}
                     focus={returnFalse}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 870af03aa..c1878115d 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -150,8 +150,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 select={emptyFunction}
                 renderDepth={1}
                 addDocTab={returnFalse}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 PanelWidth={this.return100}
                 PanelHeight={this.return100}
                 treeViewHideHeaderFields={true}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 5f63f536d..034d006f2 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -13,7 +13,7 @@ import { Doc } from '../../../fields/Doc';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { ContextMenu } from '../ContextMenu';
 import { ObjectField } from '../../../fields/ObjectField';
-import { returnFalse } from '../../../Utils';
+import { returnFalse, returnZero } from '../../../Utils';
 import { ScriptField } from '../../../fields/ScriptField';
 
 type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
@@ -54,9 +54,9 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
                         renderDepth={this.props.renderDepth + 1}
-                        dontRegisterView={layoutTemp} // temporary hack to mark documents as being a template  see imageBox
                         LayoutTemplate={this.props.ChildLayoutTemplate}
                         LayoutTemplateString={this.props.ChildLayoutString}
+                        NativeWidth={layoutTemp ? returnZero : this.props.NativeWidth}
                         Document={curDoc.layout}
                         DataDoc={curDoc.data}
                         PanelHeight={this.panelHeight}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index ea050011a..22c1f51a6 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -153,8 +153,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 onClick={undefined}
                                 ScreenToLocalTransform={this.getTransform(dref)}
                                 ContentScaling={returnOne}
-                                NativeHeight={returnZero}
-                                NativeWidth={returnZero}
                                 PanelWidth={nested ? pair.layout[WidthSym] : () => this.dimension()}// ugh - need to get rid of this inline function to avoid recomputing
                                 PanelHeight={nested ? pair.layout[HeightSym] : () => this.dimension()}
                                 renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1b68c0e1a..332a2001f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -429,8 +429,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                 <ContentFittingDocumentView
                     Document={this.previewDocument}
                     DataDoc={undefined}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     fitToBox={true}
                     FreezeDimensions={true}
                     focus={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b9a94642a..84173d3ba 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -12,7 +12,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
 import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll, returnVal } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -209,8 +209,6 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
             fitToBox={false}
             dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.dontRegisterChildViews, this.props.dontRegisterView)}
             rootSelected={this.rootSelected}
@@ -481,8 +479,8 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
     }
 
 
-    @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth) || this.props.NativeWidth() || 0; }
-    @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight) || this.props.NativeHeight() || 0; }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc._nativeWidth)); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc._nativeHeight)); }
 
     @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 4a8d66050..8e4c89922 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -31,8 +31,8 @@ export interface CollectionViewProps extends FieldViewProps {
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
     rootSelected: (outsideReaction?: boolean) => boolean;
     fieldKey: string;
-    NativeWidth: () => number;
-    NativeHeight: () => number;
+    NativeWidth?: () => number;
+    NativeHeight?: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f13fee776..7b3e95404 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -357,8 +357,8 @@ class TreeView extends React.Component<TreeViewProps> {
                     backgroundColor={this.props.backgroundColor}
                     fitToBox={this.boundsOfCollectionDocument !== undefined}
                     FreezeDimensions={true}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : returnZero}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : returnZero}
+                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
                     PanelWidth={panelWidth}
                     PanelHeight={panelHeight}
                     focus={returnFalse}
@@ -464,8 +464,6 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContentScaling={returnOne}
                 PanelWidth={this.truncateTitleWidth}
                 PanelHeight={returnZero}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 contextMenuItems={this.contextMenuItems}
                 opacity={returnOne}
                 renderDepth={1}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 1fb7aa04a..854254195 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -570,8 +570,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                     ref="overlay"><ContentFittingDocumentView
                         Document={this._showDoc}
                         DataDoc={this._showDataDoc}
-                        NativeHeight={returnZero}
-                        NativeWidth={returnZero}
                         fitToBox={true}
                         FreezeDimensions={true}
                         focus={emptyFunction}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index fb4f5c366..93d830b8a 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -306,8 +306,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 ContentScaling={returnOne}
                 PanelWidth={this.returnMiniSize}
                 PanelHeight={this.returnMiniSize}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 ScreenToLocalTransform={this.ScreenToLocalTransform}
                 renderDepth={0}
                 whenActiveChanged={emptyFunction}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 86460eee1..aa334df6c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -12,7 +12,7 @@ import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { TraceMobx } from "../../../../fields/util";
 import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnVal } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from "../../../documents/Documents";
@@ -107,8 +107,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
     @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
     @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); }
-    @computed get nativeWidth() { return this.fitToContent ? 0 : NumCast(this.Document._nativeWidth, this.props.NativeWidth()); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : NumCast(this.Document._nativeHeight, this.props.NativeHeight()); }
+    @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth)); }
+    @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight)); }
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
@@ -942,8 +942,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             moveDocument: this.props.moveDocument,
             pinToPres: this.props.pinToPres,
             whenActiveChanged: this.props.whenActiveChanged,
-            NativeHeight: returnZero,
-            NativeWidth: returnZero,
             fitToBox: false,
             DataDoc: childData,
             Document: childLayout,
@@ -1218,7 +1216,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @undoBatch
     @action
     toggleNativeDimensions = () => {
-        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
     }
 
     @undoBatch
@@ -1411,8 +1409,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @computed get contentScaling() {
         if (this.props.annotationsKey && !this.props.forceScaling) return 0;
-        const nw = NumCast(this.Document._nativeWidth, this.props.NativeWidth());
-        const nh = NumCast(this.Document._nativeHeight, this.props.NativeHeight());
+        const nw = returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth));
+        const nh = returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight));
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index df4d71526..bc04c79f9 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -165,8 +165,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             {...this.props}
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
             backgroundColor={this.props.backgroundColor}
             ContainingCollectionDoc={this.props.Document}
             PanelWidth={width}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 0afcab5a3..b3f5d1c0f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -213,19 +213,19 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         return this.props.addDocTab(doc, where);
     }
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
+        const layoutTemp = this.props.DataDoc ? true : undefined;
         return <ContentFittingDocumentView
             Document={layout}
-            DataDoc={layout.resolvedDataDoc as Doc}
+            DataDoc={layout.resolvedDataDoc as Doc || (layoutTemp ? layout : undefined)}
             backgroundColor={this.props.backgroundColor}
             LayoutTemplate={this.props.ChildLayoutTemplate}
             LayoutTemplateString={this.props.ChildLayoutString}
+            NativeWidth={layoutTemp ? returnZero : this.props.NativeWidth}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
             fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
@@ -248,6 +248,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             ContentScaling={returnOne}
         />;
     }
+    @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
     /**
      * @returns the resolved list of rendered child documents, displayed
      * at their resolved pixel widths, each separated by a resizer. 
@@ -257,6 +258,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         const { childLayoutPairs } = this;
         const { Document, PanelHeight } = this.props;
         const collector: JSX.Element[] = [];
+        const layoutTemp = this.props.DataDoc ? true : undefined;
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
             const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin));
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 53825eece..9a4fb0627 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -224,8 +224,6 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
             fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 0e3c4462c..5e7f8dfda 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -520,8 +520,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 Document={mark}
                 focus={() => this.playLink(mark)}
                 pointerEvents={true}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 rootSelected={returnFalse}
                 LayoutTemplate={undefined}
                 ContainingCollectionDoc={this.props.Document}
@@ -607,8 +605,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                                     <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={l[Id]} style={{ left: `${startTime / this.audioDuration * 100}%` }} onClick={e => e.stopPropagation()}>
                                         <DocumentView {...this.props}
                                             Document={l}
-                                            NativeHeight={returnZero}
-                                            NativeWidth={returnZero}
                                             rootSelected={returnFalse}
                                             ContainingCollectionDoc={this.props.Document}
                                             parentActive={returnTrue}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 16b2f7c2a..bfa66272e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -11,7 +11,7 @@ import { Document } from "../../../fields/documentSchemas";
 import { TraceMobx } from "../../../fields/util";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 import { List } from "../../../fields/List";
-import { numberRange, smoothScroll } from "../../../Utils";
+import { numberRange, smoothScroll, returnVal } from "../../../Utils";
 import { ComputedField } from "../../../fields/ScriptField";
 import { listSpec } from "../../../fields/Schema";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -55,8 +55,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
     @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); }
-    @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
-    @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc._nativeWidth, this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc._nativeHeight, this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
 
     public static getValues(doc: Doc, time: number) {
         const timecode = Math.round(time);
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 6050d03cc..30458b9f8 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -6,7 +6,7 @@ import { Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction } from "../../../Utils";
+import { emptyFunction, returnVal } from "../../../Utils";
 import { dropActionType } from "../../util/DragManager";
 import { CollectionView } from "../collections/CollectionView";
 import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
@@ -26,8 +26,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
 
-    nativeWidth = () => NumCast(this.layoutDoc?.[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeWidth"], this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()));
-    nativeHeight = () => NumCast(this.layoutDoc?.[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeHeight"], this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()));
+    nativeWidth = () => returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc?.[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeWidth"], (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth())));
+    nativeHeight = () => returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc?.[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeHeight"], (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())));
     @computed get scaling() {
         const wscale = this.props.PanelWidth() / this.nativeWidth();
         if (wscale * this.nativeHeight() > this.props.PanelHeight()) {
@@ -40,8 +40,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     private PanelWidth = () => this.panelWidth;
     private PanelHeight = () => this.panelHeight;
 
-    @computed get panelWidth() { return this.nativeWidth && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
-    @computed get panelHeight() { return this.nativeHeight && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
+    @computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
+    @computed get panelHeight() { return this.nativeHeight() && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
 
     private getTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
     private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index b3b7cc4f3..91f96135f 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -135,8 +135,6 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     pinToPres={this.props.pinToPres}
                     ScreenToLocalTransform={this.getTransform}
                     renderDepth={containedDoc.type !== DocumentType.DOCHOLDER && !this.props.renderDepth ? 0 : this.props.renderDepth + 1}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     PanelWidth={this.pwidth}
                     PanelHeight={this.pheight}
                     focus={this.props.focus}
@@ -166,8 +164,6 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     pinToPres={this.props.pinToPres}
                     ScreenToLocalTransform={this.getTransform}
                     renderDepth={containedDoc.type !== DocumentType.DOCHOLDER && !this.props.renderDepth ? 0 : this.props.renderDepth + 1}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     PanelWidth={this.pwidth}
                     PanelHeight={this.pheight}
                     focus={this.props.focus}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a4ac1c4dc..9992874bf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -11,7 +11,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, returnVal } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -49,8 +49,8 @@ export interface DocumentViewProps {
     docFilters: () => string[];
     searchFilterDocs: () => Doc[];
     FreezeDimensions?: boolean;
-    NativeWidth: () => number;
-    NativeHeight: () => number;
+    NativeWidth?: () => number;
+    NativeHeight?: () => number;
     Document: Doc;
     DataDoc?: Doc;
     getView?: (view: DocumentView) => any;
@@ -119,8 +119,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     public get ContentDiv() { return this._mainCont.current; }
     @computed get topMost() { return this.props.renderDepth === 0; }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
-    @computed get nativeWidth() { return NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeWidth"], this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
-    @computed get nativeHeight() { return NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeHeight"], this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
     @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
     @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 746a906c7..4ec5bc534 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -52,8 +52,8 @@ export interface FieldViewProps {
     PanelHeight: () => number;
     PanelPosition?: string;
     overflow?: boolean;
-    NativeHeight: () => number;
-    NativeWidth: () => number;
+    NativeHeight?: () => number;
+    NativeWidth?: () => number;
     setVideoBox?: (player: VideoBox) => void;
     ContentScaling: () => number;
     ChromeHeight?: () => number;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index a97f8c541..eab365445 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -164,8 +164,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     PanelWidth={this.props.PanelWidth}
                     PanelHeight={this.props.PanelHeight}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     LibraryPath={emptyPath}
                     rootSelected={this.props.rootSelected}
                     renderDepth={1}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 952e92ca6..beb6ba6a8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -75,10 +75,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
     componentDidMount() {
         this._pathDisposer = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
             ({ nativeSize, width }) => {
-                this.layoutDoc._nativeWidth = nativeSize.nativeWidth;
-                this.layoutDoc._nativeHeight = nativeSize.nativeHeight;
-                this.layoutDoc._nativeOrientation = nativeSize.nativeOrientation;
-                this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
+                if (this.props.NativeWidth?.() !== 0 || !this.layoutDoc._height) {
+                    this.layoutDoc._nativeWidth = nativeSize.nativeWidth;
+                    this.layoutDoc._nativeHeight = nativeSize.nativeHeight;
+                    this.layoutDoc._nativeOrientation = nativeSize.nativeOrientation;
+                    this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
+                }
             },
             { fireImmediately: true });
     }
@@ -164,19 +166,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
             if (!Doc.UserDoc().noviceMode) {
                 funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
                 funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
-                // funcs.push({
-                //     description: "Reset Native Dimensions", event: action(async () => {
-                //         const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
-                //         const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
-                //         if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
-                //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
-                //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
-                //         } else {
-                //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
-                //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
-                //         }
-                //     }), icon: "expand-arrows-alt"
-                // });
 
                 const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
                 const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
@@ -434,8 +423,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 forceScaling={true}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 annotationsKey={this.annotationKey}
                 isAnnotationOverlay={true}
                 focus={this.props.focus}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 74d10d087..e531083bf 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -71,8 +71,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             whenActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
-            NativeHeight: returnZero,
-            NativeWidth: returnZero,
             PanelWidth: this.props.PanelWidth,
             PanelHeight: this.props.PanelHeight,
             addDocTab: returnFalse,
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index a067f23af..64ae1051b 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -22,8 +22,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
             <CollectionTreeView {...this.props}
                 ChromeHeight={returnZero}
                 overrideDocuments={[this.dataDoc]}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
                 annotationsKey={""}
                 dontRegisterView={true}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index a70736d90..875026944 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -114,8 +114,6 @@ export class LinkDocPreview extends React.Component<Props> {
                 whenActiveChanged={returnFalse}
                 bringToFront={returnFalse}
                 ContentScaling={returnOne}
-                NativeWidth={returnZero}
-                NativeHeight={returnZero}
                 backgroundColor={this.props.backgroundColor} />;
     }
 
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 5d51c420b..34ad7f3be 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -172,8 +172,6 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
                 <CollectionFreeFormView {...this.props}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.props.PanelWidth}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     annotationsKey={""}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d50a10bdd..79123c081 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -368,8 +368,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                 <CollectionFreeFormView {...this.props}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.props.PanelWidth}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     annotationsKey={this.annotationKey}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c5d7c3c9f..41c310a96 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -369,7 +369,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @undoBatch
     @action
     toggleNativeDimensions = () => {
-        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
     }
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
@@ -650,8 +650,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             PanelHeight={this.props.PanelHeight}
                             PanelWidth={this.props.PanelWidth}
                             annotationsKey={this.annotationKey}
-                            NativeHeight={returnZero}
-                            NativeWidth={returnZero}
                             VisibleHeight={this.visibleHeiht}
                             focus={this.props.focus}
                             setPreviewCursor={this.setPreviewCursor}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 3b77735a7..90655bad6 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -245,8 +245,6 @@ export class DashDocView extends React.Component<IDashDocView> {
                             addDocTab={this._textBox.props.addDocTab}
                             pinToPres={returnFalse}
                             renderDepth={self._textBox.props.renderDepth + 1}
-                            NativeHeight={returnZero}
-                            NativeWidth={returnZero}
                             PanelWidth={finalLayout[WidthSym]}
                             PanelHeight={finalLayout[HeightSym]}
                             focus={this.outerFocus}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f0dc92b90..335605a00 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -562,7 +562,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     @undoBatch
     @action
     toggleNativeDimensions = () => {
-        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.()||0, this.props.NativeHeight?.()||0);
     }
 
     public static get DefaultLayout(): Doc | string | undefined {
@@ -1565,8 +1565,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                             <CollectionFreeFormView {...this.props}
                                 PanelHeight={this.props.PanelHeight}
                                 PanelWidth={this.sidebarWidth}
-                                NativeHeight={returnZero}
-                                NativeWidth={returnZero}
                                 scaleField={this.annotationKey + "-scale"}
                                 annotationsKey={this.annotationKey}
                                 isAnnotationOverlay={false}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index b4f648273..8524786c8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -319,8 +319,8 @@ export class FormattedTextBoxComment {
                                             whenActiveChanged={returnFalse}
                                             bringToFront={returnFalse}
                                             ContentScaling={returnOne}
-                                            NativeWidth={() => target._nativeWidth ? NumCast(target._nativeWidth) : 0}
-                                            NativeHeight={() => target._nativeHeight ? NumCast(target._nativeHeight) : 0}
+                                            NativeWidth={target._nativeWidth ? (() => NumCast(target._nativeWidth)) : undefined}
+                                            NativeHeight={target._natvieHeight ? (() => NumCast(target._nativeHeight)) : undefined}
                                         />
                                     </div>
                                 </div>;
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index f0bacb735..a80d2639d 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -145,8 +145,6 @@ export class DashDocView {
                     addDocTab={this._textBox.props.addDocTab}
                     pinToPres={returnFalse}
                     renderDepth={self._textBox.props.renderDepth + 1}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     PanelWidth={finalLayout[WidthSym]}
                     PanelHeight={finalLayout[HeightSym]}
                     focus={this.outerFocus}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 18be9b679..65eb23a06 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -679,8 +679,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 setPreviewCursor={this.setPreviewCursor}
                 PanelHeight={this.panelWidth}
                 PanelWidth={this.panelHeight}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
                 dropAction={"alias"}
                 VisibleHeight={this.visibleHeight}
                 focus={this.props.focus}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index c33637d0f..048d3a3d0 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -107,8 +107,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     ContentScaling={returnOne}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                 />
                 <div className="presElementBox-embeddedMask" />
             </div>;
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 5e86246c5..81b3869ae 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -94,8 +94,6 @@ export class AudioUpload extends React.Component {
                         ContentScaling={returnOne}
                         PanelWidth={() => 600}
                         PanelHeight={() => 400}
-                        NativeHeight={returnZero}
-                        NativeWidth={returnZero}
                         renderDepth={0}
                         focus={emptyFunction}
                         backgroundColor={() => "rgba(0,0,0,0)"}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index ae2c07c8e..cb5191e61 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -213,8 +213,6 @@ export class MobileInterface extends React.Component {
                     ContentScaling={returnOne}
                     PanelWidth={this.returnWidth}
                     PanelHeight={this.returnHeight}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
                     renderDepth={0}
                     focus={emptyFunction}
                     backgroundColor={this.whitebackground}
-- 
cgit v1.2.3-70-g09d2


From d5e086e4c161e7d445f6d69faca8ffc7391ed3dd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 9 Sep 2020 04:04:58 -0400
Subject: fixed doc decorations on selected treeview items to not overlap as
 much. fixed contentFitting's screentToLocalXf which wasn't wroking for nested
 tempaltes with native dimensions

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                | 23 +++++++++++++++++++++-
 src/client/util/HypothesisUtils.ts                 |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 ++--
 src/client/views/collections/CollectionSubView.tsx | 17 ++++++++++------
 .../views/collections/CollectionTreeView.scss      |  1 +
 .../views/collections/CollectionTreeView.tsx       |  4 ++--
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../views/nodes/ContentFittingDocumentView.scss    |  2 ++
 .../views/nodes/ContentFittingDocumentView.tsx     | 21 ++++++++++----------
 src/client/views/nodes/ImageBox.tsx                |  4 ++--
 src/client/views/nodes/WebBox.tsx                  |  8 +++++++-
 12 files changed, 63 insertions(+), 27 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7e276747c..2f7b5a449 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1050,7 +1050,7 @@ export namespace DocUtils {
                 });
             }
             ctor = Docs.Create.WebDocument;
-            options = { ...options, _nativeWidth: 850, _nativeHeight: 962, _width: 500, _height: 566, title: path, };
+            options = { ...options, _nativeWidth: 850, _width: 400, _height: 512, title: path, };
         }
         return ctor ? ctor(path, options) : undefined;
     }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7a1c193c1..82e80a394 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -245,6 +245,26 @@ export class CurrentUserUtils {
                 removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "window-maximize", system: true
             });
         }
+        if (doc["template-button-simple"] === undefined) {
+            const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create;
+
+            const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)");
+            const carousel = CarouselDocument([], {
+                title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10,
+                onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true
+            });
+
+            const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
+            const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
+            const detailView = Docs.Create.StackingDocument([carousel], { ...shared, ...detailViewOpts, system: true });
+            detailView.isTemplateDoc = makeTemplate(detailView);
+
+            doc["template-button-simple"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
+                dragFactory: new PrefetchProxy(detailView) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "simple view", icon: "window-maximize", system: true
+            });
+        }
 
         const requiredTypes = [
             doc["template-button-slides"] as Doc,
@@ -252,6 +272,7 @@ export class CurrentUserUtils {
             doc["template-button-query"] as Doc,
             doc["template-mobile-button"] as Doc,
             doc["template-button-detail"] as Doc,
+            doc["template-button-simple"] as Doc,
             doc["template-button-link"] as Doc,
             doc["template-button-switch"] as Doc];
         if (doc["template-buttons"] === undefined) {
@@ -439,7 +460,7 @@ export class CurrentUserUtils {
                 { _width: 250, _height: 250, title: "container", system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.emptyWebpage === undefined) {
-            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
         }
         if (doc.activeMobileMenu === undefined) {
             this.setupActiveMobileMenu(doc);
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 4a5b52e1e..8cb523093 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -21,7 +21,7 @@ export namespace Hypothesis {
     export const getSourceWebDoc = async (uri: string) => {
         const result = await findWebDoc(uri);
         console.log(result ? "existing doc found" : "existing doc NOT found");
-        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
+        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
     };
 
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e5c6f0aa9..abad53b6d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -647,8 +647,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
                     {closeIcon}
-                    {titleArea}
-                    {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || minimal ? (null) :
+                    {Object.keys(SelectionManager.SelectedDocuments()[0].props).includes("treeViewDoc") ? (null) : titleArea}
+                    {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || minimal || Object.keys(SelectionManager.SelectedDocuments()[0].props).includes("treeViewDoc") ? (null) :
                         <Tooltip title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
                             <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
                                 <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 81f3ddad2..aff7e8000 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -270,6 +270,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 return;
             }
 
+            console.log("Html = ", html);
             if (html) {
                 if (FormattedTextBox.IsFragment(html)) {
                     const href = FormattedTextBox.GetHref(html);
@@ -353,14 +354,15 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             if (uriList || text) {
                 if ((uriList || text).includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
                     const url = (uriList || text).replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0];
-                    this.addDocument(Docs.Create.VideoDocument(url, {
+                    console.log("Video URI = ", uriList);
+                    console.log("Add:" + this.addDocument(Docs.Create.VideoDocument(url, {
                         ...options,
                         title: url,
                         _width: 400,
                         _height: 315,
                         _nativeWidth: 600,
                         _nativeHeight: 472.5
-                    }));
+                    })));
                     return;
                 }
                 // let matches: RegExpExecArray | null;
@@ -381,27 +383,30 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 // }
             }
             if (uriList) {
+                ;
+                console.log("Web URI = ", uriList);
                 const existingWebDoc = await Hypothesis.findWebDoc(uriList);
                 if (existingWebDoc) {
                     const alias = Doc.MakeAlias(existingWebDoc);
                     alias.x = options.x;
                     alias.y = options.y;
                     alias._nativeWidth = 850;
-                    alias._nativeHeight = 962;
+                    alias._height = 512;
                     alias._width = 400;
                     this.addDocument(alias);
                 } else {
+                    console.log("Adding ...");
                     const newDoc = Docs.Create.WebDocument(uriList, {
                         ...options,
                         title: uriList.split("#annotations:")[0],
                         _width: 400,
-                        _height: 315,
+                        _height: 512,
                         _nativeWidth: 850,
-                        _nativeHeight: 962,
                         useCors: true
                     });
+                    console.log(" ... " + newDoc.title);
                     newDoc.data = new WebField(uriList.split("#annotations:")[0]); // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
-                    this.addDocument(newDoc);
+                    console.log(" ... " + this.addDocument(newDoc) + " " + newDoc.title);
                 }
                 return;
             }
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index b0a791a21..e192f1760 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -109,6 +109,7 @@
 .treeViewItem-border {
     display: flex;
     border-left: dashed 1px #00000042;
+    overflow: hidden;
 }
 
 .treeViewItem-header-editing,
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7b3e95404..6c3fb56e2 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -348,8 +348,8 @@ class TreeView extends React.Component<TreeViewProps> {
             const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
             return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
                 <ContentFittingDocumentView
-                    Document={layoutDoc}
-                    DataDoc={this.dataDoc}
+                    Document={this.doc}
+                    DataDoc={undefined}
                     LibraryPath={emptyPath}
                     renderDepth={this.props.renderDepth + 1}
                     rootSelected={returnTrue}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d8e1bcc9c..deb7e68e8 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -76,7 +76,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
         if (e.key === "?") {
             cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
-                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", useCors: true }), "add:right"));
+                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, isAnnotating: false, title: "bing", useCors: true }), "add:right"));
 
             cm.displayMenu(this._downX, this._downY);
             e.stopPropagation();
diff --git a/src/client/views/nodes/ContentFittingDocumentView.scss b/src/client/views/nodes/ContentFittingDocumentView.scss
index 50562951a..679073d44 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.scss
+++ b/src/client/views/nodes/ContentFittingDocumentView.scss
@@ -3,6 +3,8 @@
 .contentFittingDocumentView {
     position: relative;
     display: flex;
+    width: 100%;
+    height: 100%;
 
     .contentFittingDocumentView-previewDoc {
         position: relative;
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index c0bcc0a6d..7cb27451f 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -6,7 +6,7 @@ import { Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnVal } from "../../../Utils";
+import { emptyFunction, returnVal, OmitKeys } from "../../../Utils";
 import { dropActionType } from "../../util/DragManager";
 import { CollectionView } from "../collections/CollectionView";
 import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
@@ -34,8 +34,9 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
             (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())))
     @computed get scaling() {
         const wscale = this.props.PanelWidth() / this.nativeWidth();
+        const hscale = this.props.PanelHeight() / this.nativeHeight();
         if (wscale * this.nativeHeight() > this.props.PanelHeight()) {
-            return (this.props.PanelHeight() / this.nativeHeight()) || 1;
+            return hscale || 1;
         }
         return wscale || 1;
     }
@@ -47,7 +48,10 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     @computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
     @computed get panelHeight() { return this.nativeHeight() && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
 
-    private getTransform = () => this.props.dontCenter ? this.props.ScreenToLocalTransform().scale(1 / this.contentScaling()) : this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
+    @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); }
+    private getTransform = () => this.props.dontCenter ?
+        this.props.ScreenToLocalTransform().scale(this.childXf) :
+        this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(this.childXf);
     private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
 
@@ -55,10 +59,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
 
     render() {
         TraceMobx();
-        return (<div className="contentFittingDocumentView" style={{
-            width: Math.abs(this.centeringYOffset) > 0.001 ? "auto" : this.props.PanelWidth(),
-            height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(),
-        }}>
+        return (<div className="contentFittingDocumentView">
             {!this.props.Document || !this.props.PanelWidth ? (null) : (
                 <div className="contentFittingDocumentView-previewDoc"
                     style={{
@@ -67,14 +68,14 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                         height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight() / this.nativeWidth() * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
                         width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth()
                     }}>
-                    <DocumentView {...this.props}
+                    <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                         Document={this.props.Document}
                         DataDoc={this.props.DataDoc}
                         LayoutTemplate={this.props.LayoutTemplate}
                         LayoutTemplateString={this.props.LayoutTemplateString}
                         LibraryPath={this.props.LibraryPath}
-                        NativeWidth={this.nativeWidth}
-                        NativeHeight={this.nativeHeight}
+                        // NativeWidth={this.nativeWidth}
+                        // NativeHeight={this.nativeHeight}
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
                         ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index beb6ba6a8..ce056b80c 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -11,7 +11,7 @@ import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { AudioField, ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, returnZero, Utils } from '../../../Utils';
+import { emptyFunction, returnOne, returnZero, Utils, OmitKeys } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
 import { Docs } from '../../documents/Documents';
@@ -419,7 +419,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
                 borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
             }} >
-            <CollectionFreeFormView {...this.props}
+            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                 forceScaling={true}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f841fa6e1..9620e241c 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import { Dictionary } from "typescript-collections";
 import * as WebRequest from 'web-request';
-import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin, DataSym } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin, DataSym, HeightSym, WidthSym } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { HtmlField } from "../../../fields/HtmlField";
@@ -68,6 +68,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     private _iframeDragRef = React.createRef<HTMLDivElement>();
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
 
+    constructor(props: any) {
+        super(props);
+        this.dataDoc[this.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 850));
+        this.dataDoc[this.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, this.Document[HeightSym]() / this.Document[WidthSym]() * 850));
+    }
+
     iframeLoaded = action((e: any) => {
         const iframe = this._iframeRef.current;
         if (iframe && iframe.contentDocument) {
-- 
cgit v1.2.3-70-g09d2


From 57d7a09f7b8834382ab2974f70a2c5be7a694cd7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Sep 2020 15:05:41 -0400
Subject: added"_isBackground-canClick for background collections that are
 still clickable.   fixed colors for treeView.  fixed events on flyout to be
 processed after React processses them for everything else - problem was that
 clicking on the color picker deselected the item being colorized.

---
 src/client/views/MainView.tsx                                       | 4 ++--
 src/client/views/collections/CollectionTreeView.tsx                 | 6 +++---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++--
 src/client/views/nodes/ColorBox.tsx                                 | 2 +-
 src/client/views/nodes/DocumentView.tsx                             | 4 +++-
 5 files changed, 11 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bdf8c83e5..379577a25 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -101,6 +101,7 @@ export class MainView extends React.Component {
             DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
         });
         document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+        this.initEventListeners();
     }
 
     componentWillUnMount() {
@@ -147,7 +148,6 @@ export class MainView extends React.Component {
             fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
             fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload,
             fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines);
-        this.initEventListeners();
         this.initAuthenticationRouters();
     }
 
@@ -178,7 +178,7 @@ export class MainView extends React.Component {
                     SelectionManager.DeselectAll();
                 }
             }
-        });
+        }, false);
     }
 
     initAuthenticationRouters = async () => {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6c3fb56e2..d2b86d3c6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -32,7 +32,6 @@ import React = require("react");
 import { makeTemplate } from '../../util/DropConverter';
 import { TraceMobx } from '../../../fields/util';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { CollectionDockingView } from './CollectionDockingView';
 
 export interface TreeViewProps {
     document: Doc;
@@ -465,7 +464,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 PanelWidth={this.truncateTitleWidth}
                 PanelHeight={returnZero}
                 contextMenuItems={this.contextMenuItems}
-                opacity={returnOne}
+                opacity={this.props.treeView.props.Document.treeViewOutlineMode ? undefined : returnOne}
                 renderDepth={1}
                 focus={returnTrue}
                 parentActive={returnTrue}
@@ -838,12 +837,13 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
             this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged);
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
+        const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth);
 
         return !childDocs ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget" id="body"
                     style={{
-                        background: this.props.backgroundColor?.(this.doc, this.props.renderDepth),
+                        background: backgroundColor,
                         paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
                         paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
                         paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index aa334df6c..543798b7e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -217,7 +217,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            d._isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            d._isBackground === undefined && !d["_isBackground-canClick"] && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -839,7 +839,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     bringToFront = action((doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || doc._isBackground) {
+        if (sendToBack || doc._isBackground || doc["_isBackground-canClick"]) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 090cf015a..0aa0c33fb 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -55,7 +55,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
     render() {
         const selDoc = SelectionManager.SelectedDocuments()?.[0]?.rootDoc;
         return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
-            onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
+            onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} onClick={e => { (e.nativeEvent as any).stuff = true; e.stopPropagation(); }}
             style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
 
             <SketchPicker onChange={ColorBox.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 89ce32e78..7ef5eaf54 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -299,7 +299,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !this.props.Document._isBackground && this.props.bringToFront(this.props.Document);
+            !this.props.Document._isBackground && !this.props.Document["_isBackground-canClick"] && this.props.bringToFront(this.props.Document);
             if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
@@ -762,6 +762,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
         optionItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" });
         optionItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" });
+        optionItems.push({ description: "Lock in Back", event: () => this.rootDoc["_isBackground-canClick"] = !this.rootDoc["_isBackground-canClick"], icon: "expand-arrows-alt" });
         !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
 
@@ -1007,6 +1008,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get ignorePointerEvents() {
         return this.props.pointerEvents === false ||
+            (SnappingManager.GetIsDragging() && this.Document["_isBackground-canClick"]) ||
             (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
             (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None);
     }
-- 
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/CollectionTreeView.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


From 4f8fb6a10982309c7fcbfa479df36c14aba0198d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Sep 2020 21:58:20 -0400
Subject: made images in treeView outlines show up without a title

---
 src/client/util/CurrentUserUtils.ts                |  2 +-
 .../views/collections/CollectionTreeView.tsx       | 24 ++++++++++------------
 2 files changed, 12 insertions(+), 14 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b3567a4e8..20799fa26 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -419,7 +419,7 @@ export class CurrentUserUtils {
             ((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"]) });
+            doc.emptySlide = Docs.Create.TreeDocument([], { title: "slide", treeViewOutlineMode: 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"]) });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a6975f517..08906218a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -208,11 +208,7 @@ class TreeView extends React.Component<TreeViewProps> {
             Doc.UserDoc().activeSelection = new List([this.doc]);
             return false;
         }}
-        OnEmpty={undoBatch(() => {
-            if (this.props.treeView.doc.treeViewOutlineMode) {
-                this.props.removeDoc?.(this.doc);
-            }
-        })}
+        OnEmpty={undoBatch(() => 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);
@@ -384,7 +380,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     ContainingCollectionView={undefined}
                     addDocument={returnFalse}
                     moveDocument={this.props.moveDocument}
-                    removeDocument={returnFalse}
+                    removeDocument={this.props.removeDoc}
                     parentActive={this.props.active}
                     whenActiveChanged={emptyFunction}
                     addDocTab={this.props.addDocTab}
@@ -520,6 +516,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     render() {
         TraceMobx();
+        if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
         const sorting = this.doc[`${this.fieldKey}-sortAscending`];
         if (this.showTitleEdit) { // find  containing CollectionTreeView and set our maximum width so  the containing tree view won't have to scroll
             let par: any = this._header?.current;
@@ -532,7 +529,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         } else this._editMaxWidth = "";
-        return this.doc.treeViewHideTitle && this.props.firstLevel ? this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
+        return this.doc.treeViewHideTitle || (this.props.treeView.props.Document.treeViewOutlineMode && this.doc.type !== DocumentType.COL) ? this.renderContent :
             <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
                 <li className="collection-child">
                     <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
@@ -892,12 +889,13 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                         maxHeight={72}
                         height={"auto"}
                         GetValue={() => StrCast(this.dataDoc.title)}
-                        SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
-                        OnFillDown={undoBatch((value: string) => {
-                            Doc.SetInPlace(this.dataDoc, "title", 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(doc, "editTitle", "*", false);
-                            this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
+                        SetValue={undoBatch((value: string) => {
+                            if (this.props.Document.treeViewOutlineMode) {
+                                const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+                                Doc.SetInPlace(doc, "editTitle", "*", false);
+                                this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
+                            }
+                            return Doc.SetInPlace(this.dataDoc, "title", value, false);
                         })} />}
                     {this.doc.allowClear ? this.renderClearButton : (null)}
                     <ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul>
-- 
cgit v1.2.3-70-g09d2


From 2f7cdda3c766e900ae8f2ae5c6853cfb2d352d7a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Sep 2020 22:54:54 -0400
Subject: small fix to allow expanded images in treeviews to be moved and
 treeView expanded items to be right-clicked for a context menu

---
 src/client/views/collections/CollectionTreeView.tsx | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 08906218a..d1a2fd33e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -339,7 +339,7 @@ class TreeView extends React.Component<TreeViewProps> {
             const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
             return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
-                this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
+                !this.props.treeView.Document.treeViewOutlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
                 e.stopPropagation();
             }}>
                 {!docs ? (null) :
@@ -379,10 +379,10 @@ class TreeView extends React.Component<TreeViewProps> {
                     ContainingCollectionDoc={this.props.containingCollection}
                     ContainingCollectionView={undefined}
                     addDocument={returnFalse}
-                    moveDocument={this.props.moveDocument}
+                    moveDocument={this.move}
                     removeDocument={this.props.removeDoc}
                     parentActive={this.props.active}
-                    whenActiveChanged={emptyFunction}
+                    whenActiveChanged={this.props.whenActiveChanged}
                     addDocTab={this.props.addDocTab}
                     pinToPres={this.props.pinToPres}
                     bringToFront={returnFalse}
@@ -657,7 +657,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => {
-                return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
+                return add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
             };
             const childLayout = Doc.Layout(pair.layout);
             const rowHeight = () => {
-- 
cgit v1.2.3-70-g09d2


From 702242a07451fc89d176ffcd7cc881928b2bc23c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Sep 2020 17:28:08 -0400
Subject: changes to switch from 'pt' units to 'px' for fonts.  fixxes to
 presentations for treeview outlines.

---
 solr-8.3.1/bin/solr-8983.pid                       |   2 +-
 src/client/util/CurrentUserUtils.ts                |  17 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/util/SettingsManager.tsx                |   4 +-
 src/client/util/UndoManager.ts                     |   2 +
 src/client/views/MainView.tsx                      |   3 +-
 src/client/views/PropertiesView.tsx                |   2 +-
 src/client/views/animationtimeline/Timeline.tsx    |   4 +-
 src/client/views/collections/CollectionMenu.tsx    |  10 +-
 .../views/collections/CollectionTreeView.scss      |   3 +-
 .../views/collections/CollectionTreeView.tsx       | 295 ++++++++++++++-------
 src/client/views/nodes/DocumentView.tsx            |  13 +-
 src/client/views/nodes/PresBox.tsx                 |  28 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  15 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  76 +++---
 .../views/nodes/formattedText/RichTextMenu.tsx     |  36 +--
 .../views/nodes/formattedText/RichTextRules.ts     |   2 +-
 src/fields/Doc.ts                                  |   9 +-
 18 files changed, 314 insertions(+), 209 deletions(-)

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

diff --git a/solr-8.3.1/bin/solr-8983.pid b/solr-8.3.1/bin/solr-8983.pid
index 1f77d281d..cfdfb83dc 100644
--- a/solr-8.3.1/bin/solr-8983.pid
+++ b/solr-8.3.1/bin/solr-8983.pid
@@ -1 +1 @@
-1731
+1750
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 20799fa26..c2c9892be 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -208,7 +208,7 @@ export class CurrentUserUtils {
             details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" "));
 
             const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
-            const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
+            const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" };
             const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true };
 
             const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
@@ -240,7 +240,7 @@ export class CurrentUserUtils {
             });
 
             const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
-            const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" };
+            const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" };
             const detailView = Docs.Create.StackingDocument([carousel], { ...shared, ...detailViewOpts, system: true });
             detailView.isTemplateDoc = makeTemplate(detailView);
 
@@ -419,7 +419,12 @@ export class CurrentUserUtils {
             ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptySlide === undefined) {
-            doc.emptySlide = Docs.Create.TreeDocument([], { title: "slide", treeViewOutlineMode: true, _backgroundColor: "transparent", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            const textDoc = Docs.Create.TextDocument("Slide", { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewOutlineMode: true, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, _backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            Doc.GetProto(textDoc).layout = CollectionView.LayoutString("data");
+            Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
+            Doc.GetProto(textDoc).data = new List<Doc>([]);
+            FormattedTextBox.SelectOnLoad = textDoc[Id];
+            doc.emptySlide = textDoc;
         }
         if (doc.emptyComparison === undefined) {
             doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
@@ -641,13 +646,13 @@ export class CurrentUserUtils {
     // Sets up the title of the button
     static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, {
         ...opts,
-        dropAction: undefined, title: buttonTitle, _fontSize: "37pt", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", system: true
+        dropAction: undefined, title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", system: true
     }) as any as Doc
 
     // Sets up the description of the button
     static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, {
         ...opts,
-        dropAction: undefined, title: "info", _fontSize: "25pt", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true
+        dropAction: undefined, title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true
     }) as any as Doc
 
 
@@ -953,7 +958,7 @@ export class CurrentUserUtils {
         doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
         doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "");
         doc.activeDash = StrCast(doc.activeDash, "0");
-        doc.fontSize = StrCast(doc.fontSize, "12pt");
+        doc.fontSize = StrCast(doc.fontSize, "12px");
         doc.fontFamily = StrCast(doc.fontFamily, "Arial");
         doc.fontColor = StrCast(doc.fontColor, "black");
         doc.fontHighlight = StrCast(doc.fontHighlight, "");
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9797f5488..367155c90 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -329,7 +329,7 @@ export namespace DragManager {
             dragLabel = document.createElement("div");
             dragLabel.className = "dragManager-dragLabel";
             dragLabel.style.zIndex = "100001";
-            dragLabel.style.fontSize = "10pt";
+            dragLabel.style.fontSize = "10px";
             dragLabel.style.position = "absolute";
             // dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar
             dragDiv.appendChild(dragLabel);
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 0a1890ca4..c9605c620 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -81,7 +81,7 @@ export class SettingsManager extends React.Component<{}> {
         </div>;
 
         const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
-        const fontSizes = ["7pt", "8pt", "9pt", "10pt", "12pt", "14pt", "16pt", "18pt", "20pt", "24pt", "32pt", "48pt", "72pt"];
+        const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
 
         return <div className="preferences-content">
             <div className="preferences-color">
@@ -93,7 +93,7 @@ export class SettingsManager extends React.Component<{}> {
                 <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
                     {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
                 </select>
-                <select className="size-select" style={{ marginRight: "10px" }} onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7pt")}>
+                <select className="size-select" style={{ marginRight: "10px" }} onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
                     {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
                 </select>
                 <div>
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index c7b7bb215..0f7ad6d0a 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -128,6 +128,7 @@ export namespace UndoManager {
     }
 
     export function StartBatch(batchName: string): Batch {
+        // console.log("Start " + batchCounter + " " + batchName);
         batchCounter++;
         if (batchCounter > 0 && currentBatch === undefined) {
             currentBatch = [];
@@ -137,6 +138,7 @@ export namespace UndoManager {
 
     const EndBatch = action((cancel: boolean = false) => {
         batchCounter--;
+        // console.log("End " + batchCounter);
         if (batchCounter === 0 && currentBatch?.length) {
             if (!cancel) {
                 undoStack.push(currentBatch);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 379577a25..371ee3960 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,6 +1,7 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
 import * as fa from '@fortawesome/free-solid-svg-icons';
+import * as far from '@fortawesome/free-regular-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -141,7 +142,7 @@ export class MainView extends React.Component {
             fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
             fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
             fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
-            fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle,
+            fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle,
             fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
             fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
             fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5a9402d70..53261d8de 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -45,8 +45,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
     @computed get selectedDocumentView() {
+        if (PresBox.Instance?._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
         if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
-        if (PresBox.Instance && PresBox.Instance._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
         return undefined;
     }
     @computed get isPres(): boolean {
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 1131ec874..9269e6f33 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -382,10 +382,10 @@ export class Timeline extends React.Component<FieldViewProps> {
             const ttime = `Total: ${this.toReadTime(this._time)}`;
             return (
                 <div style={{ flexDirection: "column" }}>
-                    <div className="animation-text" style={{ fontSize: "10pt", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
                         {ctime}
                     </div>
-                    <div className="animation-text" style={{ fontSize: "10pt", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
                         {ttime}
                     </div>
                 </div>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index cb989d348..f3ed89c78 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -251,7 +251,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     }
 
     @computed get subChrome() {
-        switch (this.props.type) {
+        switch (this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type) { // bcz: ARgh!  hack to get menu for tree view outline items
             default: return this.otherSubChrome;
             case CollectionViewType.Invalid:
             case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />);
@@ -528,16 +528,14 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
     }
 
     @computed get selectedDocumentView() {
-        if (SelectionManager.SelectedDocuments().length) {
-            return SelectionManager.SelectedDocuments()[0];
-        } else { return undefined; }
+        return SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
     }
     @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
     @computed get isText() {
         if (this.selectedDoc) {
             const layoutField = Doc.LayoutField(this.selectedDoc);
-            return StrCast(layoutField).includes("FormattedText") ||
-                (layoutField instanceof Doc && StrCast(layoutField.layout).includes("FormattedText"));
+            const layoutStr = this.selectedDocumentView?.props.LayoutTemplateString || StrCast(layoutField);
+            return layoutStr.includes("FormattedText") || StrCast((layoutField as Doc)?.layout).includes("FormattedText");
         }
         else return false;
     }
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 3f437b799..09a46c30f 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -34,8 +34,8 @@
         position: relative;
         width: 15px;
         color: $intermediate-color;
-        margin-top: 3px;
         transform: scale(0.5);
+        display: inline-block;
     }
 
     .bullet {
@@ -127,6 +127,7 @@
 .treeViewItem-header {
     border: transparent 1px solid;
     display: flex;
+    align-items: center;
 
     .editableView-container-editing-oneLine {
         min-width: 15px;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d1a2fd33e..7765834f1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -4,7 +4,6 @@ import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
-import { PrefetchProxy } from '../../../fields/Proxy';
 import { Document, listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -22,16 +21,17 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from "../EditableView";
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import { DocumentView } from '../nodes/DocumentView';
-import { ImageBox } from '../nodes/ImageBox';
 import { KeyValueBox } from '../nodes/KeyValueBox';
 import { Templates } from '../Templates';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
-import { CollectionViewType } from './CollectionView';
+import { CollectionViewType, CollectionView } from './CollectionView';
 import React = require("react");
-import { makeTemplate } from '../../util/DropConverter';
 import { TraceMobx } from '../../../fields/util';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextField } from '../../../fields/RichTextField';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 
 export interface TreeViewProps {
     document: Doc;
@@ -90,13 +90,14 @@ class TreeView extends React.Component<TreeViewProps> {
     get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
     get displayName() { return "TreeView(" + this.doc.title + ")"; }  // this makes mobx trace() statements more descriptive
     get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
-    get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+    get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
     get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
     @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
     set treeViewOpen(c: boolean) {
         if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
         else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
     }
+    @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; }
     @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
     @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
@@ -122,12 +123,10 @@ class TreeView extends React.Component<TreeViewProps> {
         return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
     }
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
+        this.props.treeView.props.select(false);
         return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
     }
-    @undoBatch @action removeDoc = (doc: Doc | Doc[]) => {
-        return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
-            flg && Doc.RemoveDocFromList(this.doc, Doc.LayoutFieldKey(this.doc), doc), true);
-    }
+    @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
 
     constructor(props: any) {
         super(props);
@@ -144,10 +143,15 @@ class TreeView extends React.Component<TreeViewProps> {
         ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
     }
 
+    componentWillUnmount() {
+        document.removeEventListener("pointermove", this.onDragMove, true);
+    }
+
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active(true) && Doc.BrushDoc(this.dataDoc);
         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "treeViewItem-header";
+            document.removeEventListener("pointermove", this.onDragMove, true);
             document.addEventListener("pointermove", this.onDragMove, true);
         }
     }
@@ -171,6 +175,24 @@ class TreeView extends React.Component<TreeViewProps> {
         e.stopPropagation();
     }
 
+    public static makeTextBullet() {
+        const bullet = Docs.Create.TextDocument("", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
+        Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
+        Doc.GetProto(bullet).data = new List<Doc>([]);
+        Doc.SetInPlace(bullet, "editTitle", "*", false);
+        FormattedTextBox.SelectOnLoad = bullet[Id];
+        return bullet;
+    }
+
+    makeTextCollection = () => {
+        Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+        const bullet = TreeView.makeTextBullet();
+        const added = this.props.addDocument(bullet);
+        bullet.context = this.props.treeView.Document;
+        return added;
+    }
+
     editableView = (key: string, style?: string) => (<EditableView
         oneLine={true}
         display={"inline-block"}
@@ -182,27 +204,14 @@ class TreeView extends React.Component<TreeViewProps> {
         fontSize={12}
         GetValue={() => StrCast(this.doc[key])}
         SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
-            if (this.props.treeView.doc.treeViewOutlineMode && enterKey) {
+            if (this.outlineMode && 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;
+                this.makeTextCollection();
             } 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]) });
-            Doc.SetInPlace(this.doc, "editTitle", undefined, false);
-            Doc.SetInPlace(doc, "editTitle", "*", false);
-            const added = this.props.addDocument(doc);
-            doc.context = this.props.treeView.Document;
-            return added;
-        })}
         onClick={() => {
             SelectionManager.DeselectAll();
             Doc.UserDoc().activeSelection = new List([this.doc]);
@@ -240,8 +249,13 @@ class TreeView extends React.Component<TreeViewProps> {
             const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
             let addDoc = parentAddDoc;
             if (inside) {
+                const localAdd = (doc: Doc) => {
+                    const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
+                    added && (doc.context = this.doc.context);
+                    return added;
+                }
                 addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
-                    (flg: boolean, doc) => flg && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc), true) || parentAddDoc(doc);
+                    (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
             }
             const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument;
             return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false);
@@ -290,8 +304,12 @@ class TreeView extends React.Component<TreeViewProps> {
 
             if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
                 const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
-                const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
-                    (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
+                const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+                    const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+                    added && (doc.context = this.doc.context);
+                    return added;
+                }
+                const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
                     this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
@@ -328,18 +346,23 @@ class TreeView extends React.Component<TreeViewProps> {
 
     rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
     rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
 
     @computed get renderContent() {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
         if (["links", "annotations", this.fieldKey].includes(expandKey)) {
             const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
-            const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) =>
-                (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
+            const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+                const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+                added && (doc.context = this.doc.context);
+                return added;
+            }
+            const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
             const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
             return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
-                !this.props.treeView.Document.treeViewOutlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
+                !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
                 e.stopPropagation();
             }}>
                 {!docs ? (null) :
@@ -416,7 +439,7 @@ class TreeView extends React.Component<TreeViewProps> {
             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"} />}
+            {(this.doc.text as RichTextField)?.Text ? <FontAwesomeIcon icon={this.childDocs?.length && !this.treeViewOpen ? ["fas", "circle"] : ["far", "circle"]} /> : (null)}
         </div>;
     }
     @computed get renderBullet() {
@@ -442,7 +465,7 @@ class TreeView extends React.Component<TreeViewProps> {
         return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
     }
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
-    onChildDoubleClick = () => (!this.props.treeView.props.Document.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+    onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -456,11 +479,11 @@ class TreeView extends React.Component<TreeViewProps> {
                     onPointerDown={action(() => {
                         if (this.treeViewOpen) {
                             this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
-                                this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
+                                this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") :
                                     this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
                                         this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
                                             (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
-                                                this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+                                                this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields");
                         }
                         this.treeViewOpen = true;
                     })}>
@@ -488,7 +511,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 PanelWidth={this.truncateTitleWidth}
                 PanelHeight={returnZero}
                 contextMenuItems={this.contextMenuItems}
-                opacity={this.props.treeView.props.Document.treeViewOutlineMode ? undefined : returnOne}
+                opacity={this.outlineMode ? undefined : returnOne}
                 renderDepth={1}
                 focus={returnTrue}
                 parentActive={returnTrue}
@@ -529,7 +552,67 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         } else this._editMaxWidth = "";
-        return this.doc.treeViewHideTitle || (this.props.treeView.props.Document.treeViewOutlineMode && this.doc.type !== DocumentType.COL) ? this.renderContent :
+        return this.doc.treeViewHideTitle || (this.outlineMode) ?
+            !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
+                <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+                    onKeyDown={e => {
+                        e.stopPropagation();
+                        e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
+                        e.key === "Tab" && UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
+                        e.key === "Enter" && UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
+                        e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+                    }}
+                >
+                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
+                        if (this.props.active(true)) {
+                            e.stopPropagation();
+                            e.preventDefault();
+                        }
+                    }}
+                        onPointerDown={e => {
+                            if (this.props.active(true)) {
+                                e.stopPropagation();
+                                e.preventDefault();
+                            }
+                        }}
+                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                        {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
+                        <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
+                            <ContentFittingDocumentView
+                                Document={this.doc}
+                                DataDoc={undefined}
+                                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+                                LibraryPath={emptyPath}
+                                renderDepth={this.props.renderDepth + 1}
+                                rootSelected={returnTrue}
+                                treeViewDoc={undefined}
+                                backgroundColor={this.props.backgroundColor}
+                                fitToBox={this.boundsOfCollectionDocument !== undefined}
+                                PanelWidth={this.rtfWidth}
+                                PanelHeight={this.rtfOutlineHeight}
+                                focus={returnFalse}
+                                ScreenToLocalTransform={this.docTransform}
+                                docFilters={returnEmptyFilter}
+                                searchFilterDocs={returnEmptyDoclist}
+                                ContainingCollectionDoc={this.props.containingCollection}
+                                ContainingCollectionView={undefined}
+                                addDocument={this.props.addDocument}
+                                moveDocument={this.move}
+                                removeDocument={this.props.removeDoc}
+                                parentActive={this.props.active}
+                                whenActiveChanged={this.props.whenActiveChanged}
+                                addDocTab={this.props.addDocTab}
+                                pinToPres={this.props.pinToPres}
+                                bringToFront={returnFalse}
+                                ContentScaling={returnOne}
+                            />
+                        </div>
+                    </div>
+
+                    <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+                        {!this.treeViewOpen ? (null) : this.renderContent}
+                    </div>
+                </div> :
             <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
                 <li className="collection-child">
                     <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
@@ -546,11 +629,11 @@ class TreeView extends React.Component<TreeViewProps> {
                             }
                         }}
                         onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        {this.props.treeView.props.Document.treeViewOutlineMode ? this.renderOutlineBullet : this.renderBullet}
+                        {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
                         {this.renderTitle}
                     </div>
-                    <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 className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+                        {!this.treeViewOpen ? (null) : this.renderContent}
                     </div>
                 </li>
             </div>;
@@ -639,9 +722,10 @@ class TreeView extends React.Component<TreeViewProps> {
                     const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
                     const fieldKey = fieldKeysub.split("\'")[1];
                     if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
+                        remove(child);
+                        FormattedTextBox.SelectOnLoad = child[Id];
                         Doc.AddDocToList(docs[i - 1], fieldKey, child);
                         docs[i - 1].treeViewOpen = true;
-                        remove(child);
                         child.context = treeView.Document;
                     }
                 }
@@ -650,9 +734,10 @@ class TreeView extends React.Component<TreeViewProps> {
                 if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
                     const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
                     const fieldKey = fieldKeysub.split("\'")[1];
+                    remove(child);
+                    FormattedTextBox.SelectOnLoad = child[Id];
                     Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
                     parentCollectionDoc.treeViewOpen = true;
-                    remove(child);
                     child.context = treeView.Document;
                 }
             };
@@ -713,6 +798,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     private _mainEle?: HTMLDivElement;
 
     public _uniqueId = Utils.GenerateGuid();
+    _isChildActive = false;
     @computed get doc() { return this.props.Document; }
     @computed get dataDoc() { return this.props.DataDoc || this.doc; }
 
@@ -743,6 +829,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const targetDataDoc = this.doc[DataSym];
         const value = DocListCast(targetDataDoc[this.props.fieldKey]);
         const result = value.filter(v => !docs.includes(v));
+        SelectionManager.DeselectAll();
         if (result.length !== value.length) {
             targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
             return true;
@@ -785,45 +872,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
         }
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({
-            description: "Buxton Layout", icon: "eye", event: () => {
-                const { ImageDocument, PdfDocument } = Docs.Create;
-                const { Document } = this.props;
-                const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
-                const detailView = Cast(Cast(Doc.UserDoc()["template-button-detail"], Doc, null)?.dragFactory, Doc, null);
-                const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
-                heroView.proto!.layout = ImageBox.LayoutString("hero");
-                heroView._showTitle = "title";
-                heroView._showTitleHover = "titlehover";
-
-                const fallback = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 });  // replace with desired double click target
-                let pdfContent: string;
-                this.childDocs?.map(d => {
-                    DocListCast(d.data).map((img, i) => {
-                        const caption = (d.captions as any)[i];
-                        if (caption) {
-                            Doc.GetProto(img).caption = caption;
-                            Doc.GetProto(img).doubleClickView = (pdfContent = StrCast(img.additionalMedia_pdfs)) ? PdfDocument(pdfContent, { title: pdfContent }) : fallback;
-                        }
-                    });
-                    Doc.GetProto(d).type = "buxton";
-                    Doc.GetProto(d).proto = heroView; // all devices "are" heroViews that share the same layout & defaults. Seems better than making them all be independent and copy a layout string  // .layout = ImageBox.LayoutString("hero");
-                });
-
-                const iconBuxtonView = ImageDocument(fallbackImg, { title: "hero", _width: 60, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") });
-                iconBuxtonView.isTemplateDoc = makeTemplate(iconBuxtonView, true, "icon_buxton");
-                Doc.UserDoc()["template-icon-view-buxton"] = new PrefetchProxy(iconBuxtonView);
-                const tempIcons = Doc.GetProto(Cast(Doc.UserDoc()["template-icons"], Doc, null));
-                Doc.AddDocToList(tempIcons, "data", iconBuxtonView);
-
-                Document.childLayoutTemplate = heroView;
-                Document.childClickedOpenTemplateView = new PrefetchProxy(detailView);
-                Document._viewType = CollectionViewType.Time;
-                Document.forceActive = true;
-                Document._pivotField = "company";
-                Document.childDropAction = "alias";
-            }
-        });
         const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({
@@ -846,13 +894,74 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         </div >;
     }
 
-    onChildClick = () => {
-        return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
+    @undoBatch
+    makeTextCollection = action((childDocs: Doc[]) => {
+        Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+        const bullet = TreeView.makeTextBullet();
+        bullet.context = this.doc;
+        this.addDoc(bullet, childDocs.length ? childDocs[0] : undefined, true);
+        setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+    })
+
+    editableTitle(childDocs: Doc[]) {
+        return <EditableView
+            contents={this.dataDoc.title}
+            editing={false}
+            display={"block"}
+            maxHeight={72}
+            height={"auto"}
+            GetValue={() => StrCast(this.dataDoc.title)}
+            SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+                if (this.props.Document.treeViewOutlineMode && enter) {
+                    this.makeTextCollection(childDocs)
+                }
+                return Doc.SetInPlace(this.dataDoc, "title", value, false);
+            })} />;
     }
-    _isChildActive = false;
-    whenActiveChanged = (isActive: boolean) => {
-        this.props.whenActiveChanged(this._isChildActive = isActive);
+
+
+    rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.PanelWidth() - 20);
+    rtfOutlineHeight = () => Math.min(this.layoutDoc?.[HeightSym](), (StrCast(this.layoutDoc?._fontSize) ? Number(StrCast(this.layoutDoc?._fontSize, "32px").replace("px", "")) : NumCast(this.layoutDoc?._fontSize)) * 2);
+    titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20));
+    documentTitle = (childDocs: Doc[]) => {
+        return <div style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}
+            onKeyDown={e => {
+                e.stopPropagation();
+                e.key === "Enter" && this.makeTextCollection(childDocs)
+            }}>
+            <ContentFittingDocumentView
+                Document={this.doc}
+                DataDoc={undefined}
+                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+                LibraryPath={emptyPath}
+                renderDepth={this.props.renderDepth + 1}
+                rootSelected={returnTrue}
+                treeViewDoc={undefined}
+                dontRegisterView={true}
+                backgroundColor={this.props.backgroundColor}
+                PanelWidth={this.rtfWidth}
+                PanelHeight={this.rtfOutlineHeight}
+                focus={returnFalse}
+                ScreenToLocalTransform={this.titleTransform}
+                docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.doc}
+                ContainingCollectionView={this.props.CollectionView}
+                addDocument={this.props.addDocument}
+                moveDocument={returnFalse}
+                removeDocument={returnFalse}
+                parentActive={this.props.active}
+                whenActiveChanged={this.props.whenActiveChanged}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />
+        </div>
     }
+
+    onChildClick = () => { return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); }
+    whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }
     active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
     render() {
         TraceMobx();
@@ -882,21 +991,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                     onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                     onDrop={this.onTreeDrop}
                     ref={this.createTreeDropTarget}>
-                    {hideTitle ? (null) : <EditableView
-                        contents={this.dataDoc.title}
-                        editing={false}
-                        display={"block"}
-                        maxHeight={72}
-                        height={"auto"}
-                        GetValue={() => StrCast(this.dataDoc.title)}
-                        SetValue={undoBatch((value: string) => {
-                            if (this.props.Document.treeViewOutlineMode) {
-                                const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
-                                Doc.SetInPlace(doc, "editTitle", "*", false);
-                                this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
-                            }
-                            return Doc.SetInPlace(this.dataDoc, "title", value, false);
-                        })} />}
+                    {hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(childDocs)}
                     {this.doc.allowClear ? this.renderClearButton : (null)}
                     <ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul>
                 </div >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7b7c3578b..4b740d3de 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,6 +40,7 @@ import { RadialMenu } from './RadialMenu';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import React = require("react");
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { RichTextField } from '../../../fields/RichTextField';
 
 export type DocFocusFunc = () => boolean;
 
@@ -269,6 +270,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     onKeyDown = (e: React.KeyboardEvent) => {
+        if (this.rootDoc._singleLine && ((e.key === "Backspace" && this.dataDoc.text && !(this.dataDoc.text as RichTextField)?.Text) || ["Tab", "Enter"].includes(e.key))) {
+            return;
+        }
         if (e.altKey && !(e.nativeEvent as any).StopPropagationForReal) {
             (e.nativeEvent as any).StopPropagationForReal = true; // e.stopPropagation() doesn't seem to work...
             e.stopPropagation();
@@ -624,6 +628,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
+        if (this.props.LayoutTemplateString) return;
         if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
             if ((e.target as any)?.closest?.("*.lm_content")) {
                 alert("You can't perform this move most likely because you don't have permission to modify the destination.");
@@ -901,7 +906,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
             {/* {this.allAnchors} */}
-            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) :
+            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || (!this.isSelected() && this.layoutDoc.hideLinkButton) || this.props.dontRegisterView ? (null) :
                 <DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />}
         </div>
         );
@@ -1045,7 +1050,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
         const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
         const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
-        const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+        const fullDegree = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
         const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
         const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ?
@@ -1059,8 +1064,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             id={this.props.Document[Id]}
             ref={this._mainCont} onKeyDown={this.onKeyDown}
             onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-            onPointerEnter={action(() => { Doc.BrushDoc(this.props.Document); })}
-            onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
+            onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+            onPointerLeave={action(e => {
                 let entered = false;
                 const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
                 for (let child: any = target; child; child = child?.parentElement) {
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 79f1b7ddc..b23dffe80 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1129,13 +1129,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
         }
         let doc = undefined;
-        const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24pt", });
-        const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16pt" });
-        const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20pt" });
-        const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24pt" });
-        const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14pt" });
-        const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14pt" });
-        const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14pt" });
+        const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24px", });
+        const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16px" });
+        const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20px" });
+        const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24px" });
+        const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14px" });
+        const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14px" });
+        const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14px" });
         switch (layout) {
             case 'blank':
                 doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
@@ -1227,8 +1227,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         let type: string = '';
+        const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;// bcz: Argh .. .need a better way to identify a slide doc
         if (activeItem) {
-            switch (targetDoc.type) {
+            switch (effectiveType) {
                 case DocumentType.PDF: type = "PDF"; break;
                 case DocumentType.RTF: type = "Text node"; break;
                 case DocumentType.COL: type = "Collection"; break;
@@ -1248,8 +1249,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
 
 
     @computed get progressivizeDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
+        const activeItem = this.activeItem;
+        const targetDoc = this.targetDoc;
+        const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;
         if (activeItem && targetDoc) {
             const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
             const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
@@ -1258,7 +1260,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 ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: effectiveType === 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>
@@ -1274,11 +1276,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                                 </div>
                             </div>
                             {this.viewedColorPicker}
-                            {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
+                            {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (effectiveType === DocumentType.COL && targetDoc._viewType === 'freeform') || effectiveType === DocumentType.IMG ? "inline-flex" : "none" }}>
                                 <div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
                                 <div className="ribbon-button" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
                             </div> */}
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || effectiveType === DocumentType.PDF || effectiveType === DocumentType.WEB || effectiveType === DocumentType.RTF ? "inline-flex" : "none" }}>
                                 <div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
                                 <div className="ribbon-button" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
                             </div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 97d023673..3be02aa92 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -988,7 +988,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (documentId) {
             exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
         }
-        UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
+        exportState && UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
     }
 
     updateState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
@@ -1171,6 +1171,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     componentWillUnmount() {
+        this.endUndoTypingBatch();
         Object.values(this._disposers).forEach(disposer => disposer?.());
         this._editorView?.destroy();
     }
@@ -1417,7 +1418,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     _lastTimedMark: Mark | undefined = undefined;
-    onKeyPress = (e: React.KeyboardEvent) => {
+    onKeyDown = (e: React.KeyboardEvent) => {
+        // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container)
+        if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) {
+            return;
+        }
         if (e.altKey) {
             e.preventDefault();
             return;
@@ -1502,8 +1507,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
             setTimeout(() => FormattedTextBoxComment.Hide(), 0);
         }
-        const selPad = this.props.isSelected() ? -10 : 0;
-        const selclass = this.props.isSelected() ? "-selected" : "";
+        const selPad = this.props.isSelected() && !this.layoutDoc._singleLine ? -10 : 0;
+        const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : "";
         return (
             <div className={"formattedTextBox-cont"}
                 style={{
@@ -1526,7 +1531,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
                     }}
                     onContextMenu={this.specificContextMenu}
-                    onKeyDown={this.onKeyPress}
+                    onKeyDown={this.onKeyDown}
                     onFocus={this.onFocused}
                     onClick={this.onClick}
                     onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c6bacc1a8..674b63e77 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,11 +7,13 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
 import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym } from "../../../../fields/Doc";
+import { Doc, DataSym, DocListCast } from "../../../../fields/Doc";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { Id } from "../../../../fields/FieldSymbols";
 import { Docs } from "../../../documents/Documents";
 import { Utils } from "../../../../Utils";
+import { listSpec } from "../../../../fields/Schema";
+import { List } from "../../../../fields/List";
 
 const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
 
@@ -45,6 +47,29 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         keys[key] = cmd;
     }
 
+    /// bcz; Argh!!  replace with an onEnter func that conditionally handles Enter 
+    const addTextBox = (below: boolean, force?: boolean) => {
+        if (props.LayoutTemplateString) return true;
+        const layoutDoc = props.Document;
+        const originalDoc = layoutDoc.rootDocument || layoutDoc;
+        if (force || props.Document._singleLine) {
+            const layoutKey = StrCast(originalDoc.layoutKey);
+            const newDoc = Doc.MakeCopy(originalDoc, true);
+            const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)];
+            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+            if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
+            else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
+            if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
+                newDoc[layoutKey] = originalDoc[layoutKey];
+            }
+            Doc.GetProto(newDoc).text = undefined;
+            FormattedTextBox.SelectOnLoad = newDoc[Id];
+            props.addDocument(newDoc);
+            return true;
+        }
+        return false;
+    }
+
     //History commands
     bind("Mod-z", undo);
     bind("Shift-Mod-z", redo);
@@ -66,6 +91,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     bind("Ctrl-i", wrapInList(schema.nodes.ordered_list));
 
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+        /// bcz; Argh!!  replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
+        if (props.Document._singleLine) {
+            if (!props.LayoutTemplateString) return addTextBox(false, true);
+            return true;
+        }
         const ref = state.selection;
         const range = ref.$from.blockRange(ref.$to);
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -89,6 +119,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     });
 
     bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+        /// bcz; Argh!!  replace with a onShiftTab prop conditionally handles Tab);
+        if (props.Document._singleLine) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
 
         if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
@@ -136,47 +168,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return tx;
     };
 
-    const addTextOnRight = (force: boolean) => {
-        const layoutDoc = props.Document;
-        const originalDoc = layoutDoc.rootDocument || layoutDoc;
-        if (force || props.Document._singleLine) {
-            const layoutKey = StrCast(originalDoc.layoutKey);
-            const newDoc = Doc.MakeCopy(originalDoc, true);
-            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
-            newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
-            if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = originalDoc[layoutKey];
-            }
-            Doc.GetProto(newDoc).text = undefined;
-            FormattedTextBox.SelectOnLoad = newDoc[Id];
-            props.addDocument(newDoc);
-            return true;
-        }
-        return false;
-    };
-
     //Command to create a text document to the right of the selected textbox
-    bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
-        return addTextOnRight(true);
-    });
+    bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => addTextBox(false, true));
 
     //Command to create a text document to the bottom of the selected textbox
-    bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        const layoutDoc = props.Document;
-        const originalDoc = layoutDoc.rootDocument || layoutDoc;
-        if (originalDoc instanceof Doc) {
-            const layoutKey = StrCast(originalDoc.layoutKey);
-            const newDoc = Doc.MakeCopy(originalDoc, true);
-            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
-            newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
-            if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = originalDoc[layoutKey];
-            }
-            Doc.GetProto(newDoc).text = undefined;
-            FormattedTextBox.SelectOnLoad = newDoc[Id];
-            props.addDocument(newDoc);
-        }
-    });
+    bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => addTextBox(true, true));
 
     // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
     bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
@@ -199,7 +195,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
     //command to break line
     bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
-        if (addTextOnRight(false)) return true;
+        if (addTextBox(true, false)) return true;
         const trange = state.selection.$from.blockRange(state.selection.$to);
         const path = (state.selection.$from as any).path;
         const depth = trange ? liftTarget(trange) : undefined;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 307238ea1..da515aa5a 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -78,21 +78,21 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         runInAction(() => this.Pinned = true);
 
         this.fontSizeOptions = [
-            { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48pt", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72pt", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize },
+            { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize },
             { mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true },
-            { mark: null, title: "", label: "13pt", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
+            { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
         ];
 
         this.fontFamilyOptions = [
@@ -177,7 +177,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this.activeListType = this.getActiveListStyle();
         this.activeAlignment = this.getActiveAlignment();
         this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
-        this.activeFontSize = !activeSizes.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
+        this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
         this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "...";
         this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
 
@@ -255,7 +255,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 marks.forEach(m => {
                     m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
                     m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
-                    m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
+                    m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px");
                     m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
                 });
             }
@@ -384,7 +384,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                     if (!self.TextView.props.isSelected(true)) {
                         switch (mark.type) {
                             case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break;
-                            case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "pt"); break;
+                            case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
                         }
                     }
                     else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown");
@@ -425,7 +425,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
     changeFontSize = (mark: Mark, view: EditorView) => {
         if ((this.view?.state.selection.$from.pos || 0) < 2) {
-            this.TextView.layoutDoc._fontSize = mark.attrs.fontSize;
+            this.TextView.layoutDoc._fontSize = mark.attrs.fontSize === Number(mark.attrs.fontSize) ? `${mark.attrs.fontSize}pt` : mark.attrs.fontSize;
         }
         const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
         this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 5c0505909..02f9c6268 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -90,7 +90,7 @@ export class RichTextRules {
                     textDoc.inlineTextCount = numInlines + 1;
                     const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to
                     const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
-                    const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" });
+                    const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" });
                     textDocInline.title = inlineFieldKey; // give the annotation its own title
                     textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
                     textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 08d949b5e..be62249c6 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -183,7 +183,7 @@ export class Doc extends RefField {
         this.___fields = value;
         for (const key in value) {
             const field = value[key];
-            field && (this.__fieldKeys[key] = true);
+            (field !== undefined) && (this.__fieldKeys[key] = true);
             if (!(field instanceof ObjectField)) continue;
             field[Parent] = this[Self];
             field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
@@ -875,12 +875,7 @@ export namespace Doc {
     }
 
     export function isBrushedHighlightedDegree(doc: Doc) {
-        if (Doc.IsHighlighted(doc)) {
-            return 6;
-        }
-        else {
-            return Doc.IsBrushedDegree(doc);
-        }
+        return Doc.IsHighlighted(doc) ? 6 : Doc.IsBrushedDegree(doc);
     }
 
     export class DocBrush {
-- 
cgit v1.2.3-70-g09d2


From b81ad33b746387dc785580b719866f479fbd9b30 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Sep 2020 19:37:06 -0400
Subject: fixed creating text boxes with pre-set text to select that text --
 used in treeView outlines

---
 src/client/views/collections/CollectionTreeView.tsx             | 4 ++--
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 4 +++-
 src/client/views/nodes/formattedText/FormattedTextBox.tsx       | 8 ++++++--
 3 files changed, 11 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7765834f1..f92fbc9e3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -176,7 +176,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -937,7 +937,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 renderDepth={this.props.renderDepth + 1}
                 rootSelected={returnTrue}
                 treeViewDoc={undefined}
-                dontRegisterView={true}
+                //dontRegisterView={true}
                 backgroundColor={this.props.backgroundColor}
                 PanelWidth={this.rtfWidth}
                 PanelHeight={this.rtfOutlineHeight}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ebad3bf45..226d99c5b 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -23,6 +23,7 @@ import { CollectionView, CollectionViewType } from "../CollectionView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
+import { Id } from "../../../../fields/FieldSymbols";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -131,8 +132,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
                 slide.x = x;
                 slide.y = y;
+                FormattedTextBox.SelectOnLoad = slide[Id];
                 this.props.addDocument(slide);
-                setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false));
+                //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/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3be02aa92..a5e86d672 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1147,8 +1147,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (selectOnLoad && !this.props.dontRegisterView) {
             FormattedTextBox.SelectOnLoad = "";
             this.props.select(false);
-            FormattedTextBox.SelectOnLoadChar && this._editorView!.dispatch(this._editorView!.state.tr.insertText(FormattedTextBox.SelectOnLoadChar));
-            FormattedTextBox.SelectOnLoadChar = "";
+            if (FormattedTextBox.SelectOnLoadChar) {
+                FormattedTextBox.SelectOnLoadChar && this._editorView!.dispatch(this._editorView!.state.tr.insertText(FormattedTextBox.SelectOnLoadChar));
+                FormattedTextBox.SelectOnLoadChar = "";
+            } else if (curText?.Text) {
+                selectAll(this._editorView!.state, this._editorView?.dispatch);
+            }
 
         }
         selectOnLoad && this._editorView!.focus();
-- 
cgit v1.2.3-70-g09d2


From bfd2769ccce8db190913d9a9fcb6f8f3cac75935 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Sep 2020 19:51:08 -0400
Subject: fixed warnings and fixed focus on slide

---
 src/client/util/DocumentManager.ts                     |  6 ++++--
 src/client/views/collections/CollectionTreeView.tsx    | 18 +++++++++---------
 src/client/views/nodes/PresBox.tsx                     |  4 ++--
 .../nodes/formattedText/ProsemirrorExampleTransfer.ts  |  2 +-
 4 files changed, 16 insertions(+), 14 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index f41c5e4cb..9bf10d167 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,6 +10,7 @@ import { Scripting } from './Scripting';
 import { SelectionManager } from './SelectionManager';
 import { DocumentType } from '../documents/DocumentTypes';
 import { TraceMobx } from '../../fields/util';
+import { returnFalse } from '../../Utils';
 
 export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
 
@@ -86,7 +87,8 @@ export class DocumentManager {
     }
 
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
-        return this.getDocumentViews(toFind)?.find(view => view.props.Document !== originatingDoc);
+        const views = this.getDocumentViews(toFind).filter(view => view.props.Document !== originatingDoc);
+        return views?.find(view => view.props.focus !== returnFalse) || (view.length ? views[0] : undefined);
     }
     public getDocumentViews(toFind: Doc): DocumentView[] {
         const toReturn: DocumentView[] = [];
@@ -200,7 +202,7 @@ export class DocumentManager {
                             } else {
                                 setTimeout(() => findView(delay + 250), 250);
                             }
-                        }
+                        };
                         findView(0);
                     }
                 } else {  // there's no context view so we need to create one first and try again
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f92fbc9e3..ba424be37 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -253,7 +253,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
                     added && (doc.context = this.doc.context);
                     return added;
-                }
+                };
                 addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
                     (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
             }
@@ -308,7 +308,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                     added && (doc.context = this.doc.context);
                     return added;
-                }
+                };
                 const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
                     this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
@@ -357,7 +357,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
                 added && (doc.context = this.doc.context);
                 return added;
-            }
+            };
             const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
             const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
@@ -901,7 +901,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         bullet.context = this.doc;
         this.addDoc(bullet, childDocs.length ? childDocs[0] : undefined, true);
         setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
-    })
+    });
 
     editableTitle(childDocs: Doc[]) {
         return <EditableView
@@ -913,7 +913,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             GetValue={() => StrCast(this.dataDoc.title)}
             SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
                 if (this.props.Document.treeViewOutlineMode && enter) {
-                    this.makeTextCollection(childDocs)
+                    this.makeTextCollection(childDocs);
                 }
                 return Doc.SetInPlace(this.dataDoc, "title", value, false);
             })} />;
@@ -927,7 +927,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         return <div style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}
             onKeyDown={e => {
                 e.stopPropagation();
-                e.key === "Enter" && this.makeTextCollection(childDocs)
+                e.key === "Enter" && this.makeTextCollection(childDocs);
             }}>
             <ContentFittingDocumentView
                 Document={this.doc}
@@ -957,11 +957,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 bringToFront={returnFalse}
                 ContentScaling={returnOne}
             />
-        </div>
+        </div>;
     }
 
-    onChildClick = () => { return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); }
-    whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }
+    onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
+    whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); };
     active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
     render() {
         TraceMobx();
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index b23dffe80..b0563c373 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1449,8 +1449,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             let count = 0;
             const setupProgressivize = (doc: Doc) => {
                 CollectionFreeFormDocumentView.setupKeyframes([doc], count++, true);
-                targetDoc.treeViewOutlineMode && DocListCast(doc[Doc.LayoutFieldKey(doc)]).forEach(d => setupProgressivize(d));
-            }
+                targetDoc.treeViewOutlineMode && DocListCast(doc[Doc.LayoutFieldKey(doc)]).forEach(setupProgressivize);
+            };
             setupProgressivize(targetDoc);
             targetDoc.lastFrame = count;
         } else {
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 674b63e77..9895940d1 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -68,7 +68,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
             return true;
         }
         return false;
-    }
+    };
 
     //History commands
     bind("Mod-z", undo);
-- 
cgit v1.2.3-70-g09d2


From ff229819aa111c828a9134c5db12cc74ae410bce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Sep 2020 20:15:05 -0400
Subject: added lightbox button to topbar.  fixed treeview editableTitle crash

---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/collections/CollectionMenu.tsx    | 24 +++++++--------
 .../views/collections/CollectionTreeView.tsx       |  4 +--
 src/client/views/collections/CollectionView.tsx    | 36 ++++++----------------
 4 files changed, 23 insertions(+), 43 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 371ee3960..7b0dd081e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -142,7 +142,7 @@ export class MainView extends React.Component {
             fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
             fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
             fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
-            fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle,
+            fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle,
             fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
             fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
             fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index f3ed89c78..5c0a3777f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -240,11 +240,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
         runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
     }
 
-    @action
-    toggleViewSpecs = (e: React.SyntheticEvent) => {
-        this.document._facetWidth = this.document._facetWidth ? 0 : 200;
-        e.stopPropagation();
-    }
 
     @action closeViewSpecs = () => {
         this.document._facetWidth = 0;
@@ -441,6 +436,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
         </Tooltip>;
     }
 
+    @computed get lightboxButton() {
+        const targetDoc = this.selectedDoc;
+        return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Lightbox of Images"}</div>} placement="top">
+            <button className="antimodeMenu-button" ref={this._dragRef} onPointerDown={action(() => targetDoc._isLightboxOpen = true)} onClick={this.onAlias}>
+                <FontAwesomeIcon className="documentdecorations-icon" icon="desktop" size="lg" />
+            </button>
+        </Tooltip>;
+    }
+
     @computed
     get pinWithViewButton() {
         const targetDoc = this.selectedDoc;
@@ -477,14 +481,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
                     <div className="collectionViewBaseChrome">
                         {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
                         {!this._buttonizableCommands ? (null) : this.templateChrome}
-                        {Doc.UserDoc().noviceMode ? (null) :
-                            <Tooltip title={<div className="dash-tooltip">filter documents to show</div>} placement="bottom">
-                                <div className="collectionViewBaseChrome-viewSpecs" style={{ display: "grid" }}>
-                                    <button className={"antimodeMenu-button"} onClick={this.toggleViewSpecs} >
-                                        <FontAwesomeIcon icon="filter" size="lg" />
-                                    </button>
-                                </div>
-                            </Tooltip>}
+
 
                         {this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? (null) :
                             <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
@@ -494,6 +491,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
                                     <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
                                 </button>
                             </Tooltip>}
+                        {this.notACollection ? (null) : this.lightboxButton}
                         {this.aliasButton}
                         {this.pinButton}
                         {this.pinWithViewButton}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ba424be37..52e3c86b8 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -903,8 +903,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
     });
 
-    editableTitle(childDocs: Doc[]) {
-        return <EditableView
+    editableTitle = (childDocs: Doc[]) => {
+        return !this.dataDoc ? (null) : <EditableView
             contents={this.dataDoc.title}
             editing={false}
             display={"block"}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7084aba40..c9f62db36 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,21 +1,17 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
 import Lightbox from 'react-image-lightbox-with-rotate';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
 import { DateField } from '../../../fields/DateField';
-import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit, AclSym, AclPrivate, AclAdmin } from '../../../fields/Doc';
+import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
-import { RichTextField } from '../../../fields/RichTextField';
-import { listSpec } from '../../../fields/Schema';
-import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { TraceMobx, GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from '../../../Utils';
+import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
+import { returnFalse, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -23,6 +19,7 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from '../ContextMenuItem';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { Touchable } from '../Touchable';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
@@ -37,13 +34,10 @@ import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultir
 import { CollectionPileView } from './CollectionPileView';
 import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
-import { CollectionStaffView } from './CollectionStaffView';
 import { SubCollectionViewProps } from './CollectionSubView';
 import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { table } from 'console';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -93,8 +87,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
 
     _isChildActive = false;   //TODO should this be observable?
-    get _isLightboxOpen() { return BoolCast(this.props.Document.isLightboxOpen); }
-    set _isLightboxOpen(value) { this.props.Document.isLightboxOpen = value; }
+    get _isLightboxOpen() { return BoolCast(this.props.Document._isLightboxOpen); }
+    set _isLightboxOpen(value) { this.props.Document._isLightboxOpen = value; }
     @observable private _curLightboxImg = 0;
     @observable private static _safeMode = false;
     public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
@@ -286,7 +280,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
         subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
         subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
-        addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
+        subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
 
         const existingVm = ContextMenu.Instance.findByDescription(category);
         const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
@@ -363,17 +357,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
     }
 
-    get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
-    set _facetWidth(value) { this.props.Document._facetWidth = value; }
-
     bodyPanelWidth = () => this.props.PanelWidth();
-    facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth));
-    onPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
-            this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
-            return false;
-        }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false);
-    }
 
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     @computed get childLayoutString() { return StrCast(this.props.Document.childLayoutString); }
@@ -396,9 +380,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.Document._isBackground ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
-            <div className="collectionView-facetCont" style={{ display: this.props.PanelPosition === "absolute" ? "flex" : "", justifyContent: this.props.PanelPosition === "absolute" ? "center" : "", width: `calc(100% - ${this.facetWidth()}px)` }}>
-                {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
-            </div>
+            {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d =>
                 Cast(d.data, ImageField) ?
                     (Cast(d.data, ImageField)!.url.href.indexOf(window.location.origin) === -1) ?
-- 
cgit v1.2.3-70-g09d2


From 93b72ab7818e48592e9d42d092fa91b4ebedd49e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 12 Sep 2020 16:22:09 -0400
Subject: fixed sidebar views in text boxes to work in treeview outlines. fixed
 context menus to select docs again.  fixed linkAnchors to show up when
 isLinkButton is true but doc is selcted.   changed nudging to be ignore in
 annotation overlays

---
 src/client/util/SelectionManager.ts                |  4 +-
 src/client/views/PreviewCursor.tsx                 |  2 +-
 .../views/collections/CollectionTreeView.scss      | 19 ++++++++
 .../views/collections/CollectionTreeView.tsx       | 21 +++------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  4 +-
 .../nodes/formattedText/FormattedTextBox.scss      | 30 +++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 51 +++++++++++-----------
 9 files changed, 68 insertions(+), 72 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 730db4f29..7795a4a59 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -73,8 +73,8 @@ export namespace SelectionManager {
     // computed functions, such as used in IsSelected generate errors if they're called outside of a
     // reaction context.  Specifying the context with 'outsideReaction' allows an efficiency feature
     // to avoid unnecessary mobx invalidations when running inside a reaction.
-    export function IsSelected(doc: DocumentView, outsideReaction?: boolean): boolean {
-        return outsideReaction ?
+    export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
+        return !doc ? false : outsideReaction ?
             manager.SelectedDocuments.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
             computedFn(function isSelected(doc: DocumentView) {  // wraapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
                 return manager.SelectedDocuments.get(doc) ? true : false;
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 0a4d46c1f..7575aa430 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -22,7 +22,7 @@ export class PreviewCursor extends React.Component<{}> {
     static _getTransform: () => Transform;
     static _addDocument: (doc: Doc | Doc[]) => void;
     static _addLiveTextDoc: (doc: Doc) => void;
-    static _nudge: (x: number, y: number) => boolean;
+    static _nudge?: (x: number, y: number) => boolean;
     @observable static _clickPoint = [0, 0];
     @observable public static Visible = false;
     constructor(props: any) {
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 09a46c30f..feeb04134 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -103,6 +103,15 @@
     padding-bottom: 2px;
 }
 
+.treeViewItem-container-active {
+    z-index: 100;
+    position: relative;;
+    .formattedTextbox-sidebar {
+        background-color: #ffff001f !important;
+        height: 500px !important;
+    }
+}
+
 .treeViewItem-openRight {
     display: none;
     height: 17px;
@@ -128,6 +137,16 @@
     border: transparent 1px solid;
     display: flex;
     align-items: center;
+            ::-webkit-scrollbar {
+            display: none;
+    }
+    .formattedTextBox-cont {
+        .formattedTextbox-sidebar {
+            overflow: visible !important;
+            border-left: unset;
+        }
+        overflow: visible !important;
+    }
 
     .editableView-container-editing-oneLine {
         min-width: 15px;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 52e3c86b8..276e0b873 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -32,6 +32,7 @@ import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextField } from '../../../fields/RichTextField';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { DocumentManager } from '../../util/DocumentManager';
 
 export interface TreeViewProps {
     document: Doc;
@@ -176,7 +177,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 500, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -552,9 +553,10 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             }
         } else this._editMaxWidth = "";
+        const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
         return this.doc.treeViewHideTitle || (this.outlineMode) ?
             !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
-                <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+                <div className={`treeViewItem-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
                     onKeyDown={e => {
                         e.stopPropagation();
                         e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
@@ -563,18 +565,9 @@ class TreeView extends React.Component<TreeViewProps> {
                         e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
                     }}
                 >
-                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
-                        if (this.props.active(true)) {
-                            e.stopPropagation();
-                            e.preventDefault();
-                        }
-                    }}
-                        onPointerDown={e => {
-                            if (this.props.active(true)) {
-                                e.stopPropagation();
-                                e.preventDefault();
-                            }
-                        }}
+                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }}
+                        onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
+                        onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
                         onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
                         {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
                         <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 543798b7e..843314c03 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -792,7 +792,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay) return;
+        if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay || this.props.Document.treeViewOutlineMode) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1369,7 +1369,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     _nudgeTime = 0;
     nudge = action((x: number, y: number) => {
-        if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) { // bcz: this isn't ideal, but want to try it out...
+        if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ||
+            this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out...
             this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(),
                 NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "transform 500ms", true);
             this._nudgeTime = Date.now();
@@ -1381,7 +1382,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get marqueeView() {
         return <MarqueeView
             {...this.props}
-            nudge={this.nudge}
+            nudge={this.isAnnotationOverlay ? undefined : this.nudge}
             addDocTab={this.addDocTab}
             activeDocuments={this.getActiveDocuments}
             selectDocuments={this.selectDocuments}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b9795ff37..ec1ddd0a5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -32,7 +32,7 @@ interface MarqueeViewProps {
     selectDocuments: (docs: Doc[]) => void;
     addLiveTextDocument: (doc: Doc) => void;
     isSelected: () => boolean;
-    nudge: (x: number, y: number) => boolean;
+    nudge?: (x: number, y: number) => boolean;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
 }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 147745d93..b372f3691 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -835,7 +835,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 e?.stopPropagation(); // DocumentViews should stop propagation of this event
             }
             cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
-            this.isSelected(true) && SelectionManager.SelectDoc(this, false);
+            !this.isSelected(true) && SelectionManager.SelectDoc(this, false);
         });
     }
 
@@ -912,7 +912,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
             {/* {this.allAnchors} */}
-            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || (!this.isSelected() && this.layoutDoc.hideLinkButton) || this.props.dontRegisterView ? (null) :
+            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) :
                 <DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />}
         </div>
         );
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 160f4ba72..847b1bb30 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -45,10 +45,11 @@
     width: 100%;
     height: 100%;
 }
-
+    
 .formattedTextBox-sidebar-handle {
     position: absolute;
-    top: calc(50% - 17.5px);
+    top: 0;
+    //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
     width: 10px;
     height: 35px;
     background: lightgray;
@@ -56,11 +57,6 @@
     cursor:grabbing;
 }
 
-.formattedTextBox-cont>.formattedTextBox-sidebar-handle {
-    right: 0;
-    left: unset;
-}
-
 .formattedTextBox-sidebar,
 .formattedTextBox-sidebar-inking {
     border-left: dashed 1px black;
@@ -73,11 +69,6 @@
     .collectionfreeformview-container {
         position: relative;
     }
-
-    >.formattedTextBox-sidebar-handle {
-        right: unset;
-        left: -5;
-    }
 }
 
 .formattedTextBox-sidebar-inking {
@@ -396,10 +387,11 @@ footnote::after {
         width: 100%;
         height: 100%;
     }
-
+    
     .formattedTextBox-sidebar-handle {
         position: absolute;
-        top: calc(50% - 17.5px);
+        top: 0;
+        //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
         width: 10px;
         height: 35px;
         background: lightgray;
@@ -407,11 +399,6 @@ footnote::after {
         cursor:grabbing;
     }
 
-    .formattedTextBox-cont>.formattedTextBox-sidebar-handle {
-        right: 0;
-        left: unset;
-    }
-
     .formattedTextBox-sidebar,
     .formattedTextBox-sidebar-inking {
         border-left: dashed 1px black;
@@ -423,11 +410,6 @@ footnote::after {
         .collectionfreeformview-container {
             position: relative;
         }
-
-        >.formattedTextBox-sidebar-handle {
-            right: unset;
-            left: -5;
-        }
     }
 
     .formattedTextBox-sidebar-inking {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 29d9283e7..a79e70017 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1500,7 +1500,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
-    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
+    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "20%"); }
     sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
     sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
     @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
@@ -1569,32 +1569,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                             }}
                         />
                     </div>
-                    {!this.layoutDoc._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
-                        <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> :
+                    {!this.layoutDoc._showSidebar ? (null) : <>
                         <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
                             style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-                            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                PanelHeight={this.props.PanelHeight}
-                                PanelWidth={this.sidebarWidth}
-                                scaleField={this.annotationKey + "-scale"}
-                                annotationsKey={this.annotationKey}
-                                isAnnotationOverlay={false}
-                                focus={this.props.focus}
-                                isSelected={this.props.isSelected}
-                                select={emptyFunction}
-                                active={this.annotationsActive}
-                                ContentScaling={returnOne}
-                                whenActiveChanged={this.whenActiveChanged}
-                                removeDocument={this.removeDocument}
-                                moveDocument={this.moveDocument}
-                                addDocument={this.addDocument}
-                                CollectionView={undefined}
-                                ScreenToLocalTransform={this.sidebarScreenToLocal}
-                                renderDepth={this.props.renderDepth + 1}
-                                ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                            </CollectionFreeFormView>
-                            <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} />
-                        </div>}
+                            {this.sidebarWidthPercent === "0%" ? (null) :
+                                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                                    PanelHeight={this.active() ? () => 1000 : this.props.PanelHeight}
+                                    PanelWidth={this.sidebarWidth}
+                                    scaleField={this.annotationKey + "-scale"}
+                                    annotationsKey={this.annotationKey}
+                                    isAnnotationOverlay={true}
+                                    focus={this.props.focus}
+                                    isSelected={this.props.isSelected}
+                                    select={emptyFunction}
+                                    active={this.annotationsActive}
+                                    ContentScaling={returnOne}
+                                    whenActiveChanged={this.whenActiveChanged}
+                                    removeDocument={this.removeDocument}
+                                    moveDocument={this.moveDocument}
+                                    addDocument={this.addDocument}
+                                    CollectionView={undefined}
+                                    ScreenToLocalTransform={this.sidebarScreenToLocal}
+                                    renderDepth={this.props.renderDepth + 1}
+                                    ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
+                            }
+                        </div>
+                        {this.props.isSelected() ? <div className="formattedTextBox-sidebar-handle" style={{ left: `calc(100% - ${this.sidebarWidthPercent} - 5px)` }} onPointerDown={this.sidebarDown} /> : (null)}
+                    </>}
                     {!this.layoutDoc._showAudio ? (null) :
                         <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
                             <FontAwesomeIcon className="formattedTextBox-audioFont"
-- 
cgit v1.2.3-70-g09d2


From 0ddb1a572d3579469bc08469d43c6fcdb114f901 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 12 Sep 2020 23:41:31 -0400
Subject: fixed stacking view selections to have the right bounding box. fixed
 text boxes to work with headerVIew template which has multiple text boxes in
 it.

---
 src/client/util/CurrentUserUtils.ts                | 33 +++++++++++-----------
 src/client/util/DropConverter.ts                   |  2 +-
 src/client/views/Main.scss                         |  4 +++
 src/client/views/collections/CollectionMenu.tsx    |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/globalCssVariables.scss           |  4 ---
 .../nodes/formattedText/FormattedTextBox.scss      |  5 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx | 23 +++++++++------
 .../formattedText/ProsemirrorExampleTransfer.ts    |  2 +-
 10 files changed, 43 insertions(+), 36 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 365a0a409..a63a0ba08 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -47,7 +47,7 @@ export class CurrentUserUtils {
 
     @observable public static propertiesWidth: number = 0;
 
-    // sets up the default User Templates - slideView, queryView, descriptionView
+    // sets up the default User Templates - slideView,  headerView
     static setupUserTemplateButtons(doc: Doc) {
         // Prototype for mobile button (not sure if 'Advanced Item Prototypes' is ideal location)
         if (doc["template-mobile-button"] === undefined) {
@@ -85,29 +85,29 @@ export class CurrentUserUtils {
             });
         }
 
-        if (doc["template-button-description"] === undefined) {
-            const descriptionTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _height: 100, system: true }, "header")); // text needs to be a space to allow templateText to be created
-            descriptionTemplate.system = true;
-            descriptionTemplate[DataSym].layout =
+        if (doc["template-button-header"] === undefined) {
+            const headerTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _autoHeight: true, system: true }, "header")); // text needs to be a space to allow templateText to be created
+            headerTemplate.system = true;
+            headerTemplate[DataSym].layout =
                 "<div>" +
-                "    <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`orange`}' fieldKey={'header'}/>" +
+                "    <FormattedTextBox {...props} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} height='{this._headerHeight||75}px' background='{this._headerColor||`orange`}' fieldKey={'header'}/>" +
                 "    <FormattedTextBox {...props} position='absolute' top='{(this._headerHeight||75)*scale}px' height='calc({100/scale}% - {this._headerHeight||75}px)' fieldKey={'text'}/>" +
                 "</div>";
-            (descriptionTemplate.proto as Doc).isTemplateDoc = makeTemplate(descriptionTemplate.proto as Doc, true, "descriptionView");
+            (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView");
 
-            doc["template-button-description"] = CurrentUserUtils.ficon({
+            doc["template-button-header"] = CurrentUserUtils.ficon({
                 onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
-                dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
-                removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize", system: true
+                dragFactory: new PrefetchProxy(headerTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "header view", icon: "window-maximize", system: true
             });
         }
 
         if (doc["template-button-link"] === undefined) {  // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to.
-            const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _height: 100, system: true }, "header")); // text needs to be a space to allow templateText to be created
+            const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _autoHeight: true, system: true }, "header")); // text needs to be a space to allow templateText to be created
             linkTemplate.system = true;
             Doc.GetProto(linkTemplate).layout =
                 "<div>" +
-                "    <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`lightGray`}' fieldKey={'header'}/>" +
+                "    <FormattedTextBox {...props} dontSelectOnLoad={'true'} height='{this._headerHeight||75}px' ignoreAutoHeight={'true'} background='{this._headerColor||`lightGray`}' fieldKey={'header'}/>" +
                 "    <FormattedTextBox {...props} position='absolute' top='{(this._headerHeight||75)*scale}px' height='calc({100/scale}% - {this._headerHeight||75}px)' fieldKey={'text'}/>" +
                 "</div>";
             (linkTemplate.proto as Doc).isTemplateDoc = makeTemplate(linkTemplate.proto as Doc, true, "linkView");
@@ -190,9 +190,9 @@ export class CurrentUserUtils {
                 onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true
             });
 
-            const details = TextDocument("", { title: "details", _height: 350, _autoHeight: true, system: true });
-            const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 100, _autoHeight: true, system: true });
-            const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 350, _autoHeight: true, system: true });
+            const details = TextDocument("", { title: "details", _height: 200, _autoHeight: true, system: true });
+            const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 75, _autoHeight: true, system: true });
+            const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 150, _autoHeight: true, system: true });
 
             const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"];
             const detailedTemplate = {
@@ -253,7 +253,7 @@ export class CurrentUserUtils {
 
         const requiredTypes = [
             doc["template-button-slides"] as Doc,
-            doc["template-button-description"] as Doc,
+            doc["template-button-header"] as Doc,
             doc["template-mobile-button"] as Doc,
             doc["template-button-detail"] as Doc,
             doc["template-button-simple"] as Doc,
@@ -468,7 +468,6 @@ export class CurrentUserUtils {
             { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
             { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc, noviceMode: true },
             { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
-            { toolTip: "Tap to create a search box in a new pane, drag for a search box", title: "Query", icon: "search", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySearch as Doc },
             { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
             { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
             { toolTip: "Tap to create a document previewer in a new pane, drag for a document previewer", title: "Prev", icon: "expand", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyDocHolder as Doc },
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index f1848f7e5..49643b5bb 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -60,7 +60,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes("FontIconBox")) {
             //dbox = Doc.MakeAlias(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
         } else if (!doc.onDragStart && !doc.isButtonBar) {
-            const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
+            const layoutDoc = doc;// doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
             if (layoutDoc.type !== DocumentType.FONTICON) {
                 !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
             }
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 97ed0a901..b1ad4868c 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -1,6 +1,10 @@
 @import "globalCssVariables";
 @import "nodeModuleOverrides";
 
+:root {
+    --flyoutHandleWidth: 28px;
+    --menuPanelWidth: 60px;
+}
 html,
 body {
     width: 100%;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index a58edc604..3cc041e0b 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -533,7 +533,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
         if (this.selectedDoc) {
             const layoutField = Doc.LayoutField(this.selectedDoc);
             const layoutStr = this.selectedDocumentView?.props.LayoutTemplateString || StrCast(layoutField);
-            return layoutStr.includes("FormattedText") || StrCast((layoutField as Doc)?.layout).includes("FormattedText");
+            return (document.activeElement as any)?.className.includes("ProseMirror") || layoutStr.includes("FormattedText") || StrCast((layoutField as Doc)?.layout).includes("FormattedText");
         }
         else return false;
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index cbc62be91..9c9dad2c9 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -187,7 +187,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         const opacity = () => this.Document._currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
         return <ContentFittingDocumentView
             Document={doc}
-            DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
+            DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
             backgroundColor={this.props.backgroundColor}
             LayoutTemplate={this.props.ChildLayoutTemplate}
             LayoutTemplateString={this.props.ChildLayoutString}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 276e0b873..47d4aad94 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -177,7 +177,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 500, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index d0c90edcb..1539d1399 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -38,10 +38,6 @@ $MINIMIZED_ICON_SIZE:25;
 $MAX_ROW_HEIGHT: 44px;
 $DFLT_IMAGE_NATIVE_DIM: 900px;
 $MENU_PANEL_WIDTH: 60px;
-:root {
-    --flyoutHandleWidth: 28px;
-    --menuPanelWidth: 60px;
-}
 
 :export {
     contextMenuZindex: $contextMenu-zindex;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 847b1bb30..43e64d2d2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -43,7 +43,7 @@
     overflow: auto;
     display: inline-block;
     width: 100%;
-    height: 100%;
+    height: unset;
 }
     
 .formattedTextBox-sidebar-handle {
@@ -51,7 +51,8 @@
     top: 0;
     //top: calc(50% - 17.5px);  // use this to center vertically -- make sure it looks okay for slide views
     width: 10px;
-    height: 35px;
+    height: 100%;
+    max-height: 35px;
     background: lightgray;
     border-radius: 20px;
     cursor:grabbing;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a79e70017..873d88f7e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -64,6 +64,7 @@ export interface FormattedTextBoxProps {
     hideOnLeave?: boolean;  // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
     xMargin?: number;   // used to override document's settings for xMargin --- see CollectionCarouselView
     yMargin?: number;
+    dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded
 }
 export const GoogleRef = "googleDocId";
 
@@ -79,6 +80,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     public static Instance: FormattedTextBox;
     public ProseRef?: HTMLDivElement;
     public get EditorView() { return this._editorView; }
+    private _boxRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _editorView: Opt<EditorView>;
@@ -556,7 +558,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this.CurrentDiv.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = "" + 100 * (1 - (e.clientX - bounds.left) / bounds.width) + "%";
+        this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
         return false;
     }
     @undoBatch
@@ -1144,7 +1146,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
 
         const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad;
-        if (selectOnLoad && !this.props.dontRegisterView) {
+        if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad) {
             FormattedTextBox.SelectOnLoad = "";
             this.props.select(false);
             if (FormattedTextBox.SelectOnLoadChar) {
@@ -1314,7 +1316,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
             if (!node && this.ProseRef) {
                 const lastNode = this.ProseRef.children[this.ProseRef.children.length - 1].children[this.ProseRef.children[this.ProseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
-                if (e.clientY > lastNode?.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+                const boundsRect = lastNode?.getBoundingClientRect();
+                if (e.clientX > boundsRect.left && e.clientX < boundsRect.right &&
+                    e.clientY > boundsRect.bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+                    this._editorView?.focus();
                     this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size)));
                 }
             } else if ([this._editorView!.state.schema.nodes.ordered_list, this._editorView!.state.schema.nodes.listItem].includes(node?.type) &&
@@ -1473,7 +1478,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     @action
     tryUpdateHeight(limitHeight?: number) {
-        let scrollHeight = this._ref.current?.scrollHeight;
+        let scrollHeight = this.ProseRef?.scrollHeight || 0;
         if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
             scrollHeight = scrollHeight * NumCast(this.layoutDoc._viewScale, 1);
             if (limitHeight && scrollHeight > limitHeight) {
@@ -1493,14 +1498,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }, 10);
             } else {
                 try {
-                    this.layoutDoc._height = newHeight;
+                    const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+                    const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
+                    this.rootDoc._height = newHeight + outer;
                     this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
                 } catch (e) { console.log("Error in tryUpdateHeight"); }
             }
         }
     }
 
-    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "20%"); }
+    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
     sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
     sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
     @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
@@ -1516,7 +1523,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const selPad = this.props.isSelected() && !this.layoutDoc._singleLine ? -10 : 0;
         const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : "";
         return (
-            <div className={"formattedTextBox-cont"}
+            <div className={"formattedTextBox-cont"} ref={this._boxRef}
                 style={{
                     transform: `scale(${scale})`,
                     transformOrigin: "top left",
@@ -1594,7 +1601,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                                     ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
                             }
                         </div>
-                        {this.props.isSelected() ? <div className="formattedTextBox-sidebar-handle" style={{ left: `calc(100% - ${this.sidebarWidthPercent} - 5px)` }} onPointerDown={this.sidebarDown} /> : (null)}
+                        {this.props.isSelected() ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)}
                     </>}
                     {!this.layoutDoc._showAudio ? (null) :
                         <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 9895940d1..e77cc2692 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -49,7 +49,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     /// bcz; Argh!!  replace with an onEnter func that conditionally handles Enter 
     const addTextBox = (below: boolean, force?: boolean) => {
-        if (props.LayoutTemplateString) return true;
+        if (props.Document.treeViewOutlineMode) return true;  // bcz: Arghh .. need to determine if this is an treeViewOutlineBox in which case Enter's are ignored..
         const layoutDoc = props.Document;
         const originalDoc = layoutDoc.rootDocument || layoutDoc;
         if (force || props.Document._singleLine) {
-- 
cgit v1.2.3-70-g09d2


From bf5718d25e372689b97965eafc80dd7cbdd2963c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Sep 2020 11:22:22 -0400
Subject: fixed userdoc tree view layout issues caused by outline mode.  added
 treeViewHideHeader to avoid hackiness of overlaoding treeVewHideTitle

---
 src/client/documents/Documents.ts                                  | 3 ++-
 src/client/views/collections/CollectionTreeView.scss               | 4 ++--
 src/client/views/collections/CollectionTreeView.tsx                | 6 +++---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx      | 1 +
 src/client/views/nodes/formattedText/FormattedTextBox.tsx          | 7 ++++---
 5 files changed, 12 insertions(+), 9 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8fe55b9a6..abe50e011 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -190,7 +190,8 @@ export interface DocumentOptions {
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
     freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
     treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
-    treeViewHideTitle?: boolean; // whether to hide the top document of a tree view
+    treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
+    treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewOpen?: boolean; // whether this document is expanded in a tree view
     treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index feeb04134..aaf17153a 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -136,8 +136,8 @@
 .treeViewItem-header {
     border: transparent 1px solid;
     display: flex;
-    align-items: center;
-            ::-webkit-scrollbar {
+    //align-items: center;
+    ::-webkit-scrollbar {
             display: none;
     }
     .formattedTextBox-cont {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 47d4aad94..ff3c38a43 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -177,7 +177,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, treeViewHideHeader: true, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -554,7 +554,7 @@ class TreeView extends React.Component<TreeViewProps> {
             }
         } else this._editMaxWidth = "";
         const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
-        return this.doc.treeViewHideTitle || (this.outlineMode) ?
+        return this.doc.treeViewHideHeader ?
             !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
                 <div className={`treeViewItem-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
                     onKeyDown={e => {
@@ -565,7 +565,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
                     }}
                 >
-                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }}
+                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
                         onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
                         onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
                         onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 843314c03..60e347786 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1233,6 +1233,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const appearance = ContextMenu.Instance.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
+        Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
         !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 668b84903..31d471571 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -616,15 +616,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const appearance = cm.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
-        this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
+        // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
         Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({
-            description: "Convert to be a template style", event: () => {
+            description: "Make Default Layout", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
                     this.rootDoc.title = "text";
                     this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title);
-                } else {
+                } else if (!this.rootDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
                     this.rootDoc.title = "text";
                     this.rootDoc.layout = (this.layoutDoc as Doc).layout as string;
@@ -640,6 +640,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
                     }, 10);
                 }
+                Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
                 Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
             }, icon: "eye"
         });
-- 
cgit v1.2.3-70-g09d2


From 3893d4913852bd92b2a71880bb7c7cf39aba0c2c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Sep 2020 12:23:50 -0400
Subject: fixes to sidebar menus to simplify them for novices.  fixed clearAll
 menu items

---
 src/client/util/CurrentUserUtils.ts                |  4 +-
 src/client/views/ContextMenuItem.tsx               |  2 +-
 .../views/collections/CollectionTreeView.tsx       | 32 ++++-----------
 src/client/views/collections/CollectionView.tsx    | 12 +++---
 src/client/views/nodes/DocumentView.tsx            | 47 +++++++++++-----------
 5 files changed, 42 insertions(+), 55 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 3dc16b864..17f1146b1 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -779,7 +779,7 @@ export class CurrentUserUtils {
                 treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
-            const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
             (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
             (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
         }
@@ -794,7 +794,7 @@ export class CurrentUserUtils {
                 treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
                 lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
             }));
-            const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
             (doc.myFilter as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
             (doc.myFilter as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
         }
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index eddecb7a7..d3429cdfb 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -34,7 +34,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
     constructor(props: ContextMenuProps | SubmenuProps) {
         super(props);
         if ("subitems" in this.props) {
-            this.props.subitems.forEach(i => this._items.push(i));
+            this.props.subitems?.forEach(i => this._items.push(i));
         }
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ff3c38a43..60ce7e001 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -525,7 +525,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionDoc={this.props.containingCollection}
             />;
         return <>
-            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
+            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title"
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
@@ -846,40 +846,24 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.doc === CurrentUserUtils.MyDashboards) {
-            ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "plus" });
-            ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
-            e.stopPropagation();
-            e.preventDefault();
-            ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosedDocs) {
-            ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosedDocs = new List<Doc>(), icon: "plus" });
-            e.stopPropagation();
-            e.preventDefault();
-            ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-        } else {
+        if (!Doc.UserDoc().noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
+            const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+            const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+            onClicks.push({ description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" });
+            !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
         }
-        const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
-        const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-        onClicks.push({
-            description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
-        });
-        onClicks.push({
-            description: `${this.props.Document.treeViewOutlineMode ? "Delay " : "Immediate "} Title Editing`, event: () => UndoManager.RunInBatch(() => this.props.Document.treeViewOutlineMode = !this.props.Document.treeViewOutlineMode, "edit onCheckedClick"), icon: "edit"
-        });
-        !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
     }
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
     onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {});
 
     @computed get renderClearButton() {
-        return <div id="toolbar" key="toolbar">
+        return <div key="toolbar">
             <button className="toolbar-button round-button" title="Empty"
                 onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
                 <FontAwesomeIcon icon={"trash"} size="sm" />
@@ -973,7 +957,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
 
         return !childDocs ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
-                <div className="collectionTreeView-dropTarget" id="body"
+                <div className="collectionTreeView-dropTarget"
                     style={{
                         background: backgroundColor,
                         paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2148f6c4d..97b8da8da 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -282,10 +282,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
         subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
 
-        const existingVm = ContextMenu.Instance.findByDescription(category);
-        const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
-        catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
-        !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: "eye" });
+        if (!Doc.IsSystem(this.props.Document)) {
+            const existingVm = ContextMenu.Instance.findByDescription(category);
+            const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+            catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
+            !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: "eye" });
+        }
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
@@ -329,7 +331,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     icon: "edit",
                     event: () => Doc.GetProto(this.props.Document)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)),
                 }));
-            !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+            !Doc.IsSystem(this.props.Document) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
 
             if (!Doc.UserDoc().noviceMode) {
                 const more = cm.findByDescription("More...");
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bcf59f74a..771a8f526 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -766,15 +766,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
         !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
-        const options = cm.findByDescription("Options...");
-        const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-        optionItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" });
-        optionItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" });
-        optionItems.push({ description: this.rootDoc["_isBackground-canClick"] ? "Unlock from Back" : "Lock in Back", event: this.toggleLockInBack, icon: "expand-arrows-alt" });
-        !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
-        !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
-
         if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+            const options = cm.findByDescription("Options...");
+            const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+            optionItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" });
+            optionItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" });
+            optionItems.push({ description: this.rootDoc["_isBackground-canClick"] ? "Unlock from Back" : "Lock in Back", event: this.toggleLockInBack, icon: "expand-arrows-alt" });
+            !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+            !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+
             const existingOnClick = cm.findByDescription("OnClick...");
             const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
             onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
@@ -798,21 +798,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const more = cm.findByDescription("More...");
         const moreItems = more && "subitems" in more ? more.subitems : [];
-        moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) });
-        moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" });
-        //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
-        //moreItems.push({ description: "Create an Alias", event: () => this.onCopy(), icon: "copy" });
-        if (!Doc.UserDoc().noviceMode) {
-            moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
-            moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
-
-            if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
-                moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
-                moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
-                moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+        if (!Doc.IsSystem(this.rootDoc)) {
+            moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) });
+            moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" });
+            //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+            if (!Doc.UserDoc().noviceMode) {
+                moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
+                moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+
+                if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
+                    moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
+                    moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
+                    moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+                }
+                moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
+                Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc().myUserDoc, Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
             }
-            moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
-            Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc().myUserDoc, Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
         }
 
         const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
@@ -827,7 +828,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
         !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
         helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-        helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+        !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
         cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
 
         runInAction(() => {
-- 
cgit v1.2.3-70-g09d2


From 6c03a4ad74ca93311139e8fecda8a4793c103488 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Sep 2020 22:17:54 -0400
Subject: updated header text view.  fixed dragging tree view items to
 unregister listeners. fixed contextMenu icon selection to ignore
 dashFieldViews.   fixed selectOnLoadChar to not apply when a
 childlayoutstring is specified.  fixed dropping text box on item view to only
 use json parseable text.

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/CurrentUserUtils.ts                | 13 ++++++++--
 src/client/views/DocumentButtonBar.tsx             |  1 +
 src/client/views/PreviewCursor.tsx                 |  4 +--
 src/client/views/collections/CollectionMenu.tsx    |  6 +++--
 .../views/collections/CollectionTreeView.tsx       | 12 +++++++++
 src/client/views/collections/TabDocView.tsx        | 12 ++++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  4 +--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  6 ++---
 .../nodes/formattedText/FormattedTextBox.scss      |  5 +++-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 29 +++++++++++++++-------
 15 files changed, 73 insertions(+), 30 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d22e20101..0911f3e04 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -98,6 +98,7 @@ export interface DocumentOptions {
     layoutKey?: string;
     type?: string;
     title?: string;
+    version?: string; // version identifier for a document
     label?: string;
     hidden?: boolean;
     userDoc?: Doc; // the userDocument
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b38cdabbc..6d4035b75 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -408,7 +408,7 @@ export class CurrentUserUtils {
             FormattedTextBox.SelectOnLoad = textDoc[Id];
             doc.emptySlide = textDoc;
         }
-        if (doc.emptyHeader === undefined) {
+        if (doc.emptyHeader === undefined || (doc.emptyHeader as Doc).version !== "0") {
             const json = {
                 doc: {
                     type: "doc",
@@ -428,7 +428,7 @@ export class CurrentUserUtils {
                 selection: { type: "text", anchor: 1, head: 1 },
                 storedMarks: []
             };
-            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", target: doc, _headerHeight: 24, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created
+            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: "0", target: doc, _height: 70, _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created
             headerTemplate[DataSym].layout =
                 "<div>" +
                 "    <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.userColor}' />" +
@@ -1001,6 +1001,15 @@ export class CurrentUserUtils {
         doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
         doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
 
+        // uncomment this to setup a default note style that uses the custom header layout
+        PromiseValue(doc.emptyHeader).then(factory => {
+            if (Cast(doc.defaultTextLayout, Doc, null)?.version !== "0") {
+                const deleg = Doc.delegateDragFactory(factory as Doc);
+                deleg.title = "header";
+                doc.defaultTextLayout = new PrefetchProxy(deleg);
+                Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg);
+            }
+        });
         return doc;
     }
 
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index e8b18fca4..7629ed173 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -298,6 +298,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         while (child.children.length) {
             const next = Array.from(child.children).find(c => typeof (c.className) === "string");
             if (next?.className.includes("documentView-node")) break;
+            if (next?.className.includes("dashFieldView")) break;
             if (next) child = next;
             else break;
         }
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 7575aa430..ee5a390a7 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -22,7 +22,7 @@ export class PreviewCursor extends React.Component<{}> {
     static _getTransform: () => Transform;
     static _addDocument: (doc: Doc | Doc[]) => void;
     static _addLiveTextDoc: (doc: Doc) => void;
-    static _nudge?: (x: number, y: number) => boolean;
+    static _nudge?: undefined | ((x: number, y: number) => boolean);
     @observable static _clickPoint = [0, 0];
     @observable public static Visible = false;
     constructor(props: any) {
@@ -158,7 +158,7 @@ export class PreviewCursor extends React.Component<{}> {
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
         addDocument: (doc: Doc | Doc[]) => boolean,
-        nudge: (nudgeX: number, nudgeY: number) => boolean) {
+        nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
         this._addLiveTextDoc = addLiveText;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 8342c5b12..9e67faed5 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -123,8 +123,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
         params: ["target", "source"], title: "item view",
         script: "self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])",
         immediate: undoBatch((source: Doc[]) => {
-            if (source.length === 1 && source[0].type === DocumentType.RTF && Cast(source[0].text, RichTextField, null)?.Text) {
-                Doc.SetInPlace(this.target, "childLayoutString", Cast(source[0].text, RichTextField, null)?.Text, false);
+            let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
+            try { formatStr && JSON.parse(formatStr); } catch (e) { formatStr = ""; }
+            if (source.length === 1 && formatStr) {
+                Doc.SetInPlace(this.target, "childLayoutString", formatStr, false);
             } else if (source.length) {
                 this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]);
             } else {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 60ce7e001..463439762 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -145,15 +145,25 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     componentWillUnmount() {
+        console.log("DISMOUT" + this.doc.title);
         document.removeEventListener("pointermove", this.onDragMove, true);
+        document.removeEventListener("pointermove", this.onDragUp, true);
     }
 
+    onDragUp = (e: PointerEvent) => {
+        console.log("DUP" + this.doc.title);
+        document.removeEventListener("pointerup", this.onDragUp, true);
+        document.removeEventListener("pointermove", this.onDragMove, true);
+    }
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active(true) && Doc.BrushDoc(this.dataDoc);
         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "treeViewItem-header";
             document.removeEventListener("pointermove", this.onDragMove, true);
             document.addEventListener("pointermove", this.onDragMove, true);
+            document.removeEventListener("pointerup", this.onDragUp, true);
+            document.addEventListener("pointerup", this.onDragUp, true);
+            console.log("DSTART" + this.doc.title);
         }
     }
     onPointerLeave = (e: React.PointerEvent): void => {
@@ -161,6 +171,8 @@ class TreeView extends React.Component<TreeViewProps> {
         if (this._header?.current?.className !== "treeViewItem-header-editing") {
             this._header!.current!.className = "treeViewItem-header";
         }
+        console.log("DLEAVE" + this.doc.title);
+        document.removeEventListener("pointerup", this.onDragUp, true);
         document.removeEventListener("pointermove", this.onDragMove, true);
     }
     onDragMove = (e: PointerEvent): void => {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f4fda67cd..62f6d50fc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -28,6 +28,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionViewType } from './CollectionView';
 import React = require("react");
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -330,9 +331,12 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         />
                     </div>
                 </div>}
-            <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
-                <FontAwesomeIcon icon={"globe-asia"} size="lg" />
-            </div>
+
+            <Tooltip title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
+                <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
+                    <FontAwesomeIcon icon={"globe-asia"} size="lg" />
+                </div>
+            </Tooltip>
         </>;
     }
     focusFunc = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => void) => afterFocus?.();
@@ -367,7 +371,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
-                {this._document!._viewType !== CollectionViewType.Freeform ? (null) : this.renderMiniMap()}
+                {this._document._viewType !== CollectionViewType.Freeform ? (null) : this.renderMiniMap()}
             </>;
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 60e347786..ef58afeb5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1116,8 +1116,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     dataProvider={this.childDataProvider}
                     sizeProvider={this.childSizeProvider}
                     pointerEvents={this.backgroundActive || this.props.childPointerEvents ?
-                        true :
-                        (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? false : undefined}
+                        "all" :
+                        (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
                     jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))}
                     //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
                     fitToBox={BoolCast(this.props.freezeChildDimensions)} // bcz: check this
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ec1ddd0a5..6436e74e9 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -137,7 +137,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 //setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false));
                 e.stopPropagation();
             } else if (!e.ctrlKey && !e.metaKey) {
-                FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : "";
+                FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.ChildLayoutString ? e.key : "";
                 const tbox = Docs.Create.TextDocument("", {
                     _width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
                     _fontFamily: StrCast(Doc.UserDoc().fontFamily),
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 7658ee5cb..88f6a65be 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -535,7 +535,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             return <DocumentView {...this.props}
                 Document={mark}
                 focus={() => this.playLink(mark)}
-                pointerEvents={true}
+                pointerEvents={"all"}
                 rootSelected={returnFalse}
                 LayoutTemplate={undefined}
                 ContainingCollectionDoc={this.props.Document}
@@ -628,7 +628,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                                             backgroundColor={returnTransparent}
                                             ContentScaling={returnOne}
                                             forcedBackgroundColor={returnTransparent}
-                                            pointerEvents={false}
+                                            pointerEvents={"none"}
                                             LayoutTemplate={undefined}
                                             LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
                                         />
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3be32dce5..5df36435c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -265,7 +265,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 zIndex: this.ZInd,
                 mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document._isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
+                pointerEvents: this.props.Document._isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents
             }} >
 
             {Doc.UserDoc().renderStyle !== "comic" ? (null) :
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 616cddfcf..2af5b3182 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -74,7 +74,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C
 
     render() {
         const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%";
-        const childProps: DocumentViewProps = { ...this.props, pointerEvents: false, parentActive: this.props.active };
+        const childProps: DocumentViewProps = { ...this.props, pointerEvents: "none", parentActive: this.props.active };
         const clearButton = (which: string) => {
             return <div className={`clear-button ${which}`}
                 onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 771a8f526..15e571839 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -79,7 +79,7 @@ export interface DocumentViewProps {
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    pointerEvents?: boolean;
+    pointerEvents?: string;
     focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
@@ -960,7 +960,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     dontRegisterView={false}
                     forcedBackgroundColor={returnTransparent}
                     removeDocument={this.hideLinkAnchor}
-                    pointerEvents={false}
+                    pointerEvents={"none"}
                     LayoutTemplate={undefined}
                     LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
             </div >);
@@ -1017,7 +1017,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             </div>;
     }
     @computed get ignorePointerEvents() {
-        return this.props.pointerEvents === false ||
+        return this.props.pointerEvents === "none" ||
             (SnappingManager.GetIsDragging() && this.Document["_isBackground-canClick"]) ||
             (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
             (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 43e64d2d2..d1109b388 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -86,7 +86,9 @@
 }
 
 .formattedTextBox-inner-rounded, .formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner, .formattedTextBox-inner-selected {
+.formattedTextBox-inner, 
+.formattedTextBox-inner-minimal,
+.formattedTextBox-inner-selected {
     height: 100%;
     white-space: pre-wrap;
     .ProseMirror:hover {
@@ -426,6 +428,7 @@ footnote::after {
         left: 10%;
     }
 
+    .formattedTextBox-inner-minimal,
     .formattedTextBox-inner-rounded,
     .formattedTextBox-inner {
         height: 100%;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c6b4775c9..dc7d355b7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -238,7 +238,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const tsel = this._editorView.state.selection.$from;
             tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000)));
             const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
-            const curTemp = Cast(this.layoutDoc[this.props.fieldKey], RichTextField);               // the actual text in the text box
+            const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined;               // the actual text in the text box
             const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null);              // the default text inherited from a prototype
             const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
             const json = JSON.stringify(state.toJSON());
@@ -247,7 +247,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
             const removeSelection = (json: string | undefined) => {
                 return json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
-            }
+            };
 
             if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) {
                 if (!this._applyingChange && removeSelection(json) !== removeSelection(curProto?.Data)) {
@@ -617,7 +617,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
+        appearanceItems.push({ description: "Reset default note style", event: () => this.rootDoc.layoutKey = "layout", icon: "eye" });
         appearanceItems.push({
             description: "Make Default Layout", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
@@ -1155,9 +1155,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (FormattedTextBox.SelectOnLoadChar && this._editorView) {
                 const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
-                const curMarks = this._editorView!.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+                const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                this._editorView.dispatch(this._editorView!.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar).setStoredMarks(storedMarks));
+                this._editorView.dispatch(this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar).setStoredMarks(storedMarks));
                 FormattedTextBox.SelectOnLoadChar = "";
             } else if (curText?.Text) {
                 selectAll(this._editorView!.state, this._editorView?.dispatch);
@@ -1496,7 +1496,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight, 0);
             const dh = NumCast(this.rootDoc._height, 0);
             const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
-            if (this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
+            if (!this.props.LayoutTemplateString && this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
                 // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
                 console.log("Delayed height adjustment...");
                 setTimeout(() => {
@@ -1505,6 +1505,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }, 10);
             } else {
                 try {
+                    // let ele = this._boxRef.current;
+                    // while (ele && ele.className !== "documentView-contentsView") ele = ele.parentElement as any;
+                    // if (ele) {
+                    //     const docHeight = Number(getComputedStyle(ele).height.replace("px", ""));
+                    //     const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+                    //     const outer = docHeight - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
+                    //     this.rootDoc._height = newHeight + outer;
+                    //     this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
+                    // }
                     const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
                     const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
                     this.rootDoc._height = newHeight + outer;
@@ -1527,7 +1536,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
             setTimeout(() => FormattedTextBoxComment.Hide(), 0);
         }
-        const selPad = this.props.isSelected() && !this.layoutDoc._singleLine ? -10 : 0;
+        const minimal = this.props.ignoreAutoHeight;
+        const selPad = (this.props.isSelected() && !this.layoutDoc._singleLine) || minimal ? -10 : 0;
         const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : "";
         return (
             <div className={"formattedTextBox-cont"} ref={this._boxRef}
@@ -1575,10 +1585,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     <div className={`formattedTextBox-outer`} ref={this._scrollRef}
                         style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !this.props.active() ? "none" : undefined }}
                         onScroll={this.onscrolled} onDrop={this.ondrop} >
-                        <div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
+                        <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
                             style={{
                                 overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
-                                padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px  ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
+                                padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) :
+                                    `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px  ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
                                 pointerEvents: !this.props.active() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
                             }}
                         />
-- 
cgit v1.2.3-70-g09d2


From 0f3d6bdbf56f3b971094afde6205dfa6d6235f1d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 15 Sep 2020 00:10:49 -0400
Subject: made focusing on a tree view item focus on the tree view.

---
 src/client/views/collections/CollectionTreeView.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 463439762..b27f75837 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -550,6 +550,8 @@ class TreeView extends React.Component<TreeViewProps> {
         </>;
     }
 
+    refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+
     render() {
         TraceMobx();
         if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
@@ -595,7 +597,7 @@ class TreeView extends React.Component<TreeViewProps> {
                                 fitToBox={this.boundsOfCollectionDocument !== undefined}
                                 PanelWidth={this.rtfWidth}
                                 PanelHeight={this.rtfOutlineHeight}
-                                focus={returnFalse}
+                                focus={this.refocus}
                                 ScreenToLocalTransform={this.docTransform}
                                 docFilters={returnEmptyFilter}
                                 searchFilterDocs={returnEmptyDoclist}
@@ -930,7 +932,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 backgroundColor={this.props.backgroundColor}
                 PanelWidth={this.rtfWidth}
                 PanelHeight={this.rtfOutlineHeight}
-                focus={returnFalse}
+                focus={this.props.focus}
                 ScreenToLocalTransform={this.titleTransform}
                 docFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
-- 
cgit v1.2.3-70-g09d2


From 40033dedb5319aa0fd00ff3156d6bb19015dfdfe Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 15 Sep 2020 10:17:08 -0400
Subject: outline mode for tree views now always hidesHeader fields in favor of
 displaying the document.  This allows slide items to appear normally as
 titles in other tree views (eg dashboard)

---
 src/client/views/collections/CollectionTreeView.tsx | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b27f75837..1aa3a9364 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -145,13 +145,11 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     componentWillUnmount() {
-        console.log("DISMOUT" + this.doc.title);
         document.removeEventListener("pointermove", this.onDragMove, true);
         document.removeEventListener("pointermove", this.onDragUp, true);
     }
 
     onDragUp = (e: PointerEvent) => {
-        console.log("DUP" + this.doc.title);
         document.removeEventListener("pointerup", this.onDragUp, true);
         document.removeEventListener("pointermove", this.onDragMove, true);
     }
@@ -163,7 +161,6 @@ class TreeView extends React.Component<TreeViewProps> {
             document.addEventListener("pointermove", this.onDragMove, true);
             document.removeEventListener("pointerup", this.onDragUp, true);
             document.addEventListener("pointerup", this.onDragUp, true);
-            console.log("DSTART" + this.doc.title);
         }
     }
     onPointerLeave = (e: React.PointerEvent): void => {
@@ -171,7 +168,6 @@ class TreeView extends React.Component<TreeViewProps> {
         if (this._header?.current?.className !== "treeViewItem-header-editing") {
             this._header!.current!.className = "treeViewItem-header";
         }
-        console.log("DLEAVE" + this.doc.title);
         document.removeEventListener("pointerup", this.onDragUp, true);
         document.removeEventListener("pointermove", this.onDragMove, true);
     }
@@ -189,7 +185,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, treeViewHideHeader: true, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -568,7 +564,7 @@ class TreeView extends React.Component<TreeViewProps> {
             }
         } else this._editMaxWidth = "";
         const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
-        return this.doc.treeViewHideHeader ?
+        return this.doc.treeViewHideHeader || this.outlineMode ?
             !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
                 <div className={`treeViewItem-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
                     onKeyDown={e => {
-- 
cgit v1.2.3-70-g09d2


From 4fc10a64e35234da2dafd03c3f53f5e080e754d2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Sep 2020 11:40:25 -0400
Subject: cleaned up TabDocView a bit

---
 src/client/util/DragManager.ts                     |  1 +
 .../views/collections/CollectionTreeView.tsx       |  4 +-
 src/client/views/collections/TabDocView.tsx        | 96 +++++++++-------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  6 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  2 +-
 7 files changed, 48 insertions(+), 65 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 367155c90..7b64d1ccc 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -223,6 +223,7 @@ export namespace DragManager {
         };
         dragData.draggedDocuments.map(d => d.dragFactory); // does this help?  trying to make sure the dragFactory Doc is loaded
         StartDrag(eles, dragData, downX, downY, options, finishDrag);
+        return true;
     }
 
     // drag a button template and drop a new button 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 1aa3a9364..61c8f580d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -52,7 +52,7 @@ export interface TreeViewProps {
     indentDocument?: () => void;
     outdentDocument?: () => void;
     ScreenToLocalTransform: () => Transform;
-    backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
+    backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
     outerXf: () => { translateX: number, translateY: number };
     treeView: CollectionTreeView;
     parentKey: string;
@@ -655,7 +655,7 @@ class TreeView extends React.Component<TreeViewProps> {
         dropAction: dropActionType,
         addDocTab: (doc: Doc, where: string) => boolean,
         pinToPres: (document: Doc) => void,
-        backgroundColor: undefined | ((document: Doc, renderDepth: number) => string | undefined),
+        backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
         active: (outsideReaction?: boolean) => boolean,
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 84dc6c9fb..06d182f34 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -1,3 +1,5 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import { clamp } from 'lodash';
@@ -10,7 +12,7 @@ import { FieldId } from "../../../fields/RefField";
 import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -22,13 +24,11 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import { PresBox } from '../nodes/PresBox';
 import { CollectionDockingView } from './CollectionDockingView';
-import "./TabDocView.scss";
 import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionViewType } from './CollectionView';
+import "./TabDocView.scss";
 import React = require("react");
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -72,11 +72,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     tab.setActive(true);
                 }
             };
-            const onPointerDown = (e: React.PointerEvent) => {
-                setupMoveUpEvents(this, e, (e) => {
-                    !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY);
-                    return !e.defaultPrevented;
-                }, returnFalse, emptyFunction);
+            const dragBtnDown = (e: React.PointerEvent) => {
+                setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
             };
 
             // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
@@ -99,12 +96,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             dragHdl.style.position = "relative";
             dragHdl.style.paddingLeft = "0px";
             dragHdl.style.paddingRight = "12px";
-            tab._disposers.buttonDisposer = reaction(() => this.view, (view) => view &&
-                [ReactDOM.render(
-                    <span title="Drag as document" className="collectionDockingView-drag" onPointerDown={onPointerDown} >
-                        <CollectionDockingViewMenu views={() => [view]} Stack={stack} />
-                    </span>, dragHdl),
-                tab._disposers.buttonDisposer?.()],
+            tab._disposers.buttonDisposer = reaction(() => this.view, view =>
+                view && [ReactDOM.render(<span className="tabDocView-drag" onPointerDown={dragBtnDown}><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, dragHdl), tab._disposers.buttonDisposer?.()],
                 { fireImmediately: true });
             tab.reactComponents = [dragHdl];
             tab.element.append(dragHdl);
@@ -126,6 +119,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 });
         }
     }
+
     /**
      * Adds a document to the presentation view
      **/
@@ -154,13 +148,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             }
         }
     }
+
     /**
      * Adds a document to the presentation view
      **/
     @undoBatch
     @action
     public static UnpinDoc(doc: Doc) {
-        //add this new doc to props.Document
         const curPres = CurrentUserUtils.ActivePresentation;
         if (curPres) {
             const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
@@ -169,20 +163,18 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     }
 
     componentDidMount() {
-        const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white");
         const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document);
-        const updateTabColor = () => this.tab?.titleElement[0] && (this.tab.titleElement[0].style.backgroundColor = selected() ? color() : "");
-        const observer = new _global.ResizeObserver(action((entries: any) => {
+        new _global.ResizeObserver(action((entries: any) => {
             for (const entry of entries) {
                 this._panelWidth = entry.contentRect.width;
                 this._panelHeight = entry.contentRect.height;
             }
-            updateTabColor();
-        }));
-        observer.observe(this.props.glContainer._element[0]);
+        })).observe(this.props.glContainer._element[0]);
         this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
         this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged();
-        this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: color() }), updateTabColor, { fireImmediately: true });
+        this._tabReaction = reaction(() => ({ selected: selected(), color: this.tabColor, title: this.tab.titleElement[0] }),
+            ({ selected, color, title }) => title && (title.style.backgroundColor = selected ? color : ""),
+            { fireImmediately: true });
     }
 
     componentWillUnmount() {
@@ -196,30 +188,23 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             this._isActive = this.props.glContainer.tab.isActive;
             (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged();
             !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
-            // this._isActive && this.view && SelectionManager.SelectDoc(this.view, false);
         }
     }
 
-    get layoutDoc() { return this._document && Doc.Layout(this._document); }
     nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0;
     panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
         (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
     panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
     nativeWidth = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeWidth) || this._panelWidth : 0;
     nativeHeight = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeHeight) || this._panelHeight : 0;
-
     contentScaling = () => {
-        const nativeH = this.nativeHeight();
-        const nativeW = this.nativeWidth();
+        const nativeH = NumCast(this.layoutDoc?._nativeHeight);
+        const nativeW = NumCast(this.layoutDoc?._nativeWidth);
         let scaling = 1;
-        if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) {
-            scaling = 1;
-        } else if (NumCast(this.layoutDoc?._nativeWidth) && ((this.layoutDoc?._fitWidth) ||
-            this._panelHeight / NumCast(this.layoutDoc?._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc?._nativeWidth))) {
-            scaling = this._panelWidth / NumCast(this.layoutDoc?._nativeWidth);
+        if (nativeW && (this.layoutDoc?._fitWidth || this._panelHeight / nativeH > this._panelWidth / nativeW)) {
+            scaling = this._panelWidth / nativeW;  // width-limited or fitWidth
         } else if (nativeW && nativeH) {
-            const wscale = this.panelWidth() / nativeW;
-            scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+            scaling = this._panelHeight / nativeH; // height-limited
         }
         return scaling;
     }
@@ -234,6 +219,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     }
     @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; }
+    @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
 
     // adds a tab to the layout based on the locaiton parameter which can be:
     //  close[:{left,right,top,bottom}]  - e.g., "close" will close the tab, "close:left" will close the left tab, 
@@ -244,10 +230,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     //  inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
-        if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-        const locationFields = location.split(":");
+        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
         switch (locationFields[0]) {
+            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
@@ -257,35 +243,37 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         }
     }
 
-    @computed get renderContentBounds() {
+    @computed get tabColor() { return StrCast(this._document!._backgroundColor, StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))); }
+    @computed get renderBounds() {
         const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
         const xbounds = bounds[2] - bounds[0];
         const ybounds = bounds[3] - bounds[1];
         const dim = Math.max(xbounds, ybounds);
         return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
     }
-    @computed get miniLeft() { return 50 + (NumCast(this._document?._panX) - this.renderContentBounds.cx) / this.renderContentBounds.dim * 100 - this.miniWidth / 2; }
-    @computed get miniTop() { return 50 + (NumCast(this._document?._panY) - this.renderContentBounds.cy) / this.renderContentBounds.dim * 100 - this.miniHeight / 2; }
-    @computed get miniWidth() { return this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; }
-    @computed get miniHeight() { return this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; }
     childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null);
     returnMiniSize = () => NumCast(this._document?._miniMapSize, 150);
     miniDown = (e: React.PointerEvent) => {
-        this._document && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
-            this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim);
-            this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim);
+        const doc = this._document;
+        const miniSize = this.returnMiniSize();
+        doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+            doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * this.renderBounds.dim, this.renderBounds.l, this.renderBounds.l + this.renderBounds.dim);
+            doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * this.renderBounds.dim, this.renderBounds.t, this.renderBounds.t + this.renderBounds.dim);
             return false;
         }), emptyFunction, emptyFunction);
     }
-    getCurrentFrame = (): number => {
-        const presTargetDoc = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null);
-        return Cast(presTargetDoc._currentFrame, "number", null);
+    getCurrentFrame = () => {
+        return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame);
     }
-
     renderMiniMap() {
+        const miniWidth = this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderBounds.dim * 100;
+        const miniHeight = this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderBounds.dim * 100;
+        const miniLeft = 50 + (NumCast(this._document?._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2;
+        const miniTop = 50 + (NumCast(this._document?._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2;
+        const miniSize = this.returnMiniSize();
         return <>
             {this._document?.hideMinimap ? (null) :
-                <div className="miniMap" style={{ width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor, StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))), }}>
+                <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.tabColor }}>
                     <CollectionFreeFormView
                         Document={this._document!}
                         LibraryPath={emptyPath}
@@ -321,13 +309,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         fitToBox={true}
                     />
                     <div className="miniOverlay" onPointerDown={this.miniDown} >
-                        <div className="miniThumb" style={{
-                            width: `${this.miniWidth}% `,
-                            height: `${this.miniHeight}% `,
-                            left: `${this.miniLeft}% `,
-                            top: `${this.miniTop}% `,
-                        }}
-                        />
+                        <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
                     </div>
                 </div>}
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1cae12a9d..de48821af 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -372,12 +372,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    getClusterColor = (doc: Doc) => {
+    getClusterColor = (doc: Opt<Doc>) => {
         let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1);
-        const cluster = NumCast(doc.cluster);
+        const cluster = NumCast(doc?.cluster);
         if (this.Document._useClusters) {
             if (this._clusterSets.length <= cluster) {
-                setTimeout(() => this.updateCluster(doc), 0);
+                setTimeout(() => doc && this.updateCluster(doc), 0);
             } else {
                 // choose a cluster color from a palette
                 const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 966355e8f..3915ea4f6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -87,7 +87,7 @@ export interface DocumentViewProps {
     addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     backgroundHalo?: () => boolean;
-    backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
+    backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
     forcedBackgroundColor?: (doc: Doc) => string | undefined;
     opacity?: () => number | undefined;
     ChromeHeight?: () => number;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c7a1dfcb0..0d29e43b8 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -39,7 +39,7 @@ export interface FieldViewProps {
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc | Doc[]) => boolean;
     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    backgroundColor?: (document: Doc, renderDepth: number) => string | undefined;
+    backgroundColor?: (document: Opt<Doc>, renderDepth: number) => string | undefined;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     active: (outsideReaction?: boolean) => boolean;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 2d26c5c6d..89ea57f65 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -16,7 +16,7 @@ interface Props {
     linkDoc?: Doc;
     linkSrc?: Doc;
     href?: string;
-    backgroundColor: (doc: Doc, renderDepth: number) => string;
+    backgroundColor: (doc: Opt<Doc>, renderDepth: number) => string;
     addDocTab: (document: Doc, where: string) => boolean;
     location: number[];
 }
-- 
cgit v1.2.3-70-g09d2


From 19af1ce5d8216dd5eac3df0def1c059d7d8975ec Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Sep 2020 02:58:34 -0400
Subject: changed text box headers from a template to a title bar.

---
 src/client/documents/Documents.ts                  |  4 +--
 src/client/util/CurrentUserUtils.ts                | 35 +++++++++++++++-------
 src/client/util/SettingsManager.tsx                |  6 ++--
 src/client/util/SharingManager.tsx                 |  6 ++--
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/DocumentDecorations.tsx           | 18 +++++------
 src/client/views/GlobalKeyHandler.ts               |  2 +-
 src/client/views/PreviewCursor.tsx                 | 11 ++-----
 .../collections/CollectionMasonryViewFieldRow.tsx  |  2 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +--
 .../CollectionStackingViewFieldColumn.tsx          |  4 +--
 src/client/views/collections/CollectionSubView.tsx |  4 +--
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/SchemaTable.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 17 ++---------
 .../collectionGrid/CollectionGridView.tsx          |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  8 ++---
 src/client/views/nodes/DocumentView.scss           |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 35 +++++++++++++---------
 src/client/views/nodes/WebBox.tsx                  | 11 ++-----
 .../views/nodes/formattedText/DashFieldView.tsx    |  4 +--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  7 +++--
 src/client/views/pdf/PDFViewer.tsx                 |  7 +++--
 24 files changed, 97 insertions(+), 100 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 36834d2df..586c5f67c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1012,7 +1012,7 @@ export namespace DocUtils {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
             layout = CollectionView.LayoutString;
         } else {
-            created = Docs.Create.TextDocument("", { ...{ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, _height: 25, _autoHeight: true }, ...resolved });
+            created = Docs.Create.TextDocument("", { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
             layout = FormattedTextBox.LayoutString;
         }
         if (created) {
@@ -1072,7 +1072,7 @@ export namespace DocUtils {
                 description: ":" + StrCast(note.title),
                 event: undoBatch((args: { x: number, y: number }) => {
                     const textDoc = Docs.Create.TextDocument("", {
-                        _width: 200, x, y, _autoHeight: note._autoHeight !== false, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
+                        _width: 200, x, y, _autoHeight: note._autoHeight !== false,
                         title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1)
                     });
                     textDoc.layoutKey = "layout_" + note.title;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2e7fd1b21..c9c369fba 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -271,8 +271,7 @@ export class CurrentUserUtils {
         }
 
         if (doc["template-notes"] === undefined) {
-            doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-note-Note"] as any as Doc,
-            doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc, doc["template-note-Todo"] as any as Doc],
+            doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc], // doc["template-note-Todo"] as any as Doc],
                 { title: "Note Layouts", _height: 75, system: true }));
         } else {
             const curNoteTypes = Cast(doc["template-notes"], Doc, null);
@@ -943,7 +942,7 @@ export class CurrentUserUtils {
         doc.system = true;
         doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
         doc.title = Doc.CurrentUserEmail;
-        doc.userColor = "orange";
+        doc.userColor = doc.userColor || "#12121233";
         doc._raiseWhenDragged = true;
         doc.activeInkPen = doc;
         doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
@@ -984,14 +983,14 @@ export class CurrentUserUtils {
         doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
 
         // uncomment this to setup a default note style that uses the custom header layout
-        PromiseValue(doc.emptyHeader).then(factory => {
-            if (Cast(doc.defaultTextLayout, Doc, null)?.version !== headerViewVersion) {
-                const deleg = Doc.delegateDragFactory(factory as Doc);
-                deleg.title = "header";
-                doc.defaultTextLayout = new PrefetchProxy(deleg);
-                Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg);
-            }
-        });
+        // PromiseValue(doc.emptyHeader).then(factory => {
+        //     if (Cast(doc.defaultTextLayout, Doc, null)?.version !== headerViewVersion) {
+        //         const deleg = Doc.delegateDragFactory(factory as Doc);
+        //         deleg.title = "header";
+        //         doc.defaultTextLayout = new PrefetchProxy(deleg);
+        //         Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg);
+        //     }
+        // });
         return doc;
     }
 
@@ -1128,6 +1127,20 @@ export class CurrentUserUtils {
         CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
 
+    public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number) {
+        const tbox = Docs.Create.TextDocument("", {
+            _width: width || 200, _height: height || 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
+            _fontFamily: StrCast(Doc.UserDoc().fontFamily), title
+        });
+        const template = FormattedTextBox.DefaultLayout;
+        if (template instanceof Doc) {
+            tbox._width = NumCast(template._width);
+            tbox.layoutKey = "layout_" + StrCast(template.title);
+            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
+        }
+        return tbox;
+    }
+
     public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
     public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
     public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 5b8fe3576..fc8bc382e 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -52,7 +52,7 @@ export class SettingsManager extends React.Component<{}> {
     }
 
     @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
-    @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value);
+    @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined);
     @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
     @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
     @undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
@@ -109,8 +109,8 @@ export class SettingsManager extends React.Component<{}> {
                     {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
                 </select>
                 <div>
-                    <div className="preferences-check">Show title</div>
-                    <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = !Doc.UserDoc().showTitle} checked={BoolCast(Doc.UserDoc().showTitle)} />
+                    <div className="preferences-check">Show header</div>
+                    <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
                 </div>
                 <div>
                     <div className="preferences-check">Alt Buttons</div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6fdbac41b..de50deee7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -51,6 +51,7 @@ const storage = "data";
 interface ValidatedUser {
     user: User;
     notificationDoc: Doc;
+    userColor: string;
 }
 
 
@@ -58,7 +59,7 @@ interface ValidatedUser {
 export class SharingManager extends React.Component<{}> {
     public static Instance: SharingManager;
     @observable private isOpen = false; // whether the SharingManager modal is open or not
-    @observable private users: ValidatedUser[] = []; // the list of users with notificationDocs
+    @observable public users: ValidatedUser[] = []; // the list of users with notificationDocs
     @observable private targetDoc: Doc | undefined; // the document being shared
     @observable private targetDocView: DocumentView | undefined; // the DocumentView of the document being shared
     // @observable private copied = false;
@@ -129,9 +130,10 @@ export class SharingManager extends React.Component<{}> {
                     const userDocument = await DocServer.GetRefField(user.userDocumentId);
                     if (userDocument instanceof Doc) {
                         const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
+                        const userColor = StrCast(userDocument.userColor);
                         runInAction(() => {
                             if (notificationDoc instanceof Doc) {
-                                this.users.push({ user, notificationDoc });
+                                this.users.push({ user, notificationDoc, userColor });
                             }
                         });
                     }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 7629ed173..979b1198a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -308,7 +308,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     render() {
         if (!this.view0) return (null);
 
-        const isText = this.view0.props.Document[Doc.LayoutFieldKey(this.view0.props.Document)] instanceof RichTextField;
+        const isText = this.view0.props.Document[this.view0.LayoutFieldKey] instanceof RichTextField;
         const considerPull = isText && this.considerGoogleDocsPull;
         const considerPush = isText && this.considerGoogleDocsPush;
         return <div className="documentButtonBar">
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e4e27bec7..108b896a5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -434,21 +434,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
-            if (e.ctrlKey && !element.props.Document._nativeHeight) element.toggleNativeDimensions();
+        SelectionManager.SelectedDocuments().forEach(action((docView: DocumentView) => {
+            if (e.ctrlKey && !docView.props.Document._nativeHeight) docView.toggleNativeDimensions();
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = Document(element.rootDoc);
-                let nwidth = returnVal(element.NativeWidth?.(), doc._nativeWidth);
-                let nheight = returnVal(element.NativeHeight?.(), doc._nativeHeight);
+                const doc = Document(docView.rootDoc);
+                let nwidth = returnVal(docView.NativeWidth?.(), doc._nativeWidth);
+                let nheight = returnVal(docView.NativeHeight?.(), doc._nativeHeight);
                 const width = (doc._width || 0);
                 let height = (doc._height || (nheight / nwidth * width));
                 height = !height || isNaN(height) ? 20 : height;
-                const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
+                const scale = docView.props.ScreenToLocalTransform().Scale * docView.props.ContentScaling();
                 if (nwidth && nheight) {
                     if (nwidth / nheight !== width / height) {
                         height = nheight / nwidth * width;
                     }
-                    if (e.ctrlKey || (!dragBottom || !element.layoutDoc._fitWidth)) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
+                    if (e.ctrlKey || (!dragBottom || !docView.layoutDoc._fitWidth)) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
                         if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
                         else dW = dH * nwidth / nheight;
                     }
@@ -458,7 +458,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = (nwidth && nheight);
-                const fieldKey = Doc.LayoutFieldKey(doc);
+                const fieldKey = docView.LayoutFieldKey;
                 if (fixedAspect && (!nwidth || !nheight)) {
                     doc[DataSym][fieldKey + "-nativeWidth"] = doc._nativeWidth = nwidth = doc._width || 0;
                     doc[DataSym][fieldKey + "-nativeHeight"] = doc._nativeHeight = nheight = doc._height || 0;
@@ -484,7 +484,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                         else if (!fixedAspect || !e.ctrlKey) doc._height = actualdH;
                     }
                     else {
-                        if (!fixedAspect || (dragBottom && (e.ctrlKey || element.layoutDoc._fitWidth))) {
+                        if (!fixedAspect || (dragBottom && (e.ctrlKey || docView.layoutDoc._fitWidth))) {
                             doc[DataSym][fieldKey + "-nativeHeight"] = doc._nativeHeight = actualdH / (doc._height || 1) * (doc._nativeHeight || 0);
                         }
                         doc._height = actualdH;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index b231b2171..ba9334c40 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -257,7 +257,7 @@ export class KeyManager {
                 let count = 1;
                 const list: Doc[] = [];
                 const targetDataDoc = Doc.GetProto(first.props.Document);
-                const fieldKey = Doc.LayoutFieldKey(first.props.Document);
+                const fieldKey = first.LayoutFieldKey;
                 const docList = DocListCast(targetDataDoc[fieldKey]);
                 docids.map((did, i) => i && DocServer.GetRefField(did).then(async doc => {
                     count++;
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index ee5a390a7..f5f115e01 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -15,6 +15,7 @@ import { Utils } from '../../Utils';
 import { Networking } from '../Network';
 import { Upload } from '../../server/SharedMediaTypes';
 import { basename } from 'path';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
@@ -82,15 +83,7 @@ export class PreviewCursor extends React.Component<{}> {
                 else {
                     // creates text document
                     FormattedTextBox.PasteOnLoad = e;
-                    undoBatch(() => PreviewCursor._addLiveTextDoc(Docs.Create.TextDocument("", {
-                        _width: 500,
-                        limitHeight: 400,
-                        _autoHeight: true,
-                        _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
-                        x: newPoint[0],
-                        y: newPoint[1],
-                        title: "-pasted text-"
-                    })))();
+                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(CurrentUserUtils.GetNewTextDoc("-pasted text-", newPoint[0], newPoint[1], 500)), "paste");
                 }
             } else
                 //pasting in images
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index cb7ffb0e7..7014966c7 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -141,7 +141,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     addDocument = (value: string, shiftDown?: boolean) => {
         this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document._pivotField);
-        const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value });
+        const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _width: 200, title: value });
         const onLayoutDoc = this.onLayoutDoc(key);
         (onLayoutDoc ? newDoc : newDoc[DataSym])[key] = this.getValue(this.props.heading);
         const docs = this.props.parent.childDocList;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 0f8a5df0d..ff45cf71b 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -98,7 +98,7 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this.getElement(!this.SelectedCollection ? [button] :
             [<CollectionViewBaseChrome key="chrome"
                 docView={this.SelectedCollection}
-                fieldKey={Doc.LayoutFieldKey(this.SelectedCollection?.props.Document)}
+                fieldKey={this.SelectedCollection.LayoutFieldKey}
                 type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
                 prop,
                 button]);
@@ -521,7 +521,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
     }
     get document() { return this.props.docView.props.Document; }
     @computed get dataField() {
-        return this.document[Doc.LayoutFieldKey(this.document)];
+        return this.document[this.props.docView.LayoutFieldKey];
     }
     @computed get childDocs() {
         return DocListCast(this.dataField);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index fd2ae03d8..44d264b36 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -132,7 +132,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     addDocument = (value: string, shiftDown?: boolean) => {
         if (!value) return false;
         const key = StrCast(this.props.parent.props.Document._pivotField);
-        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true });
         newDoc[key] = this.getValue(this.props.heading);
         const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
         const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
@@ -265,7 +265,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
         ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
             Doc.GetProto(this.props.parent.props.Document)[name] = "";
-            const created = Docs.Create.TextDocument("", { title: name, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
             if (created) {
                 if (this.props.parent.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index dc1d5a8dd..d9c4d274a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -290,7 +290,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                             this.addDocument(Docs.Create.WebDocument(href, { ...options, _fitWidth: true, title: href }));
                         }
                     } else if (text) {
-                        this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 25 }));
+                        this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: StrCast(Doc.UserDoc().showTitle), _width: 100, _height: 25 }));
                     }
                     return;
                 }
@@ -470,7 +470,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 completed?.();
             } else {
                 if (text && !text.includes("https://")) {
-                    UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop");
+                    UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop");
                 }
             }
             disposer();
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 61c8f580d..e8944a5eb 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -533,7 +533,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionDoc={this.props.containingCollection}
             />;
         return <>
-            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title"
+            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 854254195..13aa60e7c 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -376,7 +376,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @undoBatch
     createRow = action(() => {
-        this.props.addDocument(Docs.Create.TextDocument("", { title: "", _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 30 }));
+        this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
         this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
     });
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 85641531d..24c76e88e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -949,7 +949,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate,
             LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString,
             FreezeDimensions: this.props.freezeChildDimensions,
-            layoutKey: undefined,
+            layoutKey: StrCast(this.props.Document.childLayoutKey),
             setupDragLines: this.setupDragLines,
             dontRegisterView: this.props.dontRegisterView,
             rootSelected: childData ? this.rootSelected : returnFalse,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 6436e74e9..4f2399962 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,6 +24,7 @@ import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
 import { Id } from "../../../../fields/FieldSymbols";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -138,20 +139,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 e.stopPropagation();
             } else if (!e.ctrlKey && !e.metaKey) {
                 FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.ChildLayoutString ? e.key : "";
-                const tbox = Docs.Create.TextDocument("", {
-                    _width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
-                    _fontFamily: StrCast(Doc.UserDoc().fontFamily),
-                    _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
-                    title: "-typed text-"
-                });
-                const template = FormattedTextBox.DefaultLayout;
-                if (template instanceof Doc) {
-                    tbox._width = NumCast(template._width);
-                    tbox.layoutKey = "layout_" + StrCast(template.title);
-                    Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
-                }
                 FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
-                this.props.addLiveTextDocument(tbox);
+                this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100));
                 e.stopPropagation();
             }
     }
@@ -540,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.page = -1;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
         const portal = Doc.MakeAlias(summary);
         Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected);
         Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations");
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index ba9c02b5c..d31427829 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -302,7 +302,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                 (e: PointerEvent, doubleTap?: boolean) => {
                     if (doubleTap) {
                         undoBatch(action(() => {
-                            const text = Docs.Create.TextDocument("", { _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 150, _height: 50 });
+                            const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 });
                             FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
                             Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
                             this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 88f6a65be..cba65f663 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -26,6 +26,7 @@ import { Scripting } from "../../util/Scripting";
 import Waveform from "react-audio-waveform";
 import axios from "axios";
 import { SnappingManager } from "../../util/SnappingManager";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 
 declare class MediaRecorder {
     // whatever MediaRecorder has
@@ -292,11 +293,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
 
     // creates a text document for dictation
     onFile = (e: any) => {
-        const newDoc = Docs.Create.TextDocument("", {
-            _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: "", _chromeStatus: "disabled",
-            x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
-            _width: NumCast(this.props.Document._width), _height: 2 * NumCast(this.props.Document._height)
-        });
+        const newDoc = CurrentUserUtils.GetNewTextDoc("", NumCast(this.props.Document.x), NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
+            NumCast(this.props.Document._width), 2 * NumCast(this.props.Document._height));
         Doc.GetProto(newDoc).recordingSource = this.dataDoc;
         Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
         Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("self.recordingSource.audioState");
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 430a1aa45..c31172e22 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -110,7 +110,7 @@
             transform-origin: top left;
             top: 0;
             width: 100%;
-            height: 25;
+            height: 14;
             background: rgba(0, 0, 0, .4);
             text-align: center;
             text-overflow: ellipsis;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5ca57a193..0b4f45dd6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -118,10 +118,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
+    public get LayoutFieldKey() { return StrCast(this.props.layoutKey, Doc.LayoutFieldKey(this.layoutDoc)); }
+    @computed get ShowTitle() {
+        return StrCast(this.layoutDoc._showTitle,
+            !Doc.IsSystem(this.layoutDoc) && this.rootDoc.type === DocumentType.RTF && !this.props.treeViewDoc ?
+                (this.dataDoc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") :
+                undefined);
+    }
     @computed get topMost() { return this.props.renderDepth === 0; }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
-    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
-    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
     @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
     @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@@ -737,8 +744,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this.props.bringToFront(this.props.Document, true);
             const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
             const hgt = this.Document[HeightSym]();
-            this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = wid;
-            this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeHeight"] = hgt;
+            this.props.Document[DataSym][this.LayoutFieldKey + "-nativeWidth"] = wid;
+            this.props.Document[DataSym][this.LayoutFieldKey + "-nativeHeight"] = hgt;
         }
     }
 
@@ -887,8 +894,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
 
     chromeHeight = () => {
-        const showTitle = StrCast(this.layoutDoc._showTitle);
-        const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
+        const excluded = ["PresBox", "FormattedTextBox", "FontIconBox"];
+        const showTextTitle = this.ShowTitle && !excluded.includes(StrCast(this.layoutDoc.layout)) ? this.ShowTitle : undefined;
         return showTextTitle ? 25 : 1;
     }
 
@@ -1014,10 +1021,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             </div>;
         }
 
-        const showTitle = StrCast(this.layoutDoc._showTitle);
         const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
         const showCaption = StrCast(this.layoutDoc._showCaption);
-        const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
+        const showTextTitle = this.ShowTitle && this.rootDoc.type === DocumentType.RTF ? this.ShowTitle : undefined;
         const captionView = (!showCaption ? (null) :
             <div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
                 <DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
@@ -1033,19 +1039,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     onClick={this.onClickFunc}
                     layoutKey={this.finalLayoutKey} />
             </div>);
-        const titleView = (!showTitle ? (null) :
+        const titleView = (!this.ShowTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
                 position: showTextTitle ? "relative" : "absolute",
+                background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.UserDoc().userColor) : "rgba(0,0,0,0.4)"),
                 pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
             }}>
                 <EditableView ref={this._titleRef}
-                    contents={(this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
-                    display={"block"} height={72} fontSize={12}
-                    GetValue={() => (this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
-                    SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)}
+                    contents={this.ShowTitle.split(";").map(field => field + ":" + (this.props.DataDoc || this.props.Document)[field]?.toString()).join(" ")}
+                    display={"block"} fontSize={10}
+                    GetValue={() => ""}
+                    SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[this.ShowTitle] = value) ? true : true)}
                 />
             </div>);
-        return !showTitle && !showCaption ?
+        return !this.ShowTitle && !showCaption ?
             this.contents :
             <div className="documentView-styleWrapper" >
                 {this.Document.type !== DocumentType.RTF ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 41622dbc9..dac6d3bf8 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -32,6 +32,7 @@ import "./WebBox.scss";
 import "../pdf/PDFViewer.scss";
 import React = require("react");
 import { Tooltip } from '@material-ui/core';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -498,15 +499,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         e.preventDefault();
         e.stopPropagation();
 
-        const clipDoc = Doc.MakeAlias(this.dataDoc);
-        clipDoc._fitWidth = true;
-        clipDoc._width = this.marqueeWidth();
-        clipDoc._height = this.marqueeHeight();
-        clipDoc._scrollTop = this.marqueeY();
-        const targetDoc = Docs.Create.TextDocument("", { _width: 125, _height: 125, title: "Note linked to " + this.props.Document.title });
-        Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
-        clipDoc.rootDocument = targetDoc;
-        targetDoc.layoutKey = "layout";
+        const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 125, 125);
         const annotationDoc = this.highlight("rgba(173, 216, 230, 0.35)"); // hyperlink color
         if (annotationDoc) {
             DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 0332bb4fd..a4114ed2c 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,5 +1,5 @@
-import { IReactionDisposer, observable, runInAction, computed, action } from "mobx";
-import { Doc, DocListCast, Field, LayoutSym } from "../../../../fields/Doc";
+import { IReactionDisposer, observable, computed, action } from "mobx";
+import { Doc, DocListCast, Field } from "../../../../fields/Doc";
 import { List } from "../../../../fields/List";
 import { listSpec } from "../../../../fields/Schema";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c0c3bdd80..1b7346e4d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -15,7 +15,7 @@ import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, Wid
 import { documentSchema } from '../../../../fields/documentSchemas';
 import applyDevTools = require("prosemirror-dev-tools");
 import { removeMarkWithAttrs } from "./prosemirrorPatches";
-import { Id } from '../../../../fields/FieldSymbols';
+import { Id, Copy } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from "../../../../fields/RichTextField";
@@ -577,6 +577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const cm = ContextMenu.Instance;
 
         const changeItems: ContextMenuProps[] = [];
+        changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
         const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
         DocListCast(noteTypesDoc?.data).forEach(note => {
             changeItems.push({
@@ -586,7 +587,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }), icon: "eye"
             });
         });
-        changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
         !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
         const highlighting: ContextMenuProps[] = [];
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
@@ -618,7 +618,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        !Doc.UserDoc().noviceMode && appearanceItems.push({ description: "Reset default note style", event: () => this.rootDoc.layoutKey = "layout", icon: "eye" });
         !Doc.UserDoc().noviceMode && appearanceItems.push({
             description: "Make Default Layout", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
@@ -639,6 +638,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         this.rootDoc._width = this.layoutDoc._width || 300;  // are stored on the template, since we're getting rid of the old template
                         this.rootDoc._height = this.layoutDoc._height || 200;  // we need to copy them over to the root.  This should probably apply to all '_' fields
                         this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
+                        this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, "string", null);
+                        this.rootDoc.header = Cast(this.layoutDoc.header, RichTextField, null)?.[Copy]();
                     }, 10);
                 }
                 Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 45a862954..6973b073c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -34,6 +34,7 @@ const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
 import React = require("react");
 import { LinkDocPreview } from "../nodes/LinkDocPreview";
 import { FormattedTextBoxComment } from "../nodes/formattedText/FormattedTextBoxComment";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 const _global = (window /* browser */ || global /* node */) as any;
@@ -621,11 +622,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         clipDoc._width = this.marqueeWidth();
         clipDoc._height = this.marqueeHeight();
         clipDoc._scrollTop = this.marqueeY();
-        const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
+        const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 100, 100);
         Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
         clipDoc.rootDocument = targetDoc;
-        DocUtils.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
-        targetDoc.layoutKey = "layout";
+        // DocUtils.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
+        // targetDoc.layoutKey = "layout";
         // const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
         // Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
         // const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
-- 
cgit v1.2.3-70-g09d2


From 04ab7a6a6d206014f99b46a6a8771c205cdb079a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Sep 2020 12:00:15 -0400
Subject: restored template button option for customizing layout but added a
 Full Toolbar mode to enable it.

---
 src/client/util/SettingsManager.tsx                |  6 ++--
 src/client/views/ContextMenu.tsx                   |  2 +-
 src/client/views/DocumentButtonBar.tsx             | 38 +++++++++++---------
 src/client/views/TemplateMenu.tsx                  | 28 +++++++--------
 src/client/views/Templates.tsx                     | 41 ----------------------
 .../views/collections/CollectionTreeView.tsx       |  3 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 .../formattedText/FormattedTextBoxComment.tsx      |  2 +-
 8 files changed, 42 insertions(+), 81 deletions(-)
 delete mode 100644 src/client/views/Templates.tsx

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

diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index fc8bc382e..17e93ad17 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -113,9 +113,9 @@ export class SettingsManager extends React.Component<{}> {
                     <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
                 </div>
                 <div>
-                    <div className="preferences-check">Alt Buttons</div>
-                    <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"]}
-                        checked={BoolCast(Doc.UserDoc()["documentLinksButton-hideEnd"])} />
+                    <div className="preferences-check">Full Toolbar</div>
+                    <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-fullMenu"] = !Doc.UserDoc()["documentLinksButton-fullMenu"]}
+                        checked={BoolCast(Doc.UserDoc()["documentLinksButton-fullMenu"])} />
                 </div>
                 <div>
                     <div className="preferences-check">Raise on drag</div>
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 349fd077c..1f7abf61a 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -233,7 +233,7 @@ export class ContextMenu extends React.Component {
                     <span className="icon-background">
                         <FontAwesomeIcon icon="search" size="lg" />
                     </span>
-                    <input className="contextMenu-item contextMenu-description search" type="text" placeholder="Search Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
+                    <input className="contextMenu-item contextMenu-description search" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
                 </span>
                 {this.menuItems}
             </>
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 979b1198a..b155e33ff 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -23,7 +23,6 @@ import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
 import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
 import { TemplateMenu } from "./TemplateMenu";
-import { Template, Templates } from "./Templates";
 import React = require("react");
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -255,9 +254,11 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             </div></Tooltip>;
     }
     @observable _aliasDown = false;
-    onAliasButtonDown = (e: React.PointerEvent): void => {
+    onAliasButtonDown = action((e: React.PointerEvent): void => {
+        this.props.views()[0]?.select(false);
+        this._tooltipOpen = false;
         setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
-    }
+    })
     onAliasButtonMoved = () => {
         if (this._dragRef.current) {
             const dragDocView = this.view0!;
@@ -274,23 +275,28 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         return false;
     }
 
+    _ref = React.createRef<HTMLDivElement>();
+    @observable _tooltipOpen: boolean = false;
     @computed
     get templateButton() {
         const view0 = this.view0;
-        const templates: Map<Template, boolean> = new Map();
+        const templates: Map<string, boolean> = new Map();
         const views = this.props.views();
-        Array.from(Object.values(Templates.TemplateList)).map(template =>
-            templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean)));
+        Array.from(["Caption", "Title", "TitleHover"]).map(template =>
+            templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template] ? true : false, false as boolean)));
         return !view0 ? (null) :
-            <Tooltip title={<><div className="dash-tooltip">Tap: Customize layout.  Drag: Create alias</div></>}>
-                <div className="documentButtonBar-linkFlyout" ref={this._dragRef}>
+            <Tooltip title={<div className="dash-tooltip">CustomizeLayout</div>} open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} placement="bottom">
+                <div className="documentButtonBar-linkFlyout" ref={this._dragRef}
+                    onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))} >
+
                     <Flyout anchorPoint={anchorPoints.LEFT_TOP} onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
-                        content={!this._aliasDown ? (null) : <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
+                        content={!this._aliasDown ? (null) : <div ref={this._ref}> <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} /></div>}>
                         <div className={"documentButtonBar-linkButton-empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} >
                             {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
                         </div>
                     </Flyout>
-                </div></Tooltip>;
+                </div>
+            </Tooltip>;
     }
 
     openContextMenu = (e: React.MouseEvent) => {
@@ -315,13 +321,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 <DocumentLinksButton links={this.view0.allLinks} View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
             </div>
-            {DocumentLinksButton.StartLink || !Doc.UserDoc()["documentLinksButton-hideEnd"] ? <div className="documentButtonBar-button">
+            {DocumentLinksButton.StartLink || !Doc.UserDoc()["documentLinksButton-fullMenu"] ? <div className="documentButtonBar-button">
                 <DocumentLinksButton links={this.view0.allLinks} View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
             </div> : (null)}
-            {/* <div className="documentButtonBar-button">
+            {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.templateButton}
             </div>
-            <div className="documentButtonBar-button">
+            /*<div className="documentButtonBar-button">
                 {this.metadataButton}
             </div>
             <div className="documentButtonBar-button">
@@ -330,7 +336,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
-            {!Doc.UserDoc()["documentLinksButton-hideEnd"] ? (null) : <div className="documentButtonBar-button">
+            {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
             </div>}
             {![DocumentType.VID, DocumentType.WEB].includes(StrCast(this.view0.props.Document.type) as DocumentType) ? (null) : <div className="documentButtonBar-button">
@@ -345,9 +351,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.menuButton}
             </div>
-            <div className="documentButtonBar-button">
+            {/* {Doc.UserDoc().noviceMode ? (null) : <div className="documentButtonBar-button">
                 {this.moreButton}
-            </div>
+            </div>} */}
         </div>;
     }
 }
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index c1878115d..cf2118cb2 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,10 +1,8 @@
 import { action, observable, runInAction, ObservableSet, trace, computed } from "mobx";
 import { observer } from "mobx-react";
-import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch } from "../util/UndoManager";
 import './TemplateMenu.scss';
 import { DocumentView } from "./nodes/DocumentView";
-import { Template } from "./Templates";
 import React = require("react");
 import { Doc, DocListCast } from "../../fields/Doc";
 import { Docs, DocUtils, } from "../documents/Documents";
@@ -18,13 +16,13 @@ import { List } from "../../fields/List";
 import { TraceMobx } from "../../fields/util";
 
 @observer
-class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
+class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
     render() {
         if (this.props.template) {
             return (
                 <li className="templateToggle">
                     <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
-                    {this.props.template.Name}
+                    {this.props.template}
                 </li>
             );
         } else {
@@ -46,7 +44,7 @@ class OtherToggle extends React.Component<{ checked: boolean, name: string, togg
 
 export interface TemplateMenuProps {
     docViews: DocumentView[];
-    templates: Map<Template, boolean>;
+    templates: Map<string, boolean>;
 }
 
 
@@ -69,8 +67,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     @undoBatch
     @action
-    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        this.props.docViews.forEach(d => Doc.Layout(d.layoutDoc)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : "");
+    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: string): void => {
+        this.props.docViews.forEach(d => Doc.Layout(d.layoutDoc)["_show" + template] = event.target.checked ? template.toLowerCase() : "");
     }
 
     @action
@@ -113,21 +111,21 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const firstDoc = this.props.docViews[0].props.Document;
         const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", "");
         const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
-        const addedTypes = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
+        const addedTypes = Doc.UserDoc().noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
         const layout = Doc.Layout(firstDoc);
         const templateMenu: Array<JSX.Element> = [];
-        //this.props.templates.forEach((checked, template) =>
-        //    templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
-        //templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
-        templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
+        this.props.templates.forEach((checked, template) =>
+            templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
+        templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
         templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
+        !Doc.UserDoc().noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
         addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
         this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
             <OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
         return <ul className="template-list" style={{ display: "block" }}>
-            <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />
+            {Doc.UserDoc().noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
             {templateMenu}
-            <CollectionTreeView
+            {Doc.UserDoc().noviceMode ? (null) : <CollectionTreeView
                 Document={Doc.UserDoc().templateDocs as Doc}
                 CollectionView={undefined}
                 ContainingCollectionDoc={undefined}
@@ -158,7 +156,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 fieldKey={"data"}
                 moveDocument={returnFalse}
                 removeDocument={returnFalse}
-                addDocument={returnFalse} />
+                addDocument={returnFalse} />}
         </ul>;
     }
 }
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
deleted file mode 100644
index a6dbaa650..000000000
--- a/src/client/views/Templates.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-export class Template {
-    constructor(name: string, layout: string) {
-        this._name = name;
-        this._layout = layout;
-    }
-
-    private _name: string;
-    private _layout: string;
-
-    get Name(): string {
-        return this._name;
-    }
-
-    get Layout(): string {
-        return this._layout;
-    }
-}
-
-export namespace Templates {
-    export const Caption = new Template("Caption",
-        `<div>
-            <div style="height:100%; width:100%;">{layout}</div>
-            <div style="bottom: 0; font-size:14px; width:100%; position:absolute">
-                <FormattedTextBox {...props} fieldKey={"caption"} hideOnLeave={"true"} />
-            </div>
-        </div>` );
-
-    export const Title = new Template("Title",
-        `<div>
-            <div style="height:25px; width:100%; background-color: rgba(0, 0, 0, .4); color: white; z-index: 100">
-                <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
-            </div>
-            <div style="height:calc(100% - 25px);">
-                <div style="width:100%;overflow:auto">{layout}</div>
-            </div>
-        </div>` );
-    export const TitleHover = new Template("TitleHover", Title.Layout);
-
-    export const TemplateList: Template[] = [Title, TitleHover, Caption];
-}
-
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e8944a5eb..19b8400c8 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -22,7 +22,6 @@ import { EditableView } from "../EditableView";
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import { DocumentView } from '../nodes/DocumentView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
-import { Templates } from '../Templates';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { CollectionViewType, CollectionView } from './CollectionView';
@@ -185,7 +184,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0b4f45dd6..7ff8b635c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -118,7 +118,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
-    public get LayoutFieldKey() { return StrCast(this.props.layoutKey, Doc.LayoutFieldKey(this.layoutDoc)); }
+    public get LayoutFieldKey() { return this.props.layoutKey || Doc.LayoutFieldKey(this.layoutDoc); }
     @computed get ShowTitle() {
         return StrCast(this.layoutDoc._showTitle,
             !Doc.IsSystem(this.layoutDoc) && this.rootDoc.type === DocumentType.RTF && !this.props.treeViewDoc ?
@@ -855,7 +855,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
                 }
                 moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
-                Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc().myUserDoc, Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
             }
         }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 47150c51a..f015d329c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -110,7 +110,7 @@ export class FormattedTextBoxComment {
                         if (FormattedTextBoxComment._deleteRef?.contains(e.target as any)) {
                             this.deleteLink();
                         } else if (FormattedTextBoxComment._nextRef?.contains(e.target as any)) {
-                            FormattedTextBoxComment.showPreview(FormattedTextBoxComment._lastView!, FormattedTextBoxComment._lastState, FormattedTextBoxComment._hrefs?.[(++FormattedTextBoxComment._hrefInd) % FormattedTextBoxComment._hrefs?.length])
+                            FormattedTextBoxComment.showPreview(FormattedTextBoxComment._lastView!, FormattedTextBoxComment._lastState, FormattedTextBoxComment._hrefs?.[(++FormattedTextBoxComment._hrefInd) % FormattedTextBoxComment._hrefs?.length]);
                         } else {
                             FormattedTextBoxComment.linkDoc = undefined;
                             if (linkDoc.type !== DocumentType.LINK) {
-- 
cgit v1.2.3-70-g09d2


From f62fb0f5f90eee7c946deaa3a6e856b76e88f9f1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Sep 2020 13:17:07 -0400
Subject: cleaned up warnings and split collection tree view into Tree View a

---
 src/Utils.ts                                       |   3 +-
 src/client/views/DocumentButtonBar.tsx             |   4 +-
 src/client/views/EditableView.tsx                  |   2 +-
 .../views/collections/CollectionTreeView.scss      | 112 ---
 .../views/collections/CollectionTreeView.tsx       | 810 +--------------------
 src/client/views/collections/TreeView.scss         | 115 +++
 src/client/views/collections/TreeView.tsx          | 760 +++++++++++++++++++
 src/client/views/nodes/FilterBox.tsx               |  10 +
 src/client/views/nodes/PresBox.tsx                 |  27 +-
 .../views/presentationview/PresElementBox.tsx      |   2 +-
 10 files changed, 921 insertions(+), 924 deletions(-)
 create mode 100644 src/client/views/collections/TreeView.scss
 create mode 100644 src/client/views/collections/TreeView.tsx

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 7dff1ac55..cc7ee9537 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -518,7 +518,8 @@ export function clearStyleSheetRules(sheet: any) {
     return false;
 }
 
-export function simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number, rightClick = true) {
+export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) {
+    if (!element) return;
     ["pointerdown", "pointerup"].map(event => element.dispatchEvent(
         new PointerEvent(event, {
             view: window,
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 575ebaf3b..c5fd3c777 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -184,7 +184,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     @computed
     get pinButton() {
         const targetDoc = this.view0?.props.Document;
-        let isPinned = targetDoc && Doc.isDocPinned(targetDoc);
+        const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
         return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin to presentation"}</div></>}>
             <div className="documentButtonBar-linker"
                 style={{ color: "white" }}
@@ -276,7 +276,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this.props.views()[0]?.select(false);
         this._tooltipOpen = false;
         setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
-    })
+    });
     onAliasButtonMoved = () => {
         if (this._dragRef.current) {
             const dragDocView = this.view0!;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index cbbd78a20..d35271ffd 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -204,7 +204,7 @@ export class EditableView extends React.Component<EditableProps> {
         setTimeout(() => this.props.autosuggestProps?.resetValue(), 0);
         return this.props.contents instanceof ObjectField ? (null) :
             <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`} ref={this._ref}
-                style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
+                style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: this.props.height || "auto", maxHeight: this.props.maxHeight }}
                 onClick={this.onClick} placeholder={this.props.placeholder}>
                 <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{
                     this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index aaf17153a..20bfc0e9d 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -30,24 +30,6 @@
         padding-left: 0;
     }
 
-    .outline-bullet {
-        position: relative;
-        width: 15px;
-        color: $intermediate-color;
-        transform: scale(0.5);
-        display: inline-block;
-    }
-
-    .bullet {
-        position: relative;
-        width: 15px;
-        color: $intermediate-color;
-        margin-top: 3px;
-        transform: scale(1.3, 1.3);
-        border: #80808030 1px solid;
-        border-radius: 4px;
-    }
-
     .editableView-container {
         font-weight: bold;
     }
@@ -101,98 +83,4 @@
     padding-left: 3px;
     padding-right: 3px;
     padding-bottom: 2px;
-}
-
-.treeViewItem-container-active {
-    z-index: 100;
-    position: relative;;
-    .formattedTextbox-sidebar {
-        background-color: #ffff001f !important;
-        height: 500px !important;
-    }
-}
-
-.treeViewItem-openRight {
-    display: none;
-    height: 17px;
-    width: 15px;
-}
-
-.treeViewItem-openRight:hover {
-    background: #797777;
-    cursor: pointer;
-}
-
-.treeViewItem-border-outline,
-.treeViewItem-border {
-    display: flex;
-    overflow: hidden;
-}
-.treeViewItem-border{
-    border-left: dashed 1px #00000042;
-}
-
-.treeViewItem-header-editing,
-.treeViewItem-header {
-    border: transparent 1px solid;
-    display: flex;
-    //align-items: center;
-    ::-webkit-scrollbar {
-            display: none;
-    }
-    .formattedTextBox-cont {
-        .formattedTextbox-sidebar {
-            overflow: visible !important;
-            border-left: unset;
-        }
-        overflow: visible !important;
-    }
-
-    .editableView-container-editing-oneLine {
-        min-width: 15px;
-    }
-
-    .documentView-node-topmost {
-        width: unset;
-    }
-
-    >svg {
-        display: none;
-    }
-
-}
-
-.treeViewItem-header:hover {
-    .collectionTreeView-keyHeader {
-        display: inherit;
-    }
-
-    >svg {
-        display: inherit;
-    }
-
-    .treeViewItem-openRight {
-        display: inline-block;
-        height: 17px;
-        width: 15px;
-
-        // display: inline;
-        svg {
-            display: block;
-            padding: 0px;
-            margin-left: 3px;
-        }
-    }
-}
-
-.treeViewItem-header-above {
-    border-top: black 1px solid;
-}
-
-.treeViewItem-header-below {
-    border-bottom: black 1px solid;
-}
-
-.treeViewItem-header-inside {
-    border: black 1px solid;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 19b8400c8..46f18099a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,791 +1,29 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
-import { Document, listSpec } from '../../../fields/Schema';
-import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
-import { SnappingManager } from '../../util/SnappingManager';
+import { Document } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
 import { DragManager, dropActionType } from "../../util/DragManager";
-import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from '../../util/SelectionManager';
-import { Transform } from '../../util/Transform';
+import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from "../EditableView";
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
-import { DocumentView } from '../nodes/DocumentView';
-import { KeyValueBox } from '../nodes/KeyValueBox';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
-import { CollectionViewType, CollectionView } from './CollectionView';
+import { TreeView } from "./TreeView";
 import React = require("react");
-import { TraceMobx } from '../../../fields/util';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { RichTextField } from '../../../fields/RichTextField';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { DocumentManager } from '../../util/DocumentManager';
-
-export interface TreeViewProps {
-    document: Doc;
-    dataDoc?: Doc;
-    containingCollection: Doc;
-    prevSibling?: Doc;
-    renderDepth: number;
-    removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
-    moveDocument: DragManager.MoveFunction;
-    dropAction: dropActionType;
-    addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
-    pinToPres: (document: Doc) => void;
-    panelWidth: () => number;
-    panelHeight: () => number;
-    ChromeHeight: undefined | (() => number);
-    addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
-    indentDocument?: () => void;
-    outdentDocument?: () => void;
-    ScreenToLocalTransform: () => Transform;
-    backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
-    outerXf: () => { translateX: number, translateY: number };
-    treeView: CollectionTreeView;
-    parentKey: string;
-    active: (outsideReaction?: boolean) => boolean;
-    treeViewHideHeaderFields: () => boolean;
-    treeViewPreventOpen: boolean;
-    renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
-    onCheckedClick?: () => ScriptField;
-    onChildClick?: () => ScriptField;
-    ignoreFields?: string[];
-    firstLevel: boolean;
-    whenActiveChanged: (isActive: boolean) => void;
-}
-
-@observer
-/**
- * Renders a treeView of a collection of documents
- * 
- * special fields:
- * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
- * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
- */
-class TreeView extends React.Component<TreeViewProps> {
-    private _editTitleScript: (() => ScriptField) | undefined;
-    private _openScript: (() => ScriptField) | undefined;
-    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-    private _treedropDisposer?: DragManager.DragDropDisposer;
-    private _dref = React.createRef<HTMLDivElement>();
-    private _tref = React.createRef<HTMLDivElement>();
-    private _docRef = React.createRef<DocumentView>();
-    private _uniqueId = Utils.GenerateGuid();
-    private _editMaxWidth: number | string = 0;
-
-    get doc() { return this.props.document; }
-    get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
-    get displayName() { return "TreeView(" + this.doc.title + ")"; }  // this makes mobx trace() statements more descriptive
-    get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
-    get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
-    get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
-    @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
-    set treeViewOpen(c: boolean) {
-        if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
-        else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
-    }
-    @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; }
-    @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
-    @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
-    @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
-    @computed get dataDoc() { return this.doc[DataSym]; }
-    @computed get layoutDoc() { return Doc.Layout(this.doc); }
-    @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
-    childDocList(field: string) {
-        const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
-        return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
-            (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
-            DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
-    }
-    @computed get childDocs() { return this.childDocList(this.fieldKey); }
-    @computed get childLinks() { return this.childDocList("links"); }
-    @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
-    @computed get boundsOfCollectionDocument() {
-        return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
-            Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
-    }
-
-    @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
-    @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-        return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
-    }
-    @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
-        this.props.treeView.props.select(false);
-        return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
-    }
-    @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
-
-    constructor(props: any) {
-        super(props);
-        const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
-        const openScript = ScriptField.MakeScript(`openOnRight(self)`);
-        const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
-        this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
-        this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
-        if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
-    }
-
-    protected createTreeDropTarget = (ele: HTMLDivElement) => {
-        this._treedropDisposer?.();
-        ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
-    }
-
-    componentWillUnmount() {
-        document.removeEventListener("pointermove", this.onDragMove, true);
-        document.removeEventListener("pointermove", this.onDragUp, true);
-    }
-
-    onDragUp = (e: PointerEvent) => {
-        document.removeEventListener("pointerup", this.onDragUp, true);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
-    onPointerEnter = (e: React.PointerEvent): void => {
-        this.props.active(true) && Doc.BrushDoc(this.dataDoc);
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header!.current!.className = "treeViewItem-header";
-            document.removeEventListener("pointermove", this.onDragMove, true);
-            document.addEventListener("pointermove", this.onDragMove, true);
-            document.removeEventListener("pointerup", this.onDragUp, true);
-            document.addEventListener("pointerup", this.onDragUp, true);
-        }
-    }
-    onPointerLeave = (e: React.PointerEvent): void => {
-        Doc.UnBrushDoc(this.dataDoc);
-        if (this._header?.current?.className !== "treeViewItem-header-editing") {
-            this._header!.current!.className = "treeViewItem-header";
-        }
-        document.removeEventListener("pointerup", this.onDragUp, true);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
-    onDragMove = (e: PointerEvent): void => {
-        Doc.UnBrushDoc(this.dataDoc);
-        const pt = [e.clientX, e.clientY];
-        const rect = this._header!.current!.getBoundingClientRect();
-        const before = pt[1] < rect.top + rect.height / 2;
-        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
-        this._header!.current!.className = "treeViewItem-header";
-        if (inside) this._header!.current!.className += " treeViewItem-header-inside";
-        else if (before) this._header!.current!.className += " treeViewItem-header-above";
-        else if (!before) this._header!.current!.className += " treeViewItem-header-below";
-        e.stopPropagation();
-    }
-
-    public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
-        Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
-        Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
-        Doc.GetProto(bullet).data = new List<Doc>([]);
-        Doc.SetInPlace(bullet, "editTitle", "*", false);
-        FormattedTextBox.SelectOnLoad = bullet[Id];
-        return bullet;
-    }
-
-    makeTextCollection = () => {
-        Doc.SetInPlace(this.doc, "editTitle", undefined, false);
-        const bullet = TreeView.makeTextBullet();
-        const added = this.props.addDocument(bullet);
-        bullet.context = this.props.treeView.Document;
-        return added;
-    }
-
-    editableView = (key: string, style?: string) => (<EditableView
-        oneLine={true}
-        display={"inline-block"}
-        editing={true}
-        contents={StrCast(this.doc[key])}
-        height={12}
-        sizeToContent={true}
-        fontStyle={style}
-        fontSize={12}
-        GetValue={() => StrCast(this.doc[key])}
-        SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
-            if (this.outlineMode && enterKey) {
-                Doc.SetInPlace(this.doc, key, value, false);
-                this.makeTextCollection();
-            } else {
-                Doc.SetInPlace(this.doc, key, value, false) || true;
-                Doc.SetInPlace(this.doc, "editTitle", undefined, false);
-            }
-        })}
-        onClick={() => {
-            SelectionManager.DeselectAll();
-            Doc.UserDoc().activeSelection = new List([this.doc]);
-            return false;
-        }}
-        OnEmpty={undoBatch(() => 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);
-        })}
-    />)
-
-    preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
-        const dragData = de.complete.docDragData;
-        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
-    }
-
-    @undoBatch
-    treeDrop = (e: Event, de: DragManager.DropEvent) => {
-        const pt = [de.x, de.y];
-        const rect = this._header!.current!.getBoundingClientRect();
-        const before = pt[1] < rect.top + rect.height / 2;
-        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
-        const complete = de.complete;
-        if (complete.linkDragData) {
-            const sourceDoc = complete.linkDragData.linkSourceDocument;
-            const destDoc = this.doc;
-            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
-            e.stopPropagation();
-        }
-        const docDragData = complete.docDragData;
-        if (docDragData) {
-            e.stopPropagation();
-            if (docDragData.draggedDocuments[0] === this.doc) return true;
-            const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
-            let addDoc = parentAddDoc;
-            if (inside) {
-                const localAdd = (doc: Doc) => {
-                    const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-                    added && (doc.context = this.doc.context);
-                    return added;
-                };
-                addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
-                    (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
-            }
-            const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument;
-            return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false);
-        }
-        return false;
-    }
-
-    refTransform = (ref: HTMLDivElement) => {
-        const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
-        const outerXf = this.props.outerXf();
-        const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
-        return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
-    }
-    docTransform = () => this.refTransform(this._dref.current!);
-    getTransform = () => this.refTransform(this._tref.current!);
-    docWidth = () => {
-        const layoutDoc = this.layoutDoc;
-        const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
-        if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
-        return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
-    }
-    docHeight = () => {
-        const layoutDoc = this.layoutDoc;
-        const bounds = this.boundsOfCollectionDocument;
-        return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
-            const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
-            if (aspect) return this.docWidth() * aspect;
-            if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
-            return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) :
-                Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
-                    NumCast(this.props.containingCollection._height)))) :
-                NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
-        })()));
-    }
-
-    @computed get expandedField() {
-        const ids: { [key: string]: string } = {};
-        const doc = this.doc;
-        doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
-
-        const rows: JSX.Element[] = [];
-        for (const key of Object.keys(ids).slice().sort()) {
-            if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
-            const contents = doc[key];
-            let contentElement: (JSX.Element | null)[] | JSX.Element = [];
-
-            if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
-                const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
-                const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
-                    const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                    added && (doc.context = this.doc.context);
-                    return added;
-                };
-                const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-                contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
-                    this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                    this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
-                    this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
-            } else {
-                contentElement = <EditableView key="editableView"
-                    contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
-                    height={13}
-                    fontSize={12}
-                    GetValue={() => Field.toKeyValueString(doc, key)}
-                    SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
-            }
-            rows.push(<div style={{ display: "flex" }} key={key}>
-                <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
-                &nbsp;
-                {contentElement}
-            </div>);
-        }
-        rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
-            <EditableView
-                key="editableView"
-                contents={"+key:value"}
-                height={13}
-                fontSize={12}
-                GetValue={() => ""}
-                SetValue={(value: string) => {
-                    value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true);
-                    return true;
-                }} />
-        </div>);
-        return rows;
-    }
-
-    rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
-    rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
-    rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
-
-    @computed get renderContent() {
-        TraceMobx();
-        const expandKey = this.treeViewExpandedView;
-        if (["links", "annotations", this.fieldKey].includes(expandKey)) {
-            const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
-            const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
-                const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
-                added && (doc.context = this.doc.context);
-                return added;
-            };
-            const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-            const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
-            const sortKey = `${this.fieldKey}-sortAscending`;
-            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
-                !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
-                e.stopPropagation();
-            }}>
-                {!docs ? (null) :
-                    TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
-                        this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                        StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
-                        this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
-            </ul >;
-        } else if (this.treeViewExpandedView === "fields") {
-            return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
-                {this.expandedField}
-            </div></ul>;
-        } else {
-            const layoutDoc = this.layoutDoc;
-            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
-            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
-            return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
-                <ContentFittingDocumentView
-                    Document={this.doc}
-                    DataDoc={undefined}
-                    LibraryPath={emptyPath}
-                    renderDepth={this.props.renderDepth + 1}
-                    rootSelected={returnTrue}
-                    treeViewDoc={undefined}
-                    backgroundColor={this.props.backgroundColor}
-                    fitToBox={this.boundsOfCollectionDocument !== undefined}
-                    FreezeDimensions={true}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
-                    PanelWidth={panelWidth}
-                    PanelHeight={panelHeight}
-                    focus={returnFalse}
-                    ScreenToLocalTransform={this.docTransform}
-                    docFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionDoc={this.props.containingCollection}
-                    ContainingCollectionView={undefined}
-                    addDocument={returnFalse}
-                    moveDocument={this.move}
-                    removeDocument={this.props.removeDoc}
-                    parentActive={this.props.active}
-                    whenActiveChanged={this.props.whenActiveChanged}
-                    addDocTab={this.props.addDocTab}
-                    pinToPres={this.props.pinToPres}
-                    bringToFront={returnFalse}
-                    ContentScaling={returnOne}
-                />
-            </div>;
-        }
-    }
-
-    get onCheckedClick() { return this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
-
-    @action
-    bulletClick = (e: React.MouseEvent) => {
-        if (this.onCheckedClick && this.doc.type !== DocumentType.COL) {
-            // this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check";
-            this.onCheckedClick?.script.run({
-                this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
-                heading: this.props.containingCollection.title,
-                checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
-                containingTreeView: this.props.treeView.props.Document,
-            }, console.log);
-        } else {
-            this.treeViewOpen = !this.treeViewOpen;
-        }
-        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) }}>
-            {(this.doc.text as RichTextField)?.Text ? <FontAwesomeIcon icon={this.childDocs?.length && !this.treeViewOpen ? ["fas", "circle"] : ["far", "circle"]} /> : (null)}
-        </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
-            }}>
-            {<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>;
-    }
-
-    showContextMenu = (e: React.MouseEvent) => {
-        this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
-    }
-    contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
-    truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
-    @computed get showTitleEdit() {
-        return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
-    }
-    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
-    onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
-    /**
-     * Renders the EditableView title element for placement into the tree.
-     */
-    @computed
-    get renderTitle() {
-        TraceMobx();
-        const headerElements = this.props.treeViewHideHeaderFields() ? (null) :
-            <>
-                <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
-                <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
-                    onPointerDown={action(() => {
-                        if (this.treeViewOpen) {
-                            this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
-                                this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") :
-                                    this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
-                                        this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
-                                            (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
-                                                this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields");
-                        }
-                        this.treeViewOpen = true;
-                    })}>
-                    {this.treeViewExpandedView}
-                </span>
-            </>;
-        const view = this.showTitleEdit ? this.editableView("title") :
-            <DocumentView
-                ref={this._docRef}
-                Document={this.doc}
-                DataDoc={undefined}
-                treeViewDoc={this.props.treeView.props.Document}
-                LibraryPath={emptyPath}
-                addDocument={undefined}
-                addDocTab={this.props.addDocTab}
-                rootSelected={returnTrue}
-                pinToPres={emptyFunction}
-                onClick={this.onChildClick}
-                onDoubleClick={this.onChildDoubleClick}
-                dropAction={this.props.dropAction}
-                moveDocument={this.move}
-                removeDocument={this.props.removeDoc}
-                ScreenToLocalTransform={this.getTransform}
-                ContentScaling={returnOne}
-                PanelWidth={this.truncateTitleWidth}
-                PanelHeight={returnZero}
-                contextMenuItems={this.contextMenuItems}
-                opacity={this.outlineMode ? undefined : returnOne}
-                renderDepth={1}
-                focus={returnTrue}
-                parentActive={returnTrue}
-                whenActiveChanged={this.props.whenActiveChanged}
-                bringToFront={emptyFunction}
-                dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
-                docFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={this.props.containingCollection}
-            />;
-        return <>
-            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
-                style={{
-                    fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
-                    textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
-                }} >
-                {view}
-            </div >
-            {Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode ? (null) : headerElements}
-        </>;
-    }
-
-    refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
-
-    render() {
-        TraceMobx();
-        if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
-        const sorting = this.doc[`${this.fieldKey}-sortAscending`];
-        if (this.showTitleEdit) { // find  containing CollectionTreeView and set our maximum width so  the containing tree view won't have to scroll
-            let par: any = this._header?.current;
-            if (par) {
-                while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
-                if (par) {
-                    const par_rect = (par as HTMLElement).getBoundingClientRect();
-                    const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect();
-                    this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0));
-                }
-            }
-        } else this._editMaxWidth = "";
-        const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
-        return this.doc.treeViewHideHeader || this.outlineMode ?
-            !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
-                <div className={`treeViewItem-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
-                    onKeyDown={e => {
-                        e.stopPropagation();
-                        e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
-                        e.key === "Tab" && UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
-                        e.key === "Enter" && UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
-                        e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
-                    }}
-                >
-                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
-                        onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
-                        onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
-                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
-                        <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
-                            <ContentFittingDocumentView
-                                Document={this.doc}
-                                DataDoc={undefined}
-                                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
-                                LibraryPath={emptyPath}
-                                renderDepth={this.props.renderDepth + 1}
-                                rootSelected={returnTrue}
-                                treeViewDoc={undefined}
-                                backgroundColor={this.props.backgroundColor}
-                                fitToBox={this.boundsOfCollectionDocument !== undefined}
-                                PanelWidth={this.rtfWidth}
-                                PanelHeight={this.rtfOutlineHeight}
-                                focus={this.refocus}
-                                ScreenToLocalTransform={this.docTransform}
-                                docFilters={returnEmptyFilter}
-                                searchFilterDocs={returnEmptyDoclist}
-                                ContainingCollectionDoc={this.props.containingCollection}
-                                ContainingCollectionView={undefined}
-                                addDocument={this.props.addDocument}
-                                moveDocument={this.move}
-                                removeDocument={this.props.removeDoc}
-                                parentActive={this.props.active}
-                                whenActiveChanged={this.props.whenActiveChanged}
-                                addDocTab={this.props.addDocTab}
-                                pinToPres={this.props.pinToPres}
-                                bringToFront={returnFalse}
-                                ContentScaling={returnOne}
-                            />
-                        </div>
-                    </div>
-
-                    <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
-                        {!this.treeViewOpen ? (null) : this.renderContent}
-                    </div>
-                </div> :
-            <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
-                <li className="collection-child">
-                    <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
-                        if (this.props.active(true)) {
-                            e.stopPropagation();
-                            e.preventDefault();
-                            SelectionManager.DeselectAll();
-                        }
-                    }}
-                        onPointerDown={e => {
-                            if (this.props.active(true)) {
-                                e.stopPropagation();
-                                e.preventDefault();
-                            }
-                        }}
-                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
-                        {this.renderTitle}
-                    </div>
-                    <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
-                        {!this.treeViewOpen ? (null) : this.renderContent}
-                    </div>
-                </li>
-            </div>;
-    }
-    public static GetChildElements(
-        childDocs: Doc[],
-        treeView: CollectionTreeView,
-        containingCollection: Doc,
-        dataDoc: Doc | undefined,
-        key: string,
-        parentCollectionDoc: Doc | undefined,
-        parentPrevSibling: Doc | undefined,
-        add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
-        remove: ((doc: Doc | Doc[]) => boolean),
-        move: DragManager.MoveFunction,
-        dropAction: dropActionType,
-        addDocTab: (doc: Doc, where: string) => boolean,
-        pinToPres: (document: Doc) => void,
-        backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
-        screenToLocalXf: () => Transform,
-        outerXf: () => { translateX: number, translateY: number },
-        active: (outsideReaction?: boolean) => boolean,
-        panelWidth: () => number,
-        ChromeHeight: undefined | (() => number),
-        renderDepth: number,
-        treeViewHideHeaderFields: () => boolean,
-        treeViewPreventOpen: boolean,
-        renderedIds: string[],
-        onCheckedClick: undefined | (() => ScriptField),
-        onChildClick: undefined | (() => ScriptField),
-        ignoreFields: string[] | undefined,
-        firstLevel: boolean,
-        whenActiveChanged: (isActive: boolean) => void
-    ) {
-        const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
-        if (viewSpecScript) {
-            childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
-        }
-
-        const docs = childDocs.slice();
-        const ascending = containingCollection?.[key + "-sortAscending"];
-        if (ascending !== undefined) {
-            const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
-                const reN = /[0-9]*$/;
-                const aA = a.replace(reN, ""); // get rid of trailing numbers
-                const bA = b.replace(reN, "");
-                if (aA === bA) {  // if header string matches, then compare numbers numerically
-                    const aN = parseInt(a.match(reN)![0], 10);
-                    const bN = parseInt(b.match(reN)![0], 10);
-                    return aN === bN ? 0 : aN > bN ? 1 : -1;
-                } else {
-                    return aA > bA ? 1 : -1;
-                }
-            };
-            docs.sort(function (a, b): 0 | 1 | -1 {
-                const descA = ascending ? b : a;
-                const descB = ascending ? a : b;
-                const first = descA.title;
-                const second = descB.title;
-                // TODO find better way to sort how to sort..................
-                if (typeof first === 'number' && typeof second === 'number') {
-                    return (first - second) > 0 ? 1 : -1;
-                }
-                if (typeof first === 'string' && typeof second === 'string') {
-                    return sortAlphaNum(first, second);
-                }
-                if (typeof first === 'boolean' && typeof second === 'boolean') {
-                    // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load
-                    //     return Number(descA.x) > Number(descB.x) ? 1 : -1;
-                    // }
-                    return first > second ? 1 : -1;
-                }
-                return ascending ? 1 : -1;
-            });
-        }
-
-        const rowWidth = () => panelWidth() - 20;
-        return docs.map((child, i) => {
-            const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child);
-            if (!pair.layout || pair.data instanceof Promise) {
-                return (null);
-            }
-
-            const indent = i === 0 ? undefined : () => {
-                if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
-                    const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
-                    const fieldKey = fieldKeysub.split("\'")[1];
-                    if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
-                        remove(child);
-                        FormattedTextBox.SelectOnLoad = child[Id];
-                        Doc.AddDocToList(docs[i - 1], fieldKey, child);
-                        docs[i - 1].treeViewOpen = true;
-                        child.context = treeView.Document;
-                    }
-                }
-            };
-            const outdent = !parentCollectionDoc ? undefined : () => {
-                if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
-                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
-                    const fieldKey = fieldKeysub.split("\'")[1];
-                    remove(child);
-                    FormattedTextBox.SelectOnLoad = child[Id];
-                    Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
-                    parentCollectionDoc.treeViewOpen = true;
-                    child.context = treeView.Document;
-                }
-            };
-            const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => {
-                return add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
-            };
-            const childLayout = Doc.Layout(pair.layout);
-            const rowHeight = () => {
-                const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0);
-                return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
-            };
-            return !(child instanceof Doc) ? (null) : <TreeView
-                document={pair.layout}
-                dataDoc={pair.data}
-                containingCollection={containingCollection}
-                prevSibling={docs[i]}
-                treeView={treeView}
-                key={child[Id]}
-                indentDocument={indent}
-                outdentDocument={outdent}
-                onCheckedClick={onCheckedClick}
-                onChildClick={onChildClick}
-                renderDepth={renderDepth}
-                removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
-                addDocument={addDocument}
-                backgroundColor={backgroundColor}
-                panelWidth={rowWidth}
-                panelHeight={rowHeight}
-                ChromeHeight={ChromeHeight}
-                moveDocument={move}
-                dropAction={dropAction}
-                addDocTab={addDocTab}
-                pinToPres={pinToPres}
-                ScreenToLocalTransform={screenToLocalXf}
-                outerXf={outerXf}
-                parentKey={key}
-                active={active}
-                treeViewHideHeaderFields={treeViewHideHeaderFields}
-                treeViewPreventOpen={treeViewPreventOpen}
-                renderedIds={renderedIds}
-                ignoreFields={ignoreFields}
-                firstLevel={firstLevel}
-                whenActiveChanged={whenActiveChanged} />;
-        });
-    }
-}
 
 export type collectionTreeViewProps = {
     treeViewHideTitle?: boolean;
@@ -797,18 +35,16 @@ export type collectionTreeViewProps = {
 @observer
 export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
     private treedropDisposer?: DragManager.DragDropDisposer;
+    private _isChildActive = false;
     private _mainEle?: HTMLDivElement;
-
     public _uniqueId = Utils.GenerateGuid();
-    _isChildActive = false;
+
     @computed get doc() { return this.props.Document; }
     @computed get dataDoc() { return this.props.DataDoc || this.doc; }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this.treedropDisposer?.();
-        if (this._mainEle = ele) {
-            this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
-        }
+        if (this._mainEle = ele) this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
     }
 
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -873,8 +109,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
 
     @computed get renderClearButton() {
         return <div key="toolbar">
-            <button className="toolbar-button round-button" title="Empty"
-                onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
+            <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
                 <FontAwesomeIcon icon={"trash"} size="sm" />
             </button>
         </div >;
@@ -984,15 +219,4 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             </div>
         );
     }
-}
-
-Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
-    const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
-    for (let i = 0; i < docFilters.length; i += 3) {
-        const [header, value, state] = docFilters.slice(i, i + 3);
-        if (header === facetHeader && value === facetValue) {
-            return state;
-        }
-    }
-    return undefined;
-});
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
new file mode 100644
index 000000000..17c6b0750
--- /dev/null
+++ b/src/client/views/collections/TreeView.scss
@@ -0,0 +1,115 @@
+@import "../globalCssVariables";
+
+.treeView-container,
+.treeView-container-active {
+    .bullet-outline {
+        position: relative;
+        width: 15px;
+        color: $intermediate-color;
+        transform: scale(0.5);
+        display: inline-block;
+    }
+
+    .bullet {
+        position: relative;
+        width: 15px;
+        color: $intermediate-color;
+        margin-top: 3px;
+        transform: scale(1.3, 1.3);
+        border: #80808030 1px solid;
+        border-radius: 4px;
+    }
+}
+.treeView-container-active {
+    z-index: 100;
+    position: relative;;
+    .formattedTextbox-sidebar {
+        background-color: #ffff001f !important;
+        height: 500px !important;
+    }
+}
+
+.treeView-openRight {
+    display: none;
+    height: 17px;
+    width: 15px;
+}
+
+.treeView-openRight:hover {
+    background: #797777;
+    cursor: pointer;
+}
+
+.treeView-border-outline,
+.treeView-border {
+    display: flex;
+    overflow: hidden;
+}
+.treeView-border{
+    border-left: dashed 1px #00000042;
+}
+
+.treeView-header-editing,
+.treeView-header {
+    border: transparent 1px solid;
+    display: flex;
+    //align-items: center;
+    ::-webkit-scrollbar {
+            display: none;
+    }
+    .formattedTextBox-cont {
+        .formattedTextbox-sidebar {
+            overflow: visible !important;
+            border-left: unset;
+        }
+        overflow: visible !important;
+    }
+
+    .editableView-container-editing-oneLine {
+        min-width: 15px;
+    }
+
+    .documentView-node-topmost {
+        width: unset;
+    }
+
+    >svg {
+        display: none;
+    }
+
+}
+
+.treeView-header:hover {
+    .collectionTreeView-keyHeader {
+        display: inherit;
+    }
+
+    >svg {
+        display: inherit;
+    }
+
+    .treeView-openRight {
+        display: inline-block;
+        height: 17px;
+        width: 15px;
+
+        // display: inline;
+        svg {
+            display: block;
+            padding: 0px;
+            margin-left: 3px;
+        }
+    }
+}
+
+.treeView-header-above {
+    border-top: black 1px solid;
+}
+
+.treeView-header-below {
+    border-bottom: black 1px solid;
+}
+
+.treeView-header-inside {
+    border: black 1px solid;
+}
\ No newline at end of file
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
new file mode 100644
index 000000000..e509eb78d
--- /dev/null
+++ b/src/client/views/collections/TreeView.tsx
@@ -0,0 +1,760 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from "../../util/DragManager";
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { EditableView } from "../EditableView";
+import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { KeyValueBox } from '../nodes/KeyValueBox';
+import { CollectionTreeView } from './CollectionTreeView';
+import { CollectionView, CollectionViewType } from './CollectionView';
+import "./TreeView.scss";
+import React = require("react");
+
+export interface TreeViewProps {
+    document: Doc;
+    dataDoc?: Doc;
+    containingCollection: Doc;
+    prevSibling?: Doc;
+    renderDepth: number;
+    removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
+    moveDocument: DragManager.MoveFunction;
+    dropAction: dropActionType;
+    addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
+    pinToPres: (document: Doc) => void;
+    panelWidth: () => number;
+    panelHeight: () => number;
+    ChromeHeight: undefined | (() => number);
+    addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
+    indentDocument?: () => void;
+    outdentDocument?: () => void;
+    ScreenToLocalTransform: () => Transform;
+    backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
+    outerXf: () => { translateX: number, translateY: number };
+    treeView: CollectionTreeView;
+    parentKey: string;
+    active: (outsideReaction?: boolean) => boolean;
+    treeViewHideHeaderFields: () => boolean;
+    treeViewPreventOpen: boolean;
+    renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
+    onCheckedClick?: () => ScriptField;
+    onChildClick?: () => ScriptField;
+    ignoreFields?: string[];
+    firstLevel: boolean;
+    whenActiveChanged: (isActive: boolean) => void;
+}
+
+@observer
+/**
+ * Renders a treeView of a collection of documents
+ * 
+ * special fields:
+ * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
+ * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+ * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
+ */
+export class TreeView extends React.Component<TreeViewProps> {
+    private _editTitleScript: (() => ScriptField) | undefined;
+    private _openScript: (() => ScriptField) | undefined;
+    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+    private _treedropDisposer?: DragManager.DragDropDisposer;
+    private _dref = React.createRef<HTMLDivElement>();
+    private _tref = React.createRef<HTMLDivElement>();
+    private _docRef = React.createRef<DocumentView>();
+    private _uniqueId = Utils.GenerateGuid();
+    private _editMaxWidth: number | string = 0;
+
+    get doc() { return this.props.document; }
+    get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
+    get displayName() { return "TreeView(" + this.doc.title + ")"; }  // this makes mobx trace() statements more descriptive
+    get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
+    get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
+    get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
+    @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
+    set treeViewOpen(c: boolean) {
+        if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
+        else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
+    }
+    @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; }
+    @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
+    @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
+    @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
+    @computed get dataDoc() { return this.doc[DataSym]; }
+    @computed get layoutDoc() { return Doc.Layout(this.doc); }
+    @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
+    childDocList(field: string) {
+        const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
+        return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+            (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
+            DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
+    }
+    @computed get childDocs() { return this.childDocList(this.fieldKey); }
+    @computed get childLinks() { return this.childDocList("links"); }
+    @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
+    @computed get boundsOfCollectionDocument() {
+        return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined :
+            Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
+    }
+
+    @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
+    @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
+        return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
+    }
+    @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
+        this.props.treeView.props.select(false);
+        return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
+    }
+    @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
+
+    constructor(props: any) {
+        super(props);
+        const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
+        const openScript = ScriptField.MakeScript(`openOnRight(self)`);
+        const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
+        this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
+        this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
+        if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
+    }
+
+    protected createTreeDropTarget = (ele: HTMLDivElement) => {
+        this._treedropDisposer?.();
+        ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
+    }
+
+    componentWillUnmount() {
+        document.removeEventListener("pointermove", this.onDragMove, true);
+        document.removeEventListener("pointermove", this.onDragUp, true);
+    }
+
+    onDragUp = (e: PointerEvent) => {
+        document.removeEventListener("pointerup", this.onDragUp, true);
+        document.removeEventListener("pointermove", this.onDragMove, true);
+    }
+    onPointerEnter = (e: React.PointerEvent): void => {
+        this.props.active(true) && Doc.BrushDoc(this.dataDoc);
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+            this._header!.current!.className = "treeView-header";
+            document.removeEventListener("pointermove", this.onDragMove, true);
+            document.addEventListener("pointermove", this.onDragMove, true);
+            document.removeEventListener("pointerup", this.onDragUp, true);
+            document.addEventListener("pointerup", this.onDragUp, true);
+        }
+    }
+    onPointerLeave = (e: React.PointerEvent): void => {
+        Doc.UnBrushDoc(this.dataDoc);
+        if (this._header?.current?.className !== "treeView-header-editing") {
+            this._header!.current!.className = "treeView-header";
+        }
+        document.removeEventListener("pointerup", this.onDragUp, true);
+        document.removeEventListener("pointermove", this.onDragMove, true);
+    }
+    onDragMove = (e: PointerEvent): void => {
+        Doc.UnBrushDoc(this.dataDoc);
+        const pt = [e.clientX, e.clientY];
+        const rect = this._header!.current!.getBoundingClientRect();
+        const before = pt[1] < rect.top + rect.height / 2;
+        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+        this._header!.current!.className = "treeView-header";
+        if (inside) this._header!.current!.className += " treeView-header-inside";
+        else if (before) this._header!.current!.className += " treeView-header-above";
+        else if (!before) this._header!.current!.className += " treeView-header-below";
+        e.stopPropagation();
+    }
+
+    public static makeTextBullet() {
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
+        Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
+        Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
+        Doc.GetProto(bullet).data = new List<Doc>([]);
+        Doc.SetInPlace(bullet, "editTitle", "*", false);
+        FormattedTextBox.SelectOnLoad = bullet[Id];
+        return bullet;
+    }
+
+    makeTextCollection = () => {
+        Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+        const bullet = TreeView.makeTextBullet();
+        const added = this.props.addDocument(bullet);
+        bullet.context = this.props.treeView.Document;
+        return added;
+    }
+
+    editableView = (key: string, style?: string) => (<EditableView
+        oneLine={true}
+        display={"inline-block"}
+        editing={true}
+        contents={StrCast(this.doc[key])}
+        height={12}
+        sizeToContent={true}
+        fontStyle={style}
+        fontSize={12}
+        GetValue={() => StrCast(this.doc[key])}
+        SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
+            Doc.SetInPlace(this.doc, key, value, false);
+            if (this.outlineMode && enterKey) {
+                this.makeTextCollection();
+            } else {
+                Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+            }
+        })}
+        onClick={() => {
+            SelectionManager.DeselectAll();
+            Doc.UserDoc().activeSelection = new List([this.doc]);
+            return false;
+        }}
+        OnEmpty={undoBatch(() => 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);
+        })}
+    />)
+
+    preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+        const dragData = de.complete.docDragData;
+        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
+    }
+
+    @undoBatch
+    treeDrop = (e: Event, de: DragManager.DropEvent) => {
+        const pt = [de.x, de.y];
+        const rect = this._header!.current!.getBoundingClientRect();
+        const before = pt[1] < rect.top + rect.height / 2;
+        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+        if (de.complete.linkDragData) {
+            const sourceDoc = de.complete.linkDragData.linkSourceDocument;
+            const destDoc = this.doc;
+            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
+            e.stopPropagation();
+        }
+        const docDragData = de.complete.docDragData;
+        if (docDragData) {
+            e.stopPropagation();
+            if (docDragData.draggedDocuments[0] === this.doc) return true;
+            const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
+            let addDoc = parentAddDoc;
+            if (inside) {
+                const localAdd = (doc: Doc) => {
+                    const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
+                    added && (doc.context = this.doc.context);
+                    return added;
+                };
+                addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
+                    (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
+            }
+            const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument;
+            return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false);
+        }
+        return false;
+    }
+
+    refTransform = (ref: HTMLDivElement) => {
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
+        const outerXf = this.props.outerXf();
+        const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+        return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
+    }
+    docTransform = () => this.refTransform(this._dref.current!);
+    getTransform = () => this.refTransform(this._tref.current!);
+    docWidth = () => {
+        const layoutDoc = this.layoutDoc;
+        const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
+        if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
+        return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
+    }
+    docHeight = () => {
+        const layoutDoc = this.layoutDoc;
+        const bounds = this.boundsOfCollectionDocument;
+        return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
+            const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
+            if (aspect) return this.docWidth() * aspect;
+            if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
+            return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) :
+                Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
+                    NumCast(this.props.containingCollection._height)))) :
+                NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
+        })()));
+    }
+
+    @computed get expandedField() {
+        const ids: { [key: string]: string } = {};
+        const rows: JSX.Element[] = [];
+        const doc = this.doc;
+        doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+
+        for (const key of Object.keys(ids).slice().sort()) {
+            if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
+            const contents = doc[key];
+            let contentElement: (JSX.Element | null)[] | JSX.Element = [];
+
+            if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
+                const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
+                const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+                    const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+                    added && (doc.context = this.doc.context);
+                    return added;
+                };
+                const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
+                contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
+                    this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+                    this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
+                    this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
+            } else {
+                contentElement = <EditableView key="editableView"
+                    contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
+                    height={13}
+                    fontSize={12}
+                    GetValue={() => Field.toKeyValueString(doc, key)}
+                    SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
+            }
+            rows.push(<div style={{ display: "flex" }} key={key}>
+                <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
+                &nbsp;
+                {contentElement}
+            </div>);
+        }
+        rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
+            <EditableView
+                key="editableView"
+                contents={"+key:value"}
+                height={13}
+                fontSize={12}
+                GetValue={returnEmptyString}
+                SetValue={value => value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true)} />
+        </div>);
+        return rows;
+    }
+
+    rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
+    rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
+
+    @computed get renderContent() {
+        TraceMobx();
+        const expandKey = this.treeViewExpandedView;
+        if (["links", "annotations", this.fieldKey].includes(expandKey)) {
+            const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
+            const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+                const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+                added && (doc.context = this.doc.context);
+                return added;
+            };
+            const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
+            const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
+            const sortKey = `${this.fieldKey}-sortAscending`;
+            return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
+                !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
+                e.stopPropagation();
+            }}>
+                {!docs ? (null) :
+                    TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
+                        this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+                        StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+                        this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
+            </ul >;
+        } else if (this.treeViewExpandedView === "fields") {
+            return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
+                {this.expandedField}
+            </div></ul>;
+        } else {
+            const layoutDoc = this.layoutDoc;
+            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
+            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
+            return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
+                <ContentFittingDocumentView
+                    Document={this.doc}
+                    DataDoc={undefined}
+                    LibraryPath={emptyPath}
+                    renderDepth={this.props.renderDepth + 1}
+                    rootSelected={returnTrue}
+                    treeViewDoc={undefined}
+                    backgroundColor={this.props.backgroundColor}
+                    fitToBox={this.boundsOfCollectionDocument !== undefined}
+                    FreezeDimensions={true}
+                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+                    PanelWidth={panelWidth}
+                    PanelHeight={panelHeight}
+                    focus={returnFalse}
+                    ScreenToLocalTransform={this.docTransform}
+                    docFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionDoc={this.props.containingCollection}
+                    ContainingCollectionView={undefined}
+                    addDocument={returnFalse}
+                    moveDocument={this.move}
+                    removeDocument={this.props.removeDoc}
+                    parentActive={this.props.active}
+                    whenActiveChanged={this.props.whenActiveChanged}
+                    addDocTab={this.props.addDocTab}
+                    pinToPres={this.props.pinToPres}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
+                />
+            </div>;
+        }
+    }
+
+    get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
+
+    @action
+    bulletClick = (e: React.MouseEvent) => {
+        if (this.onCheckedClick) {
+            this.onCheckedClick?.script.run({
+                this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
+                heading: this.props.containingCollection.title,
+                checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
+                containingTreeView: this.props.treeView.props.Document,
+            }, console.log);
+        } else {
+            this.treeViewOpen = !this.treeViewOpen;
+        }
+        e.stopPropagation();
+    }
+
+    @computed get renderBullet() {
+        TraceMobx();
+        const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
+        return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
+            onClick={this.bulletClick}
+            style={this.outlineMode ? { opacity: NumCast(this.doc.opacity, 1) } : {
+                color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
+                opacity: checked === "unchecked" ? undefined : 0.4
+            }}>
+            {this.outlineMode && !(this.doc.text as RichTextField)?.Text ? (null) :
+                <FontAwesomeIcon icon={this.outlineMode ? [this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"] :
+                    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>;
+    }
+    @computed get showTitleEditorControl() { return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || ""); }
+    @computed get headerElements() {
+        return (Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode) || this.props.treeViewHideHeaderFields() ? (null) :
+            <>
+                <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
+                <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
+                    onPointerDown={action(() => {
+                        if (this.treeViewOpen) {
+                            this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
+                                this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") :
+                                    this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
+                                        this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
+                                            (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
+                                                this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields");
+                        }
+                        this.treeViewOpen = true;
+                    })}>
+                    {this.treeViewExpandedView}
+                </span>
+            </>;
+    }
+
+    showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef.current?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+    contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
+    truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
+    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+    onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+    /**
+     * Renders the EditableView title element for placement into the tree.
+     */
+    @computed
+    get renderTitle() {
+        TraceMobx();
+        const view = this.showTitleEditorControl ? this.editableView("title") :
+            <DocumentView
+                ref={this._docRef}
+                Document={this.doc}
+                DataDoc={undefined}
+                treeViewDoc={this.props.treeView.props.Document}
+                LibraryPath={emptyPath}
+                addDocument={undefined}
+                addDocTab={this.props.addDocTab}
+                rootSelected={returnTrue}
+                pinToPres={emptyFunction}
+                onClick={this.onChildClick}
+                onDoubleClick={this.onChildDoubleClick}
+                dropAction={this.props.dropAction}
+                moveDocument={this.move}
+                removeDocument={this.props.removeDoc}
+                ScreenToLocalTransform={this.getTransform}
+                ContentScaling={returnOne}
+                PanelWidth={this.truncateTitleWidth}
+                PanelHeight={returnZero}
+                contextMenuItems={this.contextMenuItems}
+                opacity={this.outlineMode ? undefined : returnOne}
+                renderDepth={1}
+                focus={returnTrue}
+                parentActive={returnTrue}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={emptyFunction}
+                dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
+                docFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={this.props.containingCollection}
+            />;
+        return <>
+            <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
+                style={{
+                    fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
+                    textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
+                    outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
+                    pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+                }} >
+                {view}
+            </div >
+            {this.headerElements}
+        </>;
+    }
+
+    refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+
+    render() {
+        TraceMobx();
+        if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
+        const sorting = this.doc[`${this.fieldKey}-sortAscending`];
+        if (this.showTitleEditorControl) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
+            let par: any = this._header?.current;
+            while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
+            if (par) {
+                const par_rect = (par as HTMLElement).getBoundingClientRect();
+                const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect();
+                this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0));
+            }
+        }
+        else this._editMaxWidth = "";
+        const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc));
+        return this.doc.treeViewHideHeader || this.outlineMode ?
+            !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ?
+                this.renderContent
+                : <div className={`treeView-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+                    onKeyDown={e => {
+                        e.stopPropagation();
+                        switch (e.key) {
+                            case "Backspace": return this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
+                            case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
+                            case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+                                return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
+                        }
+                    }} >
+                    <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
+                        onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
+                        onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
+                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                        {this.renderBullet}
+                        <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
+                            <ContentFittingDocumentView
+                                Document={this.doc}
+                                DataDoc={undefined}
+                                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+                                LibraryPath={emptyPath}
+                                renderDepth={this.props.renderDepth + 1}
+                                rootSelected={returnTrue}
+                                treeViewDoc={undefined}
+                                backgroundColor={this.props.backgroundColor}
+                                fitToBox={this.boundsOfCollectionDocument !== undefined}
+                                PanelWidth={this.rtfWidth}
+                                PanelHeight={this.rtfOutlineHeight}
+                                focus={this.refocus}
+                                ScreenToLocalTransform={this.docTransform}
+                                docFilters={returnEmptyFilter}
+                                searchFilterDocs={returnEmptyDoclist}
+                                ContainingCollectionDoc={this.props.containingCollection}
+                                ContainingCollectionView={undefined}
+                                addDocument={this.props.addDocument}
+                                moveDocument={this.move}
+                                removeDocument={this.props.removeDoc}
+                                parentActive={this.props.active}
+                                whenActiveChanged={this.props.whenActiveChanged}
+                                addDocTab={this.props.addDocTab}
+                                pinToPres={this.props.pinToPres}
+                                bringToFront={returnFalse}
+                                ContentScaling={returnOne}
+                            />
+                        </div>
+                    </div>
+
+                    <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+                        {!this.treeViewOpen ? (null) : this.renderContent}
+                    </div>
+                </div> :
+            <div className="treeView-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
+                <li className="collection-child">
+                    <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
+                        if (this.props.active(true)) {
+                            e.stopPropagation();
+                            e.preventDefault();
+                            SelectionManager.DeselectAll();
+                        }
+                    }}
+                        onPointerDown={e => {
+                            if (this.props.active(true)) {
+                                e.stopPropagation();
+                                e.preventDefault();
+                            }
+                        }}
+                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+                        {this.renderBullet}
+                        {this.renderTitle}
+                    </div>
+                    <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+                        {!this.treeViewOpen ? (null) : this.renderContent}
+                    </div>
+                </li>
+            </div>;
+    }
+
+
+    public static GetChildElements(
+        childDocs: Doc[],
+        treeView: CollectionTreeView,
+        containingCollection: Doc,
+        dataDoc: Doc | undefined,
+        key: string,
+        parentCollectionDoc: Doc | undefined,
+        parentPrevSibling: Doc | undefined,
+        add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
+        remove: ((doc: Doc | Doc[]) => boolean),
+        move: DragManager.MoveFunction,
+        dropAction: dropActionType,
+        addDocTab: (doc: Doc, where: string) => boolean,
+        pinToPres: (document: Doc) => void,
+        backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
+        screenToLocalXf: () => Transform,
+        outerXf: () => { translateX: number, translateY: number },
+        active: (outsideReaction?: boolean) => boolean,
+        panelWidth: () => number,
+        ChromeHeight: undefined | (() => number),
+        renderDepth: number,
+        treeViewHideHeaderFields: () => boolean,
+        treeViewPreventOpen: boolean,
+        renderedIds: string[],
+        onCheckedClick: undefined | (() => ScriptField),
+        onChildClick: undefined | (() => ScriptField),
+        ignoreFields: string[] | undefined,
+        firstLevel: boolean,
+        whenActiveChanged: (isActive: boolean) => void
+    ) {
+        const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
+        if (viewSpecScript) {
+            childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
+        }
+
+        const docs = childDocs.slice();
+        const ascending = containingCollection?.[key + "-sortAscending"];
+        if (ascending !== undefined) {
+            const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
+                const reN = /[0-9]*$/;
+                const aA = a.replace(reN, ""); // get rid of trailing numbers
+                const bA = b.replace(reN, "");
+                if (aA === bA) {  // if header string matches, then compare numbers numerically
+                    const aN = parseInt(a.match(reN)![0], 10);
+                    const bN = parseInt(b.match(reN)![0], 10);
+                    return aN === bN ? 0 : aN > bN ? 1 : -1;
+                } else {
+                    return aA > bA ? 1 : -1;
+                }
+            };
+            docs.sort(function (a, b): 0 | 1 | -1 {
+                const first = (ascending ? b : a).title;
+                const second = (ascending ? a : b).title;
+                if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1;
+                if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second);
+                return ascending ? 1 : -1;
+            });
+        }
+
+        const rowWidth = () => panelWidth() - 20;
+        return docs.map((child, i) => {
+            const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child);
+            if (!pair.layout || pair.data instanceof Promise) {
+                return (null);
+            }
+
+            const indent = i === 0 ? undefined : () => {
+                if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+                    const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
+                    const fieldKey = fieldKeysub.split("\'")[1];
+                    if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
+                        remove(child);
+                        FormattedTextBox.SelectOnLoad = child[Id];
+                        Doc.AddDocToList(docs[i - 1], fieldKey, child);
+                        docs[i - 1].treeViewOpen = true;
+                        child.context = treeView.Document;
+                    }
+                }
+            };
+            const outdent = !parentCollectionDoc ? undefined : () => {
+                if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
+                    const fieldKey = fieldKeysub.split("\'")[1];
+                    remove(child);
+                    FormattedTextBox.SelectOnLoad = child[Id];
+                    Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
+                    parentCollectionDoc.treeViewOpen = true;
+                    child.context = treeView.Document;
+                }
+            };
+            const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
+            const childLayout = Doc.Layout(pair.layout);
+            const rowHeight = () => {
+                const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0);
+                return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+            };
+            return !(child instanceof Doc) ? (null) : <TreeView
+                document={pair.layout}
+                dataDoc={pair.data}
+                containingCollection={containingCollection}
+                prevSibling={docs[i]}
+                treeView={treeView}
+                key={child[Id]}
+                indentDocument={indent}
+                outdentDocument={!parentCollectionDoc ? undefined : outdent}
+                onCheckedClick={onCheckedClick}
+                onChildClick={onChildClick}
+                renderDepth={renderDepth}
+                removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
+                addDocument={addDocument}
+                backgroundColor={backgroundColor}
+                panelWidth={rowWidth}
+                panelHeight={rowHeight}
+                ChromeHeight={ChromeHeight}
+                moveDocument={move}
+                dropAction={dropAction}
+                addDocTab={addDocTab}
+                pinToPres={pinToPres}
+                ScreenToLocalTransform={screenToLocalXf}
+                outerXf={outerXf}
+                parentKey={key}
+                active={active}
+                treeViewHideHeaderFields={treeViewHideHeaderFields}
+                treeViewPreventOpen={treeViewPreventOpen}
+                renderedIds={renderedIds}
+                ignoreFields={ignoreFields}
+                firstLevel={firstLevel}
+                whenActiveChanged={whenActiveChanged} />;
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 9fd81bd90..748af89ef 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -193,6 +193,16 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
     }
 }
 
+Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
+    const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
+    for (let i = 0; i < docFilters.length; i += 3) {
+        const [header, value, state] = docFilters.slice(i, i + 3);
+        if (header === facetHeader && value === facetValue) {
+            return state;
+        }
+    }
+    return undefined;
+});
 Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
     const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments);
     const set = new Set<string>();
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 2df550b47..c5a64af3e 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -222,10 +222,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 const bestTarget = DocumentManager.Instance.getFirstDocumentView(presTargetDoc)?.props.Document;
                 bestTarget && runInAction(() => {
                     if (activeItem.presMovement === PresMovement.Jump) {
-                        bestTarget!._viewTransition = '0s';
+                        bestTarget._viewTransition = '0s';
                     } else {
-                        bestTarget!._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s';
-                        setTimeout(() => bestTarget!._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010);
+                        bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s';
+                        setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010);
                     }
                 });
             } else {
@@ -301,10 +301,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
             const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
             bestTarget && runInAction(() => {
-                bestTarget!._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-                bestTarget!._panX = activeItem.presPinViewX;
-                bestTarget!._panY = activeItem.presPinViewY;
-                bestTarget!._viewScale = activeItem.presPinViewScale;
+                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+                bestTarget._panX = activeItem.presPinViewX;
+                bestTarget._panY = activeItem.presPinViewY;
+                bestTarget._viewScale = activeItem.presPinViewScale;
             });
             //setTimeout(() => targetDoc._viewTransition = undefined, 1010);
         }
@@ -571,7 +571,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             if (this.childDocs.includes(doc)) {
                 if (docs.length === i + 1) return false;
             } else if (doc.type === DocumentType.LABEL) {
-                const audio = Cast(doc.annotationOn, Doc, null) as Doc;
+                const audio = Cast(doc.annotationOn, Doc, null);
                 if (audio) {
                     audio.aliasOf instanceof Doc;
                     audio.presStartTime = NumCast(doc.audioStart);
@@ -617,9 +617,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const list = this._selectedArray.map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
-            if (tagDoc) return (
-                <div className="selectedList-items">{index + 1}.  {curDoc.title}</div>
-            ); else if (curDoc) return <div className="selectedList-items">{index + 1}.  {curDoc.title}</div>
+            if (tagDoc) return <div className="selectedList-items">{index + 1}.  {curDoc.title}</div>;
+            else if (curDoc) return <div className="selectedList-items">{index + 1}.  {curDoc.title}</div>;
         });
         return list;
     }
@@ -996,7 +995,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     @computed get optionsDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin:"auto", width: 16, filter: 'invert(1)' }} />;
+        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 16, filter: 'invert(1)' }} />;
         if (activeItem && targetDoc) {
             return (
                 <div>
@@ -1047,8 +1046,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                                         const scale = targetDoc._viewScale;
                                         activeItem.presPinViewX = x;
                                         activeItem.presPinViewY = y;
-                                        activeItem.presPinViewScale = scale
-                                    }}>Update</div> : (null)}
+                                        activeItem.presPinViewScale = scale;
+                                    }}>Update</div> : (null)};
                             </div>
                             <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
                                 <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 0c8b20ae3..e9ab5911d 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -115,7 +115,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
 
     @computed get duration() {
         let durationInS: number;
-        if (this.rootDoc.type === DocumentType.AUDIO) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10 }
+        if (this.rootDoc.type === DocumentType.AUDIO) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; }
         else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
         else durationInS = 2;
         return this.rootDoc.presMovement === PresMovement.Jump ? (null) : "D: " + durationInS + "s";
-- 
cgit v1.2.3-70-g09d2


From f87037b13e0c97d1c9383ac023c8358963a58cff Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 20 Sep 2020 17:20:38 -0400
Subject: fixed slide not setting focus on first bullet point after hitting
 enter.

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

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 46f18099a..e61b9624a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -121,7 +121,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const bullet = TreeView.makeTextBullet();
         bullet.context = this.doc;
         this.addDoc(bullet, childDocs.length ? childDocs[0] : undefined, true);
-        setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
     });
 
     editableTitle = (childDocs: Doc[]) => {
-- 
cgit v1.2.3-70-g09d2


From e4ae6fa6df6bb0118f113bbdf3a40f768405f7b3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 20 Sep 2020 23:53:04 -0400
Subject: fixed undo for bullet points to not take multiple steps and to work
 properly.  no longer adds cursorfiels to the undo stack. fixed sharing
 manager to no create unnecesary documents..

---
 src/client/util/SelectionManager.ts                |   3 -
 src/client/util/SharingManager.tsx                 | 102 +++++----------------
 src/client/views/GlobalKeyHandler.ts               |   2 +
 .../views/collections/CollectionTreeView.tsx       |  15 ++-
 src/client/views/collections/TreeView.tsx          |  10 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   8 +-
 src/fields/Doc.ts                                  |   7 +-
 src/fields/util.ts                                 |  12 ++-
 8 files changed, 62 insertions(+), 97 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 7795a4a59..008ce281c 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -37,7 +37,6 @@ export namespace SelectionManager {
                 manager.SelectedDocuments.clear();
                 manager.SelectedDocuments.set(docView, true);
             }
-            Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
         }
         @action
         DeselectDoc(docView: DocumentView): void {
@@ -45,7 +44,6 @@ export namespace SelectionManager {
             if (manager.SelectedDocuments.get(docView)) {
                 manager.SelectedDocuments.delete(docView);
                 docView.props.whenActiveChanged(false);
-                Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
             }
         }
         @action
@@ -54,7 +52,6 @@ export namespace SelectionManager {
             manager.SelectedSchemaDocument = undefined;
             Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments.clear();
-            Doc.UserDoc().activeSelection = new List<Doc>([]);
         }
     }
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7991022d2..d3500970d 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, runInAction } from "mobx";
+import { action, observable, runInAction, computed } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
 import Select from "react-select";
@@ -7,7 +7,7 @@ import * as RequestPromise from "request-promise";
 import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym } from "../../fields/Doc";
 import { List } from "../../fields/List";
 import { Cast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
+import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from "../../fields/util";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { CollectionView } from "../views/collections/CollectionView";
@@ -148,7 +148,7 @@ export class SharingManager extends React.Component<{}> {
      * @param group 
      * @param permission 
      */
-    setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => {
+    setInternalGroupSharing = (group: Doc | { groupName: string }, permission: string, targetDoc?: Doc) => {
 
         const target = targetDoc || this.targetDoc!;
         const key = StrCast(group.groupName).replace(".", "_");
@@ -160,7 +160,7 @@ export class SharingManager extends React.Component<{}> {
             doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
             GetEffectiveAcl(doc) === AclAdmin && distributeAcls(acl, permission as SharingPermissions, doc);
 
-            if (key !== "Public") {
+            if (group instanceof Doc) {
                 const members: string[] = JSON.parse(StrCast(group.members));
                 const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
 
@@ -393,39 +393,22 @@ export class SharingManager extends React.Component<{}> {
     /**
      * @returns the main interface of the SharingManager.
      */
-    private get sharingInterface() {
-
+    @computed get sharingInterface() {
+        TraceMobx();
         const groupList = GroupManager.Instance?.getAllGroups() || [];
-        const sortedUsers = this.users.slice().sort(this.sortUsers)
-            .map(({ user: { email } }) => ({ label: email, value: indType + email }));
-        const sortedGroups = groupList.slice().sort(this.sortGroups)
-            .map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) }));
+        const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email }));
+        const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) }));
 
         // the next block handles the users shown (individuals/groups/both)
         const options: GroupedOptions[] = [];
         if (GroupManager.Instance) {
             if ((this.showUserOptions && this.showGroupOptions) || (!this.showUserOptions && !this.showGroupOptions)) {
-                options.push({
-                    label: 'Individuals',
-                    options: sortedUsers
-                },
-                    {
-                        label: 'Groups',
-                        options: sortedGroups
-                    });
-            }
-            else if (this.showUserOptions) {
-                options.push({
-                    label: 'Individuals',
-                    options: sortedUsers
-                });
-            }
-            else {
-                options.push({
-                    label: 'Groups',
-                    options: sortedGroups
-                });
+                options.push(
+                    { label: 'Individuals', options: sortedUsers },
+                    { label: 'Groups', options: sortedGroups });
             }
+            else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
+            else options.push({ label: 'Groups', options: sortedGroups });
         }
 
         const users = this.individualSort === "ascending" ? this.users.slice().sort(this.sortUsers) : this.individualSort === "descending" ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
@@ -510,12 +493,10 @@ export class SharingManager extends React.Component<{}> {
                 ) : null
         );
 
-        // dummy doc for public acl
-        const publicDoc = new Doc;
-        publicDoc.groupName = "Public";
+
         // the list of groups shared with
-        const groupListMap = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true);
-        groupListMap.unshift(publicDoc);
+        const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true);
+        groupListMap.unshift({ groupName: "Public" });
         const groupListContents = groupListMap.map(group => {
             const groupKey = `acl-${StrCast(group.groupName)}`;
             const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]);
@@ -527,7 +508,7 @@ export class SharingManager extends React.Component<{}> {
                     className={"container"}
                 >
                     <div className={"padding"}>{group.groupName}</div>
-                    {group.groupName !== "Public" ?
+                    {group instanceof Doc ?
                         (<div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}>
                             <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
                         </div>)
@@ -559,36 +540,6 @@ export class SharingManager extends React.Component<{}> {
                         onCloseButtonClick={action(() => GroupManager.Instance.currentGroup = undefined)}
                     /> :
                     null}
-                {/* <p className={"share-link"}>Manage the public link to {this.focusOn("this document...")}</p>
-                {!this.linkVisible ? (null) :
-                    <div className={"link-container"}>
-                        <div className={"link-box"} onClick={this.copy}>{this.sharingUrl}</div>
-                        <div
-                            title={"Copy link to clipboard"}
-                            className={"copy"}
-                            style={{ backgroundColor: this.copied ? "lawngreen" : "gainsboro" }}
-                            onClick={this.copy}
-                        >
-                            <FontAwesomeIcon icon={"copy"} />
-                        </div>
-                    </div>
-                }
-                <div className={"people-with-container"}>
-                    {!this.linkVisible ? (null) : <p className={"people-with"}>People with this link</p>}
-                    <select
-                        className={"people-with-select"}
-                        value={this.sharingDoc ? StrCast(this.sharingDoc[PublicKey], SharingPermissions.None) : SharingPermissions.None}
-                        style={{
-                            marginLeft: this.linkVisible ? 10 : 0,
-                            color: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[PublicKey], SharingPermissions.None)) : DefaultColor,
-                            borderColor: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[PublicKey], SharingPermissions.None)) : DefaultColor
-                        }}
-                        onChange={e => this.setExternalSharing(e.currentTarget.value)}
-                    >
-                        {this.sharingOptions}
-                    </select>
-                </div>
-                <div className={"hr-substitute"} /> */}
                 <div className="sharing-contents">
                     <p className={"share-title"}><b>Share </b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, "this document") : "-multiple-")}</p>
                     <div className={"close-button"} onClick={this.close}>
@@ -660,16 +611,13 @@ export class SharingManager extends React.Component<{}> {
     }
 
     render() {
-        return (
-            <MainViewModal
-                contents={this.sharingInterface}
-                isDisplayed={this.isOpen}
-                interactive={true}
-                dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
-                overlayDisplayedOpacity={this.overlayOpacity}
-                closeOnExternalClick={this.close}
-            />
-        );
+        return <MainViewModal
+            contents={this.sharingInterface}
+            isDisplayed={this.isOpen}
+            interactive={true}
+            dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
+            overlayDisplayedOpacity={this.overlayOpacity}
+            closeOnExternalClick={this.close}
+        />;
     }
-
 }
\ No newline at end of file
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index ba9334c40..0c05a1b69 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -206,10 +206,12 @@ export class KeyManager {
                 preventDefault = false;
                 break;
             case "y":
+                SelectionManager.DeselectAll();
                 UndoManager.Redo();
                 stopPropagation = false;
                 break;
             case "z":
+                SelectionManager.DeselectAll();
                 UndoManager.Undo();
                 stopPropagation = false;
                 break;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e61b9624a..efbe5581b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -24,6 +24,8 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { TreeView } from "./TreeView";
 import React = require("react");
+import { DocumentManager } from '../../util/DocumentManager';
+import { FormattedTextBoxComment } from '../nodes/formattedText/FormattedTextBoxComment';
 
 export type collectionTreeViewProps = {
     treeViewHideTitle?: boolean;
@@ -61,19 +63,26 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         this.treedropDisposer?.();
     }
 
-    @action
-    remove = (doc: Doc | Doc[]): boolean => {
+    @undoBatch
+    remove = action((doc: Doc | Doc[]): boolean => {
         const docs = doc instanceof Doc ? [doc] : doc;
         const targetDataDoc = this.doc[DataSym];
         const value = DocListCast(targetDataDoc[this.props.fieldKey]);
         const result = value.filter(v => !docs.includes(v));
         SelectionManager.DeselectAll();
         if (result.length !== value.length) {
+            const ind = targetDataDoc[this.props.fieldKey].indexOf(doc);
             targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
+            if (ind > 0) {
+                const prev = targetDataDoc[this.props.fieldKey][ind - 1];
+                FormattedTextBox.SelectOnLoad = prev[Id];
+                const prevView = DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView);
+                prevView?.select(false);
+            }
             return true;
         }
         return false;
-    }
+    })
     @action
     addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
         const doAddDoc = (doc: Doc | Doc[]) =>
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index e509eb78d..edb703135 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -121,12 +121,16 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
         this.props.treeView.props.select(false);
-        return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
+        const ind = this.dataDoc[key].indexOf(doc);
+        const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
+        res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false);
+        return res;
     }
     @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
 
     constructor(props: any) {
         super(props);
+        console.log("Ttile = " + this.props.document.title);
         const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
         const openScript = ScriptField.MakeScript(`openOnRight(self)`);
         const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
@@ -218,7 +222,6 @@ export class TreeView extends React.Component<TreeViewProps> {
         })}
         onClick={() => {
             SelectionManager.DeselectAll();
-            Doc.UserDoc().activeSelection = new List([this.doc]);
             return false;
         }}
         OnEmpty={undoBatch(() => this.props.treeView.doc.treeViewOutlineMode && this.props.removeDoc?.(this.doc))}
@@ -552,8 +555,9 @@ export class TreeView extends React.Component<TreeViewProps> {
                 : <div className={`treeView-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
                     onKeyDown={e => {
                         e.stopPropagation();
+                        e.preventDefault();
                         switch (e.key) {
-                            case "Backspace": return this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
+                            case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
                             case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
                             case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
                                 return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 771b6bbbe..41969fd3e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1162,6 +1162,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 FormattedTextBox.SelectOnLoadChar = "";
             } else if (curText?.Text) {
                 selectAll(this._editorView!.state, this._editorView?.dispatch);
+                this.startUndoTypingBatch();
             }
 
         }
@@ -1411,7 +1412,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     public startUndoTypingBatch() {
-        this._undoTyping = UndoManager.StartBatch("undoTyping");
+        !this._undoTyping && (this._undoTyping = UndoManager.StartBatch("undoTyping"));
     }
 
     public endUndoTypingBatch() {
@@ -1425,6 +1426,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     public static LiveTextUndo: UndoManager.Batch | undefined;
     public static HadSelection: boolean = false;
     onBlur = (e: any) => {
+        console.log("blur ", document.activeElement?.textContent);
         FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
         this.endUndoTypingBatch();
         this.doLinkOnDeselect();
@@ -1473,9 +1475,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
         }
 
-        if (!this._undoTyping) {
-            this.startUndoTypingBatch();
-        }
+        this.startUndoTypingBatch();
     }
 
     ondrop = (eve: React.DragEvent) => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ba7c9c7da..470f9d4be 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -24,6 +24,7 @@ import { LinkManager } from "../client/util/LinkManager";
 import JSZip = require("jszip");
 import { saveAs } from "file-saver";
 import { CollectionDockingView } from "../client/views/collections/CollectionDockingView";
+import { SelectionManager } from "../client/util/SelectionManager";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -1295,8 +1296,8 @@ Scripting.addGlobal(function docList(field: any) { return DocListCast(field); })
 Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
 Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
 Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); });
-Scripting.addGlobal(function undo() { return UndoManager.Undo(); });
-Scripting.addGlobal(function redo() { return UndoManager.Redo(); });
+Scripting.addGlobal(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
+Scripting.addGlobal(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); });
 Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
 Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
 Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
@@ -1305,7 +1306,7 @@ Scripting.addGlobal(function activePresentationItem() {
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
-    const docs = DocListCast(Doc.UserDoc().activeSelection).
+    const docs = SelectionManager.SelectedDocuments().map(dv => dv.props.Document).
         filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP &&
             (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
     return docs.length ? new List(docs) : prevValue;
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 9db79ced1..9e5890aa8 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -10,6 +10,8 @@ import { DocServer } from "../client/DocServer";
 import { ComputedField } from "./ScriptField";
 import { ScriptCast, StrCast } from "./Types";
 import { returnZero } from "../Utils";
+import CursorField from "./CursorField";
+import { List } from "@material-ui/core";
 
 
 function _readOnlySetter(): never {
@@ -360,10 +362,12 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
             const oldValue = current;
             const newValue = ObjectField.MakeCopy(value);
             current = newValue;
-            UndoManager.AddEvent({
-                redo() { receiver[prop] = newValue; },
-                undo() { receiver[prop] = oldValue; }
-            });
+            if (!(value instanceof CursorField) && !(value?.some((v: any) => v instanceof CursorField))) {
+                UndoManager.AddEvent({
+                    redo() { receiver[prop] = newValue; },
+                    undo() { receiver[prop] = oldValue; }
+                });
+            }
         }
         target[Update](diff);
     };
-- 
cgit v1.2.3-70-g09d2


From 19d22f589c1ce0475b87d5c27859d3dde76c084b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Sep 2020 14:36:24 -0400
Subject: fixed text box llnk preview to go away when docs are closed.  fixed
 tree view to be more efficient by not re-rendering when items are selected. 
 made slide view text items forceActive

---
 .../views/collections/CollectionTreeView.scss      |  1 +
 .../views/collections/CollectionTreeView.tsx       | 44 +++++++-----
 src/client/views/collections/TreeView.tsx          | 21 +++---
 src/client/views/nodes/DocumentContentsView.tsx    | 19 ++---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 82 +++++++++-------------
 .../formattedText/FormattedTextBoxComment.tsx      | 13 ++--
 6 files changed, 84 insertions(+), 96 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 20bfc0e9d..c5add7cfb 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -28,6 +28,7 @@
 
     .no-indent {
         padding-left: 0;
+        width: max-content;
     }
 
     .editableView-container {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index efbe5581b..e2247aec3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -41,7 +41,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     private _mainEle?: HTMLDivElement;
     public _uniqueId = Utils.GenerateGuid();
 
-    @computed get doc() { return this.props.Document; }
+    @computed get doc() { TraceMobx(); return this.props.Document; }
     @computed get dataDoc() { return this.props.DataDoc || this.doc; }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
@@ -117,7 +117,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {});
 
     @computed get renderClearButton() {
-        return <div key="toolbar">
+        return !this.doc.allowClear ? (null) : <div key="toolbar">
             <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
                 <FontAwesomeIcon icon={"trash"} size="sm" />
             </button>
@@ -192,37 +192,45 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
     whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); };
     active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
-    render() {
+    @computed get treeChildren() {
+        TraceMobx();
+        return this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
+    }
+    @computed get treeViewElements() {
         TraceMobx();
-        if (!(this.doc instanceof Doc)) return (null);
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
-        const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
-        const childElements = childDocs && TreeView.GetChildElements(childDocs, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
+        return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
             this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
             this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged);
+    }
+    @computed get titleBar() {
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
-        const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth);
+        return hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren)
+    }
+    render() {
+        TraceMobx();
+        if (!(this.doc instanceof Doc)) return (null);
+        const background = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth);
+        const paddingX = `${NumCast(this.doc._xPadding, 10)}px`;
+        const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`;
+        const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined;
 
-        return !childDocs ? (null) : (
+        return !this.treeChildren ? (null) : (
             <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
                 <div className="collectionTreeView-dropTarget"
-                    style={{
-                        background: backgroundColor,
-                        paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
-                        paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
-                        paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
-                        pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined,
-                    }}
+                    style={{ background, paddingLeft: paddingX, paddingRight: paddingX, paddingTop, pointerEvents }}
                     onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                     onDrop={this.onTreeDrop}
                     ref={this.createTreeDropTarget}>
-                    {hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(childDocs)}
-                    {this.doc.allowClear ? this.renderClearButton : (null)}
-                    <ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul>
+                    {this.titleBar}
+                    {this.renderClearButton}
+                    <ul className="no-indent">
+                        {this.treeViewElements}
+                    </ul>
                 </div >
             </div>
         );
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index edb703135..486e44f6b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -83,9 +83,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _uniqueId = Utils.GenerateGuid();
     private _editMaxWidth: number | string = 0;
 
-    get doc() { return this.props.document; }
+    @computed get doc() { TraceMobx(); return this.props.document; }
     get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
-    get displayName() { return "TreeView(" + this.doc.title + ")"; }  // this makes mobx trace() statements more descriptive
+    get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
     get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
     get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
     get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
@@ -100,14 +100,14 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
     @computed get dataDoc() { return this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
-    @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
+    @computed get fieldKey() { TraceMobx(); const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
     childDocList(field: string) {
         const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
         return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
             (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
             DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
     }
-    @computed get childDocs() { return this.childDocList(this.fieldKey); }
+    @computed get childDocs() { TraceMobx(); return this.childDocList(this.fieldKey); }
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
     @computed get boundsOfCollectionDocument() {
@@ -130,13 +130,12 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     constructor(props: any) {
         super(props);
-        console.log("Ttile = " + this.props.document.title);
         const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
         const openScript = ScriptField.MakeScript(`openOnRight(self)`);
         const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
         this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
         this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
-        if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
+        if (Doc.GetT(this.props.document, "editTitle", "string", true) === "*") Doc.SetInPlace(this.props.document, "editTitle", this._uniqueId, false);
     }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
@@ -185,7 +184,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
+        const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", forceActive: true, _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 });
         Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -659,8 +658,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         onChildClick: undefined | (() => ScriptField),
         ignoreFields: string[] | undefined,
         firstLevel: boolean,
-        whenActiveChanged: (isActive: boolean) => void
-    ) {
+        whenActiveChanged: (isActive: boolean) => void) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
             childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
@@ -691,7 +689,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         }
 
         const rowWidth = () => panelWidth() - 20;
-        return docs.map((child, i) => {
+        return docs.filter(child => child instanceof Doc).map((child, i) => {
             const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child);
             if (!pair.layout || pair.data instanceof Promise) {
                 return (null);
@@ -727,13 +725,12 @@ export class TreeView extends React.Component<TreeViewProps> {
                 const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0);
                 return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
             };
-            return !(child instanceof Doc) ? (null) : <TreeView
+            return <TreeView key={child[Id]}
                 document={pair.layout}
                 dataDoc={pair.data}
                 containingCollection={containingCollection}
                 prevSibling={docs[i]}
                 treeView={treeView}
-                key={child[Id]}
                 indentDocument={indent}
                 outdentDocument={!parentCollectionDoc ? undefined : outdent}
                 onCheckedClick={onCheckedClick}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 67e7c1986..e8c3662aa 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -114,22 +114,13 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
 }> {
     @computed get layout(): string {
         TraceMobx();
-        if (!this.layoutDoc) return "<p>awaiting layout</p>";
-        // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string");  // bcz: replaced this with below... is it right?
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
+        if (!this.layoutDoc) return "<p>awaiting layout</p>";
+        if (this.props.layoutKey === "layout_keyValue") return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
         const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
-        if (this.props.layoutKey === "layout_keyValue") {
-            return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
-        } else
-            if (layout === undefined) {
-                return this.props.Document.data ?
-                    "<FieldView {...props} fieldKey='data' />" :
-                    KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
-            } else if (typeof layout === "string") {
-                return layout;
-            } else {
-                return "<p>Loading layout</p>";
-            }
+        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
+        if (typeof layout === "string") return layout;
+        return "<p>Loading layout</p>";
     }
 
     get dataDoc() {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ff2ce90b7..e92efd708 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1189,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this.endUndoTypingBatch();
         Object.values(this._disposers).forEach(disposer => disposer?.());
         this._editorView?.destroy();
-        FormattedTextBoxComment.Hide();
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
     }
 
     _downEvent: any;
@@ -1522,16 +1522,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
     render() {
         TraceMobx();
+        const selected = this.props.isSelected();
+        const active = this.active();
         const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground;
-        this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
-        if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
-            setTimeout(() => FormattedTextBoxComment.Hide(), 0);
-        }
+        selected && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props)); // need to make sure that we update a text box that is selected after updating the one that was deselected
+        if (!selected && FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); }
         const minimal = this.props.ignoreAutoHeight;
-        const selPad = (this.props.isSelected() && !this.layoutDoc._singleLine) || minimal ? -10 : 0;
-        const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : "";
+        const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? -10 : 0;
+        const selclass = selected && !this.layoutDoc._singleLine ? "-selected" : "";
         return (
             <div className={"formattedTextBox-cont"} ref={this._boxRef}
                 style={{
@@ -1564,65 +1564,53 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     onMouseUp={this.onMouseUp}
                     onWheel={this.onPointerWheel}
                     onPointerEnter={action(() => this._entered = true)}
-                    onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
+                    onPointerLeave={action(e => {
                         this._entered = false;
                         const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
                         for (let child: any = target; child; child = child?.parentElement) {
-                            if (child === this._ref.current!) {
-                                this._entered = true;
-                            }
+                            child === this._ref.current! && (this._entered = true);
                         }
                     })}
                     onDoubleClick={this.onDoubleClick}
                 >
                     <div className={`formattedTextBox-outer`} ref={this._scrollRef}
-                        style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !this.props.active() ? "none" : undefined }}
+                        style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active ? "none" : undefined }}
                         onScroll={this.onscrolled} onDrop={this.ondrop} >
                         <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
                             style={{
                                 overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
                                 padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) :
                                     `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px  ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
-                                pointerEvents: !this.props.active() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
+                                pointerEvents: !active ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
                             }}
                         />
                     </div>
-                    {!this.layoutDoc._showSidebar ? (null) : <>
-                        <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
-                            style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-                            {this.sidebarWidthPercent === "0%" ? (null) :
-                                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                    PanelHeight={this.active() ? () => 1000 : this.props.PanelHeight}
-                                    PanelWidth={this.sidebarWidth}
-                                    scaleField={this.annotationKey + "-scale"}
-                                    annotationsKey={this.annotationKey}
-                                    isAnnotationOverlay={true}
-                                    focus={this.props.focus}
-                                    isSelected={this.props.isSelected}
-                                    select={emptyFunction}
-                                    active={this.annotationsActive}
-                                    ContentScaling={returnOne}
-                                    whenActiveChanged={this.whenActiveChanged}
-                                    removeDocument={this.removeDocument}
-                                    moveDocument={this.moveDocument}
-                                    addDocument={this.addDocument}
-                                    CollectionView={undefined}
-                                    ScreenToLocalTransform={this.sidebarScreenToLocal}
-                                    renderDepth={this.props.renderDepth + 1}
-                                    ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
-                            }
-                        </div>
-                        {this.props.isSelected() ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)}
-                    </>}
+                    {!this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
+                        <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                                PanelHeight={active ? () => 1000 : this.props.PanelHeight}
+                                PanelWidth={this.sidebarWidth}
+                                scaleField={this.annotationKey + "-scale"}
+                                annotationsKey={this.annotationKey}
+                                isAnnotationOverlay={true}
+                                focus={this.props.focus}
+                                isSelected={this.props.isSelected}
+                                select={emptyFunction}
+                                active={this.annotationsActive}
+                                ContentScaling={returnOne}
+                                whenActiveChanged={this.whenActiveChanged}
+                                removeDocument={this.removeDocument}
+                                moveDocument={this.moveDocument}
+                                addDocument={this.addDocument}
+                                CollectionView={undefined}
+                                ScreenToLocalTransform={this.sidebarScreenToLocal}
+                                renderDepth={this.props.renderDepth + 1}
+                                ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
+                        </div>}
+                    {selected ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)}
                     {!this.layoutDoc._showAudio ? (null) :
                         <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
-                            <FontAwesomeIcon className="formattedTextBox-audioFont"
-                                style={{
-                                    color: this._recording ? "red" : "blue",
-                                    transitionDelay: "0.6s",
-                                    opacity: this._recording ? 1 : 0.25,
-                                }}
-                                icon={"microphone"} size="sm" />
+                            <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
                         </div>}
                 </div>
             </div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index a37210de6..14bbee1ac 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -143,9 +143,12 @@ export class FormattedTextBoxComment {
 
     public static Hide() {
         FormattedTextBoxComment.textBox = undefined;
-        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
-        ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
         FormattedTextBoxComment.linkDoc = undefined;
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
+        try {
+            ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+            FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
+        } catch (e) { }
     }
     public static SetState(textBox: any, start: number, end: number, mark: Mark) {
         FormattedTextBoxComment.textBox = textBox;
@@ -228,17 +231,17 @@ export class FormattedTextBoxComment {
             if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
                 try {
                     ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+                    FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
                 } catch (e) { }
-                FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
                 FormattedTextBoxComment.tooltipText = document.createElement("div");
                 FormattedTextBoxComment.tooltipText.style.width = "100%";
                 FormattedTextBoxComment.tooltipText.style.height = "100%";
                 FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
                 FormattedTextBoxComment.tooltipText.style.cursor = "pointer";
-                FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
-
                 FormattedTextBoxComment.tooltipText.textContent = "URL: " + href;
                 (FormattedTextBoxComment.tooltipText as any).href = href;
+                FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+
                 if (href.startsWith("https://en.wikipedia.org/wiki/")) {
                     wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
                 } else {
-- 
cgit v1.2.3-70-g09d2


From 02029b5cad1c27e65630a63cd4d2b632ed6973cd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Sep 2020 20:42:51 -0400
Subject: changed acls so that only an admin owner distributes them when a doc
 is added.  changed default acls to be Add by default when not in private
 mode.   restored filtering from search bar for developer mode.

---
 src/client/documents/Documents.ts                   | 3 ++-
 src/client/views/DocumentDecorations.tsx            | 2 +-
 src/client/views/collections/CollectionTreeView.tsx | 4 ++--
 src/client/views/collections/CollectionView.tsx     | 3 ++-
 src/client/views/nodes/FilterBox.tsx                | 2 +-
 src/client/views/search/SearchBox.tsx               | 6 +++---
 6 files changed, 11 insertions(+), 9 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f7ab955f3..2d02742c5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -52,6 +52,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
 import { FilterBox } from "../views/nodes/FilterBox";
 import { SharingPermissions } from "../../fields/util";
+import { SharingManager } from "../util/SharingManager";
 const path = require('path');
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
@@ -601,7 +602,7 @@ export namespace Docs {
             viewDoc.author = Doc.CurrentUserEmail;
             viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
 
-            if (Doc.UserDoc()?.defaultAclPrivate) viewDoc["acl-Public"] = dataDoc["acl-Public"] = "Not Shared";
+            viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
 
             return Doc.assign(viewDoc, delegateProps, true);
         }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 795208c91..9432d1ffb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -139,7 +139,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         dragData.offset = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
         dragData.moveDocument = dragDocView.props.moveDocument;
         dragData.isSelectionMove = true;
-        dragData.dropAction = dragDocView.props.dropAction as dropActionType;
+        dragData.dropAction = dragDocView.props.dropAction;
         this.Interacting = true;
         this._hidden = true;
         DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e2247aec3..257eaf7f0 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -82,7 +82,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             return true;
         }
         return false;
-    })
+    });
     @action
     addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
         const doAddDoc = (doc: Doc | Doc[]) =>
@@ -209,7 +209,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     @computed get titleBar() {
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
-        return hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren)
+        return hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren);
     }
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c9496d374..e35474b81 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -144,7 +144,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     added.forEach(d => {
                         for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
                             if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true);
-                            else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
+                            else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true);
+                            //else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
                         }
                     });
                 }
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 805745f4a..067477dcf 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -121,7 +121,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                 }
             });
             let newFacet: Opt<Doc>;
-            if (facetHeader === "text" || facetValues.rtfields / allCollectionDocs.length > 0.1) {
+            if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) {
                 newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
                 newFacet._textBoxPadding = 4;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3238e4dc6..a6ac62ee4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -530,10 +530,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                                         style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
                                 </Tooltip>
                             </div>
-                            <div style={{ position: "absolute", left: 220, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
+                            <div style={{ position: "absolute", left: Doc.UserDoc().noviceMode ? 220 : 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
                                 {`${this._results.length}` + " of " + `${this.realTotalResults}`}
                             </div>
-                            {/* <div style={{ cursor: "default", left: 235, position: "absolute", }}>
+                            {Doc.UserDoc().noviceMode ? (null) : <div style={{ cursor: "default", left: 235, position: "absolute", }}>
                                 <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} >
                                     <div>
                                         <FontAwesomeIcon icon={"filter"} size="lg"
@@ -542,7 +542,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                                             onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
                                     </div>
                                 </Tooltip>
-                            </div> */}
+                            </div>}
                             {this.scopeButtons}
                         </div>
                     </div >
-- 
cgit v1.2.3-70-g09d2


From bc4f646c74dadf1a95b993dc0b11beb9f5f9f044 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 27 Sep 2020 22:23:12 -0400
Subject: trying a different solr schema that better supports regexp's by not
 doing stemming. fixed issues with schema and tree views where children need
 to be registered for tryUpdateHeight to work but not when they're in a
 sidebar that specifies dontRegisterView.   Changed
 stacking/freeform/tree/masonry to NOT be dontRegisterChildView -- this should
 be set by the context (ie, sidebar)

---
 solr-8.3.1/server/solr/dash/conf/schema.xml        |  6 ++---
 src/client/documents/Documents.ts                  | 10 ++++----
 .../views/collections/CollectionSchemaHeaders.tsx  | 29 ++++++++++------------
 .../views/collections/CollectionTreeView.tsx       |  4 +--
 src/client/views/collections/TreeView.tsx          | 12 ++++++---
 5 files changed, 31 insertions(+), 30 deletions(-)

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

diff --git a/solr-8.3.1/server/solr/dash/conf/schema.xml b/solr-8.3.1/server/solr/dash/conf/schema.xml
index 11078caeb..e21f97e00 100644
--- a/solr-8.3.1/server/solr/dash/conf/schema.xml
+++ b/solr-8.3.1/server/solr/dash/conf/schema.xml
@@ -11,8 +11,8 @@
       <!-- <tokenizer class="solr.StandardTokenizerFactory"/> -->
         <filter class="solr.StopFilterFactory" words="stopwords.txt"/>
         <filter class="solr.LowerCaseFilterFactory"/>
-        <filter class="solr.PorterStemFilterFactory"/>
-        <filter class="solr.NGramFilterFactory" minGramSize="2" maxGramSize="12"/>
+        <!-- <filter class="solr.PorterStemFilterFactory"/>
+        <filter class="solr.NGramFilterFactory" minGramSize="2" maxGramSize="12"/> -->
         <!-- <filter catenateAll="0" catenateNumbers="0" catenateWords="0" class="solr.WordDelimiterFilterFactory" generateNumberParts="1" generateWordParts="1" splitOnCaseChange="1" types="characters.txt"/> -->
     </analyzer>
     <analyzer type="query">
@@ -20,7 +20,7 @@
       <!-- <tokenizer class="solr.StandardTokenizerFactory"/> -->
         <filter class="solr.StopFilterFactory" words="stopwords.txt"/>
         <filter class="solr.LowerCaseFilterFactory"/>
-        <filter class="solr.PorterStemFilterFactory"/>
+        <!-- <filter class="solr.PorterStemFilterFactory"/> -->
         <!-- <filter catenateAll="0" catenateNumbers="0" catenateWords="0" class="solr.WordDelimiterFilterFactory" generateNumberParts="1" generateWordParts="1" splitOnCaseChange="1" types="characters.txt"/> -->
     </analyzer>
   </fieldType>
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7c420fb90..9bd428610 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -721,8 +721,8 @@ export namespace Docs {
 
             LinkManager.Instance.addLink(doc);
 
-            Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
-            Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)");
+            source.doc.links === undefined && (Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)"));
+            target.doc.links === undefined && (Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)"));
             return doc;
         }
 
@@ -774,7 +774,7 @@ export namespace Docs {
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Freeform }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Freeform }, id);
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -806,7 +806,7 @@ export namespace Docs {
         }
 
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Stacking }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id);
         }
 
         export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
@@ -818,7 +818,7 @@ export namespace Docs {
 
 
         export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Masonry });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Masonry });
         }
 
         export function LabelDocument(options?: DocumentOptions) {
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index c02e88829..66f78201e 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -297,11 +297,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
             else {
                 Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
                 this.updateFilter();
-                let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-                const blockedkeys = ["system", "title-custom", "limitHeight", "proto", "x", "y", "zIndex", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
-                keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("acl"));
-                if (keyOptions.length) {
-                    this.onSelect(keyOptions[0]);
+                if (this.showKeys.length) {
+                    this.onSelect(this.showKeys[0]);
                 } else if (this._searchTerm !== "" && this.props.canAddNew) {
                     this.setSearchTerm(this._searchTerm || this._key);
                     this.onSelect(this._searchTerm);
@@ -320,22 +317,22 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         this.props.setIsEditing(true);
     }
 
+    @computed get showKeys() {
+        const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
+        let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+        const showKeys = new Set<string>();
+        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode ||
+            whitelistKeys.includes(key)
+            || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
+        return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
+    }
     @action
     renderOptions = (): JSX.Element[] | JSX.Element => {
         if (!this._isOpen) {
             this.defaultMenuHeight = 0;
             return <></>;
         }
-        const searchTerm = this._searchTerm.trim() === "New field" ? "" : this._searchTerm;
-
-        let keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-        const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
-            this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
-
-        const blockedkeys = ["proto", "x", "y", "zIndex", "_timeStampOnEnter", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
-        keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("acl"));
-
-        const options = keyOptions.map(key => {
+        const options = this.showKeys.map(key => {
             return <div key={key} className="key-option" style={{
                 border: "1px solid lightgray",
                 width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
@@ -352,7 +349,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         // if search term does not already exist as a group type, give option to create new group type
 
         if (this._key !== this._searchTerm.slice(0, this._key.length)) {
-            if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
+            if (this._searchTerm !== "" && this.props.canAddNew) {
                 options.push(<div key={""} className="key-option" style={{
                     border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
                 }}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 257eaf7f0..549d841e4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,7 +6,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { Document } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
@@ -205,7 +205,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
             this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
-            this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged);
+            this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null));
     }
     @computed get titleBar() {
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 486e44f6b..744e8fed6 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -48,6 +48,7 @@ export interface TreeViewProps {
     indentDocument?: () => void;
     outdentDocument?: () => void;
     ScreenToLocalTransform: () => Transform;
+    dontRegisterView?: boolean;
     backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
     outerXf: () => { translateX: number, translateY: number };
     treeView: CollectionTreeView;
@@ -319,7 +320,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
+                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView);
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -372,7 +373,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
+                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
@@ -387,6 +388,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     Document={this.doc}
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
+                    dontRegisterView={this.props.dontRegisterView}
                     renderDepth={this.props.renderDepth + 1}
                     rootSelected={returnTrue}
                     treeViewDoc={undefined}
@@ -658,7 +660,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         onChildClick: undefined | (() => ScriptField),
         ignoreFields: string[] | undefined,
         firstLevel: boolean,
-        whenActiveChanged: (isActive: boolean) => void) {
+        whenActiveChanged: (isActive: boolean) => void,
+        dontRegisterView: boolean | undeifned) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
             childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
@@ -709,7 +712,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             const outdent = !parentCollectionDoc ? undefined : () => {
-                if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+                if (parentCollectionDoc._viewType === CollectionViewType.Tree && remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
                     const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
                     const fieldKey = fieldKeysub.split("\'")[1];
                     remove(child);
@@ -742,6 +745,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 panelWidth={rowWidth}
                 panelHeight={rowHeight}
                 ChromeHeight={ChromeHeight}
+                dontRegisterView={dontRegisterView}
                 moveDocument={move}
                 dropAction={dropAction}
                 addDocTab={addDocTab}
-- 
cgit v1.2.3-70-g09d2


From 3c6e77b7d2bcf4782d981b19bc32e402e07863ad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 28 Sep 2020 10:11:12 -0400
Subject: fixed docRangeFilters to be propagated everywhere and evaluated like
 docFilters (if the field isn't there, it fails)

---
 src/client/documents/Documents.ts                            |  2 +-
 src/client/views/GestureOverlay.tsx                          |  1 +
 src/client/views/MainView.tsx                                |  7 +++++++
 src/client/views/OverlayView.tsx                             |  1 +
 src/client/views/Palette.tsx                                 |  1 +
 src/client/views/PropertiesView.tsx                          |  1 +
 src/client/views/TemplateMenu.tsx                            |  1 +
 src/client/views/collections/CollectionLinearView.tsx        |  1 +
 src/client/views/collections/CollectionSchemaView.tsx        |  1 +
 src/client/views/collections/CollectionStackingView.tsx      |  1 +
 src/client/views/collections/CollectionSubView.tsx           | 12 ++++++++----
 src/client/views/collections/CollectionTreeView.tsx          |  1 +
 src/client/views/collections/SchemaTable.tsx                 |  1 +
 src/client/views/collections/TabDocView.tsx                  |  2 ++
 src/client/views/collections/TreeView.tsx                    |  3 +++
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  1 +
 .../collectionMulticolumn/CollectionMulticolumnView.tsx      |  1 +
 .../collectionMulticolumn/CollectionMultirowView.tsx         |  1 +
 src/client/views/nodes/DocHolderBox.tsx                      |  2 ++
 src/client/views/nodes/DocumentView.tsx                      |  2 ++
 src/client/views/nodes/FieldView.tsx                         |  1 +
 src/client/views/nodes/FilterBox.tsx                         |  1 +
 src/client/views/nodes/ImageBox.tsx                          |  1 +
 src/client/views/nodes/KeyValuePair.tsx                      |  1 +
 src/client/views/nodes/LinkDocPreview.tsx                    |  1 +
 src/client/views/nodes/PDFBox.tsx                            |  2 +-
 src/client/views/nodes/VideoBox.tsx                          |  1 +
 src/client/views/nodes/WebBox.tsx                            |  1 +
 src/client/views/nodes/formattedText/DashDocView.tsx         |  1 +
 .../views/nodes/formattedText/FormattedTextBoxComment.tsx    |  1 +
 src/client/views/nodes/formattedText/RichTextSchema.tsx      |  1 +
 src/client/views/pdf/PDFViewer.tsx                           |  1 -
 src/client/views/presentationview/PresElementBox.tsx         |  1 +
 src/mobile/AudioUpload.tsx                                   |  1 +
 src/mobile/MobileInterface.tsx                               |  1 +
 35 files changed, 52 insertions(+), 7 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9bd428610..1d7e4f386 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -931,7 +931,7 @@ export namespace DocUtils {
                 const min = Number(docRangeFilters[i + 1]);
                 const max = Number(docRangeFilters[i + 2]);
                 const val = Cast(d[key], "number", null);
-                if (val !== undefined && (val < min || val > max)) {
+                if (val === undefined || (val < min || val > max)) {
                     return false;
                 }
             }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 97dfb7c50..18743e850 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -905,6 +905,7 @@ export class GestureOverlay extends Touchable {
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
+                docRangeFilters={returnEmptyFilter}
                 docFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ad98f7ea2..4299dba86 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -269,6 +269,7 @@ export class MainView extends React.Component {
             whenActiveChanged={emptyFunction}
             bringToFront={emptyFunction}
             docFilters={returnEmptyFilter}
+            docRangeFilters={returnEmptyFilter}
             searchFilterDocs={returnEmptyDoclist}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined}
@@ -331,6 +332,7 @@ export class MainView extends React.Component {
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
@@ -365,6 +367,7 @@ export class MainView extends React.Component {
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
@@ -484,6 +487,7 @@ export class MainView extends React.Component {
                     focus={emptyFunction}
                     whenActiveChanged={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
@@ -549,6 +553,7 @@ export class MainView extends React.Component {
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
@@ -580,6 +585,7 @@ export class MainView extends React.Component {
                     PanelHeight={() => 800}
                     ContentScaling={returnOne}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
             </div>;
@@ -645,6 +651,7 @@ export class MainView extends React.Component {
                             PanelHeight={() => 800}
                             ContentScaling={returnOne}
                             docFilters={returnEmptyFilter}
+                            docRangeFilters={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
                         />
                     </div>;
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 4b8049e14..b40c9edfb 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -204,6 +204,7 @@ export class OverlayView extends React.Component {
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 9f08a03e1..62e0fb379 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -58,6 +58,7 @@ export default class Palette extends React.Component<PaletteProps> {
                             whenActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
                             docFilters={returnEmptyFilter}
+                            docRangeFilters={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
                             ContainingCollectionView={undefined}
                             ContainingCollectionDoc={undefined} />
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 59358ce40..5877c1d6d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -284,6 +284,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     focus={returnFalse}
                     ScreenToLocalTransform={this.getTransform}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionDoc={undefined}
                     ContainingCollectionView={undefined}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index cf2118cb2..94efff4ee 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -131,6 +131,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 rootSelected={returnFalse}
                 onCheckedClick={this.scriptField}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index ae9915331..859ee9362 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -160,6 +160,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 whenActiveChanged={emptyFunction}
                                 bringToFront={emptyFunction}
                                 docFilters={this.props.docFilters}
+                                docRangeFilters={this.props.docRangeFilters}
                                 searchFilterDocs={this.props.searchFilterDocs}
                                 ContainingCollectionView={undefined}
                                 ContainingCollectionDoc={undefined} />
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 520d4c8c5..27575374a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -439,6 +439,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     PanelHeight={this.previewHeight}
                     ScreenToLocalTransform={this.getPreviewTransform}
                     docFilters={this.docFilters}
+                    docRangeFilters={this.docRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                     ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7912cfc8e..4ee00e0f4 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -207,6 +207,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             opacity={opacity}
             focus={this.focusDocument}
             docFilters={this.docFilters}
+            docRangeFilters={this.docRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a78923312..c6737780f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -111,6 +111,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             return this.props.ignoreFields?.includes("_docFilters") ? [] :
                 [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
         }
+        docRangeFilters = () => {
+            return this.props.ignoreFields?.includes("_docRangeFilters") ? [] :
+                [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])];
+        }
         searchFilterDocs = () => {
             return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
         }
@@ -132,13 +136,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
 
             const docFilters = this.docFilters();
+            const docRangeFilters = this.docRangeFilters();
             const searchDocs = this.searchFilterDocs();
             if (this.props.Document.dontRegisterView || (!docFilters.length && !searchDocs.length)) return childDocs;
 
             const docsforFilter: Doc[] = [];
-            const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
             childDocs.forEach((d) => {
-                let notFiltered = d.z || ((!searchDocs.length || searchDocs.includes(d)) && (!docFilters.length || DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript).length > 0));
+                let notFiltered = d.z || ((!searchDocs.length || searchDocs.includes(d)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript).length > 0));
                 const fieldKey = Doc.LayoutFieldKey(d);
                 const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
                 const data = d[annos ? fieldKey + "-annotations" : fieldKey];
@@ -146,13 +150,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     let subDocs = DocListCast(data);
                     if (subDocs.length > 0) {
                         let newarray: Doc[] = [];
-                        notFiltered = notFiltered || (!searchDocs.length && docFilters.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript).length);
+                        notFiltered = notFiltered || (!searchDocs.length && (!docFilters.length && !docRangeFilters.length) && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript).length);
                         while (subDocs.length > 0 && !notFiltered) {
                             newarray = [];
                             subDocs.forEach((t) => {
                                 const fieldKey = Doc.LayoutFieldKey(t);
                                 const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
-                                notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && (!docFilters.length || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript).length));
+                                notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript).length));
                                 DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
                             });
                             subDocs = newarray;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 549d841e4..a7c8a7cdc 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -173,6 +173,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 focus={this.props.focus}
                 ScreenToLocalTransform={this.titleTransform}
                 docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionDoc={this.doc}
                 ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 13aa60e7c..eda62bf0b 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -580,6 +580,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         PanelHeight={() => 150}
                         ScreenToLocalTransform={this.getPreviewTransform}
                         docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                         ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 1effed643..23e261ef6 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -301,6 +301,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     addDocTab={this.addDocTab}
                     pinToPres={TabDocView.PinDoc}
                     docFilters={CollectionDockingView.Instance.docFilters}
+                    docRangeFilters={CollectionDockingView.Instance.docRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                     fitToBox={true}
                 />
@@ -350,6 +351,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 addDocTab={this.addDocTab}
                 pinToPres={TabDocView.PinDoc}
                 docFilters={CollectionDockingView.Instance.docFilters}
+                docRangeFilters={CollectionDockingView.Instance.docRangeFilters}
                 searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 744e8fed6..072cd23db 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -402,6 +402,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     focus={returnFalse}
                     ScreenToLocalTransform={this.docTransform}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.containingCollection}
                     ContainingCollectionView={undefined}
@@ -515,6 +516,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 bringToFront={emptyFunction}
                 dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
                 docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.containingCollection}
@@ -585,6 +587,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                 focus={this.refocus}
                                 ScreenToLocalTransform={this.docTransform}
                                 docFilters={returnEmptyFilter}
+                                docRangeFilters={returnEmptyFilter}
                                 searchFilterDocs={returnEmptyDoclist}
                                 ContainingCollectionDoc={this.props.containingCollection}
                                 ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8af048d67..75ad8e5b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -964,6 +964,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             ContainingCollectionView: this.props.CollectionView,
             ContainingCollectionDoc: this.props.Document,
             docFilters: this.docFilters,
+            docRangeFilters: this.docRangeFilters,
             searchFilterDocs: this.searchFilterDocs,
             focus: this.focusDocument,
             backgroundColor: this.getClusterColor,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 0e4785d36..b99bef15e 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -233,6 +233,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
             docFilters={this.docFilters}
+            docRangeFilters={this.docRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 9a4fb0627..c5e1816d9 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -232,6 +232,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
             docFilters={this.docFilters}
+            docRangeFilters={this.docRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 91f96135f..dd254ae93 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -120,6 +120,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                     ContainingCollectionDoc={undefined}
@@ -148,6 +149,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                     ContainingCollectionDoc={undefined}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e0955f9fe..aec5ba79a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     docFilters: () => string[];
+    docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
     FreezeDimensions?: boolean;
     NativeWidth?: () => number;
@@ -915,6 +916,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}>
             <DocumentContentsView key={1}
                 docFilters={this.props.docFilters}
+                docRangeFilters={this.props.docRangeFilters}
                 searchFilterDocs={this.props.searchFilterDocs}
                 ContainingCollectionView={this.props.ContainingCollectionView}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 0d29e43b8..d65b43704 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -29,6 +29,7 @@ export interface FieldViewProps {
     dropAction: dropActionType;
     backgroundHalo?: () => boolean;
     docFilters: () => string[];
+    docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (isCtrlPressed: boolean) => void;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 067477dcf..c1978e331 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -187,6 +187,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     fieldKey={`${this.props.fieldKey}`}
                     CollectionView={undefined}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                     ContainingCollectionView={this.props.ContainingCollectionView}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c2662b35b..164456cfb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -434,6 +434,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 ScreenToLocalTransform={this.screenToLocalTransform}
                 renderDepth={this.props.renderDepth + 1}
                 docFilters={this.props.docFilters}
+                docRangeFilters={this.props.docRangeFilters}
                 searchFilterDocs={this.props.searchFilterDocs}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                 {this.contentFunc}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index e531083bf..2e2319447 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -57,6 +57,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             DataDoc: this.props.doc,
             LibraryPath: [],
             docFilters: returnEmptyFilter,
+            docRangeFilters: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             ContainingCollectionView: undefined,
             ContainingCollectionDoc: undefined,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 89ea57f65..234dce374 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -104,6 +104,7 @@ export class LinkDocPreview extends React.Component<Props> {
                 pinToPres={returnFalse}
                 dontRegisterView={true}
                 docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 64e0eeb2c..6db95e842 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -253,7 +253,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
             <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
                 setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
                 renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
-                addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters} searchFilterDocs={this.props.searchFilterDocs}
+                addDocTab={this.props.addDocTab} focus={this.props.focus} searchFilterDocs={this.props.searchFilterDocs}
                 pinToPres={this.props.pinToPres} addDocument={this.addDocument}
                 Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
                 ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 51506970d..998ddde9a 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -383,6 +383,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform}
                     renderDepth={this.props.renderDepth + 1}
                     docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                     {this.contentFunc}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 3b9c3359e..2517943d7 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -670,6 +670,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             ScreenToLocalTransform={this.scrollXf}
                             renderDepth={this.props.renderDepth + 1}
                             docFilters={this.props.docFilters}
+                            docRangeFilters={this.props.docRangeFilters}
                             searchFilterDocs={this.props.searchFilterDocs}
                             ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
                         </CollectionFreeFormView>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index ffa6e904a..123946dea 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -246,6 +246,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                             bringToFront={emptyFunction}
                             dontRegisterView={false}
                             docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
+                            docRangeFilters={this.props.tbox?.props.docRangeFilters || returnEmptyFilter}
                             searchFilterDocs={this.props.tbox?.props.searchFilterDocs || returnEmptyDoclist}
                             ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                             ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 14bbee1ac..a986cdcb9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -308,6 +308,7 @@ export class FormattedTextBoxComment {
                                             pinToPres={returnFalse}
                                             dontRegisterView={true}
                                             docFilters={returnEmptyFilter}
+                                            docRangeFilters={returnEmptyFilter}
                                             searchFilterDocs={returnEmptyDoclist}
                                             ContainingCollectionDoc={undefined}
                                             ContainingCollectionView={undefined}
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 962085f0d..1767a04de 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -154,6 +154,7 @@ export class DashDocView {
                     bringToFront={emptyFunction}
                     dontRegisterView={false}
                     docFilters={this._textBox.props.docFilters}
+                    docRangeFilters={this._textBox.props.docRangeFilters}
                     searchFilterDocs={this._textBox.props.searchFilterDocs}
                     ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                     ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6179f81fd..caa22b58a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -59,7 +59,6 @@ interface IViewerProps {
     fieldKey: string;
     Document: Doc;
     DataDoc?: Doc;
-    docFilters: () => string[];
     searchFilterDocs: () => Doc[];
     ContainingCollectionView: Opt<CollectionView>;
     PanelWidth: () => number;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index e9ab5911d..aa44c13d1 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -104,6 +104,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     bringToFront={returnFalse}
                     opacity={returnOne}
                     docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 81b3869ae..36663c85f 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -88,6 +88,7 @@ export class AudioUpload extends React.Component {
                         rootSelected={returnTrue}
                         removeDocument={undefined}
                         docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                         onClick={undefined}
                         ScreenToLocalTransform={Transform.Identity}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index cb5191e61..72914e62b 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -220,6 +220,7 @@ export class MobileInterface extends React.Component {
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
-- 
cgit v1.2.3-70-g09d2

