From 3d5bc514936ba804eaa24e9ca1f4619bcaf81eb5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 3 May 2020 23:59:35 -0400
Subject: extended documentBox's to support drag and drop of style. 
 reorganized imports to avoid some cycles

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

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

diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 7ad15ef41..c74cfbcf4 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -132,7 +132,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         this._color = color;
     }
 
-    pointerEnteredRow = action(() => SelectionManager.GetIsDragging() && (this._background = "#b4b4b4"));
+    pointerEnteredRow = action(() => DragManager.Vals.Instance.GetIsDragging() && (this._background = "#b4b4b4"));
 
     @action
     pointerLeaveRow = () => {
-- 
cgit v1.2.3-70-g09d2


From d9227908ba8e8db5084c468a242b2839ab11a33d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 4 May 2020 12:50:46 -0400
Subject: fixed links and snap lines broken by moving things into DragManager.

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/DragManager.ts                     |  34 ++--
 src/client/util/SelectionManager.ts                |   3 +
 src/client/util/SnappingManager.ts                 |  29 +++
 src/client/views/DocumentDecorations.tsx           |   5 +-
 src/client/views/MainView.scss                     |   9 +
 src/client/views/MainView.tsx                      |  18 +-
 .../views/collections/CollectionDockingView.tsx    |   5 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   4 +-
 .../views/collections/CollectionPileView.tsx       |   6 +-
 .../views/collections/CollectionSchemaCells.tsx    |   4 +-
 .../CollectionSchemaMovableTableHOC.tsx            |   6 +-
 .../views/collections/CollectionStackingView.tsx   |   7 +-
 .../CollectionStackingViewFieldColumn.tsx          |   6 +-
 .../views/collections/CollectionTreeView.tsx       |   7 +-
 .../CollectionFreeFormLinkView.tsx                 |   9 +-
 .../CollectionFreeFormLinksView.tsx                |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   7 +-
 src/client/views/nodes/DocHolderBox.scss           |  15 ++
 src/client/views/nodes/DocHolderBox.tsx            | 207 +++++++++++++++++++++
 src/client/views/nodes/DocumentBox.scss            |  15 --
 src/client/views/nodes/DocumentBox.tsx             | 207 ---------------------
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   3 +-
 src/client/views/nodes/QueryBox.tsx                |   5 +-
 25 files changed, 328 insertions(+), 291 deletions(-)
 create mode 100644 src/client/util/SnappingManager.ts
 create mode 100644 src/client/views/nodes/DocHolderBox.scss
 create mode 100644 src/client/views/nodes/DocHolderBox.tsx
 delete mode 100644 src/client/views/nodes/DocumentBox.scss
 delete mode 100644 src/client/views/nodes/DocumentBox.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 45687f597..f5d6cd7f6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,7 +37,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { QueryBox } from "../views/nodes/QueryBox";
 import { ColorBox } from "../views/nodes/ColorBox";
 import { LinkAnchorBox } from "../views/nodes/LinkAnchorBox";
-import { DocHolderBox } from "../views/nodes/DocumentBox";
+import { DocHolderBox } from "../views/nodes/DocHolderBox";
 import { InkingStroke } from "../views/InkingStroke";
 import { InkField } from "../../new_fields/InkField";
 import { InkingControl } from "../views/InkingControl";
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 348aba588..0d208cf1b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -11,6 +11,7 @@ import { emptyFunction } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
 import { UndoManager } from "./UndoManager";
+import { SnappingManager } from "./SnappingManager";
 
 export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move
 export function SetupDrag(
@@ -49,7 +50,7 @@ export function SetupDrag(
             if (e.shiftKey) {
                 e.persist();
                 const dragDoc = await docFunc();
-                dragDoc && DragManager.Vals.Instance.StartWindowDrag?.({
+                dragDoc && DragManager.StartWindowDrag?.({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
@@ -66,19 +67,7 @@ export function SetupDrag(
 
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
-    export class Vals {
-        static Instance: Vals = new Vals();
-        @observable public IsDragging: boolean = false;
-        @observable public horizSnapLines: number[] = [];
-        @observable public vertSnapLines: number[] = [];
-        public StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
-        public SetIsDragging(dragging: boolean) { runInAction(() => this.IsDragging = dragging); }
-        public GetIsDragging() { return this.IsDragging; }
-        @action public clearSnapLines() {
-            this.vertSnapLines.length = 0;
-            this.horizSnapLines.length = 0;
-        }
-    }
+    export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
 
     export function Root() {
         const root = document.getElementById("root");
@@ -267,8 +256,7 @@ export namespace DragManager {
     }
 
     export function SetSnapLines(horizLines: number[], vertLines: number[]) {
-        DragManager.Vals.Instance.horizSnapLines.push(...horizLines);
-        DragManager.Vals.Instance.vertSnapLines.push(...vertLines);
+        SnappingManager.setSnapLines(horizLines, vertLines);
     }
 
     export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
@@ -286,8 +274,8 @@ export namespace DragManager {
         };
 
         return {
-            thisX: snapVal([xFromLeft, xFromRight], e.pageX, DragManager.Vals.Instance.vertSnapLines),
-            thisY: snapVal([yFromTop, yFromBottom], e.pageY, DragManager.Vals.Instance.horizSnapLines)
+            thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
+            thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines())
         };
     }
     export let docsBeingDragged: Doc[] = [];
@@ -299,7 +287,7 @@ export namespace DragManager {
             dragDiv.style.pointerEvents = "none";
             DragManager.Root().appendChild(dragDiv);
         }
-        DragManager.Vals.Instance.SetIsDragging(true);
+        SnappingManager.SetIsDragging(true);
         const scaleXs: number[] = [];
         const scaleYs: number[] = [];
         const xs: number[] = [];
@@ -388,7 +376,7 @@ export namespace DragManager {
                 }
                 AbortDrag();
                 finishDrag?.(new DragCompleteEvent(true, dragData));
-                DragManager.Vals.Instance.StartWindowDrag?.({
+                DragManager.StartWindowDrag?.({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
@@ -415,19 +403,19 @@ export namespace DragManager {
         const endDrag = action(() => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
-            DragManager.Vals.Instance.clearSnapLines();
+            SnappingManager.clearSnapLines();
         });
 
         AbortDrag = () => {
             hideDragShowOriginalElements();
-            DragManager.Vals.Instance.SetIsDragging(false);
+            SnappingManager.SetIsDragging(false);
             options?.dragComplete?.(new DragCompleteEvent(true, dragData));
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
             hideDragShowOriginalElements();
             dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
-            DragManager.Vals.Instance.SetIsDragging(false);
+            SnappingManager.SetIsDragging(false);
             endDrag();
             options?.dragComplete?.(new DragCompleteEvent(false, dragData));
         };
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index d509168b6..11d2cafb2 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -8,6 +8,7 @@ export namespace SelectionManager {
 
     class Manager {
 
+        @observable IsDragging: boolean = false;
         SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
 
         @action
@@ -53,6 +54,8 @@ export namespace SelectionManager {
         manager.SelectDoc(docView, ctrlPressed);
     }
 
+    export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
+    export function GetIsDragging() { return manager.IsDragging; }
     // computed functions, such as used in IsSelected generate errors if they're called outside of a
     // reaction context.  Specifying the context with 'outsideReaction' allows an efficiency feature
     // to avoid unnecessary mobx invalidations when running inside a reaction.
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
new file mode 100644
index 000000000..fc07e8ab4
--- /dev/null
+++ b/src/client/util/SnappingManager.ts
@@ -0,0 +1,29 @@
+import { observable, action, runInAction } from "mobx";
+
+export namespace SnappingManager {
+
+    class Manager {
+        @observable IsDragging: boolean = false;
+        @observable public horizSnapLines: number[] = [];
+        @observable public vertSnapLines: number[] = [];
+        @action public clearSnapLines() {
+            this.vertSnapLines = [];
+            this.horizSnapLines = [];
+        }
+        @action public setSnapLines(horizLines: number[], vertLines: number[]) {
+            this.horizSnapLines = horizLines;
+            this.vertSnapLines = vertLines;
+        }
+    }
+
+    const manager = new Manager();
+
+    export function clearSnapLines() { manager.clearSnapLines(); }
+    export function setSnapLines(horizLines: number[], vertLines: number[]) { manager.setSnapLines(horizLines, vertLines); }
+    export function horizSnapLines() { return manager.horizSnapLines; }
+    export function vertSnapLines() { return manager.vertSnapLines; }
+
+    export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
+    export function GetIsDragging() { return manager.IsDragging; }
+}
+
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index f8e77d90a..b89806656 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -21,6 +21,7 @@ import { Id } from '../../new_fields/FieldSymbols';
 import e = require('express');
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MainView } from './MainView';
+import { SnappingManager } from '../util/SnappingManager';
 
 library.add(faCaretUp);
 library.add(faObjectGroup);
@@ -364,7 +365,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         this.Interacting = false;
         (e.button === 0) && this._resizeUndo?.end();
         this._resizeUndo = undefined;
-        DragManager.Vals.Instance.clearSnapLines();
+        SnappingManager.clearSnapLines();
     }
 
     @computed
@@ -403,7 +404,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
-        if (DragManager.Vals.Instance.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+        if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
         const minimal = bounds.r - bounds.x < 100 ? true : false;
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 04288a9e1..753ba700c 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -22,6 +22,15 @@
     z-index: 1;
 }
 
+.mainView-snapLines {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    pointer-events:none;
+}
+
 .mainView-container, .mainView-container-dark {
     input {
         color: unset !important;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 71c2bf245..9ebd60c52 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -44,6 +44,7 @@ import { PreviewCursor } from './PreviewCursor';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { DragManager } from '../util/DragManager';
+import { SnappingManager } from '../util/SnappingManager';
 
 @observer
 export class MainView extends React.Component {
@@ -575,6 +576,15 @@ export class MainView extends React.Component {
         return this._mainViewRef;
     }
 
+    @computed get snapLines() {
+        return <div className="mainView-snapLines">
+            <svg style={{ width: "100%", height: "100%" }}>
+                {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+            </svg>
+        </div>
+    }
+
     render() {
         return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
             <DictationOverlay />
@@ -592,14 +602,8 @@ export class MainView extends React.Component {
             <MarqueeOptionsMenu />
             <RichTextMenu />
             <OverlayView />
-            {// TO VIEW SNAP LINES
-                <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
-                    <svg style={{ width: "100%", height: "100%" }}>
-                        {DragManager.Vals.Instance.horizSnapLines.map((l: any) => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                        {DragManager.Vals.Instance.vertSnapLines.map((l: any) => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                    </svg>
-                </div>}
             <TimelineMenu />
+            {this.snapLines}
         </div >);
     }
 }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 33ece13cc..6cd5d1b1b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -30,6 +30,7 @@ import { SubCollectionViewProps } from "./CollectionSubView";
 import { DockingViewButtonSelector } from './ParentDocumentSelector';
 import React = require("react");
 import { CollectionViewType } from './CollectionView';
+import { SnappingManager } from '../../util/SnappingManager';
 library.add(faFile);
 const _global = (window /* browser */ || global /* node */) as any;
 
@@ -68,7 +69,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
-        DragManager.Vals.Instance.StartWindowDrag = this.StartOtherDrag;
+        DragManager.StartWindowDrag = this.StartOtherDrag;
     }
     hack: boolean = false;
     undohack: any = null;
@@ -500,7 +501,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 const stack = tab.contentItem.parent;
                 // shifts the focus to this tab when another tab is dragged over it
                 tab.element[0].onmouseenter = (e: any) => {
-                    if (!this._isPointerDown || !DragManager.Vals.Instance.GetIsDragging()) return;
+                    if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return;
                     const activeContentItem = tab.header.parent.getActiveContentItem();
                     if (tab.contentItem !== activeContentItem) {
                         tab.header.parent.setActiveContentItem(tab.contentItem);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index c74cfbcf4..95c7643c9 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -12,12 +12,12 @@ import { numberRange, setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import "./CollectionStackingView.scss";
+import { SnappingManager } from "../../util/SnappingManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -132,7 +132,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         this._color = color;
     }
 
-    pointerEnteredRow = action(() => DragManager.Vals.Instance.GetIsDragging() && (this._background = "#b4b4b4"));
+    pointerEnteredRow = action(() => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4"));
 
     @action
     pointerLeaveRow = () => {
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 0a10c24b3..d3ae21f3a 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -12,7 +12,7 @@ import React = require("react");
 import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
 import { SelectionManager } from "../../util/SelectionManager";
 import { UndoManager } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 @observer
 export class CollectionPileView extends CollectionSubView(doc => doc) {
@@ -79,7 +79,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
     _undoBatch: UndoManager.Batch | undefined;
     pointerDown = (e: React.PointerEvent) => {
         let dist = 0;
-        DragManager.Vals.Instance.SetIsDragging(true);
+        SnappingManager.SetIsDragging(true);
         // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header
         setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
             if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) {
@@ -99,7 +99,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         }, () => {
             this._undoBatch?.end();
             this._undoBatch = undefined;
-            DragManager.Vals.Instance.SetIsDragging(false);
+            SnappingManager.SetIsDragging(false);
             if (!this.childDocs.length) {
                 this.props.ContainingCollectionView?.removeDocument(this.props.Document);
             }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 0e6489947..5253ee0b9 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -18,11 +18,11 @@ import "./CollectionSchemaView.scss";
 import { CollectionView } from "./CollectionView";
 import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types";
 import { Docs } from "../../documents/Documents";
-import { SelectionManager } from "../../util/SelectionManager";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faExpand } from '@fortawesome/free-solid-svg-icons';
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 library.add(faExpand);
 
@@ -189,7 +189,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                 this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
         };
         const onPointerEnter = (e: React.PointerEvent): void => {
-            if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging() && (type === "document" || type === undefined)) {
+            if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
                 dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
             }
         };
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index f9cd9a628..8636e3eb5 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -4,7 +4,6 @@ import "./CollectionSchemaView.scss";
 import { Transform } from "../../util/Transform";
 import { Doc } from "../../../new_fields/Doc";
 import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
 import { ContextMenu } from "../ContextMenu";
 import { action } from "mobx";
@@ -14,6 +13,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { DocumentManager } from "../../util/DocumentManager";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 library.add(faGripVertical, faTrash);
 
@@ -32,7 +32,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "collectionSchema-col-wrapper";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
@@ -143,7 +143,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
     private _rowDropDisposer?: DragManager.DragDropDisposer;
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "collectionSchema-row-wrapper";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index f6cdebc9b..b6faea845 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -14,7 +14,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_field
 import { TraceMobx } from "../../../new_fields/util";
 import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
@@ -26,7 +25,7 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { SnappingManager } from "../../util/SnappingManager";
 const _global = (window /* browser */ || global /* node */) as any;
 
 type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -312,7 +311,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) {
+                        if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                         }
                     }));
@@ -359,7 +358,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) {
+                        if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
                         }
                     }));
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 1d16a5478..dccef7983 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -13,7 +13,6 @@ import { ImageField } from "../../../new_fields/URLField";
 import { TraceMobx } from "../../../new_fields/util";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
@@ -23,6 +22,7 @@ import { CollectionStackingView } from "./CollectionStackingView";
 import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import "./CollectionStackingView.scss";
 import { listSpec } from "../../../new_fields/Schema";
+import { SnappingManager } from "../../util/SnappingManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -120,7 +120,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     pointerEntered = () => {
-        if (DragManager.Vals.Instance.GetIsDragging()) {
+        if (SnappingManager.GetIsDragging()) {
             this._background = "#b4b4b4";
         }
     }
@@ -355,7 +355,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             <div className="collectionStackingViewFieldColumn" key={heading}
                 style={{
                     width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`,
-                    height: undefined, // DragManager.Vals.Instance.GetIsDragging() ? "100%" : undefined,
+                    height: undefined, // DraggingManager.GetIsDragging() ? "100%" : undefined,
                     background: this._background
                 }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 288fa8794..f2b0e3155 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,6 +6,7 @@ import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
+import { PrefetchProxy } from '../../../new_fields/Proxy';
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
@@ -13,6 +14,7 @@ import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZer
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
+import { SnappingManager } from '../../util/SnappingManager';
 import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -31,7 +33,6 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { CollectionViewType } from './CollectionView';
 import React = require("react");
-import { PrefetchProxy } from '../../../new_fields/Proxy';
 
 
 export interface TreeViewProps {
@@ -147,7 +148,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active(true) && Doc.BrushDoc(this.dataDoc);
-        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "treeViewItem-header";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
@@ -451,7 +452,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     fontWeight: this.props.document.searchMatch ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: this.props.active() || DragManager.Vals.Instance.GetIsDragging() ? undefined : "none"
+                    pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
                 }} >
                 {Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
                     this.editableView("title") :
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 0c9a1aa9a..695898ca9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -9,7 +9,7 @@ import { DocumentType } from "../../../documents/DocumentTypes";
 import { observable, action, reaction, IReactionDisposer } from "mobx";
 import { StrCast } from "../../../../new_fields/Types";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { DragManager } from "../../../util/DragManager";
+import { SnappingManager } from "../../../util/SnappingManager";
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -25,7 +25,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     componentDidMount() {
         this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)],
             action(() => {
-                if (DragManager.Vals.Instance.GetIsDragging()) return;
+                if (SnappingManager.GetIsDragging()) return;
                 setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
                 setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
                 const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
@@ -83,7 +83,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     }
 
     render() {
-        if (DragManager.Vals.Instance.GetIsDragging()) return null;
+        if (SnappingManager.GetIsDragging()) return null;
         this.props.A.props.ScreenToLocalTransform().transform(this.props.B.props.ScreenToLocalTransform());
         const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
         const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
@@ -107,8 +107,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const aActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
         const bActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
         const text = StrCast(this.props.A.props.Document.linkRelationship);
+
         return !a.width || !b.width || ((!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
-            <text x={(pt1[0] + pt2[0]) / 2} y={(pt1[1] + pt2[1]) / 2}>
+            <text x={(Math.min(pt1[0], pt2[0]) * 2 + Math.max(pt1[0], pt2[0])) / 3} y={(pt1[1] + pt2[1]) / 2}>
                 {text !== "-ungrouped-" ? text : ""}
             </text>
             <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 6c6dbf653..5e5ad7db5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -9,7 +9,7 @@ import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
 import { Utils, emptyFunction } from "../../../../Utils";
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { DragManager } from "../../../util/DragManager";
+import { SnappingManager } from "../../../util/SnappingManager";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component {
@@ -36,7 +36,7 @@ export class CollectionFreeFormLinksView extends React.Component {
     }
 
     render() {
-        return DragManager.Vals.Instance.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
+        return SnappingManager.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
             <svg className="collectionfreeformlinksview-svgCanvas">
                 {this.uniqueConnections}
             </svg>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2aa9b1f5f..37957b0f6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -45,6 +45,7 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CollectionViewType } from "../CollectionView";
 import { Timeline } from "../../animationtimeline/Timeline";
+import { SnappingManager } from "../../../util/SnappingManager";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -1174,7 +1175,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         DragManager.SetSnapLines(horizLines, vertLines);
     }
     onPointerOver = (e: React.PointerEvent) => {
-        if (DragManager.Vals.Instance.GetIsDragging()) {
+        if (SnappingManager.GetIsDragging()) {
             this.setupDragLines();
         }
         e.stopPropagation();
@@ -1233,7 +1234,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
     }
-    @computed get backgroundEvents() { return this.layoutDoc.isBackground && DragManager.Vals.Instance.GetIsDragging(); }
+    @computed get backgroundEvents() { return this.layoutDoc.isBackground && SnappingManager.GetIsDragging(); }
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
@@ -1247,7 +1248,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return <div className={"collectionfreeformview-container"}
             ref={this.createDashEventsTarget}
             onPointerOver={this.onPointerOver}
-            onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: DragManager.Vals.Instance.GetIsDragging() ? "all" : undefined,
+            onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: DraggingManager.GetIsDragging() ? "all" : undefined,
             onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: this.backgroundEvents ? "all" : undefined,
diff --git a/src/client/views/nodes/DocHolderBox.scss b/src/client/views/nodes/DocHolderBox.scss
new file mode 100644
index 000000000..3a27c16c1
--- /dev/null
+++ b/src/client/views/nodes/DocHolderBox.scss
@@ -0,0 +1,15 @@
+.documentBox-container {
+    width: 100%;
+    height: 100%;
+    pointer-events: all;
+    background: rgb(241, 239, 235);
+    position: absolute;
+    .documentBox-lock {
+        margin: auto;
+        color: white;
+        position: absolute;
+    }
+    .contentFittingDocumentView {
+        position: absolute;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
new file mode 100644
index 000000000..9787877cc
--- /dev/null
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -0,0 +1,207 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, IReactionDisposer, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, Field } from "../../../new_fields/Doc";
+import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
+import { makeInterface } from "../../../new_fields/Schema";
+import { ComputedField } from "../../../new_fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from "../../../new_fields/util";
+import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { DragManager } from "../../util/DragManager";
+import { undoBatch } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import "./DocHolderBox.scss";
+import { DocumentView } from "./DocumentView";
+import { FieldView, FieldViewProps } from "./FieldView";
+import React = require("react");
+
+type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
+
+@observer
+export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
+    _prevSelectionDisposer: IReactionDisposer | undefined;
+    _dropDisposer?: DragManager.DragDropDisposer;
+    _selections: Doc[] = [];
+    _contRef = React.createRef<HTMLDivElement>();
+    _curSelection = -1;
+    componentDidMount() {
+        this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => {
+            if (data instanceof Doc && !this.isSelectionLocked()) {
+                this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1);
+                this._selections.push(data);
+                this._curSelection = this._selections.length - 1;
+            }
+        });
+    }
+    componentWillUnmount() {
+        this._prevSelectionDisposer?.();
+    }
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const funcs: ContextMenuProps[] = [];
+        funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
+        funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" });
+        funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" });
+        funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "<KeyValueBox {...props} />", icon: "project-diagram" });
+
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+    }
+    lockSelection = () => {
+        this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey];
+    }
+    showSelection = () => {
+        this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`);
+    }
+    isSelectionLocked = () => {
+        const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey);
+        return !kvpstring || kvpstring.includes("DOC");
+    }
+    toggleLockSelection = () => {
+        !this.isSelectionLocked() ? this.lockSelection() : this.showSelection();
+        return true;
+    }
+    prevSelection = () => {
+        this.lockSelection();
+        if (this._curSelection > 0) {
+            this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection];
+            return true;
+        }
+    }
+    nextSelection = () => {
+        if (this._curSelection < this._selections.length - 1 && this._selections.length) {
+            this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection];
+            return true;
+        }
+    }
+    onPointerDown = (e: React.PointerEvent) => {
+        if (this.active() && e.button === 0 && !e.ctrlKey) {
+            e.stopPropagation();
+        }
+    }
+    onLockClick = (e: React.MouseEvent) => {
+        this.toggleLockSelection();
+        (e.nativeEvent as any).formattedHandled = true;
+        e.stopPropagation();
+    }
+    get xPad() { return NumCast(this.props.Document._xPadding); }
+    get yPad() { return NumCast(this.props.Document._yPadding); }
+    onClick = (e: React.MouseEvent) => {
+        let hitWidget: boolean | undefined = false;
+        if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
+        else if (this._contRef.current!.getBoundingClientRect().bottom - this.yPad < e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
+        else {
+            if (this._contRef.current!.getBoundingClientRect().left + this.xPad > e.clientX) hitWidget = this.prevSelection();
+            if (this._contRef.current!.getBoundingClientRect().right - this.xPad < e.clientX) hitWidget = this.nextSelection();
+        }
+        if (hitWidget) {
+            (e.nativeEvent as any).formattedHandled = true;
+            e.stopPropagation();
+        }
+    }
+    pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
+    pheight = () => this.props.PanelHeight() - 2 * this.yPad;
+    getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
+    isActive = () => this.active() || !this.props.renderDepth;
+    layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null);
+    get renderContents() {
+        const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
+        const layoutTemplate = StrCast(this.layoutDoc.childLayoutString);
+        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ?
+            <DocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={layoutTemplate}
+                LayoutTemplate={this.layoutTemplateDoc}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={true}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne} /> :
+            <ContentFittingDocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={layoutTemplate}
+                LayoutTemplate={this.layoutTemplateDoc}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={true}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />;
+        return contents;
+    }
+    render() {
+        TraceMobx();
+        return <div className="documentBox-container" ref={this._contRef}
+            onContextMenu={this.specificContextMenu}
+            onPointerDown={this.onPointerDown} onClick={this.onClick}
+            style={{
+                background: StrCast(this.layoutDoc.backgroundColor),
+                border: `#00000021 solid ${this.xPad}px`,
+                borderTop: `#0000005e solid ${this.yPad}px`,
+                borderBottom: `#0000005e solid ${this.yPad}px`,
+            }}>
+            {this.renderContents}
+            <div className="documentBox-lock" onClick={this.onLockClick} ref={this.createDropTarget}
+                style={{ marginTop: - this.yPad }}>
+                <FontAwesomeIcon icon={this.isSelectionLocked() ? "lock" : "unlock"} size="sm" />
+            </div>
+        </div >;
+    }
+
+    @undoBatch
+    @action
+    drop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.complete.docDragData) {
+            if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) {
+                const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null);
+                this.props.Document.childLayoutTemplate = doc;
+            }
+        }
+    }
+    protected createDropTarget = (ele: HTMLDivElement) => {
+        this._dropDisposer?.();
+        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
+    }
+
+}
diff --git a/src/client/views/nodes/DocumentBox.scss b/src/client/views/nodes/DocumentBox.scss
deleted file mode 100644
index 3a27c16c1..000000000
--- a/src/client/views/nodes/DocumentBox.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.documentBox-container {
-    width: 100%;
-    height: 100%;
-    pointer-events: all;
-    background: rgb(241, 239, 235);
-    position: absolute;
-    .documentBox-lock {
-        margin: auto;
-        color: white;
-        position: absolute;
-    }
-    .contentFittingDocumentView {
-        position: absolute;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
deleted file mode 100644
index b53c7cfe6..000000000
--- a/src/client/views/nodes/DocumentBox.tsx
+++ /dev/null
@@ -1,207 +0,0 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, IReactionDisposer, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field } from "../../../new_fields/Doc";
-import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
-import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DragManager } from "../../util/DragManager";
-import { undoBatch } from "../../util/UndoManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent } from "../DocComponent";
-import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
-import "./DocumentBox.scss";
-import { DocumentView } from "./DocumentView";
-import { FieldView, FieldViewProps } from "./FieldView";
-import React = require("react");
-
-type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
-const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
-
-@observer
-export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
-    _prevSelectionDisposer: IReactionDisposer | undefined;
-    _dropDisposer?: DragManager.DragDropDisposer;
-    _selections: Doc[] = [];
-    _contRef = React.createRef<HTMLDivElement>();
-    _curSelection = -1;
-    componentDidMount() {
-        this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => {
-            if (data instanceof Doc && !this.isSelectionLocked()) {
-                this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1);
-                this._selections.push(data);
-                this._curSelection = this._selections.length - 1;
-            }
-        });
-    }
-    componentWillUnmount() {
-        this._prevSelectionDisposer?.();
-    }
-    specificContextMenu = (e: React.MouseEvent): void => {
-        const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
-        funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" });
-        funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" });
-        funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "<KeyValueBox {...props} />", icon: "project-diagram" });
-
-        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-    }
-    lockSelection = () => {
-        this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey];
-    }
-    showSelection = () => {
-        this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`);
-    }
-    isSelectionLocked = () => {
-        const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey);
-        return !kvpstring || kvpstring.includes("DOC");
-    }
-    toggleLockSelection = () => {
-        !this.isSelectionLocked() ? this.lockSelection() : this.showSelection();
-        return true;
-    }
-    prevSelection = () => {
-        this.lockSelection();
-        if (this._curSelection > 0) {
-            this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection];
-            return true;
-        }
-    }
-    nextSelection = () => {
-        if (this._curSelection < this._selections.length - 1 && this._selections.length) {
-            this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection];
-            return true;
-        }
-    }
-    onPointerDown = (e: React.PointerEvent) => {
-        if (this.active() && e.button === 0 && !e.ctrlKey) {
-            e.stopPropagation();
-        }
-    }
-    onLockClick = (e: React.MouseEvent) => {
-        this.toggleLockSelection();
-        (e.nativeEvent as any).formattedHandled = true;
-        e.stopPropagation();
-    }
-    get xPad() { return NumCast(this.props.Document._xPadding); }
-    get yPad() { return NumCast(this.props.Document._yPadding); }
-    onClick = (e: React.MouseEvent) => {
-        let hitWidget: boolean | undefined = false;
-        if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
-        else if (this._contRef.current!.getBoundingClientRect().bottom - this.yPad < e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
-        else {
-            if (this._contRef.current!.getBoundingClientRect().left + this.xPad > e.clientX) hitWidget = this.prevSelection();
-            if (this._contRef.current!.getBoundingClientRect().right - this.xPad < e.clientX) hitWidget = this.nextSelection();
-        }
-        if (hitWidget) {
-            (e.nativeEvent as any).formattedHandled = true;
-            e.stopPropagation();
-        }
-    }
-    pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
-    pheight = () => this.props.PanelHeight() - 2 * this.yPad;
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
-    isActive = () => this.active() || !this.props.renderDepth;
-    layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null);
-    get renderContents() {
-        const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
-        const layoutTemplate = StrCast(this.layoutDoc.childLayoutString);
-        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ?
-            <DocumentView
-                Document={containedDoc}
-                DataDoc={undefined}
-                LibraryPath={emptyPath}
-                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
-                ContainingCollectionDoc={undefined}
-                fitToBox={true}
-                LayoutTemplateString={layoutTemplate}
-                LayoutTemplate={this.layoutTemplateDoc}
-                rootSelected={this.props.isSelected}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                removeDocument={this.props.removeDocument}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                ScreenToLocalTransform={this.getTransform}
-                renderDepth={this.props.renderDepth + 1}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
-                PanelWidth={this.pwidth}
-                PanelHeight={this.pheight}
-                focus={this.props.focus}
-                parentActive={this.isActive}
-                dontRegisterView={true}
-                whenActiveChanged={this.props.whenActiveChanged}
-                bringToFront={returnFalse}
-                ContentScaling={returnOne} /> :
-            <ContentFittingDocumentView
-                Document={containedDoc}
-                DataDoc={undefined}
-                LibraryPath={emptyPath}
-                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
-                ContainingCollectionDoc={undefined}
-                fitToBox={true}
-                LayoutTemplateString={layoutTemplate}
-                LayoutTemplate={this.layoutTemplateDoc}
-                rootSelected={this.props.isSelected}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                removeDocument={this.props.removeDocument}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                ScreenToLocalTransform={this.getTransform}
-                renderDepth={this.props.renderDepth + 1}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
-                PanelWidth={this.pwidth}
-                PanelHeight={this.pheight}
-                focus={this.props.focus}
-                parentActive={this.isActive}
-                dontRegisterView={true}
-                whenActiveChanged={this.props.whenActiveChanged}
-                bringToFront={returnFalse}
-                ContentScaling={returnOne}
-            />;
-        return contents;
-    }
-    render() {
-        TraceMobx();
-        return <div className="documentBox-container" ref={this._contRef}
-            onContextMenu={this.specificContextMenu}
-            onPointerDown={this.onPointerDown} onClick={this.onClick}
-            style={{
-                background: StrCast(this.layoutDoc.backgroundColor),
-                border: `#00000021 solid ${this.xPad}px`,
-                borderTop: `#0000005e solid ${this.yPad}px`,
-                borderBottom: `#0000005e solid ${this.yPad}px`,
-            }}>
-            {this.renderContents}
-            <div className="documentBox-lock" onClick={this.onLockClick} ref={this.createDropTarget}
-                style={{ marginTop: - this.yPad }}>
-                <FontAwesomeIcon icon={this.isSelectionLocked() ? "lock" : "unlock"} size="sm" />
-            </div>
-        </div >;
-    }
-
-    @undoBatch
-    @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.complete.docDragData) {
-            if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) {
-                const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null);
-                this.props.Document.childLayoutTemplate = doc;
-            }
-        }
-    }
-    protected createDropTarget = (ele: HTMLDivElement) => {
-        this._dropDisposer?.();
-        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
-    }
-
-}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 81667e549..395a6e0b2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -14,7 +14,7 @@ import { LabelBox } from "./LabelBox";
 import { SliderBox } from "./SliderBox";
 import { LinkBox } from "./LinkBox";
 import { ScriptingBox } from "./ScriptingBox";
