1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
/**
* 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 { action, computed, makeObservable, observable } 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 { SnappingManager } from '../util/SnappingManager';
import './FilterPanel.scss';
import { ObservableReactComponent } from './ObservableReactComponent';
interface fieldsDropdownProps {
Doc: 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[];
isInactive?: boolean;
}
@observer
export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps> {
@observable _newField = '';
constructor(props: fieldsDropdownProps) {
super(props);
makeObservable(this);
}
@computed get allDescendantDocs() {
const allDocs = new Set<Doc>();
SearchUtil.foreachRecursiveDoc([this._props.Doc], (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] instanceof FInfo && opts[1].filterable)
.forEach((pair: [string, unknown]) => filteredOptions.push(pair[0]));
const options = filteredOptions.sort().map(facet => ({ value: facet, label: facet }));
return (
<Select
styles={{
control: (baseStyles /* , state */) => ({
...baseStyles,
minHeight: '5px',
maxHeight: '30px',
color: SnappingManager.userColor,
backgroundColor: SnappingManager.userBackgroundColor,
padding: 0,
margin: 0,
}),
singleValue: (baseStyles /* , state */) => ({
...baseStyles,
color: SnappingManager.userColor,
background: SnappingManager.userBackgroundColor,
display: this._props.isInactive ? 'none' : undefined,
}),
placeholder: (baseStyles /* , state */) => ({
...baseStyles,
color: SnappingManager.userColor,
background: SnappingManager.userBackgroundColor,
display: this._props.isInactive ? 'none' : undefined,
}),
input: (baseStyles /* , state */) => ({
...baseStyles,
padding: 0,
margin: 0,
color: SnappingManager.userColor,
background: 'transparent',
}),
option: (baseStyles, state) => ({
...baseStyles,
color: SnappingManager.userColor,
background: !state.isFocused ? SnappingManager.userBackgroundColor : SnappingManager.userVariantColor,
}),
menuList: (baseStyles /* , state */) => ({
...baseStyles,
backgroundColor: SnappingManager.userBackgroundColor,
}),
}}
placeholder={typeof this._props.placeholder === 'string' ? this._props.placeholder : this._props.placeholder?.()}
options={options}
isMulti={false}
onChange={val => this._props.selectFunc((val as { value: string; label: string }).value)}
onKeyDown={action(e => {
if (e.key === 'Enter') {
this._props.selectFunc((this._newField = (e.nativeEvent.target as HTMLSelectElement)?.value));
}
e.stopPropagation();
})}
onMenuClose={this._props.menuClose}
closeMenuOnSelect
value={this._props.showPlaceholder ? null : undefined}
/>
);
}
}
|