From b33ff1abdeab12d977c86f18c567ba3e84a49297 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 22 Feb 2024 16:34:32 -0500
Subject: replaced freeform document view wrapper with more direct caching of
 props.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a69030019..3670382c5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -37,7 +37,7 @@ import { GestureOverlay } from '../../GestureOverlay';
 import { CtrlKey } from '../../GlobalKeyHandler';
 import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
 import { LightboxView } from '../../LightboxView';
-import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
+import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
 import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
 import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
 import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView';
@@ -1181,7 +1181,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const childLayout = entry.pair.layout;
         const childData = entry.pair.data;
         return (
-            <CollectionFreeFormDocumentViewWrapper
+            <CollectionFreeFormDocumentView
                 {...OmitKeys(entry, ['replica', 'pair']).omit}
                 key={childLayout[Id] + (entry.replica || '')}
                 Document={childLayout}
-- 
cgit v1.2.3-70-g09d2


From 27306bd0ae259241182d90cc62225609dfc8da74 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Sun, 25 Feb 2024 12:51:26 -0500
Subject: fixed conversion of doc to schema cells to allow number 0 to be
 converted to a string.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3670382c5..50a9feff8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1962,8 +1962,8 @@ ScriptingGlobals.add(function datavizFromSchema(doc: Doc) {
         for (let i = 0; i < children.length; i++) {
             let eachRow = [];
             for (let j = 0; j < keys.length; j++) {
-                var cell = children[i][keys[j]];
-                if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, '');
+                var cell = children[i][keys[j]]?.toString();
+                if (cell) cell = cell.toString().replace(/\,/g, '');
                 eachRow.push(cell);
             }
             csvRows.push(eachRow);
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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 e973de1476194fe68a2f218e458e4619fb71c75e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 29 Feb 2024 09:34:56 -0500
Subject: fixed minimap to show at least as much as the freeformview is showing
 (was a problem for example when there was just a single document)

---
 src/client/views/collections/TabDocView.tsx                       | 8 ++++++++
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx     | 4 ++--
 2 files changed, 10 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 02aa76d82..b52f2e7b6 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -564,6 +564,12 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
         const dim = Math.max(xbounds, ybounds);
         return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, dim };
     }
+    @computed get xPadding() {
+        return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
+    }
+    @computed get yPadding() {
+        return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
+    }
     childLayoutTemplate = () => Cast(this._props.document.childLayoutTemplate, Doc, null);
     returnMiniSize = () => NumCast(this._props.document._miniMapSize, 150);
     miniDown = (e: React.PointerEvent) => {
@@ -620,6 +626,8 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
                     childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
                     searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     fitContentsToBox={returnTrue}
+                    xPadding={this.xPadding}
+                    yPadding={this.yPadding}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown}>
                     <TabMiniThumb miniLeft={miniLeft} miniTop={miniTop} miniWidth={miniWidth} miniHeight={miniHeight} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 50b3dbd70..e48656f5e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -159,8 +159,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             ? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
             : aggregateBounds(
                   this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
-                  NumCast(this.layoutDoc._xPadding, 10),
-                  NumCast(this.layoutDoc._yPadding, 10)
+                  NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 10),
+                  NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 10)
               );
     }
     @computed get nativeWidth() {
-- 
cgit v1.2.3-70-g09d2


From 49cfa95fdb3653a1224d4e5e3380e111c8e2014f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 4 Mar 2024 12:55:05 -0500
Subject: changes to restore template menu and fix freeform templates to always
 show contents.   Fix for alt-dropping an image to replace another image.

---
 src/client/views/DocumentButtonBar.tsx             | 48 +++++++++-------------
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/InkingStroke.tsx                  |  2 +-
 src/client/views/TemplateMenu.tsx                  |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  9 ++--
 src/client/views/nodes/ImageBox.tsx                |  1 +
 7 files changed, 30 insertions(+), 38 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 8a4b42ae0..d65e0b406 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -4,12 +4,12 @@ import { Tooltip } from '@mui/material';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { Doc } from '../../fields/Doc';
 import { Cast, DocCast } from '../../fields/Types';
 import { DocUtils } from '../documents/Documents';
 import { CalendarManager } from '../util/CalendarManager';
-import { DragManager } from '../util/DragManager';
+import { DragManager, dropActionType } from '../util/DragManager';
 import { IsFollowLinkScript } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
@@ -24,6 +24,9 @@ import { DocumentView, DocumentViewInternal, OpenWhere } from './nodes/DocumentV
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
 import { PinProps } from './nodes/trails';
 import { faCalendarDays } from '@fortawesome/free-solid-svg-icons';
+import { Popup } from 'browndash-components';
+import { TemplateMenu } from './TemplateMenu';
+import { FaEdit } from 'react-icons/fa';
 
 @observer
 export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> {
@@ -306,7 +309,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
             const dragDocView = this.view0!;
             const dragData = new DragManager.DocumentDragData([dragDocView.Document]);
             const [left, top] = dragDocView.screenToContentsTransform().inverse().transformPoint(0, 0);
-            dragData.defaultDropAction = 'embed';
+            dragData.defaultDropAction = dropActionType.embed;
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
             return true;
@@ -316,35 +319,24 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
 
     _ref = React.createRef<HTMLDivElement>();
     @observable _tooltipOpen: boolean = false;
+    @computed get templateMenu() {
+        return (
+            <div ref={this._ref}>
+                <TemplateMenu
+                    docViews={this._props
+                        .views()
+                        .filter(v => v)
+                        .map(v => v as DocumentView)}
+                />
+            </div>
+        );
+    }
     @computed
     get templateButton() {
-        const view0 = this.view0;
-        const views = this._props.views();
-        return !view0 ? null : (
+        return !this.view0 ? null : (
             <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embedding</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._embedDown = true))}
-                        onClose={action(() => (this._embedDown = false))}
-                        content={
-                            !this._embedDown ? null : (
-                                <div ref={this._ref}>
-                                    {' '}
-                                    <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} />
-                                </div>
-                            )
-                        }>
-                        <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}>
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />
-                        </div>
-                    </Flyout> */
-
-                        <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}>
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />
-                        </div>
-                    }
+                    <Popup icon={<FaEdit />} popup={this.templateMenu} popupContainsPt={returnTrue} />
                 </div>
             </Tooltip>
         );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e9c4d9cc5..9d53c58c0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -77,7 +77,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 const {x,y} = Utils.rotPt(e.clientX - center.x, 
                                           e.clientY - center.y, 
                                           NumCast(SelectionManager.Views.lastElement()?.screenToViewTransform().Rotate));
