From 2279f029ce19c9f7f886a6966ea5f23be9468890 Mon Sep 17 00:00:00 2001
From: eleanor-park <eleanor_park@brown.edu>
Date: Wed, 21 Feb 2024 20:00:11 -0500
Subject: presentation trail tab icon changed

---
 src/client/views/MainView.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/MainView.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b6cb845a6..496cb0bdd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -452,6 +452,7 @@ export class MainView extends ObservableReactComponent<{}> {
                 fa.faAngleDown,
                 fa.faPlayCircle,
                 fa.faClock,
+                fa.faRoute,
                 fa.faRocket,
                 fa.faExchangeAlt,
                 fa.faHashtag,
-- 
cgit v1.2.3-70-g09d2


From d99f64efe9e69f2159f1ad8f851b24533a996ba5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 26 Feb 2024 12:57:48 -0500
Subject: fixed some string types to be enumerations for dropAction.  fixed bug
 in golden layout dragging where a stack's tabs could disappear.

---
 src/client/documents/Documents.ts                  | 40 ++++++++++------
 src/client/goldenLayout.js                         |  2 +-
 src/client/util/CurrentUserUtils.ts                | 44 +++++++++---------
 src/client/util/DragManager.ts                     | 54 +++++++++++++++-------
 src/client/views/MainView.tsx                      |  6 +--
 src/client/views/collections/CollectionMenu.tsx    |  4 +-
 .../views/collections/CollectionPileView.tsx       |  3 +-
 .../views/collections/CollectionTreeView.tsx       | 16 +++++--
 src/client/views/collections/TreeView.tsx          |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  9 +++-
 .../collectionSchema/CollectionSchemaView.tsx      |  4 +-
 .../collectionSchema/SchemaTableCell.tsx           |  7 +--
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  5 +-
 src/client/views/topbar/TopBar.tsx                 |  3 +-
 src/fields/documentSchemas.ts                      |  6 +--
 17 files changed, 129 insertions(+), 82 deletions(-)

(limited to 'src/client/views/MainView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2d2f5fe4a..8a13395c3 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -68,10 +68,20 @@ class EmptyBox {
         return '';
     }
 }
