From b3d1b60ac867a472ba791f57c8eb2d7afbb95767 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 3 Dec 2020 10:55:05 -0500
Subject: changed parameters for style provider to use documentViewProps
 instead of renderDepth

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

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e2c6bcaf6..0a9eeab50 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -399,7 +399,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutString: this.childLayoutString,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "boxShadow", this.props.layerProvider);
+            this.props.styleProvider?.(this.props.Document, this.props, "boxShadow", this.props.layerProvider);
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
-- 
cgit v1.2.3-70-g09d2


From cae3cd5b324084986a631348626d0230561f34a7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Dec 2020 12:55:54 -0500
Subject: simplified styleProvider arguments.   fixed useClusters

---
 src/client/views/PropertiesView.tsx                 |  2 +-
 .../views/collections/CollectionCarouselView.tsx    |  2 +-
 src/client/views/collections/CollectionTreeView.tsx |  2 +-
 src/client/views/collections/CollectionView.tsx     |  2 +-
 src/client/views/collections/TabDocView.tsx         | 12 ++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx   |  7 ++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx  |  6 +++---
 src/client/views/nodes/DocHolderBox.tsx             |  2 +-
 src/client/views/nodes/DocumentView.tsx             | 21 +++++++++------------
 src/client/views/nodes/FieldView.tsx                |  2 +-
 src/client/views/nodes/FontIconBox.tsx              |  2 +-
 src/client/views/nodes/LinkBox.tsx                  |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx           |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx  |  2 +-
 .../views/presentationview/PresElementBox.tsx       |  2 +-
 15 files changed, 33 insertions(+), 35 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 9f1b8dad3..08394cd97 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -37,7 +37,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 interface PropertiesViewProps {
     width: number;
     height: number;
-    styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
+    styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
 }
 
 @observer
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 62c1bce3f..16ccdc6f4 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -65,7 +65,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                 </div>
                 <div className="collectionCarouselView-caption" key="caption"
                     style={{
-                        background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor", this.props.layerProvider)),
+                        background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor")),
                         color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
                         borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
                     }}>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ac01536c9..681584cc5 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -215,7 +215,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     render() {
         TraceMobx();
         if (!(this.doc instanceof Doc)) return (null);
-        const background = this.props.styleProvider?.(this.doc, this.props, "backgroundColor", this.props.layerProvider);
+        const background = this.props.styleProvider?.(this.doc, this.props, "backgroundColor");
         const paddingX = `${NumCast(this.doc._xPadding, 10)}px`;
         const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`;
         const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0a9eeab50..8f402c427 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -399,7 +399,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutString: this.childLayoutString,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            this.props.styleProvider?.(this.props.Document, this.props, "boxShadow", this.props.layerProvider);
+            this.props.styleProvider?.(this.props.Document, this.props, "boxShadow");
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 45658418c..e1fd47592 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -434,7 +434,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     //
     // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
     // 
-    public static styleProvider = (doc: Opt<Doc>, props: DocumentViewProps | undefined, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean): any => {
+    public static styleProvider = (doc: Opt<Doc>, props: DocumentViewProps | undefined, property: string): any => {
         switch (property) {
             case "backgroundColor": {
                 if (Doc.UserDoc().renderStyle === "comic") return undefined;
@@ -456,11 +456,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         default: docColor = TabDocView.darkScheme ? "black" : "white"; break;
                     }
                 }
-                if (docColor && (!doc || layerProvider?.(doc) === false)) docColor = Color(docColor).fade(0.5).toString();
+                if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor).fade(0.5).toString();
                 return docColor;
             }
             case "widgetColor": return TabDocView.darkScheme ? "lightgrey" : "dimgrey";
-            case "hidden": return (BoolCast(doc?.hidden) /* || layerProvider?.(doc) === false*/);
+            case "hidden": return (BoolCast(doc?.hidden) /* || props?.layerProvider?.(doc) === false*/);
             case "boxShadow": {
                 switch (doc?.type) {
                     case DocumentType.COL: return StrListCast(doc.layers).includes("background") ? undefined :
@@ -471,7 +471,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             case "docContents": return undefined;
             default:
                 if (property.startsWith("pointerEvents")) {
-                    const layer = doc && layerProvider?.(doc);
+                    const layer = doc && props?.layerProvider?.(doc);
                     if (doc?.Opacity === 0 || doc?.type === DocumentType.INK || doc?.isInkMask) return "none";
                     if (layer === false && !property.includes(":selected") && !SnappingManager.GetIsDragging()) return "none";
                     if (doc?.type !== DocumentType.INK && layer === true) return "all";
@@ -488,7 +488,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 }
         }
     }
-    public static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean): any => {
+    public static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc) {
             switch (property) {
                 case "docContents":
@@ -498,7 +498,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     return <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
                 default:
                     if (property.startsWith("pointerEvents")) return "none";
-                    return TabDocView.styleProvider(doc, props, property, layerProvider);
+                    return TabDocView.styleProvider(doc, props, property);
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 652b926ce..136600164 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -396,8 +396,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
-        let clusterColor = this.props.styleProvider?.(doc, props, property, layerProvider);  // bcz: check 'props'  used to be renderDepth + 1
+    getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
+        let clusterColor = this.props.styleProvider?.(doc, props, property);  // bcz: check 'props'  used to be renderDepth + 1
         if (property !== "backgroundColor") return clusterColor;
         const cluster = NumCast(doc?.cluster);
         if (this.Document._useClusters) {
@@ -985,7 +985,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
-    backgroundHalo = this.Document._useClusters ? returnTrue : computedFn(function (doc: Doc) { return NumCast(doc.group, -1) !== -1; });
+    // @ts-ignore
+    backgroundHalo = computedFn(function (doc: Doc) { return this.Document._useClusters || NumCast(doc.group, -1) !== -1; }).bind(this);
     parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false;
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 51ed49df3..b4b887617 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -142,11 +142,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     NativeHeight = () => this.nativeHeight;
     @computed get pointerEvents() {
         if (this.props.pointerEvents === "none") return "none";
-        return this.props.styleProvider?.(this.Document, this.props, !this._contentView?.docView?.isSelected() ? "pointerEvents:selected" : "pointerEvents", this.props.layerProvider);
+        return this.props.styleProvider?.(this.Document, this.props, !this._contentView?.docView?.isSelected() ? "pointerEvents:selected" : "pointerEvents");
     }
     render() {
         TraceMobx();
-        const backgroundColor = this.props.styleProvider?.(this.Document, this.props, "backgroundColor", this.props.layerProvider);
+        const backgroundColor = this.props.styleProvider?.(this.Document, this.props, "backgroundColor");
         const borderRounding = StrCast(Doc.Layout(this.layoutDoc).borderRounding) || StrCast(this.layoutDoc.borderRounding) || StrCast(this.Document.borderRounding) || undefined;
 
         const divProps = {
@@ -165,7 +165,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 boxShadow:
                     this.Opacity === 0 ? undefined :  // if it's not visible, then no shadow
                         this.layoutDoc.z ? `#9c9396  ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                            this.props.backgroundHalo?.(this.props.Document) && this.props.Document.type !== DocumentType.INK ? (`${this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor", this.props.layerProvider)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(Cast(this.layoutDoc.layers, listSpec("string"), []).includes("background") ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                            this.props.backgroundHalo?.(this.props.Document) && this.props.Document.type !== DocumentType.INK ? (`${this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor")} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(Cast(this.layoutDoc.layers, listSpec("string"), []).includes("background") ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
                                 Cast(this.layoutDoc.layers, listSpec("string"), []).includes('background') ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
                                     StrCast(this.layoutDoc.boxShadow, ""),
                 borderRadius: borderRounding,
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 8231cf89e..e14093e70 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -182,7 +182,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
             onContextMenu={this.specificContextMenu}
             onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
-                background: this.props.styleProvider?.(containedDoc, this.props, "backgroundColor", this.props.layerProvider),
+                background: this.props.styleProvider?.(containedDoc, this.props, "backgroundColor"),
                 border: `#00000021 solid ${this.xPad}px`,
                 borderTop: `#0000005e solid ${this.yPad}px`,
                 borderBottom: `#0000005e solid ${this.yPad}px`,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 903e7e478..5ed49185b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -91,7 +91,7 @@ export interface DocumentViewProps {
     addDocTab: (doc: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     backgroundHalo?: (doc: Doc) => boolean;
-    styleProvider?: (doc: Opt<Doc>, props: DocumentViewProps, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
+    styleProvider?: (doc: Opt<Doc>, props: DocumentViewProps, property: string) => any;
     forceHideLinkButton?: () => boolean;
     opacity?: () => number | undefined;
     ChromeHeight?: () => number;
@@ -1062,7 +1062,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get pointerEvents() {
         if (this.props.pointerEvents === "none") return "none";
-        return this.props.styleProvider?.(this.Document, this.props, this.isSelected() ? "pointerEvents:selected" : "pointerEvents", this.props.layerProvider);
+        return this.props.styleProvider?.(this.Document, this.props, this.isSelected() ? "pointerEvents:selected" : "pointerEvents");
     }
     @undoBatch
     @action
@@ -1086,8 +1086,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         TraceMobx();
         if (!(this.props.Document instanceof Doc)) return (null);
         if (GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate) return (null);
-        if (this.props.styleProvider?.(this.layoutDoc, this.props, "hidden", this.props.layerProvider)) return null;
-        const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, "backgroundColor", this.props.layerProvider);
+        if (this.props.styleProvider?.(this.layoutDoc, this.props, "hidden")) return null;
+        const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, "backgroundColor");
         const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
         const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
         const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
@@ -1101,14 +1101,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear && this.props.Document.type !== DocumentType.INK;
         highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]";  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
         const topmost = this.topMost ? "-topmost" : "";
-        return this.props.styleProvider?.(this.rootDoc, this.props, "docContents", this.props.layerProvider) ?? <div className={`documentView-node${topmost}`}
+        return this.props.styleProvider?.(this.rootDoc, this.props, "docContents") ?? <div className={`documentView-node${topmost}`}
             id={this.props.Document[Id]}
             onKeyDown={this.onKeyDown}
             onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
             onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
             onPointerLeave={action(e => {
                 let entered = false;
-                for (let child = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); child; child = child.parentElement) {
+                for (let child = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); !entered && child; child = child.parentElement) {
                     entered = entered || child === this.ContentDiv;
                 }
                 !entered && Doc.UnBrushDoc(this.props.Document);
@@ -1131,12 +1131,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 fontFamily: StrCast(this.Document._fontFamily, "inherit"),
                 fontSize: !this.props.treeViewDoc ? Cast(this.Document._fontSize, "string", null) : undefined,
             }}>
-            {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <>
-                {this.innards}
-                <div className="documentView-contentBlocker" />
-            </> :
-                this.innards}
-            {!this.props.treeViewDoc && this.props.styleProvider?.(this.rootDoc, this.props, this.isSelected() ? "decorations:selected" : "decorations", this.props.layerProvider) || (null)}
+            {this.innards}
+            {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)}
+            {!this.props.treeViewDoc && this.props.styleProvider?.(this.rootDoc, this.props, this.isSelected() ? "decorations:selected" : "decorations") || (null)}
         </div>;
     }
     render() {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f2fdf7a6b..5cd752fdb 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -42,7 +42,7 @@ export interface FieldViewProps {
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc | Doc[]) => boolean;
     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    styleProvider?: (document: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
+    styleProvider?: (document: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     parentActive: (outsideReaction: boolean) => boolean;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index c9ee4126e..a1bb0604e 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -61,7 +61,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
     render() {
         const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
         const color = StrCast(this.layoutDoc.color, this._foregroundColor);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, "backgroundColor", this.props.layerProvider);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, "backgroundColor");
         const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle");
         const icon = StrCast(this.dataDoc.icon, "user") as any;
         const presSize = shape === 'round' ? 25 : 30;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 1b181cef1..bf19457da 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -17,7 +17,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
     render() {
         return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
-            style={{ background: this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor", this.props.layerProvider) }} >
+            style={{ background: this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor") }} >
 
             <CollectionTreeView {...this.props}
                 ChromeHeight={returnZero}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 5017b3a71..f66811098 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -18,7 +18,7 @@ interface Props {
     linkDoc?: Doc;
     linkSrc?: Doc;
     href?: string;
-    styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
+    styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
     addDocTab: (document: Doc, where: string) => boolean;
     location: number[];
 }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 59909cea1..22e3ac1e9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1598,7 +1598,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.author).length;
         return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) :
             <div className="formattedTextBox-sidebar-handle"
-                style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightBlue" : this.props.styleProvider?.(this.props.Document, this.props, "widgetColor", this.props.layerProvider) }}
+                style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightBlue" : this.props.styleProvider?.(this.props.Document, this.props, "widgetColor") }}
                 onPointerDown={this.sidebarDown}
             />;
     }
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 9742faa2b..37752d6ee 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -322,7 +322,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     </div>}
                 {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
                     style={{
-                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, "color", this.props.layerProvider),
+                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, "color"),
                         boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
                     }}>
                     <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
-- 
cgit v1.2.3-70-g09d2


From 81ee0bf99da264b76f26a21d57259d478cac07f3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Dec 2020 11:58:25 -0500
Subject: fixed filterBox/TreeView to support checkboxes again.  Simplified
 {DocumentView/CollectionView/FieldView}props

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/views/PreviewCursor.tsx                 |  5 +-
 .../views/collections/CollectionCarousel3DView.tsx |  4 +-
 .../views/collections/CollectionCarouselView.tsx   |  4 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |  2 +-
 .../views/collections/CollectionPileView.tsx       |  6 +-
 .../views/collections/CollectionSchemaCells.tsx    |  2 +-
 .../views/collections/CollectionSchemaHeaders.tsx  |  3 -
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../views/collections/CollectionStackingView.tsx   | 12 ++--
 .../CollectionStackingViewFieldColumn.tsx          | 14 ++--
 src/client/views/collections/CollectionSubView.tsx | 42 ++----------
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 .../views/collections/CollectionTreeView.tsx       | 20 +++---
 src/client/views/collections/CollectionView.tsx    | 75 ++++++++++------------
 src/client/views/collections/SchemaTable.tsx       |  8 +--
 src/client/views/collections/TabDocView.tsx        |  2 +-
 src/client/views/collections/TreeView.scss         |  4 ++
 src/client/views/collections/TreeView.tsx          | 24 +++----
 .../CollectionFreeFormRemoteCursors.tsx            |  8 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 24 +++----
 .../collections/collectionFreeForm/MarqueeView.tsx | 28 ++++----
 .../CollectionMulticolumnView.tsx                  |  6 +-
 .../CollectionMultirowView.tsx                     |  6 +-
 src/client/views/nodes/DocumentView.tsx            | 71 ++++++++++----------
 src/client/views/nodes/FieldView.tsx               | 61 +++---------------
 src/client/views/nodes/FilterBox.tsx               |  8 +--
 src/client/views/nodes/LinkBox.tsx                 |  4 +-
 src/client/views/nodes/PresBox.tsx                 |  4 +-
 src/client/views/nodes/VideoBox.tsx                |  1 -
 30 files changed, 189 insertions(+), 265 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ae0cd8b92..8a5f0fb5e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1136,7 +1136,7 @@ export namespace DocUtils {
                         newDoc.x = x;
                         newDoc.y = y;
                         if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
-                        docAdder(newDoc);
+                        docAdder?.(newDoc);
                     }
                 }),
                 icon: "eye"
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index d756227f6..ac61dd1d8 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -11,6 +11,7 @@ import { Transform } from "../util/Transform";
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import "./PreviewCursor.scss";
+import { returnFalse } from '../../Utils';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
@@ -145,13 +146,13 @@ export class PreviewCursor extends React.Component<{}> {
         onKeyPress: (e: KeyboardEvent) => void,
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
-        addDocument: (doc: Doc | Doc[]) => boolean,
+        addDocument: undefined | ((doc: Doc | Doc[]) => boolean),
         nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
         this._addLiveTextDoc = addLiveText;
         this._getTransform = getTransform;
-        this._addDocument = addDocument;
+        this._addDocument = addDocument || returnFalse;
         this._nudge = nudge;
         this.Visible = true;
     }
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 4e30709a6..67f73d44a 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -46,8 +46,8 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
                 onDoubleClick={this.onChildDoubleClick}
                 onClick={onChildClick}
                 renderDepth={this.props.renderDepth + 1}
-                LayoutTemplate={this.props.ChildLayoutTemplate}
-                LayoutTemplateString={this.props.ChildLayoutString}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
                 Document={childPair.layout}
                 DataDoc={childPair.data}
                 PanelWidth={this.panelWidth}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 16ccdc6f4..8b7a46e2f 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -53,8 +53,8 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
                         renderDepth={this.props.renderDepth + 1}
-                        LayoutTemplate={this.props.ChildLayoutTemplate}
-                        LayoutTemplateString={this.props.ChildLayoutString}
+                        LayoutTemplate={this.props.childLayoutTemplate}
+                        LayoutTemplateString={this.props.childLayoutString}
                         Document={curDoc.layout}
                         DataDoc={curDoc.data}
                         PanelHeight={this.panelHeight}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index b35644c6b..ff69c7d05 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -146,7 +146,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         const onLayoutDoc = this.onLayoutDoc(key);
         (onLayoutDoc ? newDoc : newDoc[DataSym])[key] = this.getValue(this.props.heading);
         const docs = this.props.parent.childDocList;
-        return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument(newDoc);
+        return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument?.(newDoc) || false;
     }
 
     deleteRow = undoBatch(action(() => {
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 2636b98e5..f054e7b7f 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -36,11 +36,11 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
             <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine}
                 addDocument={undoBatch((doc: Doc | Doc[]) => {
                     (doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d));
-                    return this.props.addDocument(doc);
+                    return this.props.addDocument?.(doc) || false;
                 })}
                 moveDocument={undoBatch((doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
                     (doc instanceof Doc ? [doc] : doc).map(undoBatch((d) => Doc.deiconifyView(d)));
-                    return this.props.moveDocument(doc, targetCollection, addDoc);
+                    return this.props.moveDocument?.(doc, targetCollection, addDoc) || false;
                 })} />
         </div>;
     }
@@ -91,7 +91,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
                     const doc = this.childDocs[0];
                     doc.x = e.clientX;
                     doc.y = e.clientY;
-                    this.props.addDocTab(doc, "inParent") && this.props.removeDocument(doc);
+                    this.props.addDocTab(doc, "inParent") && (this.props.removeDocument?.(doc) || false);
                     dist = 0;
                 }
             }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 0b3dfe1e4..904b4c761 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -44,7 +44,7 @@ export interface CellProps {
     renderDepth: number;
     addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined,
+    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined,
         addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     isFocused: boolean;
     changeFocusedCellByIndex: (row: number, col: number) => void;
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index b408fd680..f50da0134 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -461,9 +461,6 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         }
     }
 
-
-    get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; }
-
     @computed get scriptField() {
         const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
         const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8ae70ce20..e22b8effe 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -484,7 +484,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             const cm = ContextMenu.Instance;
             const options = cm.findByDescription("Options...");
             const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-            optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument(this._previewDoc), icon: "trash" });
