From b235c94614141752c98d24a395682a02297d9e6a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 11:15:59 -0400
Subject: cleaned up rootSelected() to work with templates.  adjusted nudge to
 favor moving documents over panning collection

---
 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 821840e0b..86df9c080 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -102,7 +102,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
         return viewField as any as CollectionViewType;
     }
 
-    active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.rootSelected(outsideReaction) && BoolCast(this.props.Document.forceActive)) || this._isChildActive || this.props.renderDepth === 0;
+    active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0;
 
     whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
 
-- 
cgit v1.2.3-70-g09d2


From 87268612b9b765d74f1a5317b0894be0ceb1dfd1 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 16:43:50 -0400
Subject: fixed map view pointer events.

---
 src/client/views/collections/CollectionMapView.scss | 3 +++
 src/client/views/collections/CollectionMapView.tsx  | 8 ++++----
 src/client/views/collections/CollectionView.tsx     | 2 +-
 3 files changed, 8 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index c74433902..df7853da6 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,4 +1,7 @@
 .collectionMapView-contents {
     width: 100%;
     height: 100%;
+}
+.collectionMapView-contents-none {
+    pointer-events: none;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index b67daeb53..5075bbf7a 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -7,6 +7,7 @@ import { NumCast, StrCast } from "../../../new_fields/Types";
 import { CollectionSubView } from "./CollectionSubView";
 import { Utils } from "../../../Utils";
 import { Opt } from "../../../new_fields/Doc";
+import "./CollectionMapView.scss";
 
 type MapDocument = makeInterface<[typeof documentSchema]>;
 const MapDocument = makeInterface(documentSchema);
@@ -25,11 +26,10 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
             center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0;
         }
         return (
-            <div
-                className={"collectionMapView-contents"}
-            >
+            <div className={"collectionMapView-contents" + (this.props.active() ? "" : "-none")}
+                onPointerDown={e => (this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
                 <Map
-                    {...props}
+                    google={this.props.google}
                     zoom={NumCast(Document.zoom, 10)}
                     center={center}
                     initialCenter={center}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4d04f2050..8cc5146d5 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -106,7 +106,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
         return viewField as any as CollectionViewType;
     }
 
-    active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0;
+    active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
 
-- 
cgit v1.2.3-70-g09d2


From 130e26103461f23a2d8b57ea2eef6b17eefb8f8e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 21:46:20 -0400
Subject: updated childDetailedView stuff for pivot view

---
 src/client/views/TemplateMenu.tsx                      |  4 ++--
 src/client/views/collections/CollectionTimeView.tsx    | 10 ++++++----
 src/client/views/collections/CollectionTreeView.tsx    |  2 +-
 src/client/views/collections/CollectionView.tsx        |  4 ++--
 src/client/views/collections/CollectionViewChromes.tsx |  4 ++--
 src/client/views/nodes/DocumentView.tsx                |  8 ++++----
 src/new_fields/Doc.ts                                  |  8 --------
 src/new_fields/documentSchemas.ts                      |  2 +-
 8 files changed, 18 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 4d7f1e443..b76137f06 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -167,8 +167,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
 }
 
-Scripting.addGlobal(function switchView(doc: Doc, template: Doc) {
-    if (template.dragFactory) {
+Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) {
+    if (template?.dragFactory) {
         template = Cast(template.dragFactory, Doc, null);
     }
     const templateTitle = StrCast(template?.title);
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 53de2fbbe..31b720b81 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -5,7 +5,7 @@ import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { NumCast, StrCast, BoolCast, Cast } from "../../../new_fields/Types";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
 import { Scripting } from "../../util/Scripting";
 import { ContextMenu } from "../ContextMenu";
@@ -19,6 +19,7 @@ const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 import React = require("react");
+import { DocumentView } from "../nodes/DocumentView";
 
 @observer
 export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -28,14 +29,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     componentWillUnmount() {
         this.props.Document.onChildClick = undefined;
     }
-    componentDidMount() {
-        const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked;
-        const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(thisContainer.childDetailed, alias, 'layout_detailView'); alias.layoutKey='layout_detailedView'; alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+    async componentDidMount() {
+        const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" });
         this.props.Document._fitToBox = true;
         if (!this.props.Document.onViewDefClick) {
             this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
         }
+        this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked;
+            DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
     }
 
     layoutEngine = () => this._layoutEngine;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 23fb259fc..b96ee4bc4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -754,7 +754,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
                     }));
 
                 Document.childLayout = heroView;
-                Document.childDetailed = detailView;
+                Document.childDetailView = detailView;
                 Document._viewType = CollectionViewType.Time;
                 Document._forceActive = true;
                 Document._pivotField = "company";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8cc5146d5..f94277862 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -232,8 +232,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
             if (this.props.Document.childLayout instanceof Doc) {
                 layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
             }
-            if (this.props.Document.childDetailed instanceof Doc) {
-                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, "onRight"), icon: "project-diagram" });
+            if (this.props.Document.childDetailView instanceof Doc) {
+                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailView as Doc, "onRight"), icon: "project-diagram" });
             }
             layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index ba95dce00..e494c337a 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -48,8 +48,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     };
     _narrativeCommand = {
         params: ["target", "source"], title: "=> click item view",
-        script: "this.target.childDetailed = getDocTemplate(this.source?.[0])",
-        immediate: (source: Doc[]) => this.target.childDetailed = Doc.getDocTemplate(source?.[0]),
+        script: "this.target.childDetailView = getDocTemplate(this.source?.[0])",
+        immediate: (source: Doc[]) => this.target.childDetailView = Doc.getDocTemplate(source?.[0]),
         initialize: emptyFunction,
     };
     _contentCommand = {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cc3c39154..5b6478e66 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -541,7 +541,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument?.(this.props.Document); }
 
     // applies a custom template to a document.  the template is identified by it's short name (e.g, slideView not layout_slideView)
-    static makeCustomViewClicked = (doc: Doc, creator: (documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => {
+    static makeCustomViewClicked = (doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) => {
         const batch = UndoManager.StartBatch("makeCustomViewClicked");
         runInAction(() => {
             doc.layoutKey = "layout_" + templateSignature;
@@ -563,7 +563,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
         return docLayoutTemplate;
     }
-    static createCustomView = (doc: Doc, creator: (documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => {
+    static createCustomView = (doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) => {
         const templateName = templateSignature.replace(/\(.*\)/, "");
         docLayoutTemplate = docLayoutTemplate || DocumentView.findTemplate(templateName, StrCast(doc.type), templateSignature);
 
@@ -584,10 +584,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         } else if (doc.data instanceof ImageField) {
             fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
         }
-        const docTemplate = docLayoutTemplate || creator(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
+        const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
 
         fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate));
-        Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
+        docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
     }
 
     @undoBatch
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 92abb7a71..bcf0d1aec 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -603,14 +603,6 @@ export namespace Doc {
         return undefined;
     }
     export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) {
-        if (!templateDoc) {
-            target.layout = undefined;
-            target._nativeWidth = undefined;
-            target._nativeHeight = undefined;
-            target.type = undefined;
-            return;
-        }
-
         if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) {
             if (target.resolvedDataDoc) {
                 target[targetKey] = new PrefetchProxy(templateDoc);
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index b11941f40..bc63e9df8 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -74,7 +74,7 @@ export const positionSchema = createSchema({
 
 export const collectionSchema = createSchema({
     childLayout: Doc, // layout template for children of a collecion
-    childDetailed: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field)
+    childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field)
     onChildClick: ScriptField, // script to run for each child when its clicked
     onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view
 });
-- 
cgit v1.2.3-70-g09d2


From a9613d73947ee9b000c695b8f3d7685feed96a27 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 14 Apr 2020 17:37:20 -0400
Subject: fixed goldenlayout borders.  made filter flyout semistransparent.
 fixed map to stay inbounds when filter is expanded.

---
 src/client/goldenLayout.js                          | 4 +++-
 src/client/views/collections/CollectionMapView.scss | 3 +++
 src/client/views/collections/CollectionView.scss    | 1 -
 src/client/views/collections/CollectionView.tsx     | 2 +-
 4 files changed, 7 insertions(+), 3 deletions(-)

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

diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index b510385ff..2d4283b02 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -1551,7 +1551,7 @@
         },
         dimensions: {
             borderWidth: 5,
-            borderGrabWidth: 15,
+            borderGrabWidth: 5,
             minItemHeight: 10,
             minItemWidth: 10,
             headerHeight: 20,
@@ -2796,11 +2796,13 @@
             if (this._isVertical) {
                 dragHandle.css('top', -handleExcessPos);
                 dragHandle.css('height', this._size + handleExcessSize);
+                element.css('cursor', 'row-resize');
                 element.addClass('lm_vertical');
                 element['height'](this._size);
             } else {
                 dragHandle.css('left', -handleExcessPos);
                 dragHandle.css('width', this._size + handleExcessSize);
+                element.css('cursor', 'col-resize');
                 element.addClass('lm_horizontal');
                 element['width'](this._size);
             }
diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index cb58b1750..7e4f9a0f4 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -4,6 +4,9 @@
     .collectionMapView-contents {
         width: 100%;
         height: 100%;
+        > div {
+            position: unset !important; // when the sidebar filter flys out, this prevents the map from extending outside the document box
+        }
     }
 }
 
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index b92c5fdd1..a0b73b90f 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -11,7 +11,6 @@
     height: 100%;
     overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying...
 
-
     .collectionTimeView-dragger {
         background-color: lightgray;
         height: 40px;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f94277862..13a657800 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -389,7 +389,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             return false;
         }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0));
     }
-    filterBackground = () => "dimGray";
+    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(containingTreeView, heading, this.title, checked)";
-- 
cgit v1.2.3-70-g09d2


From b9483279f4a778e0cc997e304b4f0b87e6ec1e5d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 15 Apr 2020 00:29:00 -0400
Subject: changed collection chrome filter to just open filter sidebar.  fixed
 scrollbar to make sure that all datapoints are included.  simplified filter
 documents to have only a datadocument.

---
 src/client/documents/Documents.ts                  |   1 -
 .../views/collections/CollectionTreeView.tsx       |  11 +-
 src/client/views/collections/CollectionView.tsx    |  20 +-
 .../views/collections/CollectionViewChromes.scss   |   3 +-
 .../views/collections/CollectionViewChromes.tsx    | 316 ++++++---------------
 5 files changed, 100 insertions(+), 251 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e6f3b21ca..129ab0403 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -142,7 +142,6 @@ export interface DocumentOptions {
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewOpen?: boolean; // whether this document is expanded in a tree view
     treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
-    isFacetFilter?: boolean; // whether document functions as a facet filter in a tree view
     limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
     // [key: string]: Opt<Field>;
     pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b96ee4bc4..9ec8c4c5b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -840,12 +840,11 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
         }
     });
     const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue =>
-        Docs.Create.TextDocument("", {
-            title: facetValue.toString(),
-            treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",
-                {},
-                { layoutDoc, facetHeader, facetValue })
-        }));
+        { const doc = new Doc();
+            doc.title = facetValue.toString();
+            doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",{},{ layoutDoc, facetHeader, facetValue });
+            return doc;
+        });
     return new List<Doc>(facetValueDocSet);
 });
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 13a657800..831691f8c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -45,8 +45,6 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
-import { ClientUtils } from '../../util/ClientUtils';
-import { GoogleApiWrapper } from 'google-maps-react';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -279,7 +277,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
             onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
     }
