From d675e32fdf2f4e0ca5a7adf8cbc5b462e81bc7f0 Mon Sep 17 00:00:00 2001
From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com>
Date: Mon, 5 Jul 2021 13:28:54 -0700
Subject: searchbox zoom in and center on search

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

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index fb60265e3..65188e6f3 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -25,7 +25,7 @@ import { CollectionDockingView } from "./CollectionDockingView";
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
 import { CollectionLinearView } from './CollectionLinearView';
-import CollectionMapView from './CollectionMapView';
+import CollectionMapView from './MapView/CollectionMapView';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { CollectionPileView } from './CollectionPileView';
-- 
cgit v1.2.3-70-g09d2


From 3381bbb0ef5160707513f4bbbe551ca551b64b0d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 13 Nov 2021 10:38:53 -0500
Subject: change isContentActive to a tri-state to allow turning on/off and
 default - fixes issues with videobox and others so that content can be turned
 off reliably.   added annotation overlay for treeViews for ppt like slides. 
 lots of fixes to tree view to get layout to be more robust.

---
 src/Utils.ts                                       |   2 +-
 src/client/documents/Documents.ts                  |   4 +-
 src/client/util/CurrentUserUtils.ts                |  20 +-
 src/client/views/DocComponent.tsx                  |  18 +-
 src/client/views/DocumentDecorations.scss          |   6 +-
 src/client/views/DocumentDecorations.tsx           |  27 +--
 src/client/views/MainView.tsx                      |   6 +-
 src/client/views/OverlayView.tsx                   |   2 +-
 src/client/views/Palette.tsx                       |   2 +-
 src/client/views/PropertiesView.tsx                |   2 +-
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   7 +-
 .../views/collections/CollectionTreeView.scss      |   9 +
 .../views/collections/CollectionTreeView.tsx       | 252 ++++++++++++---------
 src/client/views/collections/CollectionView.tsx    |  12 +-
 src/client/views/collections/TabDocView.tsx        |   3 +-
 src/client/views/collections/TreeView.scss         |   7 +-
 src/client/views/collections/TreeView.tsx          |  49 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  10 +-
 .../CollectionMulticolumnView.tsx                  |   4 +-
 .../CollectionMultirowView.tsx                     |   4 +-
 .../collectionMulticolumn/MulticolumnResizer.tsx   |   2 +-
 .../collectionMulticolumn/MultirowResizer.tsx      |   2 +-
 .../collectionSchema/CollectionSchemaHeaders.tsx   |   2 +-
 .../collections/collectionSchema/SchemaTable.tsx   |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  10 +-
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/FilterBox.tsx               |   2 +-
 src/client/views/nodes/LinkBox.tsx                 |   2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   2 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   2 +-
 src/client/views/nodes/VideoBox.scss               |  10 +-
 src/client/views/nodes/VideoBox.tsx                |  11 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  18 +-
 src/mobile/AudioUpload.tsx                         |   2 +-
 src/mobile/MobileInterface.tsx                     |   2 +-
 36 files changed, 298 insertions(+), 221 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index bfb29fe8d..ca1432de2 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -448,7 +448,7 @@ export function returnEmptyDoclist() { return [] as any[]; }
 
 export let emptyPath = [];
 
-export function emptyFunction() { }
+export function emptyFunction() { return undefined; }
 
 export function unimplementedFunction() { throw new Error("This function is not implemented, but should be."); }
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index da5c8efa9..219f51a3a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -94,6 +94,7 @@ type DROPt = DAInfo | dropActionType;
 export class DocumentOptions {
     system?: BOOLt = new BoolInfo("is this a system created/owned doc");
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
+    allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?");
     childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
     targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? ");
     color?: string; // foreground color data doc
@@ -289,6 +290,7 @@ export class DocumentOptions {
     treeViewExpandedViewLock?: boolean; // whether the expanded view can be changed
     treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
     treeViewTruncateTitleWidth?: number;
+    treeViewHasOverlay?: boolean; // whether the treeview has an overlay for freeform annotations
     treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
     sidebarColor?: string;  // background color of text sidebar
     sidebarViewType?: string; // collection type of text sidebar
@@ -821,7 +823,7 @@ export namespace Docs {
         }
 
         export function TreeDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Tree }, id, undefined, protoId);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xMargin: 5, _yMargin: 5, ...options, _viewType: CollectionViewType.Tree }, id, undefined, protoId);
         }
 
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 9d06ad8a3..c9bef5924 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -339,7 +339,7 @@ export class CurrentUserUtils {
         const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc);
         if (doc.templateDocs === undefined) {
             doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], {
-                title: "template layouts", _xPadding: 0, system: true,
+                title: "template layouts", _xMargin: 0, system: true,
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
             }));
         }
@@ -421,7 +421,11 @@ export class CurrentUserUtils {
             ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptySlide === undefined) {
-            const textDoc = Docs.Create.TreeDocument([], { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", _autoHeight: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            const textDoc = Docs.Create.TreeDocument([], {
+                title: "Slide", _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true,
+                allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true,
+                backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"])
+            });
             Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
             FormattedTextBox.SelectOnLoad = textDoc[Id];
             doc.emptySlide = textDoc;
@@ -811,7 +815,7 @@ export class CurrentUserUtils {
             const newDashboardButton: Doc = Docs.Create.FontIconDocument({ onClick: newDashboard, _forceActive: true, toolTip: "Create new dashboard", _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, freezeChildren: "remove|add",
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true,
                 explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
@@ -840,7 +844,7 @@ export class CurrentUserUtils {
             const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
             doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Trails", _showTitle: "title", _height: 100,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
                 explainer: "All of the trails that you have created will appear here."
@@ -865,7 +869,7 @@ export class CurrentUserUtils {
             });
             doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
                 title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
@@ -884,7 +888,7 @@ export class CurrentUserUtils {
             const clearDocsButton: Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true });
             doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
                 explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -902,7 +906,7 @@ export class CurrentUserUtils {
         if (doc.currentFilter === undefined) {
             doc.currentFilter = Docs.Create.FilterDocument({
                 title: "Unnamed Filter", _height: 150,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "none",
+                treeViewHideTitle: true, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, childDropAction: "none",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true
             });
@@ -918,7 +922,7 @@ export class CurrentUserUtils {
             doc.treeViewOpen = true;
             doc.treeViewExpandedView = "fields";
             doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title",
+                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
             })) as any as Doc;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 32c351bf5..b9772fd57 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -44,7 +44,7 @@ interface ViewBoxBaseProps {
     fieldKey: string;
     layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
-    isContentActive: () => boolean;
+    isContentActive: () => boolean | undefined;
     renderDepth: number;
     rootSelected: (outsideReaction?: boolean) => boolean;
 }
@@ -65,10 +65,12 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor:
 
         lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.ContainingCollectionDoc }).result;
 
-        isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None ||
-            (this.props.isContentActive?.() || this.props.Document.forceActive ||
-                this.props.isSelected(outsideReaction) ||
-                this.props.rootSelected(outsideReaction)) ? true : false)
+        isContentActive = (outsideReaction?: boolean) => (
+            this.props.isContentActive?.() === false ? false :
+                (CurrentUserUtils.SelectedTool !== InkTool.None ||
+                    (this.props.isContentActive?.() || this.props.Document.forceActive ||
+                        this.props.isSelected(outsideReaction) ||
+                        this.props.rootSelected(outsideReaction)) ? true : undefined))
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
@@ -82,7 +84,7 @@ export interface ViewBoxAnnotatableProps {
     fieldKey: string;
     filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
     layerProvider?: (doc: Doc, assign?: boolean) => boolean;
-    isContentActive: () => boolean;
+    isContentActive: () => boolean | undefined;
     select: (isCtrlPressed: boolean) => void;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     isSelected: (outsideReaction?: boolean) => boolean;
@@ -165,13 +167,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         // otherwise, the document being moved must be able to be removed from its container before
         // moving it into the target.
         @action.bound
-        moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string): boolean => {
+        moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => {
             if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
                 return true;
             }
             const first = doc instanceof Doc ? doc : doc[0];
             if (!first?._stayInCollection && addDocument !== returnFalse) {
-                return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc));
+                return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey));
             }
             return false;
         }
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index d8ad47ecb..82dca1287 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -240,8 +240,10 @@ $linkGap: 3px;
         text-align: center;
         display: flex;
         margin-left: 5px;
-        height: 22px;
+        height: 20px;
         position: absolute;