+            optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: "trash" });
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
             cm.displayMenu(e.clientX, e.clientY);
             (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0e4029764..83ac138f5 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -152,7 +152,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
 
     @action
     moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
-        return this.props.removeDocument(doc) && addDocument(doc);
+        return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
     }
     createRef = (ele: HTMLDivElement | null) => {
         this._masonryGridRef = ele;
@@ -193,9 +193,9 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             Document={doc}
             DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
             styleProvider={this.props.styleProvider}
-            LayoutTemplate={this.props.ChildLayoutTemplate}
-            LayoutTemplateString={this.props.ChildLayoutString}
-            FreezeDimensions={this.props.freezeChildDimensions}
+            LayoutTemplate={this.props.childLayoutTemplate}
+            LayoutTemplateString={this.props.childLayoutString}
+            FreezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
@@ -232,14 +232,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
 
     getDocWidth(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const nw = Doc.NativeWidth(layoutDoc);
         return Math.min(nw && !this.layoutDoc._columnsFill ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
         const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
-        const childLayoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
+        const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index b7562c45e..ec6458d00 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -10,7 +10,7 @@ import { ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { ImageField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, setupMoveUpEvents, returnFalse } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DragManager } from "../../util/DragManager";
@@ -146,7 +146,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         newDoc.heading = heading;
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? "" : " ";
-        return this.props.parent.props.addDocument(newDoc);
+        return this.props.parent.props.addDocument?.(newDoc) || false;
     }
 
     @action
@@ -238,8 +238,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
         DocUtils.addDocumentCreatorMenuItems((doc) => {
             FormattedTextBox.SelectOnLoad = doc[Id];
-            return this.props.parent.props.addDocument(doc);
-        }, this.props.parent.props.addDocument, x, y, true);
+            return this.props.parent.props.addDocument?.(doc) || false;
+        }, this.props.parent.props.addDocument || returnFalse, x, y, true);
 
         Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof (dataDoc[fieldKey]) === "string").map(fieldKey =>
             docItems.push({
@@ -249,7 +249,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                         if (this.props.parent.Document.isTemplateDoc) {
                             Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
                         }
-                        return this.props.parent.props.addDocument(created);
+                        return this.props.parent.props.addDocument?.(created) || false;
                     }
                 }, icon: "compress-arrows-alt"
             }));
@@ -263,7 +263,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                             Doc.MakeMetadataFieldTemplate(created, container);
                             return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
                         }
-                        return this.props.parent.props.addDocument(created);
+                        return this.props.parent.props.addDocument?.(created) || false;
                     }
                 }, icon: "compress-arrows-alt"
             }));
@@ -276,7 +276,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                 if (this.props.parent.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
                 }
-                this.props.parent.props.addDocument(created);
+                this.props.parent.props.addDocument?.(created);
             }
         });
         const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 10459a497..592d6ab87 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -8,48 +8,20 @@ import { ScriptField } from "../../../fields/ScriptField";
 import { WebField } from "../../../fields/URLField";
 import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
+import { Utils, returnFalse } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Networking } from "../../Network";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { InteractionUtils } from "../../util/InteractionUtils";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocComponent } from "../DocComponent";
-import { FieldViewProps } from "../nodes/FieldView";
 import React = require("react");
 import * as rp from 'request-promise';
 import ReactLoading from 'react-loading';
 
-export interface CollectionViewProps extends FieldViewProps {
-    addDocument: (document: Doc | Doc[]) => boolean;
-    removeDocument: (document: Doc | Doc[]) => boolean;
-    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    VisibleHeight?: () => number;
-    setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
-    rootSelected: (outsideReaction?: boolean) => boolean;
-    fieldKey: string;
-    NativeWidth?: () => number;
-    NativeHeight?: () => number;
-}
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
-    children?: never | (() => JSX.Element[]) | React.ReactNode;
-    ChildLayoutTemplate?: () => Doc;
-    childOpacity?: () => number;
-    childIgnoreNativeSize?: boolean;
-    ChildLayoutString?: string;
-    childClickScript?: ScriptField;
-    childDoubleClickScript?: ScriptField;
-    freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
-    overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
-    ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox)
-    parentActive: (outsideReaction: boolean) => boolean;
-    isAnnotationOverlay?: boolean;
-    annotationsKey: string;
-    layoutEngine?: () => string;
 }
 
 export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
@@ -112,11 +84,11 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             return Cast(this.dataField, listSpec(Doc));
         }
         docFilters = () => {
-            return this.props.ignoreFields?.includes("_docFilters") ? [] :
+            return this.props.ignoreFilters ? [] :
                 [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
         }
         docRangeFilters = () => {
-            return this.props.ignoreFields?.includes("_docRangeFilters") ? [] :
+            return this.props.ignoreFilters ? [] :
                 [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])];
         }
         searchFilterDocs = () => {
@@ -220,7 +192,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             }
         }
 
-        addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc);
+        addDocument = (doc: Doc | Doc[]) => this.props.addDocument?.(doc) || false;
 
         @action
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
@@ -329,7 +301,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                             DocServer.GetRefField(docid).then(f => {
                                 if (f instanceof Doc) {
                                     if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
-                                    (f instanceof Doc) && this.props.addDocument(f);
+                                    (f instanceof Doc) && this.addDocument(f);
                                 }
                             });
                         } else {
@@ -343,7 +315,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                             const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
                             const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 });
                             Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text;
-                            this.props.addDocument(htmlDoc);
+                            this.addDocument(htmlDoc);
                             if (srcWeb) {
                                 const iframe = SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")?.[0];
                                 const focusNode = (iframe?.contentDocument?.getSelection()?.focusNode as any);
@@ -503,7 +475,7 @@ import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
-import { CollectionView, CollectionViewType } from "./CollectionView";
+import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
 import { SelectionManager } from "../../util/SelectionManager";
 import { OverlayView } from "../OverlayView";
 import { setTimeout } from "timers";
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index c2d682361..e4a3c384c 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -85,7 +85,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}>
-            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={true} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} childFreezeDimensions={true} layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 681584cc5..265f5a323 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,10 +6,11 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { Document } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils';
+import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
+import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -17,20 +18,17 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from "../EditableView";
-import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
+import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { TreeView } from "./TreeView";
 import React = require("react");
-import { DocumentManager } from '../../util/DocumentManager';
-import { FormattedTextBoxComment } from '../nodes/formattedText/FormattedTextBoxComment';
-import { DocumentView } from '../nodes/DocumentView';
 
 export type collectionTreeViewProps = {
     treeViewHideTitle?: boolean;
     treeViewHideHeaderFields?: boolean;
+    treeViewSkipFields?: string[]; // prevents specific fields from being displayed (see LinkBox)
     onCheckedClick?: () => ScriptField;
     onChildClick?: () => ScriptField;
 };
@@ -92,7 +90,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         if (this.doc.resolvedDataDoc instanceof Promise) {
             this.doc.resolvedDataDoc.then((resolved: any) => doAddDoc(doc));
         } else if (relativeTo === undefined) {
-            this.props.addDocument(doc);
+            this.props.addDocument?.(doc);
         } else {
             doAddDoc(doc);
             (doc instanceof Doc ? [doc] : doc).forEach(d => d.context = this.props.Document);
@@ -195,18 +193,18 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
     @computed get treeChildren() {
         TraceMobx();
-        return this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
+        return this.props.childDocuments || this.childDocs;
     }
     @computed get treeViewElements() {
         TraceMobx();
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
-        const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
+        const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false;
         return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, this.props.ScreenToLocalTransform,
             this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
-            this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null));
+            this.onChildClick, this.props.treeViewSkipFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null));
     }
     @computed get titleBar() {
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8f402c427..af03e4ceb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -10,7 +10,7 @@ import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail, denormalizeEmail } from '../../../fields/util';
+import { denormalizeEmail, distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { returnFalse, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -38,10 +38,7 @@ import { SubCollectionViewProps } from './CollectionSubView';
 import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
-import { listSpec } from '../../../fields/Schema';
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
+import { ScriptField } from '../../../fields/ScriptField';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
@@ -64,28 +61,29 @@ export enum CollectionViewType {
     Grid = "grid",
     Pile = "pileup"
 }
-export interface CollectionViewCustomProps {
+export interface CollectionViewProps extends FieldViewProps {
+    isAnnotationOverlay?: boolean;  // is the collection an annotation overlay (eg an overlay on an image/video/etc)
+    annotationsKey: string;    // which data field on the collection stores the collection of annotation documents
+    ignoreFilters?: boolean;
+    layoutEngine?: () => string;
+    parentActive: (outsideReaction: boolean) => boolean;
     filterAddDocument?: (doc: Doc | Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    VisibleHeight?: () => number;
+    setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+
+    // property overrides for child documents
+    childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childOpacity?: () => number;
-    hideFilter?: true;
+    childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
+    childLayoutString?: string;
+    children?: never | (() => JSX.Element[]) | React.ReactNode;
+    childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
     childIgnoreNativeSize?: boolean;
+    childClickScript?: ScriptField;
+    childDoubleClickScript?: ScriptField;
 }
-
-export interface CollectionRenderProps {
-    addDocument: (document: Doc | Doc[]) => boolean;
-    removeDocument: (document: Doc | Doc[]) => boolean;
-    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    active: () => boolean;
-    parentActive: (outsideReaction: boolean) => boolean;
-    whenActiveChanged: (isActive: boolean) => void;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    ChildLayoutTemplate?: () => Doc;// specify a layout Doc template to use for children of the collection
-    ChildLayoutString?: string;// specify a layout string to use for children of the collection
-}
-
 @observer
-export class CollectionView extends Touchable<FieldViewProps & CollectionViewCustomProps> {
+export class CollectionView extends Touchable<CollectionViewProps> {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
 
     _isChildActive = false;   //TODO should this be observable?
@@ -97,22 +95,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-    private AclMap = new Map<symbol, string>([
-        [AclPrivate, SharingPermissions.None],
-        [AclReadonly, SharingPermissions.View],
-        [AclAddonly, SharingPermissions.Add],
-        [AclEdit, SharingPermissions.Edit],
-        [AclAdmin, SharingPermissions.Admin]
-    ]);
-
     get collectionViewType(): CollectionViewType | undefined {
         const viewField = StrCast(this.props.Document._viewType);
         if (CollectionView._safeMode) {
-            if (viewField === CollectionViewType.Freeform || viewField === CollectionViewType.Schema) {
-                return CollectionViewType.Tree;
-            }
-            if (viewField === CollectionViewType.Invalid) {
-                return CollectionViewType.Freeform;
+            switch (viewField) {
+                case CollectionViewType.Freeform:
+                case CollectionViewType.Schema: return CollectionViewType.Tree;
+                case CollectionViewType.Invalid: return CollectionViewType.Freeform;
             }
         }
         return viewField as any as CollectionViewType;
@@ -136,7 +125,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
 
         const docs = doc instanceof Doc ? [doc] : doc;
 
-
         if (docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) return false;
         const targetDataDoc = this.props.Document[DataSym];
         const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
@@ -253,9 +241,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     }
 
     screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
-    private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
+    private SubView = (type: CollectionViewType, props: SubCollectionViewProps) => {
         TraceMobx();
-        const props: SubCollectionViewProps = { ...this.props, ...renderProps, ScreenToLocalTransform: this.screenToLocalTransform, CollectionView: this, annotationsKey: "" };
         switch (type) {
             case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
             case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
@@ -273,7 +260,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />);
             case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />);
             case CollectionViewType.Freeform:
-            default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} ChildLayoutString={props.ChildLayoutString} />); }
+            default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
         }
     }
 
@@ -386,7 +373,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
 
     render() {
         TraceMobx();
-        const props: CollectionRenderProps = {
+        const props: SubCollectionViewProps = {
+            ...this.props,
             addDocument: this.addDocument,
             removeDocument: this.removeDocument,
             moveDocument: this.moveDocument,
@@ -395,8 +383,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             parentActive: this.props.parentActive,
             PanelWidth: this.bodyPanelWidth,
             PanelHeight: this.props.PanelHeight,
-            ChildLayoutTemplate: this.childLayoutTemplate,
-            ChildLayoutString: this.childLayoutString,
+            childLayoutTemplate: this.childLayoutTemplate,
+            childLayoutString: this.childLayoutString,
+            ScreenToLocalTransform: this.screenToLocalTransform,
+            CollectionView: this,
+            annotationsKey: ""
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
             this.props.styleProvider?.(this.props.Document, this.props, "boxShadow");
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 664b6115a..b532d3302 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -62,9 +62,9 @@ export interface SchemaTableProps {
     ContainingCollectionDoc: Opt<Doc>;
     fieldKey: string;
     renderDepth: number;
-    deleteDocument: (document: Doc | Doc[]) => boolean;
-    addDocument: (document: Doc | Doc[]) => boolean;
-    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+    deleteDocument?: (document: Doc | Doc[]) => boolean;
+    addDocument?: (document: Doc | Doc[]) => boolean;
+    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: (outsideReaction: boolean | undefined) => boolean;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
@@ -376,7 +376,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
     @undoBatch
     createRow = action(() => {
-        this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
+        this.props.addDocument?.(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
         this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
     });
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e1fd47592..3c1aaae47 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -339,7 +339,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     parentActive={returnFalse}
-                    ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
+                    childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
                     noOverlay={true} // don't render overlay Docs since they won't scale
                     active={returnTrue}
                     select={emptyFunction}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 84b5af7be..0a97166f0 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -17,6 +17,10 @@
             left: -10px;
             position: absolute;
         }
+        .treeView-checkIcon {
+            left: -10px;
+            position: absolute;
+        }
         &:hover {
             .treeView-expandIcon {
                 display: unset;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 675ba60c0..be8a27ab0 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -49,7 +49,7 @@ export interface TreeViewProps {
     outdentDocument?: () => void;
     ScreenToLocalTransform: () => Transform;
     dontRegisterView?: boolean;
-    backgroundColor?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
+    backgroundColor?: (doc: Opt<Doc>, props: DocumentViewProps, property: string) => string | undefined;
     outerXf: () => { translateX: number, translateY: number };
     treeView: CollectionTreeView;
     parentKey: string;
@@ -59,7 +59,7 @@ export interface TreeViewProps {
     renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
     onCheckedClick?: () => ScriptField;
     onChildClick?: () => ScriptField;
-    ignoreFields?: string[];
+    skipFields?: string[];
     firstLevel: boolean;
     whenActiveChanged: (isActive: boolean) => void;
 }
@@ -311,7 +311,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
         for (const key of Object.keys(ids).slice().sort()) {
-            if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
+            if (this.props.skipFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
@@ -327,7 +327,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView);
+                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView);
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -406,7 +406,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
-                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)}
+                        [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}><div style={{ display: "inline-block" }} >
@@ -449,14 +449,14 @@ export class TreeView extends React.Component<TreeViewProps> {
                 !(this.doc.text as RichTextField)?.Text ? (null) :
                     <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> :
                 <div className="treeView-bulletIcons" >
-                    <div className="treeView-expandIcon">
-                        <FontAwesomeIcon size="sm" icon={this.outlineMode ? [this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"] :
+                    <div className={`treeView-${this.onCheckedClick ? "checkIcon" : "expandIcon"}`}>
+                        <FontAwesomeIcon size="sm" icon={
                             checked === "check" ? "check" :
                                 (checked === "x" ? "times" : checked === "unchecked" ? "square" :
                                     !this.treeViewOpen ? "caret-right" :
                                         "caret-down")} />
                     </div>
-                    <FontAwesomeIcon icon={iconType} />
+                    {this.onCheckedClick ? (null) : <FontAwesomeIcon icon={iconType} />}
                 </div>
             }
         </div>;
@@ -710,7 +710,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         dropAction: dropActionType,
         addDocTab: (doc: Doc, where: string) => boolean,
         pinToPres: (document: Doc) => void,
-        backgroundColor: undefined | ((document: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined),
+        styleProvider: undefined | ((document: Opt<Doc>, props: DocumentViewProps, property: string) => string | undefined),
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
         active: (outsideReaction?: boolean) => boolean,
@@ -722,7 +722,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         renderedIds: string[],
         onCheckedClick: undefined | (() => ScriptField),
         onChildClick: undefined | (() => ScriptField),
-        ignoreFields: string[] | undefined,
+        skipFields: string[] | undefined,
         firstLevel: boolean,
         whenActiveChanged: (isActive: boolean) => void,
         dontRegisterView: boolean | undefined) {
@@ -783,7 +783,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 renderDepth={renderDepth}
                 removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
                 addDocument={addDocument}
-                backgroundColor={backgroundColor}
+                backgroundColor={styleProvider}
                 panelWidth={rowWidth}
                 panelHeight={rowHeight}
                 ChromeHeight={ChromeHeight}
@@ -799,7 +799,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 treeViewHideHeaderFields={treeViewHideHeaderFields}
                 treeViewPreventOpen={treeViewPreventOpen}
                 renderedIds={renderedIds}
-                ignoreFields={ignoreFields}
+                skipFields={skipFields}
                 firstLevel={firstLevel}
                 whenActiveChanged={whenActiveChanged} />;
         });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 548ad78a5..9f6938e67 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,16 +1,16 @@
+import { computed } from "mobx";
 import { observer } from "mobx-react";
 import * as mobxUtils from 'mobx-utils';
 import CursorField from "../../../../fields/CursorField";
+import { FieldResult } from "../../../../fields/Doc";
+import { List } from "../../../../fields/List";
 import { listSpec } from "../../../../fields/Schema";
 import { Cast } from "../../../../fields/Types";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { CollectionViewProps } from "../CollectionSubView";
+import { CollectionViewProps } from "../CollectionView";
 import "./CollectionFreeFormView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { computed } from "mobx";
-import { FieldResult } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
 
 @observer
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 136600164..0f466fc16 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -158,11 +158,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     addDocument = action((newBox: Doc | Doc[]) => {
         let retVal = false;
         if (newBox instanceof Doc) {
-            retVal = this.props.addDocument(newBox);
+            retVal = this.props.addDocument?.(newBox) || false;
             retVal && this.bringToFront(newBox);
             retVal && this.updateCluster(newBox);
         } else {
-            retVal = this.props.addDocument(newBox);
+            retVal = this.props.addDocument?.(newBox) || false;
             // bcz: deal with clusters
         }
         if (retVal) {
@@ -270,7 +270,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             return false;
         } else {
             const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
-            this.props.addDocument(source);
+            this.props.addDocument?.(source);
             linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation", ""); // TODODO this is where in text links get passed
             e.stopPropagation();
             return true;
@@ -396,7 +396,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
+    getClusterColor = (doc: Opt<Doc>, props: DocumentViewProps, property: string) => {
         let clusterColor = this.props.styleProvider?.(doc, props, property);  // bcz: check 'props'  used to be renderDepth + 1
         if (property !== "backgroundColor") return clusterColor;
         const cluster = NumCast(doc?.cluster);
@@ -510,7 +510,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     return pass;
                 });
                 this.addDocument(Docs.Create.FreeformDocument(sel, { title: "nested collection", x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
-                sel.forEach(d => this.props.removeDocument(d));
+                sel.forEach(d => this.props.removeDocument?.(d));
                 e.stopPropagation();
                 break;
             case GestureUtils.Gestures.StartBracket:
@@ -999,9 +999,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             fitToBox: false,
             DataDoc: childData,
             Document: childLayout,
-            LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate,
-            LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString,
-            FreezeDimensions: this.props.freezeChildDimensions,
+            LayoutTemplate: childLayout.z ? undefined : this.props.childLayoutTemplate,
+            LayoutTemplateString: childLayout.z ? undefined : this.props.childLayoutString,
+            FreezeDimensions: this.props.childFreezeDimensions,
             setupDragLines: this.setupDragLines,
             dontRegisterView: this.props.dontRegisterView,
             rootSelected: childData ? this.rootSelected : returnFalse,
@@ -1032,14 +1032,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
                 doc.x = pt[0];
                 doc.y = pt[1];
-                return this.props.addDocument(doc);
+                return this.props.addDocument?.(doc) || false;
             } else {
                 (doc as any as Doc[]).forEach(doc => {
                     const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
                     doc.x = pt[0];
                     doc.y = pt[1];
                 });
-                return this.props.addDocument(doc);
+                return this.props.addDocument?.(doc) || false;
             }
         }
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
@@ -1172,8 +1172,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                         (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
                     jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))}
                     //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
-                    fitToBox={BoolCast(this.props.freezeChildDimensions)} // bcz: check this
-                    FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
+                    fitToBox={BoolCast(this.props.childFreezeDimensions)} // bcz: check this
+                    FreezeDimensions={BoolCast(this.props.childFreezeDimensions)}
                 />,
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a963b9158..39df606f7 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -8,7 +8,7 @@ import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl } from "../../../../fields/util";
-import { Utils, intersectRect } from "../../../../Utils";
+import { Utils, intersectRect, returnFalse } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -93,7 +93,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             e.stopPropagation();
         } else
             if (e.key === ":") {
-                DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y);
+                DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
 
                 cm.displayMenu(this._downX, this._downY);
                 e.stopPropagation();
@@ -121,7 +121,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                     ns.map(line => {
                         const indent = line.search(/\S|$/);
                         const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line });
-                        this.props.addDocument(newBox);
+                        this.props.addDocument?.(newBox);
                         ypos += 40 * this.Transform.Scale;
                     });
                 })();
@@ -143,11 +143,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 slide.x = x;
                 slide.y = y;
                 FormattedTextBox.SelectOnLoad = slide[Id];
-                this.props.addDocument(slide);
+                this.props.addDocument?.(slide);
                 //setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false));
                 e.stopPropagation();
             } else if (!e.ctrlKey && !e.metaKey && SelectionManager.SelectedDocuments().length < 2) {
-                FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.ChildLayoutString ? e.key : "";
+                FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.childLayoutString ? e.key : "";
                 FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
                 this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0));
                 e.stopPropagation();
@@ -191,7 +191,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }
             const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", _width: 300, _height: 100 });
 