-                (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && //
+                (this._showNothing = !DocumentButtonBar.Instance?._tooltipOpen && !(this.Bounds.x !== Number.MAX_VALUE && //
                     (this.Bounds.x > center.x+x || this.Bounds.r < center.x+x || 
                      this.Bounds.y > center.y+y || this.Bounds.b < center.y+y )));
             })); // prettier-ignore
@@ -514,7 +514,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     //
     // determines if anything being dragged directly or via a group has a fixed aspect ratio (in which case we resize uniformly)
     //
-    hasFixedAspect = (doc: Doc): boolean => (doc.isGroup ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc.layout_nativeDimEditable));
+    hasFixedAspect = (doc: Doc): boolean => (doc.isGroup ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc._layout_nativeDimEditable));
 
     //
     // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 92644d3c5..122e5c4c3 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -438,7 +438,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() impleme
                 <svg
                     className="inkStroke"
                     style={{
-                        transform: isInkMask ? `rotate(-${NumCast(this._props.CollectionFreeFormDocumentView?.()._props.w_Rotation?.() ?? 0)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                        transform: isInkMask ? `rotate(-${NumCast(this._props.CollectionFreeFormDocumentView?.()._props.rotation ?? 0)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
                         // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
                         cursor: this._props.isSelected() ? 'default' : undefined,
                     }}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5fc33207e..f7729dbc7 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -81,7 +81,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     componentDidMount() {
         !this._addedKeys && (this._addedKeys = new ObservableSet());
         [...Array.from(Object.keys(this.props.docViews[0].Document[DocData])), ...Array.from(Object.keys(this.props.docViews[0].Document))]
-            .filter(key => key.startsWith('layout_'))
+            .filter(key => key.startsWith('layout_') && key !== 'layout_fieldKey')
             .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', ''))));
     }
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index b52f2e7b6..699ec3b95 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -638,7 +638,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
     render() {
         return this._props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.document)) || this._props.document?._type_collection !== CollectionViewType.Freeform ? null : (
             <div className="miniMap-hidden">
-                <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement={'top-end'} popup={this.popup} />
+                <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e48656f5e..877569ab3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -17,7 +17,7 @@ import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../
 import { ImageField } from '../../../../fields/URLField';
 import { TraceMobx } from '../../../../fields/util';
 import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, numberValue, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -760,7 +760,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, GestureOverlay.getBounds(points), text));
     };
 