+        border-radius: 8px;
+        background: rgba(159,159,159,0.1);
 
         .documentDecorations-titleSpan, 
         .documentDecorations-titleSpan-Dark  {
@@ -288,7 +290,7 @@ $linkGap: 3px;
     text-align: center;
     display: flex;
     margin-left: 5px;
-    height: 22px;
+    height: 20px;
     position: absolute;
     .documentDecorations-titleSpan {
       width: 100%;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 522995479..1c0b1b995 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -454,17 +454,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             </Tooltip>);
 
         const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
-        const titleArea = this._edtingTitle ?
-            <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }}
-                type="text" name="dynbox" autoComplete="on"
-                value={this._accumulatedTitle}
-                onBlur={e => this.titleBlur()}
-                onChange={action(e => this._accumulatedTitle = e.target.value)}
-                onKeyPress={this.titleEntered} /> :
-            <div className="documentDecorations-title" style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }} key="title" onPointerDown={this.onTitleDown} >
-                <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
-            </div>;
+        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} style={{ width: "100%" }} key="title" /> :
+            this._edtingTitle ?
+                <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
+                    style={{ width: `calc(100% - ${hideResizers ? 0 : 20}px` }}
+                    type="text" name="dynbox" autoComplete="on"
+                    value={this._accumulatedTitle}
+                    onBlur={e => this.titleBlur()}
+                    onChange={action(e => this._accumulatedTitle = e.target.value)}
+                    onKeyPress={this.titleEntered} /> :
+                <div className="documentDecorations-title" style={{ width: `calc(100% - ${hideResizers ? 0 : 20}px` }} key="title" onPointerDown={this.onTitleDown} >
+                    <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
+                </div>;
 
         let inMainMenuPanel = false;
         for (let node = seldoc.ContentDiv; node && !inMainMenuPanel; node = node?.parentNode as any) {
@@ -498,8 +499,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
                     {!canDelete ? <div /> : topBtn("close", "times", undefined, this.onCloseClick, "Close")}
-                    {hideTitle ? (null) : titleArea}{!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
-
+                    {titleArea}
+                    {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
                         <>
                             {SelectionManager.Views().length !== 1 || hideTitle ? (null) :
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9a885fbf8..546b0e360 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -511,7 +511,7 @@ export class MainView extends React.Component {
                     bringToFront={emptyFunction}
                     select={emptyFunction}
                     isAnyChildContentActive={returnFalse}
-                    isContentActive={returnFalse}
+                    isContentActive={emptyFunction}
                     isSelected={returnFalse}
                     docViewPath={returnEmptyDoclist}
                     moveDocument={this.moveButtonDoc}
@@ -592,7 +592,7 @@ export class MainView extends React.Component {
                     pinToPres={returnFalse}
                     ScreenToLocalTransform={Transform.Identity}
                     bringToFront={returnFalse}
-                    isContentActive={returnFalse}
+                    isContentActive={emptyFunction}
                     whenChildContentsActiveChanged={returnFalse}
                     focus={returnFalse}
                     docViewPath={returnEmptyDoclist}
@@ -669,7 +669,7 @@ export class MainView extends React.Component {
                             pinToPres={returnFalse}
                             ScreenToLocalTransform={Transform.Identity}
                             bringToFront={returnFalse}
-                            isContentActive={returnFalse}
+                            isContentActive={emptyFunction}
                             whenChildContentsActiveChanged={returnFalse}
                             focus={returnFalse}
                             PanelWidth={() => 500}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index af04b967a..7cf388872 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -191,7 +191,7 @@ export class OverlayView extends React.Component {
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={1}
                     isDocumentActive={returnTrue}
-                    isContentActive={returnFalse}
+                    isContentActive={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 86ab881bb..529697f71 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -50,7 +50,7 @@ export default class Palette extends React.Component<PaletteProps> {
                             PanelHeight={() => window.screen.height}
                             renderDepth={0}
                             isDocumentActive={returnTrue}
-                            isContentActive={returnFalse}
+                            isContentActive={emptyFunction}
                             focus={emptyFunction}
                             docViewPath={returnEmptyDoclist}
                             styleProvider={returnEmptyString}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ab9022a84..1083e0075 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -295,7 +295,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     freezeDimensions={true}
                     dontCenter={"y"}
                     isDocumentActive={returnFalse}
-                    isContentActive={returnFalse}
+                    isContentActive={emptyFunction}
                     NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
                     NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
                     PanelWidth={panelWidth}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 648ff5087..bffaf86b1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -226,7 +226,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             fitWidth={this.props.childFitWidth}
-            isContentActive={returnFalse}
+            isContentActive={emptyFunction}
             isDocumentActive={this.isContentActive}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5dffc65fc..fc1bcb8b9 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -22,7 +22,6 @@ import ReactLoading from 'react-loading';
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
-    SetSubView?: (subView: any) => void;
     isAnyChildContentActive: () => boolean;
 }
 
@@ -49,10 +48,6 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             this.createDashEventsTarget(ele);
         }
 
-        componentDidMount() {
-            this.props.SetSubView?.(this);
-        }
-
         componentWillUnmount() {
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
@@ -220,7 +215,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || !this.props.isAnnotationOverlay ||
+                        const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || (!this.props.isAnnotationOverlay || this.props.Document.allowOverlayDrop) ||
                             Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
                         added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
                     } else {
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index d370d21ab..b664d9d82 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -71,6 +71,15 @@
     display: none;
 }
 
+.collectionTreeView-titleBar {
+    display: inline-block;
+    width: 100%;
+    height: max-content;
+    .contentFittingDocumentView {
+        display: block;  // makes titleBar take up full width of the treeView (flex doesn't for some reason)
+    }
+}
+
 .collectionTreeView-keyHeader:hover {
     background: #797777;
     cursor: pointer;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 3852987b9..6fb18d4c2 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,4 +1,3 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
@@ -8,13 +7,14 @@ import { Document, listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, emptyFunction } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -22,11 +22,11 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
+import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { TreeView } from "./TreeView";
 import React = require("react");
-import { Transform } from '../../util/Transform';
 const _global = (window /* browser */ || global /* node */) as any;
 
 export type collectionTreeViewProps = {
@@ -41,10 +41,13 @@ export type collectionTreeViewProps = {
 
 @observer
 export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
-    private treedropDisposer?: DragManager.DragDropDisposer;
+    private _treedropDisposer?: DragManager.DragDropDisposer;
     private _mainEle?: HTMLDivElement;
+    private _titleRef?: HTMLDivElement | HTMLInputElement | null;
     private _disposers: { [name: string]: IReactionDisposer } = {};
-    MainEle = () => this._mainEle;
+    private _isDisposing = false; // notes that instance is in process of being disposed
+    private refList: Set<any> = new Set(); // list of tree view items to monitor for height changes
+    private observer: any; // observer for mnonitoring tree view items.
 
     @computed get doc() { return this.props.Document; }
     @computed get dataDoc() { return this.props.DataDoc || this.doc; }
@@ -54,6 +57,10 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; }
     @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; }
 
+    @observable _explainerHeight = 0; // height of the description of the tree view
+
+    MainEle = () => this._mainEle;
+
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
@@ -62,11 +69,10 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
             this.props.rootSelected(outsideReaction)) ? true : false)
 
-    isDisposing = false;
     componentWillUnmount() {
-        this.isDisposing = true;
+        this._isDisposing = true;
         super.componentWillUnmount();
-        this.treedropDisposer?.();
+        this._treedropDisposer?.();
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
@@ -76,13 +82,13 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             { fireImmediately: true });
     }
 
-    refList: Set<any> = new Set();
-    observer: any;
     computeHeight = () => {
-        if (this.isDisposing) return;
-        const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.paddingTop() + this.paddingBot());
-        this.layoutDoc._autoHeightMargins = bodyHeight;
-        this.props.setHeight(this.documentTitleHeight() + bodyHeight);
+        if (!this._isDisposing) {
+            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
+            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
+            this.layoutDoc._autoHeightMargins = bodyHeight;
+            this.props.setHeight(bodyHeight + titleHeight);
+        }
     }
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
@@ -101,8 +107,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         }
     }
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
-        this.treedropDisposer?.();
-        if (this._mainEle = ele) this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
+        this._treedropDisposer?.();
+        if (this._mainEle = ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
     }
 
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -165,60 +171,54 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
     }
 
-    editableTitle = (childDocs: Doc[]) => {
-        return !this.dataDoc ? (null) :
-            <EditableView
-                contents={this.dataDoc.title}
-                display={"block"}
-                maxHeight={72}
-                height={"auto"}
-                GetValue={() => StrCast(this.dataDoc.title)}
-                SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                    if (enter && this.props.Document.treeViewType === "outline") this.makeTextCollection(childDocs);
-                    this.dataDoc.title = value;
-                    return true;
-                })} />;
+    get editableTitle() {
+        return <EditableView
+            contents={this.dataDoc.title}
+            display={"block"}
+            maxHeight={72}
+            height={"auto"}
+            GetValue={() => StrCast(this.dataDoc.title)}
+            SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+                if (enter && this.props.Document.treeViewType === "outline") this.makeTextCollection(this.treeChildren);
+                this.dataDoc.title = value;
+                return true;
+            })} />;
     }
 
 
-    documentTitle = (childDocs: Doc[]) => {
-        return <div style={{ display: "inline-block", width: "100%", height: this.documentTitleHeight() }} key={this.doc[Id]}
-            onKeyDown={e => {
-                e.stopPropagation();
-                e.key === "Enter" && this.makeTextCollection(childDocs);
-            }}>
-            <DocumentView
-                Document={this.doc}
-                DataDoc={undefined}
-                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
-                renderDepth={this.props.renderDepth + 1}
-                isContentActive={this.isContentActive}
-                isDocumentActive={this.isContentActive}
-                rootSelected={returnTrue}
-                docViewPath={this.props.docViewPath}
-                styleProvider={this.props.styleProvider}
-                layerProvider={this.props.layerProvider}
-                PanelWidth={this.documentTitleWidth}
-                PanelHeight={this.documentTitleHeight}
-                NativeWidth={this.documentTitleWidth}
-                NativeHeight={this.documentTitleHeight}
-                focus={this.props.focus}
-                treeViewDoc={this.props.Document}
-                ScreenToLocalTransform={this.titleTransform}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionDoc={this.doc}
-                ContainingCollectionView={this.props.CollectionView}
-                addDocument={this.props.addDocument}
-                moveDocument={returnFalse}
-                removeDocument={returnFalse}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                bringToFront={returnFalse}
-            />
-        </div>;
+    get documentTitle() {
+        return <DocumentView
+            Document={this.doc}
+            DataDoc={undefined}
+            LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+            renderDepth={this.props.renderDepth + 1}
+            isContentActive={this.isContentActive}
+            isDocumentActive={this.isContentActive}
+            rootSelected={returnTrue}
+            forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
+            docViewPath={this.props.docViewPath}
+            styleProvider={this.props.styleProvider}
+            layerProvider={this.props.layerProvider}
+            PanelWidth={this.documentTitleWidth}
+            PanelHeight={this.documentTitleHeight}
+            NativeWidth={this.documentTitleWidth}
+            NativeHeight={this.documentTitleHeight}
+            focus={this.props.focus}
+            treeViewDoc={this.props.Document}
+            ScreenToLocalTransform={this.titleTransform}
+            docFilters={returnEmptyFilter}
+            docRangeFilters={returnEmptyFilter}
+            searchFilterDocs={returnEmptyDoclist}
+            ContainingCollectionDoc={this.doc}
+            ContainingCollectionView={this.props.CollectionView}
+            addDocument={this.props.addDocument}
+            moveDocument={returnFalse}
+            removeDocument={returnFalse}
+            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+            addDocTab={this.props.addDocTab}
+            pinToPres={this.props.pinToPres}
+            bringToFront={returnFalse}
+        />;
     }
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
@@ -263,21 +263,31 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         );
     }
     @computed get titleBar() {
-        const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
-        return hideTitle ? (null) : (this.outlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren);
+        return this.dataDoc === null ? (null) :
+            <div className="collectionTreeView-titleBar" key={this.doc[Id]}
+                style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingRight: this.marginX(), paddingTop: this.marginTop() } : {}}
+                ref={r => this._titleRef = r}
+                onKeyDown={e => {
+                    if (this.outlineMode) {
+                        e.stopPropagation();
+                        e.key === "Enter" && this.makeTextCollection(this.treeChildren);
+                    }
+                }}>
+                {this.outlineMode ? this.documentTitle : this.editableTitle}
+            </div>;
+    }
+
+    @computed get noviceExplainer() {
+        return !Doc.UserDoc().noviceMode || !this.rootDoc.explainer ? (null) :
+            <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
     }
 
     return35 = () => 35;
     @computed get buttonMenu() {
-        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
-        if (menuDoc) {
-
-            const width: number = NumCast(menuDoc._width, 30);
-            const height: number = NumCast(menuDoc._height, 30);
-            console.log(menuDoc.title, width, height);
-            return (<div className="buttonMenu-docBtn"
-                style={{ width: width, height: height }}>
+        return !menuDoc ? null :
+            (<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -306,11 +316,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                     ContainingCollectionDoc={undefined}
                 />
             </div>);
-        }
     }
 
-    @observable _explainerHeight: number = 0;
-
     @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
     @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
 
@@ -321,47 +328,82 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
     }