-            this.props.addDocument(newCol);
+            this.props.addDocument?.(newCol);
         }
     }
 
@@ -333,7 +333,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     delete = () => {
         const selected = this.marqueeSelect(false);
         SelectionManager.DeselectAll();
-        selected.forEach(doc => this.props.removeDocument(doc));
+        selected.forEach(doc => this.props.removeDocument?.(doc));
 
         this.cleanupInteractions(false);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -363,9 +363,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => {
         const selected = this.marqueeSelect(false);
         SelectionManager.DeselectAll();
-        selected.forEach(d => this.props.removeDocument(d));
+        selected.forEach(d => this.props.removeDocument?.(d));
         const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
-        this.props.addDocument(newCollection!);
+        this.props.addDocument?.(newCollection!);
         this.props.selectDocuments([newCollection!]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
@@ -424,10 +424,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 d.y = dy - this.Bounds.top - this.Bounds.height / 2;
                 return d;
             }));
-            this.props.removeDocument(selected);
+            this.props.removeDocument?.(selected);
         }
         const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, []);
-        this.props.addDocument(newCollection);
+        this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
@@ -494,7 +494,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 // }
                 const lines = results.filter((r: any) => r.category === "line");
                 const text = lines.map((l: any) => l.recognizedText).join("\r\n");
-                this.props.addDocument(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
+                this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
             });
         }
     }
@@ -503,7 +503,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @action
     summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
         const selected = this.marqueeSelect(false).map(d => {
-            this.props.removeDocument(d);
+            this.props.removeDocument?.(d);
             d.x = NumCast(d.x) - this.Bounds.left;
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
@@ -524,7 +524,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
         const newCollection = this.getCollection([], undefined, ["background"]);
-        this.props.addDocument(newCollection);
+        this.props.addDocument?.(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
         setTimeout(() => this.props.selectDocuments([newCollection]));
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 7ce269c92..586c5d1df 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -217,9 +217,9 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            LayoutTemplate={this.props.ChildLayoutTemplate}
-            LayoutTemplateString={this.props.ChildLayoutString}
-            FreezeDimensions={this.props.freezeChildDimensions}
+            LayoutTemplate={this.props.childLayoutTemplate}
+            LayoutTemplateString={this.props.childLayoutString}
+            FreezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 7700ea128..87c56de6f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -217,9 +217,9 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            LayoutTemplate={this.props.ChildLayoutTemplate}
-            LayoutTemplateString={this.props.ChildLayoutString}
-            FreezeDimensions={this.props.freezeChildDimensions}
+            LayoutTemplate={this.props.childLayoutTemplate}
+            LayoutTemplateString={this.props.childLayoutString}
+            FreezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5ed49185b..dc048843f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -45,57 +45,60 @@ import React = require("react");
 export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
 export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
 
-export interface DocumentViewProps {
+export interface DocumentViewSharedProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
-    docFilters: () => string[];
+    Document: Doc;
+    DataDoc?: Doc;
     contentsActive?: (setActive: () => boolean) => void;
+    onClick?: () => ScriptField;
+    dropAction?: dropActionType;
+    backgroundHalo?: (doc: Doc) => boolean;
+    docFilters: () => string[];
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
-    FreezeDimensions?: boolean;
+    rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
+    renderDepth: number;
+    addDocTab: (doc: Doc, where: string) => boolean;
+    addDocument?: (doc: Doc | Doc[]) => boolean;
+    removeDocument?: (doc: Doc | Doc[]) => boolean;
+    moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+    pinToPres: (document: Doc) => void;
+    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
+    styleProvider?: (doc: Opt<Doc>, props: DocumentViewProps, property: string) => any;
+    ScreenToLocalTransform: () => Transform;
+    bringToFront: (doc: Doc, sendToBack?: boolean) => void;
+    parentActive: (outsideReaction: boolean) => boolean;
+    whenActiveChanged: (isActive: boolean) => void;
+    LayoutTemplateString?: string;
+    dontRegisterView?: boolean;
+    focus: DocFocusFunc;
+    ignoreAutoHeight?: boolean;
+    PanelWidth: () => number;
+    PanelHeight: () => number;
     NativeWidth?: () => number;
     NativeHeight?: () => number;
-    Document: Doc;
-    DataDoc?: Doc;
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
+    ContentScaling: () => number;
+    ChromeHeight?: () => number;
+    pointerEvents?: string;
+}
+export interface DocumentViewProps extends DocumentViewSharedProps {
+    // properties specific to DocumentViews but not to FieldView
+    FreezeDimensions?: boolean;
+    fitToBox?: boolean;
+    treeViewDoc?: Doc;
+    dragDivName?: string;
+    contentsPointerEvents?: string;
     getView?: (view: DocumentView) => any;
-    LayoutTemplateString?: string;
     LayoutTemplate?: () => Opt<Doc>;
-    fitToBox?: boolean;
-    ignoreAutoHeight?: boolean;
     contextMenuItems?: () => { script: ScriptField, label: string }[];
-    rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
-    onClick?: () => ScriptField;
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
     onPointerUp?: () => ScriptField;
-    treeViewDoc?: Doc;
-    dropAction?: dropActionType;
-    dragDivName?: string;
     nudge?: (x: number, y: number) => void;
-    addDocument?: (doc: Doc | Doc[]) => boolean;
-    removeDocument?: (doc: Doc | Doc[]) => boolean;
-    moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    ScreenToLocalTransform: () => Transform;
     setupDragLines?: (snapToDraggedDoc: boolean) => void;
-    renderDepth: number;
-    ContentScaling: () => number;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    pointerEvents?: string;
-    contentsPointerEvents?: string;
-    focus: DocFocusFunc;
-    parentActive: (outsideReaction: boolean) => boolean;
-    whenActiveChanged: (isActive: boolean) => void;
-    bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    addDocTab: (doc: Doc, where: string) => boolean;
-    pinToPres: (document: Doc) => void;
-    backgroundHalo?: (doc: Doc) => boolean;
-    styleProvider?: (doc: Opt<Doc>, props: DocumentViewProps, property: string) => any;
     forceHideLinkButton?: () => boolean;
     opacity?: () => number | undefined;
-    ChromeHeight?: () => number;
-    dontRegisterView?: boolean;
     layoutKey?: string;
     radialMenu?: String[];
     display?: string;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 5cd752fdb..1d58893ae 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -2,70 +2,29 @@ import React = require("react");
 import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { DateField } from "../../../fields/DateField";
-import { Doc, FieldResult, Opt, Field } from "../../../fields/Doc";
+import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
-import { ScriptField } from "../../../fields/ScriptField";
-import { AudioField, VideoField, WebField } from "../../../fields/URLField";
-import { Transform } from "../../util/Transform";
-import { CollectionView } from "../collections/CollectionView";
-import { AudioBox } from "./AudioBox";
+import { VideoField, WebField } from "../../../fields/URLField";
+import { DocumentViewSharedProps } from "./DocumentView";
 import { VideoBox } from "./VideoBox";
-import { dropActionType } from "../../util/DragManager";
-import { DocAfterFocusFunc, DocFocusFunc, DocumentViewProps } from "./DocumentView";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
 // However, that only happens because the properties are "defined" in the markup for the field view.
 // See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc. 
 //
-export interface FieldViewProps {
+export interface FieldViewProps extends DocumentViewSharedProps {
+    // FieldView specific props that are not part of DocumentView props
     fieldKey: string;
     fitToBox?: boolean;
-    ContainingCollectionView: Opt<CollectionView>;
-    ContainingCollectionDoc: Opt<Doc>;
-    Document: Doc;
-    DataDoc?: Doc;
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
-    contentsActive?: (setActive: () => boolean) => void;
-    onClick?: () => ScriptField;
-    dropAction: dropActionType;
-    backgroundHalo?: () => boolean;
-    docFilters: () => string[];
-    docRangeFilters: () => string[];
-    searchFilterDocs: () => Doc[];
-    isSelected: (outsideReaction?: boolean) => boolean;
-    select: (isCtrlPressed: boolean) => void;
-    rootSelected: (outsideReaction?: boolean) => boolean;
-    renderDepth: number;
-    addDocument?: (document: Doc | Doc[]) => boolean;
-    addDocTab: (document: Doc, where: string) => boolean;
-    pinToPres: (document: Doc) => void;
-    removeDocument?: (document: Doc | Doc[]) => boolean;
-    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    styleProvider?: (document: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
-    ScreenToLocalTransform: () => Transform;
-    bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    parentActive: (outsideReaction: boolean) => boolean;
+    overflow?: boolean; // bcz: would like to think this can be avoided -- need to look at further
     active: (outsideReaction?: boolean) => boolean;
-    whenActiveChanged: (isActive: boolean) => void;
-    LayoutTemplateString?: string;
-    dontRegisterView?: boolean;
-    focus: DocFocusFunc;
-    presMultiSelect?: (doc: Doc) => void; //added for selecting multiple documents in a presentation
-    ignoreAutoHeight?: boolean;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    PanelPosition?: string;
-    overflow?: boolean;
-    NativeHeight?: () => number;
-    NativeWidth?: () => number;
-    setVideoBox?: (player: VideoBox) => void;
-    ContentScaling: () => number;
-    ChromeHeight?: () => number;
-    childLayoutTemplate?: () => Opt<Doc>;
+    select: (isCtrlPressed: boolean) => void;
+    isSelected: (outsideReaction?: boolean) => boolean;
+
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
-    fontSize?: number;
     pointerEvents?: string;
+    fontSize?: number;
     height?: number;
     width?: number;
     background?: string;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 0161f51fd..b90bf9eb1 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -9,7 +9,7 @@ import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec, makeInterface } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
 import { Cast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero, returnTrue } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -153,7 +153,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
         }
     }
     filterBackground = () => "rgba(105, 105, 105, 0.432)";
-    get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
     @computed get scriptField() {
         const scriptText = "setDocFilter(this?.target, heading, this.title, checked)";
         const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
@@ -181,7 +180,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
             </div>
             <div className="filterBox-tree" key="tree">
                 <CollectionTreeView
-                    PanelPosition={""}
                     Document={facetCollection}
                     DataDoc={Doc.GetProto(facetCollection)}
                     fieldKey={`${this.props.fieldKey}`}
@@ -202,7 +200,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     isSelected={returnFalse}
                     select={returnFalse}
                     bringToFront={emptyFunction}
-                    active={this.props.active}
+                    active={returnTrue}
                     parentActive={returnFalse}
                     whenActiveChanged={returnFalse}
                     treeViewHideTitle={true}
@@ -210,7 +208,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     focus={returnFalse}
                     treeViewHideHeaderFields={true}
                     onCheckedClick={this.scriptField}
-                    ignoreFields={this.ignoreFields}
+                    ignoreFilters={true} // otherwise, the filters would filter themselves out of the tree view
                     annotationsKey={""}
                     dontRegisterView={true}
                     styleProvider={this.filterBackground}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index bf19457da..50ad884ca 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -21,8 +21,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
 
             <CollectionTreeView {...this.props}
                 ChromeHeight={returnZero}
-                overrideDocuments={[this.dataDoc]}
-                ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
+                childDocuments={[this.dataDoc]}
+                treeViewSkipFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
                 annotationsKey={""}
                 dontRegisterView={true}
                 renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 5985225e3..c2385cab8 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -2412,6 +2412,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 <div className="presBox-listCont">
                     {mode !== CollectionViewType.Invalid ?
                         <CollectionView {...this.props}
+                            annotationsKey={""}
                             ContainingCollectionDoc={this.props.Document}
                             PanelWidth={this.props.PanelWidth}
                             PanelHeight={this.panelHeight}
@@ -2423,7 +2424,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                             removeDocument={returnFalse}
                             dontRegisterView={true}
                             focus={this.selectElement}
-                            ScreenToLocalTransform={this.getTransform} />
+                            ScreenToLocalTransform={this.getTransform}
+                        />
                         : (null)
                     }
                 </div>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 998ca839d..f64d80731 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -183,7 +183,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     componentDidMount() {
-        if (this.props.setVideoBox) this.props.setVideoBox(this);
         this._disposers.videoStart = reaction(
             () => this.Document._videoStart,
             (videoStart) => {
-- 
cgit v1.2.3-70-g09d2


From 30338491c7b3cbba852931c76774f6bfd44d4060 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Dec 2020 12:54:41 -0500
Subject: removed annotationsKey prop in favor of reusing fieldKey.  Fixed web
 pages to allow dash annotations again.

---
 src/client/views/MainView.tsx                              |  1 -
 src/client/views/TemplateMenu.tsx                          |  1 -
 src/client/views/collections/CollectionStackingView.tsx    |  4 ++--
 src/client/views/collections/CollectionSubView.tsx         | 14 ++++++--------
 src/client/views/collections/CollectionView.tsx            |  5 +----
 src/client/views/collections/TabDocView.tsx                |  1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx          | 11 +++++------
 .../views/collections/collectionFreeForm/MarqueeView.tsx   |  2 +-
 src/client/views/nodes/FilterBox.tsx                       |  2 --
 src/client/views/nodes/ImageBox.tsx                        |  2 +-
 src/client/views/nodes/LinkBox.tsx                         |  1 -
 src/client/views/nodes/PresBox.tsx                         |  1 -
 src/client/views/nodes/ScreenshotBox.tsx                   |  1 -
 src/client/views/nodes/VideoBox.tsx                        |  2 +-
 src/client/views/nodes/WebBox.scss                         |  4 +++-
 src/client/views/nodes/WebBox.tsx                          |  2 +-
 src/client/views/nodes/formattedText/FormattedTextBox.tsx  | 13 ++++++++-----
 src/client/views/pdf/PDFViewer.tsx                         |  4 ++--
 src/client/views/search/SearchBox.tsx                      |  1 -
 19 files changed, 31 insertions(+), 41 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9b3d5c6ae..2705d2c34 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -456,7 +456,6 @@ export class MainView extends React.Component {
                     DataDoc={undefined}
                     fieldKey={"data"}
                     dropAction={"alias"}
-                    annotationsKey={""}
                     parentActive={returnFalse}
                     styleProvider={TabDocView.styleProvider}
                     rootSelected={returnTrue}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 9c5c70c60..203274681 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -152,7 +152,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 PanelWidth={this.return100}
                 PanelHeight={this.return100}
                 treeViewHideHeaderFields={true}
-                annotationsKey={""}
                 dontRegisterView={true}
                 fieldKey={"data"}
                 moveDocument={returnFalse}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 83ac138f5..058536f69 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -352,8 +352,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
                         if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             const height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => NumCast(Doc.Layout(doc)._viewScale, 1) * Number(getComputedStyle(r).height.replace("px", "")))));
-                            if (this.props.annotationsKey) {
-                                doc[this.props.annotationsKey + "-height"] = height;
+                            if (this.props.isAnnotationOverlay) {
+                                doc[this.props.fieldKey + "-height"] = height;
                             } else {
                                 Doc.Layout(doc)._height = height * NumCast(Doc.Layout(doc)._viewScale, 1);
                             }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 592d6ab87..38e461e46 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -67,14 +67,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly
             // setTimeout changes it outside of the @computed section
             setTimeout(() => {
-                if (!this.dataDoc[this.props.annotationsKey || this.props.fieldKey]) this.dataDoc[this.props.annotationsKey || this.props.fieldKey] = new List<Doc>();
+                if (!this.dataDoc[this.props.fieldKey]) this.dataDoc[this.props.fieldKey] = new List<Doc>();
             }, 1000);
-            return this.dataDoc[this.props.annotationsKey || this.props.fieldKey];
+            return this.dataDoc[this.props.fieldKey];
         }
 
         get childLayoutPairs(): { layout: Doc; data: Doc; }[] {
             const { Document, DataDoc } = this.props;
-            const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)).
+            const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)).
                 filter(pair => {  // filter out any documents that have a proto that we don't have permissions to (which we determine by not having any keys
                     return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length));
                 });
@@ -84,12 +84,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             return Cast(this.dataField, listSpec(Doc));
         }
         docFilters = () => {
-            return this.props.ignoreFilters ? [] :
-                [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
+            return [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
         }
         docRangeFilters = () => {
-            return this.props.ignoreFilters ? [] :
-                [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])];
+            return [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])];
         }
         searchFilterDocs = () => {
             return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
@@ -104,7 +102,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
                 // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image.
                 const rootDoc = Cast(this.props.Document.rootDocument, Doc, null);
-                rawdocs = rootDoc && !this.props.annotationsKey ? [Doc.GetProto(rootDoc)] : [];
+                rawdocs = rootDoc && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDoc)] : [];
             }
 
             const docs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate).map(d => d as Doc);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index af03e4ceb..511fc723d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -63,8 +63,6 @@ export enum CollectionViewType {
 }
 export interface CollectionViewProps extends FieldViewProps {
     isAnnotationOverlay?: boolean;  // is the collection an annotation overlay (eg an overlay on an image/video/etc)
-    annotationsKey: string;    // which data field on the collection stores the collection of annotation documents
-    ignoreFilters?: boolean;
     layoutEngine?: () => string;
     parentActive: (outsideReaction: boolean) => boolean;
     filterAddDocument?: (doc: Doc | Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
@@ -72,11 +70,11 @@ export interface CollectionViewProps extends FieldViewProps {
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
 
     // property overrides for child documents
+    children?: never | (() => JSX.Element[]) | React.ReactNode;
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childOpacity?: () => number;
     childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
     childLayoutString?: string;
-    children?: never | (() => JSX.Element[]) | React.ReactNode;
     childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
     childIgnoreNativeSize?: boolean;
     childClickScript?: ScriptField;
@@ -387,7 +385,6 @@ export class CollectionView extends Touchable<CollectionViewProps> {
             childLayoutString: this.childLayoutString,
             ScreenToLocalTransform: this.screenToLocalTransform,
             CollectionView: this,
-            annotationsKey: ""
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
             this.props.styleProvider?.(this.props.Document, this.props, "boxShadow");
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 3c1aaae47..fd50c6273 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -346,7 +346,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     dropAction={undefined}
                     isSelected={returnFalse}
                     dontRegisterView={true}
-                    annotationsKey={""}
                     fieldKey={Doc.LayoutFieldKey(this._document!)}
                     bringToFront={emptyFunction}
                     rootSelected={returnTrue}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0f466fc16..e09abd017 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -47,7 +47,6 @@ import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { isUndefined } from "lodash";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -950,7 +949,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
 
-            if (DocListCast(this.dataDoc[this.props.annotationsKey || this.props.fieldKey]).includes(doc)) {
+            if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
                 // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
                 if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
             }
@@ -1193,7 +1192,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
             (elements) => this._layoutElements = elements || [],
             { fireImmediately: true, name: "doLayout" });
-        if (!this.props.annotationsKey) {
+        if (!this.props.isAnnotationOverlay) {
             this._boundsReaction = reaction(() => this.contentBounds,
                 bounds => (!this.fitToContent && this._layoutElements?.length) && setTimeout(() => {
                     const rbounds = Cast(this.Document._renderContentBounds, listSpec("number"), [0, 0, 0, 0]);
@@ -1291,7 +1290,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private thumbIdentifier?: number;
 
     onContextMenu = (e: React.MouseEvent) => {
-        if (this.props.annotationsKey || this.props.Document.annotationOn || !ContextMenu.Instance) return;
+        if (this.props.isAnnotationOverlay || this.props.Document.annotationOn || !ContextMenu.Instance) return;
 
         const appearance = ContextMenu.Instance.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
@@ -1527,7 +1526,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
 
     @computed get contentScaling() {
-        if (this.props.annotationsKey && !this.props.forceScaling) return 0;
+        if (this.props.isAnnotationOverlay && !this.props.forceScaling) return 0;
         const nw = returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document));
         const nh = returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document));
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
@@ -1555,7 +1554,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 width: this.contentScaling ? `${100 / this.contentScaling}%` : "",
                 height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
             }}>
-            {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
+            {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ?
                 this.placeholder : this.marqueeView}
             {!this.props.noOverlay ? <CollectionFreeFormOverlayView elements={this.elementFunc} /> : (null)}
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 39df606f7..52310d628 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -634,7 +634,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     render() {
         return <div className="marqueeView"
-            style={{ overflow: !this.props.ContainingCollectionView && this.props.annotationsKey ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
+            style={{ overflow: !this.props.ContainingCollectionView && this.props.isAnnotationOverlay ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
             onDragOver={e => e.preventDefault()}
             onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this._visible ? this.marqueeDiv : null}
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index b90bf9eb1..ffe1684d4 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -208,8 +208,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     focus={returnFalse}
                     treeViewHideHeaderFields={true}
                     onCheckedClick={this.scriptField}
-                    ignoreFilters={true} // otherwise, the filters would filter themselves out of the tree view
-                    annotationsKey={""}
                     dontRegisterView={true}
                     styleProvider={this.filterBackground}
                     moveDocument={returnFalse}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 1c1a13061..44df00709 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -417,7 +417,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 forceScaling={true}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
-                annotationsKey={this.annotationKey}
+                fieldKey={this.annotationKey}
                 isAnnotationOverlay={true}
                 focus={this.props.focus}
                 isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 50ad884ca..b3fd13a77 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -23,7 +23,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
                 ChromeHeight={returnZero}
                 childDocuments={[this.dataDoc]}
                 treeViewSkipFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
-                annotationsKey={""}
                 dontRegisterView={true}
                 renderDepth={this.props.renderDepth + 1}
                 CollectionView={undefined}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index c2385cab8..da74edbe3 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -2412,7 +2412,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 <div className="presBox-listCont">
                     {mode !== CollectionViewType.Invalid ?
                         <CollectionView {...this.props}
-                            annotationsKey={""}
                             ContainingCollectionDoc={this.props.Document}
                             PanelWidth={this.props.PanelWidth}
                             PanelHeight={this.panelHeight}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index f467fef12..713511a94 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -172,7 +172,6 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
                 <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.props.PanelWidth}
-                    annotationsKey={""}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
                     isAnnotationOverlay={true}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index f64d80731..8d10768ff 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -429,7 +429,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     forceScaling={true}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.props.PanelWidth}
-                    annotationsKey={this.annotationKey}
+                    fieldKey={this.annotationKey}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
                     isAnnotationOverlay={true}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index ea822f553..9d9c8a883 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -40,7 +40,6 @@
         .webBox-cont {
             pointer-events: none;
         }
-
         .webBox-cont,
         .webBox-cont-interactive {
             padding: 0vw;
@@ -73,6 +72,9 @@
             top: 0;
             left: 0;
             overflow: auto;
+            .webBox-innerContent {
+                position: relative;
+            }
         }
 
         div.webBox-outerContent::-webkit-scrollbar-thumb {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 9a1ae336f..197ff20d5 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -699,7 +699,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                         <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                             PanelHeight={this.props.PanelHeight}
                             PanelWidth={this.props.PanelWidth}
-                            annotationsKey={this.annotationKey}
+                            fieldKey={this.annotationKey}
                             VisibleHeight={this.visibleHeight}
                             focus={this.props.focus}
                             setPreviewCursor={this.setPreviewCursor}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 22e3ac1e9..33515e475 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -47,7 +47,7 @@ import { FootnoteView } from "./FootnoteView";
 import { schema } from "./schema_rts";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionFreeFormView, collectionFreeformViewProps } from '../../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { ViewBoxAnnotatableComponent } from "../../DocComponent";
@@ -59,9 +59,10 @@ import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark }
 import React = require("react");
 import { DocumentManager } from '../../../util/DocumentManager';
 import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { CollectionViewType } from '../../collections/CollectionView';
+import { CollectionViewType, CollectionViewProps } from '../../collections/CollectionView';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { LinkDocPreview } from '../LinkDocPreview';
+import { SubCollectionViewProps } from '../../collections/CollectionSubView';
 
 export interface FormattedTextBoxProps {
     makeLink?: () => Opt<Doc>;  // bcz: hack: notifies the text document when the container has made a link.  allows the text doc to react and setup a hyeprlink for any selected text
@@ -1606,7 +1607,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     sidebarContentScaling = () => this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
     @computed get sidebarCollection() {
         const fitToBox = this.props.Document._fitToBox;
-        const collectionProps = {
+        const collectionProps: SubCollectionViewProps & collectionFreeformViewProps = {
             ...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit,
             NativeWidth: returnZero,
             NativeHeight: returnZero,
@@ -1616,8 +1617,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             yMargin: 0,
             chromeStatus: "enabled",
             scaleField: this.annotationKey + "-scale",
-            annotationsKey: this.annotationKey,
             isAnnotationOverlay: true,
+            fieldKey: this.annotationKey,
             fitToBox: fitToBox,
             focus: this.props.focus,
             isSelected: this.props.isSelected,
@@ -1636,7 +1637,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         return !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
             <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
                 style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-                {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? <CollectionFreeFormView {...collectionProps} /> : <CollectionStackingView {...collectionProps} />}
+                {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ?
+                    <CollectionFreeFormView {...collectionProps} /> :
+                    <CollectionStackingView {...collectionProps} />}
             </div>;
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9b689eb6b..633a34c5e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -736,7 +736,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 transform: `scale(${this._zoomed})`
             }}>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                annotationsKey={this.annotationKey}
+                isAnnotationOverlay={true}
+                fieldKey={this.annotationKey}
                 setPreviewCursor={this.setPreviewCursor}
                 PanelHeight={this.panelWidth}
                 PanelWidth={this.panelHeight}
@@ -744,7 +745,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 VisibleHeight={this.visibleHeight}
                 focus={this.props.focus}
                 isSelected={this.props.isSelected}
-                isAnnotationOverlay={true}
                 select={emptyFunction}
                 active={this.annotationsActive}
                 ContentScaling={this.contentZoom}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3adeb6133..d3fee85a7 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -559,7 +559,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                             <div style={{ display: this.open ? "flex" : "none", overflow: "auto", position: "absolute" }}>
                                 <CollectionSchemaView {...this.props}
                                     CollectionView={undefined}
-                                    annotationsKey={""}
                                     addDocument={returnFalse}
                                     Document={this.props.Document}
                                     moveDocument={returnFalse}
-- 
cgit v1.2.3-70-g09d2


From d913740b994670b1b0eadd52069aeb69ef2b2380 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Dec 2020 14:18:53 -0500
Subject: more cleanup/organization of 'props' .  removed VisibleHeight props
 and cleaned up PDF and other props

---
 src/client/util/CurrentUserUtils.ts                |  1 -
 .../util/Import & Export/DirectoryImportBox.tsx    |  1 -
 src/client/views/PropertiesView.tsx                |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../views/collections/CollectionStackingView.tsx   | 21 +++--
 .../views/collections/CollectionTimeView.tsx       |  7 +-
 src/client/views/collections/CollectionView.tsx    | 36 ++++----
 src/client/views/collections/SchemaTable.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 16 ++--
 .../collectionGrid/CollectionGridView.tsx          |  2 +-
 .../CollectionMulticolumnView.tsx                  |  2 +-
 .../CollectionMultirowView.tsx                     |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  4 +-
 src/client/views/nodes/KeyValueBox.tsx             |  1 -
 src/client/views/nodes/PDFBox.tsx                  | 96 +++++-----------------
 src/client/views/nodes/VideoBox.tsx                | 11 +--
 src/client/views/nodes/WebBox.tsx                  | 19 +----
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 -
 src/client/views/pdf/PDFViewer.tsx                 | 50 +++--------
 src/fields/documentSchemas.ts                      |  2 -
 22 files changed, 84 insertions(+), 200 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 213bc3d88..676e7ce3e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -801,7 +801,6 @@ export class CurrentUserUtils {
         }
     }
 
-
     static setupUserDoc(doc: Doc) {
         if (doc.myUserDoc === undefined) {
             doc.treeViewOpen = true;
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 7f01966b9..d9f010557 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -158,7 +158,6 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
                 importContainer = Docs.Create.SchemaDocument(headers, docs, options);
             }
             runInAction(() => this.phase = 'External: uploading files to Google Photos...');
-            importContainer._columnsStack = false;
             await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
             !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 08394cd97..233429f4d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -272,7 +272,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     treeViewDoc={undefined}
                     styleProvider={this.props.styleProvider}
                     fitToBox={true}
-                    FreezeDimensions={true}
+                    freezeDimensions={true}
                     dontCenter={"y"}
                     NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
                     NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index e22b8effe..c64048345 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -401,7 +401,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     Document={this.previewDocument}
                     DataDoc={undefined}
                     fitToBox={true}
-                    FreezeDimensions={true}
+                    freezeDimensions={true}
                     dontCenter={"y"}
                     focus={emptyFunction}
                     renderDepth={this.props.renderDepth}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 058536f69..b1c5dec84 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,7 +3,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import Switch from 'rc-switch';
 import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc";
 import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
@@ -12,23 +11,23 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
 import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll, returnVal } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, returnVal, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
+import { DocUtils } from "../../documents/Documents";
 import { DragManager, dropActionType } from "../../util/DragManager";
+import { SnappingManager } from "../../util/SnappingManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { EditableView } from "../EditableView";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { DocAfterFocusFunc } from "../nodes/DocumentView";
 import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
 import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { SnappingManager } from "../../util/SnappingManager";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocUtils } from "../../documents/Documents";
-import { DocAfterFocusFunc } from "../nodes/DocumentView";
 const _global = (window /* browser */ || global /* node */) as any;
 
 type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -56,7 +55,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
     @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, this.props.yMargin || NumCast(this.layoutDoc._yMargin, 5)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
-    @computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); }
+    @computed get isStackingView() { return this.layoutDoc._viewType === CollectionViewType.Stacking; }
     @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
     @computed get showAddAGroup() { return (this.pivotField && (this.chromeStatus !== 'view-mode' && this.chromeStatus !== 'disabled')); }
     @computed get columnWidth() {
@@ -192,13 +191,13 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         return <ContentFittingDocumentView
             Document={doc}
             DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
-            styleProvider={this.props.styleProvider}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            FreezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
+            styleProvider={this.props.styleProvider}
+            LayoutTemplate={this.props.childLayoutTemplate}
+            LayoutTemplateString={this.props.childLayoutString}
+            freezeDimensions={this.props.childFreezeDimensions}
             NativeWidth={this.props.childIgnoreNativeSize ? returnZero : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
             NativeHeight={this.props.childIgnoreNativeSize ? returnZero : undefined}
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : ""}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index e4a3c384c..c7a826669 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -85,7 +85,12 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}>
-            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} childFreezeDimensions={true} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props}
+                fitToBox={true}
+                childClickScript={this._childClickedScript}
+                viewDefDivClick={this._viewDefDivClick}
+                childFreezeDimensions={true}
+                layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 511fc723d..dbbb3e991 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -66,7 +66,6 @@ export interface CollectionViewProps extends FieldViewProps {
     layoutEngine?: () => string;
     parentActive: (outsideReaction: boolean) => boolean;
     filterAddDocument?: (doc: Doc | Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
-    VisibleHeight?: () => number;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
 
     // property overrides for child documents
@@ -242,28 +241,27 @@ export class CollectionView extends Touchable<CollectionViewProps> {
     private SubView = (type: CollectionViewType, props: SubCollectionViewProps) => {
         TraceMobx();
         switch (type) {
-            case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
-            case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
-            case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
-            //case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />);
-            case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView key="collview" {...props} />);
-            case CollectionViewType.Multirow: return (<CollectionMultirowView key="rpwview" {...props} />);
-            case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
-            case CollectionViewType.Pile: { return (<CollectionPileView key="collview" {...props} />); }
-            case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
-            case CollectionViewType.Carousel3D: { return (<CollectionCarousel3DView key="collview" {...props} />); }
-            case CollectionViewType.Stacking: { this.props.Document._columnsStack = true; return (<CollectionStackingView key="collview" {...props} />); }
-            case CollectionViewType.Masonry: { this.props.Document._columnsStack = false; return (<CollectionStackingView key="collview" {...props} />); }
-            case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); }
-            case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />);
-            case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />);
-            case CollectionViewType.Freeform:
-            default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
+            default:
+            case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
+            case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
+            case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
+            case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
+            case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
+            case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
+            case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
+            case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
+            case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
+            case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
+            case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
+            case CollectionViewType.Map: return <CollectionMapView key="collview" {...props} />;
+            case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
+            //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
         }
     }
 
     setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
-
         const subItems: ContextMenuProps[] = [];
         subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
         if (addExtras && CollectionView._safeMode) {
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index b532d3302..24a67faaf 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -571,7 +571,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         Document={this._showDoc}
                         DataDoc={this._showDataDoc}
                         fitToBox={true}
-                        FreezeDimensions={true}
+                        freezeDimensions={true}
                         focus={emptyFunction}
                         renderDepth={this.props.renderDepth}
                         rootSelected={() => false}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e09abd017..94de40bc8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -918,7 +918,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 !dontCenter && this.props.focus(doc);
                 afterFocus && setTimeout(afterFocus, delay);
             } else {
-                const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height);
+                const contextHgt = NumCast(annotOn._height);
                 const curScroll = NumCast(this.props.Document._scrollTop);
                 let scrollTo = curScroll;
                 if (curScroll + contextHgt < NumCast(doc.y)) {
@@ -998,30 +998,30 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             fitToBox: false,
             DataDoc: childData,
             Document: childLayout,
+            ContainingCollectionView: this.props.CollectionView,
+            ContainingCollectionDoc: this.props.Document,
             LayoutTemplate: childLayout.z ? undefined : this.props.childLayoutTemplate,
             LayoutTemplateString: childLayout.z ? undefined : this.props.childLayoutString,
-            FreezeDimensions: this.props.childFreezeDimensions,
             setupDragLines: this.setupDragLines,
-            dontRegisterView: this.props.dontRegisterView,
             rootSelected: childData ? this.rootSelected : returnFalse,
-            dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
             onClick: this.onChildClickHandler,
             onDoubleClick: this.onChildDoubleClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
-            renderDepth: this.props.renderDepth + 1,
             PanelWidth: childLayout[WidthSym],
             PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
-            ContainingCollectionView: this.props.CollectionView,
-            ContainingCollectionDoc: this.props.Document,
             docFilters: this.docFilters,
             docRangeFilters: this.docRangeFilters,
             searchFilterDocs: this.searchFilterDocs,
             focus: this.focusDocument,
             styleProvider: this.getClusterColor,
             backgroundHalo: this.backgroundHalo,
+            freezeDimensions: this.props.childFreezeDimensions,
+            dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
             bringToFront: this.bringToFront,
             addDocTab: this.addDocTab,
+            renderDepth: this.props.renderDepth + 1,
+            dontRegisterView: this.props.dontRegisterView,
         };
     }
 
