From 820d40eea4eeb5977889e0ef6c35f9092df44b4b Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Wed, 10 Aug 2022 17:08:47 -0400
Subject: created placeholder loading box but have to get location of final doc
 to update with loading box

---
 src/client/views/nodes/DocumentContentsView.tsx | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 381436a56..4d4985f2a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -43,6 +43,7 @@ import { VideoBox } from './VideoBox';
 import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
+import { LoadingBox } from './LoadingBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -267,6 +268,7 @@ export class DocumentContentsView extends React.Component<
                     DataVizBox,
                     HTMLtag,
                     ComparisonBox,
+                    LoadingBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From 653afba6635d676ec4fcdfa649360ca26c91cb88 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 22 Aug 2022 17:23:21 -0400
Subject: from last

---
 .../collectionFreeForm/CollectionFreeFormView.tsx       |  4 ++--
 src/client/views/nodes/DocumentContentsView.tsx         | 17 ++++++++---------
 2 files changed, 10 insertions(+), 11 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a807ba4ea..b34dad226 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1314,7 +1314,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     });
 
     getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
-        const curFrame = NumCast(this.Document._currentFrame);
+        const curFrame = Cast(this.Document._currentFrame, 'number');
         const childDoc = params.pair.layout;
         const childDocLayout = Doc.Layout(childDoc);
         const { z, zIndex } = childDoc;
@@ -1324,7 +1324,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             x: NumCast(x),
             y: NumCast(y),
             z: Cast(z, 'number'),
-            backgroundColor: Cast(backgroundColor, 'string') ?? this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.BackgroundColor),
+            backgroundColor: Cast(backgroundColor, 'string') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.BackgroundColor),
             opacity: Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
             zIndex: Cast(zIndex, 'number'),
             width: Cast(childDocLayout._width, 'number'),
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 381436a56..d065c62fb 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -25,7 +25,7 @@ import './DocumentView.scss';
 import { EquationBox } from './EquationBox';
 import { FieldView, FieldViewProps } from './FieldView';
 import { FilterBox } from './FilterBox';
-import { FormattedTextBox, FormattedTextBoxProps } from './formattedText/FormattedTextBox';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { FunctionPlotBox } from './FunctionPlotBox';
 import { ImageBox } from './ImageBox';
 import { KeyValueBox } from './KeyValueBox';
@@ -114,14 +114,13 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
 
 @observer
 export class DocumentContentsView extends React.Component<
-    DocumentViewProps &
-        FormattedTextBoxProps & {
-            isSelected: (outsideReaction: boolean) => boolean;
-            select: (ctrl: boolean) => void;
-            NativeDimScaling?: () => number;
-            setHeight?: (height: number) => void;
-            layoutKey: string;
-        }
+    DocumentViewProps & {
+        isSelected: (outsideReaction: boolean) => boolean;
+        select: (ctrl: boolean) => void;
+        NativeDimScaling?: () => number;
+        setHeight?: (height: number) => void;
+        layoutKey: string;
+    }
 > {
     @computed get layout(): string {
         TraceMobx();
-- 
cgit v1.2.3-70-g09d2


From b8d4b08716791246847d2a647a9df1f37508b87f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Nov 2022 14:43:47 -0500
Subject: making pasting form pdf work again with backlinks. text fixes for
 equationViews and dashField views -- trying to get docref linkAnchor to work.

---
 src/client/views/LightboxView.tsx                  |  2 +-
 src/client/views/PreviewCursor.tsx                 |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 26 +++----
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/formattedText/DashFieldView.scss   |  1 +
 .../views/nodes/formattedText/DashFieldView.tsx    |  6 +-
 .../views/nodes/formattedText/EquationView.tsx     | 12 ++-
 .../nodes/formattedText/FormattedTextBox.scss      |  4 +
 .../views/nodes/formattedText/FormattedTextBox.tsx | 87 ++++++++--------------
 src/client/views/nodes/formattedText/marks_rts.ts  | 18 +----
 src/client/views/pdf/PDFViewer.tsx                 |  4 +
 12 files changed, 75 insertions(+), 93 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 22b0380a2..a9fba3688 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -292,7 +292,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                             PanelWidth={this.lightboxWidth}
                             PanelHeight={this.lightboxHeight}
                             LayoutTemplate={LightboxView.LightboxDocTemplate}
-                            isDocumentActive={returnFalse}
+                            isDocumentActive={returnTrue}
                             isContentActive={returnTrue}
                             styleProvider={DefaultStyleProvider}
                             ScreenToLocalTransform={this.lightboxScreenToLocal}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 119476210..3712fff58 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -100,9 +100,9 @@ export class PreviewCursor extends React.Component<{}> {
                     batch.end();
                     e.stopPropagation();
                 } else {
-                    // creates text document
                     FormattedTextBox.PasteOnLoad = e;
-                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(DocUtils.GetNewTextDoc('-pasted text-', newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), 'paste');
+                    if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault();
+                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), 'paste');
                 }
             }
             //pasting in images
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ac3777aa6..b83d911c7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,5 +1,5 @@
 import { Bezier } from 'bezier-js';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { DateField } from '../../../../fields/DateField';
@@ -1987,19 +1987,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 {this._firstRender ? this.placeholder : this.marqueeView}
                 {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
 
-                {
-                    // uncomment to show snap lines
-                    <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
-                        <svg style={{ width: '100%', height: '100%' }}>
-                            {this._hLines?.map(l => (
-                                <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />
-                            ))}
-                            {this._vLines?.map(l => (
-                                <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
-                            ))}
-                        </svg>
-                    </div>
-                }
+                {/* // uncomment to show snap lines */}
+                <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
+                    <svg style={{ width: '100%', height: '100%' }}>
+                        {this._hLines?.map(l => (
+                            <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />
+                        ))}
+                        {this._vLines?.map(l => (
+                            <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
+                        ))}
+                    </svg>
+                </div>
 
                 {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? (
                     <div
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 6d6609317..3ab7cc0bc 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,4 +1,4 @@
-import { computed } from 'mobx';
+import { computed, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { AclPrivate, Doc, Opt } from '../../../fields/Doc';
 import { ScriptField } from '../../../fields/ScriptField';
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a35400e72..9dc4b36cf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Document } from '../../../fields/documentSchemas';
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index f17579853..ad315acc8 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -34,6 +34,7 @@
         display: inline-block;
         background-color: rgba(155, 155, 155, 0.24);
         span {
+            user-select: all;
             min-width: 100%;
             display: inline-block;
         }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index afae59733..39b38add2 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -124,7 +124,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                 return (
                     <span
                         className="dashFieldView-fieldSpan"
-                        contentEditable={this.props.editable}
+                        contentEditable={true}
                         style={{ display: strVal.length < 2 ? 'inline-block' : undefined }}
                         suppressContentEditableWarning={true}
                         defaultValue={strVal}
@@ -152,6 +152,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // we need to handle all key events on the input span or else they will propagate to prosemirror.
     @action
     fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => {
+        if (e.key === 'c' && (e.ctrlKey || e.metaKey)) {
+            navigator.clipboard.writeText(window.getSelection()?.toString() || '');
+            return;
+        }
         if (e.key === 'Enter') {
             // handle the enter key by "submitting" the current text to Dash's database.
             this.updateText(span.textContent!, true);
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 0fd2a7808..89f2a7c81 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -1,5 +1,5 @@
 import EquationEditor from 'equation-editor-react';
-import { IReactionDisposer } from 'mobx';
+import { IReactionDisposer, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { TextSelection } from 'prosemirror-state';
 import * as ReactDOM from 'react-dom/client';
@@ -12,7 +12,11 @@ import React = require('react');
 export class EquationView {
     dom: HTMLDivElement; // container for label and value
     root: any;
+    tbox: FormattedTextBox;
+    view: any;
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+        this.tbox = tbox;
+        this.view = view;
         this.dom = document.createElement('div');
         this.dom.style.width = node.attrs.width;
         this.dom.style.height = node.attrs.height;
@@ -34,7 +38,11 @@ export class EquationView {
         this._editor?.mathField.focus();
     }
     selectNode() {
-        this._editor?.mathField.focus();
+        this.tbox._applyingChange = this.tbox.fieldKey; // setting focus will make prosemirror lose focus, which will cause it to change its selection to a text selection, which causes this view to get rebuilt but it's no longer node selected, so the equationview won't have focus
+        setTimeout(() => {
+            this._editor?.mathField.focus();
+            setTimeout(() => (this.tbox._applyingChange = ''));
+        });
     }
     deselectNode() {}
 }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index d3d8c47c0..93dc0b2a1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -225,6 +225,8 @@ footnote::after {
 
 .prosemirror-attribution {
     font-size: 8px;
+    float: right;
+    display: inline;
 }
 
 .footnote-tooltip::before {
@@ -740,6 +742,8 @@ footnote::after {
 
     .prosemirror-attribution {
         font-size: 8px;
+        float: right;
+        display: inline;
     }
 
     .footnote-tooltip::before {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 63435eea8..fc8d2b7dd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { isEqual } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { baseKeymap, selectAll } from 'prosemirror-commands';
 import { history } from 'prosemirror-history';
@@ -87,7 +87,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _editorView: Opt<EditorView>;
-    private _applyingChange: string = '';
+    public _applyingChange: string = '';
     private _searchIndex = 0;
     private _lastTimedMark: Mark | undefined = undefined;
     private _cachedLinks: Doc[] = [];
@@ -499,12 +499,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const end = this._editorView.state.doc.nodeSize - 2;
             this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
         }
-        if (FormattedTextBox.PasteOnLoad) {
-            const pdfDocId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfOrigin');
-            const pdfRegionId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfRegion');
-            FormattedTextBox.PasteOnLoad = undefined;
-            setTimeout(() => pdfDocId && pdfRegionId && this.addPdfReference(pdfDocId, pdfRegionId, undefined), 10);
-        }
     };
     adoptAnnotation = (start: number, end: number, mark: Mark) => {
         const view = this._editorView!;
@@ -1235,61 +1229,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => {
-        const cbe = event as ClipboardEvent;
-        const pdfDocId = cbe.clipboardData?.getData('dash/pdfOrigin');
-        const pdfRegionId = cbe.clipboardData?.getData('dash/pdfRegion');
-        return pdfDocId && pdfRegionId && this.addPdfReference(pdfDocId, pdfRegionId, slice) ? true : false;
+        const pdfAnchorId = (event as ClipboardEvent).clipboardData?.getData('dash/pdfAnchor');
+        return pdfAnchorId && this.addPdfReference(pdfAnchorId) ? true : false;
     };
 
-    addPdfReference = (pdfDocId: string, pdfRegionId: string, slice?: Slice) => {
+    addPdfReference = (pdfAnchorId: string) => {
         const view = this._editorView!;
-        if (pdfDocId && pdfRegionId) {
-            DocServer.GetRefField(pdfDocId).then(pdfDoc => {
-                DocServer.GetRefField(pdfRegionId).then(pdfRegion => {
-                    if (pdfDoc instanceof Doc && pdfRegion instanceof Doc) {
-                        setTimeout(async () => {
-                            const targetField = Doc.LayoutFieldKey(pdfDoc);
-                            const targetAnnotations = await DocListCastAsync(pdfDoc[DataSym][targetField + '-annotations']); // bcz: better to have the PDF's view handle updating its own annotations
-                            if (targetAnnotations) targetAnnotations.push(pdfRegion);
-                            else Doc.AddDocToList(pdfDoc[DataSym], targetField + '-annotations', pdfRegion);
-                        });
-
-                        const link = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: pdfRegion }, 'PDF pasted');
-                        if (link) {
-                            const linkId = link[Id];
-                            const quote = view.state.schema.nodes.blockquote.create({ content: addMarkToFrag(slice?.content || view.state.doc.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId)) });
-                            const newSlice = new Slice(Fragment.from(quote), slice?.openStart || 0, slice?.openEnd || 0);
-                            if (slice) {
-                                view.dispatch(view.state.tr.replaceSelection(newSlice).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
-                            } else {
-                                selectAll(view.state, (tx: Transaction) => view.dispatch(tx.replaceSelection(newSlice).scrollIntoView()));
-                            }
-                        }
+        if (pdfAnchorId) {
+            DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
+                if (pdfAnchor instanceof Doc) {
+                    const dashField = view.state.schema.nodes.paragraph.create({}, [
+                        view.state.schema.nodes.dashField.create({ fieldKey: 'text', docid: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
+                            view.state.schema.marks.linkAnchor.create({
+                                allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
+                                location: 'add:right',
+                                title: `from: ${DocCast(pdfAnchor.context).title}`,
+                                noPreview: true,
+                                docref: false,
+                            }),
+                            view.state.schema.marks.pFontSize.create({ fontSize: '8px' }),
+                            view.state.schema.marks.em.create({}),
+                        ]),
+                    ]);
+
+                    const link = DocUtils.MakeLink({ doc: pdfAnchor }, { doc: this.rootDoc }, 'PDF pasted');
+                    if (link) {
+                        view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
                     }
-                });
+                }
             });
             return true;
         }
         return false;
-
-        function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) {
-            const nodes: Node[] = [];
-            frag.forEach(node => nodes.push(marker(node)));
-            return Fragment.fromArray(nodes);
-        }
-
-        function addLinkMark(node: Node, title: string, linkId: string) {
-            if (!node.isText) {
-                const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId));
-                return node.copy(content);
-            }
-            const marks = [...node.marks];
-            const linkIndex = marks.findIndex(mark => mark.type.name === 'link');
-            const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }];
-            const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: 'add:right', title, docref: true });
-            marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
-            return node.mark(marks);
-        }
     };
 
     isActiveTab(el: Element | null | undefined) {
@@ -1417,6 +1388,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
                 ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
             ];
+            if (FormattedTextBox.PasteOnLoad) {
+                const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
+                FormattedTextBox.PasteOnLoad = undefined;
+                pdfAnchorId && this.addPdfReference(pdfAnchorId);
+            }
         }
         FormattedTextBox.DontSelectInitialText = false;
     }
@@ -1530,6 +1506,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         //applyDevTools.applyDevTools(this._editorView);
         FormattedTextBox.Focused = this;
         this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
+        this.startUndoTypingBatch();
     };
     @observable public static Focused: FormattedTextBox | undefined;
 
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 97549830c..8d43c33f0 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -97,12 +97,9 @@ export const marks: { [index: string]: MarkSpec } = {
             return node.attrs.docref && node.attrs.title
                 ? [
                       'div',
-                      ['span', `"`],
                       ['span', 0],
-                      ['span', `"`],
-                      ['br'],
                       [
-                          'a',
+                          'span',
                           {
                               ...node.attrs,
                               class: 'prosemirror-attribution',
@@ -110,19 +107,8 @@ export const marks: { [index: string]: MarkSpec } = {
                           },
                           node.attrs.title,
                       ],
-                      ['br'],
                   ]
-                : //node.attrs.allLinks.length === 1 ?
-                  ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
-            // ["div", { class: "prosemirror-anchor" },
-            //     ["span", { class: "prosemirror-linkBtn" },
-            //         ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
-            //         ["input", { class: "prosemirror-hrefoptions" }],
-            //     ],
-            //     ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
-            //         ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
-            //     )]
-            // ];
+                : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
         },
     },
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index abc7336bd..7f99c30e3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -131,6 +131,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
     copy = (e: ClipboardEvent) => {
         if (this.props.isContentActive() && e.clipboardData) {
             e.clipboardData.setData('text/plain', this._selectionText);
+            const anchor = this._getAnchor();
+            if (anchor) {
+                e.clipboardData.setData('dash/pdfAnchor', anchor[Id]);
+            }
             e.preventDefault();
         }
     };
-- 
cgit v1.2.3-70-g09d2


From 67316c700980fe653c48840407dc9d66a7ed8a2b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 Jan 2023 00:35:43 -0500
Subject: added zoom box highlighting of text anchors in pdf/web pages for
 link/pres following. Added serial/parallel option for presentation group with
 up.  Added direct pinning of text seletions to trails.  fixed 'hide' option
 for preselements to work with hidebefore/after

---
 src/client/util/DocumentManager.ts                 | 67 ++++++++++++-----
 src/client/util/LinkFollower.ts                    |  9 ++-
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/MarqueeAnnotator.tsx              | 42 ++++++-----
 src/client/views/collections/CollectionSubView.tsx |  2 -
 src/client/views/collections/TabDocView.tsx        |  5 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  9 ++-
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.scss           | 16 +++++
 src/client/views/nodes/DocumentView.tsx            | 38 +++++++++-
 src/client/views/nodes/MapBox/MapBox.tsx           |  9 ---
 src/client/views/nodes/PDFBox.tsx                  |  7 ++
 src/client/views/nodes/WebBox.scss                 |  1 +
 src/client/views/nodes/WebBox.tsx                  | 27 +++++--
 src/client/views/nodes/trails/PresBox.tsx          | 83 ++++++++++++++--------
 src/client/views/nodes/trails/PresElementBox.tsx   |  3 +-
 src/client/views/pdf/PDFViewer.tsx                 | 10 ++-
 src/fields/Doc.ts                                  | 16 +++--
 18 files changed, 241 insertions(+), 106 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index d2e9e17b4..70fe7f2c0 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,8 +1,8 @@
-import { action, observable, runInAction } from 'mobx';
-import { Doc, Opt } from '../../fields/Doc';
+import { action, observable, ObservableSet, runInAction } from 'mobx';
+import { AnimationSym, Doc, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { listSpec } from '../../fields/Schema';
-import { Cast, DocCast } from '../../fields/Types';
+import { Cast, DocCast, StrCast } from '../../fields/Types';
 import { AudioField } from '../../fields/URLField';
 import { returnFalse } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -42,7 +42,7 @@ export class DocumentManager {
     callAddViewFuncs = (view: DocumentView) => {
         const callFuncs = this._viewRenderedCbs.filter(vc => vc.doc === view.rootDoc);
         if (callFuncs.length) {
-            this._viewRenderedCbs = this._viewRenderedCbs.filter(vc => callFuncs.includes(vc));
+            this._viewRenderedCbs = this._viewRenderedCbs.filter(vc => !callFuncs.includes(vc));
             const intTimer = setInterval(
                 () => {
                     if (!view.ComponentView?.incrementalRendering?.()) {
@@ -147,14 +147,15 @@ export class DocumentManager {
 
     public getDocumentView(toFind: Doc, preferredCollection?: CollectionView): DocumentView | undefined {
         const found =
-            Array.from(DocumentManager.Instance.DocumentViews).find(
-                dv =>
-                    ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFind.data as any)?.url?.href) ||
-                    ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href)
-            )?.rootDoc ?? toFind;
+            // Array.from(DocumentManager.Instance.DocumentViews).find(
+            //     dv =>
+            //         ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFind.data as any)?.url?.href) ||
+            //         ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href)
+            // )?.rootDoc ??
+            toFind;
         return this.getDocumentViewById(found[Id], preferredCollection);
     }
-    
+
     public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
         const views: DocumentView[] = [];
         Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
@@ -167,11 +168,12 @@ export class DocumentManager {
     };
     public getDocumentViews(toFindIn: Doc): DocumentView[] {
         const toFind =
-            Array.from(DocumentManager.Instance.DocumentViews).find(
-                dv =>
-                    ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFindIn.data as any)?.url?.href) ||
-                    ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFindIn.annotationOn)?.data as any)?.url?.href)
-            )?.rootDoc ?? toFindIn;
+            // Array.from(DocumentManager.Instance.DocumentViews).find(
+            //     dv =>
+            //         ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFindIn.data as any)?.url?.href) ||
+            //         ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFindIn.annotationOn)?.data as any)?.url?.href)
+            // )?.rootDoc ??
+            toFindIn;
 
         const toReturn: DocumentView[] = [];
         const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
@@ -203,6 +205,11 @@ export class DocumentManager {
         }
     }
 
+    public static removeOverlayViews() {
+        DocumentManager._overlayViews?.forEach(action(view => (view.textHtmlOverlay = undefined)));
+        DocumentManager._overlayViews?.clear();
+    }
+    static _overlayViews = new ObservableSet<DocumentView>();
     static addView = (doc: Doc, finished?: () => void) => {
         CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
         finished?.();
@@ -225,7 +232,7 @@ export class DocumentManager {
                 docView?.props.bringToFront(resolvedTarget);
             });
         }
-        const focusAndFinish = (didFocus: boolean) => {
+        const focusAndFinish = action((didFocus: boolean) => {
             const finalTargetDoc = resolvedTarget;
             if (options.toggleTarget) {
                 if (!didFocus && !wasHidden) {
@@ -236,12 +243,26 @@ export class DocumentManager {
                 finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined);
                 !options.noSelect && docView?.select(false);
             }
+            if (targetDoc.textHtml && options.zoomTextSelections) {
+                const containerView = DocumentManager.Instance.getFirstDocumentView(finalTargetDoc);
+                if (containerView) {
+                    containerView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect));
+                    containerView.textHtmlOverlay = StrCast(targetDoc.textHtml);
+                    DocumentManager._overlayViews.add(containerView);
+                    if (Doc.UnhighlightTimer) {
+                        Doc.AddUnHighlightWatcher(() => {
+                            DocumentManager.removeOverlayViews();
+                            containerView.htmlOverlayEffect = '';
+                        });
+                    } else setTimeout(() => (containerView.htmlOverlayEffect = ''));
+                }
+            }
             finished?.();
-        };
+        });
         const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && this.getFirstDocumentView(annotatedDoc);
         if (annoContainerView) {
             if (annoContainerView.props.Document.layoutKey === 'layout_icon') {
-                return annoContainerView.iconify(() => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(resolvedTarget ?? targetDoc, { ...options, toggleTarget: false }, createViewFunc, docContextPath, finished)), 30);
+                return annoContainerView.iconify(() => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(targetDoc, { ...options, originalTarget, toggleTarget: false }, createViewFunc, docContextPath, finished)), 30);
             }
             if (!docView && targetDoc.type !== DocumentType.MARKER) {
                 annoContainerView.focus(targetDoc, {}); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
@@ -254,7 +275,15 @@ export class DocumentManager {
         const targetDocContextView = (targetDocContext && this.getFirstDocumentView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (focusView) {
-            !options.noSelect && Doc.linkFollowHighlight(focusView.rootDoc, undefined, options.effect); //TODO:glr make this a setting in PresBox
+            if (focusView.rootDoc === originalTarget) {
+                if (!options.noSelect) Doc.linkFollowHighlight(focusView.rootDoc, undefined, options.effect); //TODO:glr make this a setting in PresBox
+                else {
+                    focusView.rootDoc[AnimationSym] = options.effect;
+                    if (Doc.UnhighlightTimer) {
+                        Doc.AddUnHighlightWatcher(action(() => (focusView.rootDoc[AnimationSym] = undefined)));
+                    }
+                }
+            }
             if (options.playAudio) DocumentManager.playAudioAnno(focusView.rootDoc);
             const doFocus = (forceDidFocus: boolean) =>
                 focusView.focus(originalTarget, {
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 94badbb44..d5ef9fab6 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,12 +1,10 @@
 import { action, runInAction } from 'mobx';
-import { Doc, DocListCast, Opt, WidthSym } from '../../fields/Doc';
-import { listSpec } from '../../fields/Schema';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
 import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView';
-import { PresEffect, PresEffectDirection } from '../views/nodes/trails';
 import { DocumentManager } from './DocumentManager';
 import { LinkManager } from './LinkManager';
 import { UndoManager } from './UndoManager';
@@ -59,7 +57,7 @@ export class LinkFollower {
                 createTabForTarget(false);
             } else {
                 // first focus & zoom onto this (the clicked document).  Then execute the function to focus on the target
-                docViewProps.focus(sourceDoc, { willPan: true, willPanZoom: BoolCast(sourceDoc.followLinkZoom, true), zoomScale: 1, afterFocus: createTabForTarget });
+                docViewProps.focus(sourceDoc, { willPan: true, willPanZoom: BoolCast(sourceDoc.followLinkZoom, true), zoomTime: 1000, zoomScale: 1, afterFocus: createTabForTarget });
             }
         };
         runInAction(() => (DocumentDecorations.Instance.overrideBounds = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value
@@ -70,7 +68,7 @@ export class LinkFollower {
             docViewProps.ContainingCollectionDoc,
             action(() => {
                 batch.end();
-                Doc.AddUnlightWatcher(action(() => (DocumentDecorations.Instance.overrideBounds = false)));
+                Doc.AddUnHighlightWatcher(action(() => (DocumentDecorations.Instance.overrideBounds = false)));
             }),
             altKey ? true : undefined
         );
@@ -110,6 +108,7 @@ export class LinkFollower {
                         easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any,
                         effect: sourceDoc,
                         originatingDoc: sourceDoc,
+                        zoomTextSelections: false,
                     };
                     if (target.TourMap) {
                         const fieldKey = Doc.LayoutFieldKey(target);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 42e259eed..39cc9ba8e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -482,6 +482,7 @@ export class MainView extends React.Component {
     }
 
     globalPointerDown = action((e: PointerEvent) => {
+        DocumentManager.removeOverlayViews();
         Doc.linkFollowUnhighlight();
         AudioBox.Enabled = true;
         const targets = document.elementsFromPoint(e.x, e.y);
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 2fdb59361..bf1242346 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -18,7 +18,7 @@ const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
     rootDoc: Doc;
-    down: number[];
+    down?: number[];
     iframe?: () => undefined | HTMLIFrameElement;
     scrollTop: number;
     scaling?: () => number;
@@ -45,6 +45,17 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     @observable private _width: number = 0;
     @observable private _height: number = 0;
 
+    constructor(props: any) {
+        super(props);
+
+        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('', true), true);
+        AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true));
+        AnchorMenu.Instance.OnAudio = unimplementedFunction;
+        AnchorMenu.Instance.Highlight = this.highlight;
+        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations);
+        AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor;
+    }
+
     @action
     static clearAnnotations(savedAnnotations: ObservableMap<number, HTMLDivElement[]>) {
         AnchorMenu.Instance.Status = 'marquee';
@@ -54,24 +65,21 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         savedAnnotations.clear();
     }
 
-    @action componentDidMount() {
-        // set marquee x and y positions to the spatially transformed position
-        const boundingRect = this.props.mainCont.getBoundingClientRect();
-        this._startX = this._left = (this.props.down[0] - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width);
-        this._startY = this._top = (this.props.down[1] - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop;
-        this._height = this._width = 0;
+    @action gotDownPoint() {
+        if (!this._width && !this._height) {
+            const downPt = this.props.down!;
+            // set marquee x and y positions to the spatially transformed position
+            const boundingRect = this.props.mainCont.getBoundingClientRect();
+            this._startX = this._left = (downPt[0] - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width);
+            this._startY = this._top = (downPt[1] - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop;
+        }
 
         const doc = this.props.iframe?.()?.contentDocument ?? document;
+        doc.removeEventListener('pointermove', this.onSelectMove);
+        doc.removeEventListener('pointerup', this.onSelectEnd);
         doc.addEventListener('pointermove', this.onSelectMove);
         doc.addEventListener('pointerup', this.onSelectEnd);
 
-        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('', true), true);
-        AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true));
-        AnchorMenu.Instance.OnAudio = unimplementedFunction;
-        AnchorMenu.Instance.Highlight = this.highlight;
-        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations);
-        AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor;
-
         /**
          * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.
          * It also initiates a Drag/Drop interaction to place the text annotation.
@@ -125,7 +133,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                   });
               });
     }
-    componentWillUnmount() {
+    releaseDownPt() {
         const doc = this.props.iframe?.()?.contentDocument ?? document;
         doc.removeEventListener('pointermove', this.onSelectMove);
         doc.removeEventListener('pointerup', this.onSelectEnd);
@@ -259,6 +267,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 this.highlight('rgba(245, 230, 95, 0.75)', false); // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
             }
             this.props.finishMarquee(undefined, undefined, e);
+            runInAction(() => (this._width = this._height = 0));
         } else {
             runInAction(() => (this._width = this._height = 0));
             this.props.finishMarquee(cliX, cliY, e);
@@ -266,8 +275,9 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     };
 
     render() {
-        return (
+        return !this.props.down ? null : (
             <div
+                ref={r => (r ? this.gotDownPoint() : this.releaseDownPt())}
                 className="marqueeAnnotator-dragBox"
                 style={{
                     left: `${this._left}px`,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b66dc0aa2..4f3f21cea 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -172,8 +172,6 @@ export function CollectionSubView<X>(moreProps?: X) {
                 // The following conditional detects a recurring bug we've seen on the server
                 if (proto[Id] === Docs.Prototypes.get(DocumentType.COL)[Id]) {
                     alert('COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...');
-                    console.log(doc);
-                    console.log(proto);
                     throw new Error(`AHA! You were trying to set a cursor on a collection's proto, which is the original collection proto! Look at the two previously printed lines for document values!`);
                 }
                 let cursors = Cast(proto.cursors, listSpec(CursorField));
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e45e2a1cb..cd58319cb 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -247,8 +247,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 alert('Cannot pin presentation document to itself');
                 return;
             }
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
+            const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.();
+            const pinDoc = Doc.MakeAlias(anchorDoc ?? doc);
+            pinDoc.presentationTargetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
             pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
             pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 82b97dff0..dc0eb69f3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1146,6 +1146,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return new Promise<number>(res => setTimeout(() => res(runInAction(() => (this._panZoomTransition = 0))), this._panZoomTransition)); // set transition to be smooth, then reset
     }
 
+    _focusCount = 0;
     focusDocument = (doc: Doc, options: DocFocusOptions) => {
         const state = HistoryUtil.getState();
 
@@ -1187,6 +1188,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 this.setPan(panX, panY, focusSpeed, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
             }
 
+            const focusCount = ++this._focusCount;
             const startTime = Date.now();
             // focus on this collection within its parent view.  the parent view after focusing determines whether to reset the view change within the collection
             const endFocus = async (moved: boolean) => {
@@ -1200,7 +1202,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         this.Document[this.scaleFieldKey] = restoreState.scale;
                     }
                 }
-                runInAction(() => (this._panZoomTransition = 0));
+                this._focusCount === focusCount && didMove && runInAction(() => (this._panZoomTransition = 0));
                 return resetView;
             };
             const xf = !cantTransform
@@ -2284,5 +2286,8 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
     runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
 });
 ScriptingGlobals.add(function pinWithView(readOnly: boolean, pinDocContent: boolean) {
-    !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) }));
+    !readOnly &&
+        SelectionManager.Views().forEach(view =>
+            TabDocView.PinDoc(view.rootDoc, { currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) })
+        );
 });
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 3ab7cc0bc..569579996 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -59,7 +59,7 @@ class ObserverJsxParser1 extends JsxParser {
     }
 }
 
-const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
+export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
 interface HTMLtagProps {
     Document: Doc;
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index abf6e37ab..453bdac8e 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -51,6 +51,22 @@
         height: calc(100% - 20px);
     }
 
+    .documentView-htmlOverlay {
+        position: absolute;
+        display: flex;
+        top: 0;
+        height: 100%;
+        width: 100%;
+        .documentView-htmlOverlayInner {
+            box-shadow: black 0.2vw 0.2vw 0.8vw;
+            background: rgb(255, 255, 255);
+            overflow: auto;
+            position: relative;
+            margin: auto;
+            padding: 20px;
+        }
+    }
+
     .documentView-linkAnchorBoxAnchor {
         display: flex;
         overflow: hidden;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 76cc6800a..95cf08289 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import { ObserverJsxParser } from './DocumentContentsView';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
 import { AclAdmin, AclEdit, AclPrivate, AnimationSym, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Document } from '../../../fields/documentSchemas';
@@ -110,6 +111,7 @@ export interface DocFocusOptions {
     effect?: Doc; // animation effect for focus
     noSelect?: boolean; // whether target should be selected after focusing
     playAudio?: boolean; // whether to play audio annotation on focus
+    zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
     toggleTarget?: boolean; // whether to toggle target on and off
     originatingDoc?: Doc; // document that triggered the focus
     easeFunc?: 'linear' | 'ease'; // transition method for scrolling
@@ -590,9 +592,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, { ...options, instant: options?.instant || LinkDocPreview.LinkInfo ? true : false });
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing;
+        const startTime = Date.now();
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options,
-            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), focusSpeed ?? 0)),
+            afterFocus: (didFocus: boolean) =>
+                new Promise<ViewAdjustment>(async res =>
+                    setTimeout(
+                        async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), //
+                        didFocus ? Math.max(0, (options.zoomTime ?? 500) - (Date.now() - startTime)) : 0
+                    )
+                ),
         });
     };
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
@@ -1098,11 +1107,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     };
     @observable _retryThumb = 1;
     thumbShown = () => {
+        const childHighlighted = () =>
+            Array.from(Doc.highlightedDocs.keys())
+                .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
+                .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
+        const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
         return !this.props.isSelected() &&
             LightboxView.LightboxDoc !== this.rootDoc &&
             this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
-            (!Doc.isBrushedHighlightedDegree(this.props.Document) || this.rootDoc._viewType === CollectionViewType.Docking) &&
+            ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._viewType === CollectionViewType.Docking) &&
             !this._componentView?.isAnyChildContentActive?.()
             ? true
             : false;
@@ -1754,6 +1768,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
 
+    @observable textHtmlOverlay: Opt<string>;
     @computed get anchorViewDoc() {
         return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined;
     }
@@ -1793,6 +1808,24 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     isHovering = () => this._isHovering;
     @observable _isHovering = false;
 
+    htmlOverlayEffect = '';
+    @computed get htmlOverlay() {
+        const effectProps = {
+            delay: 0,
+            duration: 500,
+        };
+        const highlight = (
+            <div className="webBox-textHighlight">
+                <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
+            </div>
+        );
+        return !this.textHtmlOverlay ? null : (
+            <div className="documentView-htmlOverlay">
+                <div className="documentView-htmlOverlayInner">{<Fade {...effectProps}>{DocumentViewInternal.AnimationEffect(highlight, { presEffect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc, this.rootDoc)} </Fade>}</div>
+            </div>
+        );
+    }
+
     render() {
         TraceMobx();
         const xshift = Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined;
@@ -1841,6 +1874,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             focus={this.props.focus || emptyFunction}
                             ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
                         />
+                        {this.htmlOverlay}
                     </div>
                 )}
 
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index b0f6f8358..c8d5b0154 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -322,7 +322,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         console.log('print all sidebar Docs');
-        console.log(this.allSidebarDocs);
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
@@ -337,8 +336,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 }
             }
         }); //add to annotation list
-        console.log('sidebaraddDocument');
-        console.log(doc);
 
         return this.addDocument(doc, sidebarKey); // add to sidebar list
     };
@@ -352,10 +349,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (this.layoutDoc._showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
-        docs.forEach(doc => {
-            console.log(this.allMapMarkers);
-            console.log(this.allSidebarDocs);
-        });
         return this.removeDocument(doc, sidebarKey);
     };
 
@@ -405,7 +398,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     @action
     private handlePlaceChanged = () => {
-        console.log(this.searchBox);
         const place = this.searchBox.getPlace();
 
         if (!place.geometry || !place.geometry.location) {
@@ -416,7 +408,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         // zoom in on the location of the search result
         if (place.geometry.viewport) {
-            console.log(this._map);
             this._map.fitBounds(place.geometry.viewport);
         } else {
             this._map.setCenter(place.geometry.location);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 88aac67a7..e22ee5021 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -213,6 +213,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), options) ?? (didToggle ? 1 : undefined);
     };
     getAnchor = () => {
+        let ele: Opt<HTMLDivElement> = undefined;
+        if (this._pdfViewer?.selectionContent()) {
+            ele = document.createElement('div');
+            ele.append(this._pdfViewer.selectionContent()!);
+        }
         const docAnchor = () => {
             const anchor = Docs.Create.TextanchorDocument({
                 title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
@@ -222,6 +227,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             return anchor;
         };
         const anchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ?? docAnchor();
+        anchor.text = ele?.textContent ?? '';
+        anchor.textHtml = ele?.innerHTML;
         this.addDocument(anchor);
         return anchor;
     };
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index a41f66ef0..6f578a9fc 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -7,6 +7,7 @@
     left: 0;
     position: relative;
     display: flex;
+    overflow: hidden;
 
     .webBox-sideResizer {
         position: absolute;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a3e83f047..8be4884ce 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -29,7 +29,7 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
 import { Annotation } from '../pdf/Annotation';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentViewProps } from './DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewInternal, DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { LinkDocPreview } from './LinkDocPreview';
 import { VideoBox } from './VideoBox';
@@ -54,10 +54,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _keyInput = React.createRef<HTMLInputElement>();
     private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop));
-    private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = '';
+
+    private get _getAnchor() {
+        return AnchorMenu.Instance?.GetAnchor;
+    }
     @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.
     @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
     @observable private _searching: boolean = false;
@@ -285,7 +288,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
     scrollFocus = (doc: Doc, options: DocFocusOptions) => {
-        if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), options.instant);
+        if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), options.instant);
         if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(options.instant);
         }
@@ -305,6 +308,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     getAnchor = () => {
+        let ele: Opt<HTMLDivElement> = undefined;
+        try {
+            const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents();
+            if (contents) {
+                ele = document.createElement('div');
+                ele.append(contents);
+            }
+        } catch (e) {}
         const anchor =
             this._getAnchor(this._savedAnnotations) ??
             Docs.Create.WebanchorDocument(this._url, {
@@ -312,6 +323,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 y: NumCast(this.layoutDoc._scrollTop),
                 unrendered: true,
             });
+        anchor.text = ele?.textContent ?? '';
+        anchor.textHtml = ele?.innerHTML;
         this.addDocumentWrapper(anchor);
         return anchor;
     };
@@ -553,8 +566,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     static urlHash = (s: string) => {
+        const split = s.split('');
         return Math.abs(
-            s.split('').reduce((a: any, b: any) => {
+            split.reduce((a: any, b: any) => {
                 a = (a << 5) - a + b.charCodeAt(0);
                 return a & a;
             }, 0)
@@ -686,7 +700,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
     @action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
-        this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this._marqueeing = undefined;
         this._isAnnotating = false;
         this._iframeClick = undefined;
@@ -1006,7 +1019,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 selectionText={returnEmptyString}
                                 annotationLayer={this._annotationLayer.current}
                                 mainCont={this._mainCont.current}
-                            />{' '}
+                            />
                         </div>
                     )}
                 </div>
@@ -1043,5 +1056,5 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 }
 ScriptingGlobals.add(function urlHash(url: string) {
-    return WebBox.urlHash(url);
+    return url ? WebBox.urlHash(url) : 0;
 });
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index b26f485be..2e3135416 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -65,7 +65,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     private _disposers: { [name: string]: IReactionDisposer } = {};
-    private _obDisposers: { [name: string]: any } = {};
     public selectedArray = new ObservableSet<Doc>();
 
     @observable public static Instance: PresBox;
@@ -142,20 +141,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // Turn of progressivize editors
         this.turnOffEdit(true);
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        Object.values(this._obDisposers).forEach(disposer => disposer?.());
     }
 
     @action
     componentDidMount() {
-        this._obDisposers.anim = observe(
-            this,
-            'activeItem',
-            change => {
-                change.oldValue && (DocCast((change.oldValue as Doc).presentationTargetDoc)[AnimationSym] = undefined);
-                change.newValue && (DocCast((change.newValue as Doc).presentationTargetDoc)[AnimationSym] = change.newValue as Doc);
-            },
-            true
-        );
         this._disposers.keyboard = reaction(
             () => this.selectedDoc,
             selected => {
@@ -217,16 +206,37 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions
     // No more frames in current doc and next slide is defined, therefore move to next slide
     nextSlide = (slideNum?: number) => {
-        CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
-        let nextSelected = slideNum ?? this.itemIndex + 1;
-        this.gotoDocument(nextSelected, this.activeItem);
-        for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) {
-            if (this.childDocs[nextSelected].groupWithUp) {
-                this.gotoDocument(nextSelected, this.activeItem, true);
-            } else {
-                break;
+        const nextSlideInd = slideNum ?? this.itemIndex + 1;
+        let curSlideInd = nextSlideInd;
+        const resetSelection = action(() => {
+            this.clearSelectedArray();
+            for (let i = nextSlideInd; i <= curSlideInd; i++) {
+                this.addToSelectedArray(this.childDocs[i]);
             }
-        }
+        });
+        CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
+        this.clearSelectedArray();
+        const doGroupWithUp =
+            (nextSelected: number, force = false) =>
+            () => {
+                if (nextSelected < this.childDocs.length) {
+                    if (force || this.childDocs[nextSelected].groupWithUp) {
+                        const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
+                        if (serial) {
+                            this.gotoDocument(nextSelected, this.activeItem, true, async () => {
+                                const waitTime = NumCast(this.activeItem.presDuration) - NumCast(this.activeItem.presTransition);
+                                await new Promise<void>(res => setTimeout(() => res(), Math.max(0, waitTime)));
+                                doGroupWithUp(nextSelected + 1)();
+                            });
+                        } else {
+                            this.gotoDocument(nextSelected, this.activeItem, undefined, resetSelection);
+                            curSlideInd = this.itemIndex;
+                            doGroupWithUp(nextSelected + 1)();
+                        }
+                    }
+                }
+            };
+        doGroupWithUp(curSlideInd, true)();
     };
 
     // Called when the user activates 'next' - to move to the next part of the pres. trail
@@ -270,9 +280,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //The function that is called when a document is clicked or reached through next or back.
     //it'll also execute the necessary actions if presentation is playing.
     @undoBatch
-    public gotoDocument = action((index: number, from?: Doc, group?: boolean) => {
+    public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => {
         Doc.UnBrushAllDocs();
-
         if (index >= 0 && index < this.childDocs.length) {
             this.rootDoc._itemIndex = index;
             const activeItem: Doc = this.activeItem;
@@ -280,10 +289,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
             if (activeFrame !== undefined) {
                 const transTime = NumCast(activeItem.presTransition, 500);
-                const context = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
+                const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
+                const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
                 if (context) {
                     const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView;
-                    if (ffview) {
+                    if (ffview?.childDocs) {
                         this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime);
                         context._currentFrame = NumCast(activeFrame);
                     }
@@ -302,7 +312,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (!group) this.clearSelectedArray();
             this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
             this.turnOffEdit();
-            this.navigateToActiveItem(); //Handles movement to element only when presTrail is list
+            this.navigateToActiveItem(finished); //Handles movement to element only when presTrail is list
             this.onHideDocument(); //Handles hide after/before
         }
     });
@@ -489,13 +499,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * a new tab. If presCollection is undefined it will open the document
      * on the right.
      */
-    navigateToActiveItem = () => {
+    navigateToActiveItem = (afterNav?: () => void) => {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
+        const finished = () => {
+            afterNav?.();
+            console.log('Finish Slide Nav: ' + targetDoc.title);
+            targetDoc[AnimationSym] = undefined;
+        };
         const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null);
         const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
         const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
-        const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc);
+        const includesDoc: boolean = DocumentManager.Instance.getDocumentView(targetDoc) ? true : false; //   DocListCast(presCollection?.data).includes(targetDoc);
         const tabMap = CollectionDockingView.Instance?.tabMap;
         const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc === srcContext || tab.DashDoc === targetDoc);
         // Handles the setting of presCollection
@@ -517,13 +532,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             selViewCache.forEach(doc => this.addToSelectedArray(doc));
             this._dragArray.splice(0, this._dragArray.length, ...dragViewCache);
             this._eleArray.splice(0, this._eleArray.length, ...eleViewCache);
+            finished();
         });
         const createDocView = (doc: Doc, finished?: () => void) => {
             DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.());
             (collectionDocView ?? this).props.addDocTab(doc, OpenWhere.lightbox);
             this.layoutDoc.presCollection = targetDoc;
         };
-        PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc || tab ? undefined : resetSelection);
+        PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc || tab ? finished : resetSelection);
     };
 
     static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, srcContext: Doc, finished?: () => void) {
@@ -552,11 +568,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
                     zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
                     zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
+                    effect: activeItem,
                     noSelect: true,
                     originatingDoc: activeItem,
                     easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
+                    zoomTextSelections: true
                 };