-    @action
     onPointerMove = (e: PointerEvent) => {
         if (this.tryDragCluster(e, this._hitCluster)) {
             e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over
@@ -1344,7 +1343,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     renderCutoffProvider = computedFn(
         function renderCutoffProvider(this: any, doc: Doc) {
-            return !this._renderCutoffData.get(doc[Id] + '');
+            return this.Document.isTemplateDoc ? false : !this._renderCutoffData.get(doc[Id] + '');
         }.bind(this)
     );
 
@@ -1467,7 +1466,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 if (!code.includes('dashDiv')) {
                     const script = CompileScript(code, { params: { docView: 'any' }, typecheck: false, editable: true });
                     if (script.compiled) script.run({ this: this.DocumentView?.() });
-                } else code && !first && eval(code);
+                } else code && !first && eval?.(code);
             },
             { fireImmediately: true }
         );
@@ -1712,7 +1711,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     incrementalRender = action(() => {
         if (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.())) {
             const layout_unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
-            const loadIncrement = 5;
+            const loadIncrement = this.Document.isTemplateDoc ? Number.MAX_VALUE : 5;
             for (var i = 0; i < Math.min(layout_unrendered.length, loadIncrement); i++) {
                 this._renderCutoffData.set(layout_unrendered[i][Id] + '', true);
             }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 923aead64..251235b93 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -141,6 +141,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
                 const targetField = Doc.LayoutFieldKey(layoutDoc);
                 const targetDoc = layoutDoc[DocData];
                 if (targetDoc[targetField] instanceof ImageField) {
+                    added = true;
                     this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
                     Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
                     Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
-- 
cgit v1.2.3-70-g09d2


From b938ac2909290bd5dd11eeb0a0056f4679dbb25e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 7 Mar 2024 10:24:56 -0500
Subject: fixed several things related to templates for labels (used as icons)
 and  FontIconboxes. Also made labels non uniformly  resizable .

---
 src/client/documents/Documents.ts                        | 16 +++++++---------
 src/client/util/CurrentUserUtils.ts                      | 16 +++++++++-------
 src/client/views/DocumentDecorations.tsx                 | 10 +++++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx        | 12 ++++++++----
 src/client/views/nodes/FontIconBox/FontIconBox.tsx       |  8 ++++++--
 src/client/views/nodes/LabelBox.tsx                      | 10 +++-------
 6 files changed, 38 insertions(+), 34 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d41cd24d4..b7d8222b9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -277,8 +277,8 @@ export class DocumentOptions {
     layout_borderRounding?: string;
     _layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false);
     _layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false);
-    _layout_reflowVertical?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
-    _layout_reflowHorizontal?: BOOLt = new BoolInfo('whether a doc with a native size can be horizonally resized, causing some form of reflow');
+    _layout_reflowVertical?: BOOLt = new BoolInfo('permit vertical resizing with content "reflow"');
+    _layout_reflowHorizontal?: BOOLt = new BoolInfo('permit horizontal resizing with content reflow');
     layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
     layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)');
     _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
@@ -317,7 +317,6 @@ export class DocumentOptions {
     _label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes', false);
     _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false);
     stroke_width?: NUMt = new NumInfo('width of an ink stroke', false);
-    icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)', false);
     mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false);
     recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
     slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)');
