aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/LinkDocPreview.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/LinkDocPreview.tsx')
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx232
1 files changed, 154 insertions, 78 deletions
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 4808feb47..04a407eab 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -1,121 +1,197 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from "mobx-react";
import wiki from "wikijs";
-import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { Id } from '../../../fields/FieldSymbols';
-import { Cast, FieldValue, NumCast } from "../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, emptyPath } from "../../../Utils";
+import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { NumCast, StrCast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
+import { DocServer } from '../../DocServer';
import { Docs } from "../../documents/Documents";
import { LinkManager } from '../../util/LinkManager';
import { Transform } from "../../util/Transform";
-import { ContextMenu } from '../ContextMenu';
-import { DocumentLinksButton } from './DocumentLinksButton';
-import { DocumentView, StyleProviderFunc, DocumentViewSharedProps } from "./DocumentView";
+import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
+import './LinkDocPreview.scss';
import React = require("react");
-interface Props {
+interface LinkDocPreviewProps {
linkDoc?: Doc;
linkSrc?: Doc;
href?: string;
docprops: DocumentViewSharedProps;
location: number[];
+ hrefs?: string[];
+ showHeader?: boolean;
}
@observer
-export class LinkDocPreview extends React.Component<Props> {
+export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@action public static Clear() { LinkDocPreview.LinkInfo = undefined; }
- @action public static SetLinkInfo(info: { linkDoc?: Doc; linkSrc: Doc; href?: string; Location: number[], docprops: DocumentViewSharedProps }) {
- LinkDocPreview.LinkInfo = info;
- }
- @observable public static LinkInfo: Opt<{ linkDoc?: Doc; linkSrc: Doc; href?: string; Location: number[], docprops: DocumentViewSharedProps }>;
+ @action public static SetLinkInfo(info?: LinkDocPreviewProps) { LinkDocPreview.LinkInfo != info && (LinkDocPreview.LinkInfo = info); }
+
+ _infoRef = React.createRef<HTMLDivElement>();
+ @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
@observable _targetDoc: Opt<Doc>;
+ @observable _linkDoc: Opt<Doc>;
+ @observable _linkSrc: Opt<Doc>;
@observable _toolTipText = "";
- _linkTarget: Opt<Doc>;
- _editRef = React.createRef<HTMLDivElement>();
+ @observable _hrefInd = 0;
+ @observable _linkTarget: Opt<Doc>;
- @action
- componentWillUnmount() { LinkDocPreview.LinkInfo = undefined; }
+ @action componentDidUpdate(props: any) {
+ if (props.linkSrc !== this.props.linkSrc ||
+ props.linkDoc !== this.props.linkDoc ||
+ props.hrefs !== this.props.hrefs) {
+ this._linkTarget = this.props.linkDoc;
+ this._linkSrc = this.props.linkSrc;
+ this._linkDoc = this.props.linkDoc;
+ this._toolTipText = "";
+ this.updatePreview();
+ }
+ }
+ @action componentDidMount() {
+ this._linkTarget = this.props.linkDoc;
+ this._linkSrc = this.props.linkSrc;
+ this._linkDoc = this.props.linkDoc;
+ this._toolTipText = "";
+ this.updatePreview();
+ document.addEventListener("pointerdown", this.onPointerDown);
+ }
+
+ componentWillUnmount() {
+ LinkDocPreview.SetLinkInfo(undefined);
+ document.removeEventListener("pointerdown", this.onPointerDown);
+ }
+
+ onPointerDown = (e: PointerEvent) => {
+ !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear();
+ }
- componentDidUpdate() { this.updatePreview(); }
- componentDidMount() { this.updatePreview(); }
- async updatePreview() {
+ updatePreview() {
const linkDoc = this.props.linkDoc;
const linkSrc = this.props.linkSrc;
- if (this.props.href) {
- if (this.props.href.startsWith("https://en.wikipedia.org/wiki/")) {
- wiki().page(this.props.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
+ if (this.props.hrefs?.length) {
+ const href = this.props.hrefs[this._hrefInd];
+ if (href.indexOf(Utils.prepend("/doc/")) !== 0) {
+ if (href.startsWith("https://en.wikipedia.org/wiki/")) {
+ wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
+ } else {
+ runInAction(() => this._toolTipText = "external => " + href);
+ }
} else {
- runInAction(() => this._toolTipText = "external => " + this.props.href);
+ const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ anchorDoc && DocServer.GetRefField(anchorDoc).then(action(async anchor => {
+ if (anchor instanceof Doc) {
+ this._linkDoc = DocListCast(anchor.links)[0];
+ this._linkSrc = anchor;
+ const targetanchor = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+ runInAction(async () => {
+ this._linkTarget = targetanchor;
+ const target = this._linkTarget?.annotationOn ? await DocCastAsync(this._linkTarget.annotationOn) : this._linkTarget;
+ this._toolTipText = "";
+ runInAction(() => this._targetDoc = target);
+ });
+ }
+ }));
}
- } else if (linkDoc && linkSrc) {
+ } else if (linkDoc) {
const anchor1 = linkDoc.anchor1 as Doc;
const anchor2 = linkDoc.anchor2 as Doc;
- this._linkTarget = Doc.AreProtosEqual(anchor1, linkSrc) || Doc.AreProtosEqual(anchor1.annotationOn as Doc, linkSrc) ? anchor2 : anchor1;
- const target = this._linkTarget?.annotationOn ? await DocCastAsync(this._linkTarget.annotationOn) : this._linkTarget;
- runInAction(() => {
+ runInAction(async () => {
+ this._linkTarget = Doc.AreProtosEqual(anchor1, linkSrc) || Doc.AreProtosEqual(anchor1.annotationOn as Doc, linkSrc) ? anchor2 : anchor1;
+ const target = this._linkTarget?.annotationOn ? await DocCastAsync(this._linkTarget.annotationOn) : this._linkTarget;
this._toolTipText = "";
- this._targetDoc = target;
+ runInAction(() => this._targetDoc = target);
});
}
}
- pointerDown = (e: React.PointerEvent) => {
- if (this.props.linkDoc && this.props.linkSrc) {
- LinkManager.FollowLink(this.props.linkDoc, this.props.linkSrc, this.props.docprops, false);
+ deleteLink = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => this._linkDoc ? LinkManager.Instance.deleteLink(this._linkDoc) : null));
+ }
+ nextHref = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => {
+ this._hrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
+ this.updatePreview();
+ }));
+ }
+
+ followLink = (e: React.PointerEvent) => {
+ if (this._linkDoc && this._linkSrc) {
+ LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docprops, false);
} else if (this.props.href) {
this.props.docprops?.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
}
}
width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
- @computed get targetDocView() {
- return !this._targetDoc ?
- <div style={{ pointerEvents: "all", maxWidth: 225, maxHeight: 225, width: "100%", height: "100%", overflow: "hidden" }}>
- <div style={{ width: "100%", height: "100%", textOverflow: "ellipsis", }} onPointerDown={this.pointerDown}>
- {this._toolTipText}
+ @computed get previewHeader() {
+ return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
+ <div className="LinkDocPreview-info" ref={this._infoRef}>
+ <div className="LinkDocPreview-title">
+ {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title}
+ <p className="LinkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
+ </div>
+ <div className="wrapper" style={{ float: "right" }}>
+ {(this.props.hrefs?.length || 0) <= 1 ? (null) :
+ <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
+ <div className="LinkDocPreview-button" onPointerDown={this.nextHref}>
+ <FontAwesomeIcon className="LinkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
+ </div>
+ </Tooltip>}
+
+ <Tooltip title={<div className="dash-tooltip">Delete Link</div>} placement="top">
+ <div className="LinkDocPreview-button" onPointerDown={this.deleteLink}>
+ <FontAwesomeIcon className="LinkDocPreview-fa-icon" icon="trash" color="white" size="sm" />
+ </div>
+ </Tooltip>
+ </div>
+ </div>;
+ }
+
+ @computed get docPreview() {
+ return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? (null) :
+ <div className="LinkDocPreview-inner">
+ {!this.props.showHeader ? (null) : this.previewHeader}
+ <div className="LinkDocPreview-preview-wrapper">
+ {this._toolTipText ? this._toolTipText :
+ <DocumentView ref={(r) => {
+ const targetanchor = LinkManager.getOppositeAnchor(this._linkDoc!, this._linkSrc!);
+ targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor);
+ }}
+ Document={this._targetDoc}
+ moveDocument={returnFalse}
+ rootSelected={returnFalse}
+ styleProvider={this.props.docprops?.styleProvider}
+ layerProvider={this.props.docprops?.layerProvider}
+ docViewPath={emptyPath}
+ ScreenToLocalTransform={Transform.Identity}
+ parentActive={returnFalse}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ dontRegisterView={true}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={undefined}
+ ContainingCollectionView={undefined}
+ renderDepth={-1}
+ PanelWidth={this.width}
+ PanelHeight={this.height}
+ focus={emptyFunction}
+ whenActiveChanged={returnFalse}
+ bringToFront={returnFalse}
+ NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined}
+ NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined}
+ />}
</div>
- </div>
- :
- <DocumentView ref={r => this._linkTarget !== this._targetDoc && this._linkTarget && r?.focus(this._linkTarget)}
- Document={this._targetDoc}
- moveDocument={returnFalse}
- rootSelected={returnFalse}
- ScreenToLocalTransform={Transform.Identity}
- parentActive={returnFalse}
- addDocument={returnFalse}
- removeDocument={returnFalse}
- addDocTab={returnFalse}
- pinToPres={returnFalse}
- dontRegisterView={true}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={undefined}
- ContainingCollectionView={undefined}
- renderDepth={-1}
- PanelWidth={this.width}
- PanelHeight={this.height}
- focus={emptyFunction}
- whenActiveChanged={returnFalse}
- bringToFront={returnFalse}
- styleProvider={this.props.docprops?.styleProvider}
- layerProvider={this.props.docprops?.layerProvider}
- docViewPath={emptyPath}
- />;
+ </div>;
}
render() {
- return <div className="linkDocPreview"
- style={{
- position: "absolute", left: this.props.location[0],
- top: this.props.location[1], width: this.width() + 16, height: this.height() + 16,
- zIndex: 2004,
- pointerEvents: "none",
- backgroundColor: "lightblue",
- border: "8px solid white",
- borderRadius: "7px",
- boxShadow: "3px 3px 1.5px grey",
- borderBottom: "8px solid white", borderRight: "8px solid white"
- }}>
- {this.targetDocView}
+ return <div className="linkDocPreview" onPointerDown={this.followLink}
+ style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + 16 }}>
+ {this.docPreview}
</div>;
}
}