-
+                if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
                 var containerDocContext = srcContext ? [srcContext] : [];
                 while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
                     containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
@@ -577,12 +595,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.childDocs.forEach((doc, index) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
-            //if (tagDoc) tagDoc.opacity = 1;
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
-            const curInd: number = itemIndexes.indexOf(index);
             if (tagDoc === this.layoutDoc.presCollection) {
                 tagDoc.opacity = 1;
             } else {
+                if (curDoc.presHide) {
+                    if (index !== this.itemIndex) {
+                        tagDoc.opacity = 1;
+                    }
+                }
                 const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
                 if (curDoc.presHideBefore && index === hidingIndBef) {
                     if (index > this.itemIndex) {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 4469be488..5e1474b89 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -507,11 +507,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
                     <div
                         className="slideButton"
-                        onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)}
+                        onClick={() => (activeItem.groupWithUp = (NumCast(activeItem.groupWithUp) + 1) % 3)}
                         style={{
                             zIndex: 1000 - this.indexInPres,
                             fontWeight: 700,
                             backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+                            outline: NumCast(activeItem.groupWithUp) > 1 ? 'solid black 1px' : undefined,
                             height: activeItem.groupWithUp ? 53 : 18,
                             transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
                         }}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 906dff377..f95d5ac2e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -68,9 +68,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
-    public _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _selectionText: string = '';
+    private _selectionContent: DocumentFragment | undefined;
     private _downX: number = 0;
     private _downY: number = 0;
     private _lastSearch = false;
@@ -78,8 +78,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _ignoreScroll = false;
     private _initialScroll: { loc: Opt<number>; easeFunc: 'linear' | 'ease' | undefined } | undefined;
     private _forcedScroll = true;
+    get _getAnchor() {
+        return AnchorMenu.Instance?.GetAnchor;
+    }
 
     selectionText = () => this._selectionText;
+    selectionContent = () => this._selectionContent;
 
     @observable isAnnotating = false;
     // key where data is stored
@@ -392,7 +396,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action
     finishMarquee = (x?: number, y?: number) => {
-        this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this.isAnnotating = false;
         this._marqueeing = undefined;
         this._textSelecting = true;
@@ -437,7 +440,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 }
             }
         }
-        this._selectionText = selRange.cloneContents().textContent || '';
+        this._selectionContent = selRange.cloneContents();
+        this._selectionText = this._selectionContent?.textContent || '';
 
         // clear selection
         if (sel.empty) {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6762665a2..19ffc5005 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1,6 +1,6 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { saveAs } from 'file-saver';
-import { action, computed, observable, ObservableMap, runInAction } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet, runInAction } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { alias, map, serializable } from 'serializr';
 import { DocServer } from '../client/DocServer';
@@ -343,6 +343,10 @@ export class Doc extends RefField {
     @observable public [DirectLinksSym]: Set<Doc> = new Set();
     @observable public [AnimationSym]: Opt<Doc>;
     @observable public [HighlightSym]: boolean = false;
+    static __Anim(Doc: Doc) {
+        // for debugging to print AnimationSym field easily.
+        return Doc[AnimationSym];
+    }
 
     private [UpdatingFromServer]: boolean = false;
     private [ForceServerWrite]: boolean = false;
@@ -1131,7 +1135,7 @@ export namespace Doc {
         BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
         SearchMatchDoc: ObservableMap<Doc, { searchMatch: number }> = new ObservableMap();
     }
-    const brushManager = new DocBrush();
+    export const brushManager = new DocBrush();
 
     export class DocData {
         @observable _user_doc: Doc = undefined!;
@@ -1284,8 +1288,8 @@ export namespace Doc {
     }
 
     let UnhighlightWatchers: (() => void)[] = [];
-    let UnhighlightTimer: any;
-    export function AddUnlightWatcher(watcher: () => void) {
+    export let UnhighlightTimer: any;
+    export function AddUnHighlightWatcher(watcher: () => void) {
         if (UnhighlightTimer) {
             UnhighlightWatchers.push(watcher);
         } else watcher();
@@ -1302,16 +1306,16 @@ export namespace Doc {
         }, 5000);
     }
 
-    var highlightedDocs = new Set<Doc>();
+    export var highlightedDocs = new ObservableSet<Doc>();
     export function IsHighlighted(doc: Doc) {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
         return doc[HighlightSym] || Doc.GetProto(doc)[HighlightSym];
     }
     export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) {
         runInAction(() => {
-            doc[AnimationSym] = presEffect;
             highlightedDocs.add(doc);
             doc[HighlightSym] = true;
+            doc[AnimationSym] = presEffect;
             if (dataAndDisplayDocs) {
                 highlightedDocs.add(Doc.GetProto(doc));
                 Doc.GetProto(doc)[HighlightSym] = true;
-- 
cgit v1.2.3-70-g09d2


From 9d2af1180f0dd5af5ab86b922cd8b0cdfcf4ea09 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sat, 28 Jan 2023 22:38:41 -0500
Subject: checkpoint: schemarow as documentview functioning

---
 package-lock.json                                  |   39 -
 src/client/documents/Documents.ts                  |    4 +
 .../collectionSchema/CollectionSchemaView.scss     |    6 +
 .../collectionSchema/CollectionSchemaView.tsx      |  129 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   80 +-
 .../OldCollectionSchemaCells.tsx                   | 1366 +++++++++----------
 .../OldCollectionSchemaMovableColumn.tsx           |  250 ++--
 .../OldCollectionSchemaMovableRow.tsx              |  274 ++--
 .../OldCollectionSchemaView.tsx                    | 1298 +++++++++---------
 .../old_collectionSchema/OldSchemaTable.tsx        | 1388 ++++++++++----------
 src/client/views/nodes/DocumentContentsView.tsx    |    2 +
 11 files changed, 2451 insertions(+), 2385 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 26e7a41d7..7f93ef3c8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5303,16 +5303,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6480,28 +6470,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.62",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6513,7 +6481,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -21699,12 +21666,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1fd07d61d..58e318879 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1150,6 +1150,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
         }
 
+        export function SchemaRowDocument(options?: DocumentOptions) {
+            return InstanceFromProto(Prototypes.get(DocumentType.SCHEMAROW), undefined, { ...(options || {}) });
+        }
+
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, viewType: CollectionViewType.Docking, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 4fa5d80e2..e0d0101a2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -126,10 +126,16 @@
     }
 }
 
+.schema-row-wrapper {
+    max-height: 70px;
+    overflow: hidden;
+}
+
 .schema-header-row,
 .schema-row {
     display: flex;
     flex-direction: row;
+    height: 100%;
     max-height: 70px;
     overflow: auto;
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f7c68c803..c9f934aec 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,14 +1,14 @@
 import React = require('react');
 import { action, computed, observable, ObservableMap, ObservableSet, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
-import { Cast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -21,6 +21,9 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
+import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { Transform } from '../../../util/Transform';
 
 export enum ColumnType {
     Number,
@@ -40,7 +43,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _closestDropIndex: number = 0;
     private _minColWidth: number = 150;
 
-    @observable _rowMenuWidth: number = 100;
+    public static _rowMenuWidth: number = 100;
     @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
     @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _isDragging: boolean = false;
@@ -69,16 +72,15 @@ export class CollectionSchemaView extends CollectionSubView() {
         let widths = Cast(
             this.layoutDoc.columnWidths,
             listSpec('number'),
-            this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length)
+            this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
         );
 
         const totalWidth = widths.reduce((sum, width) => sum + width, 0);
-        if (totalWidth !== this.props.PanelWidth() - this._rowMenuWidth) {
+        if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) {
             widths = widths.map(w => {
                 const proportion = w / totalWidth;
-                return proportion * (this.props.PanelWidth() - this._rowMenuWidth);
+                return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
             });
-            // this.layoutDoc.columnWidths = new List<number>(widths);
         }
 
         return widths;
@@ -110,8 +112,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         let currWidths = [...this.storedColumnWidths];
         const newColWidth = this.props.PanelWidth() / (currWidths.length + 1);
         currWidths = currWidths.map(w => {
-            const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth);
-            return proportion * (this.props.PanelWidth() - this._rowMenuWidth - newColWidth);
+            const proportion = w / (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
+            return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth - newColWidth);
         });
         currWidths.splice(index + 1, 0, newColWidth);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
@@ -133,8 +135,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         let currWidths = [...this.storedColumnWidths];
         const removedColWidth = currWidths[index];
         currWidths = currWidths.map(w => {
-            const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth - removedColWidth);
-            return proportion * (this.props.PanelWidth() - this._rowMenuWidth);
+            const proportion = w / (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth - removedColWidth);
+            return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
         });
         currWidths.splice(index, 1);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
@@ -142,8 +144,6 @@ export class CollectionSchemaView extends CollectionSubView() {
         let currKeys = [...this.columnKeys];
         currKeys.splice(index, 1);
         this.layoutDoc.columnKeys = new List<string>(currKeys);
-        console.log([...this.storedColumnWidths]);
-        console.log([...this.columnKeys]);
     };
 
     @action
@@ -399,6 +399,34 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
+    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+        Doc.BrushDoc(doc);
+
+        let focusSpeed = 0;
+        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
+        if (found) {
+            const top = found.getBoundingClientRect().top;
+            const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
+            if (Math.floor(localTop[1]) !== 0) {
+                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
+            }
+        }
+        const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
+        this.props.focus(this.rootDoc, {
+            ...options,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
+        });
+    };
+
+    isChildContentActive = () =>
+        this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
+
+    getDocTransform(doc: Doc, dref?: DocumentView) {
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
+        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
+        return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
+    }
+
     render() {
         return (
             <div
@@ -413,7 +441,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 onDrop={this.onExternalDrop.bind(this)}>
                 <div className="schema-table">
                     <div className="schema-header-row">
-                        <div className="row-menu" style={{ width: this._rowMenuWidth }}></div>
+                        <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}></div>
                         {this.columnKeys.map((key, index) => {
                             return (
                                 <SchemaColumnHeader
@@ -432,25 +460,58 @@ export class CollectionSchemaView extends CollectionSubView() {
                         })}
                     </div>
                     <div className="schema-table-content">
-                        {this.childDocs.map((doc: Doc, index: number) => (
-                            <SchemaRowBox
-                                {...this.props}
-                                key={index}
-                                Document={doc}
-                                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                                ContainingCollectionView={this.props.CollectionView}
-                                rowIndex={index}
-                                columnKeys={this.columnKeys}
-                                columnWidths={this.displayColumnWidths}
-                                rowMenuWidth={this._rowMenuWidth}
-                                selectedDocs={this._selectedDocs}
-                                selectRow={this.selectRow}
-                                startDrag={this.startDrag}
-                                dragging={this._isDragging}
-                                dropIndex={this.setDropIndex}
-                                addRowRef={this.addRowRef}
-                            />
-                        ))}
+                        {this.childDocs.map((doc: Doc, index: number) => {
+                            const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
+                            let dref: Opt<DocumentView>;
+
+                            return (
+                                <div className="schema-row-wrapper">
+                                    <DocumentView
+                                        {...this.props}
+                                        ref={r => (dref = r || undefined)}
+                                        LayoutTemplate={this.props.childLayoutTemplate}
+                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
+                                        renderDepth={this.props.renderDepth + 1}
+                                        Document={doc}
+                                        DataDoc={dataDoc}
+                                        ContainingCollectionView={this.props.CollectionView}
+                                        ContainingCollectionDoc={this.Document}
+                                        PanelWidth={this.props.PanelWidth}
+                                        PanelHeight={() => 70}
+                                        styleProvider={DefaultStyleProvider}
+                                        focus={this.focusDocument}
+                                        docFilters={this.childDocFilters}
+                                        docRangeFilters={this.childDocRangeFilters}
+                                        searchFilterDocs={this.searchFilterDocs}
+                                        rootSelected={this.rootSelected}
+                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
+                                        bringToFront={emptyFunction}
+                                        isContentActive={this.isChildContentActive}
+                                        hideDecorations={true}
+                                        hideTitle={true}
+                                        hideDocumentButtonBar={true}
+                                    />
+                                </div>
+                            );
+
+                            // <SchemaRowBox
+                            //     {...this.props}
+                            //     key={index}
+                            //     Document={doc}
+                            //     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                            //     ContainingCollectionView={this.props.CollectionView}
+                            //     rowIndex={index}
+                            //     columnKeys={this.columnKeys}
+                            //     columnWidths={this.displayColumnWidths}
+                            //     rowMenuWidth={CollectionSchemaView._rowMenuWidth}
+                            //     selectedDocs={this._selectedDocs}
+                            //     selectRow={this.selectRow}
+                            //     startDrag={this.startDrag}
+                            //     dragging={this._isDragging}
+                            //     dropIndex={this.setDropIndex}
+                            //     addRowRef={this.addRowRef}
+                            // />
+                        })}
                     </div>
                 </div>
                 <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 5c1f32565..f790e9dbf 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,8 +1,8 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, ObservableMap, ObservableSet } from 'mobx';
+import { action, computed, ObservableMap, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc } from '../../../../fields/Doc';
+import { Doc, StrListCast } from '../../../../fields/Doc';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -11,6 +11,10 @@ import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
 import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
+import { OpenWhere } from '../../nodes/DocumentView';
+import { Cast } from '../../../../fields/Types';
+import { listSpec } from '../../../../fields/Schema';
+import { CollectionSchemaView } from './CollectionSchemaView';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
@@ -26,40 +30,61 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(SchemaRowBox, fieldKey);
     }
 
     private _ref: HTMLDivElement | null = null;
 
-    isSelected = () => this.props.selectedDocs.has(this.props.Document);
     bounds = () => this._ref?.getBoundingClientRect();
 
+    @computed get columnKeys() {
+        return StrListCast(this.props.ContainingCollectionDoc?.columnKeys);
+    }
+
+    @computed get storedColumnWidths() {
+        let widths = Cast(
+            this.props.ContainingCollectionDoc?.columnWidths,
+            listSpec('number'),
+            this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
+        );
+
+        const totalWidth = widths.reduce((sum, width) => sum + width, 0);
+        if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) {
+            widths = widths.map(w => {
+                const proportion = w / totalWidth;
+                return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
+            });
+        }
+
+        return widths;
+    }
+
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
 
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-            emptyFunction,
-            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-        );
+        // setupMoveUpEvents(
+        //     this,
+        //     e,
+        //     e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
+        //     emptyFunction,
+        //     e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
+        // );
     };
 
     onPointerEnter = (e: any) => {
-        if (!this.props.dragging) return;
+        // if (!this.props.dragging) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
     };
 
     onPointerMove = (e: any) => {
-        if (!this.props.dragging) return;
+        // if (!this.props.dragging) return;
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
-            dragIsRow = this.props.selectedDocs.has(doc);
+            // dragIsRow = this.props.selectedDocs.has(doc);
         });
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
@@ -69,11 +94,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
             if (y <= halfLine) {
                 this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
                 this._ref.style.borderBottom = '0px';
-                this.props.dropIndex(this.props.rowIndex);
+                // this.props.dropIndex(this.props.rowIndex);
             } else if (y > halfLine) {
                 this._ref.style.borderTop = '0px';
                 this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                this.props.dropIndex(this.props.rowIndex + 1);
+                // this.props.dropIndex(this.props.rowIndex + 1);
             }
         }
     };
@@ -90,15 +115,22 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
         return (
             <div
                 className="schema-row"
-                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
-                onPointerDown={this.onRowPointerDown}
-                onPointerEnter={this.onPointerEnter}
+                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}}
+                // style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
+                // onPointerDown={this.onRowPointerDown}
+                // onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    row && this.props.addRowRef(this.props.Document, row);
+                    // row && this.props.addRowRef(this.props.Document, row);
                     this._ref = row;
                 }}>