@@ -652,8 +651,8 @@ export namespace Docs {
             [
                 DocumentType.LABEL,
                 {
-                    layout: { view: LabelBox, dataField: defaultDataKey },
-                    options: { _singleLine: true },
+                    layout: { view: LabelBox, dataField: 'title' },
+                    options: { _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true },
                 },
             ],
             [
@@ -684,8 +683,8 @@ export namespace Docs {
             [
                 DocumentType.BUTTON,
                 {
-                    layout: { view: LabelBox, dataField: 'onClick' },
-                    options: {},
+                    layout: { view: LabelBox, dataField: 'title' },
+                    options: { layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true },
                 },
             ],
             [
@@ -1881,8 +1880,7 @@ export namespace DocUtils {
         const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context));
         if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
             const pushpin = Docs.Create.FontIconDocument({
-                title: 'pushpin',
-                icon_label: '',
+                title: '',
                 annotationOn: Cast(doc.annotationOn, Doc, null),
                 followLinkToggle: true,
                 icon: 'map-pin',
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 310613bc9..cfba003fc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -22,7 +22,7 @@ import { CollectionTreeView, TreeViewType } from "../views/collections/Collectio
 import { Colors } from "../views/global/globalEnums";
 import { media_state } from "../views/nodes/AudioBox";
 import { OpenWhere } from "../views/nodes/DocumentView";
-import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
+import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox";
 import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
 import { DragManager, dropActionType } from "./DragManager";
 import { MakeTemplate } from "./DropConverter";
@@ -32,6 +32,8 @@ import { ScriptingGlobals } from "./ScriptingGlobals";
 import { ColorScheme } from "./SettingsManager";
 import { SnappingManager } from "./SnappingManager";
 import { UndoManager } from "./UndoManager";
+import { LabelBox } from "../views/nodes/LabelBox";
+import { ImageBox } from "../views/nodes/ImageBox";
 
 interface Button {
     // DocumentOptions fields a button can set
@@ -156,14 +158,14 @@ export class CurrentUserUtils {
             }
             const allopts = {isSystem: true, onClickScriptDisable: "never",  ...opts};
             return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
-                      DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
+                      DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts, templateField), true, iconFieldName, templateField))),
                 {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
         };
-        const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
-            textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+        const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
+            layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
-        const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
-        const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
+        const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
+        const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
         const iconTemplates = [
             makeIconTemplate(undefined,          "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
             makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
@@ -174,7 +176,7 @@ export class CurrentUserUtils {
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
             makeIconTemplate(DocumentType.VID,   "icon",  { iconTemplate:DocumentType.IMG}),
-            makeIconTemplate(DocumentType.BUTTON,"data",  { iconTemplate:DocumentType.FONTICON}),
+            makeIconTemplate(DocumentType.BUTTON,"title", { iconTemplate:DocumentType.FONTICON}),
             //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
             makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
             //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 87ee962a0..2fb9f0fc1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -323,7 +323,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 SelectionManager.Docs.map(doc => {
                     const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
                     const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box
-                    doc.layout_borderRounding = `${radius}px`;
+                    doc._layout_borderRounding = `${radius}px`;
                 });
                 return false;
             },
@@ -533,9 +533,9 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)];
 
             const modifyNativeDim =
-                (opts.ctrlKey && doc.layout_nativeDimEditable) || // e.g., PDF or web page
-                (doc.layout_reflowHorizontal && opts.dragHdl !== 'bottom' && opts.dragHdl !== 'top') || // eg rtf or some web pages
-                (doc.layout_reflowVertical && (opts.dragHdl === 'bottom' || opts.dragHdl === 'top' || opts.ctrlKey)); // eg rtf, web, pdf
+                (opts.ctrlKey && doc._layout_nativeDimEditable) || // e.g., PDF or web page
+                (doc._layout_reflowHorizontal && opts.dragHdl !== 'bottom' && opts.dragHdl !== 'top') || // eg rtf or some web pages
+                (doc._layout_reflowVertical && (opts.dragHdl === 'bottom' || opts.dragHdl === 'top' || opts.ctrlKey)); // eg rtf, web, pdf
             if (nwidth && nheight && !modifyNativeDim) {
                 // eg., dragging right resizer on PDF -- enforce native dimensions because not expliclty overridden with ctrl or bottom resize drag
                 scale.x === 1 ? (scale.x = scale.y) : (scale.y = scale.x);
@@ -545,7 +545,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 const setData = Doc.NativeWidth(doc[DocData]) === doc.nativeWidth;
                 doc.nativeWidth = scale.x * Doc.NativeWidth(doc);
                 if (setData) Doc.SetNativeWidth(doc[DocData], NumCast(doc.nativeWidth));
-                if (doc.layout_reflowVertical && !NumCast(doc.nativeHeight)) {
+                if (doc._layout_reflowVertical && !NumCast(doc.nativeHeight)) {
                     doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF
                 }
             }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 877569ab3..4eb946939 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
 import { Bezier } from 'bezier-js';
 import { Colors } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
@@ -840,10 +840,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     if (excludeT < startSegmentT || excludeT > docCurveTVal) {
                         const localStartTVal = startSegmentT - Math.floor(i / 4);
                         t !== (localStartTVal < 0 ? 0 : localStartTVal) && segment.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, t));