-import { DocHolderBox } from "./DocumentBox";
+import { DocHolderBox } from "./DocHolderBox";
 import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FontIconBox } from "./FontIconBox";
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7a81801e1..86e58ca7a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,6 +23,7 @@ import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { ClientUtils } from '../../util/ClientUtils';
 import { DocumentManager } from "../../util/DocumentManager";
+import { SnappingManager } from '../../util/SnappingManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { Scripting } from '../../util/Scripting';
@@ -1105,7 +1106,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get ignorePointerEvents() {
         return this.props.pointerEvents === false ||
-            (this.Document.isBackground && !this.isSelected() && !DragManager.Vals.Instance.GetIsDragging()) ||
+            (this.Document.isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
             (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
     }
     @undoBatch
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index d248b098c..2a21a380d 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -5,13 +5,12 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
 import { Id } from '../../../new_fields/FieldSymbols';
 import { makeInterface, listSpec } from "../../../new_fields/Schema";
 import { StrCast, Cast } from "../../../new_fields/Types";
-import { SelectionManager } from "../../util/SelectionManager";
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { SearchBox } from "../search/SearchBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./QueryBox.scss";
 import { List } from "../../../new_fields/List";
-import { DragManager } from "../../util/DragManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 type QueryDocument = makeInterface<[typeof documentSchema]>;
 const QueryDocument = makeInterface(documentSchema);
@@ -28,7 +27,7 @@ export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryD
     }
 
     render() {
-        const dragging = !DragManager.Vals.Instance.GetIsDragging() ? "" : "-dragging";
+        const dragging = !SnappingManager.GetIsDragging() ? "" : "-dragging";
         return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
             <SearchBox
                 id={this.props.Document[Id]}
-- 
cgit v1.2.3-70-g09d2

