From 40f3b73fef74c204c927e7715c73d021b98d7428 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 5 Sep 2021 15:09:42 -0400
Subject: changed direcotry from CollectionLinearView to collectionLinear

---
 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 229e93b9e..81375e557 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -24,7 +24,7 @@ import { CollectionCarouselView } from './CollectionCarouselView';
 import { CollectionDockingView } from "./CollectionDockingView";
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionLinearView } from './CollectionLinearView';
+import { CollectionLinearView } from './collectionLinear';
 import CollectionMapView from './CollectionMapView';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
-- 
cgit v1.2.3-70-g09d2


From 57501970d9b56ed39c6b29680286d66a536df5f1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Sep 2021 18:55:32 -0400
Subject: added showTitle prop to allow sidebarAnnos to have a custom title

---
 src/client/views/SidebarAnnos.tsx                         | 10 ++--------
 src/client/views/StyleProvider.tsx                        |  9 ++++++---
 src/client/views/collections/CollectionStackingView.tsx   | 15 ++++++++-------
 .../collections/CollectionStackingViewFieldColumn.tsx     |  2 +-
 src/client/views/collections/CollectionView.tsx           |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx         |  1 +
 src/client/views/nodes/DocumentView.tsx                   | 15 +++++++++++----
 7 files changed, 30 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 01b79ffd2..dd851c764 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -83,13 +83,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-
-    sidebarStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
-        if (property === StyleProp.ShowTitle) {
-            return doc === this.props.rootDoc ? undefined : StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title");
-        }
-        return this.props.styleProvider?.(doc, props, property);
-    }
+    showTitle = () => "title";
     setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
@@ -125,13 +119,13 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         NativeHeight={returnZero}
                         PanelHeight={this.panelHeight}
                         PanelWidth={this.panelWidth}
-                        styleProvider={this.sidebarStyleProvider}
                         docFilters={this.docFilters}
                         scaleField={this.sidebarKey + "-scale"}
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
                         scaling={returnOne}
+                        childShowTitle={this.showTitle}
                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                         childHideDecorationTitle={returnTrue}
                         removeDocument={this.removeDocument}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e528e84e3..3413179ed 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -93,9 +93,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
         case StyleProp.HideLinkButton: return props?.hideLinkButton || (!selected && (doc?.isLinkButton || doc?.hideLinkButton));
         case StyleProp.FontSize: return StrCast(doc?.[fieldKey + "fontSize"]);