@@ -1172,7 +1172,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))}
                     //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
                     fitToBox={BoolCast(this.props.childFreezeDimensions)} // bcz: check this
-                    FreezeDimensions={BoolCast(this.props.childFreezeDimensions)}
+                    freezeDimensions={BoolCast(this.props.childFreezeDimensions)}
                 />,
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index f3e385746..bef197bee 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -170,7 +170,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             PanelWidth={width}
             PanelHeight={height}
             ContentScaling={returnOne}
-            FreezeDimensions={true}
+            freezeDimensions={true}
             fitToBox={true}
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 586c5d1df..d4913a5ed 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -219,7 +219,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             styleProvider={this.props.styleProvider}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            FreezeDimensions={this.props.childFreezeDimensions}
+            freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 87c56de6f..9039090d1 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -219,7 +219,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             styleProvider={this.props.styleProvider}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            FreezeDimensions={this.props.childFreezeDimensions}
+            freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index b4b887617..d68184da6 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -52,7 +52,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         const hgt = this.props.sizeProvider && this.sizeProvider ? this.sizeProvider.height : this.layoutDoc[HeightSym]();
         return (hgt === undefined && this.nativeWidth && this.nativeHeight) ? this.width * this.nativeHeight / this.nativeWidth : hgt;
     }
-    @computed get freezeDimensions() { return this.props.FreezeDimensions; }
+    @computed get freezeDimensions() { return this.props.freezeDimensions; }
     @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
     @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); }
     @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, undefined, this.freezeDimensions)); }
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 74d7cb24e..7b767a10a 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -22,7 +22,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
             (this.props.layoutKey && Doc.Layout(this.props.Document, Cast(this.props.Document[this.props.layoutKey], Doc, null))) ||
             Doc.Layout(this.props.Document);
     }
-    @computed get freezeDimensions() { return this.props.FreezeDimensions; }
+    @computed get freezeDimensions() { return this.props.freezeDimensions; }
     @computed get nativeWidth() { return !this.layoutDoc._fitWidth && returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions)); }
     @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 0); }
     @computed get nativeScaling() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dc048843f..595c3a950 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -84,7 +84,7 @@ export interface DocumentViewSharedProps {
 }
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
-    FreezeDimensions?: boolean;
+    freezeDimensions?: boolean;
     fitToBox?: boolean;
     treeViewDoc?: Doc;
     dragDivName?: string;
@@ -133,7 +133,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 undefined);
     }
     @computed get topMost() { return this.props.renderDepth === 0; }