-                <div className="row-menu" style={{ width: this.props.rowMenuWidth }}>
+                <div
+                    className="row-menu"
+                    style={
+                        {
+                            /* width: this.props.rowMenuWidth */
+                        }
+                    }>
                     <div
                         className="schema-row-button"
                         onPointerDown={undoBatch(e => {
@@ -111,14 +143,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                         className="schema-row-button"
                         onPointerDown={e => {
                             e.stopPropagation();
-                            this.props.addDocTab(this.props.Document, 'add:right');
+                            this.props.addDocTab(this.props.Document, OpenWhere.addRight);
                         }}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.props.columnKeys.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                    {this.columnKeys.map((key, index) => (
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.storedColumnWidths[index]} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
index 97a6c5c18..5953f85ad 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
@@ -1,683 +1,683 @@
-import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import { extname } from 'path';
-import DatePicker from 'react-datepicker';
-import { CellInfo } from 'react-table';
-import { DateField } from '../../../../fields/DateField';
-import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { ComputedField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types';
-import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, Utils } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from '../../../util/DragManager';
-import { KeyCodes } from '../../../util/KeyCodes';
-import { CompileScript } from '../../../util/Scripting';
-import { SearchUtil } from '../../../util/SearchUtil';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { undoBatch } from '../../../util/UndoManager';
-import '../../../views/DocumentDecorations.scss';
-import { EditableView } from '../../EditableView';
-import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
-import { DocumentIconContainer } from '../../nodes/DocumentIcon';
-import { OverlayView } from '../../OverlayView';
-import { CollectionView } from '../CollectionView';
-import './CollectionSchemaView.scss';
-import { OpenWhere } from '../../nodes/DocumentView';
-import { PinProps } from '../../nodes/trails';
-
-// intialize cell properties
-export interface CellProps {
-    row: number;
-    col: number;
-    rowProps: CellInfo;
-    // currently unused
-    CollectionView: Opt<CollectionView>;
-    // currently unused
-    ContainingCollection: Opt<CollectionView>;
-    Document: Doc;
-    // column name
-    fieldKey: string;
-    // currently unused
-    renderDepth: number;
-    // called when a button is pressed on the node itself
-    addDocTab: (document: Doc, where: OpenWhere) => boolean;
-    pinToPres: (document: Doc, pinProps: PinProps) => void;
-    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    isFocused: boolean;
-    changeFocusedCellByIndex: (row: number, col: number) => void;
-    // set whether the cell is in the isEditing mode
-    setIsEditing: (isEditing: boolean) => void;
-    isEditable: boolean;
-    setPreviewDoc: (doc: Doc) => void;
-    setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean;
-    getField: (row: number, col?: number) => void;
-    // currnetly unused
-    showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void;
-}
-
-@observer
-export class CollectionSchemaCell extends React.Component<CellProps> {
-    // return a field key that is corrected for whether it COMMENT
-    public static resolvedFieldKey(column: string, rowDoc: Doc) {
-        const fieldKey = column;
-        if (fieldKey.startsWith('*')) {
-            const rootKey = fieldKey.substring(1);
-            const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))];
-            const matchedKeys = allKeys.filter(key => key.includes(rootKey));
-            if (matchedKeys.length) return matchedKeys[0];
-        }
-        return fieldKey;
-    }
-    @observable protected _isEditing: boolean = false;
-    protected _focusRef = React.createRef<HTMLDivElement>();
-    protected _rowDoc = this.props.rowProps.original;
-    // Gets the serialized data in proto form of the base proto that this document's proto inherits from
-    protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
-    // methods for dragging and dropping
-    protected _dropDisposer?: DragManager.DragDropDisposer;
-    @observable contents: string = '';
-
-    componentDidMount() {
-        document.addEventListener('keydown', this.onKeyDown);
-    }
-    componentWillUnmount() {
-        document.removeEventListener('keydown', this.onKeyDown);
-    }
-
-    @action
-    onKeyDown = (e: KeyboardEvent): void => {
-        // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it
-        if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) {
-            document.removeEventListener('keydown', this.onKeyDown);
-            this._isEditing = true;
-            this.props.setIsEditing(true);
-        }
-    };
-
-    @action
-    isEditingCallback = (isEditing: boolean): void => {
-        // a general method that takes a boolean that determines whether the cell should be in
-        // is-editing mode
-        // remove the event listener if it's there
-        document.removeEventListener('keydown', this.onKeyDown);
-        // it's not already in is-editing mode, re-add the event listener
-        isEditing && document.addEventListener('keydown', this.onKeyDown);
-        this._isEditing = isEditing;
-        this.props.setIsEditing(isEditing);
-        this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-    };
-
-    @action
-    onPointerDown = async (e: React.PointerEvent): Promise<void> => {
-        // pan to the cell
-        this.onItemDown(e);
-        // focus on it
-        this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-        this.props.setPreviewDoc(this.props.rowProps.original);
-
-        let url: string;
-        if ((url = StrCast(this.props.rowProps.row.href))) {
-            // opens up the the doc in a new window, blurring the old one
-            try {
-                new URL(url);
-                const temp = window.open(url)!;
-                temp.blur();
-                window.focus();
-            } catch {}
-        }
-
-        const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null);
-        doc && this.props.setPreviewDoc(doc);
-    };
-
-    @undoBatch
-    applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => {
-        // apply a specified change to the cell
-        const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) });
-        if (!res.success) return false;
-        // change what is rendered to this new changed cell content
-        doc[this.renderFieldKey] = res.result;
-        return true;
-        // return whether the change was successful
-    };
-
-    private drop = (e: Event, de: DragManager.DropEvent) => {
-        // if the drag has data at its completion
-        if (de.complete.docDragData) {
-            // if only one doc was dragged
-            if (de.complete.docDragData.draggedDocuments.length === 1) {
-                // update the renderFieldKey
-                this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
-            } else {
-                // create schema document reflecting the new column arrangement
-                const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {});
-                this._rowDataDoc[this.renderFieldKey] = coll;
-            }
-            e.stopPropagation();
-        }
-    };
-
-    protected dropRef = (ele: HTMLElement | null) => {
-        // if the drop disposer is not undefined, run its function
-        this._dropDisposer?.();
-        // if ele is not null, give ele a non-undefined drop disposer
-        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
-    };
-
-    returnHighlights(contents: string, positions?: number[]) {
-        if (positions) {
-            const results = [];
-            StrCast(this.props.Document._searchString);
-            const length = StrCast(this.props.Document._searchString).length;
-            const color = contents ? 'black' : 'grey';
-
-            results.push(
-                <span key="-1" style={{ color }}>
-                    {contents?.slice(0, positions[0])}
-                </span>
-            );
-            positions.forEach((num, cur) => {
-                results.push(
-                    <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}>
-                        {contents?.slice(num, num + length)}
-                    </span>
-                );
-                let end = 0;
-                cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]);
-                results.push(
-                    <span key={'end' + cur} style={{ color }}>
-                        {contents?.slice(num + length, end)}
-                    </span>
-                );
-            });
-            return results;
-        }
-        return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>;
-    }
-
-    @computed get renderFieldKey() {
-        // gets the resolved field key of this cell
-        return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original);
-    }
-
-    onItemDown = async (e: React.PointerEvent) => {
-        // if the document is a document used to change UI for search results in schema view
-        if (this.props.Document._searchDoc) {
-            const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
-            const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
-            // Jump to the this document
-            DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc));
-        }
-    };
-
-    renderCellWithType(type: string | undefined) {
-        const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-
-        // the column
-        const fieldKey = this.renderFieldKey;
-        // the exact cell
-        const field = this._rowDoc[fieldKey];
-
-        const onPointerEnter = (e: React.PointerEvent): void => {
-            // e.buttons === 1 means the left moue pointer is down
-            if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) {
-                dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over';
-            }
-        };
-        const onPointerLeave = (e: React.PointerEvent): void => {
-            // change the class name to indicate that the cell is no longer being dragged
-            dragRef.current!.className = 'collectionSchemaView-cellContainer';
-        };
-
-        let contents = Field.toString(field as Field);
-        // display 2 hyphens instead of a blank box for empty cells
-        contents = contents === '' ? '--' : contents;
-
-        // classname reflects the tatus of the cell
-        let className = 'collectionSchemaView-cellWrapper';
-        if (this._isEditing) className += ' editing';
-        if (this.props.isFocused && this.props.isEditable) className += ' focused';
-        if (this.props.isFocused && !this.props.isEditable) className += ' inactive';
-
-        const positions = [];
-        if (StrCast(this.props.Document._searchString).toLowerCase() !== '') {
-            // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents
-            let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase();
-            const search = StrCast(this.props.Document._searchString).toLowerCase();
-            let start = term.indexOf(search);
-            let tally = 0;
-            // if search is found in term
-            if (start !== -1) {
-                positions.push(start);
-            }
-            // if search is found in term, continue finding all instances of search in term
-            while (start < contents?.length && start !== -1) {
-                term = term.slice(start + search.length + 1);
-                tally += start + search.length + 1;
-                start = term.indexOf(search);
-                positions.push(tally + start);
-            }
-            // remove the last position
-            if (positions.length > 1) {
-                positions.pop();
-            }
-        }
-        const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined';
-        return (
-            <div
-                className="collectionSchemaView-cellContainer"
-                style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }}
-                ref={dragRef}
-                onPointerDown={this.onPointerDown}
-                onClick={action(e => (this._isEditing = true))}
-                onPointerEnter={onPointerEnter}
-                onPointerLeave={onPointerLeave}>
-                <div className={className} ref={this._focusRef} tabIndex={-1}>
-                    <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}>
-                        {!this.props.Document._searchDoc ? (
-                            <EditableView
-                                editing={this._isEditing}
-                                isEditingCallback={this.isEditingCallback}
-                                display={'inline'}
-                                contents={contents}
-                                height={'auto'}
-                                maxHeight={Number(MAX_ROW_HEIGHT)}
-                                placeholder={placeholder}
-                                GetValue={() => {
-                                    const cfield = ComputedField.WithoutComputed(() => FieldValue(field));
-                                    const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
-                                    const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1];
-                                    return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : '';
-                                }}
-                                SetValue={action((value: string) => {
-                                    // sets what is displayed after the user makes an input
-                                    let retVal = false;
-                                    if (value.startsWith(':=') || value.startsWith('=:=')) {
-                                        // decides how to compute a value when given either of the above strings
-                                        const script = value.substring(value.startsWith('=:=') ? 3 : 2);
-                                        retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col);
-                                    } else {
-                                        // check if the input is a number
-                                        let inputIsNum = true;
-                                        for (const s of value) {
-                                            if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) {
-                                                inputIsNum = false;
-                                            }
-                                        }
-                                        // check if the input is a boolean
-                                        const inputIsBool: boolean = value === 'false' || value === 'true';
-                                        // what to do in the case
-                                        if (!inputIsNum && !inputIsBool && !value.startsWith('=')) {
-                                            // if it's not a number, it's a string, and should be processed as such
-                                            // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically
-                                            // after each edit
-                                            let valueSansQuotes = value;
-                                            if (this._isEditing) {
-                                                const vsqLength = valueSansQuotes.length;
-                                                // get rid of outer quotes
-                                                valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength);
-                                            }
-                                            let inputAsString = '"';
-                                            // escape any quotes in the string
-                                            for (const i of valueSansQuotes) {
-                                                if (i === '"') {
-                                                    inputAsString += '\\"';
-                                                } else {
-                                                    inputAsString += i;
-                                                }
-                                            }
-                                            // add a closing quote
-                                            inputAsString += '"';
-                                            //two options here: we can strip off outer quotes or we can figure out what's going on with the script
-                                            const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                            const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
-                                            // change it if a change is made, otherwise, just compile using the old cell conetnts
-                                            script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
-                                            // handle numbers and expressions
-                                        } else if (inputIsNum || value.startsWith('=')) {
-                                            //TODO: make accept numbers
-                                            const inputscript = value.substring(value.startsWith('=') ? 1 : 0);
-                                            // if commas are not stripped, the parser only considers the numbers after the last comma
-                                            let inputSansCommas = '';
-                                            for (const s of inputscript) {
-                                                if (!(s === ',')) {
-                                                    inputSansCommas += s;
-                                                }
-                                            }
-                                            const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                            const changeMade = value.length - 2 !== value.length;
-                                            script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
-                                            // handle booleans
-                                        } else if (inputIsBool) {
-                                            const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                            const changeMade = value.length - 2 !== value.length;
-                                            script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
-                                        }
-                                    }
-                                    if (retVal) {
-                                        this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
-                                        this.props.setIsEditing(false);
-                                    }
-                                    return retVal;
-                                })}
-                                OnFillDown={async (value: string) => {
-                                    // computes all of the value preceded by :=
-                                    const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                    script.compiled &&
-                                        DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) =>
-                                            value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run)
-                                        );
-                                }}
-                            />
-                        ) : (
-                            this.returnHighlights(contents, positions)
-                        )}
-                    </div>
-                </div>
-            </div>
-        );
-    }
-
-    render() {
-        return this.renderCellWithType(undefined);
-    }
-}
-
-@observer
-export class CollectionSchemaNumberCell extends CollectionSchemaCell {
-    render() {
-        return this.renderCellWithType('number');
-    }
-}
-
-@observer
-export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
-    render() {
-        return this.renderCellWithType('boolean');
-    }
-}
-
-@observer
-export class CollectionSchemaStringCell extends CollectionSchemaCell {
-    render() {
-        return this.renderCellWithType('string');
-    }
-}
-
-@observer
-export class CollectionSchemaDateCell extends CollectionSchemaCell {
-    @computed get _date(): Opt<DateField> {
-        // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
-        return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined;
-    }
-
-    @action
-    handleChange = (date: any) => {
-        // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
-        // if (script.compiled) {
-        //     this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
-        // } else {
-        // ^ DateCast is always undefined for some reason, but that is what the field should be set to
-        this._rowDoc[this.renderFieldKey] = new DateField(date as Date);
-        //}
-    };
-
-    render() {
-        return !this.props.isFocused ? (
-            <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span>
-        ) : (
-            <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} />
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaDocCell extends CollectionSchemaCell {
-    _overlayDisposer?: () => void;
-
-    @computed get _doc() {
-        return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc));
-    }
-
-    @action
-    onSetValue = (value: string) => {
-        this._doc && (Doc.GetProto(this._doc).title = value);
-
-        const script = CompileScript(value, {
-            addReturn: true,
-            typecheck: true,
-            transformer: DocumentIconContainer.getTransformer(),
-        });
-        // compile the script
-        const results = script.compiled && script.run();
-        // if the script was compiled and run
-        if (results && results.success) {
-            this._rowDoc[this.renderFieldKey] = results.result;
-            return true;
-        }
-        return false;
-    };
-
-    componentWillUnmount() {
-        this.onBlur();
-    }
-
-    onBlur = () => {
-        this._overlayDisposer?.();
-    };
-    onFocus = () => {
-        this.onBlur();
-        this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
-    };
-
-    @action
-    isEditingCallback = (isEditing: boolean): void => {
-        // the isEditingCallback from a general CollectionSchemaCell
-        document.removeEventListener('keydown', this.onKeyDown);
-        isEditing && document.addEventListener('keydown', this.onKeyDown);
-        this._isEditing = isEditing;
-        this.props.setIsEditing(isEditing);
-        this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-    };
-
-    render() {
-        // if there's a doc, render it
-        return !this._doc ? (
-            this.renderCellWithType('document')
-        ) : (
-            <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}>
-                    <EditableView
-                        editing={this._isEditing}
-                        isEditingCallback={this.isEditingCallback}
-                        display={'inline'}
-                        contents={this._doc.title || '--'}
-                        height={'auto'}
-                        maxHeight={Number(MAX_ROW_HEIGHT)}
-                        GetValue={() => StrCast(this._doc?.title)}
-                        SetValue={action((value: string) => {
-                            this.onSetValue(value);
-                            return true;
-                        })}
-                    />
-                </div>
-                <div onClick={() => this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton">
-                    <FontAwesomeIcon icon="external-link-alt" size="lg" />
-                </div>
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaImageCell extends CollectionSchemaCell {
-    choosePath(url: URL) {
-        if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
-        if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
-
-        const ext = extname(url.href);
-        return url.href.replace(ext, '_o' + ext);
-    }
-
-    render() {
-        const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
-        const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
-        const altpaths = alts
-            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
-            .filter(url => url)
-            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
-        const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
-        // If there is a path, follow it; otherwise, follow a link to a default image icon
-        const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
-
-        const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio
-        let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px
-        const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px
-        width = height * aspect; // increase the width of the image if necessary to maintain proportionality
-
-        const reference = React.createRef<HTMLDivElement>();
-        return (
-            <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
-                    <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} />
-                </div>
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaListCell extends CollectionSchemaCell {
-    _overlayDisposer?: () => void;
-
-    @computed get _field() {
-        return this._rowDoc[this.renderFieldKey];
-    }
-    @computed get _optionsList() {
-        return this._field as List<any>;
-    }
-    @observable private _opened = false; // whether the list is opened
-    @observable private _text = 'select an item';
-    @observable private _selectedNum = 0; // the index of the list item selected
-
-    @action
-    onSetValue = (value: string) => {
-        // change if it's a document
-        this._optionsList[this._selectedNum] = this._text = value;
-
-        (this._field as List<any>).splice(this._selectedNum, 1, value);
-    };
-
-    @action
-    onSelected = (element: string, index: number) => {
-        // if an item is selected, the private variables should update to reflect this
-        this._text = element;
-        this._selectedNum = index;
-    };
-
-    onFocus = () => {
-        this._overlayDisposer?.();
-        this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
-    };
-
-    render() {
-        const link = false;
-        const reference = React.createRef<HTMLDivElement>();
-
-        // if the list is not opened, don't display it; otherwise, do.
-        if (this._optionsList?.length) {
-            const options = !this._opened ? null : (
-                <div>
-                    {this._optionsList.map((element, index) => {
-                        const val = Field.toString(element);
-                        return (
-                            <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}>
-                                {val}
-                            </div>
-                        );
-                    })}
-                </div>
-            );
-
-            const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>;
-            const textarea = (
-                <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}>
-                    <EditableView
-                        editing={this._isEditing}
-                        isEditingCallback={this.isEditingCallback}
-                        display={'inline'}
-                        contents={this._text}
-                        height={'auto'}
-                        maxHeight={Number(MAX_ROW_HEIGHT)}
-                        GetValue={() => this._text}
-                        SetValue={action((value: string) => {
-                            // add special for params
-                            this.onSetValue(value);
-                            return true;
-                        })}
-                    />
-                </div>
-            );
-
-            //☰
-            return (
-                <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                    <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
-                        <div className="collectionSchemaView-dropDownWrapper">
-                            <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}>
-                                <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" />
-                            </button>
-                            <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
-                        </div>
-                        {options}
-                    </div>
-                </div>
-            );
-        }
-        return this.renderCellWithType('list');
-    }
-}
-
-@observer
-export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
-    @computed get _isChecked() {
-        return BoolCast(this._rowDoc[this.renderFieldKey]);
-    }
-
-    render() {
-        const reference = React.createRef<HTMLDivElement>();
-        return (
-            <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} />
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaButtons extends CollectionSchemaCell {
-    // the navigation buttons for schema view when it is used for search.
-    render() {
-        return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? (
-            <></>
-        ) : (
-            <div style={{ paddingTop: 8, paddingLeft: 3 }}>
-                <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}>
-                    <FontAwesomeIcon icon="arrow-up" size="sm" />
-                </button>
-                <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}>
-                    <FontAwesomeIcon icon="arrow-down" size="sm" />
-                </button>
-            </div>
-        );
-    }
-}
+// import React = require('react');
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action, computed, observable } from 'mobx';
+// import { observer } from 'mobx-react';
+// import { extname } from 'path';
+// import DatePicker from 'react-datepicker';
+// import { CellInfo } from 'react-table';
+// import { DateField } from '../../../../fields/DateField';
+// import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { List } from '../../../../fields/List';
+// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { ComputedField } from '../../../../fields/ScriptField';
+// import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types';
+// import { ImageField } from '../../../../fields/URLField';
+// import { emptyFunction, Utils } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { DocumentType } from '../../../documents/DocumentTypes';
+// import { DocumentManager } from '../../../util/DocumentManager';
+// import { DragManager } from '../../../util/DragManager';
+// import { KeyCodes } from '../../../util/KeyCodes';
+// import { CompileScript } from '../../../util/Scripting';
+// import { SearchUtil } from '../../../util/SearchUtil';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { undoBatch } from '../../../util/UndoManager';
+// import '../../../views/DocumentDecorations.scss';
+// import { EditableView } from '../../EditableView';
+// import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
+// import { DocumentIconContainer } from '../../nodes/DocumentIcon';
+// import { OverlayView } from '../../OverlayView';
+// import { CollectionView } from '../CollectionView';
+// import './CollectionSchemaView.scss';
+// import { OpenWhere } from '../../nodes/DocumentView';
+// import { PinProps } from '../../nodes/trails';
+
+// // intialize cell properties
+// export interface CellProps {
+//     row: number;
+//     col: number;
+//     rowProps: CellInfo;
+//     // currently unused
+//     CollectionView: Opt<CollectionView>;
+//     // currently unused
+//     ContainingCollection: Opt<CollectionView>;
+//     Document: Doc;
+//     // column name
+//     fieldKey: string;
+//     // currently unused
+//     renderDepth: number;
+//     // called when a button is pressed on the node itself
+//     addDocTab: (document: Doc, where: OpenWhere) => boolean;
+//     pinToPres: (document: Doc, pinProps: PinProps) => void;
+//     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+//     isFocused: boolean;
+//     changeFocusedCellByIndex: (row: number, col: number) => void;
+//     // set whether the cell is in the isEditing mode
+//     setIsEditing: (isEditing: boolean) => void;
+//     isEditable: boolean;
+//     setPreviewDoc: (doc: Doc) => void;
+//     setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean;
+//     getField: (row: number, col?: number) => void;
+//     // currnetly unused
+//     showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void;
+// }
+
+// @observer
+// export class CollectionSchemaCell extends React.Component<CellProps> {
+//     // return a field key that is corrected for whether it COMMENT
+//     public static resolvedFieldKey(column: string, rowDoc: Doc) {
+//         const fieldKey = column;
+//         if (fieldKey.startsWith('*')) {
+//             const rootKey = fieldKey.substring(1);
+//             const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))];
+//             const matchedKeys = allKeys.filter(key => key.includes(rootKey));
+//             if (matchedKeys.length) return matchedKeys[0];
+//         }
+//         return fieldKey;
+//     }
+//     @observable protected _isEditing: boolean = false;
+//     protected _focusRef = React.createRef<HTMLDivElement>();
+//     protected _rowDoc = this.props.rowProps.original;
+//     // Gets the serialized data in proto form of the base proto that this document's proto inherits from
+//     protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
+//     // methods for dragging and dropping
+//     protected _dropDisposer?: DragManager.DragDropDisposer;
+//     @observable contents: string = '';
+
+//     componentDidMount() {
+//         document.addEventListener('keydown', this.onKeyDown);
+//     }
+//     componentWillUnmount() {
+//         document.removeEventListener('keydown', this.onKeyDown);
+//     }
+
+//     @action
+//     onKeyDown = (e: KeyboardEvent): void => {
+//         // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it
+//         if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) {
+//             document.removeEventListener('keydown', this.onKeyDown);
+//             this._isEditing = true;
+//             this.props.setIsEditing(true);
+//         }
+//     };
+
+//     @action
+//     isEditingCallback = (isEditing: boolean): void => {
+//         // a general method that takes a boolean that determines whether the cell should be in
+//         // is-editing mode
+//         // remove the event listener if it's there
+//         document.removeEventListener('keydown', this.onKeyDown);
+//         // it's not already in is-editing mode, re-add the event listener
+//         isEditing && document.addEventListener('keydown', this.onKeyDown);
+//         this._isEditing = isEditing;
+//         this.props.setIsEditing(isEditing);
+//         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
+//     };
+
+//     @action
+//     onPointerDown = async (e: React.PointerEvent): Promise<void> => {
+//         // pan to the cell
+//         this.onItemDown(e);
+//         // focus on it
+//         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
+//         this.props.setPreviewDoc(this.props.rowProps.original);
+
+//         let url: string;
+//         if ((url = StrCast(this.props.rowProps.row.href))) {
+//             // opens up the the doc in a new window, blurring the old one
+//             try {
+//                 new URL(url);
+//                 const temp = window.open(url)!;
+//                 temp.blur();
+//                 window.focus();
+//             } catch {}
+//         }
+
+//         const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null);
+//         doc && this.props.setPreviewDoc(doc);
+//     };
+
+//     @undoBatch
+//     applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => {
+//         // apply a specified change to the cell
+//         const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) });
+//         if (!res.success) return false;
+//         // change what is rendered to this new changed cell content
+//         doc[this.renderFieldKey] = res.result;
+//         return true;
+//         // return whether the change was successful
+//     };
+
+//     private drop = (e: Event, de: DragManager.DropEvent) => {
+//         // if the drag has data at its completion
+//         if (de.complete.docDragData) {
+//             // if only one doc was dragged
+//             if (de.complete.docDragData.draggedDocuments.length === 1) {
+//                 // update the renderFieldKey
+//                 this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
+//             } else {
+//                 // create schema document reflecting the new column arrangement
+//                 const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {});
+//                 this._rowDataDoc[this.renderFieldKey] = coll;
+//             }
+//             e.stopPropagation();
+//         }
+//     };
+
+//     protected dropRef = (ele: HTMLElement | null) => {
+//         // if the drop disposer is not undefined, run its function
+//         this._dropDisposer?.();
+//         // if ele is not null, give ele a non-undefined drop disposer
+//         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
+//     };
+
+//     returnHighlights(contents: string, positions?: number[]) {
+//         if (positions) {
+//             const results = [];
+//             StrCast(this.props.Document._searchString);
+//             const length = StrCast(this.props.Document._searchString).length;
+//             const color = contents ? 'black' : 'grey';
+
+//             results.push(
+//                 <span key="-1" style={{ color }}>
+//                     {contents?.slice(0, positions[0])}
+//                 </span>
+//             );
+//             positions.forEach((num, cur) => {
+//                 results.push(
+//                     <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}>
+//                         {contents?.slice(num, num + length)}
+//                     </span>
+//                 );
+//                 let end = 0;
+//                 cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]);
+//                 results.push(
+//                     <span key={'end' + cur} style={{ color }}>
+//                         {contents?.slice(num + length, end)}
+//                     </span>
+//                 );
+//             });
+//             return results;
+//         }
+//         return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>;
+//     }
+
+//     @computed get renderFieldKey() {
+//         // gets the resolved field key of this cell
+//         return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original);
+//     }
+
+//     onItemDown = async (e: React.PointerEvent) => {
+//         // if the document is a document used to change UI for search results in schema view
+//         if (this.props.Document._searchDoc) {
+//             const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
+//             const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
+//             // Jump to the this document
+//             DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc));
+//         }
+//     };
+
+//     renderCellWithType(type: string | undefined) {
+//         const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+
+//         // the column
+//         const fieldKey = this.renderFieldKey;
+//         // the exact cell
+//         const field = this._rowDoc[fieldKey];
+
+//         const onPointerEnter = (e: React.PointerEvent): void => {
+//             // e.buttons === 1 means the left moue pointer is down
+//             if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) {
+//                 dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over';
+//             }
+//         };
+//         const onPointerLeave = (e: React.PointerEvent): void => {
+//             // change the class name to indicate that the cell is no longer being dragged
+//             dragRef.current!.className = 'collectionSchemaView-cellContainer';
+//         };
+
+//         let contents = Field.toString(field as Field);
+//         // display 2 hyphens instead of a blank box for empty cells
+//         contents = contents === '' ? '--' : contents;
+
+//         // classname reflects the tatus of the cell
+//         let className = 'collectionSchemaView-cellWrapper';
+//         if (this._isEditing) className += ' editing';
+//         if (this.props.isFocused && this.props.isEditable) className += ' focused';
+//         if (this.props.isFocused && !this.props.isEditable) className += ' inactive';
+
+//         const positions = [];
+//         if (StrCast(this.props.Document._searchString).toLowerCase() !== '') {
+//             // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents
+//             let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase();
+//             const search = StrCast(this.props.Document._searchString).toLowerCase();
+//             let start = term.indexOf(search);
+//             let tally = 0;
+//             // if search is found in term
+//             if (start !== -1) {
+//                 positions.push(start);
+//             }
+//             // if search is found in term, continue finding all instances of search in term
+//             while (start < contents?.length && start !== -1) {
+//                 term = term.slice(start + search.length + 1);
+//                 tally += start + search.length + 1;
+//                 start = term.indexOf(search);
+//                 positions.push(tally + start);
+//             }
+//             // remove the last position
+//             if (positions.length > 1) {
+//                 positions.pop();
+//             }
+//         }
+//         const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined';
+//         return (
+//             <div
+//                 className="collectionSchemaView-cellContainer"
+//                 style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }}
+//                 ref={dragRef}
+//                 onPointerDown={this.onPointerDown}
+//                 onClick={action(e => (this._isEditing = true))}
+//                 onPointerEnter={onPointerEnter}
+//                 onPointerLeave={onPointerLeave}>
+//                 <div className={className} ref={this._focusRef} tabIndex={-1}>
+//                     <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}>
+//                         {!this.props.Document._searchDoc ? (
+//                             <EditableView
+//                                 editing={this._isEditing}
+//                                 isEditingCallback={this.isEditingCallback}
+//                                 display={'inline'}
+//                                 contents={contents}
+//                                 height={'auto'}
+//                                 maxHeight={Number(MAX_ROW_HEIGHT)}
+//                                 placeholder={placeholder}
+//                                 GetValue={() => {
+//                                     const cfield = ComputedField.WithoutComputed(() => FieldValue(field));
+//                                     const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
+//                                     const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1];
+//                                     return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : '';
+//                                 }}
+//                                 SetValue={action((value: string) => {
+//                                     // sets what is displayed after the user makes an input
+//                                     let retVal = false;
+//                                     if (value.startsWith(':=') || value.startsWith('=:=')) {
+//                                         // decides how to compute a value when given either of the above strings
+//                                         const script = value.substring(value.startsWith('=:=') ? 3 : 2);
+//                                         retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col);
+//                                     } else {
+//                                         // check if the input is a number
+//                                         let inputIsNum = true;
+//                                         for (const s of value) {
+//                                             if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) {
+//                                                 inputIsNum = false;
+//                                             }
+//                                         }
+//                                         // check if the input is a boolean
+//                                         const inputIsBool: boolean = value === 'false' || value === 'true';
+//                                         // what to do in the case
+//                                         if (!inputIsNum && !inputIsBool && !value.startsWith('=')) {
+//                                             // if it's not a number, it's a string, and should be processed as such
+//                                             // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically
+//                                             // after each edit
+//                                             let valueSansQuotes = value;
+//                                             if (this._isEditing) {
+//                                                 const vsqLength = valueSansQuotes.length;
+//                                                 // get rid of outer quotes
+//                                                 valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength);
+//                                             }
+//                                             let inputAsString = '"';
+//                                             // escape any quotes in the string
+//                                             for (const i of valueSansQuotes) {
+//                                                 if (i === '"') {
+//                                                     inputAsString += '\\"';
+//                                                 } else {
+//                                                     inputAsString += i;
+//                                                 }
+//                                             }
+//                                             // add a closing quote
+//                                             inputAsString += '"';
+//                                             //two options here: we can strip off outer quotes or we can figure out what's going on with the script
+//                                             const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                             const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
+//                                             // change it if a change is made, otherwise, just compile using the old cell conetnts
+//                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
+//                                             // handle numbers and expressions
+//                                         } else if (inputIsNum || value.startsWith('=')) {
+//                                             //TODO: make accept numbers
+//                                             const inputscript = value.substring(value.startsWith('=') ? 1 : 0);
+//                                             // if commas are not stripped, the parser only considers the numbers after the last comma
+//                                             let inputSansCommas = '';
+//                                             for (const s of inputscript) {
+//                                                 if (!(s === ',')) {
+//                                                     inputSansCommas += s;
+//                                                 }
+//                                             }
+//                                             const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                             const changeMade = value.length - 2 !== value.length;
+//                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
+//                                             // handle booleans
+//                                         } else if (inputIsBool) {
+//                                             const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                             const changeMade = value.length - 2 !== value.length;
+//                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
+//                                         }
+//                                     }
+//                                     if (retVal) {
+//                                         this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
+//                                         this.props.setIsEditing(false);
+//                                     }
+//                                     return retVal;
+//                                 })}
+//                                 OnFillDown={async (value: string) => {
+//                                     // computes all of the value preceded by :=
+//                                     const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                     script.compiled &&
+//                                         DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) =>
+//                                             value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run)
+//                                         );
+//                                 }}
+//                             />
+//                         ) : (
+//                             this.returnHighlights(contents, positions)
+//                         )}
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     }
+
+//     render() {
+//         return this.renderCellWithType(undefined);
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaNumberCell extends CollectionSchemaCell {
+//     render() {
+//         return this.renderCellWithType('number');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
+//     render() {
+//         return this.renderCellWithType('boolean');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaStringCell extends CollectionSchemaCell {
+//     render() {
+//         return this.renderCellWithType('string');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaDateCell extends CollectionSchemaCell {
+//     @computed get _date(): Opt<DateField> {
+//         // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
+//         return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined;
+//     }
+
+//     @action
+//     handleChange = (date: any) => {
+//         // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
+//         // if (script.compiled) {
+//         //     this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
+//         // } else {
+//         // ^ DateCast is always undefined for some reason, but that is what the field should be set to
+//         this._rowDoc[this.renderFieldKey] = new DateField(date as Date);
+//         //}
+//     };
+
+//     render() {
+//         return !this.props.isFocused ? (
+//             <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span>
+//         ) : (
+//             <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} />
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaDocCell extends CollectionSchemaCell {
+//     _overlayDisposer?: () => void;
+
+//     @computed get _doc() {
+//         return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc));
+//     }
+
+//     @action
+//     onSetValue = (value: string) => {
+//         this._doc && (Doc.GetProto(this._doc).title = value);
+
+//         const script = CompileScript(value, {
+//             addReturn: true,
+//             typecheck: true,
+//             transformer: DocumentIconContainer.getTransformer(),
+//         });
+//         // compile the script
+//         const results = script.compiled && script.run();
+//         // if the script was compiled and run
+//         if (results && results.success) {
+//             this._rowDoc[this.renderFieldKey] = results.result;
+//             return true;
+//         }
+//         return false;
+//     };
+
+//     componentWillUnmount() {
+//         this.onBlur();
+//     }
+
+//     onBlur = () => {
+//         this._overlayDisposer?.();
+//     };
+//     onFocus = () => {
+//         this.onBlur();
+//         this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+//     };
+
+//     @action
+//     isEditingCallback = (isEditing: boolean): void => {
+//         // the isEditingCallback from a general CollectionSchemaCell
+//         document.removeEventListener('keydown', this.onKeyDown);
+//         isEditing && document.addEventListener('keydown', this.onKeyDown);
+//         this._isEditing = isEditing;
+//         this.props.setIsEditing(isEditing);
+//         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
+//     };
+
+//     render() {
+//         // if there's a doc, render it
+//         return !this._doc ? (
+//             this.renderCellWithType('document')
+//         ) : (
+//             <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                 <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}>
+//                     <EditableView
+//                         editing={this._isEditing}
+//                         isEditingCallback={this.isEditingCallback}
+//                         display={'inline'}
+//                         contents={this._doc.title || '--'}
+//                         height={'auto'}
+//                         maxHeight={Number(MAX_ROW_HEIGHT)}
+//                         GetValue={() => StrCast(this._doc?.title)}
+//                         SetValue={action((value: string) => {
+//                             this.onSetValue(value);
+//                             return true;
+//                         })}
+//                     />
+//                 </div>
+//                 <div onClick={() => this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton">
+//                     <FontAwesomeIcon icon="external-link-alt" size="lg" />
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaImageCell extends CollectionSchemaCell {
+//     choosePath(url: URL) {
+//         if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
+//         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
+//         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
+
+//         const ext = extname(url.href);
+//         return url.href.replace(ext, '_o' + ext);
+//     }
+
+//     render() {
+//         const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+//         const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
+//         const altpaths = alts
+//             .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+//             .filter(url => url)
+//             .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+//         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
+//         // If there is a path, follow it; otherwise, follow a link to a default image icon
+//         const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
+
+//         const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio
+//         let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px
+//         const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px
+//         width = height * aspect; // increase the width of the image if necessary to maintain proportionality
+
+//         const reference = React.createRef<HTMLDivElement>();
+//         return (
+//             <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                 <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
+//                     <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} />
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaListCell extends CollectionSchemaCell {
+//     _overlayDisposer?: () => void;
+
+//     @computed get _field() {
+//         return this._rowDoc[this.renderFieldKey];
+//     }
+//     @computed get _optionsList() {
+//         return this._field as List<any>;
+//     }
+//     @observable private _opened = false; // whether the list is opened
+//     @observable private _text = 'select an item';
+//     @observable private _selectedNum = 0; // the index of the list item selected
+
+//     @action
+//     onSetValue = (value: string) => {
+//         // change if it's a document
+//         this._optionsList[this._selectedNum] = this._text = value;
+
+//         (this._field as List<any>).splice(this._selectedNum, 1, value);
+//     };
+
+//     @action
+//     onSelected = (element: string, index: number) => {
+//         // if an item is selected, the private variables should update to reflect this
+//         this._text = element;
+//         this._selectedNum = index;
+//     };
+
+//     onFocus = () => {
+//         this._overlayDisposer?.();
+//         this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+//     };
+
+//     render() {
+//         const link = false;
+//         const reference = React.createRef<HTMLDivElement>();
+
+//         // if the list is not opened, don't display it; otherwise, do.
+//         if (this._optionsList?.length) {
+//             const options = !this._opened ? null : (
+//                 <div>
+//                     {this._optionsList.map((element, index) => {
+//                         const val = Field.toString(element);
+//                         return (
+//                             <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}>
+//                                 {val}
+//                             </div>
+//                         );
+//                     })}
+//                 </div>
+//             );
+
+//             const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>;
+//             const textarea = (
+//                 <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}>
+//                     <EditableView
+//                         editing={this._isEditing}
+//                         isEditingCallback={this.isEditingCallback}
+//                         display={'inline'}
+//                         contents={this._text}
+//                         height={'auto'}
+//                         maxHeight={Number(MAX_ROW_HEIGHT)}
+//                         GetValue={() => this._text}
+//                         SetValue={action((value: string) => {
+//                             // add special for params
+//                             this.onSetValue(value);
+//                             return true;
+//                         })}
+//                     />
+//                 </div>
+//             );
+
+//             //☰
+//             return (
+//                 <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                     <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
+//                         <div className="collectionSchemaView-dropDownWrapper">
+//                             <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}>
+//                                 <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" />
+//                             </button>
+//                             <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
+//                         </div>
+//                         {options}
+//                     </div>
+//                 </div>
+//             );
+//         }
+//         return this.renderCellWithType('list');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
+//     @computed get _isChecked() {
+//         return BoolCast(this._rowDoc[this.renderFieldKey]);
+//     }
+
+//     render() {
+//         const reference = React.createRef<HTMLDivElement>();
+//         return (
+//             <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                 <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} />
+//             </div>
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaButtons extends CollectionSchemaCell {
+//     // the navigation buttons for schema view when it is used for search.
+//     render() {
+//         return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? (
+//             <></>
+//         ) : (
+//             <div style={{ paddingTop: 8, paddingLeft: 3 }}>
+//                 <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}>
+//                     <FontAwesomeIcon icon="arrow-up" size="sm" />
+//                 </button>
+//                 <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}>
+//                     <FontAwesomeIcon icon="arrow-down" size="sm" />
+//                 </button>
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
index 28d2e6ab1..c2182ae0c 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
@@ -1,138 +1,138 @@
-import React = require('react');
-import { action } from 'mobx';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import './CollectionSchemaView.scss';
+// import React = require('react');
+// import { action } from 'mobx';
+// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { DragManager } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { Transform } from '../../../util/Transform';
+// import './CollectionSchemaView.scss';
 
-export interface MovableColumnProps {
-    columnRenderer: React.ReactNode;
-    columnValue: SchemaHeaderField;
-    allColumns: SchemaHeaderField[];
-    reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
-    ScreenToLocalTransform: () => Transform;
-}
-export class MovableColumn extends React.Component<MovableColumnProps> {
-    // The header of the column
-    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-    // The container of the function that is responsible for moving the column over to a new plac
-    private _colDropDisposer?: DragManager.DragDropDisposer;
-    // initial column position
-    private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 };
-    // sensitivity to being dragged, in pixels
-    private _sensitivity: number = 16;
-    // Column reference ID
-    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+// export interface MovableColumnProps {
+//     columnRenderer: React.ReactNode;
+//     columnValue: SchemaHeaderField;
+//     allColumns: SchemaHeaderField[];
+//     reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
+//     ScreenToLocalTransform: () => Transform;
+// }
+// export class MovableColumn extends React.Component<MovableColumnProps> {
+//     // The header of the column
+//     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+//     // The container of the function that is responsible for moving the column over to a new plac
+//     private _colDropDisposer?: DragManager.DragDropDisposer;
+//     // initial column position
+//     private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 };
+//     // sensitivity to being dragged, in pixels
+//     private _sensitivity: number = 16;
+//     // Column reference ID
+//     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
-    onPointerEnter = (e: React.PointerEvent): void => {
-        // if the column is left-clicked and it is being dragged
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header!.current!.className = 'collectionSchema-col-wrapper';
-            document.addEventListener('pointermove', this.onDragMove, true);
-        }
-    };
+//     onPointerEnter = (e: React.PointerEvent): void => {
+//         // if the column is left-clicked and it is being dragged
+//         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+//             this._header!.current!.className = 'collectionSchema-col-wrapper';
+//             document.addEventListener('pointermove', this.onDragMove, true);
+//         }
+//     };
 
-    onPointerLeave = (e: React.PointerEvent): void => {
-        this._header!.current!.className = 'collectionSchema-col-wrapper';
-        document.removeEventListener('pointermove', this.onDragMove, true);
-        !e.buttons && document.removeEventListener('pointermove', this.onPointerMove);
-    };
+//     onPointerLeave = (e: React.PointerEvent): void => {
+//         this._header!.current!.className = 'collectionSchema-col-wrapper';
+//         document.removeEventListener('pointermove', this.onDragMove, true);
+//         !e.buttons && document.removeEventListener('pointermove', this.onPointerMove);
+//     };
 
-    onDragMove = (e: PointerEvent): void => {
-        // only take into account the horizonal direction when a column is dragged
-        const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        const rect = this._header!.current!.getBoundingClientRect();
-        // Now store the point at the top center of the column when it was in its original position
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
-        // to be compared with its new horizontal position
-        const before = x[0] < bounds[0];
-        this._header!.current!.className = 'collectionSchema-col-wrapper';
-        if (before) this._header!.current!.className += ' col-before';
-        if (!before) this._header!.current!.className += ' col-after';
-        e.stopPropagation();
-    };
+//     onDragMove = (e: PointerEvent): void => {
+//         // only take into account the horizonal direction when a column is dragged
+//         const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         // Now store the point at the top center of the column when it was in its original position
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
+//         // to be compared with its new horizontal position
+//         const before = x[0] < bounds[0];
+//         this._header!.current!.className = 'collectionSchema-col-wrapper';
+//         if (before) this._header!.current!.className += ' col-before';
+//         if (!before) this._header!.current!.className += ' col-after';
+//         e.stopPropagation();
+//     };
 
-    createColDropTarget = (ele: HTMLDivElement) => {
-        this._colDropDisposer?.();
-        if (ele) {
-            this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
-        }
-    };
+//     createColDropTarget = (ele: HTMLDivElement) => {
+//         this._colDropDisposer?.();
+//         if (ele) {
+//             this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
+//         }
+//     };
 
-    colDrop = (e: Event, de: DragManager.DropEvent) => {
-        document.removeEventListener('pointermove', this.onDragMove, true);
-        // we only care about whether the column is shifted to the side
-        const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        // get the dimensions of the smallest rectangle that bounds the header
-        const rect = this._header!.current!.getBoundingClientRect();
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
-        // get whether the column was dragged before or after where it is now
-        const before = x[0] < bounds[0];
-        const colDragData = de.complete.columnDragData;
-        // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables
-        if (colDragData) {
-            e.stopPropagation();
-            this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns);
-            return true;
-        }
-        return false;
-    };
+//     colDrop = (e: Event, de: DragManager.DropEvent) => {
+//         document.removeEventListener('pointermove', this.onDragMove, true);
+//         // we only care about whether the column is shifted to the side
+//         const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+//         // get the dimensions of the smallest rectangle that bounds the header
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
+//         // get whether the column was dragged before or after where it is now
+//         const before = x[0] < bounds[0];
+//         const colDragData = de.complete.columnDragData;
+//         // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables
+//         if (colDragData) {
+//             e.stopPropagation();
+//             this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns);
+//             return true;
+//         }
+//         return false;
+//     };
 
-    onPointerMove = (e: PointerEvent) => {
-        const onRowMove = (e: PointerEvent) => {
-            e.stopPropagation();
-            e.preventDefault();
+//     onPointerMove = (e: PointerEvent) => {
+//         const onRowMove = (e: PointerEvent) => {
+//             e.stopPropagation();
+//             e.preventDefault();
 
-            document.removeEventListener('pointermove', onRowMove);
-            document.removeEventListener('pointerup', onRowUp);
-            const dragData = new DragManager.ColumnDragData(this.props.columnValue);
-            DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
-        };
-        const onRowUp = (): void => {
-            document.removeEventListener('pointermove', onRowMove);
-            document.removeEventListener('pointerup', onRowUp);
-        };
-        // if the left mouse button is the one being held
-        if (e.buttons === 1) {
-            const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
-            // If the movemnt of the drag exceeds the sensitivity value
-            if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
-                document.removeEventListener('pointermove', this.onPointerMove);
-                e.stopPropagation();
+//             document.removeEventListener('pointermove', onRowMove);
+//             document.removeEventListener('pointerup', onRowUp);
+//             const dragData = new DragManager.ColumnDragData(this.props.columnValue);
+//             DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
+//         };
+//         const onRowUp = (): void => {
+//             document.removeEventListener('pointermove', onRowMove);
+//             document.removeEventListener('pointerup', onRowUp);
+//         };
+//         // if the left mouse button is the one being held
+//         if (e.buttons === 1) {
+//             const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+//             // If the movemnt of the drag exceeds the sensitivity value
+//             if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
+//                 document.removeEventListener('pointermove', this.onPointerMove);
+//                 e.stopPropagation();
 
-                document.addEventListener('pointermove', onRowMove);
-                document.addEventListener('pointerup', onRowUp);
-            }
-        }
-    };
+//                 document.addEventListener('pointermove', onRowMove);
+//                 document.addEventListener('pointerup', onRowUp);
+//             }
+//         }
+//     };
 
-    onPointerUp = (e: React.PointerEvent) => {
-        document.removeEventListener('pointermove', this.onPointerMove);
-    };
+//     onPointerUp = (e: React.PointerEvent) => {
+//         document.removeEventListener('pointermove', this.onPointerMove);
+//     };
 
-    @action
-    onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
-        this._dragRef = ref;
-        const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
-        // If the cell thing dragged is not being edited
-        if (!(e.target as any)?.tagName.includes('INPUT')) {
-            this._startDragPosition = { x: dx, y: dy };
-            document.addEventListener('pointermove', this.onPointerMove);
-        }
-    };
+//     @action
+//     onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
+//         this._dragRef = ref;
+//         const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
+//         // If the cell thing dragged is not being edited
+//         if (!(e.target as any)?.tagName.includes('INPUT')) {
+//             this._startDragPosition = { x: dx, y: dy };
+//             document.addEventListener('pointermove', this.onPointerMove);
+//         }
+//     };
 
-    render() {
-        const reference = React.createRef<HTMLDivElement>();
+//     render() {
+//         const reference = React.createRef<HTMLDivElement>();
 
-        return (
-            <div className="collectionSchema-col" ref={this.createColDropTarget}>
-                <div className="collectionSchema-col-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                    <div className="col-dragger" ref={reference} onPointerDown={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}>
-                        {this.props.columnRenderer}
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
+//         return (
+//             <div className="collectionSchema-col" ref={this.createColDropTarget}>
+//                 <div className="collectionSchema-col-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+//                     <div className="col-dragger" ref={reference} onPointerDown={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}>
+//                         {this.props.columnRenderer}
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
index 3cb2df7d3..2b39df201 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
@@ -1,152 +1,152 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action } from 'mobx';
-import * as React from 'react';
-import { ReactTableDefaults, RowInfo } from 'react-table';
-import { Doc } from '../../../../fields/Doc';
-import { Cast, FieldValue, StrCast } from '../../../../fields/Types';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import { ContextMenu } from '../../ContextMenu';
-import { OpenWhere } from '../../nodes/DocumentView';
-import './CollectionSchemaView.scss';
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action } from 'mobx';
+// import * as React from 'react';
+// import { ReactTableDefaults, RowInfo } from 'react-table';
+// import { Doc } from '../../../../fields/Doc';
+// import { Cast, FieldValue, StrCast } from '../../../../fields/Types';
+// import { DocumentManager } from '../../../util/DocumentManager';
+// import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { Transform } from '../../../util/Transform';
+// import { undoBatch } from '../../../util/UndoManager';
+// import { ContextMenu } from '../../ContextMenu';
+// import { OpenWhere } from '../../nodes/DocumentView';
+// import './CollectionSchemaView.scss';
 
-export interface MovableRowProps {
-    rowInfo: RowInfo;
-    ScreenToLocalTransform: () => Transform;
-    addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
-    removeDoc: (doc: Doc | Doc[]) => boolean;
-    rowFocused: boolean;
-    textWrapRow: (doc: Doc) => void;
-    rowWrapped: boolean;
-    dropAction: string;
-    addDocTab: any;
-}
+// export interface MovableRowProps {
+//     rowInfo: RowInfo;
+//     ScreenToLocalTransform: () => Transform;
+//     addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
+//     removeDoc: (doc: Doc | Doc[]) => boolean;
+//     rowFocused: boolean;
+//     textWrapRow: (doc: Doc) => void;
+//     rowWrapped: boolean;
+//     dropAction: string;
+//     addDocTab: any;
+// }
 
-export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> {
-    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-    private _rowDropDisposer?: DragManager.DragDropDisposer;
+// export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> {
+//     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+//     private _rowDropDisposer?: DragManager.DragDropDisposer;
 
-    // Event listeners are only necessary when the user is hovering over the table
-    // Create one when the mouse starts hovering...
-    onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header!.current!.className = 'collectionSchema-row-wrapper';
-            document.addEventListener('pointermove', this.onDragMove, true);
-        }
-    };
-    // ... and delete it when the mouse leaves
-    onPointerLeave = (e: React.PointerEvent): void => {
-        this._header!.current!.className = 'collectionSchema-row-wrapper';
-        document.removeEventListener('pointermove', this.onDragMove, true);
-    };
-    // The method for the event listener, reorders columns when dragged to their new locations.
-    onDragMove = (e: PointerEvent): void => {
-        const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        const rect = this._header!.current!.getBoundingClientRect();
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-        const before = x[1] < bounds[1];
-        this._header!.current!.className = 'collectionSchema-row-wrapper';
-        if (before) this._header!.current!.className += ' row-above';
-        if (!before) this._header!.current!.className += ' row-below';
-        e.stopPropagation();
-    };
-    componentWillUnmount() {
-        this._rowDropDisposer?.();
-    }
-    //
-    createRowDropTarget = (ele: HTMLDivElement) => {
-        this._rowDropDisposer?.();
-        if (ele) {
-            this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
-        }
-    };
-    // Controls what hppens when a row is dragged and dropped
-    rowDrop = (e: Event, de: DragManager.DropEvent) => {
-        this.onPointerLeave(e as any);
-        const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc));
-        if (!rowDoc) return false;
+//     // Event listeners are only necessary when the user is hovering over the table
+//     // Create one when the mouse starts hovering...
+//     onPointerEnter = (e: React.PointerEvent): void => {
+//         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+//             this._header!.current!.className = 'collectionSchema-row-wrapper';
+//             document.addEventListener('pointermove', this.onDragMove, true);
+//         }
+//     };
+//     // ... and delete it when the mouse leaves
+//     onPointerLeave = (e: React.PointerEvent): void => {
+//         this._header!.current!.className = 'collectionSchema-row-wrapper';
+//         document.removeEventListener('pointermove', this.onDragMove, true);
+//     };
+//     // The method for the event listener, reorders columns when dragged to their new locations.
+//     onDragMove = (e: PointerEvent): void => {
+//         const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+//         const before = x[1] < bounds[1];
+//         this._header!.current!.className = 'collectionSchema-row-wrapper';
+//         if (before) this._header!.current!.className += ' row-above';
+//         if (!before) this._header!.current!.className += ' row-below';
+//         e.stopPropagation();
+//     };
+//     componentWillUnmount() {
+//         this._rowDropDisposer?.();
+//     }
+//     //
+//     createRowDropTarget = (ele: HTMLDivElement) => {
+//         this._rowDropDisposer?.();
+//         if (ele) {
+//             this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
+//         }
+//     };
+//     // Controls what hppens when a row is dragged and dropped
+//     rowDrop = (e: Event, de: DragManager.DropEvent) => {
+//         this.onPointerLeave(e as any);
+//         const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc));
+//         if (!rowDoc) return false;
 
-        const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        const rect = this._header!.current!.getBoundingClientRect();
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-        const before = x[1] < bounds[1];
+//         const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+//         const before = x[1] < bounds[1];
 