-        case StyleProp.ShowTitle: return doc && !doc.presentationTargetDoc && StrCast(doc._showTitle,
-            !Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
-                (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || "";
+        case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc &&
+            StrCast(doc._showTitle,
+                props?.showTitle?.() ||
+                (!Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
+                    (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
+                        "author;creationDate") : "")) || "");
         case StyleProp.Color:
             if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index c92b259d0..540bfd1ef 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -236,6 +236,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
             dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
             rootSelected={this.rootSelected}
+            showTitle={this.props.childShowTitle}
             dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
@@ -517,14 +518,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     }
 
     @computed get buttonMenu() {
-        const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // TODO:glr Allow support for multiple buttons
-        if (menuDoc){
+        if (menuDoc) {
             const width: number = NumCast(menuDoc._width, 30);
             const height: number = NumCast(menuDoc._height, 30);
             console.log(menuDoc.title, width, height);
             return (<div className="buttonMenu-docBtn"
-                style = {{width: width, height: height}}>
+                style={{ width: width, height: height }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -550,7 +551,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                     docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} 
+                    ContainingCollectionDoc={undefined}
                 />
             </div>
             );
@@ -579,7 +580,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             <>
                 {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
                     {buttonMenu ? this.buttonMenu : null}
-                    {Doc.UserDoc().noviceMode && noviceExplainer ? 
+                    {Doc.UserDoc().noviceMode && noviceExplainer ?
                         <div className="documentExplanation">
                             {noviceExplainer}
                         </div>
@@ -612,10 +613,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                                 checkedChildren="edit"
                                 unCheckedChildren="view"
                             />} */}
-                    </div> 
+                    </div>
                 </div>
             </>
-            
+
         );
     }
 }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 47733994b..58289a161 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -111,7 +111,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4");
     @action pointerLeave = () => this._background = "inherit";
-    textCallback = (char: string) => this.addNewTextDoc("", false, true);
+    textCallback = (char: string) => this.addNewTextDoc("-typed text-", false, true);
 
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 81375e557..bc02c44f0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -71,6 +71,7 @@ export interface CollectionViewProps extends FieldViewProps {
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childFitWidth?: () => boolean;
+    childShowTitle?: () => string;
     childOpacity?: () => number;
     childContextMenuItems?: () => { script: ScriptField, label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 018c2a895..c8561d901 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1052,6 +1052,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             freezeDimensions={this.props.childFreezeDimensions}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             bringToFront={this.bringToFront}
+            showTitle={this.props.childShowTitle}
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
                 (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 07e0e2d52..6eeb51ffe 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -114,6 +114,7 @@ export interface DocumentViewSharedProps {
     docFilters: () => string[];
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
+    showTitle?: () => string;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
@@ -703,9 +704,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
                 const zorders = cm.findByDescription("ZOrder...");
                 const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
-                zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
-                zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
-                zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+                if (this.props.bringToFront !== emptyFunction) {
+                    zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
+                    zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
+                    zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+                }
                 !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
 
                 onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
@@ -974,7 +977,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             if (showTitle.includes("Date") || showTitle === "author") return true;
                             return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
                         } else if (value?.startsWith("#")) {
-                            Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate";
+                            if (this.props.showTitle) {
+                                this.rootDoc._showTitle = value?.substring(1) ? value.substring(1) : undefined;
+                            } else {
+                                Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate";
+                            }
                             return true;
                         }
                         return true;
-- 
cgit v1.2.3-70-g09d2


From f71b10a04506cd45ec157a05dbe41217380d814f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 15 Sep 2021 12:14:49 -0400
Subject: fixed documentdecoration resizers.  fixed grouping and added button
 to switch between freeform / group

---
 src/client/views/DocumentDecorations.scss          | 23 +---------------------
 src/client/views/PropertiesButtons.tsx             | 11 +++++++++++
 src/client/views/collections/CollectionMenu.tsx    |  1 -
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 ++++++++++++++----
 .../views/nodes/formattedText/RichTextMenu.tsx     |  1 -
 6 files changed, 28 insertions(+), 29 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 76b7481a0..a9f50f81b 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -7,27 +7,7 @@ $linkGap: 3px;
   z-index: 2000;
 }
 .documentDecorations-container {
-  z-index: $docDecorations-zindex;
-  position: absolute;
-  top: 0;
-  left: 0;
-  display: grid;
-  grid-template-rows: 20px 8px 1fr 8px;
-  grid-template-columns: 8px 16px 1fr 8px 8px;
-  pointer-events: none;
-
-  .documentDecorations-centerCont {
-    grid-column: 3;
-    background: none;
-  }
-
-  .documentDecorations-selectorButton {
-    pointer-events: auto;
-    height: 15px;
-    width: 15px;
-    left: -20px;
-    top: 20px;
-    display: inline-block;
+    z-index: $docDecorations-zindex;
     position: absolute;
     top: 0;
     left: 0;
@@ -142,7 +122,6 @@ $linkGap: 3px;
     .documentDecorations-bottomRightResizer:hover {
         opacity: 1;
     }
-  }
 
   .documentDecorations-topLeftResizer,
   .documentDecorations-leftResizer,
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index dd737dc9d..e3da7ce2a 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -16,6 +16,8 @@ import { DocumentView } from './nodes/DocumentView';
 import './PropertiesButtons.scss';
 import React = require("react");
 import { Colors } from "./global/globalEnums";
+import { CollectionFreeFormView } from "./collections/collectionFreeForm";
+import { DocumentManager } from "../util/DocumentManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -86,6 +88,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get gridButton() {
         return this.propertyToggleBtn("Grid", "_backgroundGridShow", on => `Display background grid in collection`, on => "border-all");
     }
+    @computed get groupButton() {
+        return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => {
+            doc.isGroup = !doc.isGroup;
+            doc.forceActive = doc.isGroup;
+            const dview = dv || DocumentManager.Instance.getFirstDocumentView(doc);
+            (dview?.ComponentView as CollectionFreeFormView)?.updateGroupBounds?.()
+        });
+    }
     @computed get snapButton() {
         return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true);
     }
@@ -219,6 +229,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
                 {toggle(this.chromeButton, { display: !isCollection || isNovice ? "none" : "" })}
                 {toggle(this.gridButton, { display: !isCollection ? "none" : "" })}
+                {toggle(this.groupButton, { display: !isCollection ? "none" : "" })}
                 {toggle(this.snapButton, { display: !isCollection ? "none" : "" })}
                 {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })}
                 {toggle(this.panButton, { display: !isFreeForm ? "none" : "" })}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 8e03b2db2..2c2d5dc75 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -99,7 +99,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
     }
 
     @computed get contMenuButtons() {
-        trace();
         const selDoc = Doc.UserDoc().contextMenuBtns;
         return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
             <CollectionLinearView
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bc02c44f0..5c9c8063b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -166,7 +166,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
         subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
 
-        if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.annotationOn) {
+        if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
             const existingVm = ContextMenu.Instance.findByDescription(category);
             const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
             catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c8561d901..ef127d328 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -183,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     addDocument = (newBox: Doc | Doc[]) => {
         let retVal = false;
         if (newBox instanceof Doc) {
-            if (retVal = this.props.addDocument?.(newBox) || false) {
+            if (retVal = (this.props.addDocument?.(newBox) || false)) {
                 this.bringToFront(newBox);
                 this.updateCluster(newBox);
             }
@@ -1236,6 +1236,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             { fireImmediately: true, name: "doLayout" });
 
         this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
+        this.rootDoc.isGroup && this.updateGroupBounds()
     }
 
     componentWillUnmount() {
@@ -1327,8 +1328,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
         const mores = ContextMenu.Instance.findByDescription("More...");
         const moreItems = mores && "subitems" in mores ? mores.subitems : [];
-        moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
-        moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
+        if (!Doc.UserDoc().noviceMode) {
+            moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
+            moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
+        }
         !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" });
     }
 
@@ -1484,6 +1487,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return wscale < hscale ? wscale : hscale;
     }
 
+    private groupDropDisposer?: DragManager.DragDropDisposer;
+    protected createGroupEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+        this.groupDropDisposer?.();
+        if (ele) {
+            this.groupDropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
+        }
+    }
+
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
@@ -1527,7 +1538,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 </div>}
 
             {this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ?
-                <div className="collectionFreeForm-groupDropper" ref={this.createDashEventsTarget} style={{
+                <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
                     width: this.ChildDrag ? "10000" : "100%",
                     height: this.ChildDrag ? "10000" : "100%",
                     left: this.ChildDrag ? "-5000" : 0,
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 86f2810ab..9904a7939 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -147,7 +147,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             const path = (this.view.state.selection.$from as any).path;
             for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) {
                 if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) {
-                    console.log(path[i].attrs);
                     return path[i].attrs.strong;
                 }
             }
-- 
cgit v1.2.3-70-g09d2


From f097eefd91614d4efa2a89b3d8c7d1071d6c0b6f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 17 Sep 2021 14:07:09 -0400
Subject: added 'unset' docFilters.  changed link doc views to use comparison
 box with title/caption.  fixed linkEditor to write to data doc. generalized
 comparisonBox rendering to use parameterized fields. fixed pdf/web to honor
 pointerEvents none prop and fixed textAnnotations to get rendered once as an
 Annotation. moved filterIcon stuff into DocumentView

---
 src/Utils.ts                                          |  4 +++-
 src/client/DocServer.ts                               |  2 ++
 src/client/documents/Documents.ts                     | 17 +++++++++++------
 src/client/views/collections/CollectionView.tsx       | 12 ------------
 .../collectionFreeForm/CollectionFreeFormLinkView.tsx |  6 +++---
 src/client/views/linking/LinkEditor.tsx               | 11 ++++++-----
 src/client/views/nodes/ComparisonBox.tsx              |  8 +++++---
 src/client/views/nodes/DocumentView.tsx               | 19 +++++++++++++++++--
 src/client/views/nodes/LinkBox.tsx                    | 19 ++++++-------------
 src/client/views/nodes/LinkDescriptionPopup.tsx       |  7 ++++---
 src/client/views/nodes/PDFBox.tsx                     |  9 ++++-----
 src/client/views/nodes/WebBox.tsx                     | 14 ++++++++------
 src/client/views/pdf/Annotation.scss                  |  3 +++
 src/client/views/pdf/PDFViewer.tsx                    | 13 +++++++------
 src/fields/Doc.ts                                     |  2 +-
 15 files changed, 80 insertions(+), 66 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 6eacd8296..e11f1154e 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -125,7 +125,9 @@ export namespace Utils {
         // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
         return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`;// bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
     }
-
+    export function PropUnsetFilter(prop: string) {
+        return `${prop}:any,${noRecursionHack}:unset`;
+    }
 
     export function toRGBAstr(col: { r: number, g: number, b: number, a?: number }) {
         return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")";
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index e498a7cca..3b376a0e7 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -241,6 +241,7 @@ export namespace DocServer {
             // the field has been returned from the server
             const getSerializedField = Utils.EmitCallback(_socket, MessageStore.GetRefField, id);
 
+            console.log(id)
             // when the serialized RefField has been received, go head and begin deserializing it into an object.
             // Here, once deserialized, we also invoke .proto to 'load' the document's prototype, which ensures that all
             // future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
@@ -264,6 +265,7 @@ export namespace DocServer {
                 } else {
                     delete _cache[id];
                 }
+                console.log(id, field);
                 return field;
                 // either way, overwrite or delete any promises cached at this id (that we inserted as flags
                 // to indicate that the field was in the process of being fetched). Now everything
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f50f306a3..e8185400e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -962,13 +962,17 @@ export namespace DocUtils {
                 // metadata facets that exist
                 const exists = Object.keys(facet).filter(value => facet[value] === "exists");
 
+                // metadata facets that exist
+                const unsets = Object.keys(facet).filter(value => facet[value] === "unset");
+
                 // facets that have an x next to them
                 const xs = Object.keys(facet).filter(value => facet[value] === "x");
 
-                if (!exists.length && !xs.length && !checks.length && !matches.length) return true;
+                if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
                 const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
                 const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
                 const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
+                const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
                 const satisfiesMatchFacets = !matches.length ? true : matches.some(value => {
                     if (facetKey.startsWith("*")) { //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
                         const allKeys = Array.from(Object.keys(d));
@@ -980,11 +984,11 @@ export namespace DocUtils {
                 });
                 // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
                 if ((parentCollection?.currentFilter as Doc)?.filterBoolean === "OR") {
-                    if (satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
+                    if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
                 }
                 // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
                 else {
-                    if (!satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
+                    if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
                 }
 
             }
@@ -1088,10 +1092,11 @@ export namespace DocUtils {
             "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined,
             "acl-Public": SharingPermissions.Augment,
             "_acl-Public": SharingPermissions.Augment,
-            layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null),
-            linkDisplay: true, hidden: true,
+            linkDisplay: true,
+            hidden: true,
             linkRelationship,
-            _layoutKey: "layout_linkView",
+            _showCaption: "description",
+            _showTitle: "linkRelationship",
             description
         }, id), showPopup);
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5c9c8063b..38e027fb3 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -246,12 +246,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
     @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
 
-    /**
-    * Shows the filter icon if it's a user-created collection which isn't a dashboard and has some docFilters applied on it or on the current dashboard.
-    */
-    @computed get showFilterIcon() {
-        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && this._subView?.IsFiltered();
-    }
 
     @observable _subView: any = undefined;
 
@@ -280,12 +274,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
-            {this.showFilterIcon ?
-                <FontAwesomeIcon icon={"filter"} size="lg"
-                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: this.showFilterIcon === "hasFilter" ? '#18c718bd' : "orange", zIndex: 1 }}
-                    onPointerDown={action(e => { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })}
-                />
-                : (null)}
         </div>);
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 3b3e069d8..2cb487588 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../../fields/Doc";
+import { Doc, Field } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { List } from "../../../../fields/List";
 import { NumCast, StrCast } from "../../../../fields/Types";
@@ -180,7 +180,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         if (!this.renderData) return (null);
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         LinkManager.currentLink = this.props.LinkDocs[0];
-        const linkRelationship = StrCast(LinkManager.currentLink?.linkRelationship); //get string representing relationship
+        const linkRelationship = Field.toString(LinkManager.currentLink?.linkRelationship as any as Field); //get string representing relationship
         const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
         const linkColorList = Doc.UserDoc().linkColorList as List<string>;
         //access stroke color using index of the relationship in the color list (default black)
@@ -189,7 +189,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2", stroke: strokeColor }}
                 d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`} />
             {textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
-                {StrCast(this.props.LinkDocs[0].description)}
+                {Field.toString(this.props.LinkDocs[0].description as any as Field)}
             </text>}
         </>);
     }
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 240a71c3e..219f7d3a2 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -2,13 +2,14 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../fields/Doc";
-import { DateCast, StrCast } from "../../../fields/Types";
+import { Doc, StrListCast, Field } from "../../../fields/Doc";
+import { DateCast, StrCast, Cast } from "../../../fields/Types";
 import { LinkManager } from "../../util/LinkManager";
 import { undoBatch } from "../../util/UndoManager";
 import './LinkEditor.scss';
 import { LinkRelationshipSearch } from "./LinkRelationshipSearch";
 import React = require("react");