-    @observable _facetWidth = 0;
+    get _facetWidth() { return NumCast(this.props.Document._facetWidth) }
+    set _facetWidth(value) { this.props.Document._facetWidth = value; }
 
     bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
     getTransform = () => this.props.ScreenToLocalTransform().translate(-this.facetWidth(), 0);
@@ -365,17 +364,22 @@ export class CollectionView extends Touchable<FieldViewProps> {
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
                 newFacet.treeViewExpandedView = "layout";
                 newFacet.treeViewOpen = true;
-                newFacet[newFacetField + "-min"] = ranged === undefined ? minVal : ranged[0];
-                newFacet[newFacetField + "-max"] = ranged === undefined ? maxVal : ranged[1];
-                Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = minVal;
-                Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = maxVal;
+                const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
+                const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
+                newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
+                newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
+                Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
+                Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
                 newFacet.target = this.props.Document;
                 const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
                 newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
 
                 Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
             } else {
-                newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true });
+                newFacet = new Doc();
+                newFacet.title = facetHeader;
+                newFacet.treeViewOpen = true;
+                newFacet.type = DocumentType.COL;
                 const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
                 newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables);
             }
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index a691b4805..9529ef525 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -67,10 +67,11 @@
                 color:grey;
                 margin-top:auto;
                 margin-bottom:auto;
+                margin-left: 5px;
             }
 
             .collectionViewBaseChrome-viewSpecs {
-                margin-left: 10px;
+                margin-left: 25px;
                 display: grid;
                 
                 .collectionViewBaseChrome-filterIcon {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 66f627083..cef85e148 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -6,7 +6,6 @@ import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils";
 import { DragManager } from "../../util/DragManager";
@@ -16,8 +15,6 @@ import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
 import { CollectionViewType } from "./CollectionView";
 import { CollectionView } from "./CollectionView";
 import "./CollectionViewChromes.scss";
-import * as Autosuggest from 'react-autosuggest';
-import KeyRestrictionRow from "./KeyRestrictionRow";
 const datepicker = require('js-datepicker');
 
 interface CollectionViewChromeProps {
@@ -84,62 +81,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     private _picker: any;
     private _commandRef = React.createRef<HTMLInputElement>();
     private _viewRef = React.createRef<HTMLInputElement>();
-    private _autosuggestRef = React.createRef<Autosuggest>();
     @observable private _currentKey: string = "";
-    @observable private _viewSpecsOpen: boolean = false;
-    @observable private _dateWithinValue: string = "";
-    @observable private _dateValue: Date | string = "";
-    @observable private _keyRestrictions: [JSX.Element, string][] = [];
-    @observable private suggestions: string[] = [];
-    @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
-
-    getFilters = (script: string) => {
-        const re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
-        const arr: any[] = re.exec(script);
-        const toReturn: Filter[] = [];
-        if (arr !== null) {
-            const filter: Filter = {
-                key: arr[2],
-                value: arr[3],
-                contains: (arr[1] === "!") ? false : true,
-            };
-            toReturn.push(filter);
-            script = script.replace(arr[0], "");
-            if (re.exec(script) !== null) {
-                toReturn.push(...this.getFilters(script));
-            }
-            else { return toReturn; }
-        }
-        return toReturn;
-    }
-
-    addKeyRestrictions = (fields: Filter[]) => {
-
-        if (fields.length !== 0) {
-            for (let i = 0; i < fields.length; i++) {
-                this._keyRestrictions.push([<KeyRestrictionRow field={fields[i].key} value={fields[i].value} key={Utils.GenerateGuid()} contains={fields[i].contains} script={(value: string) => runInAction(() => this._keyRestrictions[i][1] = value)} />, ""]);
-
-            }
-            if (this._keyRestrictions.length === 1) {
-                this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
-            }
-        }
-        else {
-            this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]);
-            this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={false} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
-        }
-    }
 
     componentDidMount = () => {
-
-        let fields: Filter[] = [];
-        if (this.filterValue) {
-            const string = this.filterValue.script.originalScript;
-            fields = this.getFilters(string);
-        }
-
         runInAction(() => {
-            this.addKeyRestrictions(fields);
             // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
             const chromeStatus = this.props.CollectionView.props.Document._chromeStatus;
             if (chromeStatus) {
@@ -158,7 +103,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
         //@ts-ignore
-        this.props.CollectionView.props.Document._viewType = e.target.selectedOptions[0].value;
+        this.document._viewType = e.target.selectedOptions[0].value;
     }
 
     commandChanged = (e: React.ChangeEvent) => {
@@ -167,104 +112,93 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     }
 
     @action
-    openViewSpecs = (e: React.SyntheticEvent) => {
-        if (this._viewSpecsOpen) this.closeViewSpecs();
-        else {
-            this._viewSpecsOpen = true;
-
-            //@ts-ignore
-            if (!e.target?.classList[0]?.startsWith("qs")) {
-                this.closeDatePicker();
-            }
-
-            e.stopPropagation();
-            document.removeEventListener("pointerdown", this.closeViewSpecs);
-            document.addEventListener("pointerdown", this.closeViewSpecs);
-        }
+    toggleViewSpecs = (e: React.SyntheticEvent) => {
+        this.document._facetWidth = this.document._facetWidth ? 0 : 200;
+        e.stopPropagation();
     }
 
     @action closeViewSpecs = () => {
-        this._viewSpecsOpen = false;
-        document.removeEventListener("pointerdown", this.closeViewSpecs);
-    }
-
-    @action
-    openDatePicker = (e: React.PointerEvent) => {
-        this.openViewSpecs(e);
-        if (this._picker) {
-            this._picker.alwaysShow = true;
-            this._picker.show();
-            // TODO: calendar is offset when zoomed in/out
-            // this._picker.calendar.style.position = "absolute";
-            // let transform = this.props.CollectionView.props.ScreenToLocalTransform();
-            // let x = parseInt(this._picker.calendar.style.left) / transform.Scale;
-            // let y = parseInt(this._picker.calendar.style.top) / transform.Scale;
-            // this._picker.calendar.style.left = x;
-            // this._picker.calendar.style.top = y;
-
-            e.stopPropagation();
-        }
-    }
-
-    @action
-    addKeyRestriction = (e: React.MouseEvent) => {
-        const index = this._keyRestrictions.length;
-        this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]);
-
-        this.openViewSpecs(e);
-    }
-
-    @action.bound
-    applyFilter = (e: React.MouseEvent) => {
-
-        this.openViewSpecs(e);
-
-        const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
-        const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
-        const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
-        const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
-        const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
-        let dateRestrictionScript = "";
-        if (this._dateValue instanceof Date) {
-            const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
-            const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
-            dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
-        }
-        else {
-            const createdDate = new Date(this._dateValue);
-            if (!isNaN(createdDate.getTime())) {
-                const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
-                const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
-                dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
-            }
-        }
-        const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
-            `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
-            `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
-            "true";
+        this.document._facetWidth = 0;
+    }
+
+    // @action
+    // openDatePicker = (e: React.PointerEvent) => {
+    //     if (this._picker) {
+    //         this._picker.alwaysShow = true;
+    //         this._picker.show();
+    //         // TODO: calendar is offset when zoomed in/out
+    //         // this._picker.calendar.style.position = "absolute";
+    //         // let transform = this.props.CollectionView.props.ScreenToLocalTransform();
+    //         // let x = parseInt(this._picker.calendar.style.left) / transform.Scale;
+    //         // let y = parseInt(this._picker.calendar.style.top) / transform.Scale;
+    //         // this._picker.calendar.style.left = x;
+    //         // this._picker.calendar.style.top = y;
+
+    //         e.stopPropagation();
+    //     }
+    // }
+
+    // <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
+    //     id={Utils.GenerateGuid()}
+    //     ref={this.datePickerRef}
+    //     value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
+    //     onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
+    //     onPointerDown={this.openDatePicker}
+    //     placeholder="Value" />
+    // @action.bound
+    // applyFilter = (e: React.MouseEvent) => {
+    //     const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
+    //     const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
+    //     const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
+    //     const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
+    //     const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
+    //     let dateRestrictionScript = "";
+    //     if (this._dateValue instanceof Date) {
+    //         const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
+    //         const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
+    //         dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
+    //     }
+    //     else {
+    //         const createdDate = new Date(this._dateValue);
+    //         if (!isNaN(createdDate.getTime())) {
+    //             const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
+    //             const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
+    //             dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
+    //         }
+    //     }
+    //     const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
+    //         `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
+    //         `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
+    //         "true";
+
+    //     this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction(fullScript, { doc: Doc.name });
+    // }
+
+    // datePickerRef = (node: HTMLInputElement) => {
+    //     if (node) {
+    //         try {
+    //             this._picker = datepicker("#" + node.id, {
+    //                 disabler: (date: Date) => date > new Date(),
+    //                 onSelect: (instance: any, date: Date) => runInAction(() => {}), //  this._dateValue = date),
+    //                 dateSelected: new Date()
+    //             });
+    //         } catch (e) {
+    //             console.log("date picker exception:" + e);
+    //         }
+    //     }
+    // }
 
-        this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction(fullScript, { doc: Doc.name });
-    }
-
-    @action
-    closeDatePicker = () => {
-        if (this._picker) {
-            this._picker.alwaysShow = false;
-            this._picker.hide();
-        }
-        document.removeEventListener("pointerdown", this.closeDatePicker);
-    }
 
     @action
     toggleCollapse = () => {
-        this.props.CollectionView.props.Document._chromeStatus = this.props.CollectionView.props.Document._chromeStatus === "enabled" ? "collapsed" : "enabled";
+        this.document._chromeStatus = this.document._chromeStatus === "enabled" ? "collapsed" : "enabled";
         if (this.props.collapse) {
             this.props.collapse(this.props.CollectionView.props.Document._chromeStatus !== "enabled");
         }
     }
 
     subChrome = () => {
-        const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+        const collapsed = this.document._chromeStatus !== "enabled";
         if (collapsed) return null;
         switch (this.props.type) {
             case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
@@ -279,13 +213,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         return this.props.CollectionView.props.Document;
     }
 
-    @action.bound
-    clearFilter = () => {
-        this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction("true", { doc: Doc.name });
-        this._keyRestrictions = [];
-        this.addKeyRestrictions([]);
-    }
-
     private dropDisposer?: DragManager.DragDropDisposer;
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.dropDisposer && this.dropDisposer();
@@ -304,48 +231,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         return true;
     }
 
-    datePickerRef = (node: HTMLInputElement) => {
-        if (node) {
-            try {
-                this._picker = datepicker("#" + node.id, {
-                    disabler: (date: Date) => date > new Date(),
-                    onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
-                    dateSelected: new Date()
-                });
-            } catch (e) {
-                console.log("date picker exception:" + e);
-            }
-        }
-    }
-
-    renderSuggestion = (suggestion: string) => {
-        return <p>{suggestion}</p>;
-    }
-    getSuggestionValue = (suggestion: string) => suggestion;
-
-    @action
-    onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
-        this._currentKey = newValue;
-    }
-    onSuggestionFetch = async ({ value }: { value: string }) => {
-        const sugg = await this.getKeySuggestions(value);
-        runInAction(() => this.suggestions = sugg);
-    }
-    @action
-    onSuggestionClear = () => {
-        this.suggestions = [];
-    }
-    getKeySuggestions = async (value: string): Promise<string[]> => {
-        return this._buttonizableCommands.filter(c => c.title.indexOf(value) !== -1).map(c => c.title);
-    }
-
-    autoSuggestDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
-    }
-
-    private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
-    private _sensitivity: number = 16;
-
     dragViewDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, (e, down, delta) => {
             const vtype = this.props.CollectionView.collectionViewType;
@@ -367,7 +252,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                     { target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY));
             return true;
         }, emptyFunction, emptyFunction);