-                        segment.length && segments.push(segment);
+                        if (segment.length && (Math.abs(segment[0].points[0].x - segment[0].points.lastElement().x) > 0.5 || Math.abs(segment[0].points[0].y - segment[0].points.lastElement().y) > 0.5)) segments.push(segment);
                     }
                     // start a new segment from the intersection t value
-                    segment = tVals.length - 1 === index ? [inkSegment.split(t).right] : [];
+                    if (tVals.length - 1 === index) {
+                        const split = inkSegment.split(t).right;
+                        if (split && (Math.abs(split.points[0].x - split.points.lastElement().x) > 0.5 || Math.abs(split.points[0].y - split.points.lastElement().y) > 0.5)) segment = [split];
+                        else segment = [];
+                    } else segment = [];
                     startSegmentT = docCurveTVal;
                 });
             } else {
@@ -1647,7 +1651,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         !this._props.isAnnotationOverlay &&
             !Doc.noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
-        this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
+        this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor)), icon: 'palette' });
         this._props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
         if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d24c79b10..46043eefb 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -81,9 +81,13 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         }
     };
 
-    // Determining UI Specs
+    /**
+     * this chooses the appropriate title for the label
+     * if the Document is a template, then we use the title of the data doc that it renders
+     * otherwise, we use the Document's title itself.
+     */
     @computed get label() {
-        return StrCast(this.dataDoc.icon_label, StrCast(this.Document.title));
+        return StrCast(this.Document.isTemplateDoc ? this.dataDoc.title : this.Document.title);
     }
     Icon = (color: string, iconFalse?: boolean) => {
         let icon;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index fd3074a88..be20b5934 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -17,12 +17,8 @@ import './LabelBox.scss';
 import { PinProps, PresBox } from './trails';
 import { Docs } from '../../documents/Documents';
 
-export interface LabelBoxProps extends FieldViewProps {
-    label?: string;
-}
-
 @observer
-export class LabelBox extends ViewBoxBaseComponent<LabelBoxProps>() {
+export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LabelBox, fieldKey);
     }
@@ -32,7 +28,7 @@ export class LabelBox extends ViewBoxBaseComponent<LabelBoxProps>() {
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: any;
 
-    constructor(props: LabelBoxProps) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -45,7 +41,7 @@ export class LabelBox extends ViewBoxBaseComponent<LabelBoxProps>() {
     }
 
     @computed get Title() {
-        return this.dataDoc.title_custom ? StrCast(this.Document.title) : this._props.label ? this._props.label : Field.toString(this.dataDoc[this.fieldKey] as Field);
+        return Field.toString(this.dataDoc[this.fieldKey] as Field);
     }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
-- 
cgit v1.2.3-70-g09d2


From f1ed1cff137c06afc4d4db8a8778f7827404889b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 9 Mar 2024 19:05:30 -0500
Subject: fixed up default text that uses a template to process an initial
 carriage return properly.  fixed text with inherited templates to be able to
 show fields with a default dashField value from template that can be
 overidden on instance.

---
 src/client/documents/Documents.ts                  | 11 +++---
 src/client/views/MarqueeAnnotator.tsx              |  4 +--
 src/client/views/PreviewCursor.tsx                 |  2 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 36 +++++++++++--------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../collectionSchema/SchemaTableCell.tsx           |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  4 +--
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |  4 +--
 .../views/nodes/formattedText/FormattedTextBox.tsx | 40 ++++++++++------------
 10 files changed, 55 insertions(+), 52 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e6969d1f3..7a3b965fe 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -3,7 +3,7 @@ import { action, reaction, runInAction } from 'mobx';
 import { basename } from 'path';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc';
-import { Initializing } from '../../fields/DocSymbols';
+import { DocData, Initializing } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { HtmlField } from '../../fields/HtmlField';
 import { InkField, PointData } from '../../fields/InkField';
@@ -1981,10 +1981,8 @@ export namespace DocUtils {
         }
     }
 