-        const docDragData = de.complete.docDragData;
-        if (docDragData) {
-            e.stopPropagation();
-            if (docDragData.draggedDocuments[0] === rowDoc) return true;
-            const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
-            const movedDocs = docDragData.draggedDocuments;
-            return docDragData.dropAction || docDragData.userDropAction
-                ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
-                : docDragData.moveDocument
-                ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
-                : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
-        }
-        return false;
-    };
+//         const docDragData = de.complete.docDragData;
+//         if (docDragData) {
+//             e.stopPropagation();
+//             if (docDragData.draggedDocuments[0] === rowDoc) return true;
+//             const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
+//             const movedDocs = docDragData.draggedDocuments;
+//             return docDragData.dropAction || docDragData.userDropAction
+//                 ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
+//                 : docDragData.moveDocument
+//                 ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
+//                 : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
+//         }
+//         return false;
+//     };
 
-    onRowContextMenu = (e: React.MouseEvent): void => {
-        const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row';
-        ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' });
-    };
+//     onRowContextMenu = (e: React.MouseEvent): void => {
+//         const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row';
+//         ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' });
+//     };
 
-    @undoBatch
-    @action
-    move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
-        const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
-        return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
-    };
+//     @undoBatch
+//     @action
+//     move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
+//         const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
+//         return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
+//     };
 
-    @action
-    onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        console.log('yes');
-        if (e.key === 'Backspace' || e.key === 'Delete') {
-            undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
-        }
-    };
+//     @action
+//     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+//         console.log('yes');
+//         if (e.key === 'Backspace' || e.key === 'Delete') {
+//             undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
+//         }
+//     };
 
