diff options
| author | Mohammad Amoush <mohammad_amoush@brown.edu> | 2019-07-16 12:38:37 -0400 | 
|---|---|---|
| committer | Mohammad Amoush <mohammad_amoush@brown.edu> | 2019-07-16 12:38:37 -0400 | 
| commit | 52ea2cbd66bd223730bb370470e706bc05f88fa8 (patch) | |
| tree | c7a025c9e47e217c94e2fffac6ea354967ad7187 /src/client/views/MetadataEntryMenu.tsx | |
| parent | 3593c1c4a67e8fb398e5a456ad4d758305294625 (diff) | |
| parent | 03deba08d6af54bfc4235ed7c5ac26b8f673607a (diff) | |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into presentation-reordering-mohammad
Diffstat (limited to 'src/client/views/MetadataEntryMenu.tsx')
| -rw-r--r-- | src/client/views/MetadataEntryMenu.tsx | 171 | 
1 files changed, 171 insertions, 0 deletions
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx new file mode 100644 index 000000000..bd5a307b3 --- /dev/null +++ b/src/client/views/MetadataEntryMenu.tsx @@ -0,0 +1,171 @@ +import * as React from 'react'; +import "./MetadataEntryMenu.scss"; +import { observer } from 'mobx-react'; +import { observable, action, runInAction, trace } from 'mobx'; +import { KeyValueBox } from './nodes/KeyValueBox'; +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, { 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) => { +        if (e.key === "Enter") { +            const script = KeyValueBox.CompileKVPScript(this._currentValue); +            if (!script) return; +            let doc = this.props.docs; +            if (typeof doc === "function") { +                doc = doc(); +            } +            doc = await doc; +            let success: boolean; +            if (doc instanceof Doc) { +                success = KeyValueBox.ApplyKVPScript(doc, this._currentKey, script); +            } else { +                success = doc.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)); +            } +            if (!success) { +                if (this.props.onError) { +                    if (this.props.onError()) { +                        this.clearInputs(); +                    } +                } else { +                    this.clearInputs(); +                } +            } else { +                this.clearInputs(); +            } +        } +    } + +    @action +    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: +                <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> +        ); +    } +}
\ No newline at end of file  | 
