From a888150f2e220eff4629551aa03813f92aa0b12f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 5 Feb 2024 22:56:04 -0500
Subject: changed backgroundColor to set on dataDocs. fixed pivoting on tags. 
 fixed link description editing popup.  fixed showing link editor in property
 view - still some weirdness in what is selected.  fixed dragging tree view
 items to set dragData.treeview and be able to drop at bottom of tree. fixed
 addFolder menu option for TreeViews to add locally..  added a function to
 collect all docs of a given tag into a collection.  fixed setting default
 font size to update autolayouts.  changed dropping link onto same collection
 to not leave pushpin. fixed minimap thumb updating.  added fieldvalue
 dropdown for dashFieldViews in text.

---
 src/client/views/FilterPanel.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index c4f65a5ca..818c81c9a 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -117,8 +117,8 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     //     return this.activeFilters.map(filter => filter.split(Doc.FilterSep)[0]);
     // }
 
-    gatherFieldValues(childDocs: Doc[], facetKey: string) {
-        const valueSet = new Set<string>(StrListCast(this.targetDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]));
+    static gatherFieldValues(childDocs: Doc[], facetKey: string, childFilters: string[]) {
+        const valueSet = new Set<string>(childFilters.map(filter => filter.split(Doc.FilterSep)[1]));
         let rtFields = 0;
         let subDocs = childDocs;
         if (subDocs.length > 0) {
@@ -165,7 +165,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     @computed get activeRenderedFacetInfos() {
         return new Set(
             Array.from(new Set(Array.from(this._selectedFacetHeaders).concat(this.activeFacetHeaders))).map(facetHeader => {
-                const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader);
+                const facetValues = FilterPanel.gatherFieldValues(this.targetDocChildren, facetHeader, StrListCast(this.targetDoc.childFilters));
 
                 let nonNumbers = 0;
                 let minVal = Number.MAX_VALUE,
-- 
cgit v1.2.3-70-g09d2


From 86726ad1b7ef40ac374b53f9d5902441537df7bd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 28 Feb 2024 10:38:54 -0500
Subject: changed multirow view to support a maxShown flag so that headerBar
 doesn't take forever to load with a lot of closed tabs.

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/util/DragManager.ts                     |  2 -
 src/client/views/FilterPanel.tsx                   |  4 +-
 src/client/views/OverlayView.tsx                   |  4 +-
 .../CollectionMulticolumnView.tsx                  | 58 +++++++++++++++-------
 .../CollectionMultirowView.tsx                     |  1 -
 7 files changed, 48 insertions(+), 24 deletions(-)

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8a13395c3..9b17901ca 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -267,6 +267,7 @@ export class DocumentOptions {
     _layout_reflowVertical?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
     _layout_reflowHorizontal?: BOOLt = new BoolInfo('whether a doc with a native size can be horizonally resized, causing some form of reflow');
     layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
+    layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)');
     _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
     _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false);
     _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video)  value is in seconds', false);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d396ba815..289384be0 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -886,7 +886,7 @@ export class CurrentUserUtils {
         this.setupDocTemplates(doc); // sets up the template menu of templates
         //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, layout_maxShown: 10, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index f6af6196f..aa0f77c72 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -101,8 +101,6 @@ export namespace DragManager {
     export interface DragOptions {
         dragComplete?: (e: DragCompleteEvent) => void; // function to invoke when drag has completed
         hideSource?: boolean; // hide source document during drag
-        offsetX?: number; // offset of top left of source drag visual from cursor
-        offsetY?: number;
         noAutoscroll?: boolean;
     }
 
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 818c81c9a..455801422 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -121,11 +121,13 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         const valueSet = new Set<string>(childFilters.map(filter => filter.split(Doc.FilterSep)[1]));
         let rtFields = 0;
         let subDocs = childDocs;
+        let gatheredDocs = [] as Doc[];
         if (subDocs.length > 0) {
             let newarray: Doc[] = [];
             while (subDocs.length > 0) {
                 newarray = [];
                 subDocs.forEach(t => {
+                    gatheredDocs.push(t);
                     const facetVal = t[facetKey];
                     if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
                     facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
@@ -135,7 +137,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                     DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                     annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
                 });
-                subDocs = newarray;
+                subDocs = newarray.filter(d => !gatheredDocs.includes(d));
             }
         }
         //     }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 20dc6c9fa..15b1f0275 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -9,7 +9,7 @@ import { Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { NumCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
-import { DragManager } from '../util/DragManager';
+import { DragManager, dropActionType } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { LightboxView } from './LightboxView';
 import { ObservableReactComponent } from './ObservableReactComponent';
@@ -196,7 +196,7 @@ export class OverlayView extends ObservableReactComponent<{}> {
                 if (e.metaKey) {
                     const dragData = new DragManager.DocumentDragData([d]);
                     dragData.offset = [-offsetx, -offsety];
-                    dragData.dropAction = 'move';
+                    dragData.dropAction = dropActionType.move;
                     dragData.removeDocument = this.removeOverlayDoc;
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
                         return dragData.removeDocument!(doc) ? addDocument(doc) : false;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b181b59ce..85fe7c896 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,10 +1,10 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { Button } from 'browndash-components';
-import { action, computed, makeObservable } from 'mobx';
+import { Button, IconButton } from 'browndash-components';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { emptyFunction, returnFalse } from '../../../../Utils';
+import { FaChevronRight } from 'react-icons/fa';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { DragManager, dropActionType } from '../../../util/DragManager';
@@ -37,6 +37,8 @@ const resizerWidth = 8;
 
 @observer
 export class CollectionMulticolumnView extends CollectionSubView() {
+    @observable _startIndex = 0;
+
     constructor(props: any) {
         super(props);
         makeObservable(this);
@@ -48,7 +50,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, '*') === DimUnit.Ratio);
+        return this.childLayouts.filter(layout => StrCast(layout._dimUnit, '*') === DimUnit.Ratio);
     }
 
     @computed
@@ -57,6 +59,15 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
     }
 
+    @computed get maxShown() {
+        return NumCast(this.layoutDoc.layout_maxShown);
+    }
+
+    @computed
+    private get childLayouts() {
+        return (this.maxShown ? this.childLayoutPairs.slice(this._startIndex, this._startIndex + this.maxShown) : this.childLayoutPairs).map(pair => pair.layout);
+    }
+
     /**
      * This loops through all childLayoutPairs and extracts the values for _dimUnit
      * and _dimMagnitude, ignoring any that are malformed. Additionally, it then
@@ -69,9 +80,9 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     private get resolvedLayoutInformation(): LayoutData {
         let starSum = 0;
         const widthSpecifiers: WidthSpecifier[] = [];
-        this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout._dimUnit, '*');
-            const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
+        this.childLayouts.map(layout => {
+            const unit = StrCast(layout._dimUnit, '*');
+            const magnitude = NumCast(layout._dimMagnitude, this.minimumDim);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
                 unit === DimUnit.Ratio && (starSum += magnitude);
                 widthSpecifiers.push({ magnitude, unit });
@@ -90,7 +101,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
          */
         // setTimeout(() => {
         //     const { ratioDefinedDocs } = this;
-        //     if (this.childLayoutPairs.length) {
+        //     if (this.childPairs.length) {
         //         const minimum = this.minimumDim;
         //         if (minimum !== 0) {
         //             ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1);
@@ -187,13 +198,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             return Transform.Identity(); // we're still waiting on promises to resolve
         }
         let offset = 0;
-        for (const { layout: candidate } of this.childLayoutPairs) {
+        var xf = Transform.Identity();
+        this.childLayouts.map(candidate => {
             if (candidate === layout) {
-                return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
+                return (xf = this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0));
             }
             offset += this.lookupPixels(candidate) + resizerWidth;
-        }
-        return Transform.Identity(); // type coersion, this case should never be hit
+        });
+        return xf;
     };
 
     @undoBatch