-    render() {
-        const { children = null, rowInfo } = this.props;
+//     render() {
+//         const { children = null, rowInfo } = this.props;
 
-        if (!rowInfo) {
-            return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
-        }
+//         if (!rowInfo) {
+//             return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
+//         }
 
-        const { original } = rowInfo;
-        const doc = FieldValue(Cast(original, Doc));
+//         const { original } = rowInfo;
+//         const doc = FieldValue(Cast(original, Doc));
 
-        if (!doc) return null;
+//         if (!doc) return null;
 
-        const reference = React.createRef<HTMLDivElement>();
-        const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
+//         const reference = React.createRef<HTMLDivElement>();
+//         const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
 
-        let className = 'collectionSchema-row';
-        if (this.props.rowFocused) className += ' row-focused';
-        if (this.props.rowWrapped) className += ' row-wrapped';
+//         let className = 'collectionSchema-row';
+//         if (this.props.rowFocused) className += ' row-focused';
+//         if (this.props.rowWrapped) className += ' row-wrapped';
 
-        return (
-            <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
-                <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                    <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}>
-                        <div className="row-dragger">
-                            <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}>
-                                <FontAwesomeIcon icon="trash" size="sm" />
-                            </div>
-                            <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}>
-                                <FontAwesomeIcon icon="grip-vertical" size="sm" />
-                            </div>
-                            <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, OpenWhere.addRight)}>
-                                <FontAwesomeIcon icon="external-link-alt" size="sm" />
-                            </div>
-                        </div>
-                        {children}
-                    </ReactTableDefaults.TrComponent>
-                </div>
-            </div>
-        );
-    }
-}
+//         return (
+//             <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
+//                 <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+//                     <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}>
+//                         <div className="row-dragger">
+//                             <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}>
+//                                 <FontAwesomeIcon icon="trash" size="sm" />
+//                             </div>
+//                             <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}>
+//                                 <FontAwesomeIcon icon="grip-vertical" size="sm" />
+//                             </div>
+//                             <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, OpenWhere.addRight)}>
+//                                 <FontAwesomeIcon icon="external-link-alt" size="sm" />
+//                             </div>
+//                         </div>
+//                         {children}
+//                     </ReactTableDefaults.TrComponent>
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
index 260db4b88..f3f09cbf0 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
@@ -1,649 +1,649 @@
-import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked } from 'mobx';
-import { observer } from 'mobx-react';
-import Measure from 'react-measure';
-// import { Resize } from 'react-table';
-import { Doc, Opt } from '../../../../fields/Doc';
-import { List } from '../../../../fields/List';
-import { listSpec } from '../../../../fields/Schema';
-import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast, NumCast } from '../../../../fields/Types';
-import { TraceMobx } from '../../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { DocUtils } from '../../../documents/Documents';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import { ContextMenu } from '../../ContextMenu';
-import { ContextMenuProps } from '../../ContextMenuItem';
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { DocumentView } from '../../nodes/DocumentView';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionSubView } from '../CollectionSubView';
-import './CollectionSchemaView.scss';
-// import { SchemaTable } from './SchemaTable';
-// bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
-
-export enum ColumnType {
-    Any,
-    Number,
-    String,
-    Boolean,
-    Doc,
-    Image,
-    List,
-    Date,
-}
-// this map should be used for keys that should have a const type of value
-const columnTypes: Map<string, ColumnType> = new Map([
-    ['title', ColumnType.String],
-    ['x', ColumnType.Number],
-    ['y', ColumnType.Number],
-    ['_width', ColumnType.Number],
-    ['_height', ColumnType.Number],
-    ['_nativeWidth', ColumnType.Number],
-    ['_nativeHeight', ColumnType.Number],
-    ['isPrototype', ColumnType.Boolean],
-    ['_curPage', ColumnType.Number],
-    ['_currentTimecode', ColumnType.Number],
-    ['zIndex', ColumnType.Number],
-]);
-
-@observer
-export class CollectionSchemaView extends CollectionSubView() {
-    private _previewCont?: HTMLDivElement;
-
-    @observable _previewDoc: Doc | undefined = undefined;
-    @observable _focusedTable: Doc = this.props.Document;
-    @observable _col: any = '';
-    @observable _menuWidth = 0;
-    @observable _headerOpen = false;
-    @observable _headerIsEditing = false;
-    @observable _menuHeight = 0;
-    @observable _pointerX = 0;
-    @observable _pointerY = 0;
-    @observable _openTypes: boolean = false;
-
-    @computed get previewWidth() {
-        return () => NumCast(this.props.Document.schemaPreviewWidth);
-    }
-    @computed get previewHeight() {
-        return () => this.props.PanelHeight() - 2 * this.borderWidth;
-    }
-    @computed get tableWidth() {
-        return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
-    }
-    @computed get borderWidth() {
-        return Number(COLLECTION_BORDER_WIDTH);
-    }
-    @computed get scale() {
-        return this.props.ScreenToLocalTransform().Scale;
-    }
-    @computed get columns() {
-        return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
-    }
-    set columns(columns: SchemaHeaderField[]) {
-        this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
-    }
-
-    @computed get menuCoordinates() {
-        let searchx = 0;
-        let searchy = 0;
-        if (this.props.Document._searchDoc) {
-            const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0];
-            if (el !== undefined) {
-                const rect = el.getBoundingClientRect();
-                searchx = rect.x;
-                searchy = rect.y;
-            }
-        }
-        const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
-        const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy;
-        return this.props.ScreenToLocalTransform().transformPoint(x, y);
-    }
-
-    get documentKeys() {
-        const docs = this.childDocs;
-        const keys: { [key: string]: boolean } = {};
-        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
-        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
-        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
-        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
-        //  is displayed (unlikely) it won't show up until something else changes.
-        //TODO Types
-        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
-
-        this.columns.forEach(key => (keys[key.heading] = true));
-        return Array.from(Object.keys(keys));
-    }
-
-    @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing);
-
-    @undoBatch
-    setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
-        this._openTypes = false;
-        if (columnTypes.get(columnField.heading)) return;
-
-        const columns = this.columns;
-        const index = columns.indexOf(columnField);
-        if (index > -1) {
-            columnField.setType(NumCast(type));
-            columns[index] = columnField;
-            this.columns = columns;
-        }
-    });
-
-    @undoBatch
-    setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
-        const columns = this.columns;
-        const index = columns.indexOf(columnField);
-        if (index > -1) {
-            columnField.setColor(color);
-            columns[index] = columnField;
-            this.columns = columns; // need to set the columns to trigger rerender
-        }
-    };
-
-    @undoBatch
-    @action
-    setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
-        const columns = this.columns;
-        columns.forEach(col => col.setDesc(undefined));
-
-        const index = columns.findIndex(c => c.heading === columnField.heading);
-        const column = columns[index];
-        column.setDesc(descending);
-        columns[index] = column;
-        this.columns = columns;
-    };
-
-    renderTypes = (col: any) => {
-        if (columnTypes.get(col.heading)) return null;
-
-        const type = col.type;
-
-        const anyType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}>
-                <FontAwesomeIcon icon={'align-justify'} size="sm" />
-                Any
-            </div>
-        );
-
-        const numType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}>
-                <FontAwesomeIcon icon={'hashtag'} size="sm" />
-                Number
-            </div>
-        );
-
-        const textType = (
-            <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}>
-                <FontAwesomeIcon icon={'font'} size="sm" />
-                Text
-            </div>
-        );
-
-        const boolType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
-                <FontAwesomeIcon icon={'check-square'} size="sm" />
-                Checkbox
-            </div>
-        );
-
-        const listType = (
-            <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}>
-                <FontAwesomeIcon icon={'list-ul'} size="sm" />
-                List
-            </div>
-        );
-
-        const docType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
-                <FontAwesomeIcon icon={'file'} size="sm" />
-                Document
-            </div>
-        );
-
-        const imageType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}>
-                <FontAwesomeIcon icon={'image'} size="sm" />
-                Image
-            </div>
-        );
-
-        const dateType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}>
-                <FontAwesomeIcon icon={'calendar'} size="sm" />
-                Date
-            </div>
-        );
-
-        const allColumnTypes = (
-            <div className="columnMenu-types">
-                {anyType}
-                {numType}
-                {textType}
-                {boolType}
-                {listType}
-                {docType}
-                {imageType}
-                {dateType}
-            </div>
-        );
-
-        const justColType =
-            type === ColumnType.Any
-                ? anyType
-                : type === ColumnType.Number
-                ? numType
-                : type === ColumnType.String
-                ? textType
-                : type === ColumnType.Boolean
-                ? boolType
-                : type === ColumnType.List
-                ? listType
-                : type === ColumnType.Doc
-                ? docType
-                : type === ColumnType.Date
-                ? dateType
-                : imageType;
-
-        return (
-            <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}>
-                <div>
-                    <label style={{ cursor: 'pointer' }}>Column type:</label>
-                    <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} />
-                </div>
-                {this._openTypes ? allColumnTypes : justColType}
-            </div>
-        );
-    };
-
-    renderSorting = (col: any) => {
-        const sort = col.desc;
-        return (
-            <div className="collectionSchema-headerMenu-group">
-                <label>Sort by:</label>
-                <div className="columnMenu-sort">
-                    <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}>
-                        <FontAwesomeIcon icon="sort-amount-down" size="sm" />
-                        Sort descending
-                    </div>
-                    <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}>
-                        <FontAwesomeIcon icon="sort-amount-up" size="sm" />
-                        Sort ascending
-                    </div>
-                    <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}>
-                        <FontAwesomeIcon icon="times" size="sm" />
-                        Clear sorting
-                    </div>
-                </div>
-            </div>
-        );
-    };
-
-    renderColors = (col: any) => {
-        const selected = col.color;
-
-        const pink = PastelSchemaPalette.get('pink2');
-        const purple = PastelSchemaPalette.get('purple2');
-        const blue = PastelSchemaPalette.get('bluegreen1');
-        const yellow = PastelSchemaPalette.get('yellow4');
-        const red = PastelSchemaPalette.get('red2');
-        const gray = '#f1efeb';
-
-        return (
-            <div className="collectionSchema-headerMenu-group">
-                <label>Color:</label>
-                <div className="columnMenu-colors">
-                    <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
-                </div>
-            </div>
-        );
-    };
-
-    @undoBatch
-    @action
-    changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
-        const columns = this.columns;
-        if (columns === undefined) {
-            this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]);
-        } else {
-            if (addNew) {
-                columns.push(new SchemaHeaderField(newKey, 'f1efeb'));
-                this.columns = columns;
-            } else {
-                const index = columns.map(c => c.heading).indexOf(oldKey);
-                if (index > -1) {
-                    const column = columns[index];
-                    column.setHeading(newKey);
-                    columns[index] = column;
-                    this.columns = columns;
-                    if (filter) {
-                        Doc.setDocFilter(this.props.Document, newKey, filter, 'match');
-                    } else {
-                        this.props.Document._docFilters = undefined;
-                    }
-                }
-            }
-        }
-    };
-
-    @action
-    openHeader = (col: any, screenx: number, screeny: number) => {
-        this._col = col;
-        this._headerOpen = true;
-        this._pointerX = screenx;
-        this._pointerY = screeny;
-    };
-
-    @action
-    closeHeader = () => {
-        this._headerOpen = false;
-    };
-
-    @undoBatch
-    @action
-    deleteColumn = (key: string) => {
-        const columns = this.columns;
-        if (columns === undefined) {
-            this.columns = new List<SchemaHeaderField>([]);
-        } else {
-            const index = columns.map(c => c.heading).indexOf(key);
-            if (index > -1) {
-                columns.splice(index, 1);
-                this.columns = columns;
-            }
-        }
-        this.closeHeader();
-    };
-
-    getPreviewTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth);
-    };
-
-    @action
-    onHeaderClick = (e: React.PointerEvent) => {
-        e.stopPropagation();
-    };
-
-    @action
-    onWheel(e: React.WheelEvent) {
-        const scale = this.props.ScreenToLocalTransform().Scale;
-        this.props.isContentActive(true) && e.stopPropagation();
-    }
-
-    @computed get renderMenuContent() {
-        TraceMobx();
-        return (
-            <div className="collectionSchema-header-menuOptions">
-                {this.renderTypes(this._col)}
-                {this.renderColors(this._col)}
-                <div className="collectionSchema-headerMenu-group">
-                    <button
-                        onClick={() => {
-                            this.deleteColumn(this._col.heading);
-                        }}>
-                        Hide Column
-                    </button>
-                </div>
-            </div>
-        );
-    }
-
-    private createTarget = (ele: HTMLDivElement) => {
-        this._previewCont = ele;
-        super.CreateDropTarget(ele);
-    };
-
-    isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
-
-    @action setFocused = (doc: Doc) => (this._focusedTable = doc);
-
-    @action setPreviewDoc = (doc: Opt<Doc>) => {
-        SelectionManager.SelectSchemaViewDoc(doc);
-        this._previewDoc = doc;
-    };
-
-    //toggles preview side-panel of schema
-    @action
-    toggleExpander = () => {
-        this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
-    };
-
-    onDividerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander);
-    };
-    @action
-    onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
-        const nativeWidth = this._previewCont!.getBoundingClientRect();
-        const minWidth = 40;
-        const maxWidth = 1000;
-        const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
-        const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
-        this.props.Document.schemaPreviewWidth = width;
-        return false;
-    };
-
-    onPointerDown = (e: React.PointerEvent): void => {
-        if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
-            if (this.props.isSelected(true)) e.stopPropagation();
-            else this.props.select(false);
-        }
-    };
-
-    @computed
-    get previewDocument(): Doc | undefined {
-        return this._previewDoc;
-    }
-
-    @computed
-    get dividerDragger() {
-        return this.previewWidth() === 0 ? null : (
-            <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}>
-                <div className="collectionSchemaView-dividerDragger" />
-            </div>
-        );
-    }
-
-    @computed
-    get previewPanel() {
-        return (
-            <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
-                {!this.previewDocument ? null : (
-                    <DocumentView
-                        Document={this.previewDocument}
-                        DataDoc={undefined}
-                        fitContentsToBox={returnTrue}
-                        dontCenter={'y'}
-                        focus={DocUtils.DefaultFocus}
-                        renderDepth={this.props.renderDepth}
-                        rootSelected={this.rootSelected}
-                        PanelWidth={this.previewWidth}
-                        PanelHeight={this.previewHeight}
-                        isContentActive={returnTrue}
-                        isDocumentActive={returnFalse}
-                        ScreenToLocalTransform={this.getPreviewTransform}
-                        docFilters={this.childDocFilters}
-                        docRangeFilters={this.childDocRangeFilters}
-                        searchFilterDocs={this.searchFilterDocs}
-                        styleProvider={DefaultStyleProvider}
-                        docViewPath={returnEmptyDoclist}
-                        ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                        ContainingCollectionView={this.props.CollectionView}
-                        moveDocument={this.props.moveDocument}
-                        addDocument={this.props.addDocument}
-                        removeDocument={this.props.removeDocument}
-                        whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-                        addDocTab={this.props.addDocTab}
-                        pinToPres={this.props.pinToPres}
-                        bringToFront={returnFalse}
-                    />
-                )}
-            </div>
-        );
-    }
-
-    @computed
-    get schemaTable() {
-        return (
-            <SchemaTable
-                Document={this.props.Document}
-                PanelHeight={this.props.PanelHeight}
-                PanelWidth={this.props.PanelWidth}
-                childDocs={this.childDocs}
-                CollectionView={this.props.CollectionView}
-                ContainingCollectionView={this.props.ContainingCollectionView}
-                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                fieldKey={this.props.fieldKey}
-                renderDepth={this.props.renderDepth}
-                moveDocument={this.props.moveDocument}
-                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                active={this.props.isContentActive}
-                onDrop={this.onExternalDrop}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                isSelected={this.props.isSelected}
-                isFocused={this.isFocused}
-                setFocused={this.setFocused}
-                setPreviewDoc={this.setPreviewDoc}
-                deleteDocument={this.props.removeDocument}
-                addDocument={this.props.addDocument}
-                dataDoc={this.props.DataDoc}
-                columns={this.columns}
-                documentKeys={this.documentKeys}
-                headerIsEditing={this._headerIsEditing}
-                openHeader={this.openHeader}
-                onClick={this.onTableClick}
-                onPointerDown={emptyFunction}
-                onResizedChange={this.onResizedChange}
-                setColumns={this.setColumns}
-                reorderColumns={this.reorderColumns}
-                changeColumns={this.changeColumns}
-                setHeaderIsEditing={this.setHeaderIsEditing}
-                changeColumnSort={this.setColumnSort}
-            />
-        );
-    }
-
-    @computed
-    public get schemaToolbar() {
-        return (
-            <div className="collectionSchemaView-toolbar">
-                <div className="collectionSchemaView-toolbar-item">
-                    <div id="preview-schema-checkbox-div">
-                        <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
-                        Show Preview
-                    </div>
-                </div>
-            </div>
-        );
-    }
-
-    onSpecificMenu = (e: React.MouseEvent) => {
-        if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) {
-            const cm = ContextMenu.Instance;
-            const options = cm.findByDescription('Options...');
-            const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
-            optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' });
-            !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
-            cm.displayMenu(e.clientX, e.clientY);
-            (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
-            e.stopPropagation();
-        }
-    };
-
-    @action
-    onTableClick = (e: React.MouseEvent): void => {
-        if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) {
-            this.setPreviewDoc(undefined);
-        } else {
-            e.stopPropagation();
-        }
-        this.setFocused(this.props.Document);
-        this.closeHeader();
-    };
-
-    onResizedChange = (newResized: Resize[], event: any) => {
-        const columns = this.columns;
-        newResized.forEach(resized => {
-            const index = columns.findIndex(c => c.heading === resized.id);
-            const column = columns[index];
-            column.setWidth(resized.value);
-            columns[index] = column;
-        });
-        this.columns = columns;
-    };
-
-    @action
-    setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns);
-
-    @undoBatch
-    reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
-        const columns = [...columnsValues];
-        const oldIndex = columns.indexOf(toMove);
-        const relIndex = columns.indexOf(relativeTo);
-        const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex;
-
-        if (oldIndex === newIndex) return;
-
-        columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
-        this.columns = columns;
-    };
-
-    onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation();
-
-    render() {
-        TraceMobx();
-        if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0);
-        const menuContent = this.renderMenuContent;
-        const menu = (
-            <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}>
-                <Measure
-                    offset
-                    onResize={action((r: any) => {
-                        const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
-                        this._menuWidth = dim[0];
-                        this._menuHeight = dim[1];
-                    })}>
-                    {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
-                </Measure>
-            </div>
-        );
-        return (
-            <div
-                className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')}
-                style={{
-                    overflow: this.props.scrollOverflow === true ? 'scroll' : undefined,
-                    backgroundColor: 'white',
-                    pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
-                    width: this.props.PanelWidth() || '100%',
-                    height: this.props.PanelHeight() || '100%',
-                    position: 'relative',
-                }}>
-                <div
-                    className="collectionSchemaView-tableContainer"
-                    style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
-                    onContextMenu={this.onSpecificMenu}
-                    onPointerDown={this.onPointerDown}
-                    onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
-                    onDrop={e => this.onExternalDrop(e, {})}
-                    ref={this.createTarget}>
-                    {this.schemaTable}
-                </div>
-                {this.dividerDragger}
-                {!this.previewWidth() ? null : this.previewPanel}
-                {this._headerOpen && this.props.isContentActive() ? menu : null}
-            </div>
-        );
-        TraceMobx();
-        return <div>HELLO</div>;
-    }
-}
+// import React = require('react');
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action, computed, observable, untracked } from 'mobx';
+// import { observer } from 'mobx-react';
+// import Measure from 'react-measure';
+// // import { Resize } from 'react-table';
+// import { Doc, Opt } from '../../../../fields/Doc';
+// import { List } from '../../../../fields/List';
+// import { listSpec } from '../../../../fields/Schema';
+// import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { Cast, NumCast } from '../../../../fields/Types';
+// import { TraceMobx } from '../../../../fields/util';
+// import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+// import { DocUtils } from '../../../documents/Documents';
+// import { SelectionManager } from '../../../util/SelectionManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { Transform } from '../../../util/Transform';
+// import { undoBatch } from '../../../util/UndoManager';
+// import { ContextMenu } from '../../ContextMenu';
+// import { ContextMenuProps } from '../../ContextMenuItem';
+// import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+// import { DocumentView } from '../../nodes/DocumentView';
+// import { DefaultStyleProvider } from '../../StyleProvider';
+// import { CollectionSubView } from '../CollectionSubView';
+// import './CollectionSchemaView.scss';
+// // import { SchemaTable } from './SchemaTable';
+// // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
+
+// export enum ColumnType {
+//     Any,
+//     Number,
+//     String,
+//     Boolean,
+//     Doc,
+//     Image,
+//     List,
+//     Date,
+// }
+// // this map should be used for keys that should have a const type of value
+// const columnTypes: Map<string, ColumnType> = new Map([
+//     ['title', ColumnType.String],
+//     ['x', ColumnType.Number],
+//     ['y', ColumnType.Number],
+//     ['_width', ColumnType.Number],
+//     ['_height', ColumnType.Number],
+//     ['_nativeWidth', ColumnType.Number],
+//     ['_nativeHeight', ColumnType.Number],
+//     ['isPrototype', ColumnType.Boolean],
+//     ['_curPage', ColumnType.Number],
+//     ['_currentTimecode', ColumnType.Number],
+//     ['zIndex', ColumnType.Number],
+// ]);
+
+// @observer
+// export class CollectionSchemaView extends CollectionSubView() {
+//     private _previewCont?: HTMLDivElement;
+
+//     @observable _previewDoc: Doc | undefined = undefined;
+//     @observable _focusedTable: Doc = this.props.Document;
+//     @observable _col: any = '';
+//     @observable _menuWidth = 0;
+//     @observable _headerOpen = false;
+//     @observable _headerIsEditing = false;
+//     @observable _menuHeight = 0;
+//     @observable _pointerX = 0;
+//     @observable _pointerY = 0;
+//     @observable _openTypes: boolean = false;
+
+//     @computed get previewWidth() {
+//         return () => NumCast(this.props.Document.schemaPreviewWidth);
+//     }
+//     @computed get previewHeight() {
+//         return () => this.props.PanelHeight() - 2 * this.borderWidth;
+//     }
+//     @computed get tableWidth() {
+//         return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+//     }
+//     @computed get borderWidth() {
+//         return Number(COLLECTION_BORDER_WIDTH);
+//     }
+//     @computed get scale() {
+//         return this.props.ScreenToLocalTransform().Scale;
+//     }
+//     @computed get columns() {
+//         return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
+//     }
+//     set columns(columns: SchemaHeaderField[]) {
+//         this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
+//     }
+
+//     @computed get menuCoordinates() {
+//         let searchx = 0;
+//         let searchy = 0;
+//         if (this.props.Document._searchDoc) {
+//             const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0];
+//             if (el !== undefined) {
+//                 const rect = el.getBoundingClientRect();
+//                 searchx = rect.x;
+//                 searchy = rect.y;
+//             }
+//         }
+//         const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
+//         const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy;
+//         return this.props.ScreenToLocalTransform().transformPoint(x, y);
+//     }
+
+//     get documentKeys() {
+//         const docs = this.childDocs;
+//         const keys: { [key: string]: boolean } = {};
+//         // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+//         //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
+//         //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
+//         //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
+//         //  is displayed (unlikely) it won't show up until something else changes.
+//         //TODO Types
+//         untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
+
+//         this.columns.forEach(key => (keys[key.heading] = true));
+//         return Array.from(Object.keys(keys));
+//     }
+
+//     @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing);
+
+//     @undoBatch
+//     setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
+//         this._openTypes = false;
+//         if (columnTypes.get(columnField.heading)) return;
+
+//         const columns = this.columns;
+//         const index = columns.indexOf(columnField);
+//         if (index > -1) {
+//             columnField.setType(NumCast(type));
+//             columns[index] = columnField;
+//             this.columns = columns;
+//         }
+//     });
+
+//     @undoBatch
+//     setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
+//         const columns = this.columns;
+//         const index = columns.indexOf(columnField);
+//         if (index > -1) {
+//             columnField.setColor(color);
+//             columns[index] = columnField;
+//             this.columns = columns; // need to set the columns to trigger rerender
+//         }
+//     };
+
+//     @undoBatch
+//     @action
+//     setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
+//         const columns = this.columns;
+//         columns.forEach(col => col.setDesc(undefined));
+
+//         const index = columns.findIndex(c => c.heading === columnField.heading);
+//         const column = columns[index];
+//         column.setDesc(descending);
+//         columns[index] = column;
+//         this.columns = columns;
+//     };
+
+//     renderTypes = (col: any) => {
+//         if (columnTypes.get(col.heading)) return null;
+
+//         const type = col.type;
+
+//         const anyType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}>
+//                 <FontAwesomeIcon icon={'align-justify'} size="sm" />
+//                 Any
+//             </div>
+//         );
+
+//         const numType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}>
+//                 <FontAwesomeIcon icon={'hashtag'} size="sm" />
+//                 Number
+//             </div>
+//         );
+
+//         const textType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}>
+//                 <FontAwesomeIcon icon={'font'} size="sm" />
+//                 Text
+//             </div>
+//         );
+
+//         const boolType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
+//                 <FontAwesomeIcon icon={'check-square'} size="sm" />
+//                 Checkbox
+//             </div>
+//         );
+
+//         const listType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}>
+//                 <FontAwesomeIcon icon={'list-ul'} size="sm" />
+//                 List
+//             </div>
+//         );
+
+//         const docType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
+//                 <FontAwesomeIcon icon={'file'} size="sm" />
+//                 Document
+//             </div>
+//         );
+
+//         const imageType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}>
+//                 <FontAwesomeIcon icon={'image'} size="sm" />
+//                 Image
+//             </div>
+//         );
+
+//         const dateType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}>
+//                 <FontAwesomeIcon icon={'calendar'} size="sm" />
+//                 Date
+//             </div>
+//         );
+
+//         const allColumnTypes = (
+//             <div className="columnMenu-types">
+//                 {anyType}
+//                 {numType}
+//                 {textType}
+//                 {boolType}
+//                 {listType}
+//                 {docType}
+//                 {imageType}
+//                 {dateType}
+//             </div>
+//         );
+
+//         const justColType =
+//             type === ColumnType.Any
+//                 ? anyType
+//                 : type === ColumnType.Number
+//                 ? numType
+//                 : type === ColumnType.String
+//                 ? textType
+//                 : type === ColumnType.Boolean
+//                 ? boolType
+//                 : type === ColumnType.List
+//                 ? listType
+//                 : type === ColumnType.Doc
+//                 ? docType
+//                 : type === ColumnType.Date
+//                 ? dateType
+//                 : imageType;
+
+//         return (
+//             <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}>
+//                 <div>
+//                     <label style={{ cursor: 'pointer' }}>Column type:</label>
+//                     <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} />
+//                 </div>
+//                 {this._openTypes ? allColumnTypes : justColType}
+//             </div>
+//         );
+//     };
+
+//     renderSorting = (col: any) => {
+//         const sort = col.desc;
+//         return (
+//             <div className="collectionSchema-headerMenu-group">
+//                 <label>Sort by:</label>
+//                 <div className="columnMenu-sort">
+//                     <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}>
+//                         <FontAwesomeIcon icon="sort-amount-down" size="sm" />
+//                         Sort descending
+//                     </div>
+//                     <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}>
+//                         <FontAwesomeIcon icon="sort-amount-up" size="sm" />
+//                         Sort ascending
+//                     </div>
+//                     <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}>
+//                         <FontAwesomeIcon icon="times" size="sm" />
+//                         Clear sorting
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     };
+
+//     renderColors = (col: any) => {
+//         const selected = col.color;
+
+//         const pink = PastelSchemaPalette.get('pink2');
+//         const purple = PastelSchemaPalette.get('purple2');
+//         const blue = PastelSchemaPalette.get('bluegreen1');
+//         const yellow = PastelSchemaPalette.get('yellow4');
+//         const red = PastelSchemaPalette.get('red2');
+//         const gray = '#f1efeb';
+
+//         return (
+//             <div className="collectionSchema-headerMenu-group">
+//                 <label>Color:</label>
+//                 <div className="columnMenu-colors">
+//                     <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
+//                 </div>
+//             </div>
+//         );
+//     };
+
+//     @undoBatch
+//     @action
+//     changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
+//         const columns = this.columns;
+//         if (columns === undefined) {
+//             this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]);
+//         } else {
+//             if (addNew) {
+//                 columns.push(new SchemaHeaderField(newKey, 'f1efeb'));
+//                 this.columns = columns;
+//             } else {
+//                 const index = columns.map(c => c.heading).indexOf(oldKey);
+//                 if (index > -1) {
+//                     const column = columns[index];
+//                     column.setHeading(newKey);
+//                     columns[index] = column;
+//                     this.columns = columns;
+//                     if (filter) {
+//                         Doc.setDocFilter(this.props.Document, newKey, filter, 'match');
+//                     } else {
+//                         this.props.Document._docFilters = undefined;
+//                     }
+//                 }
+//             }
+//         }
+//     };
+
+//     @action
+//     openHeader = (col: any, screenx: number, screeny: number) => {
+//         this._col = col;
+//         this._headerOpen = true;
+//         this._pointerX = screenx;
+//         this._pointerY = screeny;
+//     };
+
+//     @action
+//     closeHeader = () => {
+//         this._headerOpen = false;
+//     };
+
+//     @undoBatch
+//     @action
+//     deleteColumn = (key: string) => {
+//         const columns = this.columns;
+//         if (columns === undefined) {
+//             this.columns = new List<SchemaHeaderField>([]);
+//         } else {
+//             const index = columns.map(c => c.heading).indexOf(key);
+//             if (index > -1) {
+//                 columns.splice(index, 1);
+//                 this.columns = columns;
+//             }
+//         }
+//         this.closeHeader();
+//     };
+
+//     getPreviewTransform = (): Transform => {
+//         return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth);
+//     };
+
+//     @action
+//     onHeaderClick = (e: React.PointerEvent) => {
+//         e.stopPropagation();
+//     };
+
+//     @action
+//     onWheel(e: React.WheelEvent) {
+//         const scale = this.props.ScreenToLocalTransform().Scale;
+//         this.props.isContentActive(true) && e.stopPropagation();
+//     }
+
+//     @computed get renderMenuContent() {
+//         TraceMobx();
+//         return (
+//             <div className="collectionSchema-header-menuOptions">
+//                 {this.renderTypes(this._col)}
+//                 {this.renderColors(this._col)}
+//                 <div className="collectionSchema-headerMenu-group">
+//                     <button
+//                         onClick={() => {
+//                             this.deleteColumn(this._col.heading);
+//                         }}>
+//                         Hide Column
+//                     </button>
+//                 </div>
+//             </div>
+//         );
+//     }
+
+//     private createTarget = (ele: HTMLDivElement) => {
+//         this._previewCont = ele;
+//         super.CreateDropTarget(ele);
+//     };
+
+//     isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
+
+//     @action setFocused = (doc: Doc) => (this._focusedTable = doc);
+
+//     @action setPreviewDoc = (doc: Opt<Doc>) => {
+//         SelectionManager.SelectSchemaViewDoc(doc);
+//         this._previewDoc = doc;
+//     };
+
+//     //toggles preview side-panel of schema
+//     @action
+//     toggleExpander = () => {
+//         this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
+//     };
+
+//     onDividerDown = (e: React.PointerEvent) => {
+//         setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander);
+//     };
+//     @action
+//     onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+//         const nativeWidth = this._previewCont!.getBoundingClientRect();
+//         const minWidth = 40;
+//         const maxWidth = 1000;
+//         const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+//         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
+//         this.props.Document.schemaPreviewWidth = width;
+//         return false;
+//     };
+
+//     onPointerDown = (e: React.PointerEvent): void => {
+//         if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
+//             if (this.props.isSelected(true)) e.stopPropagation();
+//             else this.props.select(false);
+//         }
+//     };
+
+//     @computed
+//     get previewDocument(): Doc | undefined {
+//         return this._previewDoc;
+//     }
+
+//     @computed
+//     get dividerDragger() {
+//         return this.previewWidth() === 0 ? null : (
+//             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}>
+//                 <div className="collectionSchemaView-dividerDragger" />
+//             </div>
+//         );
+//     }
+
+//     @computed
+//     get previewPanel() {
+//         return (
+//             <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
+//                 {!this.previewDocument ? null : (
+//                     <DocumentView
+//                         Document={this.previewDocument}
+//                         DataDoc={undefined}
+//                         fitContentsToBox={returnTrue}
+//                         dontCenter={'y'}
+//                         focus={DocUtils.DefaultFocus}
+//                         renderDepth={this.props.renderDepth}
+//                         rootSelected={this.rootSelected}
+//                         PanelWidth={this.previewWidth}
+//                         PanelHeight={this.previewHeight}
+//                         isContentActive={returnTrue}
+//                         isDocumentActive={returnFalse}
+//                         ScreenToLocalTransform={this.getPreviewTransform}
+//                         docFilters={this.childDocFilters}
+//                         docRangeFilters={this.childDocRangeFilters}
+//                         searchFilterDocs={this.searchFilterDocs}
+//                         styleProvider={DefaultStyleProvider}
+//                         docViewPath={returnEmptyDoclist}
+//                         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+//                         ContainingCollectionView={this.props.CollectionView}
+//                         moveDocument={this.props.moveDocument}
+//                         addDocument={this.props.addDocument}
+//                         removeDocument={this.props.removeDocument}
+//                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+//                         addDocTab={this.props.addDocTab}
+//                         pinToPres={this.props.pinToPres}
+//                         bringToFront={returnFalse}
+//                     />
+//                 )}
+//             </div>
+//         );
+//     }
+
+//     @computed
+//     get schemaTable() {
+//         return (
+//             <SchemaTable
+//                 Document={this.props.Document}
+//                 PanelHeight={this.props.PanelHeight}
+//                 PanelWidth={this.props.PanelWidth}
+//                 childDocs={this.childDocs}
+//                 CollectionView={this.props.CollectionView}
+//                 ContainingCollectionView={this.props.ContainingCollectionView}
+//                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+//                 fieldKey={this.props.fieldKey}
+//                 renderDepth={this.props.renderDepth}
+//                 moveDocument={this.props.moveDocument}
+//                 ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+//                 active={this.props.isContentActive}
+//                 onDrop={this.onExternalDrop}
+//                 addDocTab={this.props.addDocTab}
+//                 pinToPres={this.props.pinToPres}
+//                 isSelected={this.props.isSelected}
+//                 isFocused={this.isFocused}
+//                 setFocused={this.setFocused}
+//                 setPreviewDoc={this.setPreviewDoc}
+//                 deleteDocument={this.props.removeDocument}
+//                 addDocument={this.props.addDocument}
+//                 dataDoc={this.props.DataDoc}
+//                 columns={this.columns}
+//                 documentKeys={this.documentKeys}
+//                 headerIsEditing={this._headerIsEditing}
+//                 openHeader={this.openHeader}
+//                 onClick={this.onTableClick}
+//                 onPointerDown={emptyFunction}
+//                 onResizedChange={this.onResizedChange}
+//                 setColumns={this.setColumns}
+//                 reorderColumns={this.reorderColumns}
+//                 changeColumns={this.changeColumns}
+//                 setHeaderIsEditing={this.setHeaderIsEditing}
+//                 changeColumnSort={this.setColumnSort}
+//             />
+//         );
+//     }
+
+//     @computed
+//     public get schemaToolbar() {
+//         return (
+//             <div className="collectionSchemaView-toolbar">
+//                 <div className="collectionSchemaView-toolbar-item">
+//                     <div id="preview-schema-checkbox-div">
+//                         <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
+//                         Show Preview
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     }
+
+//     onSpecificMenu = (e: React.MouseEvent) => {
+//         if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) {
+//             const cm = ContextMenu.Instance;
+//             const options = cm.findByDescription('Options...');
+//             const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+//             optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' });
+//             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
+//             cm.displayMenu(e.clientX, e.clientY);
+//             (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
+//             e.stopPropagation();
+//         }
+//     };
+
+//     @action
+//     onTableClick = (e: React.MouseEvent): void => {
+//         if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) {
+//             this.setPreviewDoc(undefined);
+//         } else {
+//             e.stopPropagation();
+//         }
+//         this.setFocused(this.props.Document);
+//         this.closeHeader();
+//     };
+
+//     onResizedChange = (newResized: Resize[], event: any) => {
+//         const columns = this.columns;
+//         newResized.forEach(resized => {
+//             const index = columns.findIndex(c => c.heading === resized.id);
+//             const column = columns[index];
+//             column.setWidth(resized.value);
+//             columns[index] = column;
+//         });
+//         this.columns = columns;
+//     };
+
+//     @action
+//     setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns);
+
+//     @undoBatch
+//     reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
+//         const columns = [...columnsValues];
+//         const oldIndex = columns.indexOf(toMove);
+//         const relIndex = columns.indexOf(relativeTo);
+//         const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex;
+
+//         if (oldIndex === newIndex) return;
+
+//         columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
+//         this.columns = columns;
+//     };
+
+//     onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation();
+
+//     render() {
+//         TraceMobx();
+//         if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0);
+//         const menuContent = this.renderMenuContent;
+//         const menu = (
+//             <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}>
+//                 <Measure
+//                     offset
+//                     onResize={action((r: any) => {
+//                         const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
+//                         this._menuWidth = dim[0];
+//                         this._menuHeight = dim[1];
+//                     })}>
+//                     {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
+//                 </Measure>
+//             </div>
+//         );
+//         return (
+//             <div
+//                 className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')}
+//                 style={{
+//                     overflow: this.props.scrollOverflow === true ? 'scroll' : undefined,
+//                     backgroundColor: 'white',
+//                     pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+//                     width: this.props.PanelWidth() || '100%',
+//                     height: this.props.PanelHeight() || '100%',
+//                     position: 'relative',
+//                 }}>
+//                 <div
+//                     className="collectionSchemaView-tableContainer"
+//                     style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
+//                     onContextMenu={this.onSpecificMenu}
+//                     onPointerDown={this.onPointerDown}
+//                     onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
+//                     onDrop={e => this.onExternalDrop(e, {})}
+//                     ref={this.createTarget}>
+//                     {this.schemaTable}
+//                 </div>
+//                 {this.dividerDragger}
+//                 {!this.previewWidth() ? null : this.previewPanel}
+//                 {this._headerOpen && this.props.isContentActive() ? menu : null}
+//             </div>
+//         );
+//         TraceMobx();
+//         return <div>HELLO</div>;
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
index 1b4fcf0a4..bc33f3be5 100644
--- a/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
@@ -1,694 +1,694 @@
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table';
-import { DateField } from '../../../../fields/DateField';
-import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
-import { listSpec } from '../../../../fields/Schema';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { ComputedField } from '../../../../fields/ScriptField';
-import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
-import { ImageField } from '../../../../fields/URLField';
-import { GetEffectiveAcl } from '../../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils';
-import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CompileScript, Transformer, ts } from '../../../util/Scripting';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import '../../../views/DocumentDecorations.scss';
-import { ContextMenu } from '../../ContextMenu';
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { PinProps } from '../../nodes/trails';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionView } from '../CollectionView';
-import {
-    CellProps,
-    CollectionSchemaButtons,
-    CollectionSchemaCell,
-    CollectionSchemaCheckboxCell,
-    CollectionSchemaDateCell,
-    CollectionSchemaDocCell,
-    CollectionSchemaImageCell,
-    CollectionSchemaListCell,
-    CollectionSchemaNumberCell,
-    CollectionSchemaStringCell,
-} from './CollectionSchemaCells';
-import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
-import { MovableColumn } from './OldCollectionSchemaMovableColumn';
-import { MovableRow } from './CollectionSchemaMovableRow';
-import './CollectionSchemaView.scss';
-
-enum ColumnType {
-    Any,
-    Number,
-    String,
-    Boolean,
-    Doc,
-    Image,
-    List,
-    Date,
-}
-
-// this map should be used for keys that should have a const type of value
-const columnTypes: Map<string, ColumnType> = new Map([
-    ['title', ColumnType.String],
-    ['x', ColumnType.Number],
-    ['y', ColumnType.Number],
-    ['_width', ColumnType.Number],
-    ['_height', ColumnType.Number],
-    ['_nativeWidth', ColumnType.Number],
-    ['_nativeHeight', ColumnType.Number],
-    ['isPrototype', ColumnType.Boolean],
-    ['_curPage', ColumnType.Number],
-    ['_currentTimecode', ColumnType.Number],
-    ['zIndex', ColumnType.Number],
-]);
-
-export interface SchemaTableProps {
-    Document: Doc; // child doc
-    dataDoc?: Doc;
-    PanelHeight: () => number;
-    PanelWidth: () => number;
-    childDocs?: Doc[];
-    CollectionView: Opt<CollectionView>;
-    ContainingCollectionView: Opt<CollectionView>;
-    ContainingCollectionDoc: Opt<Doc>;
-    fieldKey: string;
-    renderDepth: number;
-    deleteDocument?: (document: Doc | Doc[]) => boolean;
-    addDocument?: (document: Doc | Doc[]) => boolean;
-    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    ScreenToLocalTransform: () => Transform;
-    active: (outsideReaction: boolean | undefined) => boolean | undefined;
-    onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
-    addDocTab: (document: Doc, where: OpenWhere) => boolean;
-    pinToPres: (document: Doc, pinProps: PinProps) => void;
-    isSelected: (outsideReaction?: boolean) => boolean;
-    isFocused: (document: Doc, outsideReaction: boolean) => boolean;
-    setFocused: (document: Doc) => void;
-    setPreviewDoc: (document: Opt<Doc>) => void;
-    columns: SchemaHeaderField[];
-    documentKeys: any[];
-    headerIsEditing: boolean;
-    openHeader: (column: any, screenx: number, screeny: number) => void;
-    onClick: (e: React.MouseEvent) => void;
-    onPointerDown: (e: React.PointerEvent) => void;
-    onResizedChange: (newResized: Resize[], event: any) => void;
-    setColumns: (columns: SchemaHeaderField[]) => void;
-    reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => void;
-    changeColumns: (oldKey: string, newKey: string, addNew: boolean) => void;
-    setHeaderIsEditing: (isEditing: boolean) => void;
-    changeColumnSort: (columnField: SchemaHeaderField, descending: boolean | undefined) => void;
-}
-
-@observer
-export class SchemaTable extends React.Component<SchemaTableProps> {
-    @observable _cellIsEditing: boolean = false;
-    @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 };
-    @observable _openCollections: Set<number> = new Set();
-
-    @observable _showDoc: Doc | undefined;
-    @observable _showDataDoc: any = '';
-    @observable _showDocPos: number[] = [];
-
-    @observable _showTitleDropdown: boolean = false;
-
-    @computed get previewWidth() {
-        return () => NumCast(this.props.Document.schemaPreviewWidth);
-    }
-    @computed get previewHeight() {
-        return () => this.props.PanelHeight() - 2 * this.borderWidth;
-    }
-    @computed get tableWidth() {
-        return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
-    }
-
-    @computed get childDocs() {
-        if (this.props.childDocs) return this.props.childDocs;
-
-        const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
-        return DocListCast(doc[this.props.fieldKey]);
-    }
-    set childDocs(docs: Doc[]) {
-        const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
-        doc[this.props.fieldKey] = new List<Doc>(docs);
-    }
-
-    @computed get textWrappedRows() {
-        return Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
-    }
-    set textWrappedRows(textWrappedRows: string[]) {
-        this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
-    }
-
-    @computed get resized(): { id: string; value: number }[] {
-        return this.props.columns.reduce((resized, shf) => {
-            shf.width > -1 && resized.push({ id: shf.heading, value: shf.width });
-            return resized;
-        }, [] as { id: string; value: number }[]);
-    }
-    @computed get sorted(): SortingRule[] {
-        return this.props.columns.reduce((sorted, shf) => {
-            shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc });
-            return sorted;
-        }, [] as SortingRule[]);
-    }
-
-    @action
-    changeSorting = (col: any) => {
-        this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
-    };
-
-    @action
-    changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown);
-
-    @computed get borderWidth() {
-        return Number(COLLECTION_BORDER_WIDTH);
-    }
-    @computed get tableColumns(): Column<Doc>[] {
-        const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
-        const columns: Column<Doc>[] = [];
-        const tableIsFocused = this.props.isFocused(this.props.Document, false);
-        const focusedRow = this._focusedCell.row;
-        const focusedCol = this._focusedCell.col;
-        const isEditable = !this.props.headerIsEditing;
-
-        columns.push({
-            expander: true,
-            Header: '',
-            width: 58,
-            Expander: rowInfo => {
-                return rowInfo.original.type !== DocumentType.COL ? null : (
-                    <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}>
-                        <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" />
-                    </div>
-                );
-            },
-        });
-        columns.push(
-            ...this.props.columns.map(col => {
-                const icon: IconProp =
-                    this.getColumnType(col) === ColumnType.Number
-                        ? 'hashtag'
-                        : this.getColumnType(col) === ColumnType.String
-                        ? 'font'
-                        : this.getColumnType(col) === ColumnType.Boolean
-                        ? 'check-square'
-                        : this.getColumnType(col) === ColumnType.Doc
-                        ? 'file'
-                        : this.getColumnType(col) === ColumnType.Image
-                        ? 'image'
-                        : this.getColumnType(col) === ColumnType.List
-                        ? 'list-ul'
-                        : this.getColumnType(col) === ColumnType.Date
-                        ? 'calendar'
-                        : 'align-justify';
-
-                const keysDropdown = (
-                    <KeysDropdown
-                        keyValue={col.heading}
-                        possibleKeys={possibleKeys}
-                        existingKeys={this.props.columns.map(c => c.heading)}
-                        canAddNew={true}
-                        addNew={false}
-                        onSelect={this.props.changeColumns}
-                        setIsEditing={this.props.setHeaderIsEditing}
-                        docs={this.props.childDocs}
-                        Document={this.props.Document}
-                        dataDoc={this.props.dataDoc}
-                        fieldKey={this.props.fieldKey}
-                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                        ContainingCollectionView={this.props.ContainingCollectionView}
-                        active={this.props.active}
-                        openHeader={this.props.openHeader}
-                        icon={icon}
-                        col={col}
-                        // try commenting this out
-                        width={'100%'}
-                    />
-                );
-
-                const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up';
-                const header = (
-                    <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}>
-                        {keysDropdown}
-                        <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}>
-                            <FontAwesomeIcon icon={sortIcon} size="lg" />
-                        </div>
-                        {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
-                    </div>
-                );
-
-                return {
-                    Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
-                    accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0),
-                    id: col.heading,
-                    Cell: (rowProps: CellInfo) => {
-                        const rowIndex = rowProps.index;
-                        const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
-                        const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-
-                        const props: CellProps = {
-                            row: rowIndex,
-                            col: columnIndex,
-                            rowProps: rowProps,
-                            isFocused: isFocused,
-                            changeFocusedCellByIndex: this.changeFocusedCellByIndex,
-                            CollectionView: this.props.CollectionView,
-                            ContainingCollection: this.props.ContainingCollectionView,
-                            Document: this.props.Document,
-                            fieldKey: this.props.fieldKey,
-                            renderDepth: this.props.renderDepth,
-                            addDocTab: this.props.addDocTab,
-                            pinToPres: this.props.pinToPres,
-                            moveDocument: this.props.moveDocument,
-                            setIsEditing: this.setCellIsEditing,
-                            isEditable: isEditable,
-                            setPreviewDoc: this.props.setPreviewDoc,
-                            setComputed: this.setComputed,
-                            getField: this.getField,
-                            showDoc: this.showDoc,
-                        };
-
-                        switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
-                            case ColumnType.Number:
-                                return <CollectionSchemaNumberCell {...props} />;
-                            case ColumnType.String:
-                                return <CollectionSchemaStringCell {...props} />;
-                            case ColumnType.Boolean:
-                                return <CollectionSchemaCheckboxCell {...props} />;
-                            case ColumnType.Doc:
-                                return <CollectionSchemaDocCell {...props} />;
-                            case ColumnType.Image:
-                                return <CollectionSchemaImageCell {...props} />;
-                            case ColumnType.List:
-                                return <CollectionSchemaListCell {...props} />;
-                            case ColumnType.Date:
-                                return <CollectionSchemaDateCell {...props} />;
-                            default:
-                                return <CollectionSchemaCell {...props} />;
-                        }
-                    },
-                    minWidth: 200,
-                };
-            })
-        );
-        columns.push({
-            Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
-            accessor: (doc: Doc) => 0,
-            id: 'add',
-            Cell: (rowProps: CellInfo) => {
-                const rowIndex = rowProps.index;
-                const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
-                const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-                return (
-                    <CollectionSchemaButtons
-                        {...{
-                            row: rowProps.index,
-                            col: columnIndex,
-                            rowProps: rowProps,
-                            isFocused: isFocused,
-                            changeFocusedCellByIndex: this.changeFocusedCellByIndex,
-                            CollectionView: this.props.CollectionView,
-                            ContainingCollection: this.props.ContainingCollectionView,
-                            Document: this.props.Document,
-                            fieldKey: this.props.fieldKey,
-                            renderDepth: this.props.renderDepth,
-                            addDocTab: this.props.addDocTab,
-                            pinToPres: this.props.pinToPres,
-                            moveDocument: this.props.moveDocument,
-                            setIsEditing: this.setCellIsEditing,
-                            isEditable: isEditable,
-                            setPreviewDoc: this.props.setPreviewDoc,
-                            setComputed: this.setComputed,
-                            getField: this.getField,
-                            showDoc: this.showDoc,
-                        }}
-                    />
-                );
-            },
-            width: 28,
-            resizable: false,
-        });
-        return columns;
-    }
-
-    constructor(props: SchemaTableProps) {
-        super(props);
-        if (this.props.Document._schemaHeaders === undefined) {
-            this.props.Document._schemaHeaders = new List<SchemaHeaderField>([
-                new SchemaHeaderField('title', '#f1efeb'),
-                new SchemaHeaderField('author', '#f1efeb'),
-                new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date),
-                new SchemaHeaderField('text', '#f1efeb', ColumnType.String),
-                new SchemaHeaderField('type', '#f1efeb'),
-                new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc),
-            ]);
-        }
-    }
-
-    componentDidMount() {
-        document.addEventListener('keydown', this.onKeyDown);
-    }
-
-    componentWillUnmount() {
-        document.removeEventListener('keydown', this.onKeyDown);
-    }
-
-    tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
-        const tableDoc = this.props.Document[DataSym];
-        const effectiveAcl = GetEffectiveAcl(tableDoc);
-
-        if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
-            doc.context = this.props.Document;
-            tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
-            return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
-        }
-        return false;
-    };
-
-    private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
-        return !rowInfo
-            ? {}
-            : {
-                  ScreenToLocalTransform: this.props.ScreenToLocalTransform,
-                  addDoc: this.tableAddDoc,
-                  removeDoc: this.props.deleteDocument,
-                  rowInfo,
-                  rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
-                  textWrapRow: this.toggleTextWrapRow,
-                  rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
-                  dropAction: StrCast(this.props.Document.childDropAction),
-                  addDocTab: this.props.addDocTab,
-              };
-    };
-
-    private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
-        if (!rowInfo || column) return {};
-
-        const row = rowInfo.index;
-        //@ts-ignore
-        const col = this.columns.map(c => c.heading).indexOf(column!.id);
-        const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
-        // TODO: editing border doesn't work :(
-        return {
-            style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' },
-        };
-    };
-
-    @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing);
-
-    @action
-    onKeyDown = (e: KeyboardEvent): void => {
-        if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {
-            // && this.props.isSelected(true)) {
-            const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : '';
-            this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
-
-            if (direction) {
-                const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
-                pdoc && this.props.setPreviewDoc(pdoc);
-                e.stopPropagation();
-            }
-        } else if (e.keyCode === 27) {
-            this.props.setPreviewDoc(undefined);
-            e.stopPropagation(); // stopPropagation for left/right arrows
-        }
-    };
-
-    changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
-        switch (direction) {
-            case 'tab':
-                return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
-            case 'right':
-                return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
-            case 'left':
-                return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
-            case 'up':
-                return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
-            case 'down':
-                return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
-        }
-        return this._focusedCell;
-    };
-
-    @action
-    changeFocusedCellByIndex = (row: number, col: number): void => {
-        if (this._focusedCell.row !== row || this._focusedCell.col !== col) {
-            this._focusedCell = { row: row, col: col };
-        }
-        this.props.setFocused(this.props.Document);
-    };
-
-    @undoBatch
-    createRow = action(() => {
-        this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 }));
-        this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
-    });
-
-    @undoBatch
-    @action
-    createColumn = () => {
-        const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`;
-        for (let index = 0; index < 100; index++) {
-            if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) {
-                this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb'));
-                break;
-            }
-        }
-    };
-
-    @action
-    getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
-        if (doc && field && column.type === ColumnType.Any) {
-            const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)];
-            if (val instanceof ImageField) return ColumnType.Image;
-            if (val instanceof Doc) return ColumnType.Doc;
-            if (val instanceof DateField) return ColumnType.Date;
-            if (val instanceof List) return ColumnType.List;
-        }
-        if (column.type && column.type !== 0) {
-            return column.type;
-        }
-        if (columnTypes.get(column.heading)) {
-            return (column.type = columnTypes.get(column.heading)!);
-        }
-        return (column.type = ColumnType.Any);
-    };
-
-    @undoBatch
-    @action
-    toggleTextwrap = async () => {
-        const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
-        if (textwrappedRows.length) {
-            this.props.Document.textwrappedSchemaRows = new List<string>([]);
-        } else {
-            const docs = DocListCast(this.props.Document[this.props.fieldKey]);
-            const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
-            this.props.Document.textwrappedSchemaRows = new List<string>(allRows);
-        }
-    };
-
-    @action
-    toggleTextWrapRow = (doc: Doc): void => {
-        const textWrapped = this.textWrappedRows;
-        const index = textWrapped.findIndex(id => doc[Id] === id);
-
-        index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
-
-        this.textWrappedRows = textWrapped;
-    };
-
-    @computed
-    get reactTable() {
-        const children = this.childDocs;
-        const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
-        const expanded: { [name: string]: any } = {};
-        Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true));
-        const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
-
-        return (
-            <ReactTable
-                style={{ position: 'relative' }}
-                data={children}
-                page={0}
-                pageSize={children.length}
-                showPagination={false}
-                columns={this.tableColumns}
-                getTrProps={this.getTrProps}
-                getTdProps={this.getTdProps}
-                sortable={false}
-                TrComponent={MovableRow}
-                sorted={this.sorted}
-                expanded={expanded}
-                resized={this.resized}
-                onResizedChange={this.props.onResizedChange}
-                // if it has a child, render another table with the children
-                SubComponent={
-                    !hasCollectionChild
-                        ? undefined
-                        : row =>
-                              row.original.type !== DocumentType.COL ? null : (
-                                  <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub">
-                                      <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} />
-                                  </div>
-                              )
-                }
-            />
-        );
-    }
-
-    onContextMenu = (e: React.MouseEvent): void => {
-        ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' });
-    };
-
-    getField = (row: number, col?: number) => {
-        const docs = this.childDocs;
-
-        row = row % docs.length;
-        while (row < 0) row += docs.length;
-        const columns = this.props.columns;
-        const doc = docs[row];
-        if (col === undefined) {
-            return doc;
-        }
-        if (col >= 0 && col < columns.length) {
-            const column = this.props.columns[col].heading;
-            return doc[column];
-        }
-        return undefined;
-    };
-
-    createTransformer = (row: number, col: number): Transformer => {
-        const self = this;
-        const captures: { [name: string]: Field } = {};
-
-        const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
-            return root => {
-                function visit(node: ts.Node) {
-                    node = ts.visitEachChild(node, visit, context);
-                    if (ts.isIdentifier(node)) {
-                        const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
-                        const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
-                        if (isntPropAccess && isntPropAssign) {
-                            if (node.text === '$r') {
-                                return ts.createNumericLiteral(row.toString());
-                            } else if (node.text === '$c') {
-                                return ts.createNumericLiteral(col.toString());
-                            } else if (node.text === '$') {
-                                if (ts.isCallExpression(node.parent)) {
-                                    // captures.doc = self.props.Document;
-                                    // captures.key = self.props.fieldKey;
-                                }
-                            }
-                        }
-                    }
-
-                    return node;
-                }
-                return ts.visitNode(root, visit);
-            };
-        };
-
-        // const getVars = () => {
-        //     return { capturedVariables: captures };
-        // };
-
-        return { transformer /*getVars*/ };
-    };
-
-    setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
-        script = `const $ = (row:number, col?:number) => {
-                const rval = (doc as any)[key][row + ${row}];
-                return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
-            }
-            return ${script}`;
-        const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
-        if (compiled.compiled) {
-            doc[field] = new ComputedField(compiled);
-            return true;
-        }
-        return false;
-    };
-
-    @action
-    showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => {
-        this._showDoc = doc;
-        if (dataDoc && screenX && screenY) {
-            this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY);
-        }
-    };
-
-    onOpenClick = () => {
-        this._showDoc && this.props.addDocTab(this._showDoc, OpenWhere.addRight);
-    };
-
-    getPreviewTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth);
-    };
-
-    render() {
-        const preview = '';
-        return (
-            <div
-                className="collectionSchemaView-table"
-                onPointerDown={this.props.onPointerDown}
-                onClick={this.props.onClick}
-                onWheel={e => this.props.active(true) && e.stopPropagation()}
-                onDrop={e => this.props.onDrop(e, {})}
-                onContextMenu={this.onContextMenu}>
-                {this.reactTable}
-                {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : (
-                    <div className="collectionSchemaView-addRow" onClick={this.createRow}>
-                        + new
-                    </div>
-                )}
-                {!this._showDoc ? null : (
-                    <div
-                        className="collectionSchemaView-documentPreview"
-                        ref="overlay"
-                        style={{
-                            position: 'absolute',
-                            width: 150,
-                            height: 150,
-                            background: 'dimgray',
-                            display: 'block',
-                            top: 0,
-                            left: 0,
-                            transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`,
-                        }}>
-                        <DocumentView
-                            Document={this._showDoc}
-                            DataDoc={this._showDataDoc}
-                            styleProvider={DefaultStyleProvider}
-                            docViewPath={returnEmptyDoclist}
-                            focus={DocUtils.DefaultFocus}
-                            renderDepth={this.props.renderDepth}
-                            rootSelected={returnFalse}
-                            isContentActive={returnTrue}
-                            isDocumentActive={returnFalse}
-                            PanelWidth={() => 150}
-                            PanelHeight={() => 150}
-                            ScreenToLocalTransform={this.getPreviewTransform}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                            ContainingCollectionView={this.props.CollectionView}
-                            moveDocument={this.props.moveDocument}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            addDocTab={this.props.addDocTab}
-                            pinToPres={this.props.pinToPres}
-                            bringToFront={returnFalse}></DocumentView>
-                    </div>
-                )}
-            </div>
-        );
-    }
-}
+// import { IconProp } from '@fortawesome/fontawesome-svg-core';
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action, computed, observable } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table';
+// import { DateField } from '../../../../fields/DateField';
+// import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { List } from '../../../../fields/List';
+// import { listSpec } from '../../../../fields/Schema';
+// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { ComputedField } from '../../../../fields/ScriptField';
+// import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+// import { ImageField } from '../../../../fields/URLField';
+// import { GetEffectiveAcl } from '../../../../fields/util';
+// import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils';
+// import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+// import { DocumentType } from '../../../documents/DocumentTypes';
+// import { CompileScript, Transformer, ts } from '../../../util/Scripting';
+// import { Transform } from '../../../util/Transform';
+// import { undoBatch } from '../../../util/UndoManager';
+// import '../../../views/DocumentDecorations.scss';
+// import { ContextMenu } from '../../ContextMenu';
+// import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+// import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
+// import { PinProps } from '../../nodes/trails';
+// import { DefaultStyleProvider } from '../../StyleProvider';
+// import { CollectionView } from '../CollectionView';
+// import {
+//     CellProps,
+//     CollectionSchemaButtons,
+//     CollectionSchemaCell,
+//     CollectionSchemaCheckboxCell,
+//     CollectionSchemaDateCell,
+//     CollectionSchemaDocCell,
+//     CollectionSchemaImageCell,
+//     CollectionSchemaListCell,
+//     CollectionSchemaNumberCell,
+//     CollectionSchemaStringCell,
+// } from './CollectionSchemaCells';
+// import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
+// import { MovableColumn } from './OldCollectionSchemaMovableColumn';
+// import { MovableRow } from './CollectionSchemaMovableRow';
+// import './CollectionSchemaView.scss';
+
+// enum ColumnType {
+//     Any,
+//     Number,
+//     String,
+//     Boolean,
+//     Doc,
+//     Image,
+//     List,
+//     Date,
+// }
+
+// // this map should be used for keys that should have a const type of value
+// const columnTypes: Map<string, ColumnType> = new Map([
+//     ['title', ColumnType.String],
+//     ['x', ColumnType.Number],
+//     ['y', ColumnType.Number],
+//     ['_width', ColumnType.Number],
+//     ['_height', ColumnType.Number],
+//     ['_nativeWidth', ColumnType.Number],
+//     ['_nativeHeight', ColumnType.Number],
+//     ['isPrototype', ColumnType.Boolean],
+//     ['_curPage', ColumnType.Number],
+//     ['_currentTimecode', ColumnType.Number],
+//     ['zIndex', ColumnType.Number],
+// ]);
+
+// export interface SchemaTableProps {
+//     Document: Doc; // child doc
+//     dataDoc?: Doc;
+//     PanelHeight: () => number;
+//     PanelWidth: () => number;
+//     childDocs?: Doc[];
+//     CollectionView: Opt<CollectionView>;
+//     ContainingCollectionView: Opt<CollectionView>;
+//     ContainingCollectionDoc: Opt<Doc>;
+//     fieldKey: string;
+//     renderDepth: number;
+//     deleteDocument?: (document: Doc | Doc[]) => boolean;
+//     addDocument?: (document: Doc | Doc[]) => boolean;
+//     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+//     ScreenToLocalTransform: () => Transform;
+//     active: (outsideReaction: boolean | undefined) => boolean | undefined;
+//     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
+//     addDocTab: (document: Doc, where: OpenWhere) => boolean;
+//     pinToPres: (document: Doc, pinProps: PinProps) => void;
+//     isSelected: (outsideReaction?: boolean) => boolean;
+//     isFocused: (document: Doc, outsideReaction: boolean) => boolean;
+//     setFocused: (document: Doc) => void;
+//     setPreviewDoc: (document: Opt<Doc>) => void;
+//     columns: SchemaHeaderField[];
+//     documentKeys: any[];
+//     headerIsEditing: boolean;
+//     openHeader: (column: any, screenx: number, screeny: number) => void;
+//     onClick: (e: React.MouseEvent) => void;
+//     onPointerDown: (e: React.PointerEvent) => void;
+//     onResizedChange: (newResized: Resize[], event: any) => void;
+//     setColumns: (columns: SchemaHeaderField[]) => void;
+//     reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => void;
+//     changeColumns: (oldKey: string, newKey: string, addNew: boolean) => void;
+//     setHeaderIsEditing: (isEditing: boolean) => void;
+//     changeColumnSort: (columnField: SchemaHeaderField, descending: boolean | undefined) => void;
+// }
+
+// @observer
+// export class SchemaTable extends React.Component<SchemaTableProps> {
+//     @observable _cellIsEditing: boolean = false;
+//     @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 };
+//     @observable _openCollections: Set<number> = new Set();
+
+//     @observable _showDoc: Doc | undefined;
+//     @observable _showDataDoc: any = '';
+//     @observable _showDocPos: number[] = [];
+
+//     @observable _showTitleDropdown: boolean = false;
+
+//     @computed get previewWidth() {
+//         return () => NumCast(this.props.Document.schemaPreviewWidth);
+//     }
+//     @computed get previewHeight() {
+//         return () => this.props.PanelHeight() - 2 * this.borderWidth;
+//     }
+//     @computed get tableWidth() {
+//         return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+//     }
+
+//     @computed get childDocs() {
+//         if (this.props.childDocs) return this.props.childDocs;
+
+//         const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+//         return DocListCast(doc[this.props.fieldKey]);
+//     }
+//     set childDocs(docs: Doc[]) {
+//         const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+//         doc[this.props.fieldKey] = new List<Doc>(docs);
+//     }
+
+//     @computed get textWrappedRows() {
+//         return Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
+//     }
+//     set textWrappedRows(textWrappedRows: string[]) {
+//         this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
+//     }
+
+//     @computed get resized(): { id: string; value: number }[] {
+//         return this.props.columns.reduce((resized, shf) => {
+//             shf.width > -1 && resized.push({ id: shf.heading, value: shf.width });
+//             return resized;
+//         }, [] as { id: string; value: number }[]);
+//     }
+//     @computed get sorted(): SortingRule[] {
+//         return this.props.columns.reduce((sorted, shf) => {
+//             shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc });
+//             return sorted;
+//         }, [] as SortingRule[]);
+//     }
+
+//     @action
+//     changeSorting = (col: any) => {
+//         this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
+//     };
+
+//     @action
+//     changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown);
+
+//     @computed get borderWidth() {
+//         return Number(COLLECTION_BORDER_WIDTH);
+//     }
+//     @computed get tableColumns(): Column<Doc>[] {
+//         const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
+//         const columns: Column<Doc>[] = [];
+//         const tableIsFocused = this.props.isFocused(this.props.Document, false);
+//         const focusedRow = this._focusedCell.row;
+//         const focusedCol = this._focusedCell.col;
+//         const isEditable = !this.props.headerIsEditing;
+
+//         columns.push({
+//             expander: true,
+//             Header: '',
+//             width: 58,
+//             Expander: rowInfo => {
+//                 return rowInfo.original.type !== DocumentType.COL ? null : (
+//                     <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}>
+//                         <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" />
+//                     </div>
+//                 );
+//             },
+//         });
+//         columns.push(
+//             ...this.props.columns.map(col => {
+//                 const icon: IconProp =
+//                     this.getColumnType(col) === ColumnType.Number
+//                         ? 'hashtag'
+//                         : this.getColumnType(col) === ColumnType.String
+//                         ? 'font'
+//                         : this.getColumnType(col) === ColumnType.Boolean
+//                         ? 'check-square'
+//                         : this.getColumnType(col) === ColumnType.Doc
+//                         ? 'file'
+//                         : this.getColumnType(col) === ColumnType.Image
+//                         ? 'image'
+//                         : this.getColumnType(col) === ColumnType.List
+//                         ? 'list-ul'
+//                         : this.getColumnType(col) === ColumnType.Date
+//                         ? 'calendar'
+//                         : 'align-justify';
+
+//                 const keysDropdown = (
+//                     <KeysDropdown
+//                         keyValue={col.heading}
+//                         possibleKeys={possibleKeys}
+//                         existingKeys={this.props.columns.map(c => c.heading)}
+//                         canAddNew={true}
+//                         addNew={false}
+//                         onSelect={this.props.changeColumns}
+//                         setIsEditing={this.props.setHeaderIsEditing}
+//                         docs={this.props.childDocs}
+//                         Document={this.props.Document}
+//                         dataDoc={this.props.dataDoc}
+//                         fieldKey={this.props.fieldKey}
+//                         ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+//                         ContainingCollectionView={this.props.ContainingCollectionView}
+//                         active={this.props.active}
+//                         openHeader={this.props.openHeader}
+//                         icon={icon}
+//                         col={col}
+//                         // try commenting this out
+//                         width={'100%'}
+//                     />
+//                 );
+
+//                 const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up';
+//                 const header = (
+//                     <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}>
+//                         {keysDropdown}
+//                         <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}>
+//                             <FontAwesomeIcon icon={sortIcon} size="lg" />
+//                         </div>
+//                         {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
+//                     </div>
+//                 );
+
+//                 return {
+//                     Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
+//                     accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0),
+//                     id: col.heading,
+//                     Cell: (rowProps: CellInfo) => {
+//                         const rowIndex = rowProps.index;
+//                         const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+//                         const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+
+//                         const props: CellProps = {
+//                             row: rowIndex,
+//                             col: columnIndex,
+//                             rowProps: rowProps,
+//                             isFocused: isFocused,
+//                             changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+//                             CollectionView: this.props.CollectionView,
+//                             ContainingCollection: this.props.ContainingCollectionView,
+//                             Document: this.props.Document,
+//                             fieldKey: this.props.fieldKey,
+//                             renderDepth: this.props.renderDepth,
+//                             addDocTab: this.props.addDocTab,
+//                             pinToPres: this.props.pinToPres,
+//                             moveDocument: this.props.moveDocument,
+//                             setIsEditing: this.setCellIsEditing,
+//                             isEditable: isEditable,
+//                             setPreviewDoc: this.props.setPreviewDoc,
+//                             setComputed: this.setComputed,
+//                             getField: this.getField,
+//                             showDoc: this.showDoc,
+//                         };
+
+//                         switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
+//                             case ColumnType.Number:
+//                                 return <CollectionSchemaNumberCell {...props} />;
+//                             case ColumnType.String:
+//                                 return <CollectionSchemaStringCell {...props} />;
+//                             case ColumnType.Boolean:
+//                                 return <CollectionSchemaCheckboxCell {...props} />;
+//                             case ColumnType.Doc:
+//                                 return <CollectionSchemaDocCell {...props} />;
+//                             case ColumnType.Image:
+//                                 return <CollectionSchemaImageCell {...props} />;
+//                             case ColumnType.List:
+//                                 return <CollectionSchemaListCell {...props} />;
+//                             case ColumnType.Date:
+//                                 return <CollectionSchemaDateCell {...props} />;
+//                             default:
+//                                 return <CollectionSchemaCell {...props} />;
+//                         }
+//                     },
+//                     minWidth: 200,
+//                 };
+//             })
+//         );
+//         columns.push({
+//             Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
+//             accessor: (doc: Doc) => 0,
+//             id: 'add',
+//             Cell: (rowProps: CellInfo) => {
+//                 const rowIndex = rowProps.index;
+//                 const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+//                 const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+//                 return (
+//                     <CollectionSchemaButtons
+//                         {...{
+//                             row: rowProps.index,
+//                             col: columnIndex,
+//                             rowProps: rowProps,
+//                             isFocused: isFocused,
+//                             changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+//                             CollectionView: this.props.CollectionView,
+//                             ContainingCollection: this.props.ContainingCollectionView,
+//                             Document: this.props.Document,
+//                             fieldKey: this.props.fieldKey,
+//                             renderDepth: this.props.renderDepth,
+//                             addDocTab: this.props.addDocTab,
+//                             pinToPres: this.props.pinToPres,
+//                             moveDocument: this.props.moveDocument,
+//                             setIsEditing: this.setCellIsEditing,
+//                             isEditable: isEditable,
+//                             setPreviewDoc: this.props.setPreviewDoc,
+//                             setComputed: this.setComputed,
+//                             getField: this.getField,
+//                             showDoc: this.showDoc,
+//                         }}
+//                     />
+//                 );
+//             },
+//             width: 28,
+//             resizable: false,
+//         });
+//         return columns;
+//     }
+
+//     constructor(props: SchemaTableProps) {
+//         super(props);
+//         if (this.props.Document._schemaHeaders === undefined) {
+//             this.props.Document._schemaHeaders = new List<SchemaHeaderField>([
+//                 new SchemaHeaderField('title', '#f1efeb'),
+//                 new SchemaHeaderField('author', '#f1efeb'),
+//                 new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date),
+//                 new SchemaHeaderField('text', '#f1efeb', ColumnType.String),
+//                 new SchemaHeaderField('type', '#f1efeb'),
+//                 new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc),
+//             ]);
+//         }
+//     }
+
+//     componentDidMount() {
+//         document.addEventListener('keydown', this.onKeyDown);
+//     }
+
+//     componentWillUnmount() {
+//         document.removeEventListener('keydown', this.onKeyDown);
+//     }
+
+//     tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
+//         const tableDoc = this.props.Document[DataSym];
+//         const effectiveAcl = GetEffectiveAcl(tableDoc);
+
+//         if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
+//             doc.context = this.props.Document;
+//             tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
+//             return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+//         }
+//         return false;
+//     };
+
+//     private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
+//         return !rowInfo
+//             ? {}
+//             : {
+//                   ScreenToLocalTransform: this.props.ScreenToLocalTransform,
+//                   addDoc: this.tableAddDoc,
+//                   removeDoc: this.props.deleteDocument,
+//                   rowInfo,
+//                   rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
+//                   textWrapRow: this.toggleTextWrapRow,
+//                   rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
+//                   dropAction: StrCast(this.props.Document.childDropAction),
+//                   addDocTab: this.props.addDocTab,
+//               };
+//     };
+
+//     private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
+//         if (!rowInfo || column) return {};
+
+//         const row = rowInfo.index;
+//         //@ts-ignore
+//         const col = this.columns.map(c => c.heading).indexOf(column!.id);
+//         const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
+//         // TODO: editing border doesn't work :(
+//         return {
+//             style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' },
+//         };
+//     };
+
+//     @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing);
+
+//     @action
+//     onKeyDown = (e: KeyboardEvent): void => {
+//         if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {
+//             // && this.props.isSelected(true)) {
+//             const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : '';
+//             this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
+
+//             if (direction) {
+//                 const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
+//                 pdoc && this.props.setPreviewDoc(pdoc);
+//                 e.stopPropagation();
+//             }
+//         } else if (e.keyCode === 27) {
+//             this.props.setPreviewDoc(undefined);
+//             e.stopPropagation(); // stopPropagation for left/right arrows
+//         }
+//     };
+
+//     changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
+//         switch (direction) {
+//             case 'tab':
+//                 return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
+//             case 'right':
+//                 return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
+//             case 'left':
+//                 return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
+//             case 'up':
+//                 return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
+//             case 'down':
+//                 return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
+//         }
+//         return this._focusedCell;
+//     };
+
+//     @action
+//     changeFocusedCellByIndex = (row: number, col: number): void => {
+//         if (this._focusedCell.row !== row || this._focusedCell.col !== col) {
+//             this._focusedCell = { row: row, col: col };
+//         }
+//         this.props.setFocused(this.props.Document);
+//     };
+
+//     @undoBatch
+//     createRow = action(() => {
+//         this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 }));
+//         this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
+//     });
+
+//     @undoBatch
+//     @action
+//     createColumn = () => {
+//         const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`;
+//         for (let index = 0; index < 100; index++) {
+//             if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) {
+//                 this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb'));
+//                 break;
+//             }
+//         }
+//     };
+
+//     @action
+//     getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
+//         if (doc && field && column.type === ColumnType.Any) {
+//             const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)];
+//             if (val instanceof ImageField) return ColumnType.Image;
+//             if (val instanceof Doc) return ColumnType.Doc;
+//             if (val instanceof DateField) return ColumnType.Date;
+//             if (val instanceof List) return ColumnType.List;
+//         }
+//         if (column.type && column.type !== 0) {
+//             return column.type;
+//         }
+//         if (columnTypes.get(column.heading)) {
+//             return (column.type = columnTypes.get(column.heading)!);
+//         }
+//         return (column.type = ColumnType.Any);
+//     };
+
+//     @undoBatch
+//     @action
+//     toggleTextwrap = async () => {
+//         const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
+//         if (textwrappedRows.length) {
+//             this.props.Document.textwrappedSchemaRows = new List<string>([]);
+//         } else {
+//             const docs = DocListCast(this.props.Document[this.props.fieldKey]);
+//             const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+//             this.props.Document.textwrappedSchemaRows = new List<string>(allRows);
+//         }
+//     };
+
+//     @action
+//     toggleTextWrapRow = (doc: Doc): void => {
+//         const textWrapped = this.textWrappedRows;
+//         const index = textWrapped.findIndex(id => doc[Id] === id);
+
+//         index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
+
+//         this.textWrappedRows = textWrapped;
+//     };
+
+//     @computed
+//     get reactTable() {
+//         const children = this.childDocs;
+//         const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
+//         const expanded: { [name: string]: any } = {};
+//         Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true));
+//         const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
+
+//         return (
+//             <ReactTable
+//                 style={{ position: 'relative' }}
+//                 data={children}
+//                 page={0}
+//                 pageSize={children.length}
+//                 showPagination={false}
+//                 columns={this.tableColumns}
+//                 getTrProps={this.getTrProps}
+//                 getTdProps={this.getTdProps}
+//                 sortable={false}
+//                 TrComponent={MovableRow}
+//                 sorted={this.sorted}
+//                 expanded={expanded}
+//                 resized={this.resized}
+//                 onResizedChange={this.props.onResizedChange}
+//                 // if it has a child, render another table with the children
+//                 SubComponent={
+//                     !hasCollectionChild
+//                         ? undefined
+//                         : row =>
+//                               row.original.type !== DocumentType.COL ? null : (
+//                                   <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub">
+//                                       <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} />
+//                                   </div>
+//                               )
+//                 }
+//             />
+//         );
+//     }
+
+//     onContextMenu = (e: React.MouseEvent): void => {
+//         ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' });
+//     };
+
+//     getField = (row: number, col?: number) => {
+//         const docs = this.childDocs;
+
+//         row = row % docs.length;
+//         while (row < 0) row += docs.length;
+//         const columns = this.props.columns;
+//         const doc = docs[row];
+//         if (col === undefined) {
+//             return doc;
+//         }
+//         if (col >= 0 && col < columns.length) {
+//             const column = this.props.columns[col].heading;
+//             return doc[column];
+//         }
+//         return undefined;
+//     };
+
+//     createTransformer = (row: number, col: number): Transformer => {
+//         const self = this;
+//         const captures: { [name: string]: Field } = {};
+
+//         const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
+//             return root => {
+//                 function visit(node: ts.Node) {
+//                     node = ts.visitEachChild(node, visit, context);
+//                     if (ts.isIdentifier(node)) {
+//                         const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
+//                         const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
+//                         if (isntPropAccess && isntPropAssign) {
+//                             if (node.text === '$r') {
+//                                 return ts.createNumericLiteral(row.toString());
+//                             } else if (node.text === '$c') {
+//                                 return ts.createNumericLiteral(col.toString());
+//                             } else if (node.text === '$') {
+//                                 if (ts.isCallExpression(node.parent)) {
+//                                     // captures.doc = self.props.Document;
+//                                     // captures.key = self.props.fieldKey;
+//                                 }
+//                             }
+//                         }
+//                     }
+
+//                     return node;
+//                 }
+//                 return ts.visitNode(root, visit);
+//             };
+//         };
+
+//         // const getVars = () => {
+//         //     return { capturedVariables: captures };
+//         // };
+
+//         return { transformer /*getVars*/ };
+//     };
+
+//     setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
+//         script = `const $ = (row:number, col?:number) => {
+//                 const rval = (doc as any)[key][row + ${row}];
+//                 return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
+//             }
+//             return ${script}`;
+//         const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
+//         if (compiled.compiled) {
+//             doc[field] = new ComputedField(compiled);
+//             return true;
+//         }
+//         return false;
+//     };
+
+//     @action
+//     showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => {
+//         this._showDoc = doc;
+//         if (dataDoc && screenX && screenY) {
+//             this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY);
+//         }
+//     };
+
+//     onOpenClick = () => {
+//         this._showDoc && this.props.addDocTab(this._showDoc, OpenWhere.addRight);
+//     };
+
+//     getPreviewTransform = (): Transform => {
+//         return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth);
+//     };
+
+//     render() {
+//         const preview = '';
+//         return (
+//             <div
+//                 className="collectionSchemaView-table"
+//                 onPointerDown={this.props.onPointerDown}
+//                 onClick={this.props.onClick}
+//                 onWheel={e => this.props.active(true) && e.stopPropagation()}
+//                 onDrop={e => this.props.onDrop(e, {})}
+//                 onContextMenu={this.onContextMenu}>
+//                 {this.reactTable}
+//                 {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : (
+//                     <div className="collectionSchemaView-addRow" onClick={this.createRow}>
+//                         + new
+//                     </div>
+//                 )}
+//                 {!this._showDoc ? null : (
+//                     <div
+//                         className="collectionSchemaView-documentPreview"
+//                         ref="overlay"
+//                         style={{
+//                             position: 'absolute',
+//                             width: 150,
+//                             height: 150,
+//                             background: 'dimgray',
+//                             display: 'block',
+//                             top: 0,
+//                             left: 0,
+//                             transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`,
+//                         }}>
+//                         <DocumentView
+//                             Document={this._showDoc}
+//                             DataDoc={this._showDataDoc}
+//                             styleProvider={DefaultStyleProvider}
+//                             docViewPath={returnEmptyDoclist}
+//                             focus={DocUtils.DefaultFocus}
+//                             renderDepth={this.props.renderDepth}
+//                             rootSelected={returnFalse}
+//                             isContentActive={returnTrue}
+//                             isDocumentActive={returnFalse}
+//                             PanelWidth={() => 150}
+//                             PanelHeight={() => 150}
+//                             ScreenToLocalTransform={this.getPreviewTransform}
+//                             docFilters={returnEmptyFilter}
+//                             docRangeFilters={returnEmptyFilter}
+//                             searchFilterDocs={returnEmptyDoclist}
+//                             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+//                             ContainingCollectionView={this.props.CollectionView}
+//                             moveDocument={this.props.moveDocument}
+//                             whenChildContentsActiveChanged={emptyFunction}
+//                             addDocTab={this.props.addDocTab}
+//                             pinToPres={this.props.pinToPres}
+//                             bringToFront={returnFalse}></DocumentView>
+//                     </div>
+//                 )}
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 569579996..459554ebe 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,6 +44,7 @@ import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
 import { LoadingBox } from './LoadingBox';