-    @computed get freezeDimensions() { return this.props.FreezeDimensions; }
+    @computed get freezeDimensions() { return this.props.freezeDimensions; }
     @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
     @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
     @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 4ac932c24..8acf4081c 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -169,7 +169,6 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     getTemplate = async () => {
         const parent = Docs.Create.StackingDocument([], { _width: 800, _height: 800, title: "Template" });
-        parent._columnsStack = false;
         parent._columnWidth = 100;
         for (const row of this.rows.filter(row => row.isChecked)) {
             await this.createTemplateField(parent, row);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 42b24f6f6..a2f3fb2d1 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,28 +1,27 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable, runInAction, reaction, IReactionDisposer, trace, untracked, computed } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { Opt, WidthSym, Doc, HeightSym } from "../../../fields/Doc";
+import { Doc, Opt, WidthSym } from "../../../fields/Doc";
+import { documentSchema } from '../../../fields/documentSchemas';
 import { makeInterface } from "../../../fields/Schema";
-import { ScriptField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { PdfField, URLField } from "../../../fields/URLField";
+import { Cast, NumCast } from "../../../fields/Types";
+import { PdfField } from "../../../fields/URLField";
+import { TraceMobx } from '../../../fields/util';
 import { Utils } from '../../../Utils';
+import { KeyCodes } from '../../util/KeyCodes';
 import { undoBatch } from '../../util/UndoManager';
 import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionViewType } from '../collections/CollectionView';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { PDFViewer } from "../pdf/PDFViewer";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
-import { KeyCodes } from '../../util/KeyCodes';
 import "./PDFBox.scss";
 import React = require("react");
-import { documentSchema } from '../../../fields/documentSchemas';
-import { CollectionViewType } from '../collections/CollectionView';
-import { TraceMobx } from '../../../fields/util';
 
 type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -30,17 +29,11 @@ const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
-    private _keyValue: string = "";
-    private _valueValue: string = "";
-    private _scriptValue: string = "";
     private _searchString: string = "";
     private _initialScale: number = 0;  // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not.  Getting bigger after startup won't make it automatically be live.
     private _displayPdfLive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title
     private _pdfViewer: PDFViewer | undefined;
     private _searchRef = React.createRef<HTMLInputElement>();
-    private _keyRef = React.createRef<HTMLInputElement>();
-    private _valueRef = React.createRef<HTMLInputElement>();
-    private _scriptRef = React.createRef<HTMLInputElement>();
     private _selectReactionDisposer: IReactionDisposer | undefined;
 
     @observable private _searching: boolean = false;
@@ -130,25 +123,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
         }
     });
 
-    @undoBatch
-    @action
-    private applyFilter = () => {
-        const scriptText = this._scriptValue ? this._scriptValue :
-            this._keyValue && this._valueValue ? `this.${this._keyValue} === ${this._valueValue}` : "true";
-        this.props.Document.filterScript = ScriptField.MakeFunction(scriptText);
-    }
-
-    private resetFilters = () => {
-        this._keyValue = this._valueValue = this._scriptValue = "";
-        this._keyRef.current && (this._keyRef.current.value = "");
-        this._valueRef.current && (this._valueRef.current.value = "");
-        this._scriptRef.current && (this._scriptRef.current.value = "");
-        this.applyFilter();
-    }
-    private newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => this._keyValue = e.currentTarget.value;
-    private newValueChange = (e: React.ChangeEvent<HTMLInputElement>) => this._valueValue = e.currentTarget.value;
-    private newScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => this._scriptValue = e.currentTarget.value;
-
     whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
     setPdfViewer = (pdfViewer: PDFViewer) => { this._pdfViewer = pdfViewer; };
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
@@ -197,36 +171,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
                         onClick={action(() => this._pageControls = !this._pageControls)} />
                     {this._pageControls ? pageBtns : (null)}
                 </div>
-                {/* <div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}>
-                    <button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" >
-                        <div className="pdfBox-settingsButton-arrow" style={{ transform: `scaleX(${this._flyout ? -1 : 1})` }} />
-                        <div className="pdfBox-settingsButton-iconCont">
-                            <FontAwesomeIcon style={{ color: "white" }} icon="cog" size="lg" />
-                        </div>
-                    </button>
-                    <div className="pdfBox-settingsFlyout" style={{ right: `${this._flyout ? 20 : -1000}px` }} >
-                        <div className="pdfBox-settingsFlyout-title">
-                            Annotation View Settings
-                        </div>
-                        <div className="pdfBox-settingsFlyout-kvpInput">
-                            <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange} style={{ gridColumn: 1 }} ref={this._keyRef} />
-                            <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange} style={{ gridColumn: 3 }} ref={this._valueRef} />
-                        </div>
-                        <div className="pdfBox-settingsFlyout-kvpInput">
-                            <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
-                        </div>
-                        <div className="pdfBox-settingsFlyout-kvpInput">
-                            <button style={{ gridColumn: 1 }} onClick={this.resetFilters}>
-                                <FontAwesomeIcon style={{ color: "white" }} icon="trash" size="lg" />
-                                &nbsp; Reset Filters
-                            </button>
-                            <button style={{ gridColumn: 3 }} onClick={this.applyFilter}>
-                                <FontAwesomeIcon style={{ color: "white" }} icon="check" size="lg" />
-                                &nbsp; Apply
-                            </button>
-                        </div>
-                    </div>
-                </div> */}
             </div>);
     }
 
@@ -253,19 +197,19 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
     @computed get renderPdfView() {
         TraceMobx();
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
-        return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
-            <div className="pdfBox-background"></div>
-            <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
-                setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
-                renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
-                addDocTab={this.props.addDocTab} focus={this.props.focus} searchFilterDocs={this.props.searchFilterDocs}
-                docFilters={this.props.docFilters} docRangeFilters={this.props.docRangeFilters}
-                pinToPres={this.props.pinToPres} addDocument={this.addDocument}
-                Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
-                ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
-                isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
+        return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}
+            style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
+            <div className="pdfBox-background" />
+            <PDFViewer {...this.props}
+                pdf={this._pdf!}
+                url={pdfUrl!.url.pathname}
+                loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
+                setPdfViewer={this.setPdfViewer}
+                addDocument={this.addDocument}
+                whenActiveChanged={this.whenActiveChanged}
                 isChildActive={this.isChildActive}
-                fieldKey={this.props.fieldKey} startupLive={true} />
+                startupLive={true}
+            />
             {this.settingsPanel()}
         </div>;
     }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 8d10768ff..90a5dee23 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -427,11 +427,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
             <div className="videoBox-viewer" >
                 <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                     forceScaling={true}
-                    PanelHeight={this.props.PanelHeight}
-                    PanelWidth={this.props.PanelWidth}
                     fieldKey={this.annotationKey}
-                    focus={this.props.focus}
-                    isSelected={this.props.isSelected}
                     isAnnotationOverlay={true}
                     select={emptyFunction}
                     active={this.annotationsActive}
@@ -441,12 +437,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     moveDocument={this.moveDocument}
                     addDocument={this.addDocumentWithTimestamp}
                     CollectionView={undefined}
-                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                    renderDepth={this.props.renderDepth + 1}
-                    docFilters={this.props.docFilters}
-                    docRangeFilters={this.props.docRangeFilters}
-                    searchFilterDocs={this.props.searchFilterDocs}
-                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                    renderDepth={this.props.renderDepth + 1}>
                     {this.contentFunc}
                 </CollectionFreeFormView>
             </div>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 197ff20d5..47464d794 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -646,14 +646,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     marqueeX = () => this._marqueeX;
     marqueeY = () => this._marqueeY;
     marqueeing = () => this._marqueeing;
-    visibleHeight = () => {
-        if (this._mainCont.current) {
-            const boundingRect = this._mainCont.current.getBoundingClientRect();
-            const scaling = (Doc.NativeWidth(this.Document) || 0) / boundingRect.width;
-            return Math.min(boundingRect.height * scaling, this.props.PanelHeight() * scaling);
-        }
-        return this.props.PanelHeight();
-    }
     scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop));
     render() {
         const scaling = Number.isFinite(this.props.ContentScaling()) ? this.props.ContentScaling() || 1 : 1;
@@ -697,13 +689,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                         pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined
                     }}>
                         <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                            PanelHeight={this.props.PanelHeight}
-                            PanelWidth={this.props.PanelWidth}
                             fieldKey={this.annotationKey}
-                            VisibleHeight={this.visibleHeight}
-                            focus={this.props.focus}
                             setPreviewCursor={this.setPreviewCursor}
-                            isSelected={this.props.isSelected}
                             isAnnotationOverlay={true}
                             select={emptyFunction}
                             active={this.active}
@@ -714,11 +701,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             addDocument={this.addDocument}
                             CollectionView={undefined}
                             ScreenToLocalTransform={this.scrollXf}
-                            renderDepth={this.props.renderDepth + 1}
-                            docFilters={this.props.docFilters}
-                            docRangeFilters={this.props.docRangeFilters}
-                            searchFilterDocs={this.props.searchFilterDocs}
-                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                            renderDepth={this.props.renderDepth + 1}>
                         </CollectionFreeFormView>
                     </div>
                 </div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 33515e475..bfbd7805d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1620,8 +1620,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             isAnnotationOverlay: true,
             fieldKey: this.annotationKey,
             fitToBox: fitToBox,
-            focus: this.props.focus,
-            isSelected: this.props.isSelected,
             select: emptyFunction,
             active: this.annotationsActive,
             ContentScaling: this.sidebarContentScaling,
@@ -1632,7 +1630,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             CollectionView: undefined,
             ScreenToLocalTransform: this.sidebarScreenToLocal,
             renderDepth: this.props.renderDepth + 1,
-            ContainingCollectionDoc: this.props.ContainingCollectionDoc,
         };
         return !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
             <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 633a34c5e..199b77408 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { Dictionary } from "typescript-collections";
-import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym, Field } from "../../../fields/Doc";
+import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { InkTool } from "../../../fields/InkField";
@@ -13,29 +13,30 @@ import { ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { PdfField } from "../../../fields/URLField";
 import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils, OmitKeys } from "../../../Utils";
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { Networking } from "../../Network";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DragManager } from "../../util/DragManager";
 import { CompiledScript, CompileScript } from "../../util/Scripting";
 import { SelectionManager } from "../../util/SelectionManager";
+import { SharingManager } from "../../util/SharingManager";
 import { SnappingManager } from "../../util/SnappingManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionView } from "../collections/CollectionView";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+import { FormattedTextBoxComment } from "../nodes/formattedText/FormattedTextBoxComment";
+import { LinkDocPreview } from "../nodes/LinkDocPreview";
 import { Annotation } from "./Annotation";
 import { PDFMenu } from "./PDFMenu";
 import "./PDFViewer.scss";
 const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
 import React = require("react");