@@ -297,10 +309,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      */
     @computed
     private get contents(): JSX.Element[] | null {
-        const { childLayoutPairs } = this;
         const collector: JSX.Element[] = [];
-        for (let i = 0; i < childLayoutPairs.length; i++) {
-            const { layout } = childLayoutPairs[i];
+        this.childLayouts.forEach((layout, i) => {
             collector.push(
                 <Tooltip title={'Tab: ' + StrCast(layout.title)} key={'wrapper' + i}>
                     <div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
@@ -317,10 +327,10 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                     select={this._props.select}
                     columnUnitLength={this.getColumnUnitLength}
                     toLeft={layout}
-                    toRight={childLayoutPairs[i + 1]?.layout}
+                    toRight={this.childLayouts[i + 1]}
                 />
             );
-        }
+        });
         collector.pop(); // removes the final extraneous resize bar
         return collector;
     }
@@ -339,6 +349,20 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                     marginBottom: NumCast(this.Document._yMargin),
                 }}>
                 {this.contents}
+                {!this._startIndex ? null : (
+                    <Tooltip title={'scroll back'}>
+                        <div style={{ position: 'absolute', bottom: 0, left: 0, background: SettingsManager.userVariantColor }} onClick={action(e => (this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}>
+                            <Button tooltip="Scroll back" icon={<FontAwesomeIcon icon="chevron-left" size="lg" />} onClick={action(e => (this._startIndex = Math.max(0, this._startIndex - this.maxShown)))} color={SettingsManager.userColor} />
+                        </div>
+                    </Tooltip>
+                )}
+                {this._startIndex > this.childLayoutPairs.length - 1 ? null : (
+                    <Tooltip title={'scroll forward'}>
+                        <div style={{ position: 'absolute', bottom: 0, right: 0, background: SettingsManager.userVariantColor }} onClick={action(e => (this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}>
+                            <IconButton icon={<FaChevronRight />} color={SettingsManager.userColor} />
+                        </div>
+                    </Tooltip>
+                )}
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 659f7ccdc..17bf3e50c 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,7 +1,6 @@
 import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { emptyFunction, returnFalse } from '../../../../Utils';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { DragManager, dropActionType } from '../../../util/DragManager';
-- 
cgit v1.2.3-70-g09d2


From 4548ec81efc434c8ee5d89b896b2088e4e61d7a0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 28 Feb 2024 22:22:20 -0500
Subject: extracted field dropdown menu to a components and then cleaned up
 collectionTimeView, Documentview titles, and FilterPanel to share it

---
 src/client/documents/Documents.ts                  |  18 +-
 src/client/views/FieldsDropdown.tsx                | 120 +++++++++++
 src/client/views/FilterPanel.tsx                   | 239 ++++++---------------
 .../views/collections/CollectionTimeView.tsx       |  55 +----
 src/client/views/global/globalScripts.ts           |   2 +-
 src/client/views/nodes/DocumentView.scss           |   1 -
 src/client/views/nodes/DocumentView.tsx            |  67 +++---
 7 files changed, 236 insertions(+), 266 deletions(-)
 create mode 100644 src/client/views/FieldsDropdown.tsx

(limited to 'src/client/views/FilterPanel.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9b17901ca..031560886 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -77,6 +77,7 @@ export enum FInfoFieldType {
     enumeration,
     date,
     list,
+    rtf,
 }
 export class FInfo {
     description: string = '';
@@ -84,7 +85,7 @@ export class FInfo {
     fieldType?: FInfoFieldType;
     values?: Field[];
 
-    filterable?: boolean = true;
+    filterable?: boolean = true; // can be used as a Filter in FilterPanel
     // format?: string; // format to display values (e.g, decimal places, $, etc)
     // parse?: ScriptField; // parse a value from a string
     constructor(d: string, readOnly?: boolean) {
@@ -165,9 +166,19 @@ class DTypeInfo extends FInfo {
     override searchable = () => false;
 }
 class DateInfo extends FInfo {
+    constructor(d: string, filterable?: boolean) {
+        super(d, true);
+        this.filterable = filterable;
+    }
     fieldType? = FInfoFieldType.date;
     values?: DateField[] = [];
-    filterable = true;
+}
+class RtfInfo extends FInfo {
+    constructor(d: string, filterable?: boolean) {
+        super(d, true);
+        this.filterable = filterable;
+    }
+    fieldType? = FInfoFieldType.rtf;
 }
 class ListInfo extends FInfo {
     fieldType? = FInfoFieldType.list;
@@ -178,6 +189,7 @@ type NUMt = NumInfo | number;
 type STRt = StrInfo | string;
 type LISTt = ListInfo | List<any>;
 type DOCt = DocInfo | Doc;
+type RTFt = RtfInfo | RichTextField;
 type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
 type PEVt = PEInfo | 'none' | 'all';
 type COLLt = CTypeInfo | CollectionViewType;
@@ -191,6 +203,7 @@ export class DocumentOptions {
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]);
     overlayX?: NUMt = new NumInfo('x coordinate of document in a overlay view', false);
     overlayY?: NUMt = new NumInfo('y coordinate of document in a overlay view', false);
+    text?: RTFt = new RtfInfo('rich text of a text doc', true);
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
     latitude?: NUMt = new NumInfo('latitude coordinate for map views', false);
@@ -470,7 +483,6 @@ export class DocumentOptions {
     sidebar_type_collection?: string; // collection type of text sidebar
 
     data_dashboards?: List<any>; // list of dashboards used in shareddocs;
-    text?: string;
     textTransform?: string;
     letterSpacing?: string;
     iconTemplate?: string; // name of icon template style
diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx
new file mode 100644
index 000000000..5638d34c6
--- /dev/null
+++ b/src/client/views/FieldsDropdown.tsx
@@ -0,0 +1,120 @@
+/**
+ * This creates a dropdown menu that's populated with possible field key names (e.g., author, tags)
+ *
+ * The set of field names actually displayed is based on searching the prop 'Document' and its descendants :
+ *     The field list will contain all of the fields within the prop Document and all of its children;
+ *     this list is then pruned down to only include fields that are not marked in Documents.ts to be non-filterable
+ */
+
+import { computed, makeObservable, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import Select from 'react-select';
+import { Doc } from '../../fields/Doc';
+import { DocOptions, FInfo } from '../documents/Documents';
+import { SearchUtil } from '../util/SearchUtil';
+import { SettingsManager } from '../util/SettingsManager';
+import './FilterPanel.scss';
+import { ObservableReactComponent } from './ObservableReactComponent';
+
+interface fieldsDropdownProps {
+    Document: Doc; // show fields for this Doc if set, otherwise for all docs in dashboard
+    selectFunc: (value: string) => void;
+    menuClose?: () => void;
+    placeholder?: string | (() => string);
+    showPlaceholder?: true; // if true, then input field always shows the placeholder value; otherwise, it shows the current selection
+    addedFields?: string[];
+}
+
+@observer
+export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps> {
+    @observable _newField = '';
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
+    @computed get allDescendantDocs() {
+        const allDocs = new Set<Doc>();
+        SearchUtil.foreachRecursiveDoc([this._props.Document], (depth, doc) => allDocs.add(doc));
+        return Array.from(allDocs);
+    }
+
+    @computed get fieldsOfDocuments() {
+        const keys = new Set<string>();
+        this.allDescendantDocs.forEach(doc => SearchUtil.documentKeys(doc).filter(key => keys.add(key)));
+        const sortedKeys = Array.from(keys.keys())
+            .filter(key => key[0])
+            .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || !Doc.noviceMode)
+            .sort();
+
+        Array.from(keys).forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
+
+        return [...Array.from(keys), ...sortedKeys];
+    }
+
+    render() {
+        const filteredOptions = ['author', ...(this._newField ? [this._newField] : []), ...(this._props.addedFields ?? []), ...this.fieldsOfDocuments.filter(facet => facet[0] === facet.charAt(0).toUpperCase())];
+
+        Object.entries(DocOptions)
+            .filter(opts => opts[1].filterable)
+            .forEach((pair: [string, FInfo]) => filteredOptions.push(pair[0]));
+        const options = filteredOptions.sort().map(facet => ({ value: facet, label: facet }));
+
+        console.log(options);
+        return (
+            <Select
+                styles={{
+                    control: (baseStyles, state) => ({
+                        ...baseStyles,
+                        minHeight: '5px',
+                        maxHeight: '30px',
+                        color: SettingsManager.userColor,
+                        backgroundColor: SettingsManager.userBackgroundColor,
+                        padding: 0,
+                        margin: 0,
+                    }),
+                    singleValue: (baseStyles, state) => ({
+                        ...baseStyles,
+                        color: SettingsManager.userColor,
+                        background: SettingsManager.userBackgroundColor,
+                    }),
+                    placeholder: (baseStyles, state) => ({
+                        ...baseStyles,
+                        color: SettingsManager.userColor,
+                        background: SettingsManager.userBackgroundColor,
+                    }),
+                    input: (baseStyles, state) => ({
+                        ...baseStyles,
+                        padding: 0,
+                        margin: 0,
+                        color: SettingsManager.userColor,
+                        background: 'transparent',
+                    }),
+                    option: (baseStyles, state) => ({
+                        ...baseStyles,
+                        color: SettingsManager.userColor,
+                        background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor,
+                    }),
+                    menuList: (baseStyles, state) => ({
+                        ...baseStyles,
+                        backgroundColor: SettingsManager.userBackgroundColor,
+                    }),
+                }}
+                placeholder={typeof this._props.placeholder === 'string' ? this._props.placeholder : this._props.placeholder?.()}
+                options={options as any}
+                isMulti={false}
+                onChange={val => this._props.selectFunc((val as any as { value: string; label: string }).value)}
+                onKeyDown={e => {
+                    if (e.key === 'Enter') {
+                        runInAction(() => this._props.selectFunc((this._newField = (e.nativeEvent.target as any)?.value)));
+                    }
+                    e.stopPropagation();
+                }}
+                onMenuClose={this._props.menuClose}
+                closeMenuOnSelect={true}
+                value={this._props.showPlaceholder ? null : undefined}
+            />
+        );
+    }
+}
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 455801422..0521c4a4b 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -4,19 +4,16 @@ import * as React from 'react';
 import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
 import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai';
 import { CiCircleRemove } from 'react-icons/ci';
-import Select from 'react-select';
 import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
-import { DocOptions, FInfo } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
-import { UserOptions } from '../util/GroupManager';
 import { SearchUtil } from '../util/SearchUtil';
 import { SettingsManager } from '../util/SettingsManager';
 import { undoable } from '../util/UndoManager';
+import { FieldsDropdown } from './FieldsDropdown';
 import './FilterPanel.scss';
-import { FieldView } from './nodes/FieldView';
 import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
 import { ObservableReactComponent } from './ObservableReactComponent';
 
@@ -26,9 +23,7 @@ interface filterProps {
 
 @observer
 export class FilterPanel extends ObservableReactComponent<filterProps> {
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(FilterPanel, fieldKey);
-    }
+    @observable _selectedFacetHeaders = new Set<string>();
 
     constructor(props: any) {
         super(props);
@@ -38,45 +33,19 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     /**
      * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
      */
-    get targetDoc() {
+    get Document() {
         return this._props.Document;
     }
     @computed get targetDocChildKey() {
-        const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc);
+        const targetView = DocumentManager.Instance.getFirstDocumentView(this.Document);
         return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data';
     }
     @computed get targetDocChildren() {
-        return [...DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.targetDoc[Doc.LayoutFieldKey(this.targetDoc) + '_sidebar'])];
-    }
-
-    @computed get allDocs() {
-        const allDocs = new Set<Doc>();
-        const targetDoc = this.targetDoc;
-        if (targetDoc) {
-            SearchUtil.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc));
-        }
-        return Array.from(allDocs);
-    }
-
-    @computed get _allFacets() {
-        const noviceReqFields = ['author', 'tags', 'text', 'type', LinkedTo];
-        const noviceLayoutFields: string[] = []; //["_layout_curPage"];
-        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
-
-        const keys = new Set<string>(noviceFields);
-        this.allDocs.forEach(doc => SearchUtil.documentKeys(doc).filter(key => keys.add(key)));
-        const sortedKeys = Array.from(keys.keys())
-            .filter(key => key[0])
-            .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
-            .sort();
-
-        noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
-
-        return [...noviceFields, ...sortedKeys];
+        return [...DocListCast(this.Document?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_sidebar'])];
     }
 
     @computed get rangeFilters() {
-        return StrListCast(this.targetDoc?._childFiltersByRanges).filter((filter, i) => !(i % 3));
+        return StrListCast(this.Document?._childFiltersByRanges).filter((filter, i) => !(i % 3));
     }
 
     /**
@@ -84,7 +53,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
      * ["#tags::bob::check", "tags::joe::check", "width", "height"]
      */
     @computed get activeFilters() {
-        return StrListCast(this.targetDoc?._childFilters).concat(this.rangeFilters);
+        return StrListCast(this.Document?._childFilters).concat(this.rangeFilters);
     }
 
     @computed get mapActiveFiltersToFacets() {
@@ -100,22 +69,15 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     // this wants to return all the filter facets that have an existing filter set on them in order to show them in the rendered panel
     // this set may overlap the selectedFilters
     // if the components reloads, these will still exist and be shown
-
+    //
     // ["#tags", "width", "height"]
     //
-
     @computed get activeFacetHeaders() {
         const activeHeaders = new Array();
         this.activeFilters.map(filter => activeHeaders.push(filter.split(Doc.FilterSep)[0]));
 
         return activeHeaders;
     }
-    /**
-     * @returns a string array of the current attributes
-     */
-    // @computed get currentFacets() {
-    //     return this.activeFilters.map(filter => filter.split(Doc.FilterSep)[0]);
-    // }
 
     static gatherFieldValues(childDocs: Doc[], facetKey: string, childFilters: string[]) {
         const valueSet = new Set<string>(childFilters.map(filter => filter.split(Doc.FilterSep)[1]));
@@ -147,8 +109,8 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     }
 
     public removeFilter = (filterName: string) => {
-        Doc.setDocFilter(this.targetDoc, filterName, undefined, 'remove');
-        Doc.setDocRangeFilter(this.targetDoc, filterName, undefined);
+        Doc.setDocFilter(this.Document, filterName, undefined, 'remove');
+        Doc.setDocRangeFilter(this.Document, filterName, undefined);
     };
 
     // @observable _chosenFacets = new ObservableMap<string, 'text' | 'checkbox' | 'slider' | 'range'>();
@@ -156,18 +118,16 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     @observable _collapseReturnKeys = new Array();
 
     // this computed function gets the active filters and maps them to their headers
-
     //
     // activeRenderedFacetInfos()
     //    returns renderInfo for all user selected filters and for all existing filters set on the document
     // Map("tags" => {"checkbox"},
-    //         "width" => {"rangs", domain:[1978,1992]})
+    //     "width" => {"range", domain:[1978,1992]})
     //
-
     @computed get activeRenderedFacetInfos() {
         return new Set(
             Array.from(new Set(Array.from(this._selectedFacetHeaders).concat(this.activeFacetHeaders))).map(facetHeader => {
-                const facetValues = FilterPanel.gatherFieldValues(this.targetDocChildren, facetHeader, StrListCast(this.targetDoc.childFilters));
+                const facetValues = FilterPanel.gatherFieldValues(this.targetDocChildren, facetHeader, StrListCast(this.Document.childFilters));
 
                 let nonNumbers = 0;
                 let minVal = Number.MAX_VALUE,
@@ -187,7 +147,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                 } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
                     const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
                     const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
-                    const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter
+                    const ranged = Doc.readDocRangeFilter(this.Document, facetHeader); // not the filter range, but the zooomed in range on the filter
                     return { facetHeader, renderType: 'range', domain: [extendedMinVal, extendedMaxVal], range: ranged ? ranged : [extendedMinVal, extendedMaxVal] };
                 } else {
                     return { facetHeader, renderType: 'checkbox' };
@@ -196,8 +156,6 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         );
     }
 
-    @observable _selectedFacetHeaders = new Set<string>();
-
     /**
      * user clicks on a filter facet because they want to see it.
      * this adds this chosen filter to a set of user selected filters called:  selectedFilters
@@ -231,7 +189,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
     facetValues = (facetHeader: string) => {
         const allCollectionDocs = new Set<Doc>();
         SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
-        const set = new Set<string>([...StrListCast(this.targetDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
+        const set = new Set<string>([...StrListCast(this.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
         if (facetHeader === 'tags')
             allCollectionDocs.forEach(child =>
                 StrListCast(child[facetHeader])
@@ -255,59 +213,13 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2));
     };
 
-    @computed get fieldsDropdown() {
-        const filteredOptions = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())];
-
-        Object.entries(DocOptions)
-            .filter(opts => opts[1].filterable)
-            .forEach((pair: [string, FInfo]) => filteredOptions.push(pair[0]));
-        const options = filteredOptions.map(facet => ({ value: facet, label: facet }));
-
-        return (
-            <Select
-                styles={{
-                    control: (baseStyles, state) => ({
-                        ...baseStyles,
-                        color: SettingsManager.userColor,
-                        background: SettingsManager.userBackgroundColor,
-                    }),
-                    placeholder: (baseStyles, state) => ({
-                        ...baseStyles,
-                        color: SettingsManager.userColor,
-                        background: SettingsManager.userBackgroundColor,
-                    }),
-                    input: (baseStyles, state) => ({
-                        ...baseStyles,
-                        color: SettingsManager.userColor,
-                        background: SettingsManager.userBackgroundColor,
-                    }),
-                    option: (baseStyles, state) => ({
-                        ...baseStyles,
-                        color: SettingsManager.userColor,
-                        background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor,
-                    }),
-                    menuList: (baseStyles, state) => ({
-                        ...baseStyles,
-                        backgroundColor: SettingsManager.userBackgroundColor,
-                    }),
-                }}
-                placeholder={'add a filter'}
-                options={options}
-                isMulti={false}
-                onChange={val => this.facetClick((val as UserOptions).value)}
-                onKeyDown={e => e.stopPropagation()}
-                //onMenuClose={onClose}
-                value={null}
-                closeMenuOnSelect={true}
-            />
-        );
-    }
-
     render() {
         return (
             <div className="filterBox-treeView">
                 <div className="filterBox-select">
-                    <div style={{ width: '100%' }}>{this.fieldsDropdown}</div>
+                    <div style={{ width: '100%' }}>
+                        <FieldsDropdown Document={this.Document} selectFunc={this.facetClick} showPlaceholder={true} placeholder="add a filter" addedFields={['acl-Guest', LinkedTo]} />
+                    </div>
                     {/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
                     {/* <div className="filterBox-select-bool"> 
                         <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}>
@@ -343,11 +255,11 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                                         className="filterBox-facetHeader-remove"
                                         onClick={action(e => {
                                             if (renderInfo.facetHeader === 'text') {
-                                                Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, 'match', 'remove');
+                                                Doc.setDocFilter(this.Document, renderInfo.facetHeader, 'match', 'remove');
                                             } else {
                                                 for (var key of this.facetValues(renderInfo.facetHeader)) {
                                                     if (this.mapActiveFiltersToFacets.get(key)) {
-                                                        Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove');
+                                                        Doc.setDocFilter(this.Document, renderInfo.facetHeader, key, 'remove');
                                                     }
                                                 }
                                             }
@@ -355,7 +267,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                                             this._chosenFacetsCollapse.delete(renderInfo.facetHeader);
 
                                             if (renderInfo.domain) {
-                                                Doc.setDocRangeFilter(this.targetDoc, renderInfo.facetHeader, renderInfo.domain, 'remove');
+                                                Doc.setDocRangeFilter(this.Document, renderInfo.facetHeader, renderInfo.domain, 'remove');
                                             }
                                         })}>
                                         <CiCircleRemove />{' '}
@@ -379,16 +291,16 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
             case 'text':
                 return (
                     <input
-                        key={this.targetDoc[Id]}
+                        key={this.Document[Id]}
                         placeholder={'enter text to match'}
                         defaultValue={
-                            StrListCast(this.targetDoc._childFilters)
+                            StrListCast(this.Document._childFilters)
                                 .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader)
                                 ?.split(Doc.FilterSep)[1]
                         }
                         style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
-                        onBlur={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
-                        onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
+                        onBlur={undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
+                        onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
                     />
                 );
             case 'checkbox':
@@ -399,12 +311,12 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                             <input
                                 style={{ width: 20, marginLeft: 20 }}
                                 checked={['check', 'exists'].includes(
-                                    StrListCast(this.targetDoc._childFilters)
+                                    StrListCast(this.Document._childFilters)
                                         .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue)
                                         ?.split(Doc.FilterSep)[2] ?? ''
                                 )}
                                 type={type}
-                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')}
+                                onChange={undoable(e => Doc.setDocFilter(this.Document, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')}
                             />
                             {facetValue}
                         </div>
@@ -414,64 +326,51 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
             case 'range':
                 const domain = renderInfoDomain;
                 const range = renderInfoRange;
-
-                if (range) {
-                    console.log('this is info range ' + range[0] + ' , ' + range[1]);
-                }
-
                 if (domain) {
-                    console.log('this is info domain ' + domain[0] + ', ' + domain[1]);
-
                     return (
-                        <>
-                            {/* <div className="sliderBox-outerDiv-checkBox" style={{ float: 'left' }}>
-                                <Checkbox color="primary" onChange={action(() => console.log('on change'))} />
-                            </div> */}
-
-                            <div className="sliderBox-outerDiv" style={{ width: '95%', height: 45, float: 'right' }}>
-                                <Slider
-                                    mode={2}
-                                    step={Math.min(1, 0.1 * (domain[1] - domain[0]))}
-                                    domain={[domain[0], domain[1]]} // -1000, 1000
-                                    rootStyle={{ position: 'relative', width: '100%' }}
-                                    onChange={values => Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)}
-                                    values={renderInfoRange!}>
-                                    <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
-                                    <Handles>
-                                        {({ handles, activeHandleID, getHandleProps }) => (
-                                            <div className="slider-handles">
-                                                {handles.map((handle, i) => {
-                                                    // const value = i === 0 ? defaultValues[0] : defaultValues[1];
-                                                    return (
-                                                        <div>
-                                                            <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
-                                                        </div>
-                                                    );
-                                                })}
-                                            </div>
-                                        )}
-                                    </Handles>
-                                    <Tracks left={false} right={false}>
-                                        {({ tracks, getTrackProps }) => (
-                                            <div className="slider-tracks">
-                                                {tracks.map(({ id, source, target }) => (
-                                                    <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
-                                                ))}
-                                            </div>
-                                        )}
-                                    </Tracks>
-                                    <Ticks count={5}>
-                                        {({ ticks }) => (
-                                            <div className="slider-ticks">
-                                                {ticks.map(tick => (
-                                                    <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
-                                                ))}
-                                            </div>
-                                        )}
-                                    </Ticks>
-                                </Slider>
-                            </div>
-                        </>
+                        <div className="sliderBox-outerDiv" style={{ width: '95%', height: 45, float: 'right' }}>
+                            <Slider
+                                mode={2}
+                                step={Math.min(1, 0.1 * (domain[1] - domain[0]))}
+                                domain={[domain[0], domain[1]]} // -1000, 1000
+                                rootStyle={{ position: 'relative', width: '100%' }}
+                                onChange={values => Doc.setDocRangeFilter(this.Document, facetHeader, values)}
+                                values={renderInfoRange!}>
+                                <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
+                                <Handles>
+                                    {({ handles, activeHandleID, getHandleProps }) => (
+                                        <div className="slider-handles">
+                                            {handles.map((handle, i) => {
+                                                // const value = i === 0 ? defaultValues[0] : defaultValues[1];
+                                                return (
+                                                    <div>
+                                                        <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
+                                                    </div>
+                                                );
+                                            })}
+                                        </div>
+                                    )}
+                                </Handles>
+                                <Tracks left={false} right={false}>
+                                    {({ tracks, getTrackProps }) => (
+                                        <div className="slider-tracks">
+                                            {tracks.map(({ id, source, target }) => (
+                                                <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
+                                            ))}
+                                        </div>
+                                    )}
+                                </Tracks>
+                                <Ticks count={5}>
+                                    {({ ticks }) => (
+                                        <div className="slider-ticks">
+                                            {ticks.map(tick => (
+                                                <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
+                                            ))}
+                                        </div>
+                                    )}
+                                </Ticks>
+                            </Slider>
+                        </div>
                     );
                 }
 
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 38f6aa3e7..b92edd165 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,12 +1,10 @@
-import { toUpper } from 'lodash';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
 import { Doc, Opt, StrListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
-import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
@@ -15,7 +13,7 @@ import { DocumentManager } from '../../util/DocumentManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { EditableView } from '../EditableView';
+import { FieldsDropdown } from '../FieldsDropdown';
 import { DocumentView } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FieldView';
 import { PresBox } from '../nodes/trails';
@@ -192,51 +190,6 @@ export class CollectionTimeView extends CollectionSubView() {
 
         ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
     };
-    @computed get _allFacets() {
-        const facets = new Set<string>();
-        this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
-        Doc.AreProtosEqual(this.dataDoc, this.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key)));
-        return Array.from(facets);
-    }
-    menuCallback = (x: number, y: number) => {
-        ContextMenu.Instance.clearItems();
-        const keySet: Set<string> = new Set(['tags']);
-
-        this.childLayoutPairs.map(pair =>
-            this._allFacets
-                .filter(fieldKey => pair.layout[fieldKey] instanceof RichTextField || typeof pair.layout[fieldKey] === 'number' || typeof pair.layout[fieldKey] === 'boolean' || typeof pair.layout[fieldKey] === 'string')
-                .filter(fieldKey => fieldKey[0] !== '_' && (fieldKey === 'tags' || fieldKey[0] === toUpper(fieldKey)[0]))
-                .map(fieldKey => keySet.add(fieldKey))
-        );
-
-        const docItems: ContextMenuProps[] = Array.from(keySet).map(fieldKey => 
-                     ({ description: ':' + fieldKey, event: () => (this.layoutDoc._pivotField = fieldKey), icon: 'compress-arrows-alt' })); // prettier-ignore
-        docItems.push({ description: ':default', event: () => (this.layoutDoc._pivotField = undefined), icon: 'compress-arrows-alt' });
-        ContextMenu.Instance.addItem({ description: 'Pivot Fields ...', subitems: docItems, icon: 'eye' });
-        ContextMenu.Instance.displayMenu(x, y, ':');
-    };
-
-    @computed get pivotKeyUI() {
-        return (
-            <div className={'pivotKeyEntry'}>
-                <EditableView
-                    GetValue={returnEmptyString}
-                    SetValue={(value: any) => {
-                        if (value?.length) {
-                            this.layoutDoc._pivotField = value;
-                            return true;
-                        }
-                        return false;
-                    }}
-                    background={'#f1efeb'} // this._props.headingObject ? this._props.headingObject.color : "#f1efeb";
-                    contents={':' + StrCast(this.layoutDoc._pivotField)}
-                    showMenuOnLoad={true}
-                    display={'inline'}
-                    menuCallback={this.menuCallback}
-                />
-            </div>
-        );
-    }
 
     render() {
         let nonNumbers = 0;
@@ -263,7 +216,6 @@ export class CollectionTimeView extends CollectionSubView() {
 
         return (
             <div className={'collectionTimeView' + (doTimeline ? '' : '-pivot')} onContextMenu={this.specificMenu} style={{ width: this._props.PanelWidth(), height: '100%' }}>
-                {this.pivotKeyUI}
                 {this.contents}
                 {!this._props.isSelected() || !doTimeline ? null : (
                     <>
@@ -272,6 +224,9 @@ export class CollectionTimeView extends CollectionSubView() {
                         <div className="collectionTimeView-thumb-mid collectionTimeView-thumb" key="max" onPointerDown={this.onMidDown} />
                     </>
                 )}
+                <div style={{ right: 0, top: 0, position: 'absolute' }}>
+                    <FieldsDropdown Document={this.Document} selectFunc={fieldKey => (this.layoutDoc._pivotField = fieldKey)} placeholder={StrCast(this.layoutDoc._pivotField)} />
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 51672513b..e73ab25b5 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -78,7 +78,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
     }
     if (SelectionManager.Views.length) {
         SelectionManager.Docs.forEach(doc => {
-            Doc.GetProto(doc).layout_headingColor = color;
+            doc[DocData].layout_headingColor = color === 'transparent' ? undefined : color;
             doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title');
         });
     } else {
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index c4dab16fb..5421c1b50 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -180,7 +180,6 @@
 
         .documentView-titleWrapper,
         .documentView-titleWrapper-hover {
-            overflow: hidden;
             color: $black;
             transform-origin: top left;
             top: 0;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index abb1aa59f..b0e8cf6ff 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ import { KeyValueBox } from './KeyValueBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { PresEffect, PresEffectDirection } from './trails';
+import { FieldsDropdown } from '../FieldsDropdown';
 interface Window {
     MediaRecorder: MediaRecorder;
 }
@@ -784,28 +785,25 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
     }
 
     captionStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
-    fieldsDropdown = (reqdFields: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
-        const filteredFields = Object.entries(DocOptions).reduce((set, [field, opts]) => (opts.filterable ? set.add(field) : set), new Set(reqdFields));
+    fieldsDropdown = (placeholder: string) => {
         return (
-            <div style={{ width: dropdownWidth }}>
-                <div
-                    ref={action((r: any) => r && (this._titleDropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
-                    onPointerDown={action(e => (this._changingTitleField = true))}
-                    style={{ width: 'max-content', transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
-                    <Dropdown
-                        activeChanged={action(isOpen => !isOpen && (this._changingTitleField = false))}
-                        selectedVal={placeholder}
-                        setSelectedVal={onChange}
-                        color={SettingsManager.userColor}
-                        background={SettingsManager.userVariantColor}
-                        type={Type.TERT}
-                        closeOnSelect={true}
-                        dropdownType={DropdownType.SELECT}
-                        items={Array.from(filteredFields).map(facet => ({ val: facet, text: facet }))}
-                        width={100}
-                        fillWidth
-                    />
-                </div>
+            <div
+                ref={action((r: any) => r && (this._titleDropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
+                onPointerDown={action(e => (this._changingTitleField = true))}
+                style={{ width: 'max-content', background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
+                <FieldsDropdown
+                    Document={this.Document}
+                    placeholder={placeholder}
+                    selectFunc={action((field: string | number) => {
+                        if (this.layoutDoc.layout_showTitle) {
+                            this.layoutDoc._layout_showTitle = field;
+                        } else if (!this._props.layout_showTitle) {
+                            Doc.UserDoc().layout_showTitle = field;
+                        }
+                        this._changingTitleField = false;
+                    })}
+                    menuClose={action(() => (this._changingTitleField = false))}
+                />
             </div>
         );
     };
@@ -839,22 +837,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                     background,
                     pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this._props.isDocumentActive?.() ? 'all' : undefined,
                 }}>
-                {!dropdownWidth
-                    ? null
-                    : this.fieldsDropdown(
-                          [StrCast(this.layoutDoc.layout_showTitle)],
-                          dropdownWidth,
-                          StrCast(this.layoutDoc.layout_showTitle).split(':')[0],
-                          action((field: string | number) => {
-                              if (this.layoutDoc.layout_showTitle) {
-                                  this.layoutDoc._layout_showTitle = field;
-                              } else if (!this._props.layout_showTitle) {
-                                  Doc.UserDoc().layout_showTitle = field;
-                              }
-                              this._changingTitleField = false;
-                          }),
-                          action(() => (this._changingTitleField = false))
-                      )}
+                {!dropdownWidth ? null : <div style={{ width: dropdownWidth }}>{this.fieldsDropdown(showTitle)}</div>}
                 <div
                     style={{
                         width: `calc(100% - ${dropdownWidth}px)`,
@@ -864,10 +847,12 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                     }}>
                     <EditableView
                         ref={this._titleRef}
-                        contents={showTitle
-                            .split(';')
-                            .map(field => targetDoc[field.trim()]?.toString())
-                            .join(' \\ ')}
+                        contents={
+                            showTitle
+                                .split(';')
+                                .map(field => targetDoc[field.trim()]?.toString())
+                                .join(' \\ ') || '-unset-'
+                        }
                         display="block"
                         oneLine={true}
                         fontSize={(this.titleHeight / 15) * 10}
-- 
cgit v1.2.3-70-g09d2