+import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -268,6 +269,7 @@ export class DocumentContentsView extends React.Component<
                     HTMLtag,
                     ComparisonBox,
                     LoadingBox,
+                    SchemaRowBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From 6c58ca9d473103624be82c6f2da90f22bafd7b98 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sat, 28 Jan 2023 23:27:17 -0500
Subject: version without schemarow as documentview

---
 src/client/documents/DocumentTypes.ts              |  1 -
 src/client/documents/Documents.ts                  | 11 ---
 .../collectionSchema/CollectionSchemaView.scss     |  6 --
 .../collectionSchema/CollectionSchemaView.tsx      | 94 ++++++----------------
 .../collectionSchema/SchemaColumnHeader.tsx        |  2 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  | 88 +++++++-------------
 src/client/views/nodes/DocumentContentsView.tsx    |  2 -
 7 files changed, 53 insertions(+), 151 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index b910b26b0..d99cd2dac 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -40,7 +40,6 @@ export enum DocumentType {
     SEARCHITEM = 'searchitem',
     COMPARISON = 'comparison',
     GROUP = 'group',
-    SCHEMAROW = 'schemarow',
 
     LINKDB = 'linkdb', // database of links  ??? why do we have this
     SCRIPTDB = 'scriptdb', // database of scripts
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f6104d655..7de1221cc 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -26,7 +26,6 @@ import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
-import { SchemaRowBox } from '../views/collections/collectionSchema/SchemaRowBox';
 import { CollectionView } from '../views/collections/CollectionView';
 import { ContextMenu } from '../views/ContextMenu';
 import { ContextMenuProps } from '../views/ContextMenuItem';
@@ -650,12 +649,6 @@ export namespace Docs {
                     options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' },
                 },
             ],
-            [
-                DocumentType.SCHEMAROW,
-                {
-                    layout: { view: SchemaRowBox, dataField: defaultDataKey },
-                },
-            ],
             [
                 DocumentType.LOADING,
                 {
@@ -1148,10 +1141,6 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
         }
 
-        export function SchemaRowDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCHEMAROW), undefined, { ...(options || {}) });
-        }
-
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, viewType: CollectionViewType.Docking, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index e0d0101a2..4fa5d80e2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -126,16 +126,10 @@
     }
 }
 
-.schema-row-wrapper {
-    max-height: 70px;
-    overflow: hidden;
-}
-
 .schema-header-row,
 .schema-row {
     display: flex;
     flex-direction: row;
-    height: 100%;
     max-height: 70px;
     overflow: auto;
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index c9f934aec..29b22c0d5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,29 +1,28 @@
 import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet, trace, untracked } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, DocListCast, Opt, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyString, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
+import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { EditableView } from '../../EditableView';
+import { DocumentView } from '../../nodes/DocumentView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
-import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { Transform } from '../../../util/Transform';
 
 export enum ColumnType {
     Number,
@@ -33,7 +32,7 @@ export enum ColumnType {
     Image,
 }
 
-const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data', 'tags'];
+const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data', 'creationDate'];
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
@@ -399,25 +398,6 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
-    focusDocument = (doc: Doc, options: DocFocusOptions) => {
-        Doc.BrushDoc(doc);
-
-        let focusSpeed = 0;
-        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
-        if (found) {
-            const top = found.getBoundingClientRect().top;
-            const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
-            if (Math.floor(localTop[1]) !== 0) {
-                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
-            }
-        }
-        const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
-        this.props.focus(this.rootDoc, {
-            ...options,
-            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
-        });
-    };
-
     isChildContentActive = () =>
         this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
 
@@ -465,52 +445,24 @@ export class CollectionSchemaView extends CollectionSubView() {
                             let dref: Opt<DocumentView>;
 
                             return (
-                                <div className="schema-row-wrapper">
-                                    <DocumentView
-                                        {...this.props}
-                                        ref={r => (dref = r || undefined)}
-                                        LayoutTemplate={this.props.childLayoutTemplate}
-                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
-                                        renderDepth={this.props.renderDepth + 1}
-                                        Document={doc}
-                                        DataDoc={dataDoc}
-                                        ContainingCollectionView={this.props.CollectionView}
-                                        ContainingCollectionDoc={this.Document}
-                                        PanelWidth={this.props.PanelWidth}
-                                        PanelHeight={() => 70}
-                                        styleProvider={DefaultStyleProvider}
-                                        focus={this.focusDocument}
-                                        docFilters={this.childDocFilters}
-                                        docRangeFilters={this.childDocRangeFilters}
-                                        searchFilterDocs={this.searchFilterDocs}
-                                        rootSelected={this.rootSelected}
-                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
-                                        bringToFront={emptyFunction}
-                                        isContentActive={this.isChildContentActive}
-                                        hideDecorations={true}
-                                        hideTitle={true}
-                                        hideDocumentButtonBar={true}
-                                    />
-                                </div>
+                                <SchemaRowBox
+                                    {...this.props}
+                                    key={index}
+                                    Document={doc}
+                                    ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                                    ContainingCollectionView={this.props.CollectionView}
+                                    rowIndex={index}
+                                    columnKeys={this.columnKeys}
+                                    columnWidths={this.displayColumnWidths}
+                                    rowMenuWidth={CollectionSchemaView._rowMenuWidth}
+                                    selectedDocs={this._selectedDocs}
+                                    selectRow={this.selectRow}
+                                    startDrag={this.startDrag}
+                                    dragging={this._isDragging}
+                                    dropIndex={this.setDropIndex}
+                                    addRowRef={this.addRowRef}
+                                />
                             );
-
-                            // <SchemaRowBox
-                            //     {...this.props}
-                            //     key={index}
-                            //     Document={doc}
-                            //     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                            //     ContainingCollectionView={this.props.CollectionView}
-                            //     rowIndex={index}
-                            //     columnKeys={this.columnKeys}
-                            //     columnWidths={this.displayColumnWidths}
-                            //     rowMenuWidth={CollectionSchemaView._rowMenuWidth}
-                            //     selectedDocs={this._selectedDocs}
-                            //     selectRow={this.selectRow}
-                            //     startDrag={this.startDrag}
-                            //     dragging={this._isDragging}
-                            //     dropIndex={this.setDropIndex}
-                            //     addRowRef={this.addRowRef}
-                            // />
                         })}
                     </div>
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index a6a5f66ab..8e6d3d78a 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -40,7 +40,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
             case ColumnType.Boolean:
                 return (
                     <>
-                        <input type="checkbox" name="" id="" value={this._newFieldDefault ?? false} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />
+                        <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} />
                         {this._newFieldDefault ? 'true' : 'false'}
                     </>
                 );
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index f790e9dbf..2cf0a1b79 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,20 +1,17 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, ObservableMap, ObservableSet } from 'mobx';
+import { action, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, StrListCast } from '../../../../fields/Doc';
+import { Doc } from '../../../../fields/Doc';
+import { DragManager } from '../../../util/DragManager';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
-import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { OpenWhere } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
-import { DragManager } from '../../../util/DragManager';
-import { OpenWhere } from '../../nodes/DocumentView';
-import { Cast } from '../../../../fields/Types';
-import { listSpec } from '../../../../fields/Schema';
-import { CollectionSchemaView } from './CollectionSchemaView';
+import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
@@ -30,61 +27,37 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(SchemaRowBox, fieldKey);
-    }
-
+export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
     private _ref: HTMLDivElement | null = null;
 
     bounds = () => this._ref?.getBoundingClientRect();
 
-    @computed get columnKeys() {
-        return StrListCast(this.props.ContainingCollectionDoc?.columnKeys);
-    }
-
-    @computed get storedColumnWidths() {
-        let widths = Cast(
-            this.props.ContainingCollectionDoc?.columnWidths,
-            listSpec('number'),
-            this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
-        );
-
-        const totalWidth = widths.reduce((sum, width) => sum + width, 0);
-        if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) {
-            widths = widths.map(w => {
-                const proportion = w / totalWidth;
-                return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
-            });
-        }
-
-        return widths;
-    }
+    isSelected = () => this.props.selectedDocs.has(this.props.Document);
 
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
 
-        // setupMoveUpEvents(
-        //     this,
-        //     e,
-        //     e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-        //     emptyFunction,
-        //     e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-        // );
+        setupMoveUpEvents(
+            this,
+            e,
+            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
+            emptyFunction,
+            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
+        );
     };
 
     onPointerEnter = (e: any) => {
-        // if (!this.props.dragging) return;
+        if (!this.props.dragging) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
     };
 
     onPointerMove = (e: any) => {
-        // if (!this.props.dragging) return;
+        if (!this.props.dragging) return;
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
-            // dragIsRow = this.props.selectedDocs.has(doc);
+            dragIsRow = this.props.selectedDocs.has(doc);
         });
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
@@ -94,11 +67,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (y <= halfLine) {
                 this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
                 this._ref.style.borderBottom = '0px';
-                // this.props.dropIndex(this.props.rowIndex);
+                this.props.dropIndex(this.props.rowIndex);
             } else if (y > halfLine) {
                 this._ref.style.borderTop = '0px';
                 this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                // this.props.dropIndex(this.props.rowIndex + 1);
+                this.props.dropIndex(this.props.rowIndex + 1);
             }
         }
     };
@@ -115,22 +88,19 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}}
-                // style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
-                // onPointerDown={this.onRowPointerDown}
-                // onPointerEnter={this.onPointerEnter}
+                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
+                onPointerDown={this.onRowPointerDown}
+                onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    // row && this.props.addRowRef(this.props.Document, row);
+                    row && this.props.addRowRef(this.props.Document, row);
                     this._ref = row;
                 }}>
                 <div
                     className="row-menu"
