From 6a79d66fd3efb478369981661817f51ec9a7f2c7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 4 Sep 2023 13:56:20 -0400
Subject: fixed video import after youtube changes to urls

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

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index eb4685834..54a60271a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -298,7 +298,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                         let source = split;
                         if (split.startsWith('data:image') && split.includes('base64')) {
                             const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
-                            if (accessPaths.agnostic.client.indexOf("dashblobstore") === -1) {
+                            if (accessPaths.agnostic.client.indexOf('dashblobstore') === -1) {
                                 source = Utils.prepend(accessPaths.agnostic.client);
                             } else {
                                 source = accessPaths.agnostic.client;
@@ -347,10 +347,10 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
 
             if (uriList || text) {
-                if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed')) {
+                if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed') || text.includes('www.youtube.com/shorts')) {
                     const batch = UndoManager.StartBatch('youtube upload');
                     const generatedDocuments: Doc[] = [];
-                    this.slowLoadDocuments((uriList || text).split('v=')[1].split('&')[0], options, generatedDocuments, text, completed, addDocument).then(batch.end);
+                    this.slowLoadDocuments((uriList || text).split('v=').lastElement().split('&')[0].split('shorts/').lastElement(), options, generatedDocuments, text, completed, addDocument).then(batch.end);
 
                     return;
                 }
-- 
cgit v1.2.3-70-g09d2


From 34f3bab0f4d55b2f55f38e451bfd024994ecff42 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 20 Sep 2023 13:20:53 -0400
Subject: added carousel3D to novice and fixed it's button positions and its
 focus so that it works with trails. added dragging documents within single
 collection for rearranging order of docs in a tab collection.  fixed freeform
 views set to fitWidth to show decorations properly. turned off scaling of
 tree views.  updated lightbox buttons.  fixed tooltips on properties toggles.

---
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/LightboxView.scss                 |  11 +--
 src/client/views/LightboxView.tsx                  | 106 +++++++++++++--------
 src/client/views/PropertiesButtons.tsx             |  12 ++-
 .../views/collections/CollectionCarousel3DView.tsx |  17 +++-
 src/client/views/collections/CollectionSubView.tsx |   5 +-
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   9 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   2 +-
 11 files changed, 109 insertions(+), 69 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 259c028cc..2ea5972ee 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -492,8 +492,8 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            dropAction: "same", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
-            layout_headerButton: newDashboardButton, childDragAction: "none",
+            dropAction: "inSame", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
+            layout_headerButton: newDashboardButton, childDragAction: "inSame",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ed22e70bd..f86f9a3e5 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -14,7 +14,7 @@ import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
 
 /**
  * Initialize drag
diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss
index f86a1d211..9a9b8a437 100644
--- a/src/client/views/LightboxView.scss
+++ b/src/client/views/LightboxView.scss
@@ -5,7 +5,6 @@
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -17,11 +16,10 @@
 .lightboxView-tabBtn {
     margin: auto;
     position: absolute;
-    right: 38;
+    right: 45;
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -33,11 +31,10 @@
 .lightboxView-penBtn {
     margin: auto;
     position: absolute;
-    right: 70;
+    right: 80;
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -49,11 +46,10 @@
 .lightboxView-exploreBtn {
     margin: auto;
     position: absolute;
-    right: 100;
+    right: 115;
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -68,7 +64,6 @@
     left: 0;
     width: 100%;
     height: 100%;
-    background: #000000bb;
     z-index: 1000;
     .lightboxView-contents {
         position: absolute;
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 8f081b321..93eaec959 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,15 +1,17 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { InkTool } from '../../fields/InkField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
 import { Transform } from '../util/Transform';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
@@ -118,7 +120,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
                     bottom,
                 }}>
-                <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
+                <div className="lightboxView-navBtn" title={color} style={{ top, color: SettingsManager.userColor, background: undefined }} onClick={click}>
                     <div style={{ height: 10 }}>{color}</div>
                     <FontAwesomeIcon icon={icon as any} size="3x" />
                 </div>
@@ -229,6 +231,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     downx = e.clientX;
                     downy = e.clientY;
                 }}
+                style={{ background: SettingsManager.userBackgroundColor }}
                 onClick={e => {
                     if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
                         LightboxView.SetLightboxDoc(undefined);
@@ -242,6 +245,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         width: this.lightboxWidth(),
                         height: this.lightboxHeight(),
                         clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
+                        background: SettingsManager.userBackgroundColor,
+                        color: SettingsManager.userColor,
                     }}>
                     {/* <CollectionMenu /> TODO:glr This is where it would go*/}
 
@@ -299,47 +304,68 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     this.future()?.length.toString()
                 )}
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
-                <div
-                    className="lightboxView-navBtn"
-                    title="toggle fit width"
-                    onClick={e => {
-                        e.stopPropagation();
-                        LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
-                    }}>
-                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
+                <div className="lightboxView-navBtn">
+                    <Toggle
+                        tooltip="toggle reading view"
+                        color={SettingsManager.userColor}
+                        background={BoolCast(LightboxView.LightboxDoc!._layout_fitWidth) ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.TERT}
+                        toggleStatus={BoolCast(LightboxView.LightboxDoc!._layout_fitWidth)}
+                        onClick={e => {
+                            e.stopPropagation();
+                            LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
+                        }}
+                        icon={<FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'book-open' : 'book'} size="sm" />}
+                    />
                 </div>