-    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) {
+    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) {
         const tbox = Docs.Create.TextDocument('', {
-            _xMargin: noMargins ? 0 : undefined,
-            _yMargin: noMargins ? 0 : undefined,
             annotationOn,
             backgroundColor,
             _width: width || 200,
@@ -1997,11 +1995,14 @@ export namespace DocUtils {
             _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
             title,
         });
+
         const template = Doc.UserDoc().defaultTextLayout;
         if (template instanceof Doc) {
+            // if a default text template is specified
             tbox._width = NumCast(template._width);
             tbox.layout_fieldKey = 'layout_' + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template;
+            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; // set the text doc's layout to render with the text template
+            tbox[DocData].proto = template; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
         }
         return tbox;
     }
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index a4303c3aa..f59042b04 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -88,7 +88,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
 
         const textRegionAnno = Docs.Create.HTMLMarkerDocument([], {
             annotationOn: this.props.Document,
-            text: this.props.selectionText(),
+            text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
             backgroundColor: 'transparent',
             presentation_duration: 2100,
             presentation_transition: 500,
@@ -198,7 +198,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
             const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
 
             const targetCreator = (annotationOn: Doc | undefined) => {
-                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
                 FormattedTextBox.SetSelectOnLoad(target);
                 return target;
             };
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 456b753b4..a94c18295 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -85,7 +85,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                 } else {
                     FormattedTextBox.PasteOnLoad = e;
                     if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault();
-                    UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined)), 'paste');
+                    UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined)), 'paste');
                 }
             }
             //pasting in images
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 0c0e49411..c6bbcb0a5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -884,7 +884,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         // prettier-ignore
         switch (property.split(':')[0]) {
             case StyleProp.Opacity:         return this.treeView.outlineMode ? undefined : 1;
-            case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+            case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : undefined;//StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
             case StyleProp.Highlighting:    if (this.treeView.outlineMode) return undefined;
             case StyleProp.BoxShadow:       return undefined;
             case StyleProp.DocContents:
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4eb946939..9500b918a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -30,7 +30,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
 import { freeformScrollMode } from '../../../util/SettingsManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
 import { Timeline } from '../../animationtimeline/Timeline';
 import { ContextMenu } from '../../ContextMenu';
 import { GestureOverlay } from '../../GestureOverlay';
@@ -1146,23 +1146,29 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     isContentActive = () => this._props.isContentActive();
 
-    @undoBatch
+    /**
+     * Create a new text note of the same style as the one being typed into.
+     * If the text doc is be part of a larger templated doc, the new Doc will be a copy of the templated Doc
+     *
+     * @param fieldProps render props for the text doc being typed into
+     * @param below whether to place the new text Doc below or to the right of the one being typed into.
+     * @returns whether the new text doc was created and added successfully
+     */
+    createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => {
+        const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document);
+        const newDoc = Doc.MakeCopy(textDoc, true);
+        newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
+        newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
+        newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
+        FormattedTextBox.SetSelectOnLoad(newDoc);
+        FormattedTextBox.DontSelectInitialText = true;
+        return this.addDocument?.(newDoc);
+    }, 'copied text note');
+
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
-            const below = !e.altKey && e.key !== 'Tab';
-            const layout_fieldKey = StrCast(fieldProps.fieldKey);
-            const newDoc = Doc.MakeCopy(fieldProps.Document, true);
-            const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)];
-            newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (below) newDoc.y = NumCast(fieldProps.Document.y) + NumCast(fieldProps.Document._height) + 10;
-            else newDoc.x = NumCast(fieldProps.Document.x) + NumCast(fieldProps.Document._width) + 10;
-            if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
-                newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
-            }
-            newDoc[DocData].text = undefined;
-            FormattedTextBox.SetSelectOnLoad(newDoc);
-            return this.addDocument?.(newDoc);
+            return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
         }
     };
     @computed get childPointerEvents() {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e22b83307..b913e05ad 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -178,7 +178,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
         } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.length < 2) {
             FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : '';
             FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
-            this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this._props.xPadding === 0));
+            this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100));
             e.stopPropagation();
         }
     };
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 46867665d..ed1b519b4 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -134,7 +134,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                         if (shiftDown && enterKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
                         }