-                    style={
-                        {
-                            /* width: this.props.rowMenuWidth */
-                        }
-                    }>
+                    style={{
+                        width: this.props.rowMenuWidth,
+                    }}>
                     <div
                         className="schema-row-button"
                         onPointerDown={undoBatch(e => {
@@ -149,8 +119,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.columnKeys.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.storedColumnWidths[index]} />
+                    {this.props.columnKeys.map((key, index) => (
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 459554ebe..569579996 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,7 +44,6 @@ import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
 import { LoadingBox } from './LoadingBox';
-import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -269,7 +268,6 @@ export class DocumentContentsView extends React.Component<
                     HTMLtag,
                     ComparisonBox,
                     LoadingBox,
-                    SchemaRowBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From 222f659b8c291fafce2648e367392dd9f467cb25 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 27 Feb 2023 17:00:49 -0500
Subject: rows are documentviews but clipped

---
 package-lock.json                                  |  39 +++++++
 .../collectionSchema/CollectionSchemaView.scss     |   6 ++
 .../collectionSchema/CollectionSchemaView.tsx      |  96 ++++++++++++-----
 .../collections/collectionSchema/SchemaRowBox.tsx  | 120 ++++++++++++---------
 .../collectionSchema/SchemaTableCell.tsx           |  12 ++-
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +
 6 files changed, 195 insertions(+), 80 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 3c2dccbf8..8806887df 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5308,6 +5308,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6475,6 +6485,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6486,6 +6518,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -21371,6 +21404,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index bd0fc11b3..46c2e2d1a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -134,10 +134,16 @@
     }
 }
 
+.schema-row-wrapper {
+    max-height: 70px;
+    overflow: hidden;
+}
+
 .schema-header-row,
 .schema-row {
     display: flex;
     flex-direction: row;
+    height: 100%;
     max-height: 70px;
     overflow: auto;
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 64c39cf1a..46510b6fe 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -8,7 +8,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -17,7 +17,7 @@ import { undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { EditableView } from '../../EditableView';
-import { DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
@@ -41,9 +41,9 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _lastSelectedRow: number | undefined;
     private _selectedDocSortedArray: Doc[] = [];
     private _closestDropIndex: number = 0;
-    private _minColWidth: number = 150;
     private _previewRef: HTMLDivElement | null = null;
 
+    public static _minColWidth: number = 150;
     public static _rowMenuWidth: number = 100;
     public static _previewDividerWidth: number = 4;
     @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
@@ -110,6 +110,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     componentDidMount() {
+        this.props.setContentView?.(this);
         document.addEventListener('keydown', this.onKeyDown);
     }
 
@@ -243,8 +244,8 @@ export class CollectionSchemaView extends CollectionSubView() {
             if (shrinking === undefined || growing === undefined) return true;
 
             change = Math.abs(change);
-            if (this._displayColumnWidths[shrinking] - change < this._minColWidth) {
-                change = this._displayColumnWidths[shrinking] - this._minColWidth;
+            if (this._displayColumnWidths[shrinking] - change < CollectionSchemaView._minColWidth) {
+                change = this._displayColumnWidths[shrinking] - CollectionSchemaView._minColWidth;
             }
 
             this._displayColumnWidths[shrinking] -= change;
@@ -481,7 +482,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument('', { title: name, _autoHeight: true });
             if (created) {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
@@ -492,9 +493,25 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
+    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+        Doc.BrushDoc(doc);
+        let focusSpeed = 0;
+        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
+        if (found) {
+            const top = found.getBoundingClientRect().top;
+            const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
+            if (Math.floor(localTop[1]) !== 0) {
+                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
+            }
+        }
+        const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
+        this.props.focus(this.rootDoc, {
+            ...options,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
+        });
+    };
     isChildContentActive = () =>
         this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
-
     getDocTransform(doc: Doc, dref?: DocumentView) {
         const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
         // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
@@ -543,26 +560,55 @@ export class CollectionSchemaView extends CollectionSubView() {
                         {this.childDocs.map((doc: Doc, index: number) => {
                             const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
                             let dref: Opt<DocumentView>;
-
                             return (
-                                <SchemaRowBox
-                                    {...this.props}
-                                    key={index}
-                                    Document={doc}
-                                    ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                                    ContainingCollectionView={this.props.CollectionView}
-                                    rowIndex={index}
-                                    columnKeys={this.columnKeys}
-                                    columnWidths={this.displayColumnWidths}
-                                    rowMenuWidth={CollectionSchemaView._rowMenuWidth}
-                                    selectedDocs={this._selectedDocs}
-                                    selectRow={this.selectRow}
-                                    startDrag={this.startDrag}
-                                    dragging={this._isDragging}
-                                    dropIndex={this.setDropIndex}
-                                    addRowRef={this.addRowRef}
-                                />
+                                <div className="schema-row-wrapper">
+                                    <DocumentView
+                                        {...this.props}
+                                        ref={r => (dref = r || undefined)}
+                                        LayoutTemplate={this.props.childLayoutTemplate}
+                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
+                                        renderDepth={this.props.renderDepth + 1}
+                                        Document={doc}
+                                        DataDoc={dataDoc}
+                                        ContainingCollectionView={this.props.CollectionView}
+                                        ContainingCollectionDoc={this.Document}
+                                        PanelWidth={() => this.tableWidth}
+                                        PanelHeight={() => 70}
+                                        styleProvider={DefaultStyleProvider}
+                                        focus={this.focusDocument}
+                                        docFilters={this.childDocFilters}
+                                        docViewPath={this.props.docViewPath}
+                                        docRangeFilters={this.childDocRangeFilters}
+                                        searchFilterDocs={this.searchFilterDocs}
+                                        rootSelected={this.rootSelected}
+                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
+                                        bringToFront={emptyFunction}
+                                        isContentActive={this.isChildContentActive}
+                                        hideDecorations={true}
+                                        hideTitle={true}
+                                        hideDocumentButtonBar={true}
+                                    />
+                                </div>
                             );
+                            // return (
+                            //     <SchemaRowBox
+                            //         {...this.props}
+                            //         key={index}
+                            //         Document={doc}
+                            //         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                            //         ContainingCollectionView={this.props.CollectionView}
+                            //         rowIndex={index}
+                            //         columnKeys={this.columnKeys}
+                            //         columnWidths={this.displayColumnWidths}
+                            //         rowMenuWidth={CollectionSchemaView._rowMenuWidth}
+                            //         selectedDocs={this._selectedDocs}
+                            //         selectRow={this.selectRow}
+                            //         startDrag={this.startDrag}
+                            //         dragging={this._isDragging}
+                            //         dropIndex={this.setDropIndex}
+                            //         addRowRef={this.addRowRef}
+                            //     />
+                            // );
                         })}
                     </div>
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 2cf0a1b79..0378fa67e 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,17 +1,17 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, ObservableSet } from 'mobx';
+import { computed, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../../../fields/Doc';
-import { DragManager } from '../../../util/DragManager';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
-import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
+import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
+import { Colors } from '../../global/globalEnums';
+import { DocCast, StrCast } from '../../../../fields/Types';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
@@ -27,79 +27,93 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(SchemaRowBox, fieldKey);
+    }
+
     private _ref: HTMLDivElement | null = null;
 
     bounds = () => this._ref?.getBoundingClientRect();
 
-    isSelected = () => this.props.selectedDocs.has(this.props.Document);
+    @computed get schemaView() {
+        const vpath = this.props.docViewPath();
+        console.log(vpath[vpath.length - 2]);
+        return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined;
+    }
 
-    @action
-    onRowPointerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
+    @computed get schemaDoc() {
+        return this.props.ContainingCollectionDoc!;
+    }
 
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-            emptyFunction,
-            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-        );
-    };
+    // isSelected = () => this.props.selectedDocs.has(this.props.Document);
 
-    onPointerEnter = (e: any) => {
-        if (!this.props.dragging) return;
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.addEventListener('pointermove', this.onPointerMove);
-    };
+    // @action
+    // onRowPointerDown = (e: React.PointerEvent) => {
+    //     e.stopPropagation();
 
-    onPointerMove = (e: any) => {
-        if (!this.props.dragging) return;
-        let dragIsRow: boolean = true;
-        DragManager.docsBeingDragged.forEach(doc => {
-            dragIsRow = this.props.selectedDocs.has(doc);
-        });
-        if (this._ref && dragIsRow) {
-            const rect = this._ref.getBoundingClientRect();
-            const y = e.clientY - rect.top; //y position within the element.
-            const height = this._ref.clientHeight;
-            const halfLine = height / 2;
-            if (y <= halfLine) {
-                this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                this._ref.style.borderBottom = '0px';
-                this.props.dropIndex(this.props.rowIndex);
-            } else if (y > halfLine) {
-                this._ref.style.borderTop = '0px';
-                this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                this.props.dropIndex(this.props.rowIndex + 1);
-            }
-        }
-    };
+    //     setupMoveUpEvents(
+    //         this,
+    //         e,
+    //         e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
+    //         emptyFunction,
+    //         e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
+    //     );
+    // };
+
+    // onPointerEnter = (e: any) => {
+    //     if (!this.props.dragging) return;
+    //     document.removeEventListener('pointermove', this.onPointerMove);
+    //     document.addEventListener('pointermove', this.onPointerMove);
+    // };
+
+    // onPointerMove = (e: any) => {
+    //     if (!this.props.dragging) return;
+    //     let dragIsRow: boolean = true;
+    //     DragManager.docsBeingDragged.forEach(doc => {
+    //         dragIsRow = this.props.selectedDocs.has(doc);
+    //     });
+    //     if (this._ref && dragIsRow) {
+    //         const rect = this._ref.getBoundingClientRect();
+    //         const y = e.clientY - rect.top; //y position within the element.
+    //         const height = this._ref.clientHeight;
+    //         const halfLine = height / 2;
+    //         if (y <= halfLine) {
+    //             this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
+    //             this._ref.style.borderBottom = '0px';
+    //             this.props.dropIndex(this.props.rowIndex);
+    //         } else if (y > halfLine) {
+    //             this._ref.style.borderTop = '0px';
+    //             this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+    //             this.props.dropIndex(this.props.rowIndex + 1);
+    //         }
+    //     }
+    // };
 
     onPointerLeave = (e: any) => {
         if (this._ref) {
             this._ref.style.borderTop = '0px';
             this._ref.style.borderBottom = '0px';
         }
-        document.removeEventListener('pointermove', this.onPointerMove);
+        // document.removeEventListener('pointermove', this.onPointerMove);
     };
 
     render() {
         return (
             <div
                 className="schema-row"
-                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
-                onPointerDown={this.onRowPointerDown}
-                onPointerEnter={this.onPointerEnter}
+                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE /*, opacity: this.props.dragging ? 0.5 : 1 */ } : {}}
+                // onPointerDown={this.onRowPointerDown}
+                // onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    row && this.props.addRowRef(this.props.Document, row);
+                    // row && this.props.addRowRef(this.props.Document, row);
                     this._ref = row;
                 }}>
                 <div
                     className="row-menu"
                     style={{
-                        width: this.props.rowMenuWidth,
+                        width: CollectionSchemaView._rowMenuWidth,
                     }}>
                     <div
                         className="schema-row-button"
@@ -119,8 +133,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.props.columnKeys.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                    {this.schemaView?.columnKeys?.map((key, index) => (
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 5d9474173..e2f6d99f1 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,7 +1,14 @@
 import React = require('react');
 import { observer } from 'mobx-react';
-import { Doc, Field } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field } from '../../../../fields/Doc';
 import './CollectionSchemaView.scss';
+import { type } from 'jquery';
+import { action } from 'mobx';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { FieldValue } from '../../../../fields/Types';
+import { CompileScript } from '../../../util/Scripting';
+import { EditableView } from '../../EditableView';
+import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
 
 export interface SchemaTableCellProps {
     Document: Doc;
@@ -14,7 +21,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     render() {
         return (
             <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
-                {Field.toString(this.props.Document[this.props.fieldKey] as Field)}
+                {/* {Field.toString(this.props.Document[this.props.fieldKey] as Field)} */}
+                <EditableView contents={Field.toString(this.props.Document[this.props.fieldKey] as Field)} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string) => true} />
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 569579996..459554ebe 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,6 +44,7 @@ import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
 import { LoadingBox } from './LoadingBox';
+import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -268,6 +269,7 @@ export class DocumentContentsView extends React.Component<
                     HTMLtag,
                     ComparisonBox,
                     LoadingBox,
+                    SchemaRowBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From c3678d61a6957598d901333b4eeef6fa01407dd5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Mar 2023 18:07:12 -0500
Subject: switched filters from being a document to just being a UI

---
 src/client/documents/Documents.ts                  |  10 +-
 src/client/views/FilterPanel.scss                  | 189 +++++++
 src/client/views/FilterPanel.tsx                   | 232 ++++++++
 src/client/views/PropertiesView.tsx                |  72 +--
 src/client/views/nodes/DocumentContentsView.tsx    |   2 -
 src/client/views/nodes/FilterBox.scss              |  12 +-
 src/client/views/nodes/FilterBox.tsx               | 588 ---------------------
 .../views/nodes/formattedText/DashFieldView.tsx    |  21 +-
 src/fields/Doc.ts                                  |   6 +-
 9 files changed, 448 insertions(+), 684 deletions(-)
 create mode 100644 src/client/views/FilterPanel.scss
 create mode 100644 src/client/views/FilterPanel.tsx

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 98469a2f9..a7378c431 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -39,7 +39,6 @@ import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
 import { DocFocusOptions, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
 import { EquationBox } from '../views/nodes/EquationBox';
 import { FieldViewProps } from '../views/nodes/FieldView';
-import { FilterBox } from '../views/nodes/FilterBox';
 import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
 import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox';
 import { ImageBox } from '../views/nodes/ImageBox';
@@ -411,13 +410,6 @@ export namespace Docs {
                     options: { _width: 400, links: '@links(self)' },
                 },
             ],
-            [
-                DocumentType.FILTER,
-                {
-                    layout: { view: FilterBox, dataField: defaultDataKey },
-                    options: { _width: 400, links: '@links(self)' },
-                },
-            ],
             [
                 DocumentType.COLOR,
                 {
@@ -1260,7 +1252,7 @@ export namespace DocUtils {
                                 return Field.toString(d[facetKey] as Field).includes(value);
                             });
                       // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
-                      if ((parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR') {
+                      if (parentCollection?.filterBoolean === 'OR') {
                           if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
                       }
                       // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss
new file mode 100644
index 000000000..7f907c8d4
--- /dev/null
+++ b/src/client/views/FilterPanel.scss
@@ -0,0 +1,189 @@
+.filterBox-flyout {
+    display: block;
+    text-align: left;
+    font-weight: 100;
+
+    .filterBox-flyout-facet {
+        background-color: white;
+        text-align: left;
+        display: inline-block;
+        position: relative;
+        width: 100%;
+
+        .filterBox-flyout-facet-check {
+            margin-right: 6px;
+        }
+    }
+}
+
+.filter-bookmark {
+    //display: flex;
+
+    .filter-bookmark-icon {
+        float: right;
+        margin-right: 10px;
+        margin-top: 7px;
+    }
+}
+
+// .filterBox-bottom {
+//     // position: fixed;
+//     // bottom: 0;
+//     // width: 100%;
+// }
+
+.filterBox-select {
+    // width: 90%;
+    margin-top: 5px;
+    // margin-bottom: 15px;
+}
+
+.filterBox-saveBookmark {
+    background-color: #e9e9e9;
+    border-radius: 11px;
+    padding-left: 8px;
+    padding-right: 8px;
+    padding-top: 5px;
+    padding-bottom: 5px;
+    margin: 8px;
+    display: flex;
+    font-size: 11px;
+    cursor: pointer;
+
+    &:hover {
+        background-color: white;
+    }
+
+    .filterBox-saveBookmark-icon {
+        margin-right: 6px;
+        margin-top: 4px;
+        margin-left: 2px;
+    }
+}
+
+.filterBox-select-scope,
+.filterBox-select-bool,
+.filterBox-addWrapper,
+.filterBox-select-matched,
+.filterBox-saveWrapper {
+    font-size: 10px;
+    justify-content: center;
+    justify-items: center;
+    padding-bottom: 10px;
+    display: flex;
+}
+
+.filterBox-addWrapper {
+    font-size: 11px;
+    width: 100%;
+}
+
+.filterBox-saveWrapper {
+    width: 100%;
+}
+
+// .filterBox-top {
+//     padding-bottom: 20px;
+//     border-bottom: 2px solid black;
+//     position: fixed;
+//     top: 0;
+//     width: 100%;
+// }
+
+.filterBox-select-scope {
+    padding-bottom: 20px;
+    border-bottom: 2px solid black;
+}
+
+.filterBox-title {
+    font-size: 15;
+    // border: 2px solid black;
+    width: 100%;
+    align-self: center;
+    text-align: center;
+    background-color: #d3d3d3;
+}
+
+.filterBox-select-bool {
+    margin-top: 6px;
+}
+
+.filterBox-select-text {
+    margin-right: 8px;
+    margin-left: 8px;
+    margin-top: 3px;
+}
+
+.filterBox-select-box {
+    margin-right: 2px;
+    font-size: 30px;
+    border: 0;
+    background: transparent;
+}
+
+.filterBox-selection {
+    border-radius: 6px;
+    border: none;
+    background-color: #e9e9e9;
+    padding: 2px;
+
+    &:hover {
+        background-color: white;
+    }
+}
+
+.filterBox-addFilter {
+    width: 120px;
+    background-color: #e9e9e9;
+    border-radius: 12px;
+    padding: 5px;
+    margin: 5px;
+    display: flex;
+    text-align: center;
+    justify-content: center;
+
+    &:hover {
+        background-color: white;
+    }
+}
+
+.filterBox-treeView {
+    display: flex;
+    flex-direction: column;
+    width: 200px;
+    position: absolute;
+    right: 0;
+    top: 0;
+    z-index: 1;
+    background-color: #9f9f9f;
+
+    .filterBox-tree {
+        z-index: 0;
+    }
+
+    .filterBox-addfacet {
+        display: inline-block;
+        width: 200px;
+        height: 30px;
+        text-align: left;
+
+        .filterBox-addFacetButton {
+            display: flex;
+            margin: auto;
+            cursor: pointer;
+        }
+
+        > div,
+        > div > div {
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    .filterBox-tree {
+        display: inline-block;
+        width: 100%;
+        margin-bottom: 10px;
+        //height: calc(100% - 30px);
+    }
+}
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
new file mode 100644
index 000000000..d35494f26
--- /dev/null
+++ b/src/client/views/FilterPanel.tsx
@@ -0,0 +1,232 @@
+import React = require('react');
+import { action, computed, observable, ObservableMap } from 'mobx';
+import { observer } from 'mobx-react';
+import Select from 'react-select';
+import { Doc, DocListCast, Field, StrListCast } from '../../fields/Doc';
+import { RichTextField } from '../../fields/RichTextField';
+import { StrCast } from '../../fields/Types';
+import { DocumentManager } from '../util/DocumentManager';
+import { UserOptions } from '../util/GroupManager';
+import './FilterPanel.scss';
+import { FieldView } from './nodes/FieldView';
+import { SearchBox } from './search/SearchBox';
+
+interface filterProps {
+    rootDoc: Doc;
+}
+@observer
+export class FilterPanel extends React.Component<filterProps> {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(FilterPanel, fieldKey);
+    }
+
+    /**
+     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
+     */
+    @computed get targetDoc() {
+        return this.props.rootDoc;
+    }
+    @computed get targetDocChildKey() {
+        const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc);
+        return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data';
+    }
+    @computed get targetDocChildren() {
+        return DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data);
+    }
+
+    @computed get allDocs() {
+        const allDocs = new Set<Doc>();
+        const targetDoc = this.targetDoc;
+        if (targetDoc) {
+            SearchBox.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc));
+        }
+        return Array.from(allDocs);
+    }
+
+    @computed get _allFacets() {
+        // trace();
+        const noviceReqFields = ['author', 'tags', 'text', 'type'];
+        const noviceLayoutFields: string[] = []; //["_curPage"];
+        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
+
+        const keys = new Set<string>(noviceFields);
+        this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
+        return Array.from(keys.keys())
+            .filter(key => key[0])
+            .filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
+            .sort();
+    }
+
+    /**
+     * The current attributes selected to filter based on
+     */
+    @computed get activeFilters() {
+        return StrListCast(this.targetDoc?._docFilters);
+    }
+
+    /**
+     * @returns a string array of the current attributes
+     */
+    @computed get currentFacets() {
+        return this.activeFilters.map(filter => filter.split(':')[0]);
+    }
+
+    gatherFieldValues(childDocs: Doc[], facetKey: string) {
+        const valueSet = new Set<string>();
+        let rtFields = 0;
+        let subDocs = childDocs;
+        if (subDocs.length > 0) {
+            let newarray: Doc[] = [];
+            while (subDocs.length > 0) {
+                newarray = [];
+                subDocs.forEach(t => {
+                    const facetVal = t[facetKey];
+                    if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
+                    facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
+                    (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal));
+                    const fieldKey = Doc.LayoutFieldKey(t);
+                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
+                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                });
+                subDocs = newarray;
+            }
+        }
+        //     }
+        // });
+        return { strings: Array.from(valueSet.keys()), rtFields };
+    }
+
+    public removeFilter = (filterName: string) => {
+        Doc.setDocFilter(this.targetDoc, filterName, undefined, 'remove');
+        Doc.setDocRangeFilter(this.targetDoc, filterName, undefined);
+    };
+
+    @observable _chosenFacets = new ObservableMap<string, 'text' | 'checkbox' | 'slider' | 'range'>();
+    @computed get activeFacets() {
+        const facets = new Map<string, 'text' | 'checkbox' | 'slider' | 'range'>(this._chosenFacets);
+        StrListCast(this.targetDoc?._docFilters).map(filter => facets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox'));
+        setTimeout(() => StrListCast(this.targetDoc?._docFilters).map(action(filter => this._chosenFacets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox'))));
+        return facets;
+    }
+    /**
+     * Responds to clicking the check box in the flyout menu
+     */
+    @action
+    facetClick = (facetHeader: string) => {
+        if (!this.targetDoc) return;
+        const allCollectionDocs = this.targetDocChildren;
+        const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader);
+
+        let nonNumbers = 0;
+        let minVal = Number.MAX_VALUE,
+            maxVal = -Number.MAX_VALUE;
+        facetValues.strings.map(val => {
+            const num = val ? Number(val) : Number.NaN;
+            if (Number.isNaN(num)) {
+                val && nonNumbers++;
+            } else {
+                minVal = Math.min(num, minVal);
+                maxVal = Math.max(num, maxVal);
+            }
+        });
+        if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.rtFields > 20)) {
+            this._chosenFacets.set(facetHeader, 'text');
+        } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
+        } else {
+            this._chosenFacets.set(facetHeader, 'checkbox');
+        }
+    };
+
+    facetValues = (facetHeader: string) => {
+        const allCollectionDocs = new Set<Doc>();
+        SearchBox.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
+        const set = new Set<string>();
+        if (facetHeader === 'tags')
+            allCollectionDocs.forEach(child =>
+                Field.toString(child[facetHeader] as Field)
+                    .split(':')
+                    .forEach(key => set.add(key))
+            );
+        else
+            allCollectionDocs.forEach(child => {
+                const fieldVal = child[facetHeader] as Field;
+                set.add(Field.toString(fieldVal));
+                (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
+            });
+        const facetValues = Array.from(set).filter(v => v);
+
+        let nonNumbers = 0;
+
+        facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
+        const facetValueDocSet = (nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+            return facetValue;
+        });
+        return facetValueDocSet;
+    };
+
+    render() {
+        const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
+        return (
+            <div className="filterBox-treeView" style={{ position: 'relative', width: '100%' }}>
+                <div className="filterBox-select-bool">
+                    <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._filterBoolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.filterBoolean)}>
+                        {['AND', 'OR'].map(bool => (
+                            <option value={bool} key={bool}>
+                                {bool}
+                            </option>
+                        ))}
+                    </select>
+                    <div className="filterBox-select-text">filters together</div>
+                </div>
+
+                <div className="filterBox-select">
+                    <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
+                </div>
+
+                <div className="filterBox-tree" key="tree" style={{ overflow: 'auto' }}>
+                    {Array.from(this.activeFacets.keys()).map(facetHeader => (
+                        <div>
+                            {facetHeader}
+                            {this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader)}
+                        </div>
+                    ))}
+                </div>
+            </div>
+        );
+    }
+
+    private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string): React.ReactNode {
+        switch (type) {
+            case 'text':
+                return (
+                    <input
+                        placeholder={
+                            StrListCast(this.targetDoc._docFilters)
+                                .find(filter => filter.split(':')[0] === facetHeader)
+                                ?.split(':')[1] ?? '-empty-'
+                        }
+                        onKeyDown={e => e.key === 'Enter' && Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match')}
+                    />
+                );
+            case 'checkbox':
+                return this.facetValues(facetHeader).map(fval => {
+                    const facetValue = fval;
+                    return (
+                        <div>
+                            <input
+                                style={{ width: 20, marginLeft: 20 }}
+                                checked={
+                                    StrListCast(this.targetDoc._docFilters)
+                                        .find(filter => filter.split(':')[0] === facetHeader && filter.split(':')[1] == facetValue)
+                                        ?.split(':')[2] === 'check'
+                                }
+                                type={type}
+                                onChange={e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove')}
+                            />
+                            {facetValue}
+                        </div>
+                    );
+                });
+        }
+    }
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 1ec2b76d6..4a63930bd 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -26,7 +26,7 @@ import { EditableView } from './EditableView';
 import { Colors } from './global/globalEnums';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
-import { FilterBox } from './nodes/FilterBox';
+import { FilterPanel } from './FilterPanel';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 import { PropertiesButtons } from './PropertiesButtons';
@@ -76,13 +76,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @observable openOptions: boolean = true;
     @observable openSharing: boolean = true;
-    @observable openFields: boolean = true;
+    @observable openFields: boolean = false;
     @observable openLayout: boolean = false;
     @observable openContexts: boolean = true;
     @observable openLinks: boolean = true;
     @observable openAppearance: boolean = true;
     @observable openTransform: boolean = true;
-    @observable openFilters: boolean = true; // should be false
+    @observable openFilters: boolean = false;
 
     /**
      * autorun to set up the filter doc of a collection if that collection has been selected and the filters panel is open
@@ -104,7 +104,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     componentDidMount() {
         this.selectedDocListenerDisposer?.();
-        this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc());
+        // this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc());
     }
 
     componentWillUnmount() {
@@ -1135,37 +1135,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
-    /**
-     * Checks if a currentFilter (FilterDoc) exists on the current collection (if the Properties Panel + Filters submenu are open).
-     * If it doesn't exist, it creates it.
-     */
-    checkFilterDoc() {
-        if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = FilterBox.createFilterDoc();
-    }
-
-    /**
-     * Creates a new currentFilter for this.filterDoc,
-     */
-    createNewFilterDoc = () => {
-        if (this.selectedDoc) {
-            const curFilterDoc = DocCast(this.selectedDoc.currentFilter);
-            const currentDocFilters = this.selectedDoc._docFilters;
-            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-            this.selectedDoc._docFilters = new List<string>();
-            this.selectedDoc._docRangeFilters = new List<string>();
-            if (DocListCast(Doc.UserDoc().savedFilters).includes(curFilterDoc)) {
-                curFilterDoc._docFiltersList = currentDocFilters;
-                curFilterDoc._docRangeFiltersList = currentDocRangeFilters;
-                this.selectedDoc.currentFilter = FilterBox.createFilterDoc();
-            } else {
-                Doc.GetProto(curFilterDoc).data = undefined;
-                Doc.GetProto(curFilterDoc).title = 'Unnamed Filter';
-                curFilterDoc._docFiltersList = undefined;
-                curFilterDoc._docRangeFiltersList = undefined;
-            }
-        }
-    };
-
     /**
      * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
      */
@@ -1191,7 +1160,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     @computed get filtersSubMenu() {
-        return !(this.selectedDoc?.currentFilter instanceof Doc) ? null : (
+        return (
             <div className="propertiesView-filters">
                 <div className="propertiesView-filters-title" onPointerDown={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
                     Filters
@@ -1200,35 +1169,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     </div>
                 </div>
                 {!this.openFilters ? null : (
-                    <div className="propertiesView-filters-content" style={{ height: this.selectedDoc.currentFilter[HeightSym]() + 15 }}>
-                        <DocumentView
-                            Document={this.selectedDoc.currentFilter}
-                            DataDoc={undefined}
-                            addDocument={undefined}
-                            addDocTab={returnFalse}
-                            pinToPres={emptyFunction}
-                            rootSelected={returnTrue}
-                            removeDocument={returnFalse}
-                            ScreenToLocalTransform={this.getTransform}
-                            PanelWidth={() => this.props.width}
-                            PanelHeight={this.selectedDoc.currentFilter[HeightSym]}
-                            renderDepth={0}
-                            scriptContext={this.selectedDoc.currentFilter}
-                            focus={emptyFunction}
-                            styleProvider={DefaultStyleProvider}
-                            isContentActive={returnTrue}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            bringToFront={emptyFunction}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
-                            createNewFilterDoc={this.createNewFilterDoc}
-                            updateFilterDoc={this.updateFilterDoc}
-                            docViewPath={returnEmptyDoclist}
-                            dontCenter="y"
-                        />
+                    <div className="propertiesView-filters-content" style={{ position: 'relative', height: 'auto' }}>
+                        <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
                     </div>
                 )}
             </div>
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 569579996..2d1839dd8 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,7 +24,6 @@ import { DocumentViewProps } from './DocumentView';
 import './DocumentView.scss';
 import { EquationBox } from './EquationBox';
 import { FieldView, FieldViewProps } from './FieldView';
-import { FilterBox } from './FilterBox';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { FunctionPlotBox } from './FunctionPlotBox';
 import { ImageBox } from './ImageBox';
@@ -254,7 +253,6 @@ export class DocumentContentsView extends React.Component<
                     YoutubeBox,
                     PresElementBox,
                     SearchBox,
-                    FilterBox,
                     FunctionPlotBox,
                     ColorBox,
                     DashWebRTCVideo,
diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss
index 107ad2e36..7f907c8d4 100644
--- a/src/client/views/nodes/FilterBox.scss
+++ b/src/client/views/nodes/FilterBox.scss
@@ -16,7 +16,6 @@
     }
 }
 
-
 .filter-bookmark {
     //display: flex;
 
@@ -39,7 +38,6 @@
     // margin-bottom: 15px;
 }
 
-
 .filterBox-saveBookmark {
     background-color: #e9e9e9;
     border-radius: 11px;
@@ -61,7 +59,6 @@
         margin-top: 4px;
         margin-left: 2px;
     }
-
 }
 
 .filterBox-select-scope,
@@ -154,12 +151,11 @@
     display: flex;
     flex-direction: column;
     width: 200px;
-    height: 100%;
     position: absolute;
     right: 0;
     top: 0;
     z-index: 1;
-    background-color: #9F9F9F;
+    background-color: #9f9f9f;
 
     .filterBox-tree {
         z-index: 0;
@@ -177,8 +173,8 @@
             cursor: pointer;
         }
 
-        >div,
-        >div>div {
+        > div,
+        > div > div {
             width: 100%;
             height: 100%;
         }
@@ -190,4 +186,4 @@
         margin-bottom: 10px;
         //height: calc(100% - 30px);
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index d41a4bb82..e69de29bb 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -1,588 +0,0 @@
-import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import Select from 'react-select';
-import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from '../../../fields/Doc';
-import { List } from '../../../fields/List';
-import { RichTextField } from '../../../fields/RichTextField';
-import { listSpec } from '../../../fields/Schema';
-import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
-import { Docs, DocumentOptions } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { UserOptions } from '../../util/GroupManager';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from '../../util/SelectionManager';
-import { CollectionTreeView } from '../collections/CollectionTreeView';
-import { CollectionView } from '../collections/CollectionView';
-import { ViewBoxBaseComponent } from '../DocComponent';
-import { EditableView } from '../EditableView';
-import { SearchBox } from '../search/SearchBox';
-import { DashboardToggleButton, DefaultStyleProvider, StyleProp } from '../StyleProvider';
-import { DocumentViewProps } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
-import './FilterBox.scss';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-@observer
-export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    constructor(props: Readonly<FieldViewProps>) {
-        super(props);
-        const targetDoc = FilterBox.targetDoc;
-        if (targetDoc && !targetDoc.currentFilter) runInAction(() => (targetDoc.currentFilter = FilterBox.createFilterDoc()));
-    }
-
-    /// creates a new, empty filter doc
-    static createFilterDoc() {
-        const clearAll = `getProto(self).data = new List([])`;
-        const reqdOpts: DocumentOptions = {
-            _lockedPosition: true,
-            _autoHeight: true,
-            _fitWidth: true,
-            _height: 150,
-            _xPadding: 5,
-            _yPadding: 5,
-            _gridGap: 5,
-            _forceActive: true,
-            title: 'Unnamed Filter',
-            filterBoolean: 'AND',
-            boxShadow: '0 0',
-            childDontRegisterViews: true,
-            targetDropAction: 'same',
-            ignoreClick: true,
-            system: true,
-            childDropAction: 'none',
-            treeViewHideTitle: true,
-            treeViewTruncateTitleWidth: 150,
-            childContextMenuLabels: new List<string>(['Clear All']),
-            childContextMenuScripts: new List<ScriptField>([ScriptField.MakeFunction(clearAll)!]),
-        };
-        return Docs.Create.FilterDocument(reqdOpts);
-    }
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(FilterBox, fieldKey);
-    }
-
-    public _filterSelected = false;
-    public _filterMatch = 'matched';
-
-    @computed static get currentContainingCollectionDoc() {
-        let docView: any = SelectionManager.Views()[0];
-        let doc = docView.Document;
-
-        while (docView && doc && doc.type !== 'collection') {
-            doc = docView.props.ContainingCollectionDoc;
-            docView = docView.props.ContainingCollectionView;
-        }
-
-        return doc;
-    }
-
-    // /**
-    //  * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
-    //  */
-
-    // trying to get this to work rather than the version below this
-
-    // @computed static get targetDoc() {
-    //     console.log(FilterBox.currentContainingCollectionDoc.type);
-    //     if (FilterBox._filterScope === "Current Collection") {
-    //         return FilterBox.currentContainingCollectionDoc;
-    //     }
-    //     else return CollectionDockingView.Instance.props.Document;
-    //     // return FilterBox._filterScope === "Current Collection" ? SelectionManager.Views()[0].Document || CollectionDockingView.Instance.props.Document : CollectionDockingView.Instance.props.Document;
-    // }
-
-    /**
-     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
-     */
-    @computed static get targetDoc() {
-        return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : Doc.ActiveDashboard;
-    }
-    @computed static get targetDocChildKey() {
-        if (SelectionManager.Views().length) {
-            return SelectionManager.Views()[0].ComponentView?.annotationKey || SelectionManager.Views()[0].ComponentView?.fieldKey || 'data';
-        }
-        return 'data';
-    }
-    @computed static get targetDocChildren() {
-        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || Doc.ActiveDashboard?.data);
-    }
-
-    @observable _loaded = false;
-    componentDidMount() {
-        reaction(
-            () => DocListCastAsync(this.layoutDoc[this.fieldKey]),
-            async activeTabsAsync => {
-                const activeTabs = await activeTabsAsync;
-                activeTabs && (await SearchBox.foreachRecursiveDocAsync(activeTabs, emptyFunction));
-                runInAction(() => (this._loaded = true));
-            },
-            { fireImmediately: true }
-        );
-    }
-
-    @computed get allDocs() {
-        // trace();
-        const allDocs = new Set<Doc>();
-        const targetDoc = FilterBox.targetDoc;
-        if (this._loaded && targetDoc) {
-            SearchBox.foreachRecursiveDoc(FilterBox.targetDocChildren, (depth, doc) => allDocs.add(doc));
-        }
-        return Array.from(allDocs);
-    }
-
-    @computed get _allFacets() {
-        // trace();
-        const noviceReqFields = ['author', 'tags', 'text', 'type'];
-        const noviceLayoutFields: string[] = []; //["_curPage"];
-        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
-
-        const keys = new Set<string>(noviceFields);
-        this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
-        return Array.from(keys.keys())
-            .filter(key => key[0])
-            .filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
-            .sort();
-    }
-
-    /**
-     * The current attributes selected to filter based on
-     */
-    @computed get activeAttributes() {
-        return DocListCast(this.dataDoc[this.props.fieldKey]);
-    }
-
-    /**
-     * @returns a string array of the current attributes
-     */
-    @computed get currentFacets() {
-        return this.activeAttributes.map(attribute => StrCast(attribute.title));
-    }
-
-    gatherFieldValues(childDocs: Doc[], facetKey: string) {
-        const valueSet = new Set<string>();
-        let rtFields = 0;
-        childDocs.forEach(d => {
-            const facetVal = d[facetKey];
-            if (facetVal instanceof RichTextField) rtFields++;
-            valueSet.add(Field.toString(facetVal as Field));
-            const fieldKey = Doc.LayoutFieldKey(d);
-            const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
-            const data = d[annos ? fieldKey + '-annotations' : fieldKey];
-            if (data !== undefined) {
-                let subDocs = DocListCast(data);
-                if (subDocs.length > 0) {
-                    let newarray: Doc[] = [];
-                    while (subDocs.length > 0) {
-                        newarray = [];
-                        subDocs.forEach(t => {
-                            const facetVal = t[facetKey];
-                            if (facetVal instanceof RichTextField) rtFields++;
-                            facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
-                            const fieldKey = Doc.LayoutFieldKey(t);
-                            const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
-                            DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
-                        });
-                        subDocs = newarray;
-                    }
-                }
-            }
-        });
-        return { strings: Array.from(valueSet.keys()), rtFields };
-    }
-    removeFilterDoc = (doc: Doc | Doc[]) => ((doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false);
-    public removeFilter = (filterName: string) => {
-        const targetDoc = FilterBox.targetDoc;
-        if (targetDoc) {
-            const filterDoc = targetDoc.currentFilter as Doc;
-            const attributes = DocListCast(filterDoc.data);
-            const found = attributes.findIndex(doc => doc.title === filterName);
-            if (found !== -1) {
-                (filterDoc.data as List<Doc>).splice(found, 1);
-                const docFilter = Cast(targetDoc._docFilters, listSpec('string'));
-                if (docFilter) {
-                    let index: number;
-                    while ((index = docFilter.findIndex(item => item.split(':')[0] === filterName)) !== -1) {
-                        docFilter.splice(index, 1);
-                    }
-                }
-                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec('string'));
-                if (docRangeFilters) {
-                    let index: number;
-                    while ((index = docRangeFilters.findIndex(item => item.split(':')[0] === filterName)) !== -1) {
-                        docRangeFilters.splice(index, 3);
-                    }
-                }
-            }
-        }
-        return true;
-    };
-    /**
-     * Responds to clicking the check box in the flyout menu
-     */
-    facetClick = (facetHeader: string) => {
-        const { targetDoc, targetDocChildren } = FilterBox;
-        if (!targetDoc) return;
-        const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader);
-        if (found !== -1) {
-            this.removeFilter(facetHeader);
-        } else {
-            const allCollectionDocs = targetDocChildren;
-            const facetValues = this.gatherFieldValues(targetDocChildren, facetHeader);
-
-            let nonNumbers = 0;
-            let minVal = Number.MAX_VALUE,
-                maxVal = -Number.MAX_VALUE;
-            facetValues.strings.map(val => {
-                const num = val ? Number(val) : Number.NaN;
-                if (Number.isNaN(num)) {
-                    val && nonNumbers++;
-                } else {
-                    minVal = Math.min(num, minVal);
-                    maxVal = Math.max(num, maxVal);
-                }
-            });
-            let newFacet: Opt<Doc>;
-            if (facetHeader === 'text' || facetValues.rtFields / allCollectionDocs.length > 0.1) {
-                newFacet = Docs.Create.TextDocument('', {
-                    title: facetHeader,
-                    system: true,
-                    target: targetDoc,
-                    _width: 100,
-                    _height: 25,
-                    _stayInCollection: true,
-                    treeViewExpandedView: 'layout',
-                    _treeViewOpen: true,
-                    _forceActive: true,
-                    ignoreClick: true,
-                });
-                Doc.GetProto(newFacet).forceActive = true; // required for FormattedTextBox to be able to gain focus since it will never be 'selected'
-                Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                newFacet._textBoxPaddingX = newFacet._textBoxPaddingY = 4;
-                const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
-                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: 'string' });
-            } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
-                newFacet = Docs.Create.SliderDocument({
-                    title: facetHeader,
-                    system: true,
-                    target: targetDoc,
-                    _fitWidth: true,
-                    _height: 40,
-                    _stayInCollection: true,
-                    treeViewExpandedView: 'layout',
-                    _treeViewOpen: true,
-                    _forceActive: true,
-                    _overflow: 'visible',
-                });
-                const newFacetField = Doc.LayoutFieldKey(newFacet);
-                const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
-                Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
-                const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.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;
-                const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`;
-                newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' });
-                newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
-            } else {
-                newFacet = new Doc();
-                newFacet.system = true;
-                newFacet.title = facetHeader;
-                newFacet.treeViewOpen = true;
-                newFacet.layout = CollectionView.LayoutString('data');
-                newFacet.layoutKey = 'layout';
-                newFacet.type = DocumentType.COL;
-                newFacet.target = targetDoc;
-                newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
-            }
-            newFacet.hideContextMenu = true;
-            Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
-        }
-    };
-
-    @computed get scriptField() {
-        const scriptText = 'setDocFilter(this?.target, heading, this.title, checked)';
-        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: 'string', checked: 'string', containingTreeView: Doc.name });
-        return script ? () => script : undefined;
-    }
-
-    /**
-     * Sets whether filters are ANDed or ORed together
-     */
-    @action
-    changeBool = (e: any) => {
-        FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value);
-    };
-
-    /**
-     * Changes whether to select matched or unmatched documents
-     */
-    @action
-    changeMatch = (e: any) => {
-        this._filterMatch = e.currentTarget.value;
-    };
-
-    @action
-    changeSelected = () => {
-        if (this._filterSelected) {
-            this._filterSelected = false;
-            SelectionManager.DeselectAll();
-        } else {
-            this._filterSelected = true;
-            // helper method to select specified docs
-        }
-    };
-
-    FilteringStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) {
-        switch (property.split(':')[0]) {
-            case StyleProp.Decorations:
-                if (doc && !doc.treeViewHideHeaderFields) {
-                    return (
-                        <>
-                            <div style={{ marginRight: '5px', fontSize: '10px' }}>
-                                <select className="filterBox-selection">
-                                    <option value="Is" key="Is">
-                                        Is
-                                    </option>
-                                    <option value="Is Not" key="Is Not">
-                                        Is Not
-                                    </option>
-                                </select>
-                            </div>
-                            <div className="filterBox-treeView-close" onClick={e => this.removeFilter(StrCast(doc.title))}>
-                                <FontAwesomeIcon icon={'times'} size="sm" />
-                            </div>
-                        </>
-                    );
-                }
-        }
-        return DefaultStyleProvider(doc, props, property);
-    }
-
-    suppressChildClick = () => ScriptField.MakeScript('')!;
-
-    /**
-     * Adds a filterDoc to the list of saved filters
-     */
-    saveFilter = () => {
-        Doc.AddDocToList(Doc.UserDoc(), 'savedFilters', this.props.Document);
-    };
-
-    /**
-     * Changes the title of the filterDoc
-     */
-    onTitleValueChange = (val: string) => {
-        Doc.GetProto(this.props.Document).title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`;
-        return true;
-    };
-
-    /**
-     * The flyout from which you can select a saved filter to apply
-     */
-    @computed get flyoutPanel() {
-        return DocListCast(Doc.UserDoc().savedFilters).map(doc => {
-            return (
-                <div className="filterBox-tempFlyout" onWheel={e => e.stopPropagation()} style={{ height: 20, border: '2px' }} onPointerDown={() => this.props.updateFilterDoc?.(doc)}>
-                    {StrCast(doc.title)}
-                </div>
-            );
-        });
-    }
-    setTreeHeight = (hgt: number) => {
-        this.layoutDoc._height = NumCast(this.layoutDoc._autoHeightMargins) + 150; // need to add all the border sizes together.
-    };
-
-    /**
-     * add lock and hide button decorations for the "Dashboards" flyout TreeView
-     */
-    FilterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
-        if (property.split(':')[0] === StyleProp.Decorations) {
-            return !doc || doc.treeViewHideHeaderFields ? null : DashboardToggleButton(doc, 'hidden', 'trash', 'trash', () => this.removeFilter(StrCast(doc.title)));
-        }
-        return this.props.styleProvider?.(doc, props, property);
-    };
-
-    layoutHeight = () => this.layoutDoc[HeightSym]();
-    render() {
-        const facetCollection = this.props.Document;
-
-        const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
-        return this.props.dontRegisterView ? null : (
-            <div className="filterBox-treeView" style={{ width: '100%' }}>
-                <div className="filterBox-title">
-                    <EditableView key="editableView" contents={this.props.Document.title} height={24} fontSize={15} GetValue={() => StrCast(this.props.Document.title)} SetValue={this.onTitleValueChange} />
-                </div>
-
-                <div className="filterBox-select-bool">
-                    <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc?.currentFilter as Doc)?.filterBoolean)}>
-                        <option value="AND" key="AND">
-                            AND
-                        </option>
-                        <option value="OR" key="OR">
-                            OR
-                        </option>
-                    </select>
-                    <div className="filterBox-select-text">filters together</div>
-                </div>
-
-                <div className="filterBox-select">
-                    <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
-                </div>
-
-                <div className="filterBox-tree" key="tree">
-                    <CollectionTreeView
-                        Document={facetCollection}
-                        DataDoc={Doc.GetProto(facetCollection)}
-                        fieldKey={this.props.fieldKey}
-                        CollectionView={undefined}
-                        disableDocBrushing={true}
-                        setHeight={this.setTreeHeight} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
-                        docFilters={returnEmptyFilter}
-                        docRangeFilters={returnEmptyFilter}
-                        searchFilterDocs={returnEmptyDoclist}
-                        childDocumentsActive={returnTrue}
-                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                        ContainingCollectionView={this.props.ContainingCollectionView}
-                        PanelWidth={this.props.PanelWidth}
-                        PanelHeight={this.layoutHeight}
-                        rootSelected={this.props.rootSelected}
-                        renderDepth={1}
-                        dropAction={this.props.dropAction}
-                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                        isAnyChildContentActive={returnFalse}
-                        addDocTab={returnFalse}
-                        pinToPres={returnFalse}
-                        isSelected={returnFalse}
-                        select={returnFalse}
-                        bringToFront={emptyFunction}
-                        isContentActive={returnTrue}
-                        whenChildContentsActiveChanged={returnFalse}
-                        treeViewHideTitle={true}
-                        focus={emptyFunction}
-                        onCheckedClick={this.scriptField}
-                        treeViewHideHeaderFields={false}
-                        dontRegisterView={true}
-                        styleProvider={this.FilterStyleProvider}
-                        docViewPath={this.props.docViewPath}
-                        scriptContext={this.props.scriptContext}
-                        moveDocument={returnFalse}
-                        removeDocument={this.removeFilterDoc}
-                        addDocument={returnFalse}
-                    />
-                </div>
-                <div className="filterBox-bottom">
-                    {/* <div className="filterBox-select-matched">
-                    <input className="filterBox-select-box" type="checkbox"
-                        onChange={this.changeSelected} />
-                    <div className="filterBox-select-text">select</div>
-                    <select className="filterBox-selection" onChange={e => this.changeMatch(e)}>
-                        <option value="matched" key="matched">matched</option>
-                        <option value="unmatched" key="unmatched">unmatched</option>
-                    </select>
-                    <div className="filterBox-select-text">documents</div>
-                </div> */}
-
-                    <div style={{ display: 'flex' }}>
-                        <div className="filterBox-saveWrapper">
-                            <div className="filterBox-saveBookmark" onPointerDown={this.saveFilter}>
-                                <div>SAVE</div>
-                            </div>
-                        </div>
-                        <div className="filterBox-saveWrapper">
-                            <div className="filterBox-saveBookmark">
-                                <Flyout className="myFilters-flyout" anchorPoint={anchorPoints.TOP} content={this.flyoutPanel}>
-                                    <div>FILTERS</div>
-                                </Flyout>
-                            </div>
-                        </div>
-                        <div className="filterBox-saveWrapper">
-                            <div className="filterBox-saveBookmark" onPointerDown={this.props.createNewFilterDoc}>
-                                <div>NEW</div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
-
-ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
-    const docFilters = Cast(layoutDoc._docFilters, listSpec('string'), []);
-    for (const filter of docFilters) {
-        const fields = filter.split(':'); // split into key:value:modifiers
-        if (fields[0] === facetHeader && fields[1] === facetValue) {
-            return fields[2];
-        }
-    }
-    return undefined;
-});
-ScriptingGlobals.add(function readNumFacetData(layoutDoc: Doc, facetDoc: Doc, childKey: string, facetHeader: string) {
-    const allCollectionDocs = new Set<Doc>();
-    const activeTabs = DocListCast(layoutDoc[childKey]);
-    SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
-    const set = new Set<string>();
-    if (facetHeader === 'tags')
-        allCollectionDocs.forEach(child =>
-            Field.toString(child[facetHeader] as Field)
-                .split(':')
-                .forEach(key => set.add(key))
-        );
-    else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
-    const facetValues = Array.from(set).filter(v => v);
-
-    let minVal = Number.MAX_VALUE,
-        maxVal = -Number.MAX_VALUE;
-    facetValues.map(val => {
-        const num = val ? Number(val) : Number.NaN;
-        if (!Number.isNaN(num)) {
-            minVal = Math.min(num, minVal);
-            maxVal = Math.max(num, maxVal);
-        }
-    });
-    const newFacetField = Doc.LayoutFieldKey(facetDoc);
-    const ranged = Doc.readDocRangeFilter(layoutDoc, facetHeader);
-    const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
-    const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
-    facetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
-    facetDoc[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
-    Doc.GetProto(facetDoc)[newFacetField + '-minThumb'] = extendedMinVal;
-    Doc.GetProto(facetDoc)[newFacetField + '-maxThumb'] = extendedMaxVal;
-});
-ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
-    const allCollectionDocs = new Set<Doc>();
-    const activeTabs = DocListCast(layoutDoc[childKey]);
-    SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
-    const set = new Set<string>();
-    if (facetHeader === 'tags')
-        allCollectionDocs.forEach(child =>
-            Field.toString(child[facetHeader] as Field)
-                .split(':')
-                .forEach(key => set.add(key))
-        );
-    else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
-    const facetValues = Array.from(set).filter(v => v);
-
-    let nonNumbers = 0;
-
-    facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
-    const facetValueDocSet = (nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
-        const doc = new Doc();
-        doc.system = true;
-        doc.title = facetValue.toString();
-        doc.target = layoutDoc;
-        doc.facetHeader = facetHeader;
-        doc.facetValue = facetValue;
-        doc.treeViewHideHeaderFields = true;
-        doc.treeViewChecked = ComputedField.MakeFunction('determineCheckedState(self.target, self.facetHeader, self.facetValue)');
-        return doc;
-    });
-    return new List<Doc>(facetValueDocSet);
-});
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index b33529aeb..a89e8b4ed 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,5 +1,8 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import { NodeSelection } from 'prosemirror-state';
 import * as ReactDOM from 'react-dom/client';
 import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