+import { ToString } from "../../../fields/FieldSymbols";
 
 
 interface LinkEditorProps {
@@ -20,7 +21,7 @@ interface LinkEditorProps {
 @observer
 export class LinkEditor extends React.Component<LinkEditorProps> {
 
-    @observable description = StrCast(LinkManager.currentLink?.description);
+    @observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
     @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
     @observable openDropdown: boolean = false;
     @observable showInfo: boolean = false;
@@ -41,7 +42,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     @undoBatch
     setRelationshipValue = action((value: string) => {
         if (LinkManager.currentLink) {
-            LinkManager.currentLink.linkRelationship = value;
+            Doc.GetProto(LinkManager.currentLink).linkRelationship = value;
             const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
             // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color 
@@ -79,7 +80,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     @undoBatch
     setDescripValue = action((value: string) => {
         if (LinkManager.currentLink) {
-            LinkManager.currentLink.description = value;
+            Doc.GetProto(LinkManager.currentLink).description = value;
             this.buttonColor = "rgb(62, 133, 55)";
             setTimeout(action(() => this.buttonColor = ""), 750);
             return true;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 6708a08ee..72c7e4f45 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -15,6 +15,7 @@ import "./ComparisonBox.scss";
 import { DocumentView, DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import React = require("react");
+import { DocumentType } from '../../documents/DocumentTypes';
 
 export const comparisonSchema = createSchema({});
 
@@ -87,7 +88,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             </div>;
         };
         const displayDoc = (which: string) => {
-            const whichDoc = Cast(this.dataDoc[`compareBox-${which}`], Doc, null);
+            var whichDoc = Cast(this.dataDoc[which], Doc, null);
+            if (whichDoc.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null);
             return whichDoc ? <>
                 <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                     isContentActive={returnFalse}
@@ -112,9 +114,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
         return (
             <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}>
-                {displayBox("after", 1, this.props.PanelWidth() - 3)}
+                {displayBox(this.fieldKey === "data" ? "compareBox-after" : `${this.fieldKey}2`, 1, this.props.PanelWidth() - 3)}
                 <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, "gray") }}>
-                    {displayBox("before", 0, 0)}
+                    {displayBox(this.fieldKey === "data" ? "compareBox-before" : `${this.fieldKey}1`, 0, 0)}
                 </div>
 
                 <div className="slide-bar" style={{ left: `calc(${clipWidth} - 0.5px)`, cursor: NumCast(this.layoutDoc._clipWidth) < 5 ? "e-resize" : NumCast(this.layoutDoc._clipWidth) / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? "w-resize" : undefined }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 46a8b6629..35249c7f4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { AudioField } from "../../../fields/URLField";
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick, returnEmptyString } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -802,6 +802,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
     }
 
+    collectionFilters = () => StrListCast(this.props.Document._docFilters);
+    collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters);
+    @computed get showFilterIcon() {
+        return this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
+            this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+    }
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
@@ -832,7 +838,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none",
+                pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
@@ -869,6 +875,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         switch (property) {
+            case StyleProp.ShowTitle: return "";
             case StyleProp.PointerEvents: return "none";
             case StyleProp.LinkSource: return this.props.Document;// pass the LinkSource to the LinkAnchorBox
             default: return this.props.styleProvider?.(doc, props, property);
@@ -901,6 +908,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     dontRegisterView={false}
+                    showTitle={returnEmptyString}
+                    hideCaptions={true}
                     fitWidth={returnTrue}
                     styleProvider={this.anchorStyleProvider}
                     removeDocument={this.hideLinkAnchor}
@@ -1087,6 +1096,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     </div>
                 </>
             }
+            {this.showFilterIcon ?
+                <FontAwesomeIcon icon={"filter"} size="lg"
+                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: this.showFilterIcon === "hasFilter" ? '#18c718bd' : "orange", zIndex: 1 }}
+                    onPointerDown={action(e => { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })}
+                />
+                : (null)}
         </div>;
     }
 }
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 55ea45bb8..b82d16677 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -2,10 +2,10 @@ import React = require("react");
 import { observer } from "mobx-react";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { makeInterface } from "../../../fields/Schema";
-import { returnFalse } from "../../../Utils";
-import { CollectionTreeView } from "../collections/CollectionTreeView";
+import { emptyFunction, returnFalse } from "../../../Utils";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { StyleProp } from "../StyleProvider";
+import { ComparisonBox } from "./ComparisonBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./LinkBox.scss";
 
@@ -20,22 +20,15 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
         if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => this.dataDoc.treeViewOpen = true);
         return <div className={`linkBox-container${this.isContentActive() ? "-interactive" : ""}`}
             style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} >