-import { LinkDocPreview } from "../nodes/LinkDocPreview";
-import { FormattedTextBoxComment } from "../nodes/formattedText/FormattedTextBoxComment";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SharingManager } from "../../util/SharingManager";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+import { FieldViewProps } from "../nodes/FieldView";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 const _global = (window /* browser */ || global /* node */) as any;
@@ -54,34 +55,13 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.4.456/b
 type PdfDocument = makeInterface<[typeof documentSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(documentSchema, pageSchema);
 
-interface IViewerProps {
+interface IViewerProps extends FieldViewProps {
     pdf: Pdfjs.PDFDocumentProxy;
     url: string;
-    fieldKey: string;
-    Document: Doc;
-    DataDoc?: Doc;
-    searchFilterDocs: () => Doc[];
-    ContainingCollectionView: Opt<CollectionView>;
-    docFilters: () => string[];
-    docRangeFilters: () => string[];
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    ContentScaling: () => number;
-    select: (isCtrlPressed: boolean) => void;
-    rootSelected: (outsideReaction?: boolean) => boolean;
     startupLive: boolean;
-    renderDepth: number;
-    focus: (doc: Doc) => void;
-    isSelected: (outsideReaction?: boolean) => boolean;
     loaded?: (nw: number, nh: number, np: number) => void;
-    active: (outsideReaction?: boolean) => boolean;
     isChildActive: (outsideReaction?: boolean) => boolean;
-    addDocTab: (document: Doc, where: string) => boolean;
-    pinToPres: (document: Doc, unpin?: boolean) => void;
-    addDocument?: (doc: Doc) => boolean;
     setPdfViewer: (view: PDFViewer) => void;
-    ScreenToLocalTransform: () => Transform;
-    whenActiveChanged: (isActive: boolean) => void;
 }
 
 /**
@@ -392,13 +372,12 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
         if (scrollToAnnotation) {
-            const offset = this.visibleHeight() / 2;
+            const offset = (this.props.PanelHeight() / this.props.ContentScaling()) / 2;
             this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset);
             Doc.linkFollowHighlight(scrollToAnnotation);
         }
     }
 
-
     pageDelay: any;
     @action
     onScroll = (e: React.UIEvent<HTMLElement>) => {
@@ -742,9 +721,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 PanelHeight={this.panelWidth}
                 PanelWidth={this.panelHeight}
                 dropAction={"alias"}
-                VisibleHeight={this.visibleHeight}
-                focus={this.props.focus}
-                isSelected={this.props.isSelected}
                 select={emptyFunction}
                 active={this.annotationsActive}
                 ContentScaling={this.contentZoom}
@@ -754,12 +730,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 removeDocument={this.removeDocument}
                 moveDocument={this.moveDocument}
                 addDocument={this.addDocument}
-                docFilters={this.props.docFilters}
-                docRangeFilters={this.props.docRangeFilters}
                 CollectionView={undefined}
                 ScreenToLocalTransform={this.overlayTransform}
-                renderDepth={this.props.renderDepth + 1}
-                ContainingCollectionDoc={this.props.ContainingCollectionView?.props.Document}>
+                renderDepth={this.props.renderDepth + 1}>
             </CollectionFreeFormView>
         </div>;
     }
@@ -778,7 +751,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     marqueeX = () => this._marqueeX;
     marqueeY = () => this._marqueeY;
     marqueeing = () => this._marqueeing;
-    visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling();
     contentZoom = () => this._zoomed;
     render() {
         TraceMobx();
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 979238767..67df56015 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -49,14 +49,12 @@ export const documentSchema = createSchema({
     _showTitle: "string",       // the fieldkey whose contents should be displayed at the top of the document
     _showTitleHover: "string",  // the showTitle should be shown only on hover
     _showAudio: "boolean",      // whether to show the audio record icon on documents
-    _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
     _freeformLOD: "boolean",    // whether to enable LOD switching for CollectionFreeFormViews
     _pivotField: "string",      // specifies which field key should be used as the timeline/pivot axis
     _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
     _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
     _columnsFill: "boolean",    // whether documents in a stacking view column should be sized to fill the column
     _columnsSort: "string",     // how a document should be sorted "ascending", "descending", undefined (none)   
-    _columnsStack: "boolean",   // whether a stacking document stacks vertically (as opposed to masonry horizontal)
     _columnsHideIfEmpty: "boolean",   // whether empty stacking view column headings should be hidden
     _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
     _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
-- 
cgit v1.2.3-70-g09d2


From 6346a318634f6d4ebe561a871905982d748bf43e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 11 Dec 2020 12:25:06 -0500
Subject: converted all style sheet properties to enums.

---
 src/client/views/MainView.tsx                      |  13 +-
 src/client/views/OverlayView.tsx                   |   3 +-
 src/client/views/PropertiesView.tsx                |   3 +-
 src/client/views/StyleProvider.scss                |  19 +++
 src/client/views/StyleProvider.tsx                 | 153 +++++++++++++++++++++
 .../views/collections/CollectionCarouselView.tsx   |   3 +-
 .../views/collections/CollectionStackingView.tsx   |   3 +-
 .../views/collections/CollectionTreeView.tsx       |   3 +-
 src/client/views/collections/CollectionView.tsx    |   3 +-
 src/client/views/collections/TabDocView.tsx        | 127 ++---------------
 src/client/views/collections/TreeView.tsx          |  17 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  23 ++--
 .../collections/collectionFreeForm/MarqueeView.tsx |   4 +-
 .../collectionGrid/CollectionGridView.tsx          |   2 -
 src/client/views/nodes/AudioBox.tsx                |   9 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  11 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  19 ---
 src/client/views/nodes/DocHolderBox.tsx            |   3 +-
 src/client/views/nodes/DocumentView.scss           |  19 ---
 src/client/views/nodes/DocumentView.tsx            |  39 +++---
 src/client/views/nodes/FontIconBox.tsx             |   3 +-
 src/client/views/nodes/LabelBox.tsx                |   3 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  10 +-
 src/client/views/nodes/LinkBox.tsx                 |   3 +-
 src/client/views/nodes/SliderBox.tsx               |   3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   3 +-
 .../views/presentationview/PresElementBox.tsx      |   5 +-
 27 files changed, 280 insertions(+), 226 deletions(-)
 create mode 100644 src/client/views/StyleProvider.scss
 create mode 100644 src/client/views/StyleProvider.tsx

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2705d2c34..2cb529317 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -59,6 +59,7 @@ import { TraceMobx } from '../../fields/util';
 import { SelectionManager } from '../util/SelectionManager';
 import { UndoManager } from '../util/UndoManager';
 import { TabDocView } from './collections/TabDocView';
+import { DefaultStyleProvider } from './StyleProvider';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -319,7 +320,7 @@ export class MainView extends React.Component {
                         PanelHeight={this.getContentsHeight}
                         renderDepth={0}
                         focus={emptyFunction}
-                        styleProvider={TabDocView.styleProvider}
+                        styleProvider={DefaultStyleProvider}
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
@@ -352,7 +353,7 @@ export class MainView extends React.Component {
                 PanelHeight={this.getContentsHeight}
                 renderDepth={0}
                 focus={emptyFunction}
-                styleProvider={TabDocView.styleProvider}
+                styleProvider={DefaultStyleProvider}
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
@@ -404,7 +405,7 @@ export class MainView extends React.Component {
                 <div className="mainView-propertiesDragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
                     <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? "white" : "black"} size="sm" />
                 </div>
-                {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={TabDocView.styleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
+                {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
             </div>
         </>;
     }
@@ -457,7 +458,7 @@ export class MainView extends React.Component {
                     fieldKey={"data"}
                     dropAction={"alias"}
                     parentActive={returnFalse}
-                    styleProvider={TabDocView.styleProvider}
+                    styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
@@ -532,7 +533,7 @@ export class MainView extends React.Component {
                 pinToPres={emptyFunction}
                 rootSelected={returnTrue}
                 onClick={undefined}
-                styleProvider={TabDocView.styleProvider}
+                styleProvider={DefaultStyleProvider}
                 removeDocument={undefined}
                 ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={returnOne}
@@ -595,7 +596,7 @@ export class MainView extends React.Component {
             <CollectionMenu />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.EditLink ? <LinkMenu docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
-            {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} styleProvider={TabDocView.styleProvider}
+            {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} styleProvider={DefaultStyleProvider}
                 linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
                 addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
             <GestureOverlay >
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index b7bd88344..124da6016 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -16,6 +16,7 @@ import { DragManager } from "../util/DragManager";
 import { List } from "../../fields/List";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { TabDocView } from "./collections/TabDocView";
+import { DefaultStyleProvider } from "./StyleProvider";
 
 export type OverlayDisposer = () => void;
 
@@ -195,7 +196,7 @@ export class OverlayView extends React.Component {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     focus={emptyFunction}
-                    styleProvider={TabDocView.styleProvider}
+                    styleProvider={DefaultStyleProvider}
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
                     docFilters={returnEmptyFilter}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 03e0a35ec..eb7df0248 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -29,6 +29,7 @@ import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
 import "./PropertiesView.scss";
 import { CollectionViewType } from "./collections/CollectionView";
 import { DocumentViewProps, StyleProviderFunc } from "./nodes/DocumentView";
+import { DefaultStyleProvider } from "./StyleProvider";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -269,7 +270,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     DataDoc={this.dataDoc}
                     renderDepth={1}
                     rootSelected={returnFalse}
-                    styleProvider={this.props.styleProvider}
+                    styleProvider={DefaultStyleProvider}
                     fitToBox={true}
                     freezeDimensions={true}
                     dontCenter={"y"}
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
new file mode 100644
index 000000000..df63288f1
--- /dev/null
+++ b/src/client/views/StyleProvider.scss
@@ -0,0 +1,19 @@
+.styleProvider-lock {
+    font-size: 12px;
+    width: 20; 
+    height: 20; 
+    position: absolute; 
+    right: -5; 
+    top: -5;
+    background: transparent;
+    pointer-events: all;
+    opacity: 0.3;
+    display: flex;
+    color: gold;
+    border-radius: 3px;
+    justify-content: center;
+    cursor: default;
+}
+.styleProvider-lock:hover {
+    opacity:1;
+}
\ No newline at end of file
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
new file mode 100644
index 000000000..0fe6df84c
--- /dev/null
+++ b/src/client/views/StyleProvider.tsx
@@ -0,0 +1,153 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import 'golden-layout/src/css/goldenlayout-base.css';
+import 'golden-layout/src/css/goldenlayout-dark-theme.css';
+import { runInAction } from 'mobx';
+import { Doc, Opt, StrListCast } from "../../fields/Doc";
+import { List } from '../../fields/List';
+import { BoolCast, Cast, StrCast } from "../../fields/Types";
+import { DocumentType } from '../documents/DocumentTypes';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { SnappingManager } from '../util/SnappingManager';
+import { UndoManager } from '../util/UndoManager';
+import { CollectionViewType } from './collections/CollectionView';
+import { DocumentViewProps } from "./nodes/DocumentView";
+import "./StyleProvider.scss";
+import React = require("react");
+import Color = require('color');
+import { listSpec } from '../../fields/Schema';
+
+export enum StyleLayers {
+    Background = "background"
+}
+
+export enum StyleProp {
+    DocContents = "docContents",
+    Opacity = "opacity",
+    Hidden = "hidden",
+    BoxShadow = "boxShadow",
+    BorderRounding = "borderRounding",
+    BackgroundColor = "backgroundColor",
+    WidgetColor = "widgetColor",
+    LinkBackgroundColor = "linkBackgroundColor",
+    HideLinkButton = "hideLinkButton",
+    LinkSource = "linkSource",
+    PointerEvents = "pointerEvents",
+    Decorations = "decorations",
+}
+
+function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
+
+function toggleBackground(doc: Doc) {
+    UndoManager.RunInBatch(() => runInAction(() => {
+        const layers = StrListCast(doc.layers);
+        if (!layers.includes(StyleLayers.Background)) {
+            if (!layers.length) doc.layers = new List<string>([StyleLayers.Background]);
+            else layers.push(StyleLayers.Background);
+        }
+        else layers.splice(layers.indexOf(StyleLayers.Background), 1);
+        doc._overflow = !layers.includes(StyleLayers.Background) ? "visible" : undefined;
+        if (!layers.includes(StyleLayers.Background)) {
+            //this.props.bringToFront(doc, true);
+            // const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
+            // const hgt = this.Document[HeightSym]();
+            // Doc.SetNativeWidth(this.props.Document[DataSym], wid);
+            // Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
+        }
+    }), "toggleBackground");
+}
+
+//
+// a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
+// 
+export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any {
+    switch (property.split(":")[0]) {
+        case StyleProp.DocContents: return undefined;
+        case StyleProp.WidgetColor: return darkScheme() ? "lightgrey" : "dimgrey";
+        case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
+        case StyleProp.Hidden: return BoolCast(doc?._hidden, BoolCast(doc?.hidden));
+        case StyleProp.BorderRounding: return !doc ? undefined : StrCast(doc._borderRounding, StrCast(doc.borderRounding));
+        case StyleProp.BackgroundColor: {
+            if (Doc.UserDoc().renderStyle === "comic") return undefined;
+            let docColor: Opt<string> = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
+            if (!docColor) {
+                switch (doc?.type) {
+                    case DocumentType.PRESELEMENT: docColor = darkScheme() ? "" : ""; break;
+                    case DocumentType.PRES: docColor = darkScheme() ? "#3e3e3e" : "white"; break;
+                    case DocumentType.FONTICON: docColor = "black"; break;
+                    case DocumentType.RTF: docColor = darkScheme() ? "#2d2d2d" : "#f1efeb"; break;
+                    case DocumentType.LABEL:
+                    case DocumentType.BUTTON: docColor = darkScheme() ? "#2d2d2d" : "lightgray"; break;
+                    case DocumentType.LINK:
+                    case DocumentType.COL:
+                        docColor = Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") :
+                            StrListCast(doc.layers).includes(StyleLayers.Background) ? "cyan" :
+                                doc.annotationOn ? "#00000015" :
+                                    StrCast((props?.renderDepth || 0) > 0 ?
+                                        Doc.UserDoc().activeCollectionNestedBackground :
+                                        Doc.UserDoc().activeCollectionBackground);
+                        break;
+                    //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
+                    default: docColor = darkScheme() ? "black" : "white"; break;
+                }
+            }
+            if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor.toLowerCase()).fade(0.5).toString();
+            return docColor;
+        }
+        case StyleProp.BoxShadow: {
+            if (!doc || props?.styleProvider?.(doc, props, StyleProp.Opacity) === 0) return undefined;  // if it's not visible, then no shadow)
+            const isBackground = StrListCast(doc.layers).includes(StyleLayers.Background);
+            switch (doc?.type) {
+                case DocumentType.COL: return isBackground ? undefined :
+                    `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+                default:
+                    return doc.z ? `#9c9396  ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
+                        props?.backgroundHalo?.(doc) && doc.type !== DocumentType.INK ? (`${props?.styleProvider?.(doc, props, StyleProp.BackgroundColor)} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground ? 100 : 50) / props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                            isBackground ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
+                                StrCast(doc.boxShadow, "");
+            }
+        }
+        case StyleProp.PointerEvents:
+            const layer = doc && props?.layerProvider?.(doc);
+            if (props?.styleProvider?.(doc, props, StyleProp.Opacity) === 0 || doc?.type === DocumentType.INK || doc?.isInkMask) return "none";
+            if (layer === false && !property.includes(":selected") && !SnappingManager.GetIsDragging()) return "none";
+            if (doc?.type !== DocumentType.INK && layer === true) return "all";
+            return undefined;
+        case StyleProp.Decorations:
+            if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) {
+                const isBackground = StrListCast(doc?.layers).includes(StyleLayers.Background);
+                return doc && (isBackground || property.includes(":selected")) && (props?.renderDepth || 0) > 0 &&
+                    ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
+                    <div className="styleProvider-lock" onClick={() => toggleBackground(doc)}>
+                        <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" />
+                    </div>
+                    : (null);
+            }
+    }
+}
+
+//
+// a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
+//  currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
+//     if it matches, then the document gets pointer events, otherwise it does not.
+//
+export function DefaultLayerProvider(thisDoc: Doc) {
+    return (doc: Doc, assign?: boolean) => {
+        if (doc.z) return true;
+        if (assign) {
+            const activeLayer = StrCast(thisDoc?.activeLayer);
+            if (activeLayer) {
+                const layers = Cast(doc.layers, listSpec("string"), []);
+                if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer);
+                else if (!layers.length) doc.layers = new List<string>([activeLayer]);
+                if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer;
+            }
+            return true;
+        } else {
+            if (Doc.AreProtosEqual(doc, thisDoc)) return true;
+            const layers = Cast(doc.layers, listSpec("string"), []);
+            if (!layers.length && !thisDoc?.activeLayer) return true;
+            if (layers.includes(StrCast(thisDoc?.activeLayer))) return true;
+            return false;
+        }
+    };
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 8b7a46e2f..26c36507a 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -15,6 +15,7 @@ import { ContextMenu } from '../ContextMenu';
 import { ObjectField } from '../../../fields/ObjectField';
 import { returnFalse, returnZero, OmitKeys } from '../../../Utils';
 import { ScriptField } from '../../../fields/ScriptField';
+import { StyleProp } from '../StyleProvider';
 
 type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
 const CarouselDocument = makeInterface(documentSchema, collectionSchema);
@@ -65,7 +66,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                 </div>
                 <div className="collectionCarouselView-caption" key="caption"
                     style={{
-                        background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor")),
+                        background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BackgroundColor)),
                         color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
                         borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
                     }}>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 8dfb7cefe..2896d244e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -28,6 +28,7 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
+import { StyleProp } from "../StyleProvider";
 const _global = (window /* browser */ || global /* node */) as any;
 
 type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -188,7 +189,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
         const styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
-            if (property === "opacity" && doc) {
+            if (property === StyleProp.Opacity && doc) {
                 if (this.props.childOpacity) {
                     return this.props.childOpacity();
                 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 89ed3a147..4e521956d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -24,6 +24,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { TreeView } from "./TreeView";
 import React = require("react");
+import { StyleProp } from '../StyleProvider';
 
 export type collectionTreeViewProps = {
     treeViewHideTitle?: boolean;
@@ -211,7 +212,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     render() {
         TraceMobx();
         if (!(this.doc instanceof Doc)) return (null);
-        const background = this.props.styleProvider?.(this.doc, this.props, "backgroundColor");
+        const background = this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
         const paddingX = `${NumCast(this.doc._xPadding, 10)}px`;
         const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`;
         const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index dbbb3e991..04edc3d09 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -39,6 +39,7 @@ import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
 import { ScriptField } from '../../../fields/ScriptField';
+import { StyleProp } from '../StyleProvider';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
@@ -385,7 +386,7 @@ export class CollectionView extends Touchable<CollectionViewProps> {
             CollectionView: this,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            this.props.styleProvider?.(this.props.Document, this.props, "boxShadow");
+            this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BoxShadow);
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index ba8b5e418..8b5143013 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -32,6 +32,7 @@ import React = require("react");
 import { List } from '../../../fields/List';
 import { DocumentType } from '../../documents/DocumentTypes';
 import Color = require('color');
+import { StyleProp, DefaultStyleProvider, DefaultLayerProvider, StyleLayers } from '../StyleProvider';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -84,7 +85,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 toggle.style.borderLeft = "solid 1px black";
                 toggle.onclick = (e: MouseEvent) => {
                     if (tab.contentItem === tab.header.parent.getActiveContentItem()) {
-                        tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : "background";
+                        tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background;
                     }
                 };
                 tab.element[0].style.borderTopRightRadius = "8px";
@@ -279,7 +280,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.ContentScaling()) / 2 : 0; }
     @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.ContentScaling()) / this._panelWidth * 100}% ` : undefined; }
     @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
-    @computed static get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
 
     // adds a tab to the layout based on the locaiton parameter which can be:
     //  close[:{left,right,top,bottom}]  - e.g., "close" will close the tab, "close:left" will close the left tab, 
@@ -303,7 +303,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         }
     }
 
-    @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, TabDocView.styleProvider(this._document, undefined, "backgroundColor"))); }
+    @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); }
     @computed get renderBounds() {
         const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
         const xbounds = bounds[2] - bounds[0];
@@ -374,7 +374,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
             <Tooltip title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
                 <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })}
-                    style={{ background: TabDocView.styleProvider(this._document, undefined, "backgroundColor") }} >
+                    style={{ background: DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor) }} >
                     <FontAwesomeIcon icon={"globe-asia"} size="lg" />
                 </div>
             </Tooltip>
@@ -388,123 +388,22 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     }
     active = () => this._isActive;
 
-    //
-    // a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
-    //  currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
-    //     if it matches, then the document gets pointer events, otherwise it does not.
-    //
-    layerProvider = (doc: Doc, assign?: boolean) => {
-        if (doc.z) return true;
-        if (assign) {
-            const activeLayer = StrCast(this._document?.activeLayer);
-            if (activeLayer) {
-                const layers = Cast(doc.layers, listSpec("string"), []);
-                if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer);
-                else if (!layers.length) doc.layers = new List<string>([activeLayer]);
-                if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer;
-            }
-            return true;
-        } else {
-            if (Doc.AreProtosEqual(doc, this._document)) return true;
-            const layers = Cast(doc.layers, listSpec("string"), []);
-            if (!layers.length && !this._document?.activeLayer) return true;
-            if (layers.includes(StrCast(this._document?.activeLayer))) return true;
-            return false;
-        }
-    }
 
-    static toggleBackground = undoBatch(action((doc: Doc) => {
-        const layers = StrListCast(doc.layers);
-        if (!layers.includes("background")) {
-            if (!layers.length) doc.layers = new List<string>(["background"]);
-            else layers.push("background");
-        }
-        else layers.splice(layers.indexOf("background"), 1);
-        doc._overflow = !layers.includes("background") ? "visible" : undefined;
-        if (!layers.includes("background")) {
-            //this.props.bringToFront(doc, true);
-            // const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
-            // const hgt = this.Document[HeightSym]();
-            // Doc.SetNativeWidth(this.props.Document[DataSym], wid);
-            // Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
-        }
-    }));
-    //
-    // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
-    // 
-    public static styleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
-        switch (property.split(":")[0]) {
-            case "docContents": return undefined;
-            case "widgetColor": return TabDocView.darkScheme ? "lightgrey" : "dimgrey";
-            case "opacity": return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
-            case "hidden": return BoolCast(doc?._hidden, BoolCast(doc?.hidden));
-            case "borderRounding": return !doc ? undefined : StrCast(doc._borderRounding, StrCast(doc.borderRounding));
-            case "backgroundColor": {
-                if (Doc.UserDoc().renderStyle === "comic") return undefined;
-                let docColor: Opt<string> = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
-                if (!docColor) {
-                    switch (doc?.type) {
-                        case DocumentType.PRESELEMENT: docColor = TabDocView.darkScheme ? "" : ""; break;
-                        case DocumentType.PRES: docColor = TabDocView.darkScheme ? "#3e3e3e" : "white"; break;
-                        case DocumentType.FONTICON: docColor = "black"; break;
-                        case DocumentType.RTF: docColor = TabDocView.darkScheme ? "#2d2d2d" : "#f1efeb"; break;
-                        case DocumentType.LABEL:
-                        case DocumentType.BUTTON: docColor = TabDocView.darkScheme ? "#2d2d2d" : "lightgray"; break;
-                        case DocumentType.LINK:
-                        case DocumentType.COL:
-                            docColor = Doc.IsSystem(doc) ? (TabDocView.darkScheme ? "rgb(62,62,62)" : "lightgrey") :
-                                StrCast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
-                            break;
-                        //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
-                        default: docColor = TabDocView.darkScheme ? "black" : "white"; break;
-                    }
-                }
-                if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor.toLowerCase()).fade(0.5).toString();
-                return docColor;
-            }
-            case "boxShadow": {
-                if (!doc || props?.styleProvider?.(doc, props, "opacity") === 0) return undefined;  // if it's not visible, then no shadow)
-                const isBackground = StrListCast(doc.layers).includes("background");
-                switch (doc?.type) {
-                    case DocumentType.COL: return isBackground ? undefined :
-                        `${TabDocView.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
-                    default:
-                        return doc.z ? `#9c9396  ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                            props?.backgroundHalo?.(doc) && doc.type !== DocumentType.INK ? (`${props?.styleProvider?.(doc, props, "backgroundColor")} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground ? 100 : 50) / props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
-                                isBackground ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
-                                    StrCast(doc.boxShadow, "");
-                }
-            }
-            case "pointerEvents":
-                const layer = doc && props?.layerProvider?.(doc);
-                if (props?.styleProvider?.(doc, props, "opacity") === 0 || doc?.type === DocumentType.INK || doc?.isInkMask) return "none";
-                if (layer === false && !property.includes(":selected") && !SnappingManager.GetIsDragging()) return "none";
-                if (doc?.type !== DocumentType.INK && layer === true) return "all";
-                return undefined;
-            case "decorations":
-                if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) {
-                    const isBackground = StrListCast(doc?.layers).includes("background");
-                    return doc && (isBackground || property.includes(":selected")) && (props?.renderDepth || 0) > 0 &&
-                        ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
-                        <div className="documentView-lock" onClick={() => TabDocView.toggleBackground(doc)}>
-                            <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" />
-                        </div>
-                        : (null);
-                }
-        }
-    }
     public static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc) {
-            switch (property) {
-                case "docContents":
+            switch (property.split(":")[0]) {
+                default: return DefaultStyleProvider(doc, props, property);
+                case StyleProp.PointerEvents: return "none";
+                case StyleProp.DocContents:
                     const background = doc.type === DocumentType.PDF ? "red" : doc.type === DocumentType.IMG ? "blue" : doc.type === DocumentType.RTF ? "orange" :
                         doc.type === DocumentType.VID ? "purple" : doc.type === DocumentType.WEB ? "yellow" : "gray";
-                    return doc.type === DocumentType.COL ? undefined : <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
-                default:
-                    return (property.startsWith("pointerEvents")) ? "none" : TabDocView.styleProvider(doc, props, property);
+                    return doc.type === DocumentType.COL ?
+                        undefined :
+                        <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
             }
         }
     }
+    @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); }
     @computed get docView() {
         TraceMobx();
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
@@ -526,7 +425,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 parentActive={this.active}
                 whenActiveChanged={emptyFunction}
                 focus={this.focusFunc}
-                styleProvider={TabDocView.styleProvider}
+                styleProvider={DefaultStyleProvider}
                 addDocTab={this.addDocTab}
                 pinToPres={TabDocView.PinDoc}
                 docFilters={CollectionDockingView.Instance.docFilters}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 53cdca2af..28503a23f 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -29,6 +29,7 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { StyleProp } from '../StyleProvider';
 
 export interface TreeViewProps {
     document: Doc;
@@ -441,7 +442,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} key={"bullet"}
             title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
             onClick={this.bulletClick}
-            style={this.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, "opacity") } : {
+            style={this.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : {
                 color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
                 opacity: checked === "unchecked" ? undefined : 0.4
             }}>
@@ -499,22 +500,22 @@ export class TreeView extends React.Component<TreeViewProps> {
     titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
         if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
 
-        switch (property) {
-            case "opacity": return this.outlineMode ? undefined : 1;
-            case "backgroundColor": return StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
-            case "docContents": return !props?.treeViewDoc ? (null) :
+        switch (property.split(":")[0]) {
+            case StyleProp.Opacity: return this.outlineMode ? undefined : 1;
+            case StyleProp.BackgroundColor: return StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+            case StyleProp.DocContents: return !props?.treeViewDoc ? (null) :
                 <div className="treeView-label" style={{    // just render a title for a tree view label (identified by treeViewDoc being set in 'props')
                     maxWidth: props?.PanelWidth() || undefined,
                     position: props?.relative ? "relative" : undefined,
-                    background: props?.styleProvider?.(doc, props, "backgroundColor"),
+                    background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
                 }}>
                     {StrCast(doc?.title)}
                 </div>;
-            default: if (property.startsWith("decorations")) return (null);
+            case StyleProp.Decorations: return (null);
         }
     }
     embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
-        if (property.startsWith("decorations")) return (null);
+        if (property.startsWith(StyleProp.Decorations)) return (null);
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     }
     onKeyDown = (e: React.KeyboardEvent) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 75411b3c5..cfc5e8b61 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -47,6 +47,7 @@ import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
+import { StyleProp, StyleLayers } from "../../StyleProvider";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -246,7 +247,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            !Cast(d, listSpec("string"), []).includes("background") && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            !StrListCast(d.layers).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -404,8 +405,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
-        let clusterColor = this.props.styleProvider?.(doc, props, property);  // bcz: check 'props'  used to be renderDepth + 1
-        if (property !== "backgroundColor") return clusterColor;
+        let styleProp = this.props.styleProvider?.(doc, props, property);  // bcz: check 'props'  used to be renderDepth + 1
+        if (property !== StyleProp.BackgroundColor) return styleProp;
         const cluster = NumCast(doc?.cluster);
         if (this.Document._useClusters) {
             if (this._clusterSets.length <= cluster) {
@@ -413,14 +414,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             } else {
                 // choose a cluster color from a palette
                 const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"];
-                clusterColor = colors[cluster % colors.length];
+                styleProp = colors[cluster % colors.length];
                 const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set && set.filter(s => !Cast(s.layers, listSpec("string"), []).includes("background")).map(s => clusterColor = StrCast(s.backgroundColor));
-                set && set.filter(s => Cast(s.layers, listSpec("string"), []).includes("background")).map(s => clusterColor = StrCast(s.backgroundColor));
+                set && set.filter(s => !StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
+                set && set.filter(s => StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
             }
-        } else if (doc && NumCast(doc.group, -1) !== -1) clusterColor = "gray";
-        return clusterColor;
+        } else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
+        return styleProp;
     }
 
     @action
@@ -870,7 +871,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     bringToFront = action((doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || Cast(doc.layers, listSpec("string"), []).includes("background")) {
+        if (sendToBack || StrListCast(doc.layers).includes(StyleLayers.Background)) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
@@ -1053,7 +1054,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
         const layoutDoc = Doc.Layout(params.pair.layout);
         const { x, y, opacity } = this.Document._currentFrame === undefined ?
-            { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, "opacity") } :
+            { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } :
             CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document._currentFrame || 0);
         const { z, color, zIndex } = params.pair.layout;
         return {
@@ -1365,7 +1366,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect);
 
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc.layers).includes("background") && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc.layers).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 52310d628..f1f190bff 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -27,6 +27,7 @@ import { CollectionView } from "../CollectionView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
+import { StyleLayers } from "../../StyleProvider";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -349,7 +350,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
         newCollection.layers = new List<string>(layers);