-    paddingX = () => NumCast(this.doc._xPadding, 15);
-    paddingTop = () => NumCast(this.doc._yPadding, 20);
-    paddingBot = () => NumCast(this.doc._yPadding, 20);
+    marginX = () => NumCast(this.doc._xMargin);
+    marginTop = () => NumCast(this.doc._yMargin);
+    marginBot = () => NumCast(this.doc._yMargin);
     documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth());
     documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
     titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20));
     truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
-    panelWidth = () => (this.props.PanelWidth() - 2 * this.paddingX()) * (this.props.scaling?.() || 1);
-    render() {
-        TraceMobx();
+    panelWidth = () => (this.props.PanelWidth() - 2 * this.marginX()) * (this.props.scaling?.() || 1);
+
+    addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
+    remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
+    moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
+        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false
+
+    contentFunc = () => {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
         const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
-        const buttonMenu = this.rootDoc.buttonMenu;
-        const noviceExplainer = this.rootDoc.explainer;
-
-        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-            <>
-                {this.titleBar}
+        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : this.titleBar;
+        return [
+            <div className="collectionTreeView-contents" key="tree" style={{
+                ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+                overflow: "auto",
+                height: this.layoutDoc._autoHeight ? "max-content" : "100%"
+            }} >
+                {titleBar}
                 <div className="collectionTreeView-container"
-                    style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}}
+                    style={{
+                        transform: this.outlineMode ? `scale(${this.contentScaling})` : "",
+                        paddingLeft: `${this.marginX()}px`,
+                        height: "max-content",
+                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : ""
+                    }}
                     onContextMenu={this.onContextMenu}>
-                    {buttonMenu || noviceExplainer ? <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
-                        {buttonMenu ? this.buttonMenu : null}
-                        {Doc.UserDoc().noviceMode && noviceExplainer ?
-                            <div className="documentExplanation">
-                                {noviceExplainer}
-                            </div>
-                            : null
-                        }
-                    </div> : null}
+                    {!this.buttonMenu && !this.noviceExplainer ? (null) :
+                        <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
+                            {this.buttonMenu}
+                            {this.noviceExplainer}
+                        </div>
+                    }
                     <div className="collectionTreeView-dropTarget"
-                        style={{ background: background(), height: `calc(100% - ${this._explainerHeight}px)`, paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
+                        style={{
+                            background: background(),
+                            height: `calc(100% - ${this._explainerHeight}px)`,
+                            pointerEvents: pointerEvents()
+                        }}
                         onWheel={e => e.stopPropagation()}
                         onDrop={this.onTreeDrop}
-                        ref={this.createTreeDropTarget}>
+                        ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
                         <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
                             {this.treeViewElements}
                         </ul>
                     </div >
                 </div>
-            </>;
+            </div>
+        ];
+    }
+    render() {
+        TraceMobx();
+
+        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
+            this.doc.treeViewHasOverlay ?
+                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                    isAnnotationOverlay={true}
+                    isAnnotationOverlayScrollable={true}
+                    childDocumentsActive={this.props.isDocumentActive}
+                    fieldKey={this.props.fieldKey + "-annotations"}
+                    dropAction={"move"}
+                    select={emptyFunction}
+                    addDocument={this.addAnnotationDocument}
+                    removeDocument={this.remAnnotationDocument}
+                    moveDocument={this.moveAnnotationDocument}
+                    bringToFront={emptyFunction}
+                    renderDepth={this.props.renderDepth + 1} >
+                    {this.contentFunc}
+                </CollectionFreeFormView> :
+                this.contentFunc();
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 38e027fb3..8e84b59de 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -63,6 +63,7 @@ export enum CollectionViewType {
 }
 export interface CollectionViewProps extends FieldViewProps {
     isAnnotationOverlay?: boolean;  // is the collection an annotation overlay (eg an overlay on an image/video/etc)
+    isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 
@@ -125,8 +126,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     }
 
     screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
-    private SubView = (type: CollectionViewType, props: SubCollectionViewProps) => {
+    private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
         TraceMobx();
+        if (type === undefined) return null;
         switch (type) {
             default:
             case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
@@ -246,17 +248,13 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
     @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
 
-
-    @observable _subView: any = undefined;
-
     isContentActive = (outsideReaction?: boolean) => {
-        return this.props.isContentActive() ? true : false;
+        return this.props.isContentActive();
     }
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
             ...this.props,
-            SetSubView: action((subView: any) => this._subView = subView),
             addDocument: this.addDocument,
             moveDocument: this.moveDocument,
             removeDocument: this.removeDocument,
@@ -273,7 +271,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}>
             {this.showIsTagged()}
-            {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
+            {this.renderSubView(this.collectionViewType, props)}
         </div>);
     }
 }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index eb95bb913..5ba019698 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -476,7 +476,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
                 <CollectionFreeFormView
                     Document={this.props.document}
-                    SetSubView={() => this}
                     CollectionView={undefined}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
@@ -484,7 +483,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
                     noOverlay={true} // don't render overlay Docs since they won't scale
                     setHeight={returnFalse}
-                    isContentActive={returnFalse}
+                    isContentActive={emptyFunction}
                     isAnyChildContentActive={returnFalse}
                     select={emptyFunction}
                     dropAction={undefined}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 1ebc5873e..2e33d3564 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -53,14 +53,11 @@
     }
 }
 
+.treeView-container-outline-active
 .treeView-container-active {
     z-index: 100;
     position: relative;
-
-    .formattedTextbox-sidebar {
-        background-color: #ffff001f !important;
-        height: 500px !important;
-    }
+    pointer-events: all;
 }
 
 .treeView-openRight {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 7f2128230..a266c301f 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -81,7 +81,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     static _openLevelScript: Opt<ScriptField | undefined>;
     private _header: React.RefObject<HTMLDivElement> = React.createRef();
     private _tref = React.createRef<HTMLDivElement>();
-    private _docRef: Opt<DocumentView>;
+    @observable _docRef: Opt<DocumentView>;
     private _selDisposer: Opt<IReactionDisposer>;
     private _editTitleScript: (() => ScriptField) | undefined;
     private _openScript: (() => ScriptField) | undefined;
@@ -116,7 +116,8 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAliases() { return this.childDocList("aliases"); }
     @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
-    @computed get selected() { return SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
+    @computed get selected() { return SelectionManager.IsSelected(this._docRef); }
+    //      SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
 
     childDocList(field: string) {
         const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
@@ -125,7 +126,12 @@ export class TreeView extends React.Component<TreeViewProps> {
             DocListCastOrNull(this.doc[field]); // otherwise use the document's data field
     }
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-        return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
+        if (this.doc !== target && addDoc !== returnFalse) { // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
+            if (this.props.removeDoc?.(doc) === true) {
+                return addDoc(doc);
+            }
+        }
+        return false;
     }
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
         this.props.treeView.props.select(false);
@@ -141,8 +147,10 @@ export class TreeView extends React.Component<TreeViewProps> {
             this._editTitle = false;
         }
         else if (docView.isSelected()) {
+            const doc = docView.Document;
+            SelectionManager.SelectSchemaViewDoc(doc);
             this._editTitle = true;
-            this._selDisposer = reaction(() => docView.isSelected(), sel => !sel && this.setEditTitle(undefined));
+            this._selDisposer = reaction(() => SelectionManager.SelectedSchemaDoc(), seldoc => seldoc !== doc && this.setEditTitle(undefined));
         } else {
             docView.select(false);
         }
@@ -222,7 +230,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     public static makeTextBullet() {
         const bullet = Docs.Create.TextDocument("-text-", {
             layout: CollectionView.LayoutString("data"),
-            title: "-title-", "sidebarColor": "transparent", "sidebarViewType": CollectionViewType.Freeform,
+            title: "-title-",
             treeViewExpandedViewLock: true, treeViewExpandedView: "data",
             _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: "outline",
             x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, backgroundColor: "transparent", _width: 1000, _height: 10
@@ -266,23 +274,25 @@ export class TreeView extends React.Component<TreeViewProps> {
             e.stopPropagation();
         }
         const docDragData = de.complete.docDragData;
-        if (docDragData) {
-            e.stopPropagation();
+        if (docDragData && pt[0] < rect.left + rect.width) {
             if (docDragData.draggedDocuments[0] === this.doc) return true;
-            this.dropDocuments(docDragData.droppedDocuments, before, inside, docDragData.dropAction, docDragData.moveDocument, docDragData.treeViewDoc === this.props.treeView.props.Document);
+            if (this.dropDocuments(docDragData.droppedDocuments, before, inside, docDragData.dropAction, docDragData.moveDocument, docDragData.treeViewDoc === this.props.treeView.props.Document)) {
+                e.stopPropagation();
+            }
         }
     }
 
     dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) {
         const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
-        const canAdd = !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add") || forceAdd;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add")) || forceAdd;
         const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false;
         const addDoc = !inside ? parentAddDoc :
             (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
         const move = (!dropAction || dropAction === "proto" || dropAction === "move" || dropAction === "same") && moveDocument;
         if (canAdd) {
-            UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false) : addDoc(d)) || added, false));
+            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false) : addDoc(d)) || added, false));
         }
