From 60ceef0a3a8c11d85434a154e542424d34d9562c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 13 Jul 2019 16:46:49 -0400 Subject: Refactored style in DocDecs and added metadata entry to DocDecs --- src/client/views/MetadataEntryMenu.tsx | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/client/views/MetadataEntryMenu.tsx (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx new file mode 100644 index 000000000..0dc7e0220 --- /dev/null +++ b/src/client/views/MetadataEntryMenu.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import "./MetadataEntryMenu.scss"; +import { observer } from 'mobx-react'; +import { observable, action } from 'mobx'; +import { KeyValueBox } from './nodes/KeyValueBox'; +import { Doc } from '../../new_fields/Doc'; + +export type DocLike = Doc | Doc[] | Promise | Promise; +export interface MetadataEntryProps { + docs: DocLike | (() => DocLike); + onError?: () => boolean; +} + +@observer +export class MetadataEntryMenu extends React.Component{ + @observable private _currentKey: string = ""; + @observable private _currentValue: string = ""; + + @action + onKeyChange = (e: React.ChangeEvent) => { + this._currentKey = e.target.value; + } + + @action + onValueChange = (e: React.ChangeEvent) => { + this._currentValue = e.target.value; + } + + 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 = ""; + } + + render() { + return ( +
+ Key: + + Value: + +
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 8e45625865b20a9e25b120d6938d3ec31aa880e3 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 13 Jul 2019 20:12:27 -0400 Subject: Kinda added auto complete --- package.json | 2 ++ src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MetadataEntryMenu.tsx | 49 ++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 3 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/package.json b/package.json index 6681c4c1c..22b3a6b21 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "@types/prosemirror-view": "^1.3.1", "@types/pug": "^2.0.4", "@types/react": "^16.8.7", + "@types/react-autosuggest": "^9.3.9", "@types/react-color": "^2.14.1", "@types/react-measure": "^2.0.4", "@types/react-table": "^6.7.22", @@ -169,6 +170,7 @@ "raw-loader": "^1.0.0", "react": "^16.8.4", "react-anime": "^2.2.0", + "react-autosuggest": "^9.4.3", "react-bootstrap": "^1.0.0-beta.5", "react-bootstrap-dropdown-menu": "^1.1.15", "react-color": "^2.17.0", diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 56fbd75a0..2cb3de50f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -639,7 +639,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return (
SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} />}>{/* tfs: @bcz This might need to be the data document? */} + content={ SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} suggestWithFunction />}>{/* tfs: @bcz This might need to be the data document? */}
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 0dc7e0220..0c8fd3909 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -1,20 +1,23 @@ import * as React from 'react'; import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; -import { observable, action } from 'mobx'; +import { observable, action, runInAction } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; import { Doc } from '../../new_fields/Doc'; +import * as Autosuggest from 'react-autosuggest'; export type DocLike = Doc | Doc[] | Promise | Promise; export interface MetadataEntryProps { docs: DocLike | (() => DocLike); onError?: () => boolean; + suggestWithFunction?: boolean; } @observer export class MetadataEntryMenu extends React.Component{ @observable private _currentKey: string = ""; @observable private _currentValue: string = ""; + @observable private suggestions: string[] = []; @action onKeyChange = (e: React.ChangeEvent) => { @@ -61,11 +64,53 @@ export class MetadataEntryMenu extends React.Component{ this._currentValue = ""; } + getKeySuggestions = async (value: string): Promise => { + 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(); + 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

{suggestion}

; + } + + onSuggestionFetch = async ({ value }: { value: string }) => { + const sugg = await this.getKeySuggestions(value); + runInAction(() => { + this.suggestions = sugg; + }); + } + + @action + onSuggestionClear = () => { + this.suggestions = []; + } + render() { return (
Key: - + Value:
-- cgit v1.2.3-70-g09d2 From 86bf6857e98743502fe6bc85e7de6807808c41c4 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 12:36:46 -0400 Subject: Fixed some of the auto suggest stuff --- src/client/views/MetadataEntryMenu.scss | 57 +++++++++++++++++++++++++++++++++ src/client/views/MetadataEntryMenu.tsx | 7 ++-- 2 files changed, 61 insertions(+), 3 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index 73e5b6a73..469843350 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -7,4 +7,61 @@ .metadataEntry-outerDiv { display: flex; width: 300px; +} + +.react-autosuggest__container { + position: relative; +} + +.react-autosuggest__input { + width: 240px; + height: 30px; + padding: 10px 20px; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border: 1px solid #aaa; + border-radius: 4px; +} + +.react-autosuggest__input--focused { + outline: none; +} + +.react-autosuggest__input--open { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.react-autosuggest__suggestions-container { + display: none; +} + +.react-autosuggest__suggestions-container--open { + display: block; + position: fixed; + width: 280px; + border: 1px solid #aaa; + background-color: #fff; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + z-index: 2; +} + +.react-autosuggest__suggestions-list { + margin: 0; + padding: 0; + list-style-type: none; +} + +.react-autosuggest__suggestion { + cursor: pointer; + padding: 10px 20px; +} + +.react-autosuggest__suggestion--highlighted { + background-color: #ddd; } \ No newline at end of file diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 0c8fd3909..cb574aa96 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; -import { observable, action, runInAction } from 'mobx'; +import { observable, action, runInAction, trace } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; import { Doc } from '../../new_fields/Doc'; import * as Autosuggest from 'react-autosuggest'; @@ -20,8 +20,8 @@ export class MetadataEntryMenu extends React.Component{ @observable private suggestions: string[] = []; @action - onKeyChange = (e: React.ChangeEvent) => { - this._currentKey = e.target.value; + onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { + this._currentKey = newValue; } @action @@ -102,6 +102,7 @@ export class MetadataEntryMenu extends React.Component{ } render() { + trace(); return (
Key: -- cgit v1.2.3-70-g09d2 From d09679619e7cf0fbae60e1f9220a6dbeb9de1bd7 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 14:35:50 -0400 Subject: Fixed metadata entry css --- src/client/views/MetadataEntryMenu.scss | 23 ++++++++++------------- src/client/views/MetadataEntryMenu.tsx | 11 +++++++++-- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index 469843350..a6df3cd1e 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -1,9 +1,3 @@ -.metadataEntry-input { - width: 40%; - margin-left: 5px; - margin-right: 5px; -} - .metadataEntry-outerDiv { display: flex; width: 300px; @@ -13,15 +7,18 @@ position: relative; } +.react-autosuggest__container, +.metadataEntry-input { + width: 100%; + margin-left: 5px; + margin-right: 5px; +} + +.metadataEntry-input, .react-autosuggest__input { - width: 240px; - height: 30px; - padding: 10px 20px; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; border: 1px solid #aaa; border-radius: 4px; + width: 100%; } .react-autosuggest__input--focused { @@ -40,7 +37,7 @@ .react-autosuggest__suggestions-container--open { display: block; position: fixed; - width: 280px; + width: 180px; border: 1px solid #aaa; background-color: #fff; font-family: Helvetica, sans-serif; diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index cb574aa96..59de0e2b4 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -19,6 +19,8 @@ export class MetadataEntryMenu extends React.Component{ @observable private _currentValue: string = ""; @observable private suggestions: string[] = []; + private autosuggestRef = React.createRef(); + @action onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { this._currentKey = newValue; @@ -62,6 +64,10 @@ export class MetadataEntryMenu extends React.Component{ clearInputs = () => { this._currentKey = ""; this._currentValue = ""; + if (this.autosuggestRef.current) { + const input: HTMLInputElement = (this.autosuggestRef.current as any).input; + input && input.focus(); + } } getKeySuggestions = async (value: string): Promise => { @@ -106,12 +112,13 @@ export class MetadataEntryMenu extends React.Component{ return (
Key: - + onSuggestionsClearRequested={this.onSuggestionClear} + ref={this.autosuggestRef} /> Value:
-- cgit v1.2.3-70-g09d2 From 7d9e29690956327d1ed9981cd2882d08b72b5c86 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 16:02:08 -0400 Subject: Added value preview to MetadataEntry --- src/client/views/MetadataEntryMenu.tsx | 46 +++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 59de0e2b4..08abb9887 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; 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 | Promise; @@ -18,17 +18,60 @@ export class MetadataEntryMenu extends React.Component{ @observable private _currentKey: string = ""; @observable private _currentValue: string = ""; @observable private suggestions: string[] = []; + private userModified = false; private autosuggestRef = React.createRef(); @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) => { this._currentValue = e.target.value; + this.userModified = e.target.value.trim() !== ""; } onValueKeyDown = async (e: React.KeyboardEvent) => { @@ -64,6 +107,7 @@ export class MetadataEntryMenu extends React.Component{ clearInputs = () => { this._currentKey = ""; this._currentValue = ""; + this.userModified = false; if (this.autosuggestRef.current) { const input: HTMLInputElement = (this.autosuggestRef.current as any).input; input && input.focus(); -- cgit v1.2.3-70-g09d2