-        newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : layers.includes("background") ? "cyan" : undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection.x = this.Bounds.left;
@@ -523,7 +523,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], undefined, ["background"]);
+        const newCollection = this.getCollection([], undefined, [StyleLayers.Background]);
         this.props.addDocument?.(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index bef197bee..1a4eb8b7b 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -165,8 +165,6 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
-            styleProvider={this.props.styleProvider}
-            ContainingCollectionDoc={this.props.Document}
             PanelWidth={width}
             PanelHeight={height}
             ContentScaling={returnOne}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index c89e21312..b495cdd1b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -29,6 +29,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
 import { LinkAnchorBox } from "./LinkAnchorBox";
 import { LinkDocPreview } from "./LinkDocPreview";
+import { StyleProp } from "../StyleProvider";
 
 declare class MediaRecorder {
     // whatever MediaRecorder has
@@ -538,7 +539,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
 
     rangeScript = () => AudioBox.RangeScript;
     labelScript = () => AudioBox.LabelScript;
-
+    static audioStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
+        if (property === StyleProp.BackgroundColor) return "transparent";
+        if (property === StyleProp.PointerEvents) return "none";
+    }
     render() {
         const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : "";
         this._first = true;  // for indicating the first marker that is rendered
@@ -637,8 +641,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                                             parentActive={returnTrue}
                                             bringToFront={emptyFunction}
                                             ContentScaling={returnOne}
-                                            styleProvider={(doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => property === "backgroundColor" ? "transparent" : undefined}
-                                            pointerEvents={"none"}
+                                            styleProvider={AudioBox.audioStyleProvider}
                                             LayoutTemplate={undefined}
                                             LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
                                         />
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 46c311350..1b47f4551 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -16,6 +16,7 @@ import "./CollectionFreeFormDocumentView.scss";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
+import { StyleProp } from "../StyleProvider";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -59,7 +60,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, undefined, this.freezeDimensions)); }
 
     styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
-        if (property === "opacity" && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children
+        if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children
         return this.props.styleProvider?.(doc, props, property);
     }
 
@@ -147,13 +148,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     returnThis = () => this;
     @computed get pointerEvents() {
         if (this.props.pointerEvents === "none") return "none";
-        return this.props.styleProvider?.(this.Document, this.props, !this._contentView?.docView?.isSelected() ? "pointerEvents:selected" : "pointerEvents");
+        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (!this._contentView?.docView?.isSelected() ? ":selected" : ""));
     }
     render() {
         TraceMobx();
-        const backgroundColor = this.props.styleProvider?.(this.Document, this.props, "backgroundColor");
-        const borderRadius = this.props.styleProvider?.(this.Document, this.props, "borderRounding");
-        const boxShadow = this.props.styleProvider?.(this.Document, this.props, "boxShadow");
+        const backgroundColor = this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor);
+        const borderRadius = this.props.styleProvider?.(this.Document, this.props, StyleProp.BorderRounding);
+        const boxShadow = this.props.styleProvider?.(this.Document, this.props, StyleProp.BoxShadow);
         const divProps: DocumentViewProps = {
             ...this.props,
             CollectionFreeFormDocumentView: this.returnThis,
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 7b767a10a..ae0275c3d 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -67,32 +67,13 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                     }}>
                     <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                         ref={action((r: DocumentView | null) => this.docView = r)}
-                        Document={this.props.Document}
-                        DataDoc={this.props.DataDoc}
                         LayoutTemplate={this.props.LayoutTemplate}
-                        LayoutTemplateString={this.props.LayoutTemplateString}
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
                         ContentScaling={returnOne}
-                        fitToBox={this.props.fitToBox}
-                        layoutKey={this.props.layoutKey}
-                        dropAction={this.props.dropAction}
-                        onClick={this.props.onClick}
-                        styleProvider={this.props.styleProvider}
-                        addDocument={this.props.addDocument}
-                        removeDocument={this.props.removeDocument}
-                        moveDocument={this.props.moveDocument}
-                        whenActiveChanged={this.props.whenActiveChanged}
-                        ContainingCollectionView={this.props.ContainingCollectionView}
-                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                        addDocTab={this.props.addDocTab}
-                        pinToPres={this.props.pinToPres}
-                        parentActive={this.props.parentActive}
                         ScreenToLocalTransform={this.getTransform}
-                        renderDepth={this.props.renderDepth}
                         focus={this.props.focus || emptyFunction}
                         bringToFront={emptyFunction}
-                        dontRegisterView={this.props.dontRegisterView}
                     />
                 </div>)}
         </div>);
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index e14093e70..1bc7bc8d7 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -19,6 +19,7 @@ import "./DocHolderBox.scss";
 import { DocumentView } from "./DocumentView";
 import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
+import { StyleProp } from "../StyleProvider";
 
 type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
 const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
@@ -182,7 +183,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
             onContextMenu={this.specificContextMenu}
             onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
-                background: this.props.styleProvider?.(containedDoc, this.props, "backgroundColor"),
+                background: this.props.styleProvider?.(containedDoc, this.props, StyleProp.BackgroundColor),
                 border: `#00000021 solid ${this.xPad}px`,
                 borderTop: `#0000005e solid ${this.yPad}px`,
                 borderBottom: `#0000005e solid ${this.yPad}px`,
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index d25d7af05..8dadd2467 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -73,25 +73,6 @@
         pointer-events: none;
     }
 
-    .documentView-lock {
-        width: 20; 
-        height: 20; 
-        position: absolute; 
-        right: -5; 
-        top: -5;
-        background: transparent;
-        pointer-events: all;
-        opacity: 0.3;
-        display: flex;
-        color: gold;
-        border-radius: 3px;
-        justify-content: center;
-        cursor: default;
-    }
-    .documentView-lock:hover {
-        opacity:1;
-    }
-
     .documentView-contentBlocker {
         pointer-events: all;
         position: absolute;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cbbb27450..8a7c406d5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc";
 import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
@@ -42,11 +42,11 @@ import { RadialMenu } from './RadialMenu';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import React = require("react");
 import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
+import { StyleProp, StyleLayers } from "../StyleProvider";
 
 export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
 export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
 export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
-
 export interface DocumentViewSharedProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
@@ -118,6 +118,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _doubleTap = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _titleRef = React.createRef<EditableView>();
+    private _timeout: NodeJS.Timeout | undefined;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
     private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
@@ -310,14 +311,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-    _timeout: NodeJS.Timeout | undefined;
-
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
         if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !Cast(this.props.Document.layers, listSpec("string"), []).includes("background") && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            !StrListCast(this.props.Document.layers).includes(StyleLayers.Background) && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
@@ -957,7 +956,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 onClick={this.onClickFunc}
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
-            {this.props.styleProvider?.(this.layoutDoc, this.props, "hideLinkButton") || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) :
+            {this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton) || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) :
                 <DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />}
         </div>
         );
@@ -980,8 +979,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     anchorPanelWidth = () => this.props.PanelWidth() || 1;
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
-        if (property === "backgroundColor") return props?.LayoutTemplateString ? "transparent" : this.props.styleProvider?.(doc, props, "backgroundColor");
-        if (property === "hideLinkButton") return true;
+        switch (property.split(":")[0]) {
+            case StyleProp.BackgroundColor: return "transparent";
+            case StyleProp.LinkBackgroundColor: return this.props.styleProvider?.(doc, props, StyleProp.BackgroundColor);
+            case StyleProp.HideLinkButton: return true;
+            case StyleProp.PointerEvents: return "none";
+            case StyleProp.LinkSource: return this.props.Document;
+        }
     }
 
     @computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
@@ -999,15 +1003,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className="documentView-anchorCont" key={i + 1}>
                 <DocumentView {...this.props}
                     Document={d}
-                    ContainingCollectionView={this.props.ContainingCollectionView}
-                    ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     ContentScaling={returnOne}
                     dontRegisterView={false}
                     styleProvider={this.anchorStyleProvider}
                     removeDocument={this.hideLinkAnchor}
-                    pointerEvents={"none"}
                     LayoutTemplate={undefined}
                     LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
             </div >);
@@ -1056,7 +1057,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get pointerEvents() {
         if (this.props.pointerEvents === "none") return "none";
-        return this.props.styleProvider?.(this.Document, this.props, this.isSelected() ? "pointerEvents:selected" : "pointerEvents");
+        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.isSelected() ? ":selected" : ""));
     }
     @undoBatch
     @action
@@ -1080,13 +1081,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         TraceMobx();
         if (!(this.props.Document instanceof Doc)) return (null);
         if (GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate) return (null);
-        if (this.props.styleProvider?.(this.layoutDoc, this.props, "hidden")) return null;
-        return this.props.styleProvider?.(this.rootDoc, this.props, "docContents") ??
+        if (this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Hidden)) return null;
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents) ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 id={this.props.Document[Id]}
                 style={{
-                    background: this.props.styleProvider?.(this.layoutDoc, this.props, "backgroundColor"),
-                    opacity: this.props.styleProvider?.(this.layoutDoc, this.props, "opacity"),
+                    background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                    opacity: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity),
                     color: StrCast(this.layoutDoc.color, "inherit"),
                     fontFamily: StrCast(this.Document._fontFamily, "inherit"),
                     fontSize: Cast(this.Document._fontSize, "string", null),
@@ -1096,11 +1097,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 }}>
                 {this.innards}
                 {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)}
-                {this.props.styleProvider?.(this.rootDoc, this.props, this.isSelected() ? "decorations:selected" : "decorations") || (null)}
+                {this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.isSelected() ? ":selected" : "")) || (null)}
             </div>;
     }
     render() {
-        const borderRounding = this.props.styleProvider?.(this.layoutDoc, this.props, "borderRounding");
+        const borderRounding = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow);
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
         const highlightColor = (CurrentUserUtils.ActiveDashboard?.darkScheme ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
@@ -1127,7 +1128,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 outline: highlighting && !borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
                 border: highlighting && borderRounding && highlightStyle === "dashed" ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
                 boxShadow: highlighting && borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
-                    this.Document.isLinkButton && !this.props.dontRegisterView && !this.props.styleProvider?.(this.layoutDoc, this.props, "hideLinkButton") ?
+                    this.Document.isLinkButton && !this.props.dontRegisterView && !this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton) ?
                         StrCast(this.layoutDoc._linkButtonShadow, "lightblue 0em 0em 1em") :
                         this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
                             undefined,
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index a1bb0604e..000c72e94 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -14,6 +14,7 @@ import { ScriptField } from '../../../fields/ScriptField';
 import { Tooltip } from '@material-ui/core';
 import { DragManager } from '../../util/DragManager';
 import { GetEffectiveAcl } from '../../../fields/util';
+import { StyleProp } from '../StyleProvider';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -61,7 +62,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
     render() {
         const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
         const color = StrCast(this.layoutDoc.color, this._foregroundColor);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, "backgroundColor");
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle");
         const icon = StrCast(this.dataDoc.icon, "user") as any;
         const presSize = shape === 'round' ? 25 : 30;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index dd751b802..bc2090a33 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -13,6 +13,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LabelBox.scss';
+import { StyleProp } from '../StyleProvider';
 
 const LabelSchema = createSchema({});
 
@@ -72,7 +73,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps, LabelDocument
                 onMouseLeave={action(() => this._mouseOver = false)}
                 onMouseOver={action(() => this._mouseOver = true)}
                 ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
