From e48f447f66f7f50f39be385e0eb09df552f5f503 Mon Sep 17 00:00:00 2001
From: geireann <geireann_lindfield_roberts@brown.edu>
Date: Mon, 12 Jul 2021 13:54:23 -0400
Subject: Changed "schemaView" :arrow_right: "collectionSchema"

So style is consistent with other folders
---
 .../collectionSchema/CollectionSchemaView.tsx      | 575 +++++++++++++++++++++
 1 file changed, 575 insertions(+)
 create mode 100644 src/client/views/collections/collectionSchema/CollectionSchemaView.tsx

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
new file mode 100644
index 000000000..ef28f75c8
--- /dev/null
+++ b/src/client/views/collections/collectionSchema/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 '../../globalCssVariables.scss';
+import { ContextMenu } from "../../ContextMenu";
+import { ContextMenuProps } from "../../ContextMenuItem";
+import '../../../views/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
-- 
cgit v1.2.3-70-g09d2


From 7a8865bbbe7b16219aa63058a159eb4facb06b0b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 23 Jul 2021 10:26:02 -0400
Subject: fixed compile errors

---
 src/client/views/collections/CollectionSchemaView.tsx          |  6 +++---
 .../collections/collectionSchema/CollectionSchemaView.scss     |  2 +-
 .../collections/collectionSchema/CollectionSchemaView.tsx      | 10 +++++-----
 3 files changed, 9 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 18926264d..e1e04915a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -11,7 +11,8 @@ 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 { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
+import { DocUtils } from "../../documents/Documents";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
 import { Transform } from "../../util/Transform";
@@ -24,8 +25,7 @@ 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";
+import { SchemaTable } from "../collections/collectionSchema/SchemaTable";
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
 export enum ColumnType {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 4cc34cd32..40cdcd14b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -1,4 +1,4 @@
-@import "../../globalCssVariables";
+@import "../../global/globalCssVariables.scss";
 .collectionSchemaView-container {
     border-width: $COLLECTION_BORDER_WIDTH;
     border-color: $medium-gray;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index ef28f75c8..585cda729 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -11,21 +11,21 @@ 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 { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
+import { DocUtils } from "../../../documents/Documents";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { SnappingManager } from "../../../util/SnappingManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../globalCssVariables.scss';
+import '../../../views/DocumentDecorations.scss';
 import { ContextMenu } from "../../ContextMenu";
 import { ContextMenuProps } from "../../ContextMenuItem";
-import '../../../views/DocumentDecorations.scss';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
 import { DocumentView } from "../../nodes/DocumentView";
 import { DefaultStyleProvider } from "../../StyleProvider";
-import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "../CollectionSubView";
+import "./CollectionSchemaView.scss";
 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 {
-- 
cgit v1.2.3-70-g09d2


From 70f98c6d4d3e457a05e7c84260bf33580ee0166a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 4 Aug 2021 10:34:25 -0400
Subject: restructured filters again so that filter icons show whether a
 document has a filter or inherits one.  following a link with a view spec
 overrides only locally set filters (not inherited ones).

---
 src/client/views/.DS_Store                         | Bin 10244 -> 10244 bytes
 .../views/collections/CollectionSchemaView.tsx     |   4 +--
 .../views/collections/CollectionStackingView.tsx   |   4 +--
 src/client/views/collections/CollectionSubView.tsx |  29 ++++++++++++---------
 .../views/collections/CollectionTimeView.tsx       |  12 +++------
 src/client/views/collections/CollectionView.tsx    |  14 +++++++---
 src/client/views/collections/TabDocView.tsx        |   9 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   8 ++----
 .../CollectionMulticolumnView.tsx                  |   4 +--
 .../CollectionMultirowView.tsx                     |   4 +--
 .../collectionSchema/CollectionSchemaView.tsx      |   4 +--
 src/client/views/nodes/DocumentView.tsx            |   6 ++---
 12 files changed, 50 insertions(+), 48 deletions(-)

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

diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
index 33e624ef4..e4ac87aad 100644
Binary files a/src/client/views/.DS_Store and b/src/client/views/.DS_Store differ
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index e1e04915a..380f82813 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -413,8 +413,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     isContentActive={returnTrue}
                     isDocumentActive={returnFalse}
                     ScreenToLocalTransform={this.getPreviewTransform}
-                    docFilters={this.docFilters}
-                    docRangeFilters={this.docRangeFilters}
+                    docFilters={this.childDocFilters}
+                    docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
                     layerProvider={undefined}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7aa8dfd56..b9bc83d49 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -239,10 +239,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={stackedDocTransform}
             focus={this.focusDocument}
-            docFilters={this.docFilters}
+            docFilters={this.childDocFilters}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             hideTitle={this.props.childHideTitle?.()}
-            docRangeFilters={this.docRangeFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3b143aeef..e5f266acf 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -8,7 +8,7 @@ import { ScriptField } from "../../../fields/ScriptField";
 import { WebField } from "../../../fields/URLField";
 import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Utils, returnFalse } from "../../../Utils";
+import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Networking } from "../../Network";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
@@ -22,6 +22,7 @@ import ReactLoading from 'react-loading';
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
+    SetSubView: (subView: any) => void;
 }
 
 export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
@@ -30,6 +31,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
         protected _mainCont?: HTMLDivElement;
+        @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
+        @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
         protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
             this.dropDisposer?.();
             this.gestureDisposer?.();
@@ -45,6 +48,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             this.createDashEventsTarget(ele);
         }
 
+        componentDidMount() {
+            this.props.SetSubView(this);
+        }
+
         componentWillUnmount() {
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
@@ -81,15 +88,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
-        docFilters = () => {
-            return (this.props.docFilters?.() ?? Cast(this.props.Document._docFilters, listSpec("string"), []));
-        }
-        docRangeFilters = () => {
-            return (this.props.docRangeFilters?.() ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []));
-        }
-        searchFilterDocs = () => {
-            return this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
-        }
+        collectionFilters = () => this._focusFilters ?? Cast(this.props.Document._docFilters, listSpec("string"), []);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+        childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()];
+        childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
+        IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
+            this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined;
+        searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
             let rawdocs: (Doc | Promise<Doc>)[] = [];
@@ -108,8 +113,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
             const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
 
-            const docFilters = this.docFilters();
-            const docRangeFilters = this.docRangeFilters();
+            const docFilters = this.childDocFilters();
+            const docRangeFilters = this.childDocRangeFilters();
             const searchDocs = this.searchFilterDocs();
             if (this.props.Document.dontRegisterView || (!docFilters.length && !docRangeFilters.length && !searchDocs.length)) {
                 return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 08b5e6bac..292dfd77c 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -32,9 +32,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _collapsed: boolean = false;
     @observable _childClickedScript: Opt<ScriptField>;
     @observable _viewDefDivClick: Opt<ScriptField>;
-    @observable _focusDocFilters: Opt<string[]>; // fields that get overridden by a focus anchor
     @observable _focusPivotField: Opt<string>;
-    @observable _focusRangeFilters: Opt<string[]>;
 
     getAnchor = () => {
         const anchor = Docs.Create.HTMLAnchorDocument([], {
@@ -72,9 +70,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @action
     setViewSpec = (anchor: Doc, preview: boolean) => {
         if (preview) {   // if in preview, then override document's fields with view spec
+            this._focusFilters = StrListCast(Doc.GetProto(anchor).docFilters);
+            this._focusRangeFilters = StrListCast(Doc.GetProto(anchor).docRangeFilters);
             this._focusPivotField = StrCast(anchor.pivotField);
-            this._focusDocFilters = StrListCast(anchor.docFilters);
-            this._focusRangeFilters = StrListCast(anchor.docRangeFilters);
         } else if (anchor.pivotField !== undefined) {  // otherwise set document's fields based on anchor view spec
             this.layoutDoc._prevFilterIndex = 1;
             this.layoutDoc._pivotField = StrCast(anchor.pivotField);
@@ -84,8 +82,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         return 0;
     }
 
-    pivotDocFilters = () => this._focusDocFilters || this.props.docFilters();
-    pivotDocRangeFilters = () => this._focusRangeFilters || this.props.docRangeFilters();
     layoutEngine = () => this._layoutEngine;
     toggleVisibility = action(() => this._collapsed = !this._collapsed);
 
@@ -139,10 +135,8 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : "none" }}
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
-                engineProps={{ pivotField: this.pivotField, docFilters: this.docFilters, docRangeFilters: this.docRangeFilters }}
+                engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
                 fitContentsToDoc={returnTrue}
-                docFilters={this.pivotDocFilters}
-                docRangeFilters={this.pivotDocRangeFilters}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e225c4a11..2718cbbbf 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { computed, observable, runInAction } from 'mobx';
+import { computed, observable, runInAction, action } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
@@ -236,13 +236,19 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     * Shows the filter icon if it's a user-created collection which isn't a dashboard and has some docFilters applied on it or on the current dashboard.
     */
     @computed get showFilterIcon() {
-        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && ((StrListCast(this.props.Document._docFilters).length || StrListCast(this.props.Document._docRangeFilters).length || StrListCast(CurrentUserUtils.ActiveDashboard._docFilters).length || StrListCast(CurrentUserUtils.ActiveDashboard._docRangeFilters).length));
+        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && this._subView?.IsFiltered()
     }
 
+    @observable _subView: any = undefined;
+
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
             ...this.props,
+            SetSubView: action((subView: any) => {
+                console.log("Setting sub", subView);
+                return this._subView = subView
+            }),
             addDocument: this.addDocument,
             moveDocument: this.moveDocument,
             removeDocument: this.removeDocument,
@@ -260,8 +266,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
             {this.showFilterIcon ?
                 <FontAwesomeIcon icon={"filter"} size="lg"
-                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: '#18c718bd', zIndex: 1 }}
-                    onPointerDown={e => { runInAction(() => CurrentUserUtils.propertiesWidth = 250); e.stopPropagation(); }}
+                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: this.showFilterIcon === "hasFilter" ? '#18c718bd' : "orange", zIndex: 1 }}
+                    onPointerDown={action(e => { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })}
                 />
                 : (null)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index a24f1eb7a..623e0f58d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -363,8 +363,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 PanelHeight={this.PanelHeight}
                 layerProvider={this.layerProvider}
                 styleProvider={DefaultStyleProvider}
-                docFilters={CollectionDockingView.Instance.docFilters}
-                docRangeFilters={CollectionDockingView.Instance.docRangeFilters}
+                docFilters={CollectionDockingView.Instance.childDocFilters}
+                docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                 searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                 addDocument={undefined}
                 removeDocument={undefined}
@@ -468,6 +468,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
                 <CollectionFreeFormView
                     Document={this.props.document}
+                    SetSubView={() => this}
                     CollectionView={undefined}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
@@ -496,8 +497,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     layerProvider={undefined}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
-                    docFilters={CollectionDockingView.Instance.docFilters}
-                    docRangeFilters={CollectionDockingView.Instance.docRangeFilters}
+                    docFilters={CollectionDockingView.Instance.childDocFilters}
+                    docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                     fitContentsToDoc={returnTrue}
                 />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ecc93285e..ba6222605 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -110,8 +110,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef = React.createRef<HTMLDivElement>();
     @observable _keyframeEditing = false;
-    @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
-    @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
@@ -159,8 +157,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.layoutDoc._viewScale = vals.scale;
     }
     freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined;
