diff options
Diffstat (limited to 'src/client/views/linking/LinkEditor.tsx')
-rw-r--r-- | src/client/views/linking/LinkEditor.tsx | 103 |
1 files changed, 81 insertions, 22 deletions
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index f74b422d3..240a71c3e 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -2,11 +2,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../fields/Doc"; +import { Doc, StrListCast } from "../../../fields/Doc"; import { DateCast, StrCast } from "../../../fields/Types"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; import './LinkEditor.scss'; +import { LinkRelationshipSearch } from "./LinkRelationshipSearch"; import React = require("react"); @@ -26,6 +27,8 @@ export class LinkEditor extends React.Component<LinkEditorProps> { @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; } @observable private buttonColor: string = ""; @observable private relationshipButtonColor: string = ""; + @observable private relationshipSearchVisibility: string = "none"; + @observable private searchIsActive: boolean = false; //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION"; @@ -39,12 +42,40 @@ export class LinkEditor extends React.Component<LinkEditorProps> { setRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { LinkManager.currentLink.linkRelationship = value; + const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); + const linkColorList = StrListCast(Doc.UserDoc().linkColorList); + // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color + if (linkRelationshipList && !linkRelationshipList.includes(value)) { + linkRelationshipList.push(value); + const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")"; + linkColorList.push(randColor); + } this.relationshipButtonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.relationshipButtonColor = ""), 750); return true; } }); + /** + * returns list of strings with possible existing relationships that contain what is currently in the input field + */ + @action + getRelationshipResults = () => { + const query = this.relationship; //current content in input box + const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); + if (linkRelationshipList) { + return linkRelationshipList.filter(rel => rel.includes(query)); + } + } + + /** + * toggles visibility of the relationship search results when the input field is focused on + */ + @action + toggleRelationshipResults = () => { + this.relationshipSearchVisibility = this.relationshipSearchVisibility === "none" ? "block" : "none"; + } + @undoBatch setDescripValue = action((value: string) => { if (LinkManager.currentLink) { @@ -55,7 +86,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> { } }); - onKey = (e: React.KeyboardEvent<HTMLInputElement>) => { + onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => { if (e.key === "Enter") { this.setDescripValue(this.description); document.getElementById('input')?.blur(); @@ -69,16 +100,38 @@ export class LinkEditor extends React.Component<LinkEditorProps> { } } - onDown = () => this.setDescripValue(this.description); - onRelationshipDown = () => this.setRelationshipValue(this.description); + onDescriptionDown = () => this.setDescripValue(this.description); + onRelationshipDown = () => this.setRelationshipValue(this.relationship); + + onBlur = () => { + //only hide the search results if the user clicks out of the input AND not on any of the search results + // i.e. if search is not active + if (!this.searchIsActive) { + this.toggleRelationshipResults(); + } + } + onFocus = () => { + this.toggleRelationshipResults(); + } + toggleSearchIsActive = () => { + this.searchIsActive = !this.searchIsActive; + } @action - handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; } + handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; } @action - handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; } - + handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { + this.relationship = e.target.value; + } + @action + handleRelationshipSearchChange = (result: string) => { + this.setRelationshipValue(result); + this.toggleRelationshipResults(); + this.relationship = result; + } @computed get editRelationship() { + //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS return <div className="linkEditor-description"> <div className="linkEditor-description-label">Link Relationship:</div> <div className="linkEditor-description-input"> @@ -87,11 +140,18 @@ export class LinkEditor extends React.Component<LinkEditorProps> { style={{ width: "100%" }} id="input" value={this.relationship} - placeholder={"enter link label"} - // color={"rgb(88, 88, 88)"} + placeholder={"Enter link relationship"} onKeyDown={this.onRelationshipKey} onChange={this.handleRelationshipChange} + onFocus={this.onFocus} + onBlur={this.onBlur} ></input> + <LinkRelationshipSearch + results={this.getRelationshipResults()} + display={this.relationshipSearchVisibility} + handleRelationshipSearchChange={this.handleRelationshipSearchChange} + toggleSearch={this.toggleSearchIsActive} + /> </div> <div className="linkEditor-description-add-button" style={{ background: this.relationshipButtonColor }} @@ -110,15 +170,14 @@ export class LinkEditor extends React.Component<LinkEditorProps> { style={{ width: "100%" }} id="input" value={this.description} - placeholder={"enter link label"} - // color={"rgb(88, 88, 88)"} - onKeyDown={this.onKey} - onChange={this.handleChange} + placeholder={"Enter link description"} + onKeyDown={this.onDescriptionKey} + onChange={this.handleDescriptionChange} ></input> </div> <div className="linkEditor-description-add-button" style={{ background: this.buttonColor }} - onPointerDown={this.onDown}>Set</div> + onPointerDown={this.onDescriptionDown}>Set</div> </div> </div>; } @@ -149,35 +208,35 @@ export class LinkEditor extends React.Component<LinkEditorProps> { <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("default")}> Default - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("add:left")}> Always open in new left pane - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("add:right")}> Always open in new right pane - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("replace:right")}> Always replace right tab - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("replace:left")}> Always replace left tab - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("fullScreen")}> Always open full screen - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("add")}> Always open in a new tab - </div> + </div> <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("replace")}> Replace Tab - </div> + </div> {this.props.linkDoc.linksToAnnotation ? <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior("openExternal")}> |