-                <div
-                    className="lightboxView-tabBtn"
-                    title="open in tab"
-                    onClick={e => {
-                        const lightdoc = LightboxView._docTarget || LightboxView._doc!;
-                        e.stopPropagation();
-                        Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
-                        CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
-                        SelectionManager.DeselectAll();
-                        LightboxView.SetLightboxDoc(undefined);
-                    }}>
-                    <FontAwesomeIcon icon={'file-download'} size="2x" />
+                <div className="lightboxView-tabBtn">
+                    <Toggle
+                        tooltip="open document in a tab"
+                        color={SettingsManager.userColor}
+                        background={SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.TERT}
+                        icon={<FontAwesomeIcon icon="file-download" size="sm" />}
+                        onClick={e => {
+                            const lightdoc = LightboxView._docTarget || LightboxView._doc!;
+                            e.stopPropagation();
+                            Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
+                            CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
+                            SelectionManager.DeselectAll();
+                            LightboxView.SetLightboxDoc(undefined);
+                        }}
+                    />
                 </div>
-                <div
-                    className="lightboxView-penBtn"
-                    title="toggle pen annotation"
-                    style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
-                    onClick={e => {
-                        e.stopPropagation();
-                        Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
-                    }}>
-                    <FontAwesomeIcon color={Doc.ActiveTool === InkTool.Pen ? 'black' : 'white'} icon={'pen'} size="2x" />
+                <div className="lightboxView-penBtn">
+                    <Toggle
+                        tooltip="toggle pen annotation"
+                        color={SettingsManager.userColor}
+                        background={Doc.ActiveTool === InkTool.Pen ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        toggleStatus={Doc.ActiveTool === InkTool.Pen}
+                        type={Type.TERT}
+                        icon={<FontAwesomeIcon icon="pen" size="sm" />}
+                        onClick={e => {
+                            e.stopPropagation();
+                            Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+                        }}
+                    />
                 </div>
-                <div
-                    className="lightboxView-exploreBtn"
-                    title="toggle explore mode to navigate among documents only"
-                    style={{ background: DocumentView.ExploreMode ? 'white' : undefined }}
-                    onClick={action(e => {
-                        e.stopPropagation();
-                        DocumentView.ExploreMode = !DocumentView.ExploreMode;
-                    })}>
-                    <FontAwesomeIcon color={DocumentView.ExploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" />
+                <div className="lightboxView-exploreBtn">
+                    <Toggle
+                        tooltip="toggle explore mode to navigate among documents only"
+                        color={SettingsManager.userColor}
+                        background={DocumentView.ExploreMode ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.TERT}
+                        toggleStatus={DocumentView.ExploreMode}
+                        icon={<FontAwesomeIcon icon="globe-americas" size="sm" />}
+                        onClick={action(e => {
+                            e.stopPropagation();
+                            DocumentView.ExploreMode = !DocumentView.ExploreMode;
+                        })}
+                    />
                 </div>
             </div>
         );
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 40d42a4de..d1561fd67 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -57,6 +57,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return !targetDoc ? null : (
             <Toggle
                 toggleStatus={BoolCast(targetDoc[property])}
+                tooltip={tooltip(BoolCast(targetDoc[property]))}
                 text={label(targetDoc?.[property])}
                 color={SettingsManager.userColor}
                 icon={icon(targetDoc?.[property] as any)}
@@ -165,9 +166,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get forceActiveButton() {
         //select text
         return this.propertyToggleBtn(
-            on => (on ? 'INACTIVE INTERACTION' : 'ACTIVE INTERACTION'),
+            on => (on ? 'SELECT TO INTERACT' : 'ALWAYS INTERACTIVE'),
             '_forceActive',
-            on => `${on ? 'Select to activate' : 'Contents always active'} `,
+            on => `${on ? 'Document must be selected to interact with its contents' : 'Contents always active (respond to click/drag events)'} `,
             on => <MdTouchApp /> // 'eye'
         );
     }
@@ -210,9 +211,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     @computed get layout_fitWidthButton() {
         return this.propertyToggleBtn(
-            on => (on ? 'RESTRICT WIDTH' : 'FIT WIDTH'), //'Fit\xA0Width',
+            on => (on ? 'SCALED VIEW' : 'READING VIEW'), //'Fit\xA0Width',
             '_layout_fitWidth',
-            on => `${on ? "Don't" : 'Do'} fit content to width of container`,
+            on =>
+                on
+                    ? "Scale document so it's width and height fit container (no effect when document is viewed on freeform canvas)"
+                    : "Scale document so it's width fits container and its height expands/contracts to fit available space  (no effect when document is viewed on freeform canvas)",
             on => (on ? <AiOutlineColumnWidth /> : <RxWidth />) // 'arrows-alt-h'
         );
     }
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index d94e552b4..cd59a91a1 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -2,14 +2,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { returnFalse, returnZero, Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
 import { StyleProp } from '../StyleProvider';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView } from './CollectionSubView';
@@ -46,6 +47,15 @@ export class CollectionCarousel3DView extends CollectionSubView() {
             .translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this.props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
             .scale(1 / this.centerScale);
 
+    focus = (anchor: Doc, options: DocFocusOptions) => {
+        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
+        options.didMove = true;
+        const target = DocCast(anchor.annotationOn) ?? anchor;
+        const index = docs.indexOf(target);
+        index !== -1 && (this.layoutDoc._carousel_index = index);
+        return undefined;
+    };
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
         const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
@@ -61,6 +71,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
                     LayoutTemplateString={this.props.childLayoutString}
                     Document={childPair.layout}
                     DataDoc={childPair.data}
+                    focus={this.focus}
                     ScreenToLocalTransform={this.childScreenToLocal}
                     isContentActive={this.isChildContentActive}
                     isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 54a60271a..26272d2ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -210,11 +210,12 @@ export function CollectionSubView<X>(moreProps?: X) {
                 const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
                 const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
                 if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
-                if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
+                if ((!dropAction || dropAction === 'inSame' || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
                     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 = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+                        const canAdd =
+                            (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
                         const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
                         added = canAdd || moved ? moved : undefined;
                     } else {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index eed04b3ee..9e5ac77d9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -149,7 +149,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             if (isAlreadyInTree() !== sameTree) {
                 console.log('WHAAAT');
             }
-            dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+            dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree && dragData.dropAction !== 'inSame' ? 'same' : dragData.dropAction;
             e.stopPropagation();
         }
     };
@@ -438,7 +438,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     render() {
         TraceMobx();
 
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
+        const scale = this.props.NativeDimScaling?.() || 1;
         return (
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
                 {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index cd526c77e..f89aa065b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -433,9 +433,9 @@ export class TreeView extends React.Component<TreeViewProps> {
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
         };
         const addDoc = inside ? localAdd : parentAddDoc;
-        const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
+        const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
         const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
-        if (canAdd) {
+        if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this.props.parentTreeView?.doc))) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
             const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
@@ -598,7 +598,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
+                !dataIsComputed && added && Doc.SetContainer(doc, this.doc);
 
                 return added;
             };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8a812c671..9df96fabc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -33,7 +33,6 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss';
 import { Timeline } from '../../animationtimeline/Timeline';
 import { ContextMenu } from '../../ContextMenu';
-import { DocumentDecorations } from '../../DocumentDecorations';
 import { GestureOverlay } from '../../GestureOverlay';
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
 import { LightboxView } from '../../LightboxView';
@@ -187,7 +186,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     }
     @computed get cachedGetTransform(): Transform {
-        return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+        return this.getContainerTransform()
+            .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim())
+            .translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
+            .transform(this.cachedGetLocalTransform);
     }
 
     public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
@@ -862,7 +864,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
                 if (!e.shiftKey) {
                     this._eraserLock++;
-                    this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
+                    const segments = this.segmentInkStroke(intersect.inkView, intersect.t);
+                    segments.forEach(segment =>
                         this.forceStrokeGesture(
                             e,
                             GestureUtils.Gestures.Stroke,
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 394108be4..14a3d16ef 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -226,7 +226,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             } else {
                 return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
             }
-            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
         } else {
             text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
             // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-- 
cgit v1.2.3-70-g09d2


From c5c511fe51e858ca52615aaedb193dc947b90932 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 29 Sep 2023 10:37:14 -0400
Subject: fixed autoHeight text scrolling in its own tab and elsewhere.  update
 ChildDrag to GroupChildDrag and simplified documentView code.  fixed
 MapAnchorMenu to allow linking by dragging text icon.

---
 src/client/util/DragManager.ts                     | 19 +++++-
 src/client/views/InkingStroke.tsx                  |  2 -
 src/client/views/MarqueeAnnotator.tsx              |  2 +-
 .../views/collections/CollectionCarousel3DView.tsx |  1 -
 .../views/collections/CollectionCarouselView.tsx   |  3 +-
 src/client/views/collections/CollectionSubView.tsx |  1 -
 .../collectionFreeForm/CollectionFreeFormView.scss |  9 +++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 75 ++++++----------------
 .../collectionSchema/SchemaTableCell.tsx           |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 20 ++----
 src/client/views/nodes/FieldView.tsx               |  1 -
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx    | 62 ++++++++----------
 src/client/views/nodes/MapBox/MapBox.tsx           | 36 ++++++++++-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 10 +--
 src/client/views/pdf/AnchorMenu.tsx                |  3 +-
 15 files changed, 121 insertions(+), 125 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index f86f9a3e5..4f30e92ce 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,7 +4,7 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { ScriptCast, StrCast } from '../../fields/Types';
+import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
 import { emptyFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import * as globalCssVariables from '../views/global/globalCssVariables.scss';
@@ -108,9 +108,11 @@ export namespace DragManager {
         constructor(dragDoc: Doc[], dropAction?: dropActionType) {
             this.draggedDocuments = dragDoc;
             this.droppedDocuments = [];
+            this.draggedViews = [];
             this.offset = [0, 0];
             this.dropAction = dropAction;
         }
+        draggedViews: DocumentView[];
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
         treeViewDoc?: Doc;
@@ -189,6 +191,13 @@ export namespace DragManager {
 
     // drag a document and drop it (or make an embed/copy on drop)
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) {
+        dragData.draggedViews.forEach(
+            action(view => {
+                const ffview = view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+                ffview && (ffview.GroupChildDrag = BoolCast(ffview.Document._isGroup));
+                ffview?.setupDragLines(false);
+            })
+        );
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField());
             dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
@@ -196,6 +205,14 @@ export namespace DragManager {
         };
         const finishDrag = async (e: DragCompleteEvent) => {
             const docDragData = e.docDragData;
+            setTimeout(() =>
+                dragData.draggedViews.forEach(
+                    action(view => {
+                        const ffview = view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+                        ffview && (ffview.GroupChildDrag = false);
+                    })
+                )
+            );
             onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f63a27426..9b52f5870 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -446,7 +446,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 mask,
                 highlightColor
             );
-        const fsize = +StrCast(this.props.Document._text_fontSize, '12px').replace('px', '');
         // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size.
         // this attempts to figure out the lineHeight ratio by inquiring the body's lineHeight and dividing by the fontsize which should yield 1.428571429
         // see: https://bibwild.wordpress.com/2019/06/10/bootstrap-3-to-4-changes-in-how-font-size-line-height-and-spacing-is-done-or-what-happened-to-line-height-computed/
@@ -493,7 +492,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                             yPadding={10}
                             xPadding={10}
                             fieldKey="text"
-                            fontSize={fsize}
                             dontRegisterView={true}
                             noSidebar={true}
                             dontScale={true}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index a958607de..0987b0867 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -211,7 +211,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DocData]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
-        return (annotationDoc as Doc) ?? undefined;
+        return annotationDoc as Doc;
     };
 
     public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 4bc72772a..a8d080953 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -28,7 +28,6 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     }
 
     protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
-        //used for stacking and masonry view
         this._dropDisposer?.();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 040a584b8..33a92d406 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -21,7 +21,6 @@ export class CollectionCarouselView extends CollectionSubView() {
     }
 
     protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
-        //used for stacking and masonry view
         this._dropDisposer?.();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
@@ -86,7 +85,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                         marginLeft: this.marginX,
                         width: `calc(100% - ${this.marginX * 2}px)`,
                     }}>
-                    <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
+                    <FormattedTextBox key={index} {...captionProps} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
                 </div>
             </>
         );
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 26272d2ee..158f9d8ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -31,7 +31,6 @@ export function CollectionSubView<X>(moreProps?: X) {
         @observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
         @observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
         protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
-            //used for stacking and masonry view
             this.dropDisposer?.();
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index e4ae251c8..c90fdf013 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -11,6 +11,15 @@
     touch-action: none;
     border-radius: inherit;
 }
+.collectionFreeForm-groupDropper {
+    width: 10000;
+    height: 10000;
+    left: -5000;
+    top: -5000;
+    position: absolute;
+    background: transparent;
+    pointer-events: all;
+}
 
 .collectionfreeformview-grid {
     transform-origin: top left;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a64ccbb2c..676e96714 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -124,7 +124,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef: HTMLDivElement | null = null;
     @observable _marqueeViewRef = React.createRef<MarqueeView>();
-    @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
+    @observable GroupChildDrag: boolean = false; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
     @observable _brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; // highlighted region of freeform canvas used by presentations to indicate a region
 
     constructor(props: any) {
@@ -342,7 +342,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
-        if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false;
         if (!super.onInternalDrop(e, de)) return false;
         const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
@@ -1763,17 +1762,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @undoBatch
     toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight);
 
+    ///
+    /// resetView restores a freeform collection to unit scale and centered at (0,0)  UNLESS
+    ///  the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
+    ///
+    @undoBatch
+    resetView = () => {
+        if (!this.props.Document._isGroup) {
+            this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0;
+            this.props.Document[this.scaleFieldKey] = 1;
+        }
+    };
+
     onContextMenu = (e: React.MouseEvent) => {
         if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return;
 
         const appearance = ContextMenu.Instance.findByDescription('Appearance...');
         const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
-        !this.props.Document._isGroup &&
-            appearanceItems.push({
-                description: 'Reset View',
-                event: () => CollectionFreeFormView.ResetView(this),
-                icon: 'compress-arrows-alt',
-            });
+        !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
         !Doc.noviceMode &&
             appearanceItems.push({
                 description: 'Toggle auto arrange',
@@ -1975,15 +1981,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
     nativeDim = () => this.nativeDimScaling;
 
-    private groupDropDisposer?: DragManager.DragDropDisposer;
-    protected createGroupEventsTarget = (ele: HTMLDivElement) => {
-        //used for stacking and masonry view
-        this.groupDropDisposer?.();
-        if (ele) {
-            this.groupDropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
-        }
-    };
-
     @action
     brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => {
         this._brushtimer1 && clearTimeout(this._brushtimer1);
@@ -2068,51 +2065,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         {/* // uncomment to show snap lines */}
                         <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
                             <svg style={{ width: '100%', height: '100%' }}>
-                                {this._hLines?.map(l => (
-                                    <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />
-                                ))}
-                                {this._vLines?.map(l => (
-                                    <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
-                                ))}
+                                {(this._hLines ?? [])
+                                    .map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />) //
+                                    .concat((this._vLines ?? []).map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)) ?? []}
                             </svg>
                         </div>
 
-                        {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? (
-                            <div
-                                className="collectionFreeForm-groupDropper"
-                                ref={this.createGroupEventsTarget}
-                                style={{
-                                    width: this.ChildDrag ? '10000' : '100%',
-                                    height: this.ChildDrag ? '10000' : '100%',
-                                    left: this.ChildDrag ? '-5000' : 0,
-                                    top: this.ChildDrag ? '-5000' : 0,
-                                    position: 'absolute',
-                                    background: '#0009930',
-                                    pointerEvents: 'all',
-                                }}
-                            />
-                        ) : null}
+                        {this.GroupChildDrag ? <div className="collectionFreeForm-groupDropper" /> : null}
                     </>
                 )}
             </div>
         );
     }
