aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/linking/LinkFollowBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/linking/LinkFollowBox.tsx')
-rw-r--r--src/client/views/linking/LinkFollowBox.tsx605
1 files changed, 605 insertions, 0 deletions
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
new file mode 100644
index 000000000..74663f9af
--- /dev/null
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -0,0 +1,605 @@
+import { observable, computed, action, runInAction, reaction, IReactionDisposer } from "mobx";
+import React = require("react");
+import { observer } from "mobx-react";
+import { FieldViewProps, FieldView } from "../nodes/FieldView";
+import { Doc, DocListCastAsync } from "../../../new_fields/Doc";
+import { undoBatch } from "../../util/UndoManager";
+import { NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
+import { CollectionViewType } from "../collections/CollectionBaseView";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { SelectionManager } from "../../util/SelectionManager";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DocumentView } from "../nodes/DocumentView";
+import "./LinkFollowBox.scss";
+import { SearchUtil } from "../../util/SearchUtil";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { listSpec } from "../../../new_fields/Schema";
+import { DocServer } from "../../DocServer";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faTimes } from '@fortawesome/free-solid-svg-icons';
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+
+enum FollowModes {
+ OPENTAB = "Open in Tab",
+ OPENRIGHT = "Open in Right Split",
+ OPENFULL = "Open Full Screen",
+ PAN = "Pan to Document",
+ INPLACE = "Open In Place"
+}
+
+enum FollowOptions {
+ ZOOM = "Zoom",
+ NOZOOM = "No Zoom",
+}
+
+@observer
+export class LinkFollowBox extends React.Component<FieldViewProps> {
+
+ public static LayoutString() { return FieldView.LayoutString(LinkFollowBox); }
+ public static Instance: LinkFollowBox | undefined;
+ @observable static linkDoc: Doc | undefined = undefined;
+ @observable static destinationDoc: Doc | undefined = undefined;
+ @observable static sourceDoc: Doc | undefined = undefined;
+ @observable selectedMode: string = "";
+ @observable selectedContext: Doc | undefined = undefined;
+ @observable selectedContextAliases: Doc[] | undefined = undefined;
+ @observable selectedOption: string = "";
+ @observable selectedContextString: string = "";
+ @observable sourceView: DocumentView | undefined = undefined;
+ @observable canPan: boolean = false;
+ @observable shouldUseOnlyParentContext = false;
+ _contextDisposer?: IReactionDisposer;
+
+ @observable private _docs: { col: Doc, target: Doc }[] = [];
+ @observable private _otherDocs: { col: Doc, target: Doc }[] = [];
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ LinkFollowBox.Instance = this;
+ this.resetVars();
+ this.props.Document.isBackground = true;
+ }
+
+ componentDidMount = () => {
+ this.resetVars();
+
+ this._contextDisposer = reaction(
+ () => this.selectedContextString,
+ async () => {
+ let ref = await DocServer.GetRefField(this.selectedContextString);
+ runInAction(() => {
+ if (ref instanceof Doc) {
+ this.selectedContext = ref;
+ }
+ });
+ if (this.selectedContext instanceof Doc) {
+ let aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext);
+ runInAction(() => { this.selectedContextAliases = aliases; });
+ }
+ }
+ );
+ }
+
+ componentWillUnmount = () => {
+ this._contextDisposer && this._contextDisposer();
+ }
+
+ async resetPan() {
+ if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionView) {
+ let colDoc = this.sourceView.props.ContainingCollectionView.props.Document;
+ runInAction(() => { this.canPan = false; });
+ if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) {
+ let docs = Cast(colDoc.data, listSpec(Doc), []);
+ let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
+
+ aliases.forEach(alias => {
+ if (docs.filter(doc => doc === alias).length > 0) {
+ runInAction(() => { this.canPan = true; });
+ }
+ });
+ }
+ }
+ }
+
+ @action
+ resetVars = () => {
+ this.selectedContext = undefined;
+ this.selectedContextString = "";
+ this.selectedMode = "";
+ this.selectedOption = "";
+ LinkFollowBox.linkDoc = undefined;
+ LinkFollowBox.sourceDoc = undefined;
+ LinkFollowBox.destinationDoc = undefined;
+ this.sourceView = undefined;
+ this.canPan = false;
+ this.shouldUseOnlyParentContext = false;
+ }
+
+ async fetchDocuments() {
+ if (LinkFollowBox.destinationDoc) {
+ let dest: Doc = LinkFollowBox.destinationDoc;
+ let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest));
+ const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${dest[Id]}"` });
+ const map: Map<Doc, Doc> = new Map;
+ const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
+ allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index])));
+ docs.forEach(doc => map.delete(doc));
+ runInAction(async () => {
+ this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest }));
+ this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target }));
+ let tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.targetContext, Doc)) as Doc;
+ runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest }));
+ });
+ }
+ }
+
+ @action
+ setLinkDocs = (linkDoc: Doc, source: Doc, dest: Doc) => {
+ this.resetVars();
+
+ LinkFollowBox.linkDoc = linkDoc;
+ LinkFollowBox.sourceDoc = source;
+ LinkFollowBox.destinationDoc = dest;
+ this.fetchDocuments();
+
+ SelectionManager.SelectedDocuments().forEach(dv => {
+ if (dv.props.Document === LinkFollowBox.sourceDoc) {
+ this.sourceView = dv;
+ }
+ });
+
+ this.resetPan();
+ }
+
+ unhighlight = () => {
+ Doc.UnhighlightAll();
+ document.removeEventListener("pointerdown", this.unhighlight);
+ }
+
+ @action
+ highlightDoc = () => {
+ if (LinkFollowBox.destinationDoc) {
+ document.removeEventListener("pointerdown", this.unhighlight);
+ Doc.HighlightDoc(LinkFollowBox.destinationDoc);
+ window.setTimeout(() => {
+ document.addEventListener("pointerdown", this.unhighlight);
+ }, 10000);
+ }
+ }
+
+ @undoBatch
+ openFullScreen = () => {
+ if (LinkFollowBox.destinationDoc) {
+ let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
+ view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ @undoBatch
+ openColFullScreen = (options: { context: Doc }) => {
+ if (LinkFollowBox.destinationDoc) {
+ if (NumCast(options.context.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
+ const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc.width) / NumCast(LinkFollowBox.destinationDoc.zoomBasis, 1) / 2;
+ const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc.height) / NumCast(LinkFollowBox.destinationDoc.zoomBasis, 1) / 2;
+ options.context.panX = newPanX;
+ options.context.panY = newPanY;
+ }
+ let view = DocumentManager.Instance.getDocumentView(options.context);
+ view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ // should container be a doc or documentview or what? This one needs work and is more long term
+ @undoBatch
+ openInContainer = (options: { container: Doc }) => {
+
+ }
+
+ @undoBatch
+ openLinkColRight = (options: { context: Doc, shouldZoom: boolean }) => {
+ if (LinkFollowBox.destinationDoc) {
+ options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context;
+ if (NumCast(options.context.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
+ const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc.width) / NumCast(LinkFollowBox.destinationDoc.zoomBasis, 1) / 2;
+ const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc.height) / NumCast(LinkFollowBox.destinationDoc.zoomBasis, 1) / 2;
+ options.context.panX = newPanX;
+ options.context.panY = newPanY;
+ }
+ CollectionDockingView.Instance.AddRightSplit(options.context, undefined);
+
+ if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
+
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ @undoBatch
+ openLinkRight = () => {
+ if (LinkFollowBox.destinationDoc) {
+ let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+ CollectionDockingView.Instance.AddRightSplit(alias, undefined);
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+
+ }
+
+ @undoBatch
+ jumpToLink = async (options: { shouldZoom: boolean }) => {
+ if (LinkFollowBox.destinationDoc && LinkFollowBox.linkDoc) {
+ let jumpToDoc: Doc = LinkFollowBox.destinationDoc;
+ let pdfDoc = FieldValue(Cast(LinkFollowBox.destinationDoc, Doc));
+ if (pdfDoc) {
+ jumpToDoc = pdfDoc;
+ }
+ let proto = Doc.GetProto(LinkFollowBox.linkDoc);
+ let targetContext = await Cast(proto.targetContext, Doc);
+ let sourceContext = await Cast(proto.sourceContext, Doc);
+
+ let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
+
+ if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, false, async document => dockingFunc(document), undefined, targetContext);
+ }
+ else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, false, document => dockingFunc(sourceContext!));
+ }
+ else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, undefined, undefined,
+ NumCast((LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 ? LinkFollowBox.linkDoc.anchor2Page : LinkFollowBox.linkDoc.anchor1Page)));
+
+ }
+ else {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, false, dockingFunc);
+ }
+
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ @undoBatch
+ openLinkTab = () => {
+ if (LinkFollowBox.destinationDoc) {
+ let fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+ // THIS IS EMPTY FUNCTION
+ this.props.addDocTab(fullScreenAlias, undefined, "inTab");
+ console.log(this.props.addDocTab);
+
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ @undoBatch
+ openLinkColTab = (options: { context: Doc, shouldZoom: boolean }) => {
+ if (LinkFollowBox.destinationDoc) {
+ options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context;
+ if (NumCast(options.context.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) {
+ const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc.width) / NumCast(LinkFollowBox.destinationDoc.zoomBasis, 1) / 2;
+ const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc.height) / NumCast(LinkFollowBox.destinationDoc.zoomBasis, 1) / 2;
+ options.context.panX = newPanX;
+ options.context.panY = newPanY;
+ }
+ this.props.addDocTab(options.context, undefined, "inTab");
+ if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom });
+
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ @undoBatch
+ openLinkInPlace = (options: { shouldZoom: boolean }) => {
+
+ if (LinkFollowBox.destinationDoc && LinkFollowBox.sourceDoc) {
+ let alias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
+ let y = NumCast(LinkFollowBox.sourceDoc.y);
+ let x = NumCast(LinkFollowBox.sourceDoc.x);
+
+ let width = NumCast(LinkFollowBox.sourceDoc.width);
+ let height = NumCast(LinkFollowBox.sourceDoc.height);
+
+ alias.x = x + width + 30;
+ alias.y = y;
+ alias.width = width;
+ alias.height = height;
+
+ if (this.sourceView && this.sourceView.props.addDocument) {
+ this.sourceView.props.addDocument(alias, false);
+ }
+
+ this.jumpToLink({ shouldZoom: options.shouldZoom });
+
+ this.highlightDoc();
+ SelectionManager.DeselectAll();
+ }
+ }
+
+ //set this to be the default link behavior, can be any of the above
+ public defaultLinkBehavior: (options?: any) => void = this.openLinkTab;
+
+ @action
+ currentLinkBehavior = () => {
+ // this.resetPan();
+ if (LinkFollowBox.destinationDoc) {
+ if (this.selectedContextString === "") {
+ this.selectedContextString = "self";
+ this.selectedContext = LinkFollowBox.destinationDoc;
+ }
+ if (this.selectedOption === "") this.selectedOption = FollowOptions.NOZOOM;
+ let shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true;
+ let notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id];
+
+ if (this.selectedMode === FollowModes.INPLACE) {
+ if (shouldZoom !== undefined) this.openLinkInPlace({ shouldZoom: shouldZoom });
+ }
+ else if (this.selectedMode === FollowModes.OPENFULL) {
+ if (notOpenInContext) this.openFullScreen();
+ else this.selectedContext && this.openColFullScreen({ context: this.selectedContext });
+ }
+ else if (this.selectedMode === FollowModes.OPENRIGHT) {
+ if (notOpenInContext) this.openLinkRight();
+ else this.selectedContext && this.openLinkColRight({ context: this.selectedContext, shouldZoom: shouldZoom });
+ }
+ else if (this.selectedMode === FollowModes.OPENTAB) {
+ if (notOpenInContext) this.openLinkTab();
+ else this.selectedContext && this.openLinkColTab({ context: this.selectedContext, shouldZoom: shouldZoom });
+ }
+ else if (this.selectedMode === FollowModes.PAN) {
+ this.jumpToLink({ shouldZoom: shouldZoom });
+ }
+ else return;
+ }
+ }
+
+ @action
+ handleModeChange = (e: React.ChangeEvent) => {
+ let target = e.target as HTMLInputElement;
+ this.selectedMode = target.value;
+ this.selectedContext = undefined;
+ this.selectedContextString = "";
+
+ this.shouldUseOnlyParentContext = (this.selectedMode === FollowModes.INPLACE || this.selectedMode === FollowModes.PAN);
+
+ if (this.shouldUseOnlyParentContext) {
+ if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
+ this.selectedContext = this.sourceView.props.ContainingCollectionView.props.Document;
+ this.selectedContextString = (StrCast(this.sourceView.props.ContainingCollectionView.props.Document.title));
+ }
+ }
+ }
+
+ @action
+ handleOptionChange = (e: React.ChangeEvent) => {
+ let target = e.target as HTMLInputElement;
+ this.selectedOption = target.value;
+ }
+
+ @action
+ handleContextChange = (e: React.ChangeEvent) => {
+ let target = e.target as HTMLInputElement;
+ this.selectedContextString = target.value;
+ // selectedContext is updated in reaction
+ this.selectedOption = "";
+ }
+
+ @computed
+ get canOpenInPlace() {
+ if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
+ let colView = this.sourceView.props.ContainingCollectionView;
+ let colDoc = colView.props.Document;
+ if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) return true;
+ }
+ return false;
+ }
+
+ @computed
+ get availableModes() {
+ return (
+ <div>
+ <label><input
+ type="radio"
+ name="mode"
+ value={FollowModes.OPENRIGHT}
+ checked={this.selectedMode === FollowModes.OPENRIGHT}
+ onChange={this.handleModeChange}
+ disabled={false} />
+ {FollowModes.OPENRIGHT}
+ </label><br />
+ <label><input
+ type="radio"
+ name="mode"
+ value={FollowModes.OPENTAB}
+ checked={this.selectedMode === FollowModes.OPENTAB}
+ onChange={this.handleModeChange}
+ disabled={false} />
+ {FollowModes.OPENTAB}
+ </label><br />
+ <label><input
+ type="radio"
+ name="mode"
+ value={FollowModes.OPENFULL}
+ checked={this.selectedMode === FollowModes.OPENFULL}
+ onChange={this.handleModeChange}
+ disabled={false} />
+ {FollowModes.OPENFULL}
+ </label><br />
+ <label><input
+ type="radio"
+ name="mode"
+ value={FollowModes.PAN}
+ checked={this.selectedMode === FollowModes.PAN}
+ onChange={this.handleModeChange}
+ disabled={!this.canPan} />
+ {FollowModes.PAN}
+ </label><br />
+ <label><input
+ type="radio"
+ name="mode"
+ value={FollowModes.INPLACE}
+ checked={this.selectedMode === FollowModes.INPLACE}
+ onChange={this.handleModeChange}
+ disabled={!this.canOpenInPlace} />
+ {FollowModes.INPLACE}
+ </label><br />
+ </div>
+ );
+ }
+
+ @computed
+ get parentName() {
+ if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
+ let colView = this.sourceView.props.ContainingCollectionView;
+ return colView.props.Document.title;
+ }
+ }
+
+ @computed
+ get parentID(): string {
+ if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
+ let colView = this.sourceView.props.ContainingCollectionView;
+ return StrCast(colView.props.Document[Id]);
+ }
+ return "col";
+ }
+
+ @computed
+ get availableContexts() {
+ return (
+ this.shouldUseOnlyParentContext ?
+ <label><input
+ type="radio" disabled={true}
+ name="context"
+ value={this.parentID}
+ checked={true} />
+ {this.parentName} (Parent Collection)
+ </label>
+ :
+ <div>
+ <label><input
+ type="radio" disabled={LinkFollowBox.linkDoc ? false : true}
+ name="context"
+ value={LinkFollowBox.destinationDoc ? StrCast(LinkFollowBox.destinationDoc[Id]) : "self"}
+ checked={LinkFollowBox.destinationDoc ? this.selectedContextString === StrCast(LinkFollowBox.destinationDoc[Id]) || this.selectedContextString === "self" : true}
+ onChange={this.handleContextChange} />
+ Open Self
+ </label><br />
+ {[...this._docs, ...this._otherDocs].map(doc => {
+ if (doc && doc.target && doc.col.title !== "Recently Closed") {
+ return <div key={doc.col[Id] + doc.target[Id]}><label key={doc.col[Id] + doc.target[Id]}>
+ <input
+ type="radio" disabled={LinkFollowBox.linkDoc ? false : true}
+ name="context"
+ value={StrCast(doc.col[Id])}
+ checked={this.selectedContextString === StrCast(doc.col[Id])}
+ onChange={this.handleContextChange} />
+ {doc.col.title}
+ </label><br /></div>;
+ }
+ })}
+ </div>
+ );
+ }
+
+ @computed
+ get shouldShowZoom(): boolean {
+ if (this.selectedMode === FollowModes.OPENFULL) return false;
+ if (this.shouldUseOnlyParentContext) return true;
+ if (LinkFollowBox.destinationDoc ? this.selectedContextString === LinkFollowBox.destinationDoc[Id] : "self") return false;
+
+ let contextMatch: boolean = false;
+ if (this.selectedContextAliases) {
+ this.selectedContextAliases.forEach(alias => {
+ if (alias.viewType === CollectionViewType.Freeform) contextMatch = true;
+ });
+ }
+ if (contextMatch) return true;
+
+ return false;
+ }
+
+ @computed
+ get availableOptions() {
+ if (LinkFollowBox.destinationDoc) {
+ return (
+ this.shouldShowZoom ?
+ <div>
+ <label><input
+ type="radio"
+ name="option"
+ value={FollowOptions.ZOOM}
+ checked={this.selectedOption === FollowOptions.ZOOM}
+ onChange={this.handleOptionChange}
+ disabled={false} />
+ {FollowOptions.ZOOM}
+ </label><br />
+ <label><input
+ type="radio"
+ name="option"
+ value={FollowOptions.NOZOOM}
+ checked={this.selectedOption === FollowOptions.NOZOOM}
+ onChange={this.handleOptionChange}
+ disabled={false} />
+ {FollowOptions.NOZOOM}
+ </label><br />
+ </div>
+ :
+ <div>No Available Options</div>
+ );
+ }
+ return null;
+ }
+
+ render() {
+ return (
+ <div className="linkFollowBox-main" style={{ height: NumCast(this.props.Document.height), width: NumCast(this.props.Document.width) }}>
+ <div className="linkFollowBox-header">
+ <div className="topHeader">
+ {LinkFollowBox.linkDoc ? "Link Title: " + StrCast(LinkFollowBox.linkDoc.title) : "No Link Selected"}
+ <div onClick={() => this.props.Document.isMinimized = true} className="closeDocument"><FontAwesomeIcon icon={faTimes} size="lg" /></div>
+ </div>
+ <div className=" direction-indicator">{LinkFollowBox.linkDoc ?
+ LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc ? "Source: " + StrCast(LinkFollowBox.sourceDoc.title) + ", Destination: " + StrCast(LinkFollowBox.destinationDoc.title)
+ : "" : ""}</div>
+ </div>
+ <div className="linkFollowBox-content" style={{ height: NumCast(this.props.Document.height) - 110 }}>
+ <div className="linkFollowBox-item">
+ <div className="linkFollowBox-item title">Mode</div>
+ <div className="linkFollowBox-itemContent">
+ {LinkFollowBox.linkDoc ? this.availableModes : "Please select a link to view modes"}
+ </div>
+ </div>
+ <div className="linkFollowBox-item">
+ <div className="linkFollowBox-item title">Context</div>
+ <div className="linkFollowBox-itemContent">
+ {this.selectedMode !== "" ? this.availableContexts : "Please select a mode to view contexts"}
+ </div>
+ </div>
+ <div className="linkFollowBox-item">
+ <div className="linkFollowBox-item title">Options</div>
+ <div className="linkFollowBox-itemContent">
+ {this.selectedContextString !== "" ? this.availableOptions : "Please select a context to view options"}
+ </div>
+ </div>
+ </div>
+ <div className="linkFollowBox-footer">
+ <button
+ onClick={this.resetVars}>
+ Clear Link
+ </button>
+ <div style={{ width: 20 }}></div>
+ <button
+ onClick={this.currentLinkBehavior}
+ disabled={(LinkFollowBox.linkDoc) ? false : true}>
+ Follow Link
+ </button>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file