-            <CollectionTreeView {...this.props}
-                childDocuments={[this.dataDoc]}
-                treeViewOpen={true}
-                treeViewExpandedView={"fields"}
-                treeViewHideTitle={true}
-                treeViewSkipFields={["treeViewExpandedView", "aliases", "_removeDropProperties",
-                    "treeViewOpen", "aliasNumber", "isPrototype", "creationDate", "author"]}
+            <ComparisonBox {...this.props}
+                fieldKey="anchor"
+                setHeight={emptyFunction}
                 dontRegisterView={true}
                 renderDepth={this.props.renderDepth + 1}
-                CollectionView={undefined}
-                isAnyChildContentActive={returnFalse}
                 isContentActive={this.isContentActiveFunc}
                 addDocument={returnFalse}
                 removeDocument={returnFalse}
-                moveDocument={returnFalse}>
-            </CollectionTreeView>
+                moveDocument={returnFalse} />
         </div>;
     }
 } 
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index b62a4dd56..a9d33f161 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,8 +1,9 @@
 import React = require("react");
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import "./LinkDescriptionPopup.scss";
-import { observable, action } from "mobx";
+import { Doc } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
+import "./LinkDescriptionPopup.scss";
 import { TaskCompletionBox } from "./TaskCompletedBox";
 
 
