From 501bf6a5d6c81ea1e58509e51ab8c252eaf178ca Mon Sep 17 00:00:00 2001
From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com>
Date: Mon, 28 Jun 2021 16:57:56 -0400
Subject: resolved bugs and removes console.log

---
 .../CollectionSchemaMovableTableHOC.tsx            | 92 +++++++++++-----------
 1 file changed, 48 insertions(+), 44 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 881246bd4..a842d629e 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -143,16 +143,20 @@ export class MovableRow extends React.Component<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();
@@ -167,14 +171,14 @@ export class MovableRow extends React.Component<MovableRowProps> {
 
         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));
@@ -200,59 +204,59 @@ export class MovableRow extends React.Component<MovableRowProps> {
         return false;
     }
 
-    onRowContextMenu = (e: React.MouseEvent): void => {
+    onRowContextMenu = (e: React.MouseEvent): void =>
         e.preventDefault();
-        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" });
+    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" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
-                            <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
-                            <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
-                        </div>
-                        {children}
-                    </ReactTableDefaults.TrComponent>
-                </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" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
+                        <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
+                        <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+                    </div>
+                    {children}
+                </ReactTableDefaults.TrComponent>
             </div>
-        );
-    }
+        </div>
+    );
+}
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 48640698459e073118a8a36d5428639c88cb6864 Mon Sep 17 00:00:00 2001
From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com>
Date: Thu, 1 Jul 2021 16:55:16 -0400
Subject: fixed bizarre bug

---
 .../CollectionSchemaMovableTableHOC.tsx            | 85 +++++++++++-----------
 1 file changed, 42 insertions(+), 43 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index a842d629e..149677976 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -204,59 +204,58 @@ export class MovableRow extends React.Component<MovableRowProps> {
         return false;
     }
 
-    onRowContextMenu = (e: React.MouseEvent): void =>
-        e.preventDefault();
-    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" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
-                        <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
-                        <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
-                    </div>
-                    {children}
-                </ReactTableDefaults.TrComponent>
+        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" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
+                            <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
+                            <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+                        </div>
+                        {children}
+                    </ReactTableDefaults.TrComponent>
+                </div>
             </div>
-        </div>
-    );
-}
+        );
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 7f42c98318899a51cb64080fc4f4e315e43bb150 Mon Sep 17 00:00:00 2001
From: 0x85FB9C51 <77808164+0x85FB9C51@users.noreply.github.com>
Date: Tue, 6 Jul 2021 23:47:38 -0400
Subject: reoganization of schema view files, partial

---
 .idea/.gitignore                                   |   3 +
 .idea/Dash-Web.iml                                 |   9 +
 .idea/modules.xml                                  |   8 +
 .idea/vcs.xml                                      |   6 +
 .../views/collections/CollectionSchemaCells.tsx    | 584 -------------------
 .../views/collections/CollectionSchemaHeaders.tsx  | 518 -----------------
 .../CollectionSchemaMovableTableHOC.tsx            | 261 ---------
 .../views/collections/CollectionSchemaView.scss    | 641 ---------------------
 .../views/collections/CollectionSchemaView.tsx     | 575 ------------------
 src/client/views/collections/SchemaTable.tsx       | 599 -------------------
 .../schemaView/CollectionSchemaCells.tsx           | 584 +++++++++++++++++++
 .../schemaView/CollectionSchemaHeaders.tsx         | 518 +++++++++++++++++
 .../schemaView/CollectionSchemaMovableTableHOC.tsx | 261 +++++++++
 .../schemaView/CollectionSchemaView.scss           | 552 ++++++++++++++++++
 .../schemaView/CollectionSchemaView.tsx            | 575 ++++++++++++++++++
 .../views/collections/schemaView/SchemaTable.tsx   | 599 +++++++++++++++++++
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +-
 17 files changed, 3116 insertions(+), 3179 deletions(-)
 create mode 100644 .idea/.gitignore
 create mode 100644 .idea/Dash-Web.iml
 create mode 100644 .idea/modules.xml
 create mode 100644 .idea/vcs.xml
 delete mode 100644 src/client/views/collections/CollectionSchemaCells.tsx
 delete mode 100644 src/client/views/collections/CollectionSchemaHeaders.tsx
 delete mode 100644 src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
 delete mode 100644 src/client/views/collections/CollectionSchemaView.scss
 delete mode 100644 src/client/views/collections/CollectionSchemaView.tsx
 delete mode 100644 src/client/views/collections/SchemaTable.tsx
 create mode 100644 src/client/views/collections/schemaView/CollectionSchemaCells.tsx
 create mode 100644 src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx
 create mode 100644 src/client/views/collections/schemaView/CollectionSchemaMovableTableHOC.tsx
 create mode 100644 src/client/views/collections/schemaView/CollectionSchemaView.scss
 create mode 100644 src/client/views/collections/schemaView/CollectionSchemaView.tsx
 create mode 100644 src/client/views/collections/schemaView/SchemaTable.tsx

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

diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..26d33521a
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/Dash-Web.iml b/.idea/Dash-Web.iml
new file mode 100644
index 000000000..d6ebd4805
--- /dev/null
+++ b/.idea/Dash-Web.iml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..35c51c015
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/Dash-Web.iml" filepath="$PROJECT_DIR$/.idea/Dash-Web.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
deleted file mode 100644
index 3fb9910b5..000000000
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ /dev/null
@@ -1,584 +0,0 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import DatePicker from "react-datepicker";
-import "react-datepicker/dist/react-datepicker.css";
-import { CellInfo } from "react-table";
-import "react-table/react-table.css";
-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, NumCast, StrCast } from "../../../fields/Types";
-import { ImageField } from "../../../fields/URLField";
-import { Utils, emptyFunction } 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 '../DocumentDecorations.scss';
-import { EditableView } from "../EditableView";
-import { MAX_ROW_HEIGHT } from '../globalCssVariables.scss';
-import { DocumentIconContainer } from "../nodes/DocumentIcon";
-import { OverlayView } from "../OverlayView";
-import "./CollectionSchemaView.scss";
-import { CollectionView } from "./CollectionView";
-const path = require('path');
-
-export interface CellProps {
-    row: number;
-    col: number;
-    rowProps: CellInfo;
-    CollectionView: Opt<CollectionView>;
-    ContainingCollection: Opt<CollectionView>;
-    Document: Doc;
-    fieldKey: string;
-    renderDepth: number;
-    addDocTab: (document: Doc, where: string) => boolean;
-    pinToPres: (document: Doc) => void;
-    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined,
-        addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    isFocused: boolean;
-    changeFocusedCellByIndex: (row: number, col: number) => void;
-    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;
-    showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void;
-}
-
-@observer
-export class CollectionSchemaCell extends React.Component<CellProps> {
-    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;
-    protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
-    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 (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 => {
-        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);
-    }
-
-    @action
-    onPointerDown = async (e: React.PointerEvent): Promise<void> => {
-        this.onItemDown(e);
-        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)) {
-            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) => {
-        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;
-        doc[this.renderFieldKey] = res.result;
-        return true;
-    }
-
-    private drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.complete.docDragData) {
-            if (de.complete.docDragData.draggedDocuments.length === 1) {
-                this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
-            }
-            else {
-                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) => {
-        this._dropDisposer?.();
-        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() { return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); }
-    onItemDown = async (e: React.PointerEvent) => {
-        if (this.props.Document._searchDoc) {
-            const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
-            const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
-            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext,
-                undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
-        }
-    }
-    renderCellWithType(type: string | undefined) {
-        const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-
-        const fieldKey = this.renderFieldKey;
-        const field = this._rowDoc[fieldKey];
-
-        const onPointerEnter = (e: React.PointerEvent): void => {
-            if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
-                dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
-            }
-        };
-        const onPointerLeave = (e: React.PointerEvent): void => {
-            dragRef.current!.className = "collectionSchemaView-cellContainer";
-        };
-
-        let contents = Field.toString(field as Field);
-        contents = contents === "" ? "--" : contents;
-
-        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() !== "") {
-            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 (start !== -1) {
-                positions.push(start);
-            }
-            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);
-            }
-            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 (let s of value) {
-                                            if (isNaN(parseInt(s))) {
-                                                inputIsNum = false;
-                                            }
-                                        }
-                                        // check if the input is a boolean
-                                        let 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
-                                            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 (let 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 !== value.length || 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 !== value.length || 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) => {
-                                    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> { 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: false,
-            transformer: DocumentIconContainer.getTransformer()
-        });
-
-        const results = script.compiled && script.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 => {
-        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() {
-        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, "add:right")} 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 (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here
-
-        const ext = path.extname(url.href);
-        return url.href.replace(ext, "_o" + path.extname(url.href));
-    }
-
-    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;
-        const url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
-
-        const aspect = Doc.NativeAspect(this._rowDoc);
-        let width = Math.min(75, this.props.rowProps.width);
-        const height = Math.min(75, width / aspect);
-        width = height * aspect;
-
-        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;
-    @observable private _text = "select an item";
-    @observable private _selectedNum = 0;
-
-    @action
-    onSetValue = (value: string) => {
-        // change if its 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) => {
-        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 (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 {
-    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>;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
deleted file mode 100644
index 3b52e6408..000000000
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ /dev/null
@@ -1,518 +0,0 @@
-import React = require("react");
-import { IconProp, library } from "@fortawesome/fontawesome-svg-core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { listSpec } from "../../../fields/Schema";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../fields/Types";
-import { undoBatch } from "../../util/UndoManager";
-import { SearchBox } from "../search/SearchBox";
-import { ColumnType } from "./CollectionSchemaView";
-import "./CollectionSchemaView.scss";
-import { CollectionView } from "./CollectionView";
-
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-
-export interface AddColumnHeaderProps {
-    createColumn: () => void;
-}
-
-@observer
-export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHeaderProps> {
-    render() {
-        return (
-            <button className="add-column" onClick={() => this.props.createColumn()}><FontAwesomeIcon icon="plus" size="sm" /></button>
-        );
-    }
-}
-
-
-export interface ColumnMenuProps {
-    columnField: SchemaHeaderField;
-    // keyValue: string;
-    possibleKeys: string[];
-    existingKeys: string[];
-    // keyType: ColumnType;
-    typeConst: boolean;
-    menuButtonContent: JSX.Element;
-    addNew: boolean;
-    onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
-    setIsEditing: (isEditing: boolean) => void;
-    deleteColumn: (column: string) => void;
-    onlyShowOptions: boolean;
-    setColumnType: (column: SchemaHeaderField, type: ColumnType) => void;
-    setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void;
-    anchorPoint?: any;
-    setColumnColor: (column: SchemaHeaderField, color: string) => void;
-}
-@observer
-export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> {
-    @observable private _isOpen: boolean = false;
-    @observable private _node: HTMLDivElement | null = null;
-
-    componentDidMount() { document.addEventListener("pointerdown", this.detectClick); }
-
-    componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); }
-
-    @action
-    detectClick = (e: PointerEvent) => {
-        !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false);
-    }
-
-    @action
-    toggleIsOpen = (): void => {
-        this.props.setIsEditing(this._isOpen = !this._isOpen);
-    }
-
-    changeColumnType = (type: ColumnType) => {
-        this.props.setColumnType(this.props.columnField, type);
-    }
-
-    changeColumnSort = (desc: boolean | undefined) => {
-        this.props.setColumnSort(this.props.columnField, desc);
-    }
-
-    changeColumnColor = (color: string) => {
-        this.props.setColumnColor(this.props.columnField, color);
-    }
-
-    @action
-    setNode = (node: HTMLDivElement): void => {
-        if (node) {
-            this._node = node;
-        }
-    }
-
-    renderTypes = () => {
-        if (this.props.typeConst) return (null);
-
-        const type = this.props.columnField.type;
-        return (
-            <div className="collectionSchema-headerMenu-group">
-                <label>Column type:</label>
-                <div className="columnMenu-types">
-                    <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Any)}>
-                        <FontAwesomeIcon icon={"align-justify"} size="sm" />
-                        Any
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Number)}>
-                        <FontAwesomeIcon icon={"hashtag"} size="sm" />
-                        Number
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.String)}>
-                        <FontAwesomeIcon icon={"font"} size="sm" />
-                        Text
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Boolean)}>
-                        <FontAwesomeIcon icon={"check-square"} size="sm" />
-                        Checkbox
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.List)}>
-                        <FontAwesomeIcon icon={"list-ul"} size="sm" />
-                        List
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}>
-                        <FontAwesomeIcon icon={"file"} size="sm" />
-                        Document
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Image)}>
-                        <FontAwesomeIcon icon={"image"} size="sm" />
-                        Image
-                    </div>
-                    <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Date)}>
-                        <FontAwesomeIcon icon={"calendar"} size="sm" />
-                        Date
-                    </div>
-                </div>
-            </div >
-        );
-    }
-
-    renderSorting = () => {
-        const sort = this.props.columnField.desc;
-        return (
-            <div className="collectionSchema-headerMenu-group">
-                <label>Sort by:</label>
-                <div className="columnMenu-sort">
-                    <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.changeColumnSort(true)}>
-                        <FontAwesomeIcon icon="sort-amount-down" size="sm" />
-                        Sort descending
-                    </div>
-                    <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.changeColumnSort(false)}>
-                        <FontAwesomeIcon icon="sort-amount-up" size="sm" />
-                        Sort ascending
-                    </div>
-                    <div className="columnMenu-option" onClick={() => this.changeColumnSort(undefined)}>
-                        <FontAwesomeIcon icon="times" size="sm" />
-                        Clear sorting
-                    </div>
-                </div>
-            </div>
-        );
-    }
-
-    renderColors = () => {
-        const selected = this.props.columnField.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.changeColumnColor(pink!)}></div>
-                    <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div>
-                    <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div>
-                    <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div>
-                    <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div>
-                    <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div>
-                </div>
-            </div>
-        );
-    }
-
-    renderContent = () => {
-        return (
-            <div className="collectionSchema-header-menuOptions">
-                {this.props.onlyShowOptions ? <></> :
-                    <>
-                        {this.renderTypes()}
-                        {this.renderSorting()}
-                        {this.renderColors()}
-                        <div className="collectionSchema-headerMenu-group">
-                            <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Delete Column</button>
-                        </div>
-                    </>
-                }
-            </div>
-        );
-    }
-
-    render() {
-        return (
-            <div className="collectionSchema-header-menu" ref={this.setNode}>
-                <Flyout anchorPoint={this.props.anchorPoint ? this.props.anchorPoint : anchorPoints.TOP_CENTER} content={this.renderContent()}>
-                    <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div>
-                </ Flyout >
-            </div>
-        );
-    }
-}
-
-
-export interface KeysDropdownProps {
-    keyValue: string;
-    possibleKeys: string[];
-    existingKeys: string[];
-    canAddNew: boolean;
-    addNew: boolean;
-    onSelect: (oldKey: string, newKey: string, addnew: boolean, filter?: string) => void;
-    setIsEditing: (isEditing: boolean) => void;
-    width?: string;
-    docs?: Doc[];
-    Document: Doc;
-    dataDoc: Doc | undefined;
-    fieldKey: string;
-    ContainingCollectionDoc: Doc | undefined;
-    ContainingCollectionView: Opt<CollectionView>;
-    active?: (outsideReaction?: boolean) => boolean;
-    openHeader: (column: any, screenx: number, screeny: number) => void;
-    col: SchemaHeaderField;
-    icon: IconProp;
-}
-@observer
-export class KeysDropdown extends React.Component<KeysDropdownProps> {
-    @observable private _key: string = this.props.keyValue;
-    @observable private _searchTerm: string = this.props.keyValue;
-    @observable private _isOpen: boolean = false;
-    @observable private _node: HTMLDivElement | null = null;
-    @observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef();
-
-    @action setSearchTerm = (value: string): void => { this._searchTerm = value; };
-    @action setKey = (key: string): void => { this._key = key; };
-    @action setIsOpen = (isOpen: boolean): void => { this._isOpen = isOpen; };
-
-    @action
-    onSelect = (key: string): void => {
-        this.props.onSelect(this._key, key, this.props.addNew);
-        this.setKey(key);
-        this._isOpen = false;
-        this.props.setIsEditing(false);
-    }
-
-    @action
-    setNode = (node: HTMLDivElement): void => {
-        if (node) {
-            this._node = node;
-        }
-    }
-
-    componentDidMount() {
-        document.addEventListener("pointerdown", this.detectClick);
-        const filters = Cast(this.props.Document._docFilters, listSpec("string"));
-        if (filters?.some(filter => filter.split(":")[0] === this._key)) {
-            runInAction(() => this.closeResultsVisibility = "contents");
-        }
-    }
-
-    @action
-    detectClick = (e: PointerEvent): void => {
-        if (this._node && this._node.contains(e.target as Node)) {
-        } else {
-            this._isOpen = false;
-            this.props.setIsEditing(false);
-        }
-    }
-
-    private tempfilter: string = "";
-    @undoBatch
-    onKeyDown = (e: React.KeyboardEvent): void => {
-        if (e.key === "Enter") {
-            if (this._searchTerm.includes(":")) {
-                const colpos = this._searchTerm.indexOf(":");
-                const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
-                if (temp === "") {
-                    Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
-                    this.updateFilter();
-                }
-                else {
-                    Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
-                    this.tempfilter = temp;
-                    Doc.setDocFilter(this.props.Document, this._key, temp, "check");
-                    this.props.col.setColor("green");
-                    this.closeResultsVisibility = "contents";
-                }
-            }
-            else {
-                Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
-                this.updateFilter();
-                if (this.showKeys.length) {
-                    this.onSelect(this.showKeys[0]);
-                } else if (this._searchTerm !== "" && this.props.canAddNew) {
-                    this.setSearchTerm(this._searchTerm || this._key);
-                    this.onSelect(this._searchTerm);
-                }
-            }
-        }
-    }
-
-    onChange = (val: string): void => {
-        this.setSearchTerm(val);
-    }
-
-    @action
-    onFocus = (e: React.FocusEvent): void => {
-        this._isOpen = true;
-        this.props.setIsEditing(true);
-    }
-
-    @computed get showKeys() {
-        const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
-        const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
-        const showKeys = new Set<string>();
-        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode ||
-            whitelistKeys.includes(key)
-            || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
-        return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
-    }
-    @action
-    renderOptions = (): JSX.Element[] | JSX.Element => {
-        if (!this._isOpen) {
-            this.defaultMenuHeight = 0;
-            return <></>;
-        }
-        const options = this.showKeys.map(key => {
-            return <div key={key} className="key-option" style={{
-                border: "1px solid lightgray",
-                width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
-            }}
-                onPointerDown={e => {
-                    e.stopPropagation();
-                }}
-                onClick={() => {
-                    this.onSelect(key);
-                    this.setSearchTerm("");
-                }}>{key}</div>;
-        });
-
-        // if search term does not already exist as a group type, give option to create new group type
-
-        if (this._key !== this._searchTerm.slice(0, this._key.length)) {
-            if (this._searchTerm !== "" && this.props.canAddNew) {
-                options.push(<div key={""} className="key-option" style={{
-                    border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
-                }}
-                    onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
-                    Create "{this._searchTerm}" key</div>);
-            }
-        }
-
-        if (options.length === 0) {
-            this.defaultMenuHeight = 0;
-        }
-        else {
-            if (this.props.docs) {
-                const panesize = this.props.docs.length * 30;
-                options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
-            }
-            else {
-                options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
-            }
-        }
-        return options;
-    }
-
-    docSafe: Doc[] = [];
-
-    @action
-    renderFilterOptions = (): JSX.Element[] | JSX.Element => {
-        if (!this._isOpen || !this.props.dataDoc) {
-            this.defaultMenuHeight = 0;
-            return <></>;
-        }
-        const keyOptions: string[] = [];
-        const colpos = this._searchTerm.indexOf(":");
-        const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
-        if (this.docSafe.length === 0) {
-            this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
-        }
-        const docs = this.docSafe;
-        docs.forEach((doc) => {
-            const key = StrCast(doc[this._key]);
-            if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") {
-                keyOptions.push(key);
-            }
-        });
-
-        const filters = Cast(this.props.Document._docFilters, listSpec("string"));
-        if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) {
-            this.props.col.setColor("rgb(241, 239, 235)");
-            this.closeResultsVisibility = "none";
-        }
-        for (let i = 0; i < (filters?.length ?? 0) - 1; i++) {
-            if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i].split(":")[1]) === false) {
-                keyOptions.push(filters![i + 1]);
-            }
-        }
-        const options = keyOptions.map(key => {
-            let bool = false;
-            if (filters !== undefined) {
-                const ind = filters.findIndex(filter => filter.split(":")[0] === key);
-                const fields = ind === -1 ? undefined : filters[ind].split(":");
-                bool = fields ? fields[1] === "check" : false;
-            }
-            return <div key={key} className="key-option" style={{
-                border: "1px solid lightgray", paddingLeft: 5, textAlign: "left",
-                width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
-            }}
-            >
-                <input type="checkbox"
-                    onPointerDown={e => e.stopPropagation()}
-                    onClick={e => e.stopPropagation()}
-                    onChange={(e) => {
-                        e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, "remove");
-                        e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log("");
-                        e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter();
-                        e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove");
-                    }}
-                    checked={bool}
-                />
-                <span style={{ paddingLeft: 4 }}>
-                    {key}
-                </span>
-
-            </div>;
-        });
-        if (options.length === 0) {
-            this.defaultMenuHeight = 0;
-        }
-        else {
-            if (this.props.docs) {
-                const panesize = this.props.docs.length * 30;
-                options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
-            }
-            else {
-                options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
-            }
-
-        }
-        return options;
-    }
-
-    @observable defaultMenuHeight = 0;
-
-
-    updateFilter() {
-        const filters = Cast(this.props.Document._docFilters, listSpec("string"));
-        if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) {
-            this.props.col.setColor("rgb(241, 239, 235)");
-            this.closeResultsVisibility = "none";
-        }
-    }
-
-    @computed get scriptField() {
-        const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
-        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
-        return script ? () => script : undefined;
-    }
-    filterBackground = () => "rgba(105, 105, 105, 0.432)";
-    @observable filterOpen: boolean | undefined = undefined;
-    closeResultsVisibility: string = "none";
-
-    removeFilters = (e: React.PointerEvent): void => {
-        const keyOptions: string[] = [];
-        if (this.docSafe.length === 0 && this.props.dataDoc) {
-            this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
-        }
-        const docs = this.docSafe;
-        docs.forEach((doc) => {
-            const key = StrCast(doc[this._key]);
-            if (keyOptions.includes(key) === false) {
-                keyOptions.push(key);
-            }
-        });
-
-        Doc.setDocFilter(this.props.Document, this._key, "", "remove");
-        this.props.col.setColor("rgb(241, 239, 235)");
-        this.closeResultsVisibility = "none";
-    }
-    render() {
-        return (
-            <div style={{ display: "flex" }} ref={this.setNode}>
-                <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
-
-                {/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
-                    runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen })
-                }} /> */}
-
-                <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
-                    <input className="keys-search" style={{ width: "100%" }}
-                        ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
-                        onChange={e => this.onChange(e.target.value)}
-                        onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
-                        onFocus={this.onFocus} ></input>
-                    <div style={{ display: this.closeResultsVisibility }}>
-                        <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
-                            style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
-                    </div>
-                    {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{
-                        width: this.props.width, maxWidth: this.props.width, height: "auto",
-                    }}>
-                        {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()}
-                    </div>}
-                </div >
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
deleted file mode 100644
index 149677976..000000000
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ /dev/null
@@ -1,261 +0,0 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
-import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
-import { Doc } from "../../../fields/Doc";
-import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-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 "./CollectionSchemaView.scss";
-
-export interface MovableColumnProps {
-    columnRenderer: TableCellRenderer;
-    columnValue: SchemaHeaderField;
-    allColumns: SchemaHeaderField[];
-    reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
-    ScreenToLocalTransform: () => Transform;
-}
-export class MovableColumn extends React.Component<MovableColumnProps> {
-    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-    private _colDropDisposer?: DragManager.DragDropDisposer;
-    private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
-    private _sensitivity: number = 16;
-    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-
-    onPointerEnter = (e: React.PointerEvent): void => {
-        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);
-    }
-    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.right - rect.left) / 2), rect.top);
-        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));
-        }
-    }
-
-    colDrop = (e: Event, de: DragManager.DropEvent) => {
-        document.removeEventListener("pointermove", this.onDragMove, true);
-        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.right - rect.left) / 2), rect.top);
-        const before = x[0] < bounds[0];
-        const colDragData = de.complete.columnDragData;
-        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();
-
-            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 (e.buttons === 1) {
-            const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
-            if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
-                document.removeEventListener("pointermove", this.onPointerMove);
-                e.stopPropagation();
-
-                document.addEventListener("pointermove", onRowMove);
-                document.addEventListener("pointerup", onRowUp);
-            }
-        }
-    }
-
-    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 (!(e.target as any)?.tagName.includes("INPUT")) {
-            this._startDragPosition = { x: dx, y: dy };
-            document.addEventListener("pointermove", this.onPointerMove);
-        }
-    }
-
-
-    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>
-        );
-    }
-}
-
-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<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;
-
-        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;
-    }
-
-    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);
-    }
-
-    @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;
-
-        if (!rowInfo) {
-            return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
-        }
-
-        const { original } = rowInfo;
-        const doc = FieldValue(Cast(original, Doc));
-
-        if (!doc) return (null);
-
-        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";
-
-        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" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
-                            <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
-                            <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
-                        </div>
-                        {children}
-                    </ReactTableDefaults.TrComponent>
-                </div>
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
deleted file mode 100644
index 2bdd280ec..000000000
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ /dev/null
@@ -1,641 +0,0 @@
-@import "../globalCssVariables";
-
-.collectionSchemaView-container {
-    border-width: $COLLECTION_BORDER_WIDTH;
-    border-color: $intermediate-color;
-    border-style: solid;
-    border-radius: $border-radius;
-    box-sizing: border-box;
-    position: relative;
-    top: 0;
-    width: 100%;
-    height: 100%;
-    margin-top: 0;
-    transition: top 0.5s;
-    display: flex;
-    justify-content: space-between;
-    flex-wrap: nowrap;
-    touch-action: none;
-
-    div {
-        touch-action: none;
-    }
-
-
-    .collectionSchemaView-tableContainer {
-        width: 100%;
-        height: 100%;
-    }
-
-    .collectionSchemaView-dividerDragger {
-        position: relative;
-        height: 100%;
-        width: $SCHEMA_DIVIDER_WIDTH;
-        z-index: 20;
-        right: 0;
-        top: 0;
-        background: gray;
-        cursor: col-resize;
-    }
-
-    // .documentView-node:first-child {
-    //     background: $light-color;
-    // }
-}
-
-.collectionSchemaView-searchContainer {
-    border-width: $COLLECTION_BORDER_WIDTH;
-    border-color: $intermediate-color;
-    border-style: solid;
-    border-radius: $border-radius;
-    box-sizing: border-box;
-    position: relative;
-    top: 0;
-    width: 100%;
-    height: 100%;
-    margin-top: 0;
-    transition: top 0.5s;
-    display: flex;
-    justify-content: space-between;
-    flex-wrap: nowrap;
-    touch-action: none;
-    padding: 2px;
-
-    div {
-        touch-action: none;
-    }
-
-
-    .collectionSchemaView-tableContainer {
-        width: 100%;
-        height: 100%;
-    }
-
-    .collectionSchemaView-dividerDragger {
-        position: relative;
-        height: 100%;
-        width: 20px;
-        z-index: 20;
-        right: 0;
-        top: 0;
-        background: gray;
-        cursor: col-resize;
-    }
-
-    // .documentView-node:first-child {
-    //     background: $light-color;
-    // }
-}
-
-.ReactTable {
-    width: 100%;
-    background: white;
-    box-sizing: border-box;
-    border: none !important;
-    float: none !important;
-
-    .rt-table {
-        height: 100%;
-        display: -webkit-inline-box;
-        direction: ltr;
-        overflow: visible;
-    }
-    .rt-noData {
-        display: none;
-    }
-
-    .rt-thead {
-        width: 100%;
-        z-index: 100;
-        overflow-y: visible;
-
-        &.-header {
-            font-size: 12px;
-            height: 30px;
-            box-shadow: none;
-            z-index: 100;
-            overflow-y: visible;
-        }
-
-        .rt-resizable-header-content {
-            height: 100%;
-            overflow: visible;
-        }
-
-        .rt-th {
-            padding: 0;
-            border: solid lightgray;
-            border-width: 0 1px;
-            border-bottom: 2px solid lightgray;
-        }
-    }
-
-    .rt-th {
-        font-size: 13px;
-        text-align: center;
-
-        &:last-child {
-            overflow: visible;
-        }
-    }
-
-    .rt-tbody {
-        width: 100%;
-        direction: rtl;
-        overflow: visible;
-
-        .rt-td {
-            border-right: 1px solid rgba(0, 0, 0, 0.2);
-        }
-    }
-
-    .rt-tr-group {
-        direction: ltr;
-        flex: 0 1 auto;
-        min-height: 30px;
-        border: 0 !important;
-    }
-
-    .rt-tr {
-        width: 100%;
-        min-height: 30px;
-    }
-
-    .rt-td {
-        padding: 0;
-        font-size: 13px;
-        text-align: center;
-        white-space: nowrap;
-        display: flex;
-        align-items: center;
-
-        .imageBox-cont {
-            position: relative;
-            max-height: 100%;
-        }
-
-        .imageBox-cont img {
-            object-fit: contain;
-            max-width: 100%;
-            height: 100%;
-        }
-
-        .videoBox-cont {
-            object-fit: contain;
-            width: auto;
-            height: 100%;
-        }
-    }
-    .rt-td.rt-expandable {
-        display: flex;
-        align-items: center;
-        height: inherit;
-    }
-
-    .rt-resizer {
-        width: 8px;
-        right: -4px;
-    }
-
-    .rt-resizable-header {
-        padding: 0;
-        height: 30px;
-    }
-
-    .rt-resizable-header:last-child {
-        overflow: visible;
-
-        .rt-resizer {
-            width: 5px !important;
-        }
-    }
-}
-
-.documentView-node-topmost {
-    text-align: left;
-    transform-origin: center top;
-    display: inline-block;
-}
-
-.collectionSchema-col {
-    height: 100%;
-}
-
-
-.collectionSchema-header-menu {
-    height: auto;
-    z-index: 100;
-    position: absolute;
-    background: white;
-    padding: 5px;
-    position: fixed;
-    background: white;
-    border: black 1px solid;
-
-    .collectionSchema-header-toggler {
-        z-index: 100;
-        width: 100%;
-        height: 100%;
-        padding: 4px;
-        letter-spacing: 2px;
-        text-transform: uppercase;
-
-        svg {
-            margin-right: 4px;
-        }
-    }
-}
-
-.collectionSchemaView-header {
-    height: 100%;
-    color: gray;
-    z-index: 100;
-    overflow-y: visible;
-    display: flex;
-    justify-content: space-between;
-    flex-wrap: wrap;
-}
-
-button.add-column {
-    width: 28px;
-}
-
-.collectionSchema-header-menuOptions {
-    color: black;
-    width: 180px;
-    text-align: left;
-
-    .collectionSchema-headerMenu-group {
-        padding: 7px 0;
-        border-bottom: 1px solid lightgray;
-        cursor: pointer;
-
-        &:first-child {
-            padding-top: 0;
-        }
-
-        &:last-child {
-            border: none;
-            text-align: center;
-            padding: 12px 0 0 0;
-        }
-    }
-
-    label {
-        color: $main-accent;
-        font-weight: normal;
-        letter-spacing: 2px;
-        text-transform: uppercase;
-    }
-
-    input {
-        color: black;
-        width: 100%;
-    }
-
-    .columnMenu-option {
-        cursor: pointer;
-        padding: 3px;
-        background-color: white;
-        transition: background-color 0.2s;
-
-        &:hover {
-            background-color: $light-color-secondary;
-        }
-
-        &.active {
-            font-weight: bold;
-            border: 2px solid $light-color-secondary;
-        }
-
-        svg {
-            color: gray;
-            margin-right: 5px;
-            width: 10px;
-        }
-    }
-
-    .keys-dropdown {
-        position: relative;
-        //width: 100%;
-        background-color: white;
-
-        input {
-            border: 2px solid $light-color-secondary;
-            padding: 3px;
-            height: 28px;
-            font-weight: bold;
-            letter-spacing: "2px";
-            text-transform: "uppercase";
-
-            &:focus {
-                font-weight: normal;
-            }
-        }
-
-        .keys-options-wrapper {
-            width: 100%;
-            max-height: 150px;
-            overflow-y: scroll;
-            position: absolute;
-            top: 28px;
-            box-shadow: 0 10px 16px rgba(0, 0, 0, 0.1);
-            background-color: white;
-
-            .key-option {
-                background-color: white;
-                border: 1px solid lightgray;
-                padding: 2px 3px;
-
-                &:not(:first-child) {
-                    border-top: 0;
-                }
-
-                &:hover {
-                    background-color: $light-color-secondary;
-                }
-            }
-        }
-    }
-
-    .columnMenu-colors {
-        display: flex;
-        justify-content: space-between;
-        flex-wrap: wrap;
-
-        .columnMenu-colorPicker {
-            cursor: pointer;
-            width: 20px;
-            height: 20px;
-            border-radius: 10px;
-
-            &.active {
-                border: 2px solid white;
-                box-shadow: 0 0 0 2px lightgray;
-            }
-        }
-    }
-}
-
-.collectionSchema-row {
-    height: 100%;
-    background-color: white;
-
-    &.row-focused .rt-td {
-        background-color: #bfffc0; //$light-color-secondary;
-    }
-
-    &.row-wrapped {
-        .rt-td {
-            white-space: normal;
-        }
-    }
-
-    .row-dragger {
-        display: flex;
-        justify-content: space-around;
-        //flex: 50 0 auto;
-        width: 0;
-        max-width: 50px;
-        //height: 100%;
-        min-height: 30px;
-        align-items: center;
-        color: lightgray;
-        background-color: white;
-        transition: color 0.1s ease;
-
-        .row-option {
-            // padding: 5px;
-            cursor: pointer;
-            position: absolute;
-            transition: color 0.1s ease;
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            z-index: 2;
-
-            &:hover {
-                color: gray;
-            }
-        }
-    }
-
-    .collectionSchema-row-wrapper {
-
-        &.row-above {
-            border-top: 1px solid red;
-        }
-
-        &.row-below {
-            border-bottom: 1px solid red;
-        }
-
-        &.row-inside {
-            border: 1px solid red;
-        }
-
-        .row-dragging {
-            background-color: blue;
-        }
-    }
-}
-
-.collectionSchemaView-cellContainer {
-    width: 100%;
-    height: unset;
-}
-
-.collectionSchemaView-cellWrapper {
-    height: 100%;
-    padding: 4px;
-    text-align: left;
-    padding-left: 19px;
-
-    position: relative;
-
-    &:focus {
-        outline: none;
-    }
-
-    &.editing {
-        padding: 0;
-
-        input {
-            outline: 0;
-            border: none;
-            background-color: rgb(255, 217, 217);
-            width: 100%;
-            height: 100%;
-            padding: 2px 3px;
-            min-height: 26px;
-        }
-    }
-
-    &.focused {
-
-        &.inactive {
-            border: none;
-        }
-    }
-
-    p {
-        width: 100%;
-        height: 100%;
-    }
-
-    &:hover .collectionSchemaView-cellContents-docExpander {
-        display: block;
-    }
-
-
-    .collectionSchemaView-cellContents-document {
-        display: inline-block;
-    }
-
-    .collectionSchemaView-cellContents-docButton {
-        float: right;
-        width: "15px";
-        height: "15px";
-    }
-
-    .collectionSchemaView-dropdownWrapper {
-
-        border: grey;
-        border-style: solid;
-        border-width: 1px;
-        height: 30px;
-
-        .collectionSchemaView-dropdownButton {
-
-            //display: inline-block;
-            float: left;
-            height: 100%;
-
-
-        }
-
-        .collectionSchemaView-dropdownText {
-            display: inline-block;
-            //float: right;
-            height: 100%;
-            display: "flex";
-            font-size: 13;
-            justify-content: "center";
-            align-items: "center";
-        }
-
-    }
-
-    .collectionSchemaView-dropdownContainer {
-        position: absolute;
-        border: 1px solid rgba(0, 0, 0, 0.04);
-        box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14);
-
-        .collectionSchemaView-dropdownOption:hover {
-            background-color: rgba(0, 0, 0, 0.14);
-            cursor: pointer;
-        }
-    }
-}
-
-.collectionSchemaView-cellContents-docExpander {
-    height: 30px;
-    width: 30px;
-    display: none;
-    position: absolute;
-    top: 0;
-    right: 0;
-    background-color: lightgray;
-
-}
-
-.doc-drag-over {
-    background-color: red;
-}
-
-.collectionSchemaView-toolbar {
-    z-index: 100;
-}
-
-.collectionSchemaView-toolbar {
-    height: 30px;
-    display: flex;
-    justify-content: flex-end;
-    padding: 0 10px;
-    border-bottom: 2px solid gray;
-
-    .collectionSchemaView-toolbar-item {
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-    }
-}
-
-#preview-schema-checkbox-div {
-    margin-left: 20px;
-    font-size: 12px;
-}
-
-.collectionSchemaView-table {
-    width: 100%;
-    height: 100%;
-    overflow: auto;
-    padding: 3px;
-}
-
-.rt-td.rt-expandable {
-    overflow: visible;
-    position: relative;
-    height:100%;
-    z-index: 1;
-}
-.reactTable-sub {
-    background-color: rgb(252, 252, 252);
-    width: 100%;
-
-    .rt-thead {
-        display: none;
-    }
-
-    .row-dragger {
-        background-color: rgb(252, 252, 252);
-    }
-
-    .rt-table {
-        background-color: rgb(252, 252, 252);
-    }
-
-    .collectionSchemaView-table {
-        width: 100%; 
-        border: solid 1px;
-        overflow: visible;
-        padding: 0px;
-    }
-}
-
-.collectionSchemaView-expander {
-    height: 100%;
-    min-height: 30px;
-    position: absolute;
-    color: gray;
-    width: 20;
-    height: auto;
-    left: 55;
-
-    svg {
-        position: absolute;
-        top: 50%;
-        left: 10;
-        transform: translate(-50%, -50%);
-    }
-}
-
-.collectionSchemaView-addRow {
-    color: gray;
-    letter-spacing: 2px;
-    text-transform: uppercase;
-    cursor: pointer;
-    font-size: 10.5px;
-    margin-left: 50px;
-    margin-top: 10px;
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
deleted file mode 100644
index b33c437a9..000000000
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ /dev/null
@@ -1,575 +0,0 @@
-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 "react-table/react-table.css";
-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, emptyPath, returnFalse, setupMoveUpEvents, returnEmptyDoclist, returnTrue } from "../../../Utils";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globalCssVariables.scss';
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import '../DocumentDecorations.scss';
-import { DocumentView } from "../nodes/DocumentView";
-import { DefaultStyleProvider } from "../StyleProvider";
-import "./CollectionSchemaView.scss";
-import { CollectionSubView } from "./CollectionSubView";
-import { SchemaTable } from "./SchemaTable";
-import { DocUtils } from "../../documents/Documents";
-// 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(doc => doc) {
-    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); }}
-                >Delete 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.SelectSchemaView(this, 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}
-                    fitContentsToDoc={returnTrue}
-                    freezeDimensions={true}
-                    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.docFilters}
-                    docRangeFilters={this.docRangeFilters}
-                    searchFilterDocs={this.searchFilterDocs}
-                    styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
-                    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: name === "collectionSchemaView-searchContainer" ? "auto" : 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>;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
deleted file mode 100644
index 0c69ee030..000000000
--- a/src/client/views/collections/SchemaTable.tsx
+++ /dev/null
@@ -1,599 +0,0 @@
-import React = require("react");
-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 ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
-import "react-table/react-table.css";
-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, emptyPath, 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 { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globalCssVariables.scss';
-import { ContextMenu } from "../ContextMenu";
-import '../DocumentDecorations.scss';
-import { DocumentView } from "../nodes/DocumentView";
-import { DefaultStyleProvider } from "../StyleProvider";
-import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
-import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
-import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
-import "./CollectionSchemaView.scss";
-import { CollectionView } from "./CollectionView";
-
-
-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;
-    onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
-    addDocTab: (document: Doc, where: string) => boolean;
-    pinToPres: (document: Doc) => 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: "hand" }}>
-                    <FontAwesomeIcon icon={sortIcon} size="lg" />
-                </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 = () => {
-        let index = 0;
-        let found = this.props.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
-        while (found) {
-            index++;
-            found = this.props.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
-        }
-        this.props.columns.push(new SchemaHeaderField(`New field ${index ? "(" + index + ")" : ""}`, "#f1efeb"));
-    }
-
-    @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}
-            SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
-                <div 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, "add:right");
-    }
-
-    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 ? 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}
-                        layerProvider={undefined}
-                        docViewPath={returnEmptyDoclist}
-                        freezeDimensions={true}
-                        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>;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/schemaView/CollectionSchemaCells.tsx b/src/client/views/collections/schemaView/CollectionSchemaCells.tsx
new file mode 100644
index 000000000..2549beaae
--- /dev/null
+++ b/src/client/views/collections/schemaView/CollectionSchemaCells.tsx
@@ -0,0 +1,584 @@
+import React = require("react");
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import DatePicker from "react-datepicker";
+import "react-datepicker/dist/react-datepicker.css";
+import { CellInfo } from "react-table";
+import "react-table/react-table.css";
+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, NumCast, StrCast } from "../../../../fields/Types";
+import { ImageField } from "../../../../fields/URLField";
+import { Utils, emptyFunction } 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 '../DocumentDecorations.scss';
+import { EditableView } from "../../EditableView";
+import { MAX_ROW_HEIGHT } from '../../globalCssVariables.scss';
+import { DocumentIconContainer } from "../../nodes/DocumentIcon";
+import { OverlayView } from "../../OverlayView";
+import "./CollectionSchemaView.scss";
+import { CollectionView } from "../CollectionView";
+const path = require('path');
+
+export interface CellProps {
+    row: number;
+    col: number;
+    rowProps: CellInfo;
+    CollectionView: Opt<CollectionView>;
+    ContainingCollection: Opt<CollectionView>;
+    Document: Doc;
+    fieldKey: string;
+    renderDepth: number;
+    addDocTab: (document: Doc, where: string) => boolean;
+    pinToPres: (document: Doc) => void;
+    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined,
+        addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+    isFocused: boolean;
+    changeFocusedCellByIndex: (row: number, col: number) => void;
+    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;
+    showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void;
+}
+
+@observer
+export class CollectionSchemaCell extends React.Component<CellProps> {
+    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;
+    protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
+    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 (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 => {
+        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);
+    }
+
+    @action
+    onPointerDown = async (e: React.PointerEvent): Promise<void> => {
+        this.onItemDown(e);
+        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)) {
+            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) => {
+        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;
+        doc[this.renderFieldKey] = res.result;
+        return true;
+    }
+
+    private drop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.complete.docDragData) {
+            if (de.complete.docDragData.draggedDocuments.length === 1) {
+                this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
+            }
+            else {
+                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) => {
+        this._dropDisposer?.();
+        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() { return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); }
+    onItemDown = async (e: React.PointerEvent) => {
+        if (this.props.Document._searchDoc) {
+            const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
+            const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
+            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext,
+                undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
+        }
+    }
+    renderCellWithType(type: string | undefined) {
+        const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+
+        const fieldKey = this.renderFieldKey;
+        const field = this._rowDoc[fieldKey];
+
+        const onPointerEnter = (e: React.PointerEvent): void => {
+            if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
+                dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
+            }
+        };
+        const onPointerLeave = (e: React.PointerEvent): void => {
+            dragRef.current!.className = "collectionSchemaView-cellContainer";
+        };
+
+        let contents = Field.toString(field as Field);
+        contents = contents === "" ? "--" : contents;
+
+        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() !== "") {
+            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 (start !== -1) {
+                positions.push(start);
+            }
+            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);
+            }
+            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 (let s of value) {
+                                            if (isNaN(parseInt(s))) {
+                                                inputIsNum = false;
+                                            }
+                                        }
+                                        // check if the input is a boolean
+                                        let 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
+                                            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 (let 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 !== value.length || 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 !== value.length || 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) => {
+                                    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> { 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: false,
+            transformer: DocumentIconContainer.getTransformer()
+        });
+
+        const results = script.compiled && script.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 => {
+        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() {
+        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, "add:right")} 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 (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
+        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here
+
+        const ext = path.extname(url.href);
+        return url.href.replace(ext, "_o" + path.extname(url.href));
+    }
+
+    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;
+        const url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+
+        const aspect = Doc.NativeAspect(this._rowDoc);
+        let width = Math.min(75, this.props.rowProps.width);
+        const height = Math.min(75, width / aspect);
+        width = height * aspect;
+
+        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;
+    @observable private _text = "select an item";
+    @observable private _selectedNum = 0;
+
+    @action
+    onSetValue = (value: string) => {
+        // change if its 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) => {
+        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 (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 {
+    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>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx b/src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx
new file mode 100644
index 000000000..b825d6d96
--- /dev/null
+++ b/src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx
@@ -0,0 +1,518 @@
+import React = require("react");
+import { IconProp, library } from "@fortawesome/fontawesome-svg-core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast, Opt } from "../../../../fields/Doc";
+import { listSpec } from "../../../../fields/Schema";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { ScriptField } from "../../../../fields/ScriptField";
+import { Cast, StrCast } from "../../../../fields/Types";
+import { undoBatch } from "../../../util/UndoManager";
+import { SearchBox } from "../../search/SearchBox";
+import { ColumnType } from "../CollectionSchemaView";
+import "./CollectionSchemaView.scss";
+import { CollectionView } from "../CollectionView";
+
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+
+export interface AddColumnHeaderProps {
+    createColumn: () => void;
+}
+
+@observer
+export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHeaderProps> {
+    render() {
+        return (
+            <button className="add-column" onClick={() => this.props.createColumn()}><FontAwesomeIcon icon="plus" size="sm" /></button>
+        );
+    }
+}
+
+
+export interface ColumnMenuProps {
+    columnField: SchemaHeaderField;
+    // keyValue: string;
+    possibleKeys: string[];
+    existingKeys: string[];
+    // keyType: ColumnType;
+    typeConst: boolean;
+    menuButtonContent: JSX.Element;
+    addNew: boolean;
+    onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
+    setIsEditing: (isEditing: boolean) => void;
+    deleteColumn: (column: string) => void;
+    onlyShowOptions: boolean;
+    setColumnType: (column: SchemaHeaderField, type: ColumnType) => void;
+    setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void;
+    anchorPoint?: any;
+    setColumnColor: (column: SchemaHeaderField, color: string) => void;
+}
+@observer
+export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> {
+    @observable private _isOpen: boolean = false;
+    @observable private _node: HTMLDivElement | null = null;
+
+    componentDidMount() { document.addEventListener("pointerdown", this.detectClick); }
+
+    componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); }
+
+    @action
+    detectClick = (e: PointerEvent) => {
+        !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false);
+    }
+
+    @action
+    toggleIsOpen = (): void => {
+        this.props.setIsEditing(this._isOpen = !this._isOpen);
+    }
+
+    changeColumnType = (type: ColumnType) => {
+        this.props.setColumnType(this.props.columnField, type);
+    }
+
+    changeColumnSort = (desc: boolean | undefined) => {
+        this.props.setColumnSort(this.props.columnField, desc);
+    }
+
+    changeColumnColor = (color: string) => {
+        this.props.setColumnColor(this.props.columnField, color);
+    }
+
+    @action
+    setNode = (node: HTMLDivElement): void => {
+        if (node) {
+            this._node = node;
+        }
+    }
+
+    renderTypes = () => {
+        if (this.props.typeConst) return (null);
+
+        const type = this.props.columnField.type;
+        return (
+            <div className="collectionSchema-headerMenu-group">
+                <label>Column type:</label>
+                <div className="columnMenu-types">
+                    <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Any)}>
+                        <FontAwesomeIcon icon={"align-justify"} size="sm" />
+                        Any
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Number)}>
+                        <FontAwesomeIcon icon={"hashtag"} size="sm" />
+                        Number
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.String)}>
+                        <FontAwesomeIcon icon={"font"} size="sm" />
+                        Text
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Boolean)}>
+                        <FontAwesomeIcon icon={"check-square"} size="sm" />
+                        Checkbox
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.List)}>
+                        <FontAwesomeIcon icon={"list-ul"} size="sm" />
+                        List
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}>
+                        <FontAwesomeIcon icon={"file"} size="sm" />
+                        Document
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Image)}>
+                        <FontAwesomeIcon icon={"image"} size="sm" />
+                        Image
+                    </div>
+                    <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Date)}>
+                        <FontAwesomeIcon icon={"calendar"} size="sm" />
+                        Date
+                    </div>
+                </div>
+            </div >
+        );
+    }
+
+    renderSorting = () => {
+        const sort = this.props.columnField.desc;
+        return (
+            <div className="collectionSchema-headerMenu-group">
+                <label>Sort by:</label>
+                <div className="columnMenu-sort">
+                    <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.changeColumnSort(true)}>
+                        <FontAwesomeIcon icon="sort-amount-down" size="sm" />
+                        Sort descending
+                    </div>
+                    <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.changeColumnSort(false)}>
+                        <FontAwesomeIcon icon="sort-amount-up" size="sm" />
+                        Sort ascending
+                    </div>
+                    <div className="columnMenu-option" onClick={() => this.changeColumnSort(undefined)}>
+                        <FontAwesomeIcon icon="times" size="sm" />
+                        Clear sorting
+                    </div>
+                </div>
+            </div>
+        );
+    }
+
+    renderColors = () => {
+        const selected = this.props.columnField.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.changeColumnColor(pink!)}></div>
+                    <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div>
+                    <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div>
+                    <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div>
+                    <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div>
+                    <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div>
+                </div>
+            </div>
+        );
+    }
+
+    renderContent = () => {
+        return (
+            <div className="collectionSchema-header-menuOptions">
+                {this.props.onlyShowOptions ? <></> :
+                    <>
+                        {this.renderTypes()}
+                        {this.renderSorting()}
+                        {this.renderColors()}
+                        <div className="collectionSchema-headerMenu-group">
+                            <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Delete Column</button>
+                        </div>
+                    </>
+                }
+            </div>
+        );
+    }
+
+    render() {
+        return (
+            <div className="collectionSchema-header-menu" ref={this.setNode}>
+                <Flyout anchorPoint={this.props.anchorPoint ? this.props.anchorPoint : anchorPoints.TOP_CENTER} content={this.renderContent()}>
+                    <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div>
+                </ Flyout >
+            </div>
+        );
+    }
+}
+
+
+export interface KeysDropdownProps {
+    keyValue: string;
+    possibleKeys: string[];
+    existingKeys: string[];
+    canAddNew: boolean;
+    addNew: boolean;
+    onSelect: (oldKey: string, newKey: string, addnew: boolean, filter?: string) => void;
+    setIsEditing: (isEditing: boolean) => void;
+    width?: string;
+    docs?: Doc[];
+    Document: Doc;
+    dataDoc: Doc | undefined;
+    fieldKey: string;
+    ContainingCollectionDoc: Doc | undefined;
+    ContainingCollectionView: Opt<CollectionView>;
+    active?: (outsideReaction?: boolean) => boolean;
+    openHeader: (column: any, screenx: number, screeny: number) => void;
+    col: SchemaHeaderField;
+    icon: IconProp;
+}
+@observer
+export class KeysDropdown extends React.Component<KeysDropdownProps> {
+    @observable private _key: string = this.props.keyValue;
+    @observable private _searchTerm: string = this.props.keyValue;
+    @observable private _isOpen: boolean = false;
+    @observable private _node: HTMLDivElement | null = null;
+    @observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef();
+
+    @action setSearchTerm = (value: string): void => { this._searchTerm = value; };
+    @action setKey = (key: string): void => { this._key = key; };
+    @action setIsOpen = (isOpen: boolean): void => { this._isOpen = isOpen; };
+
+    @action
+    onSelect = (key: string): void => {
+        this.props.onSelect(this._key, key, this.props.addNew);
+        this.setKey(key);
+        this._isOpen = false;
+        this.props.setIsEditing(false);
+    }
+
+    @action
+    setNode = (node: HTMLDivElement): void => {
+        if (node) {
+            this._node = node;
+        }
+    }
+
+    componentDidMount() {
+        document.addEventListener("pointerdown", this.detectClick);
+        const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+        if (filters?.some(filter => filter.split(":")[0] === this._key)) {
+            runInAction(() => this.closeResultsVisibility = "contents");
+        }
+    }
+
+    @action
+    detectClick = (e: PointerEvent): void => {
+        if (this._node && this._node.contains(e.target as Node)) {
+        } else {
+            this._isOpen = false;
+            this.props.setIsEditing(false);
+        }
+    }
+
+    private tempfilter: string = "";
+    @undoBatch
+    onKeyDown = (e: React.KeyboardEvent): void => {
+        if (e.key === "Enter") {
+            if (this._searchTerm.includes(":")) {
+                const colpos = this._searchTerm.indexOf(":");
+                const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
+                if (temp === "") {
+                    Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
+                    this.updateFilter();
+                }
+                else {
+                    Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
+                    this.tempfilter = temp;
+                    Doc.setDocFilter(this.props.Document, this._key, temp, "check");
+                    this.props.col.setColor("green");
+                    this.closeResultsVisibility = "contents";
+                }
+            }
+            else {
+                Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
+                this.updateFilter();
+                if (this.showKeys.length) {
+                    this.onSelect(this.showKeys[0]);
+                } else if (this._searchTerm !== "" && this.props.canAddNew) {
+                    this.setSearchTerm(this._searchTerm || this._key);
+                    this.onSelect(this._searchTerm);
+                }
+            }
+        }
+    }
+
+    onChange = (val: string): void => {
+        this.setSearchTerm(val);
+    }
+
+    @action
+    onFocus = (e: React.FocusEvent): void => {
+        this._isOpen = true;
+        this.props.setIsEditing(true);
+    }
+
+    @computed get showKeys() {
+        const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
+        const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+        const showKeys = new Set<string>();
+        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode ||
+            whitelistKeys.includes(key)
+            || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
+        return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
+    }
+    @action
+    renderOptions = (): JSX.Element[] | JSX.Element => {
+        if (!this._isOpen) {
+            this.defaultMenuHeight = 0;
+            return <></>;
+        }
+        const options = this.showKeys.map(key => {
+            return <div key={key} className="key-option" style={{
+                border: "1px solid lightgray",
+                width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
+            }}
+                onPointerDown={e => {
+                    e.stopPropagation();
+                }}
+                onClick={() => {
+                    this.onSelect(key);
+                    this.setSearchTerm("");
+                }}>{key}</div>;
+        });
+
+        // if search term does not already exist as a group type, give option to create new group type
+
+        if (this._key !== this._searchTerm.slice(0, this._key.length)) {
+            if (this._searchTerm !== "" && this.props.canAddNew) {
+                options.push(<div key={""} className="key-option" style={{
+                    border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
+                }}
+                    onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
+                    Create "{this._searchTerm}" key</div>);
+            }
+        }
+
+        if (options.length === 0) {
+            this.defaultMenuHeight = 0;
+        }
+        else {
+            if (this.props.docs) {
+                const panesize = this.props.docs.length * 30;
+                options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
+            }
+            else {
+                options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
+            }
+        }
+        return options;
+    }
+
+    docSafe: Doc[] = [];
+
+    @action
+    renderFilterOptions = (): JSX.Element[] | JSX.Element => {
+        if (!this._isOpen || !this.props.dataDoc) {
+            this.defaultMenuHeight = 0;
+            return <></>;
+        }
+        const keyOptions: string[] = [];
+        const colpos = this._searchTerm.indexOf(":");
+        const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
+        if (this.docSafe.length === 0) {
+            this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
+        }
+        const docs = this.docSafe;
+        docs.forEach((doc) => {
+            const key = StrCast(doc[this._key]);
+            if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") {
+                keyOptions.push(key);
+            }
+        });
+
+        const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+        if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) {
+            this.props.col.setColor("rgb(241, 239, 235)");
+            this.closeResultsVisibility = "none";
+        }
+        for (let i = 0; i < (filters?.length ?? 0) - 1; i++) {
+            if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i].split(":")[1]) === false) {
+                keyOptions.push(filters![i + 1]);
+            }
+        }
+        const options = keyOptions.map(key => {
+            let bool = false;
+            if (filters !== undefined) {
+                const ind = filters.findIndex(filter => filter.split(":")[0] === key);
+                const fields = ind === -1 ? undefined : filters[ind].split(":");
+                bool = fields ? fields[1] === "check" : false;
+            }
+            return <div key={key} className="key-option" style={{
+                border: "1px solid lightgray", paddingLeft: 5, textAlign: "left",
+                width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
+            }}
+            >
+                <input type="checkbox"
+                    onPointerDown={e => e.stopPropagation()}
+                    onClick={e => e.stopPropagation()}
+                    onChange={(e) => {
+                        e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, "remove");
+                        e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log("");
+                        e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter();
+                        e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove");
+                    }}
+                    checked={bool}
+                />
+                <span style={{ paddingLeft: 4 }}>
+                    {key}
+                </span>
+
+            </div>;
+        });
+        if (options.length === 0) {
+            this.defaultMenuHeight = 0;
+        }
+        else {
+            if (this.props.docs) {
+                const panesize = this.props.docs.length * 30;
+                options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
+            }
+            else {
+                options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
+            }
+
+        }
+        return options;
+    }
+
+    @observable defaultMenuHeight = 0;
+
+
+    updateFilter() {
+        const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+        if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) {
+            this.props.col.setColor("rgb(241, 239, 235)");
+            this.closeResultsVisibility = "none";
+        }
+    }
+
+    @computed get scriptField() {
+        const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
+        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+        return script ? () => script : undefined;
+    }
+    filterBackground = () => "rgba(105, 105, 105, 0.432)";
+    @observable filterOpen: boolean | undefined = undefined;
+    closeResultsVisibility: string = "none";
+
+    removeFilters = (e: React.PointerEvent): void => {
+        const keyOptions: string[] = [];
+        if (this.docSafe.length === 0 && this.props.dataDoc) {
+            this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
+        }
+        const docs = this.docSafe;
+        docs.forEach((doc) => {
+            const key = StrCast(doc[this._key]);
+            if (keyOptions.includes(key) === false) {
+                keyOptions.push(key);
+            }
+        });
+
+        Doc.setDocFilter(this.props.Document, this._key, "", "remove");
+        this.props.col.setColor("rgb(241, 239, 235)");
+        this.closeResultsVisibility = "none";
+    }
+    render() {
+        return (
+            <div style={{ display: "flex" }} ref={this.setNode}>
+                <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
+
+                {/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
+                    runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen })
+                }} /> */}
+
+                <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
+                    <input className="keys-search" style={{ width: "100%" }}
+                        ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
+                        onChange={e => this.onChange(e.target.value)}
+                        onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
+                        onFocus={this.onFocus} ></input>
+                    <div style={{ display: this.closeResultsVisibility }}>
+                        <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
+                            style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
+                    </div>
+                    {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{
+                        width: this.props.width, maxWidth: this.props.width, height: "auto",
+                    }}>
+                        {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()}
+                    </div>}
+                </div >
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/collections/schemaView/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/schemaView/CollectionSchemaMovableTableHOC.tsx
new file mode 100644
index 000000000..149677976
--- /dev/null
+++ b/src/client/views/collections/schemaView/CollectionSchemaMovableTableHOC.tsx
@@ -0,0 +1,261 @@
+import React = require("react");
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action } from "mobx";
+import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
+import { Doc } from "../../../fields/Doc";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+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 "./CollectionSchemaView.scss";
+
+export interface MovableColumnProps {
+    columnRenderer: TableCellRenderer;
+    columnValue: SchemaHeaderField;
+    allColumns: SchemaHeaderField[];
+    reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
+    ScreenToLocalTransform: () => Transform;
+}
+export class MovableColumn extends React.Component<MovableColumnProps> {
+    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+    private _colDropDisposer?: DragManager.DragDropDisposer;
+    private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
+    private _sensitivity: number = 16;
+    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+
+    onPointerEnter = (e: React.PointerEvent): void => {
+        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);
+    }
+    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.right - rect.left) / 2), rect.top);
+        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));
+        }
+    }
+
+    colDrop = (e: Event, de: DragManager.DropEvent) => {
+        document.removeEventListener("pointermove", this.onDragMove, true);
+        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.right - rect.left) / 2), rect.top);
+        const before = x[0] < bounds[0];
+        const colDragData = de.complete.columnDragData;
+        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();
+
+            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 (e.buttons === 1) {
+            const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+            if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
+                document.removeEventListener("pointermove", this.onPointerMove);
+                e.stopPropagation();
+
+                document.addEventListener("pointermove", onRowMove);
+                document.addEventListener("pointerup", onRowUp);
+            }
+        }
+    }
+
+    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 (!(e.target as any)?.tagName.includes("INPUT")) {
+            this._startDragPosition = { x: dx, y: dy };
+            document.addEventListener("pointermove", this.onPointerMove);
+        }
+    }
+
+
+    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>
+        );
+    }
+}
+
+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<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;
+
+        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;
+    }
+
+    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);
+    }
+
+    @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;
+
+        if (!rowInfo) {
+            return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
+        }
+
+        const { original } = rowInfo;
+        const doc = FieldValue(Cast(original, Doc));
+
+        if (!doc) return (null);
+
+        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";
+
+        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" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
+                            <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
+                            <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+                        </div>
+                        {children}
+                    </ReactTableDefaults.TrComponent>
+                </div>
+            </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/schemaView/CollectionSchemaView.scss b/src/client/views/collections/schemaView/CollectionSchemaView.scss
new file mode 100644
index 000000000..b57fee0e4
--- /dev/null
+++ b/src/client/views/collections/schemaView/CollectionSchemaView.scss
@@ -0,0 +1,552 @@
+@import "../../globalCssVariables";
+.collectionSchemaView-container {
+    border-width: $COLLECTION_BORDER_WIDTH;
+    border-color: $intermediate-color;
+    border-style: solid;
+    border-radius: $border-radius;
+    box-sizing: border-box;
+    position: relative;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    margin-top: 0;
+    transition: top 0.5s;
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: nowrap;
+    touch-action: none;
+    div {
+        touch-action: none;
+    }
+    .collectionSchemaView-tableContainer {
+        width: 100%;
+        height: 100%;
+    }
+    .collectionSchemaView-dividerDragger {
+        position: relative;
+        height: 100%;
+        width: $SCHEMA_DIVIDER_WIDTH;
+        z-index: 20;
+        right: 0;
+        top: 0;
+        background: gray;
+        cursor: col-resize;
+    }
+    // .documentView-node:first-child {
+    //     background: $light-color;
+    // }
+}
+
+.collectionSchemaView-searchContainer {
+    border-width: $COLLECTION_BORDER_WIDTH;
+    border-color: $intermediate-color;
+    border-style: solid;
+    border-radius: $border-radius;
+    box-sizing: border-box;
+    position: relative;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    margin-top: 0;
+    transition: top 0.5s;
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: nowrap;
+    touch-action: none;
+    padding: 2px;
+    div {
+        touch-action: none;
+    }
+    .collectionSchemaView-tableContainer {
+        width: 100%;
+        height: 100%;
+    }
+    .collectionSchemaView-dividerDragger {
+        position: relative;
+        height: 100%;
+        width: 20px;
+        z-index: 20;
+        right: 0;
+        top: 0;
+        background: gray;
+        cursor: col-resize;
+    }
+    // .documentView-node:first-child {
+    //     background: $light-color;
+    // }
+}
+
+.ReactTable {
+    width: 100%;
+    background: white;
+    box-sizing: border-box;
+    border: none !important;
+    float: none !important;
+    .rt-table {
+        height: 100%;
+        display: -webkit-inline-box;
+        direction: ltr;
+        overflow: visible;
+    }
+    .rt-noData {
+        display: none;
+    }
+    .rt-thead {
+        width: 100%;
+        z-index: 100;
+        overflow-y: visible;
+        &.-header {
+            font-size: 12px;
+            height: 30px;
+            box-shadow: none;
+            z-index: 100;
+            overflow-y: visible;
+        }
+        .rt-resizable-header-content {
+            height: 100%;
+            overflow: visible;
+        }
+        .rt-th {
+            padding: 0;
+            border: solid lightgray;
+            border-width: 0 1px;
+            border-bottom: 2px solid lightgray;
+        }
+    }
+    .rt-th {
+        font-size: 13px;
+        text-align: center;
+        &:last-child {
+            overflow: visible;
+        }
+    }
+    .rt-tbody {
+        width: 100%;
+        direction: rtl;
+        overflow: visible;
+        .rt-td {
+            border-right: 1px solid rgba(0, 0, 0, 0.2);
+        }
+    }
+    .rt-tr-group {
+        direction: ltr;
+        flex: 0 1 auto;
+        min-height: 30px;
+        border: 0 !important;
+    }
+    .rt-tr {
+        width: 100%;
+        min-height: 30px;
+    }
+    .rt-td {
+        padding: 0;
+        font-size: 13px;
+        text-align: center;
+        white-space: nowrap;
+        display: flex;
+        align-items: center;
+        .imageBox-cont {
+            position: relative;
+            max-height: 100%;
+        }
+        .imageBox-cont img {
+            object-fit: contain;
+            max-width: 100%;
+            height: 100%;
+        }
+        .videoBox-cont {
+            object-fit: contain;
+            width: auto;
+            height: 100%;
+        }
+    }
+    .rt-td.rt-expandable {
+        display: flex;
+        align-items: center;
+        height: inherit;
+    }
+    .rt-resizer {
+        width: 8px;
+        right: -4px;
+    }
+    .rt-resizable-header {
+        padding: 0;
+        height: 30px;
+    }
+    .rt-resizable-header:last-child {
+        overflow: visible;
+        .rt-resizer {
+            width: 5px !important;
+        }
+    }
+}
+
+.documentView-node-topmost {
+    text-align: left;
+    transform-origin: center top;
+    display: inline-block;
+}
+
+.collectionSchema-col {
+    height: 100%;
+}
+
+.collectionSchema-header-menu {
+    height: auto;
+    z-index: 100;
+    position: absolute;
+    background: white;
+    padding: 5px;
+    position: fixed;
+    background: white;
+    border: black 1px solid;
+    .collectionSchema-header-toggler {
+        z-index: 100;
+        width: 100%;
+        height: 100%;
+        padding: 4px;
+        letter-spacing: 2px;
+        text-transform: uppercase;
+        svg {
+            margin-right: 4px;
+        }
+    }
+}
+
+.collectionSchemaView-header {
+    height: 100%;
+    color: gray;
+    z-index: 100;
+    overflow-y: visible;
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: wrap;
+}
+
+button.add-column {
+    width: 28px;
+}
+
+.collectionSchema-header-menuOptions {
+    color: black;
+    width: 180px;
+    text-align: left;
+    .collectionSchema-headerMenu-group {
+        padding: 7px 0;
+        border-bottom: 1px solid lightgray;
+        cursor: pointer;
+        &:first-child {
+            padding-top: 0;
+        }
+        &:last-child {
+            border: none;
+            text-align: center;
+            padding: 12px 0 0 0;
+        }
+    }
+    label {
+        color: $main-accent;
+        font-weight: normal;
+        letter-spacing: 2px;
+        text-transform: uppercase;
+    }
+    input {
+        color: black;
+        width: 100%;
+    }
+    .columnMenu-option {
+        cursor: pointer;
+        padding: 3px;
+        background-color: white;
+        transition: background-color 0.2s;
+        &:hover {
+            background-color: $light-color-secondary;
+        }
+        &.active {
+            font-weight: bold;
+            border: 2px solid $light-color-secondary;
+        }
+        svg {
+            color: gray;
+            margin-right: 5px;
+            width: 10px;
+        }
+    }
+    .keys-dropdown {
+        position: relative;
+        //width: 100%;
+        background-color: white;
+        input {
+            border: 2px solid $light-color-secondary;
+            padding: 3px;
+            height: 28px;
+            font-weight: bold;
+            letter-spacing: "2px";
+            text-transform: "uppercase";
+            &:focus {
+                font-weight: normal;
+            }
+        }
+        .keys-options-wrapper {
+            width: 100%;
+            max-height: 150px;
+            overflow-y: scroll;
+            position: absolute;
+            top: 28px;
+            box-shadow: 0 10px 16px rgba(0, 0, 0, 0.1);
+            background-color: white;
+            .key-option {
+                background-color: white;
+                border: 1px solid lightgray;
+                padding: 2px 3px;
+                &:not(:first-child) {
+                    border-top: 0;
+                }
+                &:hover {
+                    background-color: $light-color-secondary;
+                }
+            }
+        }
+    }
+    .columnMenu-colors {
+        display: flex;
+        justify-content: space-between;
+        flex-wrap: wrap;
+        .columnMenu-colorPicker {
+            cursor: pointer;
+            width: 20px;
+            height: 20px;
+            border-radius: 10px;
+            &.active {
+                border: 2px solid white;
+                box-shadow: 0 0 0 2px lightgray;
+            }
+        }
+    }
+}
+
+.collectionSchema-row {
+    height: 100%;
+    background-color: white;
+    &.row-focused .rt-td {
+        background-color: #bfffc0; //$light-color-secondary;
+    }
+    &.row-wrapped {
+        .rt-td {
+            white-space: normal;
+        }
+    }
+    .row-dragger {
+        display: flex;
+        justify-content: space-around;
+        //flex: 50 0 auto;
+        width: 0;
+        max-width: 50px;
+        //height: 100%;
+        min-height: 30px;
+        align-items: center;
+        color: lightgray;
+        background-color: white;
+        transition: color 0.1s ease;
+        .row-option {
+            // padding: 5px;
+            cursor: pointer;
+            position: absolute;
+            transition: color 0.1s ease;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            z-index: 2;
+            &:hover {
+                color: gray;
+            }
+        }
+    }
+    .collectionSchema-row-wrapper {
+        &.row-above {
+            border-top: 1px solid red;
+        }
+        &.row-below {
+            border-bottom: 1px solid red;
+        }
+        &.row-inside {
+            border: 1px solid red;
+        }
+        .row-dragging {
+            background-color: blue;
+        }
+    }
+}
+
+.collectionSchemaView-cellContainer {
+    width: 100%;
+    height: unset;
+}
+
+.collectionSchemaView-cellWrapper {
+    height: 100%;
+    padding: 4px;
+    text-align: left;
+    padding-left: 19px;
+    position: relative;
+    &:focus {
+        outline: none;
+    }
+    &.editing {
+        padding: 0;
+        input {
+            outline: 0;
+            border: none;
+            background-color: rgb(255, 217, 217);
+            width: 100%;
+            height: 100%;
+            padding: 2px 3px;
+            min-height: 26px;
+        }
+    }
+    &.focused {
+        &.inactive {
+            border: none;
+        }
+    }
+    p {
+        width: 100%;
+        height: 100%;
+    }
+    &:hover .collectionSchemaView-cellContents-docExpander {
+        display: block;
+    }
+    .collectionSchemaView-cellContents-document {
+        display: inline-block;
+    }
+    .collectionSchemaView-cellContents-docButton {
+        float: right;
+        width: "15px";
+        height: "15px";
+    }
+    .collectionSchemaView-dropdownWrapper {
+        border: grey;
+        border-style: solid;
+        border-width: 1px;
+        height: 30px;
+        .collectionSchemaView-dropdownButton {
+            //display: inline-block;
+            float: left;
+            height: 100%;
+        }
+        .collectionSchemaView-dropdownText {
+            display: inline-block;
+            //float: right;
+            height: 100%;
+            display: "flex";
+            font-size: 13;
+            justify-content: "center";
+            align-items: "center";
+        }
+    }
+    .collectionSchemaView-dropdownContainer {
+        position: absolute;
+        border: 1px solid rgba(0, 0, 0, 0.04);
+        box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14);
+        .collectionSchemaView-dropdownOption:hover {
+            background-color: rgba(0, 0, 0, 0.14);
+            cursor: pointer;
+        }
+    }
+}
+
+.collectionSchemaView-cellContents-docExpander {
+    height: 30px;
+    width: 30px;
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 0;
+    background-color: lightgray;
+}
+
+.doc-drag-over {
+    background-color: red;
+}
+
+.collectionSchemaView-toolbar {
+    z-index: 100;
+}
+
+.collectionSchemaView-toolbar {
+    height: 30px;
+    display: flex;
+    justify-content: flex-end;
+    padding: 0 10px;
+    border-bottom: 2px solid gray;
+    .collectionSchemaView-toolbar-item {
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+    }
+}
+
+#preview-schema-checkbox-div {
+    margin-left: 20px;
+    font-size: 12px;
+}
+
+.collectionSchemaView-table {
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    padding: 3px;
+}
+
+.rt-td.rt-expandable {
+    overflow: visible;
+    position: relative;
+    height: 100%;
+    z-index: 1;
+}
+
+.reactTable-sub {
+    background-color: rgb(252, 252, 252);
+    width: 100%;
+    .rt-thead {
+        display: none;
+    }
+    .row-dragger {
+        background-color: rgb(252, 252, 252);
+    }
+    .rt-table {
+        background-color: rgb(252, 252, 252);
+    }
+    .collectionSchemaView-table {
+        width: 100%;
+        border: solid 1px;
+        overflow: visible;
+        padding: 0px;
+    }
+}
+
+.collectionSchemaView-expander {
+    height: 100%;
+    min-height: 30px;
+    position: absolute;
+    color: gray;
+    width: 20;
+    height: auto;
+    left: 55;
+    svg {
+        position: absolute;
+        top: 50%;
+        left: 10;
+        transform: translate(-50%, -50%);
+    }
+}
+
+.collectionSchemaView-addRow {
+    color: gray;
+    letter-spacing: 2px;
+    text-transform: uppercase;
+    cursor: pointer;
+    font-size: 10.5px;
+    margin-left: 50px;
+    margin-top: 10px;
+}
\ No newline at end of file
diff --git a/src/client/views/collections/schemaView/CollectionSchemaView.tsx b/src/client/views/collections/schemaView/CollectionSchemaView.tsx
new file mode 100644
index 000000000..b33c437a9
--- /dev/null
+++ b/src/client/views/collections/schemaView/CollectionSchemaView.tsx
@@ -0,0 +1,575 @@
+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 "react-table/react-table.css";
+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, emptyPath, returnFalse, setupMoveUpEvents, returnEmptyDoclist, returnTrue } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+import { SnappingManager } from "../../util/SnappingManager";
+import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globalCssVariables.scss';
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import '../DocumentDecorations.scss';
+import { DocumentView } from "../nodes/DocumentView";
+import { DefaultStyleProvider } from "../StyleProvider";
+import "./CollectionSchemaView.scss";
+import { CollectionSubView } from "./CollectionSubView";
+import { SchemaTable } from "./SchemaTable";
+import { DocUtils } from "../../documents/Documents";
+// 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(doc => doc) {
+    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); }}
+                >Delete 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.SelectSchemaView(this, 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}
+                    fitContentsToDoc={returnTrue}
+                    freezeDimensions={true}
+                    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.docFilters}
+                    docRangeFilters={this.docRangeFilters}
+                    searchFilterDocs={this.searchFilterDocs}
+                    styleProvider={DefaultStyleProvider}
+                    layerProvider={undefined}
+                    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: name === "collectionSchemaView-searchContainer" ? "auto" : 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>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/schemaView/SchemaTable.tsx b/src/client/views/collections/schemaView/SchemaTable.tsx
new file mode 100644
index 000000000..c305f6806
--- /dev/null
+++ b/src/client/views/collections/schemaView/SchemaTable.tsx
@@ -0,0 +1,599 @@
+import React = require("react");
+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 ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
+import "react-table/react-table.css";
+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, emptyPath, 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 { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../globalCssVariables.scss';
+import { ContextMenu } from "../../ContextMenu";
+import '../DocumentDecorations.scss';
+import { DocumentView } from "../../nodes/DocumentView";
+import { DefaultStyleProvider } from "../../StyleProvider";
+import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
+import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
+import "./CollectionSchemaView.scss";
+import { CollectionView } from "../CollectionView";
+
+
+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;
+    onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
+    addDocTab: (document: Doc, where: string) => boolean;
+    pinToPres: (document: Doc) => 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: "hand" }}>
+                    <FontAwesomeIcon icon={sortIcon} size="lg" />
+                </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 = () => {
+        let index = 0;
+        let found = this.props.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
+        while (found) {
+            index++;
+            found = this.props.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
+        }
+        this.props.columns.push(new SchemaHeaderField(`New field ${index ? "(" + index + ")" : ""}`, "#f1efeb"));
+    }
+
+    @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}
+            SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
+                <div 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, "add:right");
+    }
+
+    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 ? 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}
+                        layerProvider={undefined}
+                        docViewPath={returnEmptyDoclist}
+                        freezeDimensions={true}
+                        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>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f0a54e4ac..ecf4c0901 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -8,7 +8,7 @@ import { emptyPath, OmitKeys, Without } from "../../../Utils";
 import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionSchemaView } from "../collections/CollectionSchemaView";
+import { CollectionSchemaView } from "../collections/schemaView/CollectionSchemaView";
 import { CollectionView } from "../collections/CollectionView";
 import { InkingStroke } from "../InkingStroke";
 import { PresElementBox } from "../presentationview/PresElementBox";
-- 
cgit v1.2.3-70-g09d2