-    /// resetView restores a freeform collection to unit scale and centered at (0,0)  UNLESS
-    //  the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
-    static ResetView(view?: CollectionFreeFormView) {
-        if (view) {
-            if (view.props.Document._isGroup) return;
-            view.props.Document[view.panXFieldKey] = view.props.Document[view.panYFieldKey] = 0;
-            view.props.Document[view.scaleFieldKey] = 1;
-        } else {
-            SelectionManager.Docs()
-                .filter(doc => !doc._isGroup)
-                .forEach(doc => {
-                    doc._freeform_panX = doc._freeform_panY = 0;
-                    doc._freeform_scale = 1;
-                });
-        }
-    }
 }
 
 interface CollectionFreeFormOverlayViewProps {
@@ -2358,6 +2322,3 @@ ScriptingGlobals.add(function bringToFront() {
 ScriptingGlobals.add(function sendToBack(doc: Doc) {
     SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true));
 });
-ScriptingGlobals.add(function resetView() {
-    CollectionFreeFormView.ResetView();
-});
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 4092d9722..9d5b533d1 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -272,7 +272,7 @@ export class SchemaRTFCell extends React.Component<SchemaTableCellProps> {
         fieldProps.isContentActive = this.selectedFunc;
         return (
             <div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}>
-                {this.selected ? <FormattedTextBox allowScroll={true} {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+                {this.selected ? <FormattedTextBox {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ef96e64be..e4fc6c4a2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -394,18 +394,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.treeViewDoc = this.props.treeViewDoc;
             dragData.removeDocument = this.props.removeDocument;
             dragData.moveDocument = this.props.moveDocument;
+            dragData.draggedViews = [this.props.DocumentView()];
             dragData.canEmbed = this.rootDoc.dragAction ?? this.props.dragAction ? true : false;
-            const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
-            ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
             DragManager.StartDocumentDrag(
                 selected.map(dv => dv.docView!._mainCont.current!),
                 dragData,
                 x,
                 y,
-                { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) },
-                () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))
+                { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }
             ); // this needs to happen after the drop event is processed.
-            ffview?.setupDragLines(false);
         }
     }
 
@@ -495,11 +492,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 }
                 const sendToBack = e.altKey;
                 this._singleClickFunc =