+        return false;
     }
 
     refTransform = (ref: HTMLDivElement | undefined | null) => {
@@ -432,7 +442,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 </div>
             </ul>;
         }
-        return <ul>{this.renderEmbeddedDocument(false)}</ul>; // "layout"
+        return <ul onPointerDown={e => { e.preventDefault(); e.stopPropagation(); }}>{this.renderEmbeddedDocument(false, returnFalse)}</ul>; // "layout"
     }
 
     get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
@@ -581,6 +591,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
 
+    return18 = () => 18;
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -636,10 +647,10 @@ export class TreeView extends React.Component<TreeViewProps> {
                 moveDocument={this.move}
                 removeDocument={this.props.removeDoc}
                 ScreenToLocalTransform={this.getTransform}
-                NativeHeight={() => 18}
+                NativeHeight={this.return18}
                 NativeWidth={this.titleWidth}
                 PanelWidth={this.titleWidth}
-                PanelHeight={() => 18}
+                PanelHeight={this.return18}
                 contextMenuItems={this.contextMenuItems}
                 renderDepth={1}
                 isContentActive={this.props.isContentActive}
@@ -679,6 +690,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
         return <>
             <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
+                style={{ width: "max-content" }}
                 ref={this._header}
                 onClick={this.ignoreEvent}
                 onPointerDown={this.ignoreEvent}
@@ -691,7 +703,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
 
-    renderEmbeddedDocument = (asText: boolean) => {
+    renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
         const layout = StrCast(Doc.LayoutField(this.layoutDoc));
         const isExpandable = layout.includes(FormattedTextBox.name) || layout.includes(SliderBox.name);
         const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
@@ -704,8 +716,8 @@ export class TreeView extends React.Component<TreeViewProps> {
             NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
             NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
             LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
-            isContentActive={asText ? this.props.isContentActive : returnFalse}
-            isDocumentActive={asText ? this.props.isContentActive : returnFalse}
+            isContentActive={isActive}
+            isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
             fitContentsToDoc={returnTrue}
@@ -749,7 +761,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get renderDocumentAsHeader() {
         return <>
             {this.renderBullet}
-            {this.renderEmbeddedDocument(true)}
+            {this.renderEmbeddedDocument(true, this.props.isContentActive)}
         </>;
     }
 
@@ -776,13 +788,12 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
             <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
                 ref={this.createTreeDropTarget}
-
                 onDrop={this.onTreeDrop}
                 //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
                 onKeyDown={this.onKeyDown}>
                 <li className="collection-child">
                     {hideTitle && this.doc.type !== DocumentType.RTF ?
-                        this.renderEmbeddedDocument(false) :
+                        this.renderEmbeddedDocument(false, returnFalse) :
                         this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader, this._editTitle)}
                 </li>
             </div>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 37caf508f..190d0e76a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -165,7 +165,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
     zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1));