-                        const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value);
+                        const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
                         this._props.finishEdit?.();
                         return ret;
                     }, 'edit schema cell')}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c185c66fc..927e6fad4 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -379,7 +379,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         });
 
         const targetCreator = (annotationOn: Doc | undefined) => {
-            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
@@ -592,7 +592,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.Document.title,
-            text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location',
+            text: (StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location') as any,
             config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude),
             config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index 8a5bd7ce6..3eb051dbf 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -232,7 +232,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
         });
 
         const targetCreator = (annotationOn: Doc | undefined) => {
-            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
@@ -466,7 +466,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.Document.title,
-            text: StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location',
+            text: (StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location') as any,
             config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
             config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 54e3e7b44..1ff7274f8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -67,7 +67,6 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
 import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
-import Select from 'react-select';
 // import * as applyDevTools from 'prosemirror-dev-tools';
 @observer
 export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
@@ -100,7 +99,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _recordingStart: number = 0;
     private _ignoreScroll = false;
-    private _lastText = '';
     private _hadDownFocus = false;
     private _focusSpeed: Opt<number>;
     private _keymap: any = undefined;
@@ -306,7 +304,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             e.preventDefault();
             e.stopPropagation();
             const targetCreator = (annotationOn?: Doc) => {
-                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn);
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn);
                 FormattedTextBox.SetSelectOnLoad(target);
                 return target;
             };
@@ -1457,39 +1455,37 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
 
         const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, FormattedTextBox.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()));
-        if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
-            const selLoadChar = FormattedTextBox.SelectOnLoadChar;
+        const selLoadChar = FormattedTextBox.SelectOnLoadChar;
+        if (selectOnLoad) {
             FormattedTextBox.SelectOnLoad = undefined;
+            FormattedTextBox.SelectOnLoadChar = '';
+        }
+        if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             this._props.select(false);
             if (selLoadChar) {
                 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 storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const tr = this._editorView.state.tr
-                    .setStoredMarks(storedMarks)
-                    .insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size)
-                    .setStoredMarks(storedMarks);
+                const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks);
+                const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1);
+                const tr = tr2.setStoredMarks(storedMarks);
+
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
             } else if (curText && !FormattedTextBox.DontSelectInitialText) {
                 selectAll(this._editorView.state, this._editorView?.dispatch);
             }
         }
-        selectOnLoad && this._editorView!.focus();
+        if (selectOnLoad) {
+            FormattedTextBox.DontSelectInitialText = false;
+            this._editorView!.focus();
+        }
         if (this._props.isContentActive()) this.prepareForTyping();
-        if (this._editorView) {
-            const tr = this._editorView.state.tr;
-            const { from, to } = tr.selection;
-            // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated.
-            if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250);
-
-            if (FormattedTextBox.PasteOnLoad) {
-                const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
-                FormattedTextBox.PasteOnLoad = undefined;
-                pdfAnchorId && this.addPdfReference(pdfAnchorId);
-            }
+        if (this._editorView && FormattedTextBox.PasteOnLoad) {
+            const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
+            FormattedTextBox.PasteOnLoad = undefined;
+            pdfAnchorId && this.addPdfReference(pdfAnchorId);
         }
-        FormattedTextBox.DontSelectInitialText = false;
     }
 
     // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
-- 
cgit v1.2.3-70-g09d2