-                style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, "boxShadow") }}>
+                style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
                 <div className="labelBox-mainButton" style={{
                     background: StrCast(this.layoutDoc.backgroundColor),
                     backgroundColor: this.backColor,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index b5b53501d..f4528fbe0 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -19,6 +19,7 @@ import { SelectionManager } from "../../util/SelectionManager";
 import { TraceMobx } from "../../../fields/util";
 import { Id } from "../../../fields/FieldSymbols";
 import { LinkDocPreview } from "./LinkDocPreview";
+import { StyleProp } from "../StyleProvider";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -68,7 +69,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
             this.props.select(false);
         }
         if (!this._doubleTap && !e.ctrlKey && e.button < 2) {
-            const anchorContainerDoc = this.props.ContainingCollectionDoc; // bcz: hack!  need a better prop for passing the anchor's container 
+            const anchorContainerDoc = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
             this._editing = true;
             anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
             if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
@@ -117,7 +118,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
         const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView
         const x = NumCast(this.rootDoc[this.fieldKey + "_x"], 100);
         const y = NumCast(this.rootDoc[this.fieldKey + "_y"], 100);
-        const c = this.props.styleProvider?.(this.dataDoc, OmitKeys(this.props, ["LayoutTemplateString"]).omit, "backgroundColor");
+        const linkSource = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
+        const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkBackgroundColor);
         const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
         const anchorScale = !this.dataDoc[this.fieldKey + "-useLinkSmallAnchor"] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25;
 
@@ -135,13 +137,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
             onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
             onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
                 addDocTab: this.props.addDocTab,
-                linkSrc: this.props.ContainingCollectionDoc!,
+                linkSrc: linkSource,
                 linkDoc: this.rootDoc,
                 Location: [e.clientX, e.clientY + 20]
             })}
             onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
             ref={this._ref} style={{
-                background: c,
+                background,
                 left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
                 top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
                 transform: `scale(${anchorScale / this.props.ContentScaling()})`
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index b3fd13a77..a12a23dff 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -8,6 +8,7 @@ import { ViewBoxBaseComponent } from "../DocComponent";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./LinkBox.scss";
 import { Cast } from "../../../fields/Types";
+import { StyleProp } from "../StyleProvider";
 
 type LinkDocument = makeInterface<[typeof documentSchema]>;
 const LinkDocument = makeInterface(documentSchema);
@@ -17,7 +18,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
     render() {
         return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
-            style={{ background: this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor") }} >
+            style={{ background: this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BackgroundColor) }} >
 
             <CollectionTreeView {...this.props}
                 ChromeHeight={returnZero}
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index bcaf04da6..2aa71b2bd 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -13,6 +13,7 @@ import { ScriptBox } from '../ScriptBox';
 import { FieldView, FieldViewProps } from './FieldView';
 import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
 import './SliderBox.scss';
+import { StyleProp } from '../StyleProvider';
 
 const SliderSchema = createSchema({
     _sliderMin: "number",
@@ -48,7 +49,7 @@ export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocume
         const defaultValues = [NumCast(this.dataDoc[this.minThumbKey]), NumCast(this.dataDoc[this.maxThumbKey])];
         return domain[1] <= domain[0] ? (null) : (
             <div className="sliderBox-outerDiv" onContextMenu={this.specificContextMenu} onPointerDown={e => e.stopPropagation()}
-                style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, "boxShadow") }}>
+                style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
                 <div className="sliderBox-mainButton" onContextMenu={this.specificContextMenu} style={{
                     background: StrCast(this.layoutDoc.backgroundColor), color: StrCast(this.layoutDoc.color, "black"),
                     fontSize: StrCast(this.layoutDoc._fontSize), letterSpacing: StrCast(this.layoutDoc.letterSpacing)
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 415c8c184..6812b4f38 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -63,6 +63,7 @@ import { CollectionViewType, CollectionViewProps } from '../../collections/Colle
 import { SnappingManager } from '../../../util/SnappingManager';
 import { LinkDocPreview } from '../LinkDocPreview';
 import { SubCollectionViewProps } from '../../collections/CollectionSubView';
+import { StyleProp } from '../../StyleProvider';
 
 export interface FormattedTextBoxProps {
     makeLink?: () => Opt<Doc>;  // bcz: hack: notifies the text document when the container has made a link.  allows the text doc to react and setup a hyeprlink for any selected text
@@ -1599,7 +1600,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.author).length;
         return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) :
             <div className="formattedTextBox-sidebar-handle"
-                style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightblue" : this.props.styleProvider?.(this.props.Document, this.props, "widgetColor") }}
+                style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightblue" : this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor) }}
                 onPointerDown={this.sidebarDown}
             />;
     }
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 45d943bff..3b6b0259d 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -22,6 +22,7 @@ import { undoBatch } from "../../util/UndoManager";
 import { EditableView } from "../EditableView";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DocumentViewProps } from "../nodes/DocumentView";
+import { StyleProp } from "../StyleProvider";
 
 export const presSchema = createSchema({
     presentationTargetDoc: Doc,
@@ -78,7 +79,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
     // embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
     embedWidth = (): number => this.props.PanelWidth() - 35;
     styleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
-        if (property === "opacity") return 1;
+        if (property === StyleProp.Opacity) return 1;
         return this.props.styleProvider?.(doc, props, property);
     }
     /**
@@ -326,7 +327,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     </div>}
                 {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
                     style={{
-                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, "color"),
+                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
                         boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
                     }}>
                     <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
-- 
cgit v1.2.3-70-g09d2


From a6de26815ba43ae8cbc93cce91f34aecf6542b61 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 15 Dec 2020 19:41:24 -0500
Subject: added real grouping

---
 src/client/util/DragManager.ts                     |  8 +++-
 src/client/views/StyleProvider.tsx                 | 29 ++++++-------
 src/client/views/collections/CollectionView.tsx    |  6 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 48 +++++++++++++++++++---
 .../collections/collectionFreeForm/MarqueeView.tsx | 25 +++++++----
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +
 src/client/views/nodes/DocumentView.tsx            |  5 ++-
 7 files changed, 90 insertions(+), 33 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index bebdc8bb2..d24348746 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -213,7 +213,7 @@ export namespace DragManager {
         };
         const finishDrag = (e: DragCompleteEvent) => {
             const docDragData = e.docDragData;
-            if (dropEvent) dropEvent(); // glr: optional additional function to be called - in this case with presentation trails
+            dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
                 docDragData.droppedDocuments =
@@ -344,6 +344,9 @@ export namespace DragManager {
             dragDiv.appendChild(dragLabel);
             DragManager.Root().appendChild(dragDiv);
         }
+        dragDiv.style.width = "";
+        dragDiv.style.height = "";
+        dragDiv.style.overflow = "";
         dragDiv.hidden = false;
         const scaleXs: number[] = [];
         const scaleYs: number[] = [];
@@ -544,6 +547,9 @@ export namespace DragManager {
             return ret;
         });
         dragDiv.hidden = true;
+        dragDiv.style.width = "0";
+        dragDiv.style.height = "0";
+        dragDiv.style.overflow = "hidden";
         const target = document.elementFromPoint(e.x, e.y);
         removed.map(r => {
             r.ele.style.width = r.w;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 912d718cf..3389095b8 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -91,7 +91,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
             const backColor = backgroundCol() || "black";
             const col = Color(backColor).rgb();
             const colsum = (col.red() + col.green() + col.blue());
-            if (colsum / col.alpha() > 600 || col.alpha() < 0.25) return "black";
+            if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return "black";
             return "white";
         case StyleProp.Hidden: return BoolCast(doc?._hidden, BoolCast(doc?.hidden));
         case StyleProp.BorderRounding: return StrCast(doc?._borderRounding, StrCast(doc?.borderRounding));
@@ -119,13 +119,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
                 case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
                 case DocumentType.LINK: return "transparent";
                 case DocumentType.COL:
-                    docColor = docColor ||
-                        (Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") :
-                            isBackground() ? "cyan" :
-                                doc.annotationOn ? "#00000015" :
-                                    StrCast((props?.renderDepth || 0) > 0 ?
-                                        Doc.UserDoc().activeCollectionNestedBackground :
-                                        Doc.UserDoc().activeCollectionBackground));
+                    docColor = docColor ? docColor :
+                        doc?._isGroup ? "#00000004" :
+                            (Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") :
+                                isBackground() ? "cyan" :
+                                    doc.annotationOn ? "#00000015" :
+                                        StrCast((props?.renderDepth || 0) > 0 ?
+                                            Doc.UserDoc().activeCollectionNestedBackground :
+                                            Doc.UserDoc().activeCollectionBackground));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = darkScheme() ? "black" : "white"; break;
@@ -135,18 +136,18 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
         }
         case StyleProp.BoxShadow: {
             if (!doc || opacity() === 0) return undefined;  // if it's not visible, then no shadow)
-            const backgroundHalo = (doc: Doc) => props?.ContainingCollectionDoc?._useClusters || NumCast(doc.group, -1) !== -1;
 
             if (doc?.isLinkButton && doc.type !== DocumentType.LINK) return StrCast(doc?._linkButtonShadow, "lightblue 0em 0em 1em");
 
             switch (doc?.type) {
-                case DocumentType.COL: return isBackground() ? undefined :
-                    `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+                case DocumentType.COL: return StrCast(doc?.boxShadow, isBackground() || (doc?._isGroup && !SnappingManager.GetIsDragging()) ? undefined :
+                    `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
                 default:
                     return doc.z ? `#9c9396  ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                        backgroundHalo(doc) && doc.type !== DocumentType.INK ? (`${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
-                            isBackground() ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
-                                StrCast(doc.boxShadow, "");
+                        props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK ? (`${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                            NumCast(doc.group, -1) !== -1 && doc.type !== DocumentType.INK ? (`gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                                isBackground() ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
+                                    StrCast(doc.boxShadow, "");
             }
         }
         case StyleProp.PointerEvents:
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 04edc3d09..8c0cdba07 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -40,6 +40,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
 import { ScriptField } from '../../../fields/ScriptField';
 import { StyleProp } from '../StyleProvider';
+import { SnappingManager } from '../../util/SnappingManager';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
@@ -108,6 +109,7 @@ export class CollectionView extends Touchable<CollectionViewProps> {
     active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) ||
         this.props.rootSelected(outsideReaction) ||
         this.props.Document.forceActive ||
+        this.props.Document._isGroup ||
         this._isChildActive ||
         this.props.renderDepth === 0) ?
         true :
@@ -385,10 +387,8 @@ export class CollectionView extends Touchable<CollectionViewProps> {
             ScreenToLocalTransform: this.screenToLocalTransform,
             CollectionView: this,
         };
-        const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BoxShadow);
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}>
+            style={{ pointerEvents: (this.props.Document._isGroup && !SnappingManager.GetIsDragging()) ? "all" : this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => Cast(d.data, ImageField, null)).map(d =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d17e87db4..9cc459495 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import { Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from "../../../../fields/Doc";
@@ -31,9 +31,9 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
 import { Timeline } from "../../animationtimeline/Timeline";
 import { ContextMenu } from "../../ContextMenu";
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewProps } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
-import { DocumentViewProps, DocAfterFocusFunc } from "../../nodes/DocumentView";
+import { DocumentViewProps, DocAfterFocusFunc, DocumentView } from "../../nodes/DocumentView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { pageSchema } from "../../nodes/ImageBox";
 import { PresBox } from "../../nodes/PresBox";
@@ -220,8 +220,28 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY));
     }
 
+    updateGroupBounds = () => {
+        if (!this.props.Document._isGroup) return;
+        const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+        const cbounds = aggregateBounds(clist, 0, 0);
+        const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+        const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
+        const pbounds = {
+            x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
+            r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
+        };
+
+        this.layoutDoc._width = (pbounds.r - pbounds.x);
+        this.layoutDoc._height = (pbounds.b - pbounds.y);
+        this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
+        this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
+        this.layoutDoc.x = pbounds.x;
+        this.layoutDoc.y = pbounds.y;
+    }
+
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
+        if (!this.ChildDrag && this.props.Document._isGroup) return false;
         if (!super.onInternalDrop(e, de)) return false;
         const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y);
@@ -252,6 +272,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             !StrListCast(d.layers).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
+        this.updateGroupBounds();
+
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
         return true;
     }
@@ -422,7 +444,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 set && set.filter(s => !StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
                 set && set.filter(s => StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
             }
-        } else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
+        } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
         return styleProp;
     }
 
@@ -654,6 +676,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             return;
         }
         if (!e.cancelBubble) {
+            if (this.props.Document._isGroup) return;
             if (Doc.GetSelectedTool() === InkTool.None) {
                 if (this.tryDragCluster(e, this._hitCluster)) {
                     e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -987,6 +1010,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height)));
     }
 
+    @observable ChildDrag: DocumentView | undefined;
     onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false;
@@ -1165,6 +1189,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     key={entry[1].pair.layout[Id] + (entry[1].replica || "")}
                     {...this.getChildDocumentViewProps(entry[1].pair.layout, entry[1].pair.data)}
                     replica={entry[1].replica}
+                    CollectionFreeFormView={this}
                     dataProvider={this.childDataProvider}
                     sizeProvider={this.childSizeProvider}
                     layerProvider={this.props.layerProvider}
@@ -1202,6 +1227,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     }
                 }));
         }
+        //this.updateGroupBounds();
 
         this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
     }
@@ -1307,7 +1333,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" });
         this.props.ContainingCollectionView &&
-            optionItems.push({ description: "Move Items Out of Collection", event: this.promoteCollection, icon: "table" });
+            optionItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
         optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
         this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
         if (!Doc.UserDoc().noviceMode) {
@@ -1468,6 +1494,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get marqueeView() {
         return <MarqueeView
             {...this.props}
+            ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
             nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
             addDocTab={this.addDocTab}
             trySelectCluster={this.trySelectCluster}
@@ -1547,6 +1574,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                         {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
                     </svg>
                 </div>}
+
+            {this.ChildDrag && this.props.Document._isGroup ?
+                <div className="collectionFreeForm-groupDropper" ref={this.createDashEventsTarget} style={{
+                    width: "10000",
+                    height: "10000",
+                    left: "-5000",
+                    top: "-5000",
+                    position: "absolute",
+                    background: "#0009930",
+                    pointerEvents: "all"
+                }} /> : (null)}
         </div >;
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index f1f190bff..0ab20fea2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -38,6 +38,7 @@ interface MarqueeViewProps {
     isSelected: () => boolean;
     trySelectCluster: (addToSel: boolean) => boolean;
     nudge?: (x: number, y: number) => boolean;
+    ungroup?: () => void;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
 }
 @observer
@@ -93,7 +94,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             cm.displayMenu(this._downX, this._downY);
             e.stopPropagation();
         } else
-            if (e.key === ":") {
+            if (e.key === "u" && this.props.ungroup) {
+                e.stopPropagation();
+                this.props.ungroup();
+            }
+            else if (e.key === ":") {
                 DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
 
                 cm.displayMenu(this._downX, this._downY);
@@ -341,8 +346,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[]) => {
-        const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
+    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[], makeGroup: Opt<boolean>) => {
+        const newCollection = creator ? creator(selected, { title: makeGroup ? "grouping" : "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = "nested freeform";
             doc._panX = doc._panY = 0;
@@ -352,6 +357,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.layers = new List<string>(layers);
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
+        newCollection._isGroup = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
         selected.forEach(d => d.context = newCollection);
@@ -410,9 +416,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @undoBatch
     @action
-    collection = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+    collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => {
         const selected = this.marqueeSelect(false);
-        if (e instanceof KeyboardEvent ? e.key === "c" : true) {
+        if (e instanceof KeyboardEvent ? "cg".includes(e.key) : true) {
             selected.map(action(d => {
                 const dx = NumCast(d.x);
                 const dy = NumCast(d.y);
@@ -426,7 +432,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, []);
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, [], group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -523,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], undefined, [StyleLayers.Background]);
+        const newCollection = this.getCollection([], undefined, [StyleLayers.Background], undefined);
         this.props.addDocument?.(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
@@ -542,11 +548,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             this.delete();
             e.stopPropagation();
         }
-        if ("cbtsSp".indexOf(e.key) !== -1) {
+        if ("cbtsSpg".indexOf(e.key) !== -1) {
             this._commandExecuted = true;
             e.stopPropagation();
             e.preventDefault();
             (e as any).propagationIsStopped = true;
+            if (e.key === "g") this.collection(e, true);
             if (e.key === "c" || e.key === "t") this.collection(e);
             if (e.key === "s" || e.key === "S") this.summary(e);
             if (e.key === "b") this.background(e);
@@ -634,7 +641,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     render() {
         return <div className="marqueeView"
-            style={{ overflow: !this.props.ContainingCollectionView && this.props.isAnnotationOverlay ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
+            style={{ overflow: this.props.Document._isGroup || (!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
             onDragOver={e => e.preventDefault()}
             onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this._visible ? this.marqueeDiv : null}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index a06dd530b..a9bcf17e2 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -16,6 +16,7 @@ import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import { FieldViewProps } from "./FieldView";
 import React = require("react");
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -26,6 +27,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     jitterRotation: number;
     dataTransition?: string;
     replica: string;
+    CollectionFreeFormView: CollectionFreeFormView;
 }
 
 @observer
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index eb1807a71..5909fceb5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -338,6 +338,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     startDragging(x: number, y: number, dropAction: dropActionType) {
         if (this._mainCont.current) {
+            const ffview = this.props.CollectionFreeFormDocumentView;
+            ffview && runInAction(() => (ffview().props.CollectionFreeFormView.ChildDrag = this.props.DocumentView));
             const dragData = new DragManager.DocumentDragData([this.props.Document]);
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
             dragData.offset = this.props.ScreenToLocalTransform().scale(this.ContentScale).transformDirection(x - left, y - top);
@@ -345,7 +347,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.treeViewDoc = this.props.treeViewDoc;
             dragData.removeDocument = this.props.removeDocument;
             dragData.moveDocument = this.props.moveDocument;
-            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart });
+            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart },
+                () => setTimeout(action(() => ffview && (ffview().props.CollectionFreeFormView.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
         }
     }
     onKeyDown = (e: React.KeyboardEvent) => {
-- 
cgit v1.2.3-70-g09d2


From d222ccf613fa7dc1edabc325d226fed4f88d2db0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 15 Dec 2020 21:22:39 -0500
Subject: fixed some events with groups - they remain active when in
 "background"

---
 src/client/views/StyleProvider.tsx                    | 19 ++++++++++---------
 src/client/views/collections/CollectionView.tsx       |  5 ++---
 .../collectionFreeForm/CollectionFreeFormView.tsx     | 16 ++++++++--------
 .../collections/collectionFreeForm/MarqueeView.tsx    |  6 +++---
 src/client/views/nodes/DocumentView.tsx               |  5 +++--
 5 files changed, 26 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 3389095b8..134f4cf0b 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -43,13 +43,12 @@ function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkSc
 
 function toggleBackground(doc: Doc) {
     UndoManager.RunInBatch(() => runInAction(() => {
-        const layers = StrListCast(doc.layers);
+        let layers = StrListCast(doc.layers);
         if (!layers.includes(StyleLayers.Background)) {
-            if (!layers.length) doc.layers = new List<string>([StyleLayers.Background]);
+            if (!layers.length) doc.layers = layers = new List<string>([StyleLayers.Background]);
             else layers.push(StyleLayers.Background);
         }
         else layers.splice(layers.indexOf(StyleLayers.Background), 1);
-        doc._overflow = !layers.includes(StyleLayers.Background) ? "visible" : undefined;
         if (!layers.includes(StyleLayers.Background)) {
             //this.props.bringToFront(doc, true);
             // const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
@@ -120,10 +119,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
                 case DocumentType.LINK: return "transparent";
                 case DocumentType.COL:
                     docColor = docColor ? docColor :
-                        doc?._isGroup ? "#00000004" :
-                            (Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") :
-                                isBackground() ? "cyan" :
-                                    doc.annotationOn ? "#00000015" :
+                        doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
+                            (Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") : // system docs (seen in treeView) get a grayish background
+                                isBackground() ? "cyan" : // ?? is there a good default for a background collection
+                                    doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
                                             Doc.UserDoc().activeCollectionBackground));
@@ -140,8 +139,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
             if (doc?.isLinkButton && doc.type !== DocumentType.LINK) return StrCast(doc?._linkButtonShadow, "lightblue 0em 0em 1em");
 
             switch (doc?.type) {
-                case DocumentType.COL: return StrCast(doc?.boxShadow, isBackground() || (doc?._isGroup && !SnappingManager.GetIsDragging()) ? undefined :
-                    `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
+                case DocumentType.COL:
+                    return StrCast(doc?.boxShadow,
+                        isBackground() || doc?._isGroup ? undefined : // groups have no drop shadow -- they're supposed to be "invisible"
+                            `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
                 default:
                     return doc.z ? `#9c9396  ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
                         props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK ? (`${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8c0cdba07..90806857f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -108,8 +108,7 @@ export class CollectionView extends Touchable<CollectionViewProps> {
 
     active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) ||
         this.props.rootSelected(outsideReaction) ||
-        this.props.Document.forceActive ||
-        this.props.Document._isGroup ||
+        (this.props.layerProvider?.(this.props.Document) !== false && (this.props.Document.forceActive || this.props.Document._isGroup)) ||
         this._isChildActive ||
         this.props.renderDepth === 0) ?
         true :
@@ -388,7 +387,7 @@ export class CollectionView extends Touchable<CollectionViewProps> {
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: (this.props.Document._isGroup && !SnappingManager.GetIsDragging()) ? "all" : this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}>
+            style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => Cast(d.data, ImageField, null)).map(d =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9cc459495..58e2863e5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -241,7 +241,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
-        if (!this.ChildDrag && this.props.Document._isGroup) return false;
+        if (!this.ChildDrag && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
         if (!super.onInternalDrop(e, de)) return false;
         const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y);
@@ -676,7 +676,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             return;
         }
         if (!e.cancelBubble) {
-            if (this.props.Document._isGroup) return;
+            if (this.props.Document._isGroup) return; // groups don't pan when dragged -- instead let the event go through to allow the group itself to drag
             if (Doc.GetSelectedTool() === InkTool.None) {
                 if (this.tryDragCluster(e, this._hitCluster)) {
                     e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -1406,7 +1406,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     onPointerOver = (e: React.PointerEvent) => {
-        (DocumentDecorations.Instance.Interacting || SnappingManager.GetIsDragging()) && this.setupDragLines(e.ctrlKey || e.shiftKey);
+        (DocumentDecorations.Instance.Interacting || (this.props.layerProvider?.(this.props.Document) === false && SnappingManager.GetIsDragging())) && this.setupDragLines(e.ctrlKey || e.shiftKey);
         e.stopPropagation();
     }
 
@@ -1575,12 +1575,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     </svg>
                 </div>}
 
-            {this.ChildDrag && this.props.Document._isGroup ?
+            {(this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) && SnappingManager.GetIsDragging() && this.props.Document._isGroup ?
                 <div className="collectionFreeForm-groupDropper" ref={this.createDashEventsTarget} style={{
-                    width: "10000",
-                    height: "10000",
-                    left: "-5000",
-                    top: "-5000",
+                    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"
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 0ab20fea2..47ffc48fb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -347,9 +347,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     }
 
     getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[], makeGroup: Opt<boolean>) => {
-        const newCollection = creator ? creator(selected, { title: makeGroup ? "grouping" : "nested stack", }) : ((doc: Doc) => {
+        const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
-            Doc.GetProto(doc).title = "nested freeform";
+            Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
             doc._panX = doc._panY = 0;
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
@@ -641,7 +641,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     render() {
         return <div className="marqueeView"
-            style={{ overflow: this.props.Document._isGroup || (!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
+            style={{ overflow: (!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
             onDragOver={e => e.preventDefault()}
             onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this._visible ? this.marqueeDiv : null}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5909fceb5..3c140a22a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -154,8 +154,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
 
     componentWillUnmount() { this.cleanupHandlers(true); }
-    componentDidMount() { this.componentDidUpdate(); }
-    componentDidUpdate() {
+    componentDidMount() { this.setupHandlers(); }
+    componentDidUpdate() { this.setupHandlers(); }
+    setupHandlers() {
         this.cleanupHandlers(false);
         if (this._mainCont.current) {
             this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document);
-- 
cgit v1.2.3-70-g09d2


From ada340bb7b6e86814db431e70f1e892162302814 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 15 Dec 2020 22:18:08 -0500
Subject: from last

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

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 90806857f..bea5ac2ed 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -147,7 +147,7 @@ export class CollectionView extends Touchable<CollectionViewProps> {
 
                 if (effectiveAcl === AclAddonly) {
                     added.map(doc => {
-                        this.props.layerProvider?.(doc, true);
+                        this.props.layerProvider?.(doc, true);// assigns layer values to the newly added document... testing the utility of this
                         Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
                         doc.context = this.props.Document;
                     });
@@ -172,7 +172,7 @@ export class CollectionView extends Touchable<CollectionViewProps> {
                         doc._stayInCollection = undefined;
                         doc.context = this.props.Document;
                     });
-                    added.map(doc => this.props.layerProvider?.(doc, true));
+                    added.map(doc => this.props.layerProvider?.(doc, true));// assigns layer values to the newly added document... testing the utility of this
                     (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
                     targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 58e2863e5..4ac4f4079 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1575,7 +1575,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     </svg>
                 </div>}
 
-            {(this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) && SnappingManager.GetIsDragging() && this.props.Document._isGroup ?
+            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ?
                 <div className="collectionFreeForm-groupDropper" ref={this.createDashEventsTarget} style={{
                     width: this.ChildDrag ? "10000" : "100%",
                     height: this.ChildDrag ? "10000" : "100%",
-- 
cgit v1.2.3-70-g09d2


From 25df5c6ebc3e23d4c3dc8eaaded0eda9d9956031 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Dec 2020 22:47:36 -0500
Subject: from last

---
 src/client/views/collections/CollectionView.tsx           | 2 --
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 7 ++++---
 2 files changed, 4 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bea5ac2ed..83c639871 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -39,8 +39,6 @@ import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
 import { ScriptField } from '../../../fields/ScriptField';
-import { StyleProp } from '../StyleProvider';
-import { SnappingManager } from '../../util/SnappingManager';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3c29e0043..0823fc83a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -7,8 +7,11 @@ import { listSpec } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { numberRange, returnOne } from "../../../Utils";
+import { numberRange } from "../../../Utils";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { DocComponent } from "../DocComponent";
 import { InkingStroke } from "../InkingStroke";
 import { StyleProp } from "../StyleProvider";
@@ -16,8 +19,6 @@ import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import { FieldViewProps } from "./FieldView";
 import React = require("react");
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { SelectionManager } from "../../util/SelectionManager";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
-- 
cgit v1.2.3-70-g09d2