@@ -25,7 +26,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
     onDismiss = (add: boolean) => {
         LinkDescriptionPopup.descriptionPopup = false;
         if (add) {
-            LinkManager.currentLink && (LinkManager.currentLink.description = this.description);
+            LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).description = this.description);
         }
     }
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index f0a502e31..30b4dc92a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,9 +3,9 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym, StrListCast } from "../../../fields/Doc";
 import { documentSchema } from '../../../fields/documentSchemas';
-import { makeInterface } from "../../../fields/Schema";
+import { makeInterface, listSpec } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { PdfField } from "../../../fields/URLField";
 import { TraceMobx } from '../../../fields/util';
@@ -25,6 +25,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 
 type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -249,9 +250,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable _showSidebar = false;
     @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
 
-    contentScaling = () => {
-        return 1;
-    }
+    contentScaling = () => 1;
     @computed get renderPdfView() {
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 6a20ca14a..60a83d612 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -532,7 +532,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             NumCast(this.layoutDoc.nativeWidth)
 
     @computed get content() {
-        return <div className={"webBox-cont" + (!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
+        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
+        return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
             style={{ width: !this.layoutDoc.allowReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
             {this.urlContent}
         </div>;
@@ -575,10 +576,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")];
+    transparentFilter = () => [Utils.IsTransparentFilter(), ...this.basicFilter()];
+    opaqueFilter = () => [Utils.IsOpaqueFilter(), ...this.basicFilter()];
     render() {
-        const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined;
+        const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents ? this.props.pointerEvents as any : undefined;
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
         const renderAnnotations = (docFilters?: () => string[]) =>
@@ -593,7 +595,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 ScreenToLocalTransform={this.scrollXf}
                 scaling={returnOne}
                 dropAction={"alias"}
-                docFilters={docFilters || this.props.docFilters}
+                docFilters={docFilters || this.basicFilter}
                 dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
                 ContentScaling={returnOne}
@@ -606,7 +608,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="webBox" ref={this._mainCont}
-                style={{ pointerEvents: this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none" }} >
+                style={{ pointerEvents: this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none" }} >
                 <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
                     <base target="_blank" />
                     <div className={"webBox-outerContent"} ref={this._outerRef}
diff --git a/src/client/views/pdf/Annotation.scss b/src/client/views/pdf/Annotation.scss
index e98f071c3..1de60ffed 100644
--- a/src/client/views/pdf/Annotation.scss
+++ b/src/client/views/pdf/Annotation.scss
@@ -4,4 +4,7 @@
     position: absolute;
     background-color: rgba(146, 245, 95, 0.467);
     transition: opacity 0.5s;
+    &:hover {
+        cursor: pointer;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 7aa18e46f..78adedf5b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -179,7 +179,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight);
+            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight);
             if (scrollTo !== undefined) {
                 focusSpeed = 500;
 
@@ -502,8 +502,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
     overlayTransform = () => this.scrollXf().scale(1 / this._zoomed);
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
-    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")];
+    transparentFilter = () => [Utils.IsTransparentFilter(), ...this.basicFilter()];
+    opaqueFilter = () => [Utils.IsOpaqueFilter(), ...this.basicFilter()];
     @computed get overlayLayer() {
         const renderAnnotations = (docFilters?: () => string[]) =>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
@@ -516,7 +517,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 select={emptyFunction}
                 ContentScaling={this.contentZoom}
                 bringToFront={emptyFunction}
-                docFilters={docFilters || this.props.docFilters}
+                docFilters={docFilters || this.basicFilter}
                 dontRenderDocuments={docFilters ? false : true}
                 CollectionView={undefined}
                 ScreenToLocalTransform={this.overlayTransform}
@@ -543,7 +544,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         </div>;
     }
     @computed get pdfViewerDiv() {
-        return <div className={"pdfViewerDash-text" + (this._textSelecting && (this.props.isSelected() || this.props.isContentActive()) ? "-selected" : "")} ref={this._viewer} />;
+        return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && (this.props.isSelected() || this.props.isContentActive()) ? "-selected" : "")} ref={this._viewer} />;
     }
     @computed get contentScaling() { return this.props.ContentScaling?.() || 1; }
     @computed get standinViews() {
@@ -556,7 +557,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     render() {
         TraceMobx();
         return <div className="pdfViewer-content">
-            <div className={`pdfViewerDash${this.props.isContentActive() ? "-interactive" : ""}`} ref={this._mainCont}
+            <div className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents !== "none" ? "-interactive" : ""}`} ref={this._mainCont}
                 onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
                 style={{
                     overflowX: this._zoomed !== 1 ? "scroll" : undefined,
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4d040f3bc..57bd0f46f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1136,7 +1136,7 @@ export namespace Doc {
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x" | "exists", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
+    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x" | "exists" | "unset", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
         if (!container) return;
         const filterField = "_" + (fieldSuffix ? fieldSuffix + "-" : "") + "docFilters";
         const docFilters = Cast(container[filterField], listSpec("string"), []);
-- 
cgit v1.2.3-70-g09d2

