aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts18
-rw-r--r--src/client/views/FieldsDropdown.tsx120
-rw-r--r--src/client/views/FilterPanel.tsx239
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx55
-rw-r--r--src/client/views/global/globalScripts.ts2
-rw-r--r--src/client/views/nodes/DocumentView.scss1
-rw-r--r--src/client/views/nodes/DocumentView.tsx67
7 files changed, 236 insertions, 266 deletions
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}