-    contentTransform = () => `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
+    contentTransform = () => !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 ? "" : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
     getTransform = () => this.cachedGetTransform.copy();
     getLocalTransform = () => this.cachedGetLocalTransform.copy();
     getContainerTransform = () => this.cachedGetContainerTransform.copy();
@@ -802,7 +802,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         else if (this.props.isContentActive(true) && !this.Document._isGroup) {
             e.stopPropagation();
             e.preventDefault();
-            this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+            !this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
         }
     }
 
@@ -1015,7 +1015,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
-            isContentActive={returnFalse}
+            isContentActive={emptyFunction}
             isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.focusDocument}
             addDocTab={this.addDocTab}
@@ -1034,7 +1034,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             bringToFront={this.bringToFront}
             showTitle={this.props.childShowTitle}
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
-            pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
+            pointerEvents={this.props.isContentActive() === false ? "none" : this.backgroundActive || this.props.childPointerEvents ? "all" :
                 (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
         //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
@@ -1474,6 +1474,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
+                    isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
                     transform={this.contentTransform}
                     zoomScaling={this.zoomScaling}
                     presPaths={BoolCast(this.Document.presPathView)}
@@ -1582,6 +1583,7 @@ interface CollectionFreeFormViewPannableContentsProps {
     progressivize?: boolean;
     presPinView?: boolean;
     isAnnotationOverlay: boolean | undefined;
+    isAnnotationOverlayScrollable: boolean | undefined;
 }
 
 @observer
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 65c345547..ec1cbadd5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas';
 import { List } from '../../../../fields/List';
 import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse, emptyPath, returnEmptyDoclist } from '../../../../Utils';
+import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -228,7 +228,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             LayoutTemplateString={this.props.childLayoutString}
             freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
-            isContentActive={returnFalse}
+            isContentActive={emptyFunction}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 30836854a..a2d51e2e7 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas';
 import { List } from '../../../../fields/List';
 import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse, emptyPath, returnEmptyDoclist } from '../../../../Utils';
+import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -237,7 +237,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
-            isContentActive={returnFalse}
+            isContentActive={emptyFunction}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 9fe18d118..273e609ca 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -11,7 +11,7 @@ import { StyleProp } from "../../StyleProvider";
 interface ResizerProps {
     width: number;
     styleProvider?: StyleProviderFunc;
-    isContentActive?: () => boolean;
+    isContentActive?: () => boolean | undefined;
     columnUnitLength(): number | undefined;
     toLeft?: Doc;
     toRight?: Doc;
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 5478bf709..006ef4df6 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -11,7 +11,7 @@ import { StyleProviderFunc } from "../../nodes/DocumentView";
 interface ResizerProps {
     height: number;
     styleProvider?: StyleProviderFunc;
-    isContentActive?: () => boolean;
+    isContentActive?: () => boolean | undefined;
     columnUnitLength(): number | undefined;
     toTop?: Doc;
     toBottom?: Doc;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index 1306b79cb..dc35b5749 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -225,7 +225,7 @@ export interface KeysDropdownProps {
     fieldKey: string;
     ContainingCollectionDoc: Doc | undefined;
     ContainingCollectionView: Opt<CollectionView>;
-    active?: (outsideReaction?: boolean) => boolean;
+    active?: (outsideReaction?: boolean) => boolean | undefined;
     openHeader: (column: any, screenx: number, screeny: number) => void;
     col: SchemaHeaderField;
     icon: IconProp;
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index bc5a9559f..2219345f6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -68,7 +68,7 @@ export interface SchemaTableProps {
     addDocument?: (document: Doc | Doc[]) => boolean;
     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
-    active: (outsideReaction: boolean | undefined) => boolean;
+    active: (outsideReaction: boolean | undefined) => boolean | undefined;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
     addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6c058ff97..c8a32a911 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -138,6 +138,7 @@ export interface DocumentViewSharedProps {
     hideLinkButton?: boolean;
     hideCaptions?: boolean;
     ignoreAutoHeight?: boolean;
+    forceAutoHeight?: boolean;
     disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     pointerEvents?: string;
     scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
@@ -224,7 +225,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     componentWillUnmount() { this.cleanupHandlers(true); }
     componentDidMount() { this.setupHandlers(); }
-    componentDidUpdate() { this.setupHandlers(); }
+    //componentDidUpdate() { this.setupHandlers(); }
     setupHandlers() {
         this.cleanupHandlers(false);
         if (this._mainCont.current) {
@@ -825,13 +826,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
-        return CurrentUserUtils.SelectedTool !== InkTool.None ||
+        return this.props.isContentActive() === false ? false : (
+            CurrentUserUtils.SelectedTool !== InkTool.None ||
             SnappingManager.GetIsDragging() ||
             this.props.rootSelected() ||
             this.props.Document.forceActive ||
             this.props.isSelected(outsideReaction) ||
             this._componentView?.isAnyChildContentActive?.() ||
-            this.props.isContentActive() ? true : false;
+            this.props.isContentActive()) ? true : undefined;
     }
     @computed get contents() {
         TraceMobx();
@@ -1256,7 +1258,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
                         width: isButton ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: isButton ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
+                        height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
                     <DocumentViewInternal {...this.props}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ee81e106a..943b9f153 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -18,7 +18,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     scrollOverflow?: boolean; // bcz: would like to think this can be avoided -- need to look at further
 
     select: (isCtrlPressed: boolean) => void;
-    isContentActive: (outsideReaction?: boolean) => boolean;
+    isContentActive: (outsideReaction?: boolean) => boolean | undefined;
     isDocumentActive?: () => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     scaling?: () => number;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 2041c7399..fb8e89da9 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -225,7 +225,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, ignoreClick: true
                 });
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                newFacet._textBoxPadding = 4;
+                newFacet._textBoxPaddingX = newFacet._textBoxPaddingY = 4;
                 const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
                 newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
             } else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) {
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index b82d16677..879a63248 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -15,7 +15,7 @@ const LinkDocument = makeInterface(documentSchema);
 @observer
 export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
-    isContentActiveFunc = () => this.isContentActive() ? true : false;
+    isContentActiveFunc = () => this.isContentActive();
     render() {
         if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => this.dataDoc.treeViewOpen = true);
         return <div className={`linkBox-container${this.isContentActive() ? "-interactive" : ""}`}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 424083dac..2e29c0656 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -166,7 +166,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
-                            isContentActive={returnFalse}
+                            isContentActive={emptyFunction}
                             addDocument={returnFalse}
                             removeDocument={returnFalse}
                             addDocTab={returnFalse}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 7ad96bf05..0c631e5f9 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -313,7 +313,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                             PanelHeight={this.formattedPanelHeight}
                             isAnnotationOverlay={true}
                             select={emptyFunction}
-                            isContentActive={returnFalse}
+                            isContentActive={emptyFunction}
                             scaling={returnOne}
                             xPadding={25}
                             yPadding={10}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 4871599b8..f0d7bd2f3 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -48,10 +48,18 @@
     width: 100%;
     z-index: -1; // 0;  // logically this should be 0 (or unset) which would give us transparent brush strokes over videos.  However, this makes Chrome crawl to a halt
     position: absolute;
+    video {
+        width: auto;
+        height: 100%; 
+        display: flex; 
+        margin: auto;
+    }
 }
 
 .videoBox-content, .videoBox-content-interactive, .videoBox-content-fullScreen {
-    height: Auto; 
+    width: 100%;
+    height: 100%; 
+    left: 0px;
 }
 
 .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen  {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 440ccf638..615d595c0 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -307,12 +307,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @computed get content() {
         const field = Cast(this.dataDoc[this.fieldKey], VideoField);
         const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
-        const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
+        const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div key="loading">Loading</div> :
-            <div className="container" key="container" style={{ mixBlendMode: "multiply", pointerEvents: this.props.isContentActive() ? "all" : "none" }}>
-                <div className={`${style}`} style={{ width: "100%", height: "100%", left: "0px" }}>
+            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply" }}>
+                <div className={classname}>
                     <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef}
-                        style={{ height: "100%", width: "auto", display: "flex", margin: "auto" }}
                         onCanPlay={this.videoLoad}
                         controls={VideoBox._nativeControls}
                         onPlay={() => this.Play()}
@@ -457,11 +456,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @computed get youtubeContent() {
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
-        const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+        const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
         const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onPointerLeave={this.updateTimecode}
-            onLoad={this.youtubeIframeLoaded} className={`${style}`} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
+            onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
             src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
     }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index aa53f751d..1170528df 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -123,7 +123,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     @computed get sidebarWidthPercent() { return this._showSidebar ? "20%" : StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
     @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
-    @computed get autoHeight() { return this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight; }
+    @computed get autoHeight() { return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight; }
     @computed get textHeight() { return NumCast(this.rootDoc[this.fieldKey + "-height"]); }
     @computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); }
     @computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); }
@@ -1565,7 +1565,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     setHeight={this.setSidebarHeight}
                     fitContentsToDoc={this.fitToBox}
                     noSidebar={true}
-                    fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : this.SidebarKey} />;
+                    fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
         return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.SelectedTool !== InkTool.None ? "-inking" : "")}
             style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
@@ -1581,10 +1581,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
-        const margins = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const selPad = Math.min(margins, 10);
-        const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
-        const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
+        const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
+        const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
+        const selPad = ((selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0);
+        const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? "-selected" : "";
         const styleFromString = this.styleFromLayoutString(scale);   // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
         return (styleFromString?.height === "0px" ? (null) :
             <div className="formattedTextBox-cont"
@@ -1628,7 +1628,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         onScroll={this.onScroll} onDrop={this.ondrop} >
                         <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
                             style={{
-                                padding: StrCast(this.layoutDoc._textBoxPadding, `${padding}px`),
+                                padding: StrCast(this.layoutDoc._textBoxPadding),
+                                paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
+                                paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
+                                paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
+                                paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
                                 pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? "none" : undefined) : undefined
                             }}
                         />
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 71ddda866..88221732e 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -94,7 +94,7 @@ export class AudioUpload extends React.Component {
                         PanelHeight={() => 400}
                         renderDepth={0}
                         isDocumentActive={returnTrue}
-                        isContentActive={returnFalse}
+                        isContentActive={emptyFunction}
                         focus={emptyFunction}
                         layerProvider={undefined}
                         styleProvider={() => "rgba(0,0,0,0)"}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 652804126..d732a6e2f 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -211,7 +211,7 @@ export class MobileInterface extends React.Component {
                     PanelHeight={this.returnHeight}
                     renderDepth={0}
                     isDocumentActive={returnTrue}
-                    isContentActive={returnFalse}
+                    isContentActive={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={this.whitebackground}
                     layerProvider={undefined}
-- 
cgit v1.2.3-70-g09d2


From 657a4c11b6b1526bacc79fbefe4b461e932c75e7 Mon Sep 17 00:00:00 2001
From: Aubrey Li <Aubrey-Li>
Date: Thu, 2 Dec 2021 15:05:52 -0500
Subject: mapbox cleanup

---
 package-lock.json                                  | 142 +++++++++--
 package.json                                       |   2 +-
 src/client/views/collections/CollectionView.tsx    |   2 -
 .../collections/MapView/CollectionMapView.scss     |  51 ----
 .../collections/MapView/CollectionMapView.tsx      | 272 ---------------------
 5 files changed, 127 insertions(+), 342 deletions(-)
 delete mode 100644 src/client/views/collections/MapView/CollectionMapView.scss
 delete mode 100644 src/client/views/collections/MapView/CollectionMapView.tsx

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

diff --git a/package-lock.json b/package-lock.json
index 288247869..bbc5dc95c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -385,9 +385,9 @@
             }
         },
         "@googlemaps/js-api-loader": {
-            "version": "1.11.4",
-            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.11.4.tgz",
-            "integrity": "sha512-atFTSgEXXsE7mOsOIInUKG4JApu/NdyZR79Ed6jF92MjVQTcD6dz/Xo8djf4cIetnq4WYQmetem0d8/uS4o8oQ==",
+            "version": "1.12.8",
+            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.12.8.tgz",
+            "integrity": "sha512-h1RNVIJkHWL4UrdJo8fwECJwSRg8CSIfTEtPdzvThcji7tAPvVBDVBPzvPKhvl8e8mWEGPhnYOt6kPfSxJL+vQ==",
             "requires": {
                 "fast-deep-equal": "^3.1.3"
             },
@@ -399,6 +399,24 @@
                 }
             }
         },
+        "@googlemaps/markerclusterer": {
+            "version": "1.0.12",
+            "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-1.0.12.tgz",
+            "integrity": "sha512-DSmN2w1ZoI+PBWfNCKYcr+zalP0uaaKIU08+UgebJYZ96X+J6CdUpD+xOVwBV1OJDiIXkneHquOZnT+V6dm+Sg==",
+            "requires": {
+                "@turf/clusters-dbscan": "^6.4.0",
+                "@turf/clusters-kmeans": "^6.4.0",
+                "fast-deep-equal": "^3.1.3",
+                "supercluster": "^7.1.3"
+            },
+            "dependencies": {
+                "fast-deep-equal": {
+                    "version": "3.1.3",
+                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                }
+            }
+        },
         "@hig/flyout": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/@hig/flyout/-/flyout-1.2.1.tgz",
@@ -542,25 +560,27 @@
             }
         },
         "@react-google-maps/api": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.2.0.tgz",
-            "integrity": "sha512-kMmzHyveLdEiLP+WN/H4vYEyaDTJn59mXj5Y0BxjJs5SIMhUtYxJ6EeJCV0aRSN+Gq5hYFLheBoy8Pfqtabq5g==",
-            "requires": {
-                "@googlemaps/js-api-loader": "1.11.4",
-                "@react-google-maps/infobox": "2.2.0",
-                "@react-google-maps/marker-clusterer": "2.2.0",
+            "version": "2.7.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.7.0.tgz",
+            "integrity": "sha512-Fb/l7aR69ObYvzhpt1d1931454ylRkTPKF1C8GfLvXtg19wc36zTev2Afbahd+qAdyKuQyDdf5/SQyMjYBHI/A==",
+            "requires": {
+                "@googlemaps/js-api-loader": "1.12.8",
+                "@googlemaps/markerclusterer": "1.0.12",
+                "@react-google-maps/infobox": "2.6.0",
+                "@react-google-maps/marker-clusterer": "2.6.0",
+                "@types/google.maps": "3.46.1",
                 "invariant": "2.2.4"
             }
         },
         "@react-google-maps/infobox": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.2.0.tgz",
-            "integrity": "sha512-nQbsPdcSTodXaaOgM9CaqyXytBkmollMuaYvERv5zxdoTWT1JvuViSaRbxpc3dYDsEHmp3myLx4ZbDcnOjf7Hg=="
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.6.0.tgz",
+            "integrity": "sha512-2dXVZsuZ2PjelVg+slpUSYPnvoSIySwf4P/wScGijjqu6bpvhyX9AivitsF7zHfbwnZ0gAPX1Q8xtQhKgawkEg=="
         },
         "@react-google-maps/marker-clusterer": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.2.0.tgz",
-            "integrity": "sha512-QaQAfY/FdaSwPmm0JnW9AW+t0ocfUYmuso9WmeIMwTRCqcqNxc4GFe5uT0LDLmsYt/wxAeYoGWVaD8sd/Vquig=="
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.6.0.tgz",
+            "integrity": "sha512-EjeoCM+U/6W9qoGWqurcDbCubqTEDTFET7Jd3XcLHqTLvFAE473x2YbQg98mJXKNgPKcPTNEcTiYYGiIz9tEjA=="
         },
         "@react-three/fiber": {
             "version": "6.0.16",
@@ -606,6 +626,68 @@
                 "unicode-trie": "^0.3.0"
             }
         },
+        "@turf/clone": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz",
+            "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/clusters-dbscan": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.5.0.tgz",
+            "integrity": "sha512-SxZEE4kADU9DqLRiT53QZBBhu8EP9skviSyl+FGj08Y01xfICM/RR9ACUdM0aEQimhpu+ZpRVcUK+2jtiCGrYQ==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "density-clustering": "1.3.0"
+            }
+        },
+        "@turf/clusters-kmeans": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.5.0.tgz",
+            "integrity": "sha512-DwacD5+YO8kwDPKaXwT9DV46tMBVNsbi1IzdajZu1JDSWoN7yc7N9Qt88oi+p30583O0UPVkAK+A10WAQv4mUw==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "skmeans": "0.9.7"
+            }
+        },
+        "@turf/distance": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz",
+            "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/helpers": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
+            "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="
+        },
+        "@turf/invariant": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
+            "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/meta": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
+            "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
         "@types/adm-zip": {
             "version": "0.4.33",
             "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.33.tgz",
@@ -931,6 +1013,11 @@
                 "google-maps-react": "*"
             }
         },
+        "@types/google.maps": {
+            "version": "3.46.1",
+            "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.46.1.tgz",
+            "integrity": "sha512-GAa5ZWYgXG50yLXybb7A824esGm/L0LKHS7qD0qkP0IA/Qp5r922P9tmYcbCkGEf3Zgf7Ukbp7l08/IGIJuQwQ=="
+        },
         "@types/googlemaps": {
             "version": "3.39.8",
             "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.39.8.tgz",
@@ -5217,6 +5304,11 @@
             "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
             "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
         },
+        "density-clustering": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz",
+            "integrity": "sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU="
+        },
         "depcheck": {
             "version": "0.9.2",
             "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.9.2.tgz",
@@ -8773,6 +8865,11 @@
             "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
             "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw=="
         },
+        "kdbush": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz",
+            "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
+        },
         "keycode": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
@@ -16115,6 +16212,11 @@
                 }
             }
         },
+        "skmeans": {
+            "version": "0.9.7",
+            "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
+            "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="
+        },
         "slash": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -16946,6 +17048,14 @@
             "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
             "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
         },
+        "supercluster": {
+            "version": "7.1.4",
+            "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.4.tgz",
+            "integrity": "sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g==",
+            "requires": {
+                "kdbush": "^3.0.0"
+            }
+        },
         "supports-color": {
             "version": "5.5.0",
             "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
diff --git a/package.json b/package.json
index 40504b9d8..810d0a026 100644
--- a/package.json
+++ b/package.json
@@ -125,7 +125,7 @@
         "@hig/theme-context": "^2.1.3",
         "@hig/theme-data": "^2.16.1",
         "@material-ui/core": "^4.11.0",
-        "@react-google-maps/api": "^2.2.0",
+        "@react-google-maps/api": "^2.7.0",
         "@react-three/fiber": "^6.0.16",
         "@types/bezier-js": "^4.1.0",
         "@types/cors": "^2.8.8",
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a7ca57b0b..958ba62f7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -25,7 +25,6 @@ import { CollectionDockingView } from "./CollectionDockingView";
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
 import { CollectionLinearView } from './collectionLinear';
-import CollectionMapView from './MapView/CollectionMapView';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { CollectionPileView } from './CollectionPileView';
@@ -142,7 +141,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
             case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
             case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
-            case CollectionViewType.Map: return <CollectionMapView key="collview" {...props} />;
             case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
             //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
         }
diff --git a/src/client/views/collections/MapView/CollectionMapView.scss b/src/client/views/collections/MapView/CollectionMapView.scss
deleted file mode 100644
index 0dc226c04..000000000
--- a/src/client/views/collections/MapView/CollectionMapView.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-.collectionMapView {
-    width: 100%;
-    height: 100%;
-    overflow: hidden;
-
-    .collectionMapView-contents {
-        width: 100%;
-        height: 100%;
-        overflow: hidden;
-        > div {
-            position: unset !important; // when the sidebar filter flys out, this prevents the map from extending outside the document box
-        }
-
-        .map-wrapper {
-            .searchbox {
-                box-sizing: border-box;
-                border: 1px solid transparent;
-                width: 300px;
-                height: 32px;
-                padding: 0 12px;
-                border-radius: 3px;
-                box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
-                font-size: 14px;
-                outline: none;
-                text-overflow: ellipses;
-                position: absolute;
-                left: 50%;
-                margin-left: -120px;
-                margin-top: 5px;
-            }
-        }
-    }
-}
-
-.loadingWrapper {
-    width: 100%;
-    height: 100%;
-    background-color: pink;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    align-items: center;
-    text-align: center;
-
-    .loadingGif {
-        align-self: center;
-        justify-self: center;
-        width: 50px;
-        height: 50px;
-    }
-}
diff --git a/src/client/views/collections/MapView/CollectionMapView.tsx b/src/client/views/collections/MapView/CollectionMapView.tsx
deleted file mode 100644
index 885f51bfb..000000000
--- a/src/client/views/collections/MapView/CollectionMapView.tsx
+++ /dev/null
@@ -1,272 +0,0 @@
-import { GoogleMap, Marker, InfoWindow, InfoBox, useJsApiLoader, LoadScript, GoogleMapProps, StandaloneSearchBox, Autocomplete } from '@react-google-maps/api';
-import { observable, action, computed, Lambda, runInAction, IReactionDisposer } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../../fields/Doc";
-import { documentSchema } from "../../../../fields/documentSchemas";
-import { Id } from "../../../../fields/FieldSymbols";
-import { makeInterface } from "../../../../fields/Schema";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
-import { LinkManager } from "../../../util/LinkManager";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import "./CollectionMapView.scss";
-import { CollectionSubView } from "../CollectionSubView";
-import React = require("react");
-import requestPromise = require("request-promise");
-import ReactDOM from 'react-dom';
-import { DragManager } from '../../../util/DragManager';
-import { MapMarker } from '../../nodes/MapBox/MapMarker';
-
-
-/**
- * Idea behind storing a marker:
- * 1. on the map api, have a button "add marker" that adds the marker on the map & store the marker as a node in the collection
- * (but don't render the marker in the collection itself)
- * 2. each marker, as a node, has the same feature as all other nodes for linking (the same way one could form a link between a node inside a child collection 
- * and a node outside the child collection)
- * 
- * /util/LinkManager.ts -- link relations
- * 
- */
-
-type MapSchema = makeInterface<[typeof documentSchema]>;
-const MapSchema = makeInterface(documentSchema);
-
-export type Coordinates = {
-    lat: number,
-    lng: number,
-}
-
-export type LocationData = {
-    id: string;
-    pos: Coordinates;
-};
-
-const mapContainerStyle = {
-    height: '100%',
-};
-
-const defaultCenter = {
-    lat: 38.685,
-    lng: -115.234,
-};
-
-const mapOptions = {
-    fullscreenControl: false,
-}
-
-// const drawingManager = new google.maps.drawing.DrawingManager({
-//     drawingControl: true,
-//     drawingControlOptions: {
-//         position: google.maps.ControlPosition.TOP_RIGHT,
-//         drawingModes: [
-//             google.maps.drawing.OverlayType.MARKER,
-//             // currently we are not supporting the following drawing mode on map, a thought for future development
-//             // google.maps.drawing.OverlayType.CIRCLE,
-//             // google.maps.drawing.OverlayType.POLYLINE,
-//         ],
-//     },
-// });
-
-const options = {
-    fields: ["formatted_address", "geometry", "name"], // note: level of details is charged by item per retrieval, not recommended to return all fields
-    strictBounds: false,
-    types: ["establishment"], // type pf places, subject of change according to user need
-} as google.maps.places.AutocompleteOptions;
-
-@observer
-export default class CollectionMapView extends CollectionSubView<MapSchema, Partial<GoogleMapProps>>(MapSchema) {
-    private _dropDisposer?: DragManager.DragDropDisposer;
-    private _disposers: { [name: string]: IReactionDisposer } = {};
-
-
-    @observable private _map = null as unknown as google.maps.Map;
-    @observable private selectedPlace: LocationData | undefined;
-    @observable private markerMap: { [id: string]: google.maps.Marker } = {};
-    @observable private center = defaultCenter;
-    @observable private zoom = 2.5;
-    @observable private infoWindowOpen = false;
-    @observable private bounds = new window.google.maps.LatLngBounds();
-    @observable private inputRef = React.createRef<HTMLInputElement>();
-    @observable private searchMarkers: google.maps.Marker[] = [];
-    @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
-
-    @observable private myPlaces: LocationData[] = [
-        { id: "id1", pos: { lat: 39.09366509575983, lng: -94.58751660204751 } },
-        { id: "id2", pos: { lat: 41.82399, lng: -71.41283 } },
-        { id: "id3", pos: { lat: 47.606214, lng: -122.33207 } },
-    ];
-
-    @action
-    private setSearchBox = (searchBox: any) => {
-        this.searchBox = searchBox;
-    }
-
-    // iterate myPlaces to size, center, and zoom map to contain all markers
-    private fitBounds = (map: google.maps.Map) => {
-        console.log('map bound is:' + this.bounds);
-        this.myPlaces ? this.myPlaces.map(place => {
-            this.bounds.extend(place.pos!);
-        }) : null;
-        map.fitBounds(this.bounds);
-    }
-
-    // store a reference to google map instance; fit map bounds to contain all markers
-    @action
-    private loadHandler = (map: google.maps.Map) => {
-        this._map = map;
-        drawingManager.setMap(map);
-        this.fitBounds(map);
-    }
-
-    @action
-    private markerClickHandler = (e: MouseEvent, place: any) => {
-        // set which place was clicked
-        this.selectedPlace = place;
-
-        console.log(this.selectedPlace);
-
-        // used so clicking a second marker works
-        if (this.infoWindowOpen) {
-            this.infoWindowOpen = false;
-            console.log("closeinfowindow")
-        }
-        this.infoWindowOpen = true;
-        console.log("open infowindow")
-    }
-
-    @action
-    private handleInfoWindowClose = () => {
-        if (this.infoWindowOpen) {
-            this.infoWindowOpen = false;
-        }
-        this.infoWindowOpen = false;
-        this.selectedPlace = undefined;
-    }
-
-    @action
-    private handlePlaceChanged = () => {
-        console.log(this.searchBox);
-        const place = this.searchBox.getPlace();
-
-        if (!place.geometry || !place.geometry.location) {
-            // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
-            window.alert("No details available for input: '" + place.name + "'");
-            return;
-        }
-
-        // zoom in on the location of the search result
-        if (place.geometry.viewport) {
-            console.log(this._map);
-            this._map.fitBounds(place.geometry.viewport);
-        } else {
-            console.log(this._map);
-            this._map.setCenter(place.geometry.location);
-            this._map.setZoom(17);
-        }
-
-        // customize icon => customized icon for the nature of the location selected
-        const icon = {
-            url: place.icon as string,
-            size: new google.maps.Size(71, 71),
-            origin: new google.maps.Point(0, 0),
-            anchor: new google.maps.Point(17, 34),
-            scaledSize: new google.maps.Size(25, 25),
-        };
-
-        // put temporary cutomized marker on searched location
-        this.searchMarkers.forEach((marker) => {
-            marker.setMap(null);
-        });
-        this.searchMarkers = [];
-        this.searchMarkers.push(
-            new window.google.maps.Marker({
-                map: this._map,
-                icon,
-                title: place.name,
-                position: place.geometry.location,
-            })
-        )
-    }
-
-
-    @action
-    private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => {
-        new window.google.maps.Marker({
-            position: location,
-            map: map
-        });
-    }
-
-    @action
-    private markerLoadHandler = (marker: google.maps.Marker, place: LocationData) => {
-        place.id ? this.markerMap[place.id] = marker : null;
-    }
-
-    render() {
-        const { Document, fieldKey, isContentActive: active } = this.props;
-
-        return <div className="collectionMapView" ref={this.createDashEventsTarget}>
-
-            <div className={"collectionMapView-contents"}
-                style={{ pointerEvents: active() ? undefined : "none", overflow: 'hidden' }}
-                onWheel={e => e.stopPropagation()}
-                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
-                {/* <LoadScript
-                    googleMapsApiKey={process.env.GOOGLE_MAPS!}
-                    libraries={['places', 'drawing']}
-                > */}
-                <div className="map-wrapper">
-                    <GoogleMap
-                        mapContainerStyle={mapContainerStyle}
-                        zoom={this.zoom}
-                        center={this.center}
-                        onLoad={map => this.loadHandler(map)}
-                        options={mapOptions}
-                    >
-                        <Autocomplete
-                            onLoad={this.setSearchBox}
-                            onPlaceChanged={this.handlePlaceChanged}>
-                            <input ref={this.inputRef} className="searchbox" type="text" placeholder="Search anywhere:" />
-                        </Autocomplete>
-
-                        {this.myPlaces ? this.myPlaces.map(place =>
-                            <Marker
-                                position={place.pos}
-                                onLoad={marker => this.markerLoadHandler(marker, place)}
-                                onClick={e => this.markerClickHandler(e, place)}
-                                draggable={false}
-                            />
-                        ) : null}
-                        {this.infoWindowOpen && this.selectedPlace && (
-                            <InfoWindow
-                                anchor={this.markerMap[this.selectedPlace.id!]}
-                                onCloseClick={this.handleInfoWindowClose}
-                            >
-                                <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
-                                    <div style={{ fontSize: 16 }}>
-                                        <div>
-                                            <img src="http://placekitten.com/200/300" />
-                                            <hr />
-                                            <form>
-                                                <label>Title: </label><br />
-                                                <input type="text" id="fname" name="fname"></input><br />
-                                                <label>Desription: </label><br />
-                                                <textarea style={{ height: 150 }} id="lname" name="lname" placeholder="Notes, a short description of this location, a brief comment, etc."></textarea>
-                                            </form>
-                                            <hr />
-                                            <div>
-                                                <button>New link+</button>
-                                            </div>
-                                        </div>
-                                    </div>
-                                </div>
-                            </InfoWindow>
-                        )}
-                    </GoogleMap>
-                </div>
-                {/* </LoadScript > */}
-            </div >
-        </div >;
-    }
-
-}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 639eb465369f89dc541dfbeeaed34dfe133a8810 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 7 Dec 2021 11:23:36 -0500
Subject: restored keeping properties pane open when selection changes.   added
 Doc paraemter to fitWidth() prop function.  split MapBoxInfoWindow out of
 MapBox and added an Add Note button.

---
 src/client/util/SelectionManager.ts                |   5 +-
 .../views/collections/CollectionStackingView.tsx   |  12 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 src/client/views/nodes/MapBox/MapBox.scss          |   7 +
 src/client/views/nodes/MapBox/MapBox.tsx           | 194 +++++++--------------
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |  86 +++++++++
 7 files changed, 162 insertions(+), 148 deletions(-)
 create mode 100644 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 6674f684d..b71086561 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -44,11 +44,8 @@ export namespace SelectionManager {
         }
         @action
         DeselectAll(): void {
-            if (CurrentUserUtils.propertiesWidth > 0) {
-                CurrentUserUtils.propertiesWidth = 0;
-            }
             manager.SelectedSchemaDocument = undefined;
-            Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenChildContentsActiveChanged(false));
+            Array.from(manager.SelectedViews.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
             manager.SelectedViews.clear();
         }
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index bffaf86b1..eddb97878 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -231,8 +231,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
             freezeDimensions={this.props.childFreezeDimensions}
-            NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.() || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-            NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.() || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
+            NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+            NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
             dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
             rootSelected={this.rootSelected}
@@ -271,7 +271,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         if (!d) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const maxWidth = this.columnWidth / this.numGroupColumns;
-        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._fitWidth || this.props.childFitWidth?.())) {
+        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d))) {
             return Math.min(d[WidthSym](), maxWidth);
         }
         return maxWidth;
@@ -281,8 +281,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
         const maxHeight = (lim => lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim)(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.()) ? d[WidthSym]() : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.()) ? d[HeightSym]() : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
@@ -291,7 +291,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                 docWid * nh / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = (childLayoutDoc._fitWidth || this.props.childFitWidth?.()) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = (childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 510ec6ba9..f66bac20e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -70,7 +70,7 @@ export interface CollectionViewProps extends FieldViewProps {
     children?: never | (() => JSX.Element[]) | React.ReactNode;
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
-    childFitWidth?: () => boolean;
+    childFitWidth?: (child: Doc) => boolean;
     childShowTitle?: () => string;
     childOpacity?: () => number;
     childContextMenuItems?: () => { script: ScriptField, label: string }[];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 138bad9b8..9fc4b7890 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -118,7 +118,7 @@ export interface DocumentViewSharedProps {
     layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean);
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
-    fitWidth?: () => boolean;
+    fitWidth?: (doc: Doc) => boolean;
     docFilters: () => string[];
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
@@ -1140,7 +1140,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     get ComponentView() { return this.docView?._componentView; }
     get allLinks() { return this.docView?.allLinks || []; }
     get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; }
-    get fitWidth() { return this.props.fitWidth?.() || this.layoutDoc.fitWidth; }
+    get fitWidth() { return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; }
 
     @computed get docViewPath() { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
     @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index f275bed54..1714fcaa9 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -8,6 +8,13 @@
             position: unset !important; // when the sidebar filter flys out, this prevents the map from extending outside the document box
         }
 
+        .mapBox-infoWindow {
+            background-color: white;
+            opacity: 0.75;
+            padding: 12; 
+            font-size: 17;
+        }
+
         .mapBox-overlayButton-sidebar {
             background: #121721;
             height: 25px;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 7875060e2..8b23405d8 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,5 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Autocomplete, GoogleMap, GoogleMapProps, InfoWindow, LoadScript, Marker } from '@react-google-maps/api';
+import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import * as dotenv from 'dotenv';
 import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from "react";
@@ -10,16 +11,12 @@ import { InkTool } from '../../../../fields/InkField';
 import { makeInterface } from '../../../../fields/Schema';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { TraceMobx } from '../../../../fields/util';
-import { emptyFunction, OmitKeys, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DragManager } from '../../../util/DragManager';
-import { SelectionManager } from '../../../util/SelectionManager';
 import { SnappingManager } from '../../../util/SnappingManager';
-import { undoBatch } from '../../../util/UndoManager';
 import { CollectionFreeFormView, MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
-import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { CollectionViewType } from '../../collections/CollectionView';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
@@ -28,8 +25,8 @@ import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '../FieldView';
-import * as dotenv from 'dotenv';
 import "./MapBox.scss";
+import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 
 /**
  * MapBox architecture:
@@ -146,7 +143,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
-        console.log('map bound is:' + this.bounds);
         this.allMapMarkers.map(place => {
             this.bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) });
         });
@@ -232,7 +228,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //drawingManager.setMap(map);
         if (navigator.geolocation) {
             navigator.geolocation.getCurrentPosition(
-                (position: GeolocationPosition) => {
+                (position: Position) => {
                     const pos = {
                         lat: position.coords.latitude,
                         lng: position.coords.longitude,
@@ -243,17 +239,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         } else {
             alert("Your geolocation is not supported by browser.")
         };
-        console.log("all sidebar docs during map loading is:")
-        console.log(this.allSidebarDocs);
         this.fitBounds(map);
 
 
         // listener to addmarker event
-        this._map.addListener('click', (e) => {
-            console.log("add marker map status:" + this.toggleAddMarker);
+        this._map.addListener('click', (e: MouseEvent) => {
             if (this.toggleAddMarker == true) {
-                this.placeMarker(e.latLng, map)
-                console.log(this.allMapMarkers)
+                this.placeMarker((e as any).latLng, map);
             }
         })
     }
@@ -266,9 +258,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
         place[Id] ? this.markerMap[place[Id]] = marker : null;
-
-        console.log("the following is a markerMap from id to Marker:")
-        console.log(this.markerMap);
     }
 
     /**
@@ -277,15 +266,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @param place 
      */
     @action
-    private markerClickHandler = (e: MouseEvent, place: Doc) => {
+    private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
         // set which place was clicked
         this.selectedPlace = place;
-
-        console.log("you have selected this location:");
-        console.log(this.selectedPlace);
-
         place.infoWindowOpen = true;
-        console.log("open infowindow")
     }
 
     /**
@@ -406,15 +390,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         )
     }
 
-
-    @action
-    private handleInfoWindowClose = (place: Doc) => {
-        if (place.infoWindowOpen) {
-            place.infoWindowOpen = false;
-        }
-        place.infoWindowOpen = false;
-    }
-
     /**
      * Handles toggle of sidebar on click the little comment button
      */
@@ -476,19 +451,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.addDocument(doc, annotationKey);
     }
 
-    pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
-
     @computed get annotationLayer() {
-        TraceMobx();
-        const pe = this.pointerEvents();
+        const pe = this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" :
+            SnappingManager.GetIsDragging() ? undefined : "none"
         return <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
             {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={pe} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)}
+                <Annotation key={`${anno[Id]}-annotation`} {...this.props}
+                    fieldKey={this.annotationKey} pointerEvents={pe} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />)}
         </div>;
-
     }
 
-
     getAnchor = () => {
         const anchor =
             AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
@@ -496,43 +468,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return anchor;
     }
 
-    infoWidth = () => this.props.PanelWidth() / 5;
-    infoHeight = () => this.props.PanelWidth() / 5;
-
-    // Collection stacking view for documents in the infowindow of a map marker
-    private renderChildDocs = (selectedDoc: Doc) => {
-        return <div style={{ width: this.infoWidth(), height: this.infoHeight() }}>
-            <CollectionStackingView {
-                ...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                Document={selectedDoc}
-                DataDoc={undefined}
-                NativeWidth={returnZero}
-                NativeHeight={returnZero}
-                PanelHeight={this.infoHeight}
-                PanelWidth={this.infoWidth}
-                docFilters={returnEmptyFilter}
-                setHeight={emptyFunction}
-                isAnnotationOverlay={false}
-                select={emptyFunction}
-                scaling={returnOne}
-                isContentActive={returnTrue}
-                chromeHidden={true}
-                rootSelected={returnFalse}
-                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-                childHideDecorationTitle={returnTrue}
-                // childDocumentsActive={returnFalse}
-                removeDocument={this.removeDocument}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}
-                CollectionView={undefined}
-                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                renderDepth={this.props.renderDepth + 1}
-                viewType={CollectionViewType.Stacking}
-                fieldKey={"data"}
-                pointerEvents={"all"}
-            /></div>;
-    }
-
     /**
      * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
      * @returns 
@@ -543,41 +478,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 key={place[Id]}
                 position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }}
                 onLoad={marker => this.markerLoadHandler(marker, place)}
-                onClick={e => this.markerClickHandler(e, place)}
+                onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)}
             />
         ))
     }
 
-    /**
-     * Renders infowindow corresponding to a map marker document
-     * @param place 
-     * @returns 
-     */
-    private renderInfoWindow = (place: Doc) => {
-
-        return place.infoWindowOpen && (
-            <InfoWindow
-                key={place[Id]}
-                anchor={this.markerMap[place[Id]]}
-                onCloseClick={() => this.handleInfoWindowClose(place)}
-            >
-                <div className="mapbox-infowindow" style={{ backgroundColor: 'white', opacity: 0.75, padding: 12, fontSize: 17 }}>
-                    {this.renderChildDocs(place)}
-                    <hr />
-                    <div>
-                        <button>New link+</button>
-                    </div>
-                </div>
-            </InfoWindow>
-        )
-    }
-
     // TODO: auto center on select a document in the sidebar
     private handleMapCenter = (map: google.maps.Map) => {
-        console.log("print the selected views in selectionManager:")
-        if (SelectionManager.Views().lastElement()) {
-            console.log(SelectionManager.Views().lastElement());
-        }
+        // console.log("print the selected views in selectionManager:")
+        // if (SelectionManager.Views().lastElement()) {
+        //     console.log(SelectionManager.Views().lastElement());
+        // }
     }
 
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
@@ -585,35 +496,41 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
-
+    infoWidth = () => this.props.PanelWidth() / 5;
+    infoHeight = () => this.props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
 
     render() {
-        const renderAnnotations = (docFilters?: () => string[]) =>
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                renderDepth={this.props.renderDepth + 1}
-                isAnnotationOverlay={true}
-                fieldKey={this.annotationKey}
-                CollectionView={undefined}
-                setPreviewCursor={this.setPreviewCursor}
-                PanelWidth={this.panelWidth}
-                PanelHeight={this.panelHeight}
-                ScreenToLocalTransform={this.scrollXf}
-                scaling={returnOne}
-                dropAction={"alias"}
-                docFilters={docFilters || this.props.docFilters}
-                dontRenderDocuments={docFilters ? false : true}
-                select={emptyFunction}
-                ContentScaling={returnOne}
-                bringToFront={emptyFunction}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                removeDocument={this.removeDocument}
-                moveDocument={this.moveDocument}
-                addDocument={this.sidebarAddDocument}
-                childPointerEvents={true}
-                pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+        const renderAnnotations = (docFilters?: () => string[]) => (null);
+        // bcz: commmented this out.  Otherwise, any documents that are rendered with an InfoWindow of a marker
+        //      will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
+        //      freeform documents on the map does anything anyway, so getting rid of it for now.  Also, since documents
+        //      are rendered twice, adding a new note to the InfoWindow loses focus immediately on creation since it gets
+        //      shifted to the non-visible view of the document in this freeform view.
+        // <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+        //     renderDepth={this.props.renderDepth + 1}
+        //     isAnnotationOverlay={true}
+        //     fieldKey={this.annotationKey}
+        //     CollectionView={undefined}
+        //     setPreviewCursor={this.setPreviewCursor}
+        //     PanelWidth={this.panelWidth}
+        //     PanelHeight={this.panelHeight}
+        //     ScreenToLocalTransform={this.scrollXf}
+        //     scaling={returnOne}
+        //     dropAction={"alias"}
+        //     docFilters={docFilters || this.props.docFilters}
+        //     dontRenderDocuments={docFilters ? false : true}
+        //     select={emptyFunction}
+        //     ContentScaling={returnOne}
+        //     bringToFront={emptyFunction}
+        //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+        //     removeDocument={this.removeDocument}
+        //     moveDocument={this.moveDocument}
+        //     addDocument={this.sidebarAddDocument}
+        //     childPointerEvents={true}
+        //     pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return <div className="mapBox" ref={this._ref}>
-            {console.log(apiKey)}
+            {/*console.log(apiKey)*/}
             {/* <LoadScript
                 googleMapsApiKey={apiKey!}
                 libraries={['places', 'drawing']}
@@ -642,9 +559,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     </Autocomplete>
 
                     {this.renderMarkers()}
-                    {this.allMapMarkers.map(place => (
-                        this.renderInfoWindow(place)
-                    ))}
+                    {this.allMapMarkers.filter(marker => marker.infoWindowOpen).map(marker => <MapBoxInfoWindow key={marker[Id]}
+                        {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                        place={marker}
+                        markerMap={this.markerMap}
+                        PanelWidth={this.infoWidth}
+                        PanelHeight={this.infoHeight}
+                        moveDocument={this.moveDocument}
+                        isAnyChildContentActive={this.isAnyChildContentActive}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                    />)}
                     {this.handleMapCenter(this._map)}
                 </GoogleMap>
                 {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
@@ -688,4 +612,4 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </div>
         </div>;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
new file mode 100644
index 000000000..e6f98e5cf
--- /dev/null
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -0,0 +1,86 @@
+import { InfoWindow } from '@react-google-maps/api';
+import { action, computed } from 'mobx';
+import { observer } from "mobx-react";
+import * as React from "react";
+import { Doc } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { emptyFunction, OmitKeys, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { CollectionStackingView } from '../../collections/CollectionStackingView';
+import { CollectionViewType } from '../../collections/CollectionView';
+import { ViewBoxAnnotatableProps } from '../../DocComponent';
+import { FieldViewProps } from '../FieldView';
+import { FormattedTextBox } from '../formattedText/FormattedTextBox';
+import "./MapBox.scss";
+
+
+interface MapBoxInfoWindowProps {
+    place: Doc;
+    renderDepth: number;
+    markerMap: { [id: string]: google.maps.Marker };
+    isAnyChildContentActive: () => boolean;
+}
+@observer
+export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps>{
+
+    @action
+    private handleInfoWindowClose = () => {
+        if (this.props.place.infoWindowOpen) {
+            this.props.place.infoWindowOpen = false;
+        }
+        this.props.place.infoWindowOpen = false;
+    }
+
+    addNoteClick = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
+            const newBox = Docs.Create.TextDocument("Note", { _autoHeight: true });
+            FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+            Doc.AddDocToList(this.props.place, "data", newBox);
+            e.stopPropagation();
+            e.preventDefault();
+        });
+    }
+
+    // Collection stacking view for documents in the infowindow of a map marker
+    @computed get renderChildDocs() {
+        return;
+    }
+    render() {
+        return <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose} >
+            <div className="mapbox-infowindow">
+                <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+                    <CollectionStackingView
+                        {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                        Document={this.props.place}
+                        DataDoc={undefined}
+                        fieldKey="data"
+                        CollectionView={undefined}
+                        NativeWidth={returnZero}
+                        NativeHeight={returnZero}
+                        docFilters={returnEmptyFilter}
+                        setHeight={emptyFunction}
+                        isAnnotationOverlay={false}
+                        select={emptyFunction}
+                        scaling={returnOne}
+                        isContentActive={returnTrue}
+                        chromeHidden={true}
+                        rootSelected={returnFalse}
+                        childHideDecorationTitle={returnTrue}
+                        childFitWidth={doc => doc.type === DocumentType.RTF}
+                        // childDocumentsActive={returnFalse}
+                        removeDocument={(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean)}
+                        addDocument={(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean)}
+                        renderDepth={this.props.renderDepth + 1}
+                        viewType={CollectionViewType.Stacking}
+                        pointerEvents="all"
+                    />
+                </div>
+                <hr />
+                <div onPointerDown={this.addNoteClick} onClick={e => { e.stopPropagation(); e.preventDefault(); }} >
+                    Add Note
+                </div>
+            </div>
+        </InfoWindow>;
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From f8ce34c8ed42646691d1e392effe79bc27daf810 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Dec 2021 22:48:46 -0500
Subject: Added auto scroll to bottom of map info view stack when note is
 added.  hide resize handles for map view info window stack documents.

---
 src/client/views/collections/CollectionStackingView.tsx | 5 +++++
 src/client/views/collections/CollectionView.tsx         | 1 +
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx      | 6 ++++++
 3 files changed, 12 insertions(+)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index eddb97878..cdc680a08 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -178,6 +178,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         return this.props.addDocTab(doc, where);
     }
 
+    scrollToBottom = () => {
+        smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
+    }
+
     focusDocument = (doc: Doc, options?: DocFocusOptions) => {
         Doc.BrushDoc(doc);
 
@@ -244,6 +248,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             focus={this.focusDocument}
             docFilters={this.childDocFilters}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideTitle={this.props.childHideTitle?.()}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f66bac20e..681a15e3d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -76,6 +76,7 @@ export interface CollectionViewProps extends FieldViewProps {
     childContextMenuItems?: () => { script: ScriptField, label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
     childHideDecorationTitle?: () => boolean;
+    childHideResizeHandles?: () => boolean;
     childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
     childLayoutString?: string;
     childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index e6f98e5cf..0d5fedb7b 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -37,11 +37,15 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
             const newBox = Docs.Create.TextDocument("Note", { _autoHeight: true });
             FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
             Doc.AddDocToList(this.props.place, "data", newBox);
+            this._stack?.scrollToBottom();
             e.stopPropagation();
             e.preventDefault();
         });
     }
 
+
+    _stack: CollectionStackingView | null | undefined;
+
     // Collection stacking view for documents in the infowindow of a map marker
     @computed get renderChildDocs() {
         return;
@@ -51,6 +55,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
             <div className="mapbox-infowindow">
                 <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
                     <CollectionStackingView
+                        ref={r => this._stack = r}
                         {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                         Document={this.props.place}
                         DataDoc={undefined}
@@ -66,6 +71,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                         isContentActive={returnTrue}
                         chromeHidden={true}
                         rootSelected={returnFalse}
+                        childHideResizeHandles={returnTrue}
                         childHideDecorationTitle={returnTrue}
                         childFitWidth={doc => doc.type === DocumentType.RTF}
                         // childDocumentsActive={returnFalse}
-- 
cgit v1.2.3-70-g09d2

