aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/linking/LinkEditor.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/linking/LinkEditor.tsx')
-rw-r--r--src/client/views/linking/LinkEditor.tsx346
1 files changed, 185 insertions, 161 deletions
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 1414bfdf7..ba301962b 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -1,15 +1,14 @@
-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, NumListCast, StrListCast, Field } from "../../../fields/Doc";
-import { DateCast, StrCast, Cast } from "../../../fields/Types";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch } from "../../util/UndoManager";
+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, NumListCast, StrListCast, Field } from '../../../fields/Doc';
+import { DateCast, StrCast, Cast } from '../../../fields/Types';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
import './LinkEditor.scss';
-import { LinkRelationshipSearch } from "./LinkRelationshipSearch";
-import React = require("react");
-
+import { LinkRelationshipSearch } from './LinkRelationshipSearch';
+import React = require('react');
interface LinkEditorProps {
sourceDoc: Doc;
@@ -19,16 +18,20 @@ interface LinkEditorProps {
}
@observer
export class LinkEditor extends React.Component<LinkEditorProps> {
-
@observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
@observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
@observable zoomFollow = StrCast(this.props.sourceDoc.followLinkZoom);
@observable openDropdown: boolean = false;
@observable showInfo: boolean = false;
- @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";
+ @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";
@@ -37,7 +40,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
deleteLink = (): void => {
LinkManager.Instance.deleteLink(this.props.linkDoc);
this.props.showLinks();
- }
+ };
@undoBatch
setRelationshipValue = action((value: string) => {
@@ -49,11 +52,11 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
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 the relationship does not exist in the list, add it and a corresponding unique randomly generated color
if (!linkRelationshipList?.includes(value)) {
linkRelationshipList.push(value);
linkRelationshipSizes.push(1);
- const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
+ const randColor = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
linkColorList.push(randColor);
// if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
} else if (linkRelationshipList && value !== prevRelationship) {
@@ -61,20 +64,22 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
//increment size of new relationship size
if (index !== -1 && index < linkRelationshipSizes.length) {
const pvalue = linkRelationshipSizes[index];
- linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1);
+ linkRelationshipSizes[index] = pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1;
}
//decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
if (linkRelationshipList.includes(prevRelationship)) {
const pindex = linkRelationshipList.indexOf(prevRelationship);
if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
const pvalue = linkRelationshipSizes[pindex];
- linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1));
+ linkRelationshipSizes[pindex] = Math.max(0, pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1);
}
}
-
}
- this.relationshipButtonColor = "rgb(62, 133, 55)";
- setTimeout(action(() => this.relationshipButtonColor = ""), 750);
+ this.relationshipButtonColor = 'rgb(62, 133, 55)';
+ setTimeout(
+ action(() => (this.relationshipButtonColor = '')),
+ 750
+ );
return true;
}
});
@@ -89,144 +94,143 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
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";
- }
+ this.relationshipSearchVisibility = this.relationshipSearchVisibility === 'none' ? 'block' : 'none';
+ };
@undoBatch
setDescripValue = action((value: string) => {
if (LinkManager.currentLink) {
Doc.GetProto(LinkManager.currentLink).description = value;
- this.buttonColor = "rgb(62, 133, 55)";
- setTimeout(action(() => this.buttonColor = ""), 750);
+ this.buttonColor = 'rgb(62, 133, 55)';
+ setTimeout(
+ action(() => (this.buttonColor = '')),
+ 750
+ );
return true;
}
});
onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key === "Enter") {
+ if (e.key === 'Enter') {
this.setDescripValue(this.description);
document.getElementById('input')?.blur();
}
e.stopPropagation();
- }
+ };
onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key === "Enter") {
+ if (e.key === 'Enter') {
this.setRelationshipValue(this.relationship);
document.getElementById('input')?.blur();
}
e.stopPropagation();
- }
+ };
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
+ //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
- handleDescriptionChange = (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
- handleZoomFollowChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.props.sourceDoc.followLinkZoom = e.target.checked; }
+ handleZoomFollowChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.props.sourceDoc.followLinkZoom = e.target.checked;
+ };
@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">
- <div className="linkEditor-description-editing">
- <input
- style={{ width: "100%" }}
- id="input"
- value={this.relationship}
- autoComplete={"off"}
- 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}
- />
+ return (
+ <div className="linkEditor-description">
+ <div className="linkEditor-description-label">Link Relationship:</div>
+ <div className="linkEditor-description-input">
+ <div className="linkEditor-description-editing">
+ <input
+ style={{ width: '100%' }}
+ id="input"
+ value={this.relationship}
+ autoComplete={'off'}
+ 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 }} onPointerDown={this.onRelationshipDown}>
+ Set
+ </div>
</div>
- <div className="linkEditor-description-add-button"
- style={{ background: this.relationshipButtonColor }}
- onPointerDown={this.onRelationshipDown}>Set</div>
</div>
- </div>;
+ );
}
@computed
get editZoomFollow() {
//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-zoomFollow">
- <div className="linkEditor-zoomFollow-label">Zoom To Link Target:</div>
- <div className="linkEditor-zoomFollow-input">
- <div className="linkEditor-zoomFollow-editing">
- <input
- style={{ width: "100%" }}
- type="checkbox"
- value={this.zoomFollow}
- onChange={this.handleZoomFollowChange} />
+ return (
+ <div className="linkEditor-zoomFollow">
+ <div className="linkEditor-zoomFollow-label">Zoom To Link Target:</div>
+ <div className="linkEditor-zoomFollow-input">
+ <div className="linkEditor-zoomFollow-editing">
+ <input style={{ width: '100%' }} type="checkbox" value={this.zoomFollow} onChange={this.handleZoomFollowChange} />
+ </div>
</div>
</div>
- </div >;
+ );
}
@computed
get editDescription() {
- return <div className="linkEditor-description">
- <div className="linkEditor-description-label">Link Description:</div>
- <div className="linkEditor-description-input">
- <div className="linkEditor-description-editing">
- <input
- style={{ width: "100%" }}
- autoComplete={"off"}
- id="input"
- value={this.description}
- placeholder={"Enter link description"}
- onKeyDown={this.onDescriptionKey}
- onChange={this.handleDescriptionChange}
- ></input>
+ return (
+ <div className="linkEditor-description">
+ <div className="linkEditor-description-label">Link Description:</div>
+ <div className="linkEditor-description-input">
+ <div className="linkEditor-description-editing">
+ <input style={{ width: '100%' }} autoComplete={'off'} id="input" value={this.description} placeholder={'Enter link description'} onKeyDown={this.onDescriptionKey} onChange={this.handleDescriptionChange}></input>
+ </div>
+ <div className="linkEditor-description-add-button" style={{ background: this.buttonColor }} onPointerDown={this.onDescriptionDown}>
+ Set
+ </div>
</div>
- <div className="linkEditor-description-add-button"
- style={{ background: this.buttonColor }}
- onPointerDown={this.onDescriptionDown}>Set</div>
</div>
- </div>;
+ );
}
@action
- changeDropdown = () => { this.openDropdown = !this.openDropdown; }
+ changeDropdown = () => {
+ this.openDropdown = !this.openDropdown;
+ };
@undoBatch
changeFollowBehavior = action((follow: string) => {
@@ -236,94 +240,114 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
@computed
get followingDropdown() {
- return <div className="linkEditor-followingDropdown">
- <div className="linkEditor-followingDropdown-label">Follow Behavior:</div>
- <div className="linkEditor-followingDropdown-dropdown">
- <div className="linkEditor-followingDropdown-header"
- onPointerDown={this.changeDropdown}>
- {StrCast(this.props.linkDoc.followLinkLocation, "default")}
- <FontAwesomeIcon className="linkEditor-followingDropdown-icon"
- icon={this.openDropdown ? "chevron-up" : "chevron-down"}
- size={"lg"} />
- </div>
- <div className="linkEditor-followingDropdown-optionsList"
- style={{ display: this.openDropdown ? "" : "none" }}>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("default")}>
- Default
+ return (
+ <div className="linkEditor-followingDropdown">
+ <div className="linkEditor-followingDropdown-label">Follow Behavior:</div>
+ <div className="linkEditor-followingDropdown-dropdown">
+ <div className="linkEditor-followingDropdown-header" onPointerDown={this.changeDropdown}>
+ {StrCast(this.props.linkDoc.followLinkLocation, 'default')}
+ <FontAwesomeIcon className="linkEditor-followingDropdown-icon" icon={this.openDropdown ? 'chevron-up' : 'chevron-down'} size={'lg'} />
</div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("add:left")}>
- Always open in new left pane
- </div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("add:right")}>
- Always open in new right pane
- </div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("replace:right")}>
- Always replace right tab
- </div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("replace:left")}>
- Always replace left tab
- </div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
- Always open full screen
- </div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("add")}>
- Always open in a new tab
- </div>
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("replace")}>
- Replace Tab
- </div>
- {this.props.linkDoc.linksToAnnotation ?
- <div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("openExternal")}>
- Always open in external page
+ <div className="linkEditor-followingDropdown-optionsList" style={{ display: this.openDropdown ? '' : 'none' }}>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('default')}>
+ Default
+ </div>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add:left')}>
+ Always open in new left pane
+ </div>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add:right')}>
+ Always open in new right pane
+ </div>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace:right')}>
+ Always replace right tab
+ </div>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace:left')}>
+ Always replace left tab
</div>
- : null}
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('fullScreen')}>
+ Always open full screen
+ </div>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add')}>
+ Always open in a new tab
+ </div>
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace')}>
+ Replace Tab
+ </div>
+ {this.props.linkDoc.linksToAnnotation ? (
+ <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('openExternal')}>
+ Always open in external page
+ </div>
+ ) : null}
+ </div>
</div>
</div>
- </div>;
+ );
}
@action
- changeInfo = () => { this.showInfo = !this.showInfo; }
+ changeInfo = () => {
+ this.showInfo = !this.showInfo;
+ };
render() {
const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
- return !destination ? (null) : (
+ return !destination ? null : (
<div className="linkEditor" tabIndex={0} onKeyDown={e => e.stopPropagation()}>
<div className="linkEditor-info">
- <Tooltip title={<><div className="dash-tooltip">Return to link menu</div></>} placement="top">
- <button className="linkEditor-button-back"
- style={{ display: this.props.hideback ? "none" : "" }}
- onClick={this.props.showLinks}>
- <FontAwesomeIcon icon="arrow-left" size="sm" /> </button>
+ <Tooltip
+ title={
+ <>
+ <div className="dash-tooltip">Return to link menu</div>
+ </>
+ }
+ placement="top">
+ <button className="linkEditor-button-back" style={{ display: this.props.hideback ? 'none' : '' }} onClick={this.props.showLinks}>
+ <FontAwesomeIcon icon="arrow-left" size="sm" />{' '}
+ </button>
</Tooltip>
- <p className="linkEditor-linkedTo">Editing Link to: <b>{
- destination.proto?.title ?? destination.title ?? "untitled"}</b></p>
- <Tooltip title={<><div className="dash-tooltip">Show more link information</div></>} placement="top">
- <div className="linkEditor-downArrow"><FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} /></div>
+ <p className="linkEditor-linkedTo">
+ Editing Link to: <b>{StrCast(destination.proto?.title, StrCast(destination.title, 'untitled'))}</b>
+ </p>
+ <Tooltip
+ title={
+ <>
+ <div className="dash-tooltip">Show more link information</div>
+ </>
+ }
+ placement="top">
+ <div className="linkEditor-downArrow">
+ <FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} />
+ </div>
</Tooltip>
</div>
- {this.showInfo ? <div className="linkEditor-moreInfo">
- <div>{this.props.linkDoc.author ? <div> <b>Author:</b> {this.props.linkDoc.author}</div> : null}</div>
- <div>{this.props.linkDoc.creationDate ? <div> <b>Creation Date:</b>
- {DateCast(this.props.linkDoc.creationDate).toString()}</div> : null}</div>
- </div> : null}
+ {this.showInfo ? (
+ <div className="linkEditor-moreInfo">
+ <div>
+ {this.props.linkDoc.author ? (
+ <div>
+ {' '}
+ <b>Author:</b> {StrCast(this.props.linkDoc.author)}
+ </div>
+ ) : null}
+ </div>
+ <div>
+ {this.props.linkDoc.creationDate ? (
+ <div>
+ {' '}
+ <b>Creation Date:</b>
+ {DateCast(this.props.linkDoc.creationDate).toString()}
+ </div>
+ ) : null}
+ </div>
+ </div>
+ ) : null}
{this.editDescription}
{this.editRelationship}
{this.editZoomFollow}
{this.followingDropdown}
</div>
-
);
}
-} \ No newline at end of file
+}