+
+export enum FInfoFieldType {
+    string,
+    boolean,
+    number,
+    Doc,
+    enumeration,
+    date,
+    list,
+}
 export class FInfo {
     description: string = '';
     readOnly: boolean = false;
-    fieldType?: string = '';
+    fieldType?: FInfoFieldType;
     values?: Field[];
 
     filterable?: boolean = true;
@@ -84,7 +94,7 @@ export class FInfo {
     searchable = () => true;
 }
 class BoolInfo extends FInfo {
-    fieldType? = 'boolean';
+    fieldType? = FInfoFieldType.boolean;
     values?: boolean[] = [true, false];
     constructor(d: string, filterable?: boolean) {
         super(d);
@@ -93,7 +103,7 @@ class BoolInfo extends FInfo {
     override searchable = () => false;
 }
 class NumInfo extends FInfo {
-    fieldType? = 'number';
+    fieldType? = FInfoFieldType.number;
     values?: number[] = [];
     constructor(d: string, filterable?: boolean, readOnly?: boolean, values?: number[]) {
         super(d, readOnly);
@@ -103,7 +113,7 @@ class NumInfo extends FInfo {
     override searchable = () => false;
 }
 class StrInfo extends FInfo {
-    fieldType? = 'string';
+    fieldType? = FInfoFieldType.string;
     values?: string[] = [];
     constructor(d: string, filterable?: boolean, readOnly?: boolean, values?: string[]) {
         super(d, readOnly);
@@ -112,7 +122,7 @@ class StrInfo extends FInfo {
     }
 }
 class DocInfo extends FInfo {
-    fieldType? = 'Doc';
+    fieldType? = FInfoFieldType.Doc;
     values?: Doc[] = [];
     constructor(d: string, filterable?: boolean, values?: Doc[]) {
         super(d, true);
@@ -122,45 +132,45 @@ class DocInfo extends FInfo {
     override searchable = () => false;
 }
 class DimInfo extends FInfo {
-    fieldType? = 'enumeration';
+    fieldType? = FInfoFieldType.enumeration;
     values? = [DimUnit.Pixel, DimUnit.Ratio];
     readOnly = false;
     filterable = false;
     override searchable = () => false;
 }
 class PEInfo extends FInfo {
-    fieldType? = 'enumeration';
+    fieldType? = FInfoFieldType.enumeration;
     values? = ['all', 'none'];
     readOnly = false;
     filterable = false;
     override searchable = () => false;
 }
 class DAInfo extends FInfo {
-    fieldType? = 'enumeration';
-    values? = ['embed', 'copy', 'move', 'same', 'proto', 'none'];
+    fieldType? = FInfoFieldType.enumeration;
+    values? = ['embed', 'copy', 'move', 'same', 'add', 'inSame', 'proto'];
     readOnly = false;
     filterable = false;
     override searchable = () => false;
 }
 class CTypeInfo extends FInfo {
-    fieldType? = 'enumeration';
+    fieldType? = FInfoFieldType.enumeration;
     values? = Array.from(Object.keys(CollectionViewType));
     readOnly = false;
     filterable = false;
     override searchable = () => false;
 }
 class DTypeInfo extends FInfo {
-    fieldType? = 'enumeration';
+    fieldType? = FInfoFieldType.enumeration;
     values? = Array.from(Object.keys(DocumentType));
     override searchable = () => false;
 }
 class DateInfo extends FInfo {
-    fieldType? = 'date';
+    fieldType? = FInfoFieldType.date;
     values?: DateField[] = [];
     filterable = true;
 }
 class ListInfo extends FInfo {
-    fieldType? = 'list';
+    fieldType? = FInfoFieldType.list;
     values?: List<any>[] = [];
 }
 type BOOLt = BoolInfo | boolean;
@@ -728,7 +738,7 @@ export namespace Docs {
                 {
                     data: '',
                     layout: { view: ComparisonBox, dataField: defaultDataKey },
-                    options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsLayoutSplit' },
+                    options: { backgroundColor: 'gray', dropAction: dropActionType.move, waitForDoubleClickToClick: 'always', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsLayoutSplit' },
                 },
             ],
             [
@@ -1182,7 +1192,7 @@ export namespace Docs {
             return InstanceFromProto(
                 Prototypes.get(DocumentType.COL),
                 new List(documents),
-                { backgroundColor: 'transparent', dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
+                { backgroundColor: 'transparent', dropAction: dropActionType.move, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
                 id
             );
         }
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 2b94d35ee..cb1dfd76a 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -4727,7 +4727,7 @@
                  */
             } else {
                 type = isVertical ? 'column' : 'row';
-                if (this.parent.contentItems.length === 1) {
+                if (this.parent.contentItems.length === 1 && this.contentItems.length === 1) {
                     let grandparent = this.parent.parent;
                     let correctRowOrCol = this.layoutManager.createContentItem({ type: type }, this);
                     grandparent.replaceChild(this.parent, correctRowOrCol);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 714e33d25..d396ba815 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -14,7 +14,7 @@ import { SetCachedGroups, SharingPermissions } from "../../fields/util";
 import { GestureUtils } from "../../pen-gestures/GestureUtils";
 import { DocServer } from "../DocServer";
 import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
-import { DocUtils, Docs, DocumentOptions, FInfo } from "../documents/Documents";
+import { DocUtils, Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents";
 import { DashboardView } from "../views/DashboardView";
 import { OverlayView } from "../views/OverlayView";
 import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView";
@@ -257,11 +257,11 @@ export class CurrentUserUtils {
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
             {key: "ViewSlide",  creator: slideView,                                         opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
                                                                                                 treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
-                                                                                                dropAction:'move', treeView_Type: TreeViewType.outline, 
+                                                                                                dropAction:dropActionType.move, treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'this.text?.Text'}},
             ];
@@ -311,7 +311,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             title: "Document Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-             childDragAction: 'embed' 
+             childDragAction: dropActionType.embed
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -353,7 +353,7 @@ export class CurrentUserUtils {
         });
 
         const reqdStackOpts:DocumentOptions ={
-            title: "menuItemPanel", childDragAction: "same", layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            title: "menuItemPanel", childDragAction: dropActionType.same, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
             _chromeHidden: true, _gridGap: 0, _yMargin: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
@@ -436,7 +436,7 @@ export class CurrentUserUtils {
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: "embed", 
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: dropActionType.embed, 
                 _lockedPosition: true, _type_collection: CollectionViewType.Schema  });
     }
 
@@ -476,8 +476,8 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            dropAction: "inSame", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
-            layout_headerButton: newDashboardButton, childDragAction: "inSame",
+            dropAction: dropActionType.inPlace, treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
+            layout_headerButton: newDashboardButton, childDragAction: dropActionType.inPlace,
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
@@ -510,9 +510,9 @@ export class CurrentUserUtils {
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _forceActive: true, 
-            title: "My Documents", layout_headerButton: newFolderButton, treeView_HideTitle: true, dropAction: 'add', isSystem: true,
+            title: "My Documents", layout_headerButton: newFolderButton, treeView_HideTitle: true, dropAction: dropActionType.add, isSystem: true,
             isFolder: true, treeView_Type: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
-            treeView_TruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
+            treeView_TruncateTitleWidth: 350, ignoreClick: true, childDragAction: dropActionType.embed,
             layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
         };
         const fileFolders = new Set(DocListCast(DocCast(doc[field])?.data));
@@ -522,8 +522,8 @@ export class CurrentUserUtils {
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
-            title: "My Recently Closed", childHideLinkButton: true, treeView_HideTitle: true, childDragAction: "move", isSystem: true,
-            treeView_TruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
+            title: "My Recently Closed", childHideLinkButton: true, treeView_HideTitle: true, childDragAction: dropActionType.move, isSystem: true,
+            treeView_TruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: dropActionType.same,
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
             layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -546,7 +546,7 @@ export class CurrentUserUtils {
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
-            layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true,
+            layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: dropActionType.same, ignoreClick: true, isSystem: true,
             treeView_HideTitle: true, treeView_TruncateTitleWidth: 350
         };
         if (!doc[field]) DocUtils.AssignOpts(doc, {treeView_Open: true, treeView_ExpandedView: "fields" });
@@ -587,7 +587,7 @@ export class CurrentUserUtils {
         ];
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
+            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
             childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: true, ignoreClick: true
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "Redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
@@ -743,7 +743,7 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: dropActionType.embed, childDontRegisterViews: true,  linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => this.setupContextMenuBtn(params, ctxtMenuBtnsDoc) );
         return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
@@ -769,7 +769,7 @@ export class CurrentUserUtils {
         ];
         const btns = btnDescs.map(desc => dockBtn({_width: desc.opts.width??30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts, desc.funcs));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
+            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
             childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: false, ignoreClick: true
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
@@ -809,7 +809,7 @@ export class CurrentUserUtils {
             // childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             // childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
-            childDragAction: "embed", isSystem: true, childContentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+            childDragAction: dropActionType.embed, isSystem: true, childContentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
             _layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
@@ -824,7 +824,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             title: "My Imports", _forceActive: true, _layout_showTitle: "title", childLayoutString: ImportElementBox.LayoutString('data'),
             _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never",
-            childDragAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
+            childDragAction: dropActionType.copy, _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
             dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
         };
         const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.MasonryDocument([], opts), reqdOpts, undefined, {onClick: "deselectAll()"});
@@ -886,7 +886,7 @@ export class CurrentUserUtils {
         this.setupDocTemplates(doc); // sets up the template menu of templates
         //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
@@ -909,9 +909,9 @@ export class CurrentUserUtils {
                 const options = pair[1] as FInfo;
                 const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
                 switch (options.fieldType) {
-                    case "boolean": opts.fieldValues = new List<boolean>(options.values as any); break;
-                    case "number": opts.fieldValues = new List<number>(options.values as any); break;
-                    case Doc.name: opts.fieldValues = new List<Doc>(options.values as any); break;
+                    case FInfoFieldType.boolean: opts.fieldValues = new List<boolean>(options.values as any); break;
+                    case FInfoFieldType.number: opts.fieldValues = new List<number>(options.values as any); break;
+                    case FInfoFieldType.Doc: opts.fieldValues = new List<Doc>(options.values as any); break;
                     default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
                 }
                 DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 1f093a33c..f6af6196f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,10 +1,24 @@
+/**
+ * The DragManager handles all dragging interactions that occur entirely within Dash (as opposed to external drag operations from the file system, etc)
+ *
+ * Events are generated for
+ *  a pause in the drag movement (dashDragMovePause) as a Doc(s) is dragged,
+ *  just before (dashPreDrop) a Doc(s) is dropped,
+ *  and just after (dashDropEvent) a Doc(s) is dropped
+ * If the document is dragged and paused over the golden layout header tabs, the
+ *   drag interaction will switch to a golden layout tab drag.
+ *
+ * All drag operations can be aborted by hitting the Esc key
+ *
+ */
+
 import { action, observable, runInAction } from 'mobx';
 import { DateField } from '../../fields/DateField';
 import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { ScriptCast, StrCast } from '../../fields/Types';
+import { ScriptCast } from '../../fields/Types';
 import { emptyFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView';
@@ -16,7 +30,15 @@ import { UndoManager } from './UndoManager';
 import { DocData } from '../../fields/DocSymbols';
 const { default : { contextMenuZindex } } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
 
-export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export enum dropActionType {
+    embed = 'embed', // create a new embedding of the dragged document for the new location
+    copy = 'copy', // copy the dragged document
+    move = 'move', // move the dragged document to the drop location after removing it from where it was
+    add = 'add', // add the dragged document to the drop location without removing it from where it was
+    same = 'same', // only allow drop within same collection (or same hierarchical tree collection)
+    inPlace = 'inSame', // keep document in place (unless overridden by a drag modifier)
+    proto = 'proto',
+} // undefined = move, same = move but doesn't call dropPropertiesToRemove
 
 /**
  * Initialize drag
@@ -129,9 +151,9 @@ export namespace DragManager {
         treeViewDoc?: Doc;
         offset: number[];
         canEmbed?: boolean;
-        userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
+        userDropAction?: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
         defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
-        dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
+        dropAction?: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
         dropPropertiesToRemove?: string[];
         moveDocument?: MoveFunction;
         removeDocument?: RemoveFunction;
@@ -170,8 +192,8 @@ export namespace DragManager {
         dropDocCreator: (annotationOn: Doc | undefined) => Doc;
         dropDocument?: Doc;
         offset: number[];
-        dropAction: dropActionType;
-        userDropAction: dropActionType;
+        dropAction?: dropActionType;
+        userDropAction?: dropActionType;
     }
 
     let defaultPreDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -189,7 +211,7 @@ export namespace DragManager {
         const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
         const preDropHandler = (e: Event) => {
             const de = (e as CustomEvent<DropEvent>).detail;
-            (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc.dropAction) as dropActionType);
+            (preDropFunc ?? defaultPreDropFunc)(e, de, doc.dropAction as any as dropActionType);
         };
         element.addEventListener('dashOnDrop', handler);
         element.addEventListener('dashPreDrop', preDropHandler);
@@ -218,19 +240,19 @@ export namespace DragManager {
                         dragData.draggedDocuments.map(async d =>
                             !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
                                 ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
-                                : docDragData.dropAction === 'embed'
+                                : docDragData.dropAction === dropActionType.embed
                                   ? Doc.BestEmbedding(d)
-                                  : docDragData.dropAction === 'add'
+                                  : docDragData.dropAction === dropActionType.add
                                     ? d
-                                    : docDragData.dropAction === 'proto'
+                                    : docDragData.dropAction === dropActionType.proto
                                       ? d[DocData]
-                                      : docDragData.dropAction === 'copy'
+                                      : docDragData.dropAction === dropActionType.copy
                                         ? (await Doc.MakeClone(d)).clone
                                         : d
                         )
                     )
                 ).filter(d => d);
-                !['same', 'proto'].includes(docDragData.dropAction as any) &&
+                ![dropActionType.same, dropActionType.proto].includes(docDragData.dropAction as any) &&
                     docDragData.droppedDocuments
                         // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
                         .forEach((drop: Doc, i: number) => {
@@ -504,7 +526,7 @@ export namespace DragManager {
         const moveHandler = (e: PointerEvent) => {
             e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
             if (dragData instanceof DocumentDragData) {
-                dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.shiftKey ? 'move' : e.ctrlKey ? 'embed' : dragData.defaultDropAction;
+                dragData.userDropAction = e.ctrlKey && e.altKey ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : dragData.defaultDropAction;
             }
             if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(typeof (e.target as any).className === 'string' ? (e.target as any)?.className : '') && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
@@ -530,7 +552,7 @@ export namespace DragManager {
             if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._freeform_noAutoPan)) {
                 const autoScrollHandler = () => {
                     target.dispatchEvent(
-                        new CustomEvent<React.DragEvent>('dashDragAutoScroll', {
+                        new CustomEvent<React.DragEvent>('dashDragMovePause', {
                             bubbles: true,
                             detail: {
                                 shiftKey: e.shiftKey,
@@ -552,7 +574,7 @@ export namespace DragManager {
                                 screenY: e.screenY,
                                 detail: e.detail,
                                 view: e.view ? e.view : (new Window() as any),
-                                nativeEvent: new DragEvent('dashDragAutoScroll'),
+                                nativeEvent: new DragEvent('dashDragMovePause'),
                                 currentTarget: target,
                                 target: target,
                                 bubbles: true,
@@ -566,7 +588,7 @@ export namespace DragManager {
                                 isPropagationStopped: () => ('not implemented for this event' ? false : false),
                                 persist: emptyFunction,
                                 timeStamp: e.timeStamp,
-                                type: 'dashDragAutoScroll',
+                                type: 'dashDragMovePause',
                             },
                         })
                     );
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b6cb845a6..3be52597a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -19,7 +19,7 @@ import { Docs } from '../documents/Documents';
 import { CalendarManager } from '../util/CalendarManager';
 import { CaptureManager } from '../util/CaptureManager';
 import { DocumentManager } from '../util/DocumentManager';
-import { DragManager } from '../util/DragManager';
+import { DragManager, dropActionType } from '../util/DragManager';
 import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
@@ -623,7 +623,7 @@ export class MainView extends ObservableReactComponent<{}> {
                     isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                     ScreenToLocalTransform={this.headerBarScreenXf}
                     childHideResizeHandles={true}
-                    childDragAction="move"
+                    childDragAction={dropActionType.move}
                     dontRegisterView={true}
                     hideResizeHandles={true}
                     PanelWidth={this.headerBarDocWidth}
@@ -905,7 +905,7 @@ export class MainView extends ObservableReactComponent<{}> {
                     Document={Doc.MyDockedBtns}
                     docViewPath={returnEmptyDocViewList}
                     fieldKey="data"
-                    dropAction="embed"
+                    dropAction={dropActionType.embed}
                     styleProvider={DefaultStyleProvider}
                     select={emptyFunction}
                     isAnyChildContentActive={returnFalse}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 0f90818ef..8729ef549 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -11,7 +11,7 @@ import { RichTextField } from '../../../fields/RichTextField';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DragManager } from '../../util/DragManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { Transform } from '../../util/Transform';
@@ -91,7 +91,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                     Document={selDoc}
                     docViewPath={returnEmptyDocViewList}
                     fieldKey="data"
-                    dropAction="embed"
+                    dropAction={dropActionType.embed}
                     styleProvider={DefaultStyleProvider}
                     select={emptyFunction}
                     isContentActive={returnTrue}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index d0df77cbe..7d7f0bb61 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -13,6 +13,7 @@ import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import './CollectionPileView.scss';
 import { CollectionSubView } from './CollectionSubView';
+import { dropActionType } from '../../util/DragManager';
 
 @observer
 export class CollectionPileView extends CollectionSubView() {
@@ -72,7 +73,7 @@ export class CollectionPileView extends CollectionSubView() {
                     // pile children never have their contents active, but will be document active whenever the entire pile is.
                     childContentsActive={returnFalse}
                     childDocumentsActive={this._props.isDocumentActive}
-                    childDragAction="move"
+                    childDragAction={dropActionType.move}
                     childClickScript={this.toggleIcon}
                 />
             </div>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 786301136..4d60cbefc 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -153,12 +153,18 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return res;
     }
 
-    protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, dropAction: dropActionType) => {
+    protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetDropAction: dropActionType) => {
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.Document) ? true : false;
+            const sourceDragAction = dragData.dropAction;
+            const sameTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.Document);
             const isAlreadyInTree = () => sameTree || dragData.draggedDocuments.some(d => d.embedContainer === this.Document && this.childDocs.includes(d));
-            dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree && dragData.dropAction !== 'inSame' ? 'same' : dragData.dropAction;
+            dragData.dropAction =
+                targetDropAction && !isAlreadyInTree() // if dropped document is not in the tree
+                    ? targetDropAction // then use the target's drop action if it's specified
+                    : !sameTree() || sourceDragAction === dropActionType.inPlace // if doc from another tree, or a non inPlace source drag action is specified
+                      ? sourceDragAction // use the source dragAction
+                      : dropActionType.same; // otherwise use same tree semantics to move within tree
             e.stopPropagation();
         }
     };
@@ -287,7 +293,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _renderCount = 1;
     @computed get treeViewElements() {
         TraceMobx();
-        const dragAction = StrCast(this.Document.childDragAction) as dropActionType;
+        const dragAction = StrCast(this.Document.childDragAction) as any 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) || false;
         if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20))));
@@ -473,7 +479,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                         isAnnotationOverlayScrollable={true}
                         childDocumentsActive={this._props.isContentActive}
                         fieldKey={this._props.fieldKey + '_annotations'}
-                        dropAction="move"
+                        dropAction={dropActionType.move}
                         select={emptyFunction}
                         addDocument={this.addAnnotationDocument}
                         removeDocument={this.remAnnotationDocument}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 85f7cf7fe..4f2d8b9c0 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -439,7 +439,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         droppedDocuments: Doc[],
         before: boolean,
         inside: number | boolean,
-        dropAction: dropActionType,
+        dropAction: dropActionType | undefined,
         removeDocument: DragManager.RemoveFunction | undefined,
         moveDocument: DragManager.MoveFunction | undefined,
         forceAdd: boolean,
@@ -1161,7 +1161,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         const before = pt[1] < rect.top + rect.height / 2;
         const inside = this.treeView.fileSysMode && !this.Document.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
 
-        const docs = this.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, undefined, false, false));
+        this.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, dropActionType.copy, undefined, undefined, false, false));
     };
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 50a9feff8..50b3dbd70 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -462,7 +462,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
                 const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView?.())!);
                 const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 };
-                const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
+                const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined);
                 de.moveDocument = this._props.moveDocument;
                 de.offset = this.screenToFreeformContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
                 DragManager.StartDocumentDrag(
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a417d777a..d0e59180d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -635,8 +635,13 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
     }
     MarqueeRef: HTMLDivElement | null = null;
 
+    /**
+     * This is called for every drag movement when a document is dragged over this collection.
+     * If the document is dragged within 25 pixels of the edge of the collection and paused, this will
+     * auto scroll the collection so that it can be dragged farther (unless auto panning has been disabled)
+     */
     @action
-    onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
+    onDragMovePause = (e: CustomEvent<React.DragEvent>) => {
         if ((e as any).handlePan || this._props.isAnnotationOverlay) return;
         (e as any).handlePan = true;
 
@@ -659,7 +664,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
             <div
                 className="marqueeView"
                 ref={r => {
-                    r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
+                    r?.addEventListener('dashDragMovePause', this.onDragMovePause as any);
                     this.MarqueeRef = r;
                 }}
                 style={{
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 31b4a2dd4..eee3836d2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -10,7 +10,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty
 import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
 import { Docs, DocumentOptions, DocUtils, FInfo } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from '../../../util/DragManager';
+import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoable, undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
@@ -981,7 +981,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
                 styleProvider={this.noOpacityStyleProvider}
                 waitForDoubleClickToClick={returnNever}
                 defaultDoubleClick={returnIgnore}
-                dragAction="move"
+                dragAction={dropActionType.move}
                 onClickScriptDisable="always"
                 focus={this._props.schema.focusDocument}
                 childFilters={this._props.schema.childDocFilters}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 001ad5ab6..46867665d 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -10,7 +10,7 @@ import { Doc, DocListCast, Field } from '../../../../fields/Doc';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { FInfo } from '../../../documents/Documents';
+import { FInfo, FInfoFieldType } from '../../../documents/Documents';
 import { DocFocusOrOpen } from '../../../util/DocumentManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch, undoable } from '../../../util/UndoManager';
@@ -29,6 +29,7 @@ import { Popup, Size, Type } from 'browndash-components';
 import { IconLookup, faCaretDown } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { SettingsManager } from '../../../util/SettingsManager';
+import { dropActionType } from '../../../util/DragManager';
 
 export interface SchemaTableCellProps {
     Document: Doc;
@@ -86,7 +87,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dragAction: 'move',
+            dragAction: dropActionType.move,
             renderDepth: 1,
             isContentActive: returnFalse,
             whenChildContentsActiveChanged: emptyFunction,
@@ -149,7 +150,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         if (cellValue instanceof DateField) return ColumnType.Date;
         if (cellValue instanceof RichTextField) return ColumnType.RTF;
         if (typeof cellValue === 'number') return ColumnType.Any;
-        if (typeof cellValue === 'string' && columnTypeStr !== 'enumeration') return ColumnType.Any;
+        if (typeof cellValue === 'string' && columnTypeStr !== FInfoFieldType.enumeration) return ColumnType.Any;
         if (typeof cellValue === 'boolean') return ColumnType.Boolean;
 
         if (columnTypeStr && columnTypeStr in FInfotoColType) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d131f72d5..abb1aa59f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -413,7 +413,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
         if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
             this.cleanupPointerEvents();
             this._longPressSelector && clearTimeout(this._longPressSelector);
-            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dragAction || this._props.dragAction || undefined) as dropActionType));
+            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && dropActionType.embed) || ((this.Document.dragAction || this._props.dragAction || undefined) as dropActionType));
         }
     };
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index b8f6575dd..918987034 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -36,6 +36,7 @@ import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
 import { ScriptingBox } from '../ScriptingBox';
 import './PresBox.scss';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
+import { dropActionType } from '../../../util/DragManager';
 export interface pinDataTypes {
     scrollable?: boolean;
     dataviz?: number[];
@@ -1074,7 +1075,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     childLayoutTemplate = () => Docs.Create.PresElementBoxDocument();
-    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.Document, this.fieldKey, doc);
+    removeDocument = (doc: Doc | Doc[]) => !(doc instanceof Doc ? [doc] : doc).map(d => Doc.RemoveDocFromList(this.Document, this.fieldKey, d)).some(p => !p);
     getTransform = () => this.ScreenToLocalBoxXf().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this._props.PanelHeight() - 40;
     /**
@@ -2607,7 +2608,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 childIgnoreNativeSize={true}
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
-                                childDragAction="move"
+                                childDragAction={dropActionType.move}
                                 setContentViewBox={emptyFunction}
                                 //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 4155800b8..0952dda20 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -25,6 +25,7 @@ import { Colors } from '../global/globalEnums';
 import { DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView';
 import { DefaultStyleProvider } from '../StyleProvider';
 import './TopBar.scss';
+import { dropActionType } from '../../util/DragManager';
 
 /**
  * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user
@@ -102,7 +103,7 @@ export class TopBar extends React.Component {
                     Document={selDoc}
                     docViewPath={returnEmptyDocViewList}
                     fieldKey="data"
-                    dropAction="embed"
+                    dropAction={dropActionType.embed}
                     styleProvider={DefaultStyleProvider}
                     select={emptyFunction}
                     isContentActive={returnTrue}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 8eeb52709..1cacfe30c 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -96,9 +96,9 @@ export const documentSchema = createSchema({
     // drag drop properties
     _dragOnlyWithinContainer: 'boolean', // whether document can be dropped into a different collection
     dragFactory: Doc, // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    dropAction: 'string', // override specifying what should happen when something is dropped on this document (can be "embed", "copy", "move")
-    dragAction: 'string', // override specifying what should happen when this document s dragged (can be "embed", "copy", "move")
-    childDragAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
+    dropAction: 'string', // override specifying what should happen when something is dropped on this document (dropActionType)
+    dragAction: 'string', // override specifying what should happen when this document s dragged (dropActionType)
+    childDragAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (dropActionType)
     dropPropertiesToRemove: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
 });
 
-- 
cgit v1.2.3-70-g09d2


From a9eb266296d1b71f1016c867f39e20299c011eea Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 9 Mar 2024 23:08:15 -0500
Subject: added a template button to top bar to set default layout.  fixed show
 title when value is a list.  fixed typeahead for createing notes with
 templates.

---
 src/client/util/CurrentUserUtils.ts                |  7 ++++---
 src/client/views/GlobalKeyHandler.ts               |  3 +++
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/TemplateMenu.tsx                  | 13 -------------
 src/client/views/global/globalScripts.ts           | 19 +++++++++++++++++++
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 21 +++++++++++++++------
 7 files changed, 43 insertions(+), 23 deletions(-)

(limited to 'src/client/views/MainView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 564a46514..15255641d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -124,11 +124,11 @@ export class CurrentUserUtils {
             { title: "Postit", backgroundColor: "yellow",    icon: "sticky-note"},
             { title: "Idea",   backgroundColor: "pink",      icon: "lightbulb" },
             { title: "Topic",  backgroundColor: "lightblue", icon: "book-open" }];
-        const reqdNoteList = reqdTempOpts.map(opts => {
+        const reqdNoteList = [...reqdTempOpts.map(opts => {
             const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true};
             const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.title === opts.title): undefined;
             return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts));
-        });
+        }), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
 
         const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
         return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
@@ -788,7 +788,8 @@ pie title Minerals in my tap water
                     CollectionViewType.Grid, CollectionViewType.NoteTaking]),
               title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                                                                    ignoreClick: true, width: 100, scripts: { script:  'setView(value, _readOnly_)'}},
             { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         expertMode: false,                                                                                                                             width: 30,  scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
-            { title: "Header",  icon: "heading",       toolTip: "Doc Titlebar Color",   btnType: ButtonType.ColorButton,         expertMode: true,                                                                                                           ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'} },
+            { title: "Header",  icon: "heading",       toolTip: "Doc Titlebar Color",   btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'} },
+            { title: "Template",icon: "scroll",        toolTip: "Default Note Template",btnType: ButtonType.ToggleButton,        expertMode: false, toolType:DocumentType.RTF,                                                                                                              scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} },
             { title: "Fill",    icon: "fill-drip",     toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true, width: 30,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
             { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode, true)'},       scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(this.toolType, this.expertMode)'}, width: 30,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 733383002..e800798ca 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -24,6 +24,7 @@ import { MainView } from './MainView';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { OpenWhereMod } from './nodes/DocumentView';
 import { AnchorMenu } from './pdf/AnchorMenu';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 
 const modifiers = ['control', 'meta', 'shift', 'alt'];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo;
@@ -61,6 +62,8 @@ export class KeyManager {
     });
 
     public handle = action((e: KeyboardEvent) => {
+        // accumulate buffer of characters to insert in a new text note.  once the note is created, it will stop keyboard events from reaching this function.
+        if (FormattedTextBox.SelectOnLoadChar) FormattedTextBox.SelectOnLoadChar = FormattedTextBox.SelectOnLoadChar + (e.key === 'Enter' ? '\n' : e.key);
         e.key === 'Control' && (CtrlKey = true);
         //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
         const keyname = e.key && e.key.toLowerCase();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3be52597a..e369e2876 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -397,6 +397,7 @@ export class MainView extends ObservableReactComponent<{}> {
                 fa.faPaintBrush,
                 fa.faTimes,
                 fa.faFlag,
+                fa.faScroll,
                 fa.faEye,
                 fa.faArrowsAlt,
                 fa.faQuoteLeft,
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 00195f7d7..eed197b0b 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -45,7 +45,6 @@ class OtherToggle extends React.Component<{ checked: boolean; name: string; togg
 
 export interface TemplateMenuProps {
     docViews: DocumentView[];
-    templates?: Map<string, boolean>;
 }
 
 @observer
@@ -61,17 +60,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         this.props.docViews.map(dv => dv.switchViews(false, 'layout'));
     };
 
-    @undoBatch
-    @action
-    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
-    toggleTemplateActivity = (): void => {
-        this._hidden = !this._hidden;
-    };
-
     // todo: add brushes to brushMap to save with a style name
     onCustomKeypress = (e: React.KeyboardEvent) => {
         if (e.key === 'Enter') {
@@ -104,7 +92,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
         const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const templateMenu: Array<JSX.Element> = [];
-        this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key="default" name={firstDoc.layout instanceof Doc ? StrCast(firstDoc.layout.title) : 'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
         addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
         this._addedKeys &&
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 33704e8fe..0579b07c7 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -21,6 +21,9 @@ import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import { WebBox } from '../nodes/WebBox';
 import { VideoBox } from '../nodes/VideoBox';
 import { DocData } from '../../../fields/DocSymbols';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { PrefetchProxy } from '../../../fields/Proxy';
+import { MakeTemplate } from '../../util/DropConverter';
 
 ScriptingGlobals.add(function IsNoneSelected() {
     return SelectionManager.Views.length <= 0;
@@ -71,6 +74,22 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
     }
 });
 
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
+    if (checkResult) {
+        return Doc.UserDoc().defaultTextLayout;
+    }
+    const view = SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView instanceof FormattedTextBox ? SelectionManager.Views[0] : undefined;
+
+    if (view) {
+        const tempDoc = view.Document;
+        if (!view.layoutDoc.isTemplateDoc) {
+            MakeTemplate(tempDoc);
+        }
+        Doc.UserDoc().defaultTextLayout = new PrefetchProxy(tempDoc);
+        tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', tempDoc);
+    } else Doc.UserDoc().defaultTextLayout = undefined;
+});
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 431781d24..29266bd8e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -849,7 +849,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                         contents={
                             showTitle
                                 .split(';')
-                                .map(field => targetDoc[field.trim()]?.toString())
+                                .map(field => Field.toString(targetDoc[field.trim()] as Field))
                                 .join(' \\ ') || '-unset-'
                         }
                         display="block"
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 46043eefb..f02ad7300 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
@@ -312,6 +312,8 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         );
     }
 
+    @observable _hackToRecompute = 0; // bcz: ugh ... <Toggle>'s toggleStatus initializes but doesn't track its value after a click.  so a click that does nothing to the toggle state will toggle the button anyway.  this forces the Toggle to re-read the ToggleStatus value.
+
     @computed get toggleButton() {
         // Determine the type of toggle button
         const buttonText = StrCast(this.dataDoc.buttonText);
@@ -319,7 +321,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
 
         const script = ScriptCast(this.Document.onClick);
         const double = ScriptCast(this.Document.onDoubleClick);
-        const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false;
+        const toggleStatus = script?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? false;
         // Colors
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
         const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
@@ -336,10 +338,17 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
                 icon={this.Icon(color)!}
                 label={this.label}
                 onPointerDown={e =>
-                    setupMoveUpEvents(this, e, returnTrue, emptyFunction, (e, doubleTap) => {
-                        (!doubleTap || !double) && script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
-                        doubleTap && double && double.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
-                    })
+                    setupMoveUpEvents(
+                        this,
+                        e,
+                        returnTrue,
+                        emptyFunction,
+                        action((e, doubleTap) => {
+                            (!doubleTap || !double) && script?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+                            doubleTap && double?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
+                            this._hackToRecompute = this._hackToRecompute + 1;
+                        })
+                    )
                 }
             />
         );
-- 
cgit v1.2.3-70-g09d2