-                    clickFunc ??
-                    (() =>
-                        sendToBack
-                            ? this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.bringToFront(this.rootDoc, true)
-                            : this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? this.props.select(e.ctrlKey || e.metaKey || e.shiftKey));
+                    // prettier-ignore
+                    clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.rootDoc, true) : 
+                                                      this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? 
+                                        this.props.select(e.ctrlKey || e.metaKey || e.shiftKey)));
                 const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
                 if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
                     this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
@@ -1099,7 +1095,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
     @computed get innards() {
         TraceMobx();
-        const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
         const showTitle = this.layout_showTitle?.split(':')[0];
         const showTitleHover = this.layout_showTitle?.includes(':hover');
         const captionView = !this.layout_showCaption ? null : (
@@ -1107,8 +1102,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 className="documentView-captionWrapper"
                 style={{
                     pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
-                    minWidth: 50 * ffscale(),
-                    maxHeight: `max(100%, ${20 * ffscale()}px)`,
                     background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'),
                     color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')),
                 }}>
@@ -1117,7 +1110,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     yPadding={10}
                     xPadding={10}
                     fieldKey={this.layout_showCaption}
-                    fontSize={12 * Math.max(1, (2 * ffscale()) / 3)}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
                     noSidebar={true}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4ebf22ddf..f014f842e 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -34,7 +34,6 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     backgroundColor?: string;
     treeViewDoc?: Doc;
     color?: string;
-    fontSize?: number;
     height?: number;
     width?: number;
     noSidebar?: boolean;
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index f0827936b..7af4d9b59 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,41 +1,32 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
+import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { ColorState } from 'react-color';
 import { Doc, Opt } from '../../../../fields/Doc';
-import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../../Utils';
+import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
-import { LinkPopup } from '../../linking/LinkPopup';
-import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
 // import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
-import { EditorView } from 'prosemirror-view';
-import './MapAnchorMenu.scss';
-import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { StrCast } from '../../../../fields/Types';
-import { DocumentType } from '../../../documents/DocumentTypes';
+import { IconButton } from 'browndash-components';
 import { SettingsManager } from '../../../util/SettingsManager';
+import './MapAnchorMenu.scss';
 
 @observer
 export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     static Instance: MapAnchorMenu;
 
     private _disposer: IReactionDisposer | undefined;
-    private _disposer2: IReactionDisposer | undefined;
-    private _commentCont = React.createRef<HTMLButtonElement>();
+    private _commentRef = React.createRef<HTMLDivElement>();
 
     public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
 
     public Center: () => void = unimplementedFunction;
-    // public OnClick: (e: PointerEvent) => void = unimplementedFunction;
+    public OnClick: (e: PointerEvent) => void = unimplementedFunction;
     // public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
-    // public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
-    // public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+    public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
     public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
     public Delete: () => void = unimplementedFunction;
-    public LinkNote: () => void = unimplementedFunction;
     // public MakeTargetToggle: () => void = unimplementedFunction;
     // public ShowTargetTrail: () => void = unimplementedFunction;
     public IsTargetToggler: () => boolean = returnFalse;
@@ -52,23 +43,12 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     componentWillUnmount() {
         this._disposer?.();
-        this._disposer2?.();
     }
 
     componentDidMount() {
-        this._disposer2 = reaction(
-            () => this._opacity,
-            opacity => {
-                if (!opacity) {
-                }
-            },
-            { fireImmediately: true }
-        );
         this._disposer = reaction(
             () => SelectionManager.Views().slice(),
-            selected => {
-                MapAnchorMenu.Instance.fadeOut(true);
-            }
+            selected => MapAnchorMenu.Instance.fadeOut(true)
         );
     }
     // audioDown = (e: React.PointerEvent) => {
@@ -87,6 +67,18 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     //         e => this.OnCrop?.(e)
     //     );
     // };
+    notePointerDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(
+            this,
+            e,
+            (e: PointerEvent) => {
+                this.StartDrag(e, this._commentRef.current!);
+                return true;
+            },
+            returnFalse,
+            e => this.OnClick(e)
+        );
+    };
 
     static top = React.createRef<HTMLDivElement>();
     // public get Top(){
@@ -105,12 +97,14 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                     />
                 }
                 {
-                    <IconButton
-                        tooltip="Link Note to Pin" //
-                        onPointerDown={this.LinkNote}
-                        icon={<FontAwesomeIcon icon="sticky-note" />}
-                        color={SettingsManager.userColor}
-                    />
+                    <div ref={this._commentRef}>
+                        <IconButton
+                            tooltip="Link Note to Pin" //
+                            onPointerDown={this.notePointerDown}
+                            icon={<FontAwesomeIcon icon="sticky-note" />}
+                            color={SettingsManager.userColor}
+                        />
+                    </div>
                 }
                 {
                     <IconButton
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index d7469e530..a419858fc 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -6,6 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
 import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols';
+import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
@@ -24,6 +25,7 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
+import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../trails';
 import { MapAnchorMenu } from './MapAnchorMenu';
 import './MapBox.scss';
@@ -215,6 +217,37 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
+    startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => {
+        e.preventDefault();
+        e.stopPropagation();
+
+        const sourceAnchorCreator = action(() => {
+            const note = this.getAnchor(true);
+            if (note && this.selectedPin) {
+                note.latitude = this.selectedPin.latitude;
+                note.longitude = this.selectedPin.longitude;
+                note.map = this.selectedPin.map;
+            }
+            return note as Doc;
+        });
+
+        const targetCreator = (annotationOn: Doc | undefined) => {
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+            FormattedTextBox.SelectOnLoad = target[Id];
+            return target;
+        };
+        const docView = this.props.DocumentView?.();
+        docView &&
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+                dragComplete: e => {
+                    if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
+                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document;
+                        e.annoDragData.linkSourceDoc.followLinkZoom = false;
+                    }
+                },
+            });
+    };
+
     createNoteAnnotation = () => {
         const createFunc = undoable(
             action(() => {
@@ -388,7 +421,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
         MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
-        MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
+        MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
+        MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
 
         const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
         const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 6a92d09d9..90ebf5206 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -78,9 +78,7 @@ const translate = setCORS('http://cors-anywhere.herokuapp.com/');
 export const GoogleRef = 'googleDocId';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
-export interface FormattedTextBoxProps {
-    allowScroll?: boolean;
-}
+export interface FormattedTextBoxProps {}
 @observer
 export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() {
     public static LayoutString(fieldStr: string) {
@@ -562,7 +560,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.annoDragData) {
             de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
-            e.stopPropagation();
             return true;
         }
         const dragData = de.complete.docDragData;
@@ -2067,10 +2064,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
 
         // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
-        if (this.props.isContentActive() && !this.props.allowScroll) {
+        if (this.props.isContentActive()) {
             // prevent default if selected || child is active  but this doc isn't scrollable
             if (
-                (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(Number(this.layoutDoc._height)) && //
+                (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(this.props.PanelHeight()) && //
                 (this.props.isSelected() || this.isAnyChildContentActive())
             ) {
                 e.preventDefault();
@@ -2131,7 +2128,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     ref={this._ref}
                     style={{
                         cursor: this.props.isContentActive() ? 'text' : undefined,
-                        overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
                         height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
                         pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none',
                     }}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 85c1cce8c..18cf633f4 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -5,16 +5,15 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from 'mobx-react';
 import { ColorState } from 'react-color';
 import { Doc, Opt } from '../../../fields/Doc';
-import { StrCast } from '../../../fields/Types';
 import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils';
 import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
 import { LinkPopup } from '../linking/LinkPopup';
 import './AnchorMenu.scss';
 import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';
-import { SettingsManager } from '../../util/SettingsManager';
 
 @observer
 export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
-- 
cgit v1.2.3-70-g09d2