@@ -7,18 +10,14 @@ import { listSpec } from '../../../../fields/Schema';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { Cast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { OpenWhere } from '../DocumentView';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
-import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
-import { Tooltip } from '@material-ui/core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { CollectionViewType } from '../../../documents/DocumentTypes';
-import { NodeSelection } from 'prosemirror-state';
-import { OpenWhere } from '../DocumentView';
-import { FormattedTextBoxComment } from './FormattedTextBoxComment';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
@@ -102,7 +101,11 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._textBoxDoc = this.props.tbox.props.Document;
 
         if (this.props.docid) {
-            DocServer.GetRefField(this.props.docid).then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+            DocServer.GetRefField(this.props.docid).then(
+                action(async dashDoc => {
+                    dashDoc instanceof Doc && (this._dashDoc = dashDoc);
+                })
+            );
         } else {
             this._dashDoc = this.props.tbox.rootDoc;
         }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7713d884e..ee164ab31 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1424,14 +1424,14 @@ export namespace Doc {
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
+    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
-        const filterField = '_' + (fieldSuffix ? fieldSuffix + '-' : '') + 'docFilters';
+        const filterField = '_' + (fieldPrefix ? fieldPrefix + '-' : '') + 'docFilters';
         const docFilters = Cast(container[filterField], listSpec('string'), []);
         runInAction(() => {
             for (let i = 0; i < docFilters.length; i++) {
                 const fields = docFilters[i].split(':'); // split key:value:modifier
-                if (fields[0] === key && (fields[1] === value || modifiers === 'match')) {
+                if (fields[0] === key && (fields[1] === value || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
                     if (fields[2] === modifiers && modifiers && fields[1] === value) {
                         if (toggle) modifiers = 'remove';
                         else return;
-- 
cgit v1.2.3-70-g09d2


From 081f328c117ffdf7ab284be86cdf0342041e7708 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Mar 2023 15:32:06 -0400
Subject: cleaned up pointer events so that nested documents can be selected
 directly without selecting their container.  fixed following link to video
 timeline marker.  fixed focusing on groups.  added didMove to DocFocusOptions
 to restore ability to do toggle on/off of target.  fixed lockingPosition of
 ink strokes.  fixed clicking on inkstrokes in groups to use visiblePainted
 instead of all for pointer events.

---
 src/client/documents/Documents.ts                  |  3 ++-
 src/client/util/DocumentManager.ts                 |  3 ++-
 src/client/views/DocComponent.tsx                  |  5 +++++
 src/client/views/InkingStroke.tsx                  |  2 +-
 src/client/views/PropertiesView.tsx                | 10 ++++-----
 src/client/views/StyleProvider.tsx                 | 25 +++++++++++-----------
 .../collections/CollectionStackedTimeline.tsx      |  5 +----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 15 ++++++++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  1 -
 src/client/views/nodes/DocumentContentsView.tsx    |  4 ++--
 src/client/views/nodes/DocumentView.tsx            | 22 +++++++++----------
 src/client/views/nodes/button/FontIconBox.tsx      |  2 +-
 src/client/views/pdf/Annotation.tsx                |  6 +++---
 src/fields/Doc.ts                                  |  8 +++----
 14 files changed, 62 insertions(+), 49 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ff6c8d440..3e89c8347 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -584,7 +584,7 @@ export namespace Docs {
             [
                 DocumentType.FONTICON,
                 {
-                    layout: { view: FontIconBox, dataField: defaultDataKey },
+                    layout: { view: FontIconBox, dataField: 'icon' },
                     options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
@@ -1686,6 +1686,7 @@ export namespace DocUtils {
                 x: Cast(doc.x, 'number', null),
                 y: Cast(doc.y, 'number', null),
                 backgroundColor: '#ACCEF7',
+                hideAllLinks: true,
                 _width: 15,
                 _height: 15,
                 _xPadding: 0,
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index f2c554866..947613801 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -265,6 +265,7 @@ export class DocumentManager {
         let rootContextView = await new Promise<DocumentView>(res => {
             const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc));
             if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!);
+            options.didMove = true;
             docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation as OpenWhere);
             this.AddViewRenderedCb(docContextPath[0], dv => res(dv));
         });
@@ -299,7 +300,7 @@ export class DocumentManager {
             PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500);
             Doc.linkFollowHighlight(docView.rootDoc, undefined, options.effect);
             if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc);
-            if (options.toggleTarget) docView.rootDoc.hidden = !docView.rootDoc.hidden;
+            if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden;
             if (options.effect) docView.rootDoc[AnimationSym] = options.effect;
 
             if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.textHtml) {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 7c81d92d4..0b92fd864 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -16,6 +16,7 @@ import { Touchable } from './Touchable';
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
     Document: Doc;
+    fieldKey?: string;
     LayoutTemplate?: () => Opt<Doc>;
     LayoutTemplateString?: string;
 }
@@ -37,6 +38,10 @@ export function DocComponent<P extends DocComponentProps>() {
         @computed get dataDoc() {
             return this.props.Document[DataSym] as Doc;
         }
+        // key where data is stored
+        @computed get fieldKey() {
+            return this.props.fieldKey;
+        }
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 4f08a8e22..3861331b5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -418,7 +418,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 inkScaleX,
                 inkScaleY,
                 '',
-                this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? 'none' : 'visiblepainted'),
+                this.props.pointerEvents?.() ?? 'visiblepainted',
                 0.0,
                 false,
                 downHdlr,
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 03b4100a7..f3a5a5393 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1678,8 +1678,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                         <div className="propertiesView-input inline">
                                             <p>Center Target (no zoom)</p>
                                             <button
-                                                style={{ background: this.sourceAnchor?.followLinkZoomScale !== 0 ? '' : '#4476f7', borderRadius: 3 }}
-                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomScale', this.sourceAnchor, 0, 0.5, val => !val && this.sourceAnchor && (this.sourceAnchor.followLinkZoom = true))}
+                                                style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
                                                 onClick={e => e.stopPropagation()}
                                                 className="propertiesButton">
                                                 <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
@@ -1687,7 +1687,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                         </div>
                                         <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
                                             <p>Zoom %</p>
-                                            <div className="ribbon-property" style={{ display: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? 'none' : 'inline-flex' }}>
+                                            <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
                                                 <input className="presBox-input" style={{ width: '100%' }} type="number" value={zoom} />
                                                 <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
                                                     <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
@@ -1706,11 +1706,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                                 <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
                                             </button>
                                         </div>
-                                        {!targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+                                        {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
                                         <div
                                             className={'slider-headers'}
                                             style={{
-                                                display: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? 'none' : 'grid',
+                                                display: !targZoom ? 'none' : 'grid',
                                                 justifyContent: 'space-between',
                                                 width: `calc(100% - ${indent * 2}px)`,
                                                 marginLeft: indent,
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index b11117b11..096396a41 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -89,7 +89,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isAnnotated = property.includes(':annotated');
     const isOpen = property.includes(':open');
     const fieldKey = props?.fieldKey ? props.fieldKey + '-' : isCaption ? 'caption-' : '';
-    const isBackground = () => doc && BoolCast(doc._lockedPosition);
+    const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
     const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
@@ -268,7 +268,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         doc?.boxShadow,
                         doc?._viewType === CollectionViewType.Pile
                             ? '4px 4px 10px 2px'
-                            : isBackground() || doc?._isGroup || docProps?.LayoutTemplateString
+                            : lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString
                             ? undefined // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
                             : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, '0.2vw 0.2vw 0.8vw')}`
                     );
@@ -279,27 +279,28 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
                         : props?.ContainingCollectionDoc?._useClusters
-                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
-                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
-                        : isBackground()
+                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        : lockedPosition()
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : StrCast(doc.boxShadow, '');
             }
         }
         case StyleProp.PointerEvents:
-            if (MainView.Instance._exploreMode) return 'all';
+            const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
+            if (MainView.Instance._exploreMode) return isInk ? 'visiblePainted' : 'all';
             if (doc?.pointerEvents) return StrCast(doc.pointerEvents);
+            if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
             if (props?.pointerEvents?.() === 'none') return 'none';
-            const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
-            if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return 'none';
-            if (!isInk) return props?.isDocumentActive?.() ? 'all' : undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
-            return undefined;
+            if (opacity() === 0 || doc?.isInkMask) return 'none';
+            if (props?.isDocumentActive?.()) return isInk ? 'visiblePainted' : 'all';
+            return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
-                return doc && doc.pointerEvents === 'none' && isBackground() && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 ? (
+                return doc && doc.pointerEvents === 'none' && lockedPosition() && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 ? (
                     <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
-                        <FontAwesomeIcon icon={isBackground() ? 'lock' : 'unlock'} style={{ color: isBackground() ? 'red' : undefined }} size="lg" />
+                        <FontAwesomeIcon icon={lockedPosition() ? 'lock' : 'unlock'} style={{ color: lockedPosition() ? 'red' : undefined }} size="lg" />
                     </div>
                 ) : null;
             }
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index d4e83f609..4941bc722 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -817,10 +817,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
     // renders anchor LabelBox
     renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
         const anchor = observable({ view: undefined as any });
-        const focusFunc = (doc: Doc, options: DocFocusOptions) => {
-            this.props.playLink(mark);
-            this.props.focus(doc, options);
-        };
+        const focusFunc = (doc: Doc, options: DocFocusOptions) => this.props.playLink(mark);
         return {
             anchor,
             view: (
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f0c140ef1..ac90c67a5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -293,6 +293,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
     }
 
+    groupFocus = (anchor: Doc, options: DocFocusOptions) => {
+        options.docTransform = new Transform(-NumCast(this.rootDoc.panX) + NumCast(anchor.x), -NumCast(this.rootDoc.panY) + NumCast(anchor.y), 1);
+        const res = this.props.focus(this.rootDoc, options);
+        options.docTransform = undefined;
+        return res;
+    };
+
     focus = (anchor: Doc, options: DocFocusOptions) => {
         const xfToCollection = options?.docTransform ?? Transform.Identity();
         const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
@@ -301,6 +308,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         // focus on the document in the collection
         const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
+        if (didMove) options.didMove = true;
         // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
         if (didMove) {
             const focusTime = options?.instant ? 0 : options.zoomTime ?? 500;
@@ -1320,7 +1328,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 searchFilterDocs={this.searchFilterDocs}
                 isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
                 isContentActive={emptyFunction}
-                focus={this.isAnnotationOverlay ? this.props.focus : this.focus}
+                focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
                 addDocTab={this.addDocTab}
                 addDocument={this.props.addDocument}
                 removeDocument={this.props.removeDocument}
@@ -2246,8 +2254,9 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
             zoomTime: browseTransitionTime,
         }) === undefined
     ) {
-        const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
-        const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+        const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
+        let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+        while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
         const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
         ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime);
         Doc.linkFollowHighlight(dv?.props.Document, false);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 901415d1e..9bdb2cee7 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -7,7 +7,6 @@ import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { numberRange } from '../../../Utils';
-import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2d1839dd8..679c10a20 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,4 +1,4 @@
-import { computed, trace } from 'mobx';
+import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import { AclPrivate, Doc, Opt } from '../../../fields/Doc';
 import { ScriptField } from '../../../fields/ScriptField';
@@ -31,6 +31,7 @@ import { KeyValueBox } from './KeyValueBox';
 import { LabelBox } from './LabelBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { LinkBox } from './LinkBox';
+import { LoadingBox } from './LoadingBox';
 import { MapBox } from './MapBox/MapBox';
 import { PDFBox } from './PDFBox';
 import { RecordingBox } from './RecordingBox';
@@ -42,7 +43,6 @@ import { VideoBox } from './VideoBox';
 import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
-import { LoadingBox } from './LoadingBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 805e58cd0..36e8facf5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -95,6 +95,7 @@ export interface DocFocusOptions {
     willZoomCentered?: boolean; // determines whether to zoom in on target document
     zoomScale?: number; // percent of containing frame to zoom into document
     zoomTime?: number;
+    didMove?: boolean; // whether a document was changed during the showDocument process
     docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom.  when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
     instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
     preview?: boolean; // whether changes should be previewed by the componentView or written to the document
@@ -313,7 +314,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get titleHeight() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
     }
-    @computed get pointerEvents() {
+    @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
         return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
     }
     @computed get finalLayoutKey() {
@@ -990,7 +991,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const more = cm.findByDescription('More...');
             const moreItems = more && 'subitems' in more ? more.subitems : [];
             if (!Doc.IsSystem(this.rootDoc)) {
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
                 if (!Doc.noviceMode) {
                     moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: 'concierge-bell' });
                     moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' });
@@ -1003,6 +1003,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' });
                 }
                 moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
+
+                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
             }
 
             if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) {
@@ -1130,6 +1132,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </Tooltip>
         );
     }
+    pointerEventsFunc = () => this.pointerEvents;
     @computed get contents() {
         TraceMobx();
         return (
@@ -1137,12 +1140,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 className="documentView-contentsView"
                 style={{
                     // prettier-ignore
-                    pointerEvents:
-                        this.opacity === 0 ? 'none' : 
-                        (this.props.pointerEvents?.() as any) ?? this.rootDoc.layoutKey === 'layout_icon'  ? 'none' :
-                        (this.props.contentPointerEvents as any)  ? (this.props.contentPointerEvents as any)  : 
-                        this.rootDoc.type !== DocumentType.INK && this.isContentActive() ? 'all'  : 
-                        'none',
+                    pointerEvents: 
+                        (this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents) ??
+                        (this.rootDoc.layoutKey === 'layout_icon' ? 'none' : 'all'),
                     height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
                 }}>
                 {!this._retryThumb || !this.thumbShown() ? null : (
@@ -1163,7 +1163,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 <DocumentContentsView
                     key={1}
                     {...this.props}
-                    pointerEvents={this.opacity === 0 ? returnNone : this.props.pointerEvents}
+                    pointerEvents={this.opacity === 0 ? returnNone : this.pointerEventsFunc}
                     docViewPath={this.props.viewPath}
                     thumbShown={this.thumbShown}
                     isHovering={this.props.isHovering}
@@ -1175,7 +1175,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     ScreenToLocalTransform={this.screenToLocal}
                     rootSelected={this.rootSelected}
                     onClick={this.onClickFunc}
-                    focus={emptyFunction}
+                    focus={this.props.focus}
                     layoutKey={this.finalLayoutKey}
                 />
                 {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
@@ -1501,7 +1501,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 style={{
                     display: this.hidden ? 'inline' : undefined,
                     borderRadius: this.borderRounding,
-                    pointerEvents: this.pointerEvents,
+                    pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents,
                 }}>
                 {!borderPath.path ? (
                     animRenderDoc
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index b3a3c3ae4..339887757 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -101,7 +101,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
     }
     Icon = (color: string) => {
-        const icon = StrCast(this.dataDoc.icon, 'user') as any;
+        const icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
         const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
         return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
     };
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index d1f3397f5..db6b1f011 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,5 +1,5 @@
 import React = require('react');
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -7,10 +7,10 @@ import { List } from '../../../fields/List';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { LinkFollower } from '../../util/LinkFollower';
 import { undoBatch } from '../../util/UndoManager';
+import { OpenWhere } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { AnchorMenu } from './AnchorMenu';
 import './Annotation.scss';
-import { OpenWhere } from '../nodes/DocumentView';
 
 interface IAnnotationProps extends FieldViewProps {
     anno: Doc;
@@ -82,7 +82,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             e.stopPropagation();
         } else if (e.button === 0) {
             e.stopPropagation();
-            LinkFollower.FollowLink(undefined, this.annoTextRegion,false);
+            LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
         }
     };
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 44314dca2..40ef67f92 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -891,10 +891,10 @@ export namespace Doc {
         // img.file("smile.gif", imgData, {base64: true});
 
         // Generate the zip file asynchronously
-        // zip.generateAsync({ type: 'blob' }).then((content: any) => {
-        //     // Force down of the Zip file
-        //     saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
-        // });
+        zip.generateAsync({ type: 'blob' }).then((content: any) => {
+            // Force down of the Zip file
+            saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
+        });
     }
     //
     // Determines whether the layout needs to be expanded (as a template).
-- 
cgit v1.2.3-70-g09d2


From 8ab9236664c561d54d6a41ecb1eb2eaf6064fc0c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 27 Mar 2023 18:21:13 -0400
Subject: changed longPress to always select and to show decorations.  fixed
 single/double-click code and cleaned up behavior timeouts.  fixed pointer
 events for tree view editing titles and using as powerpoint.

---
 src/client/documents/Documents.ts                  |  11 +-
 src/client/util/DragManager.ts                     |  34 ++--
 src/client/views/DocumentDecorations.tsx           |   3 +-
 src/client/views/InkingStroke.tsx                  |   1 -
 src/client/views/MainView.tsx                      |  17 ++
 .../collections/CollectionNoteTakingViewColumn.tsx |   4 +-
 src/client/views/collections/TabDocView.tsx        |   2 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   3 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   6 +-
 src/client/views/nodes/DocumentView.tsx            | 197 ++++++++++-----------
 src/client/views/nodes/KeyValueBox.tsx             |  24 ++-
 src/client/views/nodes/LinkBox.tsx                 |  54 +++---
 src/client/views/nodes/ScriptingBox.tsx            |   4 +-
 src/client/views/nodes/WebBox.tsx                  |   3 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  11 +-
 src/client/views/pdf/AnchorMenu.tsx                |   2 +-
 18 files changed, 201 insertions(+), 183 deletions(-)

(limited to 'src/client/views/nodes/DocumentContentsView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0b6d4380d..2bc1b5b1d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -228,7 +228,8 @@ export class DocumentOptions {
     contextMenuScripts?: List<ScriptField>;
     contextMenuLabels?: List<string>;
     contextMenuIcons?: List<string>;
-    allowClickBeforeDoubleClick?: boolean; // whether a click function can fire before the timeout for a double click has expired
+    defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
+    waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
     description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
@@ -408,6 +409,7 @@ export namespace Docs {
                         nativeDimModifiable: true,
                         nativeHeightUnfrozen: true,
                         forceReflow: true,
+                        defaultDoubleClick: 'ignore',
                     },
                 },
             ],
@@ -436,7 +438,7 @@ export namespace Docs {
                 DocumentType.WEB,
                 {
                     layout: { view: WebBox, dataField: defaultDataKey },
-                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
+                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
                 },
             ],
             [
@@ -578,14 +580,14 @@ export namespace Docs {
                 DocumentType.PRES,
                 {
                     layout: { view: PresBox, dataField: defaultDataKey },
-                    options: {},
+                    options: { defaultDoubleClick: 'ignore' },
                 },
             ],
             [
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
@@ -983,6 +985,7 @@ export namespace Docs {
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
             I.data = new InkField(points);
+            I.defaultDoubleClick = 'click';
             I.creationDate = new DateField();
             I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             //I['acl-Override'] = SharingPermissions.Unset;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c35941ee5..cc116fb46 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,6 +1,6 @@
 import { action, observable, runInAction } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { Doc, Field, Opt } from '../../fields/Doc';
+import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
@@ -207,24 +207,26 @@ export namespace DragManager {
             dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
-                docDragData.droppedDocuments = await Promise.all(
-                    dragData.draggedDocuments.map(async d =>
-                        !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
-                            ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
-                            : docDragData.dropAction === 'alias'
-                            ? Doc.BestAlias(d)
-                            : docDragData.dropAction === 'proto'
-                            ? Doc.GetProto(d)
-                            : docDragData.dropAction === 'copy'
-                            ? (
-                                  await Doc.MakeClone(d)
-                              ).clone
-                            : d
+                docDragData.droppedDocuments = (
+                    await Promise.all(
+                        dragData.draggedDocuments.map(async d =>
+                            !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
+                                ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
+                                : docDragData.dropAction === 'alias'
+                                ? Doc.BestAlias(d)
+                                : docDragData.dropAction === 'proto'
+                                ? Doc.GetProto(d)
+                                : docDragData.dropAction === 'copy'
+                                ? (
+                                      await Doc.MakeClone(d)
+                                  ).clone
+                                : d
+                        )
                     )
-                );
+                ).filter(d => d);
                 !['same', 'proto'].includes(docDragData.dropAction as any) &&
                     docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
-                        const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec('string'), []);
+                        const dragProps = StrListCast(dragData.draggedDocuments[i].removeDropProperties);
                         const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
                         remProps.map(prop => (drop[prop] = undefined));
                     });
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2982f8a99..2d2d3c2f6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -67,8 +67,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         reaction(
             () => SelectionManager.Views().slice(),
             action(docs => {
-                this._showNothing = true;
-                docs.length > 1 && (this._showNothing = false); // show decorations if multiple docs are selected
+                this._showNothing = !DocumentView.LongPress && docs.length === 1; // show decorations if multiple docs are selected or we're long pressing
                 this._editingTitle = false;
             })
         );
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 3861331b5..a085b69a5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -179,7 +179,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                       UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
                   }),
             action((e: PointerEvent, doubleTap: boolean | undefined) => {
-                doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick;
                 if (doubleTap) {
                     InkStrokeProperties.Instance._controlButton = true;
                     InkStrokeProperties.Instance._currentPoint = -1;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a30d139be..c84d204d5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -214,6 +214,8 @@ export class MainView extends React.Component {
         window.removeEventListener('keyup', KeyManager.Instance.unhandle);
         window.removeEventListener('keydown', KeyManager.Instance.handle);
         window.removeEventListener('pointerdown', this.globalPointerDown, true);
+        window.removeEventListener('pointermove', this.globalPointerMove, true);
+        window.removeEventListener('mouseclick', this.globalPointerClick, true);
         window.removeEventListener('paste', KeyManager.Instance.paste as any);
         document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
     }
@@ -485,7 +487,20 @@ export class MainView extends React.Component {
         );
     }
 
+    private longPressTimer: NodeJS.Timeout | undefined;
+    globalPointerClick = action((e: any) => {
+        this.longPressTimer && clearTimeout(this.longPressTimer);
+        DocumentView.LongPress = false;
+    });
+    globalPointerMove = action((e: PointerEvent) => {
+        if (e.movementX > 3 || e.movementY > 3) this.longPressTimer && clearTimeout(this.longPressTimer);
+    });
     globalPointerDown = action((e: PointerEvent) => {
+        DocumentView.LongPress = false;
+        this.longPressTimer = setTimeout(
+            action(() => (DocumentView.LongPress = true)),
+            1000
+        );
         DocumentManager.removeOverlayViews();
         Doc.linkFollowUnhighlight();
         AudioBox.Enabled = true;
@@ -506,6 +521,8 @@ export class MainView extends React.Component {
         window.addEventListener('dragover', e => e.preventDefault(), false);
         // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
         document.addEventListener('pointerdown', this.globalPointerDown, true);
+        document.addEventListener('pointermove', this.globalPointerMove, true);
+        document.addEventListener('mouseclick', this.globalPointerClick, true);
         document.addEventListener(
             'click',
             (e: MouseEvent) => {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 621e3d93b..28bdd0cb9 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -268,7 +268,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                             {this.props.renderChildren(this.props.docList)}
                         </div>
 
-                        {!this.props.chromeHidden && type !== DocumentType.PRES ? (
+                        {!this.props.chromeHidden ? (
                             <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
                                 <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
                                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
@@ -289,7 +289,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         const heading = this._heading;
         return (
             <div
-                className={'collectionNoteTakingViewFieldColumn' + (SnappingManager.GetIsDragging() ? 'Dragging' : '')}
+                className="collectionNoteTakingViewFieldColumn"
                 key={heading}
                 style={{
                     width: this.columnWidth,
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 631b9add5..5e6849e7c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -422,7 +422,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         this._lastView = this._view;
                     })}
                     renderDepth={0}
-                    LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString('data') : undefined}
+                    LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString() : undefined}
                     Document={this._document}
                     DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
                     ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index af2d148e0..257428d56 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -305,7 +305,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument('-text-', {
+        const bullet = Docs.Create.TextDocument('', {
             layout: CollectionView.LayoutString('data'),
             title: '-title-',
             treeViewExpandedViewLock: true,
@@ -326,7 +326,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
-        FormattedTextBox.SelectOnLoad = bullet[Id];
+        DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
+
         return bullet;
     }
 
@@ -902,6 +903,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
+                onClickScriptDisable="never"
                 docViewPath={returnEmptyDoclist}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index be4c2a60e..8104ab1a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -822,7 +822,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             Doc.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
-        } else if (!e.cancelBubble) {
+        } else {
+            this.props.DocumentView?.().docView?.cancelMoveEvents();
             if (this.tryDragCluster(e, this._hitCluster)) {
                 return true;
             }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index c21bc9754..9e56de8c2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -127,9 +127,9 @@ export class DocumentContentsView extends React.Component<
         TraceMobx();
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
         if (!this.layoutDoc) return '<p>awaiting layout</p>';
-        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString('data'));
+        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
         const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string');
-        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? 'proto' : '');
+        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
         if (typeof layout === 'string') return layout;
         return '<p>Loading layout</p>';
     }
@@ -273,7 +273,7 @@ export class DocumentContentsView extends React.Component<
                 jsx={layoutFrame}
                 showWarnings={true}
                 onError={(test: any) => {
-                    console.log('DocumentContentsView:' + test);
+                    console.log('DocumentContentsView:' + test, bindings, layoutFrame);
                 }}
             />
         );
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 42c2b28ba..c8f677c5f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -124,6 +124,7 @@ export interface DocComponentView {
     select?: (ctrlKey: boolean, shiftKey: boolean) => void;
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
+    onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
     getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     playFrom?: (time: number, endTime?: number) => void;
@@ -133,6 +134,7 @@ export interface DocComponentView {
     setFocus?: () => void; // sets input focus to the componentView
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     incrementalRendering?: () => void;
+    fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
     fieldKey?: string;
     annotationKey?: string;
     getTitle?: () => string;
@@ -192,6 +194,7 @@ export interface DocumentViewSharedProps {
     ignoreAutoHeight?: boolean;
     forceAutoHeight?: boolean;
     disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
+    onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
     pointerEvents?: () => Opt<string>;
     scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
     createNewFilterDoc?: () => void;
@@ -245,9 +248,10 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
 @observer
 export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
     public static SelectAfterContextMenu = true; //  whether a document should be selected after it's contextmenu is triggered.
-    private _cursorTimer: NodeJS.Timeout | undefined;
-    private _longPress = false;
     private _disposers: { [name: string]: IReactionDisposer } = {};
+    private _doubleClickTimeout: NodeJS.Timeout | undefined;
+    private _singleClickFunc: undefined | (() => any);
+    private _longPressSelector: NodeJS.Timeout | undefined;
     private _downX: number = 0;
     private _downY: number = 0;
     private _downTime: number = 0;
@@ -257,7 +261,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     private _doubleTap = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _titleRef = React.createRef<EditableView>();
-    private _timeout: NodeJS.Timeout | undefined;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
@@ -265,8 +268,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
     @observable _animateScaleTime: Opt<number>; // milliseconds for animating between views.  defaults to 300 if not uset
     @observable _animateScalingTo = 0;
-    @observable _pendingDoubleClick = false;
-    @observable _cursorPress = false;
 
     private get topMost() {
         return this.props.renderDepth === 0 && !LightboxView.LightboxDoc;
@@ -331,9 +332,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get nativeHeight() {
         return this.props.NativeHeight();
     }
+    @computed get disableClickScriptFunc() {
+        return (
+            DocumentView.LongPress ||
+            this.props.onClickScriptDisable === 'always' ||
+            this._componentView?.onClickScriptDisable?.() ||
+            this._componentView?.isAnyChildContentActive?.()
+        );
+    }
     @computed get onClickHandler() {
-        const noClick = this.props.LayoutTemplateString?.includes(KeyValueBox.name) || this.rootSelected() || this.props.isSelected() || this._componentView?.isAnyChildContentActive?.();
-        return noClick ? undefined : this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
+        return this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
     }
     @computed get onDoubleClickHandler() {
         return this.props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
@@ -446,7 +454,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         } else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
-                if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
+                if (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) {
                     this.cleanUpInteractions();
                     this.startDragging(this._downX, this._downY, this.Document.dropAction ? (this.Document.dropAction as any) : e.ctrlKey || e.altKey ? 'alias' : undefined);
                 }
@@ -585,91 +593,76 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             let stopPropagate = true;
             let preventDefault = true;
-            const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
             (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
-            if (this._doubleTap && (![DocumentType.FONTICON, DocumentType.PRES].includes(this.props.Document.type as any) || this.onDoubleClickHandler)) {
-                // && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
-                if (this._timeout) {
-                    clearTimeout(this._timeout);
-                    this._pendingDoubleClick = false;
-                    this._timeout = undefined;
+            if (this._doubleTap) {
+                if (this.onDoubleClickHandler?.script) {
+                    const { clientX, clientY, shiftKey, altKey, ctrlKey } = e; // or we could call e.persist() to capture variables
+                    // prettier-ignore
+                    const func = () => this.onDoubleClickHandler.script.run( {
+                            this: this.layoutDoc,
+                            self: this.rootDoc,
+                            scriptContext: this.props.scriptContext,
+                            thisContainer: this.props.ContainingCollectionDoc,
+                            documentView: this.props.DocumentView(),
+                            clientX, clientY,  altKey,  shiftKey,  ctrlKey,
+                            value: undefined,
+                        }, console.log  );
+                    UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
+                } else if (!Doc.IsSystem(this.rootDoc) && (this.Document.defaultDoubleClick === undefined || this.Document.defaultDoubleClick === 'default')) {
+                    UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
+                    SelectionManager.DeselectAll();
+                    Doc.UnBrushDoc(this.props.Document);
+                } else {
+                    this._singleClickFunc?.();
                 }
-                if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) {
-                    // bcz: hack? don't execute script if you're clicking on a scripting box itself
-                    const { clientX, clientY, shiftKey, altKey, ctrlKey } = e;
+                this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
+                this._doubleClickTimeout = undefined;
+                this._singleClickFunc = undefined;
+            } else {
+                let clickFunc: undefined | (() => any);
+                if (!this.disableClickScriptFunc && this.onClickHandler?.script) {
+                    const { clientX, clientY, shiftKey, altKey, metaKey } = e;
                     const func = () =>
-                        this.onDoubleClickHandler.script.run(
+                        this.onClickHandler?.script.run(
                             {
                                 this: this.layoutDoc,
                                 self: this.rootDoc,
+                                _readOnly_: false,
                                 scriptContext: this.props.scriptContext,
                                 thisContainer: this.props.ContainingCollectionDoc,
                                 documentView: this.props.DocumentView(),
                                 clientX,
                                 clientY,
-                                altKey,
                                 shiftKey,
-                                ctrlKey,
-                                value: undefined,
+                                altKey,
+                                metaKey,
                             },
                             console.log
-                        );
-                    UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
-                } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) {
-                    //UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap');
-                    UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
-                    SelectionManager.DeselectAll();
-                    Doc.UnBrushDoc(this.props.Document);
+                        ).result?.select === true
+                            ? this.props.select(false)
+                            : '';
+                    clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
+                } else if (!this.disableClickScriptFunc && this.allLinks.length && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+                    clickFunc = () => {
+                        SelectionManager.DeselectAll();
+                        LinkFollower.FollowLink(undefined, this.Document, e.altKey);
+                    };
+                } else {
+                    if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
+                        // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+                        stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
+                    }
+                    preventDefault = false;
                 }
-            } else if (!this._longPress && this.onClickHandler?.script && !isScriptBox()) {
-                // bcz: hack? don't execute script if you're clicking on a scripting box itself
-                const { clientX, clientY, shiftKey, altKey, metaKey } = e;
-                const func = () =>
-                    this.onClickHandler?.script.run(
-                        {
-                            this: this.layoutDoc,
-                            self: this.rootDoc,
-                            _readOnly_: false,
-                            scriptContext: this.props.scriptContext,
-                            thisContainer: this.props.ContainingCollectionDoc,
-                            documentView: this.props.DocumentView(),
-                            clientX,
-                            clientY,
-                            shiftKey,
-                            altKey,
-                            metaKey,
-                        },
-                        console.log
-                    ).result?.select === true
-                        ? this.props.select(false)
-                        : '';
-                const clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
-                if (this.onDoubleClickHandler && !this.props.Document.allowClickBeforeDoubleClick) {
-                    runInAction(() => (this._pendingDoubleClick = true));
-                    this._timeout = setTimeout(() => {
-                        this._timeout = undefined;
-                        clickFunc();
-                    }, 150);
-                } else clickFunc();
-            } else if (!this._longPress && this.allLinks.length && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
-                SelectionManager.DeselectAll();
-                this.allLinks.length && LinkFollower.FollowLink(undefined, this.props.Document, e.altKey);
-            } else {
-                if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
-                    // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
-                    stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
+
+                this._singleClickFunc = clickFunc ?? (() => (this._componentView?.select ?? this.props.select)(e.ctrlKey || e.metaKey, e.shiftKey));
+                if ((clickFunc && this.Document.waitForDoubleClickToClick !== 'never') || this.Document.waitForDoubleClickToClick === 'always') {
+                    this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
+                    this._doubleClickTimeout = setTimeout(this._singleClickFunc, 300);
                 } else {
-                    runInAction(() => (this._pendingDoubleClick = true));
-                    this._timeout = setTimeout(
-                        action(() => {
-                            this._pendingDoubleClick = false;
-                            this._timeout = undefined;
-                        }),
-                        350
-                    );
-                    (this._componentView?.select ?? this.props.select)(e.ctrlKey || e.metaKey, e.shiftKey);
+                    this._singleClickFunc();
+                    this._singleClickFunc = undefined;
                 }
-                preventDefault = false;
             }
             stopPropagate && e.stopPropagation();
             preventDefault && e.preventDefault();
@@ -678,6 +671,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
+        this._longPressSelector = setTimeout(() => DocumentView.LongPress && this.props.select(false), 1000);
         if (!(e.nativeEvent as any).DownDocView) (e.nativeEvent as any).DownDocView = GestureOverlay.DownDocView = this.props.DocumentView();
         if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
@@ -689,13 +683,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             return;
         }
-        this._cursorTimer = setTimeout(
-            action(() => {
-                this._cursorPress = true;
-                this.props.select(false);
-            }),
-            1000 // long press required duration
-        );
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._downTime = Date.now();
@@ -719,44 +706,36 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             document.removeEventListener('pointerup', this.onPointerUp);
             document.addEventListener('pointerup', this.onPointerUp);
-        } else {
-            this._cursorTimer && clearTimeout(this._cursorTimer);
-            this._cursorPress = false;
         }
     };
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        if (e.cancelBubble) return;
+        if (this.layoutDoc._lockedPosition || DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        if (((!this.topMost && this.props.isDocumentActive?.()) || this.layoutDoc.onDragStart) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
-                if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
-                    document.removeEventListener('pointermove', this.onPointerMove);
-                    document.removeEventListener('pointerup', this.onPointerUp);
-                    this._cursorTimer && clearTimeout(this._cursorTimer);
-                    this._cursorPress = false;
-                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
-                }
+                this.cleanupPointerEvents();
+                this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
             e.preventDefault();
         }
     };
 
+    cancelMoveEvents = () => document.removeEventListener('pointermove', this.onPointerMove);
+
     cleanupPointerEvents = () => {
         this.cleanUpInteractions();
-        document.removeEventListener('pointermove', this.onPointerMove);
+        this.cancelMoveEvents();
         document.removeEventListener('pointerup', this.onPointerUp);
     };
 
     @action
     onPointerUp = (e: PointerEvent): void => {
         this.cleanupPointerEvents();
-        this._longPress = this._cursorPress;
-        this._cursorTimer && clearTimeout(this._cursorTimer);
-        this._cursorPress = false;
+        this._longPressSelector && clearTimeout(this._longPressSelector);
 
         const now = Date.now();
         if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
@@ -766,6 +745,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             // bcz: this is a placeholder.  documents, when selected, should stopPropagation on doubleClicks if they want to keep the DocumentView from getting them
             if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
+        if (DocumentView.LongPress) e.preventDefault();
     };
 
     @undoBatch
@@ -1051,7 +1031,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         // Add link to help documentation
         if (documentationDescription && documentationLink) {
-            console.log('add documentation item');
             helpItems.push({
                 description: documentationDescription,
                 event: () => {
@@ -1078,7 +1057,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
-    onClickFunc = () => this.onClickHandler as any as ScriptField; // bcz: typing HACK.  check and fix.
+    onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean) => {
@@ -1101,7 +1080,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
                 .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
         const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
-        return !this.props.isSelected() &&
+        return !this.props.LayoutTemplateString &&
+            !this.props.isSelected() &&
             LightboxView.LightboxDoc !== this.rootDoc &&
             this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
@@ -1129,7 +1109,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </Tooltip>
         );
     }
-    contentPointerEvents = () => (this.onClickHandler ? 'none' : this.pointerEvents);
+    contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents);
     @computed get contents() {
         TraceMobx();
         return (
@@ -1326,7 +1306,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div
                 className="documentView-captionWrapper"
                 style={{
-                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                    pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     minWidth: 50 * ffscale(),
                     maxHeight: `max(100%, ${20 * ffscale()}px)`,
                 }}>
@@ -1360,7 +1340,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     width: !this.headerMargin ? `calc(100% - 18px)` : '100%', // leave room for annotation button
                     color: lightOrDark(background),
                     background,
-                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                    pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                 }}>
                 <EditableView
                     ref={this._titleRef}
@@ -1420,7 +1400,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         ...style,
                         background: isButton || thumb ? undefined : this.backgroundColor,
                         opacity: this.opacity,
-                        cursor: this._cursorPress ? 'wait' : Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
+                        cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
                         color: StrCast(this.layoutDoc.color, 'inherit'),
                         fontFamily: StrCast(this.Document._fontFamily, 'inherit'),
                         fontSize: Cast(this.Document._fontSize, 'string', null),
@@ -1428,7 +1408,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this.animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
                     }}>
                     {this.innards}
-                    {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
+                    {!this.disableClickScriptFunc && this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
                     {this.widgetDecorations ?? null}
                 </div>
             )
@@ -1530,6 +1510,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
     public static ROOT_DIV = 'documentView-effectsWrapper';
+    @observable public static LongPress = false;
     public get displayName() {
         return 'DocumentView(' + this.props.Document?.title + ')';
     } // this makes mobx trace() statements more descriptive
@@ -1628,7 +1609,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?.LayoutFieldKey || 'layout';
     }
     get fitWidth() {
-        return this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
+        return this.docView?._componentView?.fitWidth?.() ?? this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
     }
 
     @computed get hideLinkButton() {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 0b1de0ff6..9c31ed3e4 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -3,24 +3,24 @@ import { observer } from 'mobx-react';
 import { Doc, Field, FieldResult } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
-import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { Cast, DocCast, FieldValue, NumCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
 import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting';
 import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
-import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
 import e = require('express');
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { ImageBox } from './ImageBox';
-import { OpenWhere } from './DocumentView';
+import { returnTrue } from '../../../Utils';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -30,8 +30,8 @@ export type KVPScript = {
 
 @observer
 export class KeyValueBox extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldStr: string) {
-        return FieldView.LayoutString(KeyValueBox, fieldStr);
+    public static LayoutString() {
+        return FieldView.LayoutString(KeyValueBox, 'data');
     }
 
     private _mainCont = React.createRef<HTMLDivElement>();
@@ -39,6 +39,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     private _keyInput = React.createRef<HTMLInputElement>();
     private _valInput = React.createRef<HTMLInputElement>();
 
+    componentDidMount() {
+        this.props.setContentView?.(this);
+    }
+    onClickScriptDisable: () => 'always' = () => 'always';
+    fitWidth = returnTrue;
+
     @observable private rows: KeyValuePair[] = [];
 
     @computed get splitPercentage() {
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 43f4b43fb..470f7e803 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,29 +1,39 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { emptyFunction, returnFalse } from "../../../Utils";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { StyleProp } from "../StyleProvider";
-import { ComparisonBox } from "./ComparisonBox";
+import React = require('react');
+import { observer } from 'mobx-react';
+import { emptyFunction, returnFalse, returnTrue } from '../../../Utils';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { StyleProp } from '../StyleProvider';
+import { ComparisonBox } from './ComparisonBox';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./LinkBox.scss";
+import './LinkBox.scss';
 
 @observer
 export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(LinkBox, fieldKey);
+    }
     isContentActiveFunc = () => this.isContentActive();
+
+    onClickScriptDisable: () => 'always' = () => 'always';
+    componentDidMount() {
+        this.props.setContentView?.(this);
+    }
     render() {
-        if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => this.dataDoc.treeViewOpen = true);
-        return <div className={`linkBox-container${this.isContentActive() ? "-interactive" : ""}`}
-            style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} >
-            <ComparisonBox {...this.props}
-                fieldKey="anchor"
-                setHeight={emptyFunction}
-                dontRegisterView={true}
-                renderDepth={this.props.renderDepth + 1}
-                isContentActive={this.isContentActiveFunc}
-                addDocument={returnFalse}
-                removeDocument={returnFalse}
-                moveDocument={returnFalse} />
-        </div>;
+        if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => (this.dataDoc.treeViewOpen = true));
+        return (
+            <div className={`linkBox-container${this.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
+                <ComparisonBox
+                    {...this.props}
+                    fieldKey="anchor"
+                    setHeight={emptyFunction}
+                    dontRegisterView={true}
+                    renderDepth={this.props.renderDepth + 1}
+                    isContentActive={this.isContentActiveFunc}
+                    addDocument={returnFalse}
+                    removeDocument={returnFalse}
+                    moveDocument={returnFalse}
+                />
+            </div>
+        );
     }
-} 
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index a6e2c3e90..5683b0fe7 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -8,7 +8,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { returnEmptyString } from '../../../Utils';
+import { returnEmptyString, returnTrue } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { CompileScript, ScriptParam } from '../../util/Scripting';
@@ -114,6 +114,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         }
     }
 
+    onClickScriptDisable: () => 'always' = () => 'always';
+
     @action
     componentDidMount() {
         this.props.setContentView?.(this);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f7425b26a..73283263f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -205,7 +205,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     this._webPageHasBeenRendered = true;
                 } else if (
                     (!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
-                    !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
                     LightboxView.LightboxDoc !== this.rootDoc
                 ) {
                     // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
@@ -877,7 +876,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
     };
     @computed get content() {
-        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
+        const interactive = this.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
         return (
             <div className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}>
                 {this.urlContent}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d9364e5b5..468bcc4d8 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -817,7 +817,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
             setMode: () => SetActiveInkWidth(value.toString()),
         }],
         ['strokeColor', {
-            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.color) : ActiveInkColor()),
+            checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()),
             setInk: (doc: Doc) => (doc.color = String(value)),
             setMode: () => SetActiveInkColor(StrCast(value)),
         }],
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ddec86606..3ce2366f8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1477,7 +1477,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             e.preventDefault();
         }
     };
-    onSelectMove = (e: PointerEvent) => e.stopPropagation();
+    onSelectMove = (e: PointerEvent) => {
+        this.props.DocumentView?.().docView?.cancelMoveEvents();
+        e.stopPropagation();
+    };
     onSelectEnd = (e: PointerEvent) => {
         document.removeEventListener('pointerup', this.onSelectEnd);
         document.removeEventListener('pointermove', this.onSelectMove);
@@ -1497,10 +1500,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 return;
             }
         }
-
-        if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
-            e.stopPropagation();
-        }
     };
     @action
     onDoubleClick = (e: React.MouseEvent): void => {
@@ -1566,8 +1565,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (this.props.isSelected(true)) {
             // if text box is selected, then it consumes all click events
             (e.nativeEvent as any).handledByInnerReactInstance = true;
-            if (this.ProseRef?.children[0] !== e.nativeEvent.target) e.stopPropagation(); // if you double click on text, then it will be selected instead of sending a double click to DocumentView & opening a lightbox.  Also,if a text box has isLinkButton, this will prevent link following if you've selected the document to edit it.
-            // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks (see above comment)
             this.hitBulletTargets(e.clientX, e.clientY, !this._editorView?.state.selection.empty || this._forceUncollapse, false, this._forceDownNode, e.shiftKey);
         }
         this._forceUncollapse = !(this._editorView!.root as any).getSelection().isCollapsed;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index fc8f1da49..7392d2706 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -80,7 +80,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             { fireImmediately: true }
         );
         this._disposer = reaction(
-            () => SelectionManager.Views(),
+            () => SelectionManager.Views().slice(),
             selected => {
                 this._showLinkPopup = false;
                 AnchorMenu.Instance.fadeOut(true);
-- 
cgit v1.2.3-70-g09d2

