aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2020-06-25 00:19:40 -0400
committerBob Zeleznik <zzzman@gmail.com>2020-06-25 00:19:40 -0400
commitba42ca25ae828b6279f618dea87f7cd37a3b3814 (patch)
treee57ef1edd14f3484aff15fc607c68b5c9531c7da /src
parentfb69ecbbb00ae9f784ee8519729651299049cd86 (diff)
added link preview for blue buttons.
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DragManager.ts2
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/nodes/DocumentLinksButton.scss28
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx15
-rw-r--r--src/client/views/nodes/DocumentView.tsx10
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx8
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx107
8 files changed, 148 insertions, 28 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 91bc51101..417ddf989 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -353,7 +353,7 @@ export namespace DragManager {
const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
const rect = ele.getBoundingClientRect();
const scaleX = rect.width / ele.offsetWidth,
- scaleY = rect.height / ele.offsetHeight;
+ scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX;
elesCont.left = Math.min(rect.left, elesCont.left);
elesCont.top = Math.min(rect.top, elesCont.top);
elesCont.right = Math.max(rect.right, elesCont.right);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 8cd1b650e..106b7163e 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -248,7 +248,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
get linkButton() {
const view0 = this.view0;
const linkCount = view0 && DocListCast(view0.props.Document.links).length;
- return !view0 || linkCount ? (null) :
+ return !view0 ? (null) :
<div className="documentButtonBar-button">
<div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
<div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 374fd7421..4e2c067ac 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -57,6 +57,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { DocumentManager } from '../util/DocumentManager';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { LinkMenu } from './linking/LinkMenu';
+import { LinkDocPreview } from './nodes/LinkDocPreview';
@observer
export class MainView extends React.Component {
@@ -581,6 +582,9 @@ export class MainView extends React.Component {
</GestureOverlay>
<PreviewCursor />
{DocumentLinksButton.EditLink ? <LinkMenu location={DocumentLinksButton.EditLinkLoc} docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
+ {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
+ linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
+ addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
<ContextMenu />
<RadialMenu />
<PDFMenu />
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index b4c59b91c..79364ab8e 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,21 +1,9 @@
@import "../globalCssVariables.scss";
-.documentLinksButton-button-empty:hover {
- background: $main-accent;
- transform: scale(1.05);
- cursor: pointer;
-}
-
-.documentLinksButton-button-nonempty:hover {
- background: $main-accent;
- transform: scale(1.05);
- cursor: pointer;
-}
-
-.documentLinksButton-button,
-.documentLinksButton-button-empty,
-.documentLinksButton-button-nonempty {
+.documentLinksButton,
+.documentLinksButton-endLink,
+.documentLinksButton-startLink {
height: 20px;
width: 20px;
left: -15px;
@@ -34,18 +22,18 @@
align-items: center;
&:hover {
- background: $main-accent;
+ background: deepskyblue;
transform: scale(1.05);
- cursor: pointer;
+ cursor: default;
}
}
-.documentLinksButton-button-nonempty {
+.documentLinksButton {
background-color: $link-color;
}
-.documentLinksButton-button-empty {
+.documentLinksButton-endLink {
border: red solid 2px;
}
-.documentLinksButton-button {
+.documentLinksButton-startLink {
border: red solid 2px;
background-color: rgba(255, 192, 203, 0.5);
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index c9083a65f..fc314ae8b 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -9,6 +9,7 @@ import { DocumentView } from "./DocumentView";
import React = require("react");
import { DocUtils } from "../../documents/Documents";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { LinkDocPreview } from "./LinkDocPreview";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -80,11 +81,19 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
const links = DocListCast(this.props.View.props.Document.links);
return !links.length || links[0].hidden ? (null) :
<div title="Drag(create link) Tap(view links)" ref={this._linkButton}>
- <div className={"documentLinksButton-button-nonempty"} onPointerDown={this.onLinkButtonDown} >
+ <div className={"documentLinksButton"} style={{ backgroundColor: DocumentLinksButton.StartLink ? "transparent" : "" }}
+ onPointerDown={this.onLinkButtonDown}
+ onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
+ onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
+ addDocTab: this.props.View.props.addDocTab,
+ linkSrc: this.props.View.props.Document,
+ linkDoc: links[0],
+ Location: [e.clientX, e.clientY + 20]
+ })} >
{links.length ? links.length : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
</div>
- {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ? <div className={"documentLinksButton-button-empty"} onPointerDown={this.completeLink} /> : (null)}
- {DocumentLinksButton.StartLink === this.props.View ? <div className={"documentLinksButton-button"} /> : (null)}
+ {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ? <div className={"documentLinksButton-endLink"} onPointerDown={this.completeLink} /> : (null)}
+ {DocumentLinksButton.StartLink === this.props.View ? <div className={"documentLinksButton-startLink"} /> : (null)}
</div>;
}
render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 20a606937..4cb08bab3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1047,7 +1047,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClick={this.onClickHandler}
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.showLinks ? this.anchors : (null)}
- {this.props.forcedBackgroundColor?.(this.Document) === "transparent" ? (null) : <DocumentLinksButton View={this} />}
+ {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.props.dontRegisterView ? (null) : <DocumentLinksButton View={this} />}
</div>
);
}
@@ -1071,7 +1071,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
anchorPanelHeight = () => this.props.PanelHeight() || 1;
@computed get anchors() {
TraceMobx();
- return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+ return this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.presBox || this.props.dontRegisterView ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
<DocumentView {...this.props} key={i + 1}
Document={d}
ContainingCollectionView={this.props.ContainingCollectionView}
@@ -1079,6 +1079,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
PanelWidth={this.anchorPanelWidth}
PanelHeight={this.anchorPanelHeight}
ContentScaling={returnOne}
+ dontRegisterView={false}
forcedBackgroundColor={returnTransparent}
removeDocument={this.hideLinkAnchor}
pointerEvents={false}
@@ -1202,7 +1203,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
color: StrCast(this.layoutDoc.color, "inherit"),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
- boxShadow: this.Document.isLinkButton ? StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") : this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
+ boxShadow: this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
+ StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") :
+ this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
+ undefined,
background: finalColor,
opacity: finalOpacity,
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 2b64cdab6..2bcc6168b 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -18,6 +18,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SelectionManager } from "../../util/SelectionManager";
import { TraceMobx } from "../../../fields/util";
import { Id } from "../../../fields/FieldSymbols";
+import { LinkDocPreview } from "./LinkDocPreview";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -132,6 +133,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
);
const small = this.props.PanelWidth() <= 1;
return <div className={`linkAnchorBox-cont${small ? "-small" : ""} ${this.rootDoc[Id]}`}
+ onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
+ onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
+ addDocTab: this.props.addDocTab,
+ linkSrc: this.props.ContainingCollectionDoc!,
+ linkDoc: this.rootDoc,
+ Location: [e.clientX, e.clientY + 20]
+ })}
onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
ref={this._ref} style={{
background: c,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
new file mode 100644
index 000000000..126dc240a
--- /dev/null
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -0,0 +1,107 @@
+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 { Cast, FieldValue, NumCast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { Transform } from "../../util/Transform";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import React = require("react");
+import { DocumentView } from './DocumentView';
+
+interface Props {
+ linkDoc?: Doc;
+ linkSrc?: Doc;
+ href?: string;
+ backgroundColor: (doc: Doc) => string;
+ addDocTab: (document: Doc, where: string) => boolean;
+ location: number[];
+}
+@observer
+export class LinkDocPreview extends React.Component<Props> {
+ @observable public static LinkInfo: Opt<{ linkDoc?: Doc; addDocTab: (document: Doc, where: string) => boolean, linkSrc: Doc; href?: string; Location: number[] }>;
+ @observable _targetDoc: Opt<Doc>;
+ @observable _toolTipText = "";
+
+ componentDidUpdate() { this.updatePreview() }
+ componentDidMount() { this.updatePreview() }
+ async 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))));
+ } else {
+ runInAction(() => this._toolTipText = "external => " + this.props.href);
+ }
+ } else if (linkDoc && linkSrc) {
+ const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), linkSrc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
+ const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
+ runInAction(() => {
+ this._toolTipText = "";
+ this._targetDoc = target;
+ if (anchor !== this._targetDoc && anchor && this._targetDoc) {
+ this._targetDoc._scrollY = NumCast(anchor?.y);
+ }
+ });
+ }
+ }
+ pointerDown = (e: React.PointerEvent) => {
+ if (this.props.linkDoc && this.props.linkSrc) {
+ DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ } else if (this.props.href) {
+ this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight");
+ }
+ }
+ width = () => Math.min(350, NumCast(this._targetDoc?.[WidthSym](), 350));
+ height = () => Math.min(350, NumCast(this._targetDoc?.[HeightSym](), 350));
+ @computed get targetDocView() {
+ return !this._targetDoc ?
+ <div style={{ pointerEvents: "all", maxWidth: 350, maxHeight: 250, width: "100%", height: "100%", overflow: "hidden" }}>
+ <div style={{ width: "100%", height: "100%", textOverflow: "ellipsis", }} onPointerDown={this.pointerDown}>
+ {this._toolTipText}
+ </div>
+ </div> :
+ <ContentFittingDocumentView
+ Document={this._targetDoc}
+ LibraryPath={emptyPath}
+ fitToBox={true}
+ backgroundColor={this.props.backgroundColor}
+ moveDocument={returnFalse}
+ rootSelected={returnFalse}
+ ScreenToLocalTransform={Transform.Identity}
+ parentActive={returnFalse}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ dontRegisterView={true}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionDoc={undefined}
+ ContainingCollectionView={undefined}
+ renderDepth={0}
+ PanelWidth={this.width}
+ PanelHeight={this.height}
+ focus={emptyFunction}
+ whenActiveChanged={returnFalse}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ />;
+ }
+
+ render() {
+ return <div className="linkDocPreview"
+ style={{
+ position: "absolute", left: this.props.location[0],
+ top: this.props.location[1], width: this.width(), height: this.height(),
+ boxShadow: "black 2px 2px 1em"
+ }}>
+ {this.targetDocView}
+ </div>;
+ }
+} \ No newline at end of file