-    freeformDocFilters = () => this._focusFilters;
-    freeformRangeDocFilters = () => this._focusRangeFilters;
     reverseNativeScaling = () => this.fitToContent ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
@@ -1030,8 +1026,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             ScreenToLocalTransform={childLayout.z ? this.getTransformOverlay : this.getTransform}
             PanelWidth={childLayout[WidthSym]}
             PanelHeight={childLayout[HeightSym]}
-            docFilters={this.freeformDocFilters}
-            docRangeFilters={this.freeformRangeDocFilters}
+            docFilters={this.childDocFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse}
             isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index f3a39a262..65c345547 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -237,8 +237,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            docFilters={this.docFilters}
-            docRangeFilters={this.docRangeFilters}
+            docFilters={this.childDocFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 29cb3511a..30836854a 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -236,9 +236,9 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            docFilters={this.docFilters}
+            docFilters={this.childDocFilters}
             isContentActive={returnFalse}
-            docRangeFilters={this.docRangeFilters}
+            docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 585cda729..fed64b620 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -413,8 +413,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     isContentActive={returnTrue}
                     isDocumentActive={returnFalse}
                     ScreenToLocalTransform={this.getPreviewTransform}
-                    docFilters={this.docFilters}
-                    docRangeFilters={this.docRangeFilters}
+                    docFilters={this.childDocFilters}
+                    docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
                     layerProvider={undefined}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c218b805e..bea219831 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -105,9 +105,9 @@ export interface DocumentViewSharedProps {
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
     fitWidth?: () => boolean;
-    docFilters?: () => Opt<string[]>;
-    docRangeFilters?: () => Opt<string[]>;
-    searchFilterDocs?: () => Opt<Doc[]>;
+    docFilters: () => string[];
+    docRangeFilters: () => string[];
+    searchFilterDocs: () => Doc[];
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
-- 
cgit v1.2.3-70-g09d2

