aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/MetadataEntryMenu.tsx
diff options
context:
space:
mode:
authoryipstanley <stanley_yip@brown.edu>2019-07-14 20:46:20 -0400
committeryipstanley <stanley_yip@brown.edu>2019-07-14 20:46:20 -0400
commitc39114a07f1710205b512391de3a115a93883311 (patch)
tree49153465f4cbb8b70b3c583279d79bac6439f93d /src/client/views/MetadataEntryMenu.tsx
parentc4f324757c4ce3c9da077f5b10370f3b53cb3411 (diff)
parent66f0d72f07d3cf28e01abd0d9b2659c951defcd9 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src/client/views/MetadataEntryMenu.tsx')
-rw-r--r--src/client/views/MetadataEntryMenu.tsx107
1 files changed, 102 insertions, 5 deletions
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 0dc7e0220..bd5a307b3 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -1,29 +1,77 @@
import * as React from 'react';
import "./MetadataEntryMenu.scss";
import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import { observable, action, runInAction, trace } from 'mobx';
import { KeyValueBox } from './nodes/KeyValueBox';
-import { Doc } from '../../new_fields/Doc';
+import { Doc, Field } from '../../new_fields/Doc';
+import * as Autosuggest from 'react-autosuggest';
export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
export interface MetadataEntryProps {
docs: DocLike | (() => DocLike);
onError?: () => boolean;
+ suggestWithFunction?: boolean;
}
@observer
export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
@observable private _currentKey: string = "";
@observable private _currentValue: string = "";
+ @observable private suggestions: string[] = [];
+ private userModified = false;
+
+ private autosuggestRef = React.createRef<Autosuggest>();
@action
- onKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._currentKey = e.target.value;
+ onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
+ this._currentKey = newValue;
+ if (!this.userModified) {
+ this.previewValue();
+ }
+ }
+
+ previewValue = async () => {
+ let field: Field | undefined | null = null;
+ let onProto: boolean = false;
+ let value: string | undefined = undefined;
+ let docs = this.props.docs;
+ if (typeof docs === "function") {
+ if (this.props.suggestWithFunction) {
+ docs = docs();
+ } else {
+ return;
+ }
+ }
+ docs = await docs;
+ if (docs instanceof Doc) {
+ await docs[this._currentKey];
+ value = Field.toKeyValueString(docs, this._currentKey);
+ } else {
+ for (const doc of docs) {
+ const v = await doc[this._currentKey];
+ onProto = onProto || !Object.keys(doc).includes(this._currentKey);
+ if (field === null) {
+ field = v;
+ } else if (v !== field) {
+ value = "multiple values";
+ }
+ }
+ }
+ if (value === undefined) {
+ if (field !== null && field !== undefined) {
+ value = (onProto ? "" : "= ") + Field.toScriptString(field);
+ } else {
+ value = "";
+ }
+ }
+ const s = value;
+ runInAction(() => this._currentValue = s);
}
@action
onValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this._currentValue = e.target.value;
+ this.userModified = e.target.value.trim() !== "";
}
onValueKeyDown = async (e: React.KeyboardEvent) => {
@@ -59,13 +107,62 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
clearInputs = () => {
this._currentKey = "";
this._currentValue = "";
+ this.userModified = false;
+ if (this.autosuggestRef.current) {
+ const input: HTMLInputElement = (this.autosuggestRef.current as any).input;
+ input && input.focus();
+ }
+ }
+
+ getKeySuggestions = async (value: string): Promise<string[]> => {
+ value = value.toLowerCase();
+ let docs = this.props.docs;
+ if (typeof docs === "function") {
+ if (this.props.suggestWithFunction) {
+ docs = docs();
+ } else {
+ return [];
+ }
+ }
+ docs = await docs;
+ if (docs instanceof Doc) {
+ return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
+ } else {
+ const keys = new Set<string>();
+ docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+ return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
+ }
+ }
+ getSuggestionValue = (suggestion: string) => suggestion;
+
+ renderSuggestion = (suggestion: string) => {
+ return <p>{suggestion}</p>;
+ }
+
+ onSuggestionFetch = async ({ value }: { value: string }) => {
+ const sugg = await this.getKeySuggestions(value);
+ runInAction(() => {
+ this.suggestions = sugg;
+ });
+ }
+
+ @action
+ onSuggestionClear = () => {
+ this.suggestions = [];
}
render() {
return (
<div className="metadataEntry-outerDiv">
Key:
- <input className="metadataEntry-input" value={this._currentKey} onChange={this.onKeyChange} />
+ <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
+ getSuggestionValue={this.getSuggestionValue}
+ suggestions={this.suggestions}
+ alwaysRenderSuggestions
+ renderSuggestion={this.renderSuggestion}
+ onSuggestionsFetchRequested={this.onSuggestionFetch}
+ onSuggestionsClearRequested={this.onSuggestionClear}
+ ref={this.autosuggestRef} />
Value:
<input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
</div>