-        this._startDragPosition = { x: e.clientX, y: e.clientY };
     }
 
     render() {
@@ -392,7 +276,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             title="Collapse collection chrome" onClick={this.toggleCollapse}>
                             <FontAwesomeIcon icon="caret-up" size="2x" />
                         </button>
-                        <div className="collectionViewBaseChrome-template" style={{ marginLeft: 25, display: collapsed ? "none" : undefined }}>
+                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
+                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
+                                <FontAwesomeIcon icon="filter" size="2x" />
+                            </div>
+                        </div>
+                        <div className="collectionViewBaseChrome-template" style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}>
                                 <div className="commandEntry-drop">
                                     <FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
@@ -414,49 +303,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                 </select>
                             </div>
                         </div>
-                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
-                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.openViewSpecs} >
-                                <FontAwesomeIcon icon="filter" size="2x" />
-                            </div>
-                            <div className="collectionViewBaseChrome-viewSpecsMenu"
-                                onPointerDown={this.openViewSpecs}
-                                style={{
-                                    height: this._viewSpecsOpen ? "fit-content" : "0px",
-                                    overflow: this._viewSpecsOpen ? "initial" : "hidden"
-                                }}>
-                                {this._keyRestrictions.map(i => i[0])}
-                                <div className="collectionViewBaseChrome-viewSpecsMenu-row">
-                                    <div className="collectionViewBaseChrome-viewSpecsMenu-rowLeft">
-                                        CREATED WITHIN:
-                                    </div>
-                                    <select className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
-                                        style={{ textTransform: "uppercase", textAlign: "center" }}
-                                        value={this._dateWithinValue}
-                                        onChange={(e) => runInAction(() => this._dateWithinValue = e.target.value)}>
-                                        <option value="1d">1 day of</option>
-                                        <option value="3d">3 days of</option>
-                                        <option value="1w">1 week of</option>
-                                        <option value="2w">2 weeks of</option>
-                                        <option value="1m">1 month of</option>
-                                        <option value="2m">2 months of</option>
-                                        <option value="6m">6 months of</option>
-                                        <option value="1y">1 year of</option>
-                                    </select>
-                                    <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
-                                        id={Utils.GenerateGuid()}
-                                        ref={this.datePickerRef}
-                                        value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
-                                        onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
-                                        onPointerDown={this.openDatePicker}
-                                        placeholder="Value" />
-                                </div>
-                                <div className="collectionViewBaseChrome-viewSpecsMenu-lastRow">
-                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}> ADD KEY RESTRICTION </button>
-                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}> APPLY FILTER </button>
-                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}> CLEAR </button>
-                                </div>
-                            </div>
-                        </div>
                         <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
                                 <div className="commandEntry-drop">
-- 
cgit v1.2.3-70-g09d2


From d5c0ed9a29593583a4180973f854106dc10d4b30 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 15 Apr 2020 02:32:47 -0400
Subject: moved filter to right side of collection.  got rid of back button in
 pivot view - click background.

---
 src/Utils.ts                                       | 10 +++--
 .../views/collections/CollectionTimeView.tsx       | 30 +++++++--------
 src/client/views/collections/CollectionView.scss   |  4 +-
 src/client/views/collections/CollectionView.tsx    | 43 +++++++++++++++-------
 .../views/collections/CollectionViewChromes.scss   |  8 +++-
 .../views/collections/CollectionViewChromes.tsx    | 12 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  3 +-
 8 files changed, 69 insertions(+), 43 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index a8cde0624..58f272ba5 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -475,7 +475,9 @@ export function setupMoveUpEvents(
     e: React.PointerEvent,
     moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean,
     upEvent: (e: PointerEvent) => void,
-    clickEvent: (e: PointerEvent) => void) {
+    clickEvent: (e: PointerEvent) => void,
+    stopPropagation: boolean = true
+) {
     (target as any)._downX = (target as any)._lastX = e.clientX;
     (target as any)._downY = (target as any)._lastY = e.clientY;
 
@@ -499,8 +501,10 @@ export function setupMoveUpEvents(
         document.removeEventListener("pointermove", _moveEvent);
         document.removeEventListener("pointerup", _upEvent);
     };
-    e.stopPropagation();
-    e.preventDefault();
+    if (stopPropagation) {
+        e.stopPropagation();
+        e.preventDefault();
+    }
     document.removeEventListener("pointermove", _moveEvent);
     document.removeEventListener("pointerup", _upEvent);
     document.addEventListener("pointermove", _moveEvent);
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 31b720b81..1760e2419 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -72,8 +72,22 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         }), returnFalse, emptyFunction);
     }
 
+    contentsDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+            let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex);
+            if (prevFilterIndex > 0) {
+                prevFilterIndex--;
+                this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField);
+                this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField);
+                this.props.Document._prevFilterIndex = prevFilterIndex;
+            } else {
+                this.props.Document._docFilters = new List([]);
+            }
+        }), false);
+    }
+
     @computed get contents() {
-        return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }}>
+        return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
             <CollectionFreeFormView {...this.props} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
         </div>;
     }
@@ -132,20 +146,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
             color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
         };
         return <div className={"pivotKeyEntry"}>
-            <button className="collectionTimeView-backBtn"
-                onClick={action(() => {
-                    let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex);
-                    if (prevFilterIndex > 0) {
-                        prevFilterIndex--;
-                        this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField);
-                        this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField);
-                        this.props.Document._prevFilterIndex = prevFilterIndex;
-                    } else {
-                        this.props.Document._docFilters = new List([]);
-                    }
-                })}>
-                back
-            </button>
             <EditableView {...newEditableViewProps} display={"inline"} menuCallback={this.menuCallback} />
         </div>;
     }
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index a0b73b90f..d43dd387a 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -20,7 +20,7 @@
         top: 55%;
         border: 1px black solid;
         z-index: 2;
-        left: -10px;
+        right: -10px;
     }
     .collectionTimeView-treeView {
         display: flex;
@@ -28,7 +28,7 @@
         width: 200px;
         height: 100%;
         position: absolute;
-        left: 0;
+        right: 0;
         top: 0;
 
         .collectionTimeView-addfacet {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 831691f8c..0a3577bc7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ import { List } from '../../../new_fields/List';
 import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
 import { ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
-import { Utils, setupMoveUpEvents, returnFalse, returnZero } from '../../../Utils';
+import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
@@ -45,6 +45,7 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
+import { Transform } from 'prosemirror-transform';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -281,7 +282,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
     set _facetWidth(value) { this.props.Document._facetWidth = value; }
 
     bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-this.facetWidth(), 0);
     facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth));
 
     @computed get dataDoc() {
@@ -389,7 +389,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
-            this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
+            this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
             return false;
         }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0));
     }
@@ -415,27 +415,44 @@ export class CollectionView extends Touchable<FieldViewProps> {
                 <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
                     <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
                         <div className="collectionTimeView-button">
-                            <span className="collectionTimeView-span">Facet Filters</span>
                             <FontAwesomeIcon icon={faEdit} size={"lg"} />
+                            <span className="collectionTimeView-span">Facet Filters</span>
                         </div>
                     </Flyout>
                 </div>
                 <div className="collectionTimeView-tree" key="tree">
-                    <CollectionTreeView {...this.props}
+                    <CollectionTreeView
+                        Document={facetCollection}
+                        DataDoc={facetCollection}
+                        fieldKey={`${this.props.fieldKey}-filter`}
                         CollectionView={this}
-                        treeViewHideTitle={true}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        PanelWidth={this.facetWidth}
+                        PanelHeight={this.props.PanelHeight}
                         NativeHeight={returnZero}
                         NativeWidth={returnZero}
+                        LibraryPath={emptyPath}
+                        rootSelected={this.props.rootSelected}
+                        renderDepth={1}
+                        dropAction={this.props.dropAction}
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        addDocTab={returnFalse}
+                        pinToPres={returnFalse}
+                        isSelected={returnFalse}
+                        select={returnFalse}
+                        bringToFront={emptyFunction}
+                        active={this.props.active}
+                        whenActiveChanged={returnFalse}
+                        treeViewHideTitle={true}
+                        ContentScaling={returnOne}
+                        focus={returnFalse}
                         treeViewHideHeaderFields={true}
                         onCheckedClick={this.scriptField!}
                         ignoreFields={this.ignoreFields}
                         annotationsKey={""}
                         dontRegisterView={true}
-                        PanelWidth={this.facetWidth}
-                        DataDoc={facetCollection}
-                        Document={facetCollection}
                         backgroundColor={this.filterBackground}
-                        fieldKey={`${this.props.fieldKey}-filter`}
                         moveDocument={returnFalse}
                         removeDocument={returnFalse}
                         addDocument={returnFalse} />
@@ -461,7 +478,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             }}
             onContextMenu={this.onContextMenu}>
             {this.showIsTagged()}
-            <div style={{ width: `calc(100% - ${this.facetWidth()}px)`, marginLeft: `${this.facetWidth()}px` }}>
+            <div style={{ width: `calc(100% - ${this.facetWidth()}px)` }}>
                 {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             </div>
             {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d =>
@@ -471,9 +488,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
                     :
                     ""))}
             {!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) :
-                <div className="collectionTimeView-dragger" key="dragger" onPointerDown={this.onPointerDown} style={{ transform: `translate(${this.facetWidth()}px, 0px)` }} >
-                    <span title="library View Dragger" style={{ width: "5px", position: "absolute", top: "0" }} />
-                </div>
+                <div className="collectionTimeView-dragger" title="library View Dragger" onPointerDown={this.onPointerDown} style={{ right: this.facetWidth() - 10 }} />
             }
             {this.filterView}
         </div>);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 9529ef525..5203eb55f 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -61,7 +61,8 @@
                 pointer-events: all;
                 // margin-top: 10px;
             }
-            .collectionViewBaseChrome-template {
+            .collectionViewBaseChrome-template,
+            .collectionViewBaseChrome-viewModes {
                 display: grid;
                 background: rgb(238, 238, 238);
                 color:grey;
@@ -69,9 +70,12 @@
                 margin-bottom:auto;
                 margin-left: 5px;
             }
+            .collectionViewBaseChrome-viewModes {
+                margin-left: 25px;
+            }
 
             .collectionViewBaseChrome-viewSpecs {
-                margin-left: 25px;
+                margin-left: 5px;
                 display: grid;
                 
                 .collectionViewBaseChrome-filterIcon {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index cef85e148..7315d2c4e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -276,12 +276,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             title="Collapse collection chrome" onClick={this.toggleCollapse}>
                             <FontAwesomeIcon icon="caret-up" size="2x" />
                         </button>
-                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
-                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
-                                <FontAwesomeIcon icon="filter" size="2x" />
-                            </div>
-                        </div>
-                        <div className="collectionViewBaseChrome-template" style={{ display: collapsed ? "none" : undefined }}>
+                        <div className="collectionViewBaseChrome-viewModes" style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}>
                                 <div className="commandEntry-drop">
                                     <FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
@@ -303,6 +298,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                 </select>
                             </div>
                         </div>
+                        <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
+                            <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
+                                <FontAwesomeIcon icon="filter" size="2x" />
+                            </div>
+                        </div>
                         <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
                                 <div className="commandEntry-drop">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a77f09163..d98fe190c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -907,6 +907,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
     onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
         (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
+        e.stopPropagation();
     }
     private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
         const { x, y, z } = viewDef;
@@ -991,6 +992,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
                     {...this.getChildDocumentViewProps(pair.layout, pair.data)}
                     dataProvider={this.childDataProvider}
                     LayoutDoc={this.childLayoutDocFunc}
+                    pointerEvents={this.props.layoutEngine?.() !== undefined ? "none" : undefined}
                     jitterRotation={NumCast(this.props.Document.jitterRotation)}
                     fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)}
                     FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e7cb2c015..05ad98c43 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -22,6 +22,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     width?: number;
     height?: number;
     jitterRotation: number;
+    pointerEvents?: "none";
     transition?: string;
     fitToBox?: boolean;
 }
@@ -92,7 +93,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 height: this.height,
                 zIndex: this.ZInd,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document.isBackground ? "none" : undefined
+                pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents
             }} >
 
             {!this.props.fitToBox ?
-- 
cgit v1.2.3-70-g09d2


From a6d17ef79fb94334735998d3d11f7ea068dea93a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 15 Apr 2020 03:15:25 -0400
Subject: cleaned up some more of the pivot view event handling api

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

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

diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 1760e2419..e05223ca0 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { RichTextField } from "../../../new_fields/RichTextField";
@@ -26,18 +26,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     _changing = false;
     @observable _layoutEngine = "pivot";
     @observable _collapsed: boolean = false;
-    componentWillUnmount() {
-        this.props.Document.onChildClick = undefined;
-    }
+    @observable _childClickedScript: Opt<ScriptField>;
+    @observable _viewDefDivClick: Opt<ScriptField>;
     async componentDidMount() {
-        const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
-        this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" });
-        this.props.Document._fitToBox = true;
-        if (!this.props.Document.onViewDefClick) {
-            this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
-        }
-        this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked;
-            DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const childText = "const alias = getAlias(this); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+        runInAction(() => {
+            this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
+            this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
+        });
     }
 
     layoutEngine = () => this._layoutEngine;
@@ -88,7 +85,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
-            <CollectionFreeFormView {...this.props} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0a3577bc7..19e235ff2 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -278,7 +278,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
             onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
     }
-    get _facetWidth() { return NumCast(this.props.Document._facetWidth) }
+    get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
     set _facetWidth(value) { this.props.Document._facetWidth = value; }
 
     bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d98fe190c..f19181a20 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,6 +44,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CollectionViewType } from "../CollectionView";
+import { Script } from "vm";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -69,6 +70,8 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchem
 const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
 export type collectionFreeformViewProps = {
     forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
+    childClickScript?: ScriptField;
+    viewDefDivClick?: ScriptField;
 };
 
 @observer
@@ -847,7 +850,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
     }
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
-    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
     backgroundHalo = () => BoolCast(this.Document.useClusters);
 
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
@@ -906,7 +909,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
     }
 
     onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
-        (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
+        (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
         e.stopPropagation();
     }
     private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
-- 
cgit v1.2.3-70-g09d2


From 67c56296339bd1c760b704c1d14710ec6f575d49 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 17 Apr 2020 22:55:10 -0400
Subject: added freeze button for web boxes.  cleaned up pointerevent setting
 throughout code.

---
 src/client/views/InkingStroke.scss                 |  8 +++-
 src/client/views/InkingStroke.tsx                  |  5 +--
 src/client/views/Main.scss                         |  1 -
 src/client/views/MainView.scss                     |  1 +
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionFreeForm/MarqueeView.scss            |  1 -
 src/client/views/nodes/DocumentView.tsx            |  4 +-
 src/client/views/nodes/FormattedTextBox.scss       |  1 -
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 src/client/views/nodes/ImageBox.scss               |  1 -
 src/client/views/nodes/KeyValueBox.scss            |  1 -
 src/client/views/nodes/LabelBox.scss               |  1 -
 src/client/views/nodes/LinkAnchorBox.scss          |  1 -
 src/client/views/nodes/PresBox.scss                |  1 -
 src/client/views/nodes/QueryBox.scss               |  1 -
 src/client/views/nodes/ScreenshotBox.scss          |  5 ---
 src/client/views/nodes/ScriptingBox.scss           |  1 -
 src/client/views/nodes/SliderBox.scss              |  1 -
 src/client/views/nodes/VideoBox.scss               |  7 ++--
 src/client/views/nodes/WebBox.scss                 | 15 ++++----
 src/client/views/nodes/WebBox.tsx                  | 44 +++++++++-------------
 22 files changed, 42 insertions(+), 64 deletions(-)

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

diff --git a/src/client/views/InkingStroke.scss b/src/client/views/InkingStroke.scss
index cdbfdcff3..433433a42 100644
--- a/src/client/views/InkingStroke.scss
+++ b/src/client/views/InkingStroke.scss
@@ -1,3 +1,7 @@
-.inkingStroke-marker {
-    mix-blend-mode: multiply
+.inkingStroke {
+    mix-blend-mode: multiply;
+    stroke-linejoin: round;
+    stroke-linecap: round;
+    overflow: visible !important;
+    transform-origin: top left;
 }
\ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f66c04e1f..7a318d5c2 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,4 +1,3 @@
-import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { documentSchema } from "../../new_fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../new_fields/InkField";
@@ -47,14 +46,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const scaleX = this.props.PanelWidth() / width;
         const scaleY = this.props.PanelHeight() / height;
         return (
-            <svg
+            <svg className="inkingStroke"
                 width={width}
                 height={height}
                 style={{
-                    transformOrigin: "top left",
                     transform: `scale(${scaleX}, ${scaleY})`,
                     mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
-                    pointerEvents: "all"
                 }}
                 onContextMenu={() => {
                     ContextMenu.Instance.addItem({
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 4709e7ef2..a2a9ceca5 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -24,7 +24,6 @@ body {
 .jsx-parser {
     width: 100%;
     height: 100%;
-    pointer-events: none;
     border-radius: inherit;
     position: inherit;
    // background: inherit;
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index e95802e54..e9f2248ad 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -28,6 +28,7 @@
     width: 100%;
     height: 100%;
     position: absolute;
+    pointer-events: all;
     top: 0;
     left: 0;
     z-index: 1;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d2c8cc3ad..510c9924b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -453,7 +453,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     fontWeight: this.props.document.searchMatch ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
+                    pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? undefined : "none"
                 }} >
                 {Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
                     this.editableView("title") :
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 19e235ff2..877e4355f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -472,7 +472,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
         };
         return (<div className={"collectionView"}
             style={{
-                pointerEvents: this.props.Document.isBackground ? "none" : "all",
+                pointerEvents: this.props.Document.isBackground ? "none" : undefined,
                 boxShadow: this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
                     `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31)" : "#9c9396"} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
             }}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 18d6da0da..1291e7dc1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -6,7 +6,6 @@
     width:100%;
     height:100%;
     overflow: hidden;
-    pointer-events: inherit;
     border-radius: inherit;
 }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fb0b1d4f4..2f22488e4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1092,7 +1092,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const titleView = (!showTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} style={{
                 position: showTextTitle ? "relative" : "absolute",
-                pointerEvents: SelectionManager.GetIsDragging() || this.onClickHandler || this.Document.ignoreClick ? "none" : "all",
+                pointerEvents: SelectionManager.GetIsDragging() || this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
             }}>
                 <EditableView ref={this._titleRef}
                     contents={(this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
@@ -1154,7 +1154,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 transformOrigin: this._animate ? "center center" : undefined,
                 transform: this._animate ? `scale(${this._animate})` : undefined,
                 transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
-                pointerEvents: this.ignorePointerEvents ? "none" : "all",
+                pointerEvents: this.ignorePointerEvents ? "none" : undefined,
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
                 border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 7d40b3149..7a05ec3a3 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -25,7 +25,6 @@
     overflow-x: hidden;
     color: initial;
     height: 100%;
-    pointer-events: all;
     max-height: 100%;
     display: flex;
     flex-direction: row;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d641dc791..62911dc5c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1185,7 +1185,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"]),
                     opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
                     color: this.props.hideOnLeave ? "white" : "inherit",
-                    pointerEvents: interactive ? "none" : "all",
+                    pointerEvents: interactive ? "none" : undefined,
                     fontSize: NumCast(this.layoutDoc.fontSize, 13),
                     fontFamily: StrCast(this.layoutDoc.fontFamily, "Crimson Text"),
                 }}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 7bbf4a368..49425c2c2 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,6 +1,5 @@
 .imageBox,
 .imageBox-dragging {
-    pointer-events: all;
     border-radius: inherit;
     width: 100%;
     height: 100%;
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index a26880c9e..eb7c2f32b 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -8,7 +8,6 @@
     border-radius: $border-radius;
     box-sizing: border-box;
     display: inline-block;
-    pointer-events: all;
     cursor: default;
     .imageBox-cont img {
         width: auto;
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index ab5b2c6b3..56dd86ff9 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -1,7 +1,6 @@
 .labelBox-outerDiv {
     width: 100%;
     height: 100%;
-    pointer-events: all;
     border-radius: inherit;
     display: flex;
     flex-direction: column;
diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss
index 7b6093ebd..24f9c1ea0 100644
--- a/src/client/views/nodes/LinkAnchorBox.scss
+++ b/src/client/views/nodes/LinkAnchorBox.scss
@@ -4,7 +4,6 @@
     width: 15;
     height: 15;
     border-radius: 20px;
-    pointer-events: all;
     user-select: none;
 
     .linkAnchorBox-linkCloser {
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index ba8389fda..6676d8cd2 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -10,7 +10,6 @@
     letter-spacing: 2px;
     overflow: hidden;
     transition: 0.7s opacity ease;
-    pointer-events: all;
 
     .presBox-buttons {
         padding: 10px;
diff --git a/src/client/views/nodes/QueryBox.scss b/src/client/views/nodes/QueryBox.scss
index 82f64054c..b5f90aa1e 100644
--- a/src/client/views/nodes/QueryBox.scss
+++ b/src/client/views/nodes/QueryBox.scss
@@ -2,5 +2,4 @@
     width: 100%;
     height: 100%; 
     position: absolute;
-    pointer-events: all;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss
index 6cc184948..141960f60 100644
--- a/src/client/views/nodes/ScreenshotBox.scss
+++ b/src/client/views/nodes/ScreenshotBox.scss
@@ -1,5 +1,4 @@
 .screenshotBox {
-    pointer-events: all;
     transform-origin: top left;
     background: white;
     color: black;
@@ -21,10 +20,6 @@
     height: Auto; 
 }
 
-.screenshotBox-content-interactive, .screenshotBox-content-fullScreen  {
-    pointer-events: all;
-}
-
 .screenshotBox-uiButtons {
     background:dimgray;
     border: orange solid 1px;
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
index 678a1a22d..43695f00d 100644
--- a/src/client/views/nodes/ScriptingBox.scss
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -3,7 +3,6 @@
     height: 100%;
     display: flex;
     flex-direction: column;
-    pointer-events: all;
     background-color: rgb(241, 239, 235);
     padding: 10px;
     .scriptingBox-inputDiv {
diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss
index 4ef277d8c..78015bd70 100644
--- a/src/client/views/nodes/SliderBox.scss
+++ b/src/client/views/nodes/SliderBox.scss
@@ -1,7 +1,6 @@
 .sliderBox-outerDiv {
     width: 100%;
     height: 100%;
-    pointer-events: all;
     border-radius: inherit;
     display: flex;
     flex-direction: column;
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index fabbf5196..0c0854ac2 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,5 +1,4 @@
 .videoBox {
-    pointer-events: all;
     transform-origin: top left;
     .videoBox-viewer {
         opacity: 0.99;  // hack!  overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
@@ -24,9 +23,9 @@
     height: 100%;
 }
 
-.videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen  {
-    pointer-events: all;
-}
+// .videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen  {
+//     pointer-events: all;
+// }
 
 .videoBox-time{
     color : white;
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 3014b9425..cdeac4bd1 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -18,8 +18,7 @@
         display:none;
     }
 }
-.webBox-cont,
-.webBox-cont-dragging {
+.webBox-cont {
     padding: 0vw;
     position: absolute;
     top: 0;
@@ -32,8 +31,6 @@
 }
 
 .webBox-cont-interactive {
-    pointer-events: all;
-
     span {
         user-select: text !important;
     }
@@ -49,7 +46,6 @@
     width: 100%;
     height: 100%;
     position: absolute;
-    pointer-events: all;
 }
 
 .webBox-button {
@@ -59,7 +55,7 @@
     height: 100%;
 }
 
-.webView-urlEditor {
+.webBox-urlEditor {
     position: relative;
     opacity: 0.9;
     z-index: 9001;
@@ -74,6 +70,12 @@
         padding-bottom: 10px;
         overflow: hidden;
 
+        .webBox-freeze {
+            display: flex;
+            align-items: center; 
+            justify-content: center;
+        }
+
         .editorBase {
             display: flex;
 
@@ -115,7 +117,6 @@
     width: 100%;
     height: 100%;
     position: absolute;
-    pointer-events: all;
 
     .indicator {
         position: absolute;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index b48437464..26e947f5b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
-import { faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { faStickyNote, faLock, faUnlock } from '@fortawesome/free-solid-svg-icons';
 import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, FieldResult } from "../../../new_fields/Doc";
@@ -23,6 +23,7 @@ import React = require("react");
 import * as WebRequest from 'web-request';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
+import { DocumentView } from "./DocumentView";
 const htmlToText = require("html-to-text");
 
 library.add(faStickyNote);
@@ -116,31 +117,21 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
-
-    switchToText = () => {
-        let url: string = "";
-        const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
-        if (field) url = field.url.href;
-
-        const newBox = Docs.Create.TextDocument(url, {
-            x: NumCast(this.rootDoc.x),
-            y: NumCast(this.rootDoc.y),
-            title: url,
-            _width: 200,
-            _height: 70,
-        });
-
-        SelectionManager.SelectedDocuments().map(dv => {
-            dv.props.addDocument?.(newBox);
-            dv.props.removeDocument?.(dv.rootDoc);
-        });
-
-        Doc.BrushDoc(newBox);
+    toggleNativeDimensions = () => {
+        if (this.Document._nativeWidth || this.Document._nativeHeight) {
+            DocumentView.unfreezeNativeDimensions(this.layoutDoc);
+            this.layoutDoc.lockedTransform = false;
+        }
+        else {
+            Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
+            this.layoutDoc.lockedTransform = true;
+        }
     }
 
     urlEditor() {
+        const frozen = this.layoutDoc._nativeWidth && this.layoutDoc._nativeHeight;
         return (
-            <div className="webView-urlEditor" style={{ top: this.collapsed ? -70 : 0 }}>
+            <div className="webBox-urlEditor" style={{ top: this.collapsed ? -70 : 0 }}>
                 <div className="urlEditor">
                     <div className="editorBase">
                         <button className="editor-collapse"
@@ -169,8 +160,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                                 <button className="submitUrl" onClick={this.submitURL}>
                                     SUBMIT
                                 </button>
-                                <div className="switchToText" title="Convert web to text doc" onClick={this.switchToText} style={{ display: "flex", alignItems: "center", justifyContent: "center" }} >
-                                    <FontAwesomeIcon icon={faStickyNote} size={"lg"} />
+                                <div className="webBox-freeze" title={frozen ? "Unfreeze" : "Freeze Dimensions"} onClick={this.toggleNativeDimensions} >
+                                    <FontAwesomeIcon icon={frozen ? faUnlock : faLock} size={"lg"} />
                                 </div>
                             </div>
                         </div>
@@ -331,7 +322,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     {content}
                 </div>
                 {!frozen ? (null) :
-                    <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
+                    <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
+                        onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
                         <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
                             <div className="indicator" ref={this._iframeIndicatorRef}></div>
                             <div className="dragger" ref={this._iframeDragRef}></div>
@@ -350,7 +342,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             }} >
             {this.content}
             <div className={"webBox-outerContent"} ref={this._outerRef}
-                style={{ pointerEvents: this.layoutDoc._nativeHeight ? "all" : "none" }}
+                style={{ pointerEvents: this.layoutDoc._nativeHeight && !this.layoutDoc.isBackground ? "all" : "none" }}
                 onWheel={e => e.stopPropagation()}
                 onScroll={e => {
                     if (this._iframeRef.current!.contentDocument!.children[0].scrollTop !== this._outerRef.current!.scrollTop) {
-- 
cgit v1.2.3-70-g09d2


From 0bc20bc8c1f2b6b77624d37047d63a31fa0cf299 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 Apr 2020 11:25:30 -0400
Subject: added additional rendition menu item

---
 src/client/views/collections/CollectionView.tsx | 67 ++++++++++++++-----------
 1 file changed, 37 insertions(+), 30 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 877e4355f..178c3189b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -192,38 +192,45 @@ export class CollectionView extends Touchable<FieldViewProps> {
     }
 
 
+    setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
+        const existingVm = ContextMenu.Instance.findByDescription(category);
+        const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+
+        subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
+        if (addExtras && CollectionView._safeMode) {
+            ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
+        }
+        subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" });
+        subItems.push({ description: "Treeview", event: () => func(CollectionViewType.Tree), icon: "tree" });
+        subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" });
+        subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
+        subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" });
+        subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
+        subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
+        subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
+        subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
+        subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
+        subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+        if (addExtras) switch (this.props.Document._viewType) {
+            case CollectionViewType.Freeform: {
+                subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
+                break;
+            }
+        }
+        addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
+        !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" });
+    }
+
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            const existingVm = ContextMenu.Instance.findByDescription("View Modes...");
-            const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
-            subItems.push({ description: "Freeform", event: () => { this.props.Document._viewType = CollectionViewType.Freeform; }, icon: "signature" });
-            if (CollectionView._safeMode) {
-                ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document._viewType = CollectionViewType.Invalid, icon: "project-diagram" });
-            }
-            subItems.push({ description: "Schema", event: () => this.props.Document._viewType = CollectionViewType.Schema, icon: "th-list" });
-            subItems.push({ description: "Treeview", event: () => this.props.Document._viewType = CollectionViewType.Tree, icon: "tree" });
-            subItems.push({ description: "Stacking", event: () => this.props.Document._viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
-            subItems.push({
-                description: "Stacking (AutoHeight)", event: () => {
-                    this.props.Document._viewType = CollectionViewType.Stacking;
-                    this.props.Document._autoHeight = true;
-                }, icon: "ellipsis-v"
-            });
-            subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" });
-            subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" });
-            subItems.push({ description: "Multirow", event: () => this.props.Document._viewType = CollectionViewType.Multirow, icon: "columns" });
-            subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" });
-            subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
-            subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
-            subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" });
-            switch (this.props.Document._viewType) {
-                case CollectionViewType.Freeform: {
-                    subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
-                    break;
-                }
-            }
-            subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
-            !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
+
+            this.setupViewTypes("View Modes ...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true);
+            this.setupViewTypes("Additional Rendition ...", vtype => {
+                const newRendition = Doc.MakeAlias(this.props.Document);
+                newRendition._viewType = vtype;
+                this.props.addDocTab(newRendition, "onRight");
+                return newRendition;
+            }, false);
 
             const existing = ContextMenu.Instance.findByDescription("Layout...");
             const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
-- 
cgit v1.2.3-70-g09d2


From 3d629495644631e6b614b1b48f1d32d6c7aeefc6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 Apr 2020 17:25:17 -0400
Subject: added parameterization capability to layout strings.  added Div's
 that can take Dash field values

---
 src/client/util/RichTextSchema.tsx              |  6 ++--
 src/client/views/DocumentDecorations.tsx        |  4 +--
 src/client/views/collections/CollectionView.tsx |  7 ++--
 src/client/views/nodes/DocumentContentsView.tsx | 45 +++++++++++++++++++++----
 4 files changed, 46 insertions(+), 16 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 1522f5e21..d23962d5c 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -852,9 +852,9 @@ export class DashDocView {
             };
             this._renderDisposer?.();
             this._renderDisposer = reaction(() => {
-                if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
-                    finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here?
-                }
+                // if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
+                //     finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here?
+                // }
                 const layoutKey = StrCast(finalLayout.layoutKey);
                 const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
                 if (finalLayout !== dashDoc && finalKey) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c8766a6b3..bd41788d4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -3,7 +3,7 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym } from "../../new_fields/Doc";
+import { Doc, DataSym, Field } from "../../new_fields/Doc";
 import { PositionDocument } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
@@ -366,7 +366,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || "";
             }
             if (this._titleControlString.startsWith("#")) {
-                return selected.props.Document[this._titleControlString.substring(1)]?.toString() || "-unset-";
+                return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
             }
             return this._accumulatedTitle;
         } else if (SelectionManager.SelectedDocuments().length > 1) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 178c3189b..4448fcf21 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -211,11 +211,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
         subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
-        if (addExtras) switch (this.props.Document._viewType) {
-            case CollectionViewType.Freeform: {
-                subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
-                break;
-            }
+        if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) {
+            subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
         }
         addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
         !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" });
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 7522af3a3..765416658 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,8 +1,8 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../new_fields/Doc";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { OmitKeys, Without } from "../../../Utils";
+import { Doc, Opt, Field } from "../../../new_fields/Doc";
+import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { OmitKeys, Without, emptyPath } from "../../../Utils";
 import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -53,6 +53,31 @@ class ObserverJsxParser1 extends JsxParser {
 
 const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
+
+interface DivProps {
+    Document: Doc;
+}
+
+@observer
+export class Div extends React.Component<DivProps> {
+    render() {
+        const style: { [key: string]: any } = {};
+        const divKeys = OmitKeys(this.props, ["children", "Document", "key", "__proto__"]).omit;
+        Object.keys(divKeys).map((prop: string) => {
+            let p = (this.props as any)[prop] as string;
+            if (p?.startsWith("@")) {  // bcz: extend this to support expression -- is this really a script?
+                const key = p.substring(1);
+                const n = Cast(this.props.Document[key], "number", null);
+                p = n ? n.toString() : StrCast(this.props.Document[key], p);
+            }
+            style[prop] = p;
+        });
+        return <div style={style}>
+            {this.props.children}
+        </div>;
+    }
+}
+
 @observer
 export class DocumentContentsView extends React.Component<DocumentViewProps & {
     isSelected: (outsideReaction: boolean) => boolean,
@@ -102,21 +127,29 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
     render() {
         TraceMobx();
-        return (this.props.renderDepth > 12 || !this.layout || !this.layoutDoc) ? (null) :
+        let layoutFrame = this.layout;
+        const replacer = (match: any, p1: string, offset: any, string: any) => {
+            const n = Cast(this.props.Document[p1], "number", null);
+            return n !== undefined ? n.toString() : StrCast(this.props.Document[p1], p1);
+        };
+        layoutFrame = layoutFrame.replace(/\{([a-zA-Z0-9_-]+)\}/g, replacer);
+        return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
             this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
                 <FormattedTextBox {...this.CreateBindings().props} fieldKey={this.props.forceFieldKey} />
                 :
                 <ObserverJsxParser
+                    key={42}
                     blacklistedAttrs={[]}
+                    renderInWrapper={false}
                     components={{
                         FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
                         CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
                         PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
                         ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
-                        RecommendationsBox, ScreenshotBox
+                        RecommendationsBox, ScreenshotBox, Div
                     }}
                     bindings={this.CreateBindings()}
-                    jsx={this.layout}
+                    jsx={layoutFrame}
                     showWarnings={true}
 
                     onError={(test: any) => { console.log(test); }}
-- 
cgit v1.2.3-70-g09d2


From 04bc902ad31eaf07e3a5edc34854744a88ffb3bc Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 22 Apr 2020 14:08:19 -0400
Subject: fixed clicking gear icon on doc decorations.  moved repl to Drag
 items.  Removed Open... menu.  Added Open FIelds & Full SCreen to Open New
 Persepectives.

---
 src/client/views/DocumentDecorations.tsx               |  4 ++--
 src/client/views/MainView.tsx                          |  3 ++-
 src/client/views/OverlayView.tsx                       |  6 +++++-
 src/client/views/collections/CollectionView.tsx        |  8 ++------
 src/client/views/nodes/DocumentView.tsx                | 11 ++---------
 src/server/authentication/models/current_user_utils.ts |  9 +++++----
 6 files changed, 18 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index bd41788d4..5d6242201 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -144,8 +144,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action onSettingsClick = (e: PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey) {
             let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0];
-            while (child.children.length && child.className !== "jsx-parser") child = child.children[0];
-            simulateMouseClick(child.children[0], e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+            while (child.children.length) child = child.children[0];
+            simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f8e246315..8fb67c435 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faTerminal, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons';
+import { faTerminal, faCalculator, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -103,6 +103,7 @@ export class MainView extends React.Component {
         }
 
         library.add(faTerminal);
+        library.add(faCalculator);
         library.add(faWindowMaximize);
         library.add(faFileAlt);
         library.add(faAddressCard);
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 614217bc8..20aa14f84 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -9,6 +9,8 @@ import { Transform } from "../util/Transform";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
 import { DocumentView } from "./nodes/DocumentView";
 import './OverlayView.scss';
+import { Scripting } from "../util/Scripting";
+import { ScriptingRepl } from './ScriptingRepl';
 
 export type OverlayDisposer = () => void;
 
@@ -210,4 +212,6 @@ export class OverlayView extends React.Component {
             </div>
         );
     }
-}
\ No newline at end of file
+}
+// bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error
+Scripting.addGlobal(function addOverlayWindow(Tag: string, options: OverlayElementOptions) { const x = <ScriptingRepl />; OverlayView.Instance.addWindow(x, options); });
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4448fcf21..ade54f2c9 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -221,8 +221,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
 
-            this.setupViewTypes("View Modes ...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true);
-            this.setupViewTypes("Additional Rendition ...", vtype => {
+            this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true);
+            this.setupViewTypes("Open New Perspective...", vtype => {
                 const newRendition = Doc.MakeAlias(this.props.Document);
                 newRendition._viewType = vtype;
                 this.props.addDocTab(newRendition, "onRight");
@@ -242,10 +242,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
             !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
 
-            const open = ContextMenu.Instance.findByDescription("Open...");
-            const openItems = open && "subitems" in open ? open.subitems : [];
-            !open && ContextMenu.Instance.addItem({ description: "Open...", subitems: openItems, icon: "hand-point-right" });
-
             const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
             const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
             const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField }];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 286044ab2..d8783c458 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,8 +40,6 @@ import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
 import { InkingControl } from '../InkingControl';
 import { KeyphraseQueryView } from '../KeyphraseQueryView';
-import { OverlayView } from '../OverlayView';
-import { ScriptingRepl } from '../ScriptingRepl';
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import { RadialMenu } from './RadialMenu';
@@ -737,17 +735,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
         !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
 
-        const open = ContextMenu.Instance.findByDescription("Open...");
+        const open = cm.findByDescription("Open New Perspective...");
         const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
         openItems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" });
-        openItems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, "inTab", this.props.LibraryPath), icon: "folder" });
-        openItems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
-        openItems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" });
-        openItems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" });
         openItems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
         templateDoc && openItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
-        openItems.push({ description: "Open Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
-        !open && cm.addItem({ description: "Open...", subitems: openItems, icon: "external-link-alt" });
+        !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" });
 
 
         const existingOnClick = cm.findByDescription("OnClick...");
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 3955f64fc..b4f9ead53 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -84,7 +84,7 @@ export class CurrentUserUtils {
 
         if (doc["template-buttons"] === undefined) {
             doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], {
-                title: "Template Item Creators", _xMargin: 0, _showTitle: "title",
+                title: "Compound Item Creators", _xMargin: 0, _showTitle: "title",
                 _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
@@ -186,6 +186,7 @@ export class CurrentUserUtils {
             { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
             { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
             { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
+            { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
             { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
             { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
             { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
@@ -194,10 +195,10 @@ export class CurrentUserUtils {
             // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
             // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
             // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
-            { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
             { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
-            // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" },
+            { title: "Drag a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
         ];
+
     }
 
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
@@ -229,7 +230,7 @@ export class CurrentUserUtils {
 
         if (dragCreatorSet === undefined) {
             doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
-                title: "Standard Item Creators", _showTitle: "title", _xMargin: 0,
+                title: "Basic Item Creators", _showTitle: "title", _xMargin: 0,
                 _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
-- 
cgit v1.2.3-70-g09d2


From 72f614f023a4bac6a2f0b63a1b1a16e6f4545c7a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 23 Apr 2020 00:53:08 -0400
Subject: fixed browser back after link navigation. simplified menus more.
 fixed jitter mode to be seeded. fixed menu gear for  collections. .

---
 src/client/util/History.ts                         |  6 +++-
 src/client/util/ProsemirrorExampleTransfer.ts      |  4 +--
 src/client/views/DocumentDecorations.tsx           |  6 +++-
 .../views/collections/CollectionDockingView.tsx    |  6 +++-
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  4 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 21 +++++------
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  9 +++--
 src/client/views/nodes/DocumentBox.tsx             |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 41 ++++++++++++++++++++--
 src/client/views/nodes/KeyValueBox.tsx             | 23 ++++++++----
 src/client/views/nodes/LinkAnchorBox.tsx           |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  2 +-
 src/client/views/nodes/SliderBox.tsx               |  2 +-
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 20 files changed, 103 insertions(+), 39 deletions(-)

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

diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 545e8acb4..2c53d7e52 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -40,8 +40,12 @@ export namespace HistoryUtil {
         // }
     }
 
+    let _lastStatePush = 0;
     export function pushState(state: ParsedUrl) {
-        history.pushState(state, "", createUrl(state));
+        if (Date.now() - _lastStatePush > 1000) {
+            history.pushState(state, "", createUrl(state));
+        }
+        _lastStatePush = Date.now();
     }
 
     export function replaceState(state: ParsedUrl) {
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index 42247f177..680f48f70 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -154,7 +154,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
         const originalDoc = layoutDoc.rootDocument || layoutDoc;
         if (originalDoc instanceof Doc) {
             const newDoc = Docs.Create.TextDocument("", {
-                title: "", layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
+                layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
                 x: NumCast(originalDoc.x), y: NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10, _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height)
             });
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -172,7 +172,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
         const originalDoc = layoutDoc.rootDocument || layoutDoc;
         if (force || props.Document._singleLine) {
             const newDoc = Docs.Create.TextDocument("", {
-                title: "", layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
+                layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine),
                 x: NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10, y: NumCast(originalDoc.y), _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height)
             });
             FormattedTextBox.SelectOnLoad = newDoc[Id];
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 43c7751fa..6e699bc68 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -134,7 +134,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action onSettingsClick = (e: PointerEvent): void => {
         if (e.button === 0 && !e.altKey && !e.ctrlKey) {
             let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0];
-            while (child.children.length) child = child.children[0];
+            while (child.children.length) {
+                const next = Array.from(child.children).find(c => !c.className.includes("collectionViewChrome"));
+                if (next) child = next;
+                else break;
+            }
             simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         }
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5e77bc0bb..0d859c3f1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -29,6 +29,7 @@ import "./CollectionDockingView.scss";
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { DockingViewButtonSelector } from './ParentDocumentSelector';
 import React = require("react");
+import { CollectionViewType } from './CollectionView';
 library.add(faFile);
 const _global = (window /* browser */ || global /* node */) as any;
 
@@ -93,6 +94,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @undoBatch
     @action
     public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
+        if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
+            return MainView.Instance.openWorkspace(docView.props.Document);
+        }
         const document = Doc.MakeAlias(docView.props.Document);
         const newItemStackConfig = {
             type: 'stack',
@@ -771,7 +775,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
-        if (doc.dockingConfig) {
+        if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
             return MainView.Instance.openWorkspace(doc);
         } else if (location === "onRight") {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b15649d83..24a3119cc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -397,7 +397,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
-            ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
         }
     }
 
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 742a818bc..06ebf6d2d 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -102,7 +102,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         layoutItems.push({ description: "Auto Time/Pivot layout", event: () => { doc._forceRenderEngine = undefined; }, icon: "compress-arrows-alt" });
         layoutItems.push({ description: "Sync with presentation", event: () => CollectionTimeView.SyncTimelineToPresentation(doc), icon: "compress-arrows-alt" });
 
-        ContextMenu.Instance.addItem({ description: "Pivot/Time Options ...", subitems: layoutItems, icon: "eye" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
     }
     @computed get _allFacets() {
         const facets = new Set<string>();
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f589c2c76..a052d045c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -711,7 +711,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
             layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
             layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
-            ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
         }
         ContextMenu.Instance.addItem({
             description: "Buxton Layout", icon: "eye", event: () => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ade54f2c9..4f1242c33 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -229,7 +229,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
                 return newRendition;
             }, false);
 
-            const existing = ContextMenu.Instance.findByDescription("Layout...");
+            const existing = ContextMenu.Instance.findByDescription("Options...");
             const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
             if (this.props.Document.childLayout instanceof Doc) {
@@ -240,7 +240,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             }
             layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
-            !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
+            !existing && ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" });
 
             const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
             const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3b5101a4d..55e55e56c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1048,16 +1048,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     onContextMenu = (e: React.MouseEvent) => {
         if (this.props.children && this.props.annotationsKey) return;
-        const layoutItems: ContextMenuProps[] = [];
-
-        layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
-        layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
-        layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
-        layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
-        layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
+        const options = ContextMenu.Instance.findByDescription("Options...");
+        const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+
+        optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
+        optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
+        optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
+        optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
         // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
-        layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
-        layoutItems.push({
+        optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
+        optionItems.push({
             description: "Import document", icon: "upload", event: ({ x, y }) => {
                 const input = document.createElement("input");
                 input.type = "file";
@@ -1085,7 +1086,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             }
         });
 
-        ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
     }
 
     private childViews = () => {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8f40ea2be..6ff6d1b42 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -162,7 +162,7 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
 
-        ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
     stopRecording = action(() => {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3a7e005ac..615b05e43 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,3 @@
-import anime from "animejs";
 import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
@@ -29,8 +28,14 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
     @observable _animPos: number[] | undefined = undefined;
+    random(min: number, max: number) { // min should not be equal to max
+        const mseed = Math.abs(this.X * this.Y);
+        const seed = (mseed * 9301 + 49297) % 233280;
+        var rnd = seed / 233280;
+        return min + rnd * (max - min);
+    }
     get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
-    get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${anime.random(-1, 1) * this.props.jitterRotation}deg)`; }
+    get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
     get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
     get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
     get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index ac562f19a..7583aa070 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -45,7 +45,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         funcs.push({ description: (this.props.Document.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => Doc.GetProto(this.props.Document).excludeCollections = !this.props.Document.excludeCollections, icon: "expand-arrows-alt" });
         funcs.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
 
-        ContextMenu.Instance.addItem({ description: "DocumentBox Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
     @computed get contentDoc() {
         return (this.props.Document.isTemplateDoc || this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a19ba6506..cab7b965a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -741,7 +741,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const existingOnClick = cm.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-        onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(this, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
+        onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
         onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
         onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
         onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2c89d53d8..1329f7dcf 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
 import { ReplaceStep } from 'prosemirror-transform';
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../new_fields/DateField';
-import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { InkTool } from '../../../new_fields/InkField';
@@ -48,6 +48,7 @@ import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './Format
 import React = require("react");
 import { PrefetchProxy } from '../../../new_fields/Proxy';
 import { makeTemplate } from '../../util/DropConverter';
+import { DocumentView } from './DocumentView';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -396,6 +397,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         return Cast(Doc.UserDoc().defaultTextLayout, Doc, null) || StrCast(Doc.UserDoc().defaultTextLayout, null);
     }
     specificContextMenu = (e: React.MouseEvent): void => {
+        const cm = ContextMenu.Instance;
+
         const funcs: ContextMenuProps[] = [];
         this.props.Document.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" });
         funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
@@ -407,10 +410,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         });
         funcs.push({ description: "Toggle Single Line", event: () => this.props.Document._singleLine = !this.props.Document._singleLine, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Sidebar", event: () => this.props.Document._showSidebar = !this.props.Document._showSidebar, icon: "expand-arrows-alt" });
-        funcs.push({ description: "Toggle Audio", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
+        funcs.push({ description: "Toggle Dictation Icon", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" });
+
+        const highlighting: ContextMenuProps[] = [];
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
-            funcs.push({
+            highlighting.push({
                 description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
                     e.stopPropagation();
                     if (FormattedTextBox._highlights.indexOf(option) === -1) {
@@ -421,8 +426,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     this.updateHighlights();
                 }, icon: "expand-arrows-alt"
             }));
+        funcs.push({ description: "highlighting...", subitems: highlighting, icon: "hand-point-right" });
 
         ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+
+        const change = cm.findByDescription("Change Perspective...");
+        const changeItems: ContextMenuProps[] = change && "subitems" in change ? change.subitems : [];
+
+        const noteTypesDoc = Cast(Doc.UserDoc().noteTypes, Doc, null);
+        const noteTypes = DocListCast(noteTypesDoc?.data);
+        noteTypes.forEach(note => {
+            changeItems.push({
+                description: StrCast(note.title), event: () => {
+                    Doc.setNativeView(this.props.Document);
+                    DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
+                }, icon: "eye"
+            })
+        });
+        changeItems.push({ description: "FreeForm", event: () => DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" })
+        !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
+
+        const open = cm.findByDescription("Open New Perspective...");
+        const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+
+        openItems.push({
+            description: "FreeForm", event: () => {
+                const alias = Doc.MakeAlias(this.rootDoc);
+                DocumentView.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform");
+                this.props.addDocTab(alias, "onRight");
+            }, icon: "eye"
+        })
+        !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" });
+
     }
 
     recordDictation = () => {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 7aad6f90e..2970674a2 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -16,6 +16,8 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./KeyValueBox.scss";
 import { KeyValuePair } from "./KeyValuePair";
 import React = require("react");
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
 
 export type KVPScript = {
     script: CompiledScript;
@@ -34,11 +36,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
-    get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; }
-
-    constructor(props: FieldViewProps) {
-        super(props);
-    }
+    get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
 
     @action
     onEnterKey = (e: React.KeyboardEvent): void => {
@@ -234,13 +232,26 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         return new Doc;
     }
 
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const cm = ContextMenu.Instance;
+        const open = cm.findByDescription("Change Perspective...");
+        const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+        openItems.push({
+            description: "Default Perspective", event: () => {
+                this.props.addDocTab(this.fieldDocToLayout, "inTab");
+                this.props.addDocTab(this.props.Document, "close");
+            }, icon: "image"
+        });
+        !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
+    }
+
     render() {
         const dividerDragger = this.splitPercentage === 0 ? (null) :
             <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
                 <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
             </div>;
 
-        return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} ref={this._mainCont}>
+        return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}>
             <table className="keyValueBox-table">
                 <tbody className="keyValueBox-tbody">
                     <tr className="keyValueBox-header">
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 8e3c15908..eb647d0e4 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -109,7 +109,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
         funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" });
         funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" });
 
-        ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
     render() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6db36e43c..3712c648e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -210,7 +210,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
         pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Fit Width " + (this.Document._fitWidth ? "Off" : "On"), event: () => this.Document._fitWidth = !this.Document._fitWidth, icon: "expand-arrows-alt" });
 
-        ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
     @computed get contentScaling() { return this.props.ContentScaling(); }
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 11b24b059..125690dc7 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -120,7 +120,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
                     this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
                 }), icon: "expand-arrows-alt"
             });
-            ContextMenu.Instance.addItem({ description: "Screenshot Funcs...", subitems: subitems, icon: "video" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
         }
     }
 
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 746ea0b64..b2d451ea8 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -40,7 +40,7 @@ export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocume
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: "Edit Thumb Change Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Thumb Change ...", this.props.Document, "onThumbChange", obj.x, obj.y) });
-        ContextMenu.Instance.addItem({ description: "Slider Funcs...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
     onChange = (values: readonly number[]) => runInAction(() => {
         this.dataDoc[this.minThumbKey] = values[0];
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 588068334..613929bca 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -209,7 +209,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
                 }), icon: "expand-arrows-alt"
             });
-            ContextMenu.Instance.addItem({ description: "Video Funcs...", subitems: subitems, icon: "video" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
         }
     }
 
-- 
cgit v1.2.3-70-g09d2

