diff options
| author | bobzel <zzzman@gmail.com> | 2023-04-05 22:44:03 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2023-04-05 22:44:03 -0400 |
| commit | 9b41da1af16b982ee8ac2fc09f2f8b5d67eac9fb (patch) | |
| tree | bc3f57cd5b31fd453d272c925f6d5b728ab63bae /src/client/views/nodes/LinkDocPreview.tsx | |
| parent | 9dae453967183b294bf4f7444b948023a1d52d39 (diff) | |
| parent | 8f7e99641f84ad15f34ba9e4a60b664ac93d2e5d (diff) | |
Merge branch 'master' into data-visualization-view-naafi
Diffstat (limited to 'src/client/views/nodes/LinkDocPreview.tsx')
| -rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 123 |
1 files changed, 73 insertions, 50 deletions
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 93ca22d5d..fcc5b6975 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -3,8 +3,8 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; -import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; import { DocServer } from '../../DocServer'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -12,12 +12,12 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; +import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; -import { undoBatch } from '../../util/UndoManager'; -import { DocumentView, DocumentViewSharedProps } from './DocumentView'; +import { SearchBox } from '../search/SearchBox'; +import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); -import { LinkEditor } from '../linking/LinkEditor'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -26,6 +26,7 @@ interface LinkDocPreviewProps { location: number[]; hrefs?: string[]; showHeader?: boolean; + noPreview?: boolean; } @observer export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { @@ -36,15 +37,23 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info); } + static _instance: Opt<LinkDocPreview>; + _infoRef = React.createRef<HTMLDivElement>(); _linkDocRef = React.createRef<HTMLDivElement>(); @observable public static LinkInfo: Opt<LinkDocPreviewProps>; @observable _targetDoc: Opt<Doc>; + @observable _markerTargetDoc: Opt<Doc>; @observable _linkDoc: Opt<Doc>; @observable _linkSrc: Opt<Doc>; @observable _toolTipText = ''; @observable _hrefInd = 0; + constructor(props: any) { + super(props); + LinkDocPreview._instance = this; + } + @action init() { var linkTarget = this.props.linkDoc; this._linkSrc = this.props.linkSrc; @@ -56,11 +65,12 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { } if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) { // want to show annotation context document if annotation is not text - linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._targetDoc = anno))); + linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._markerTargetDoc = this._targetDoc = anno))); } else { - this._targetDoc = linkTarget; + this._markerTargetDoc = this._targetDoc = linkTarget; } this._toolTipText = ''; + this.updateHref(); } componentDidUpdate(props: any) { if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init(); @@ -70,6 +80,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { document.addEventListener('pointerdown', this.onPointerDown, true); } + @action componentWillUnmount() { LinkDocPreview.SetLinkInfo(undefined); document.removeEventListener('pointerdown', this.onPointerDown, true); @@ -79,7 +90,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview }; - @computed get href() { + @action + updateHref() { if (this.props.hrefs?.length) { const href = this.props.hrefs[this._hrefInd]; if (href.indexOf(Doc.localServerPath()) !== 0) { @@ -89,45 +101,54 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { .page(href.replace('https://en.wikipedia.org/wiki/', '')) .then(page => page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500))))); } else { - setTimeout(action(() => (this._toolTipText = 'url => ' + href))); + this._toolTipText = 'url => ' + href; } } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated - const anchorDoc = href.replace(Doc.localServerPath(), '').split('?')[0]; - anchorDoc && - DocServer.GetRefField(anchorDoc).then( - action(anchor => { - if (anchor instanceof Doc && DocListCast(anchor.links).length) { - this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; - const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; - if (automaticLink) { - // automatic links specify the target in the link info, not the source - const linkTarget = anchor; - this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); - this._targetDoc = linkTarget; - } else { - this._linkSrc = anchor; - const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; - } - this._toolTipText = ''; + const anchorDocId = href.replace(Doc.localServerPath(), '').split('?')[0]; + const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined; + anchorDoc?.then?.( + action(anchor => { + if (anchor instanceof Doc && LinkManager.Links(anchor).length) { + this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0]; + const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; + if (automaticLink) { + // automatic links specify the target in the link info, not the source + const linkTarget = anchor; + this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); + this._markerTargetDoc = this._targetDoc = linkTarget; + } else { + this._linkSrc = anchor; + const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + this._markerTargetDoc = linkTarget; + this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } - }) - ); + this._toolTipText = 'link to ' + this._targetDoc?.title; + if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); + } + }) + ); } return href; } return undefined; } - @observable _showEditor = false; + + @action editLink = (e: React.PointerEvent): void => { - LinkManager.currentLink = this.props.linkDoc; setupMoveUpEvents( this, e, returnFalse, emptyFunction, - action(() => (this._showEditor = !this._showEditor)) + action(() => { + LinkManager.currentLink = this._linkDoc; + LinkManager.currentLinkAnchor = this._linkSrc; + this.props.docProps.DocumentView?.().select(false); + if ((SettingsManager.propertiesWidth ?? 0) < 100) { + SettingsManager.propertiesWidth = 250; + } + }) ); }; nextHref = (e: React.PointerEvent) => { @@ -148,11 +169,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { }; followLink = () => { + LinkDocPreview.Clear(); if (this._linkDoc && this._linkSrc) { - LinkDocPreview.Clear(); - LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false); + LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false); } else if (this.props.hrefs?.length) { - this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right'); + const webDoc = + Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ?? + Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }); + this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox); } }; @@ -173,31 +197,31 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)); }; @computed get previewHeader() { - return !this._linkDoc || !this._targetDoc || !this._linkSrc ? null : ( + return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : ( <div className="linkDocPreview-info"> + <div className="linkDocPreview-buttonBar" style={{ float: 'left' }}> + <Tooltip title={<div className="dash-tooltip">Edit Link</div>} placement="top"> + <div className="linkDocPreview-button" onPointerDown={this.editLink}> + <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="edit" color="white" size="sm" /> + </div> + </Tooltip> + </div> <div className="linkDocPreview-title" style={{ pointerEvents: 'all' }}> - {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + '...' : StrCast(this._targetDoc.title)} + {StrCast(this._markerTargetDoc.title).length > 16 ? StrCast(this._markerTargetDoc.title).substr(0, 16) + '...' : StrCast(this._markerTargetDoc.title)} <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p> </div> - <div className="linkDocPreview-buttonBar"> + <div className="linkDocPreview-buttonBar" style={{ float: 'right' }}> <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top"> <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}> <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" /> </div> </Tooltip> - - <Tooltip title={<div className="dash-tooltip">Edit Link</div>} placement="top"> - <div className="linkDocPreview-button" onPointerDown={this.editLink}> - <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="edit" color="white" size="sm" /> - </div> - </Tooltip> </div> </div> ); } @computed get docPreview() { - const href = this.href; // needs to be here to trigger lookup of web pages and docs on server return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : ( <div className="linkDocPreview-inner"> {!this.props.showHeader ? null : this.previewHeader} @@ -228,7 +252,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { <DocumentView ref={r => { const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor); + targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {}); }} Document={this._targetDoc!} moveDocument={returnFalse} @@ -249,7 +273,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { searchFilterDocs={returnEmptyDoclist} ContainingCollectionDoc={undefined} ContainingCollectionView={undefined} - renderDepth={-1} + renderDepth={0} suppressSetHeight={true} PanelWidth={this.width} PanelHeight={this.height} @@ -274,9 +298,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { className="linkDocPreview" ref={this._linkDocRef} onPointerDown={this.followLinkPointerDown} - style={{ left: this.props.location[0], top: this.props.location[1], width: this._showEditor ? 'auto' : this.width() + borders, height: this._showEditor ? 'max-content' : this.height() + borders + (this.props.showHeader ? 37 : 0) }}> - {this._showEditor ? null : this.docPreview} - {!this._showEditor || !this._linkSrc || !this._linkDoc ? null : <LinkEditor sourceDoc={this._linkSrc} linkDoc={this._linkDoc} showLinks={action(() => (this._showEditor = !this._showEditor))} />} + style={{ display: !this._toolTipText ? 'none' : undefined, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}> + {this.docPreview} </div> ); } |
