aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/linking
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
commitf3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch)
treea7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/views/linking
parentb5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff)
parent136f3d9f349d54e8bdd73b6380ea47c19e5edebf (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/linking')
-rw-r--r--src/client/views/linking/LinkMenu.scss6
-rw-r--r--src/client/views/linking/LinkMenu.tsx23
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx18
-rw-r--r--src/client/views/linking/LinkMenuItem.scss2
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx104
-rw-r--r--src/client/views/linking/LinkPopup.tsx17
-rw-r--r--src/client/views/linking/LinkRelationshipSearch.tsx2
7 files changed, 91 insertions, 81 deletions
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 0b9f32eee..636b6415c 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.linkMenu {
width: auto;
@@ -11,7 +11,9 @@
display: inline-block;
position: relative;
border: 1px solid #e4e4e4;
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
+ box-shadow:
+ 0 10px 20px rgba(0, 0, 0, 0.19),
+ 0 6px 6px rgba(0, 0, 0, 0.23);
max-height: 230px;
overflow-y: scroll;
z-index: 10;
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 9dc133e28..12b83414c 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,13 +1,14 @@
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc } from '../../../fields/Doc';
import { LinkManager } from '../../util/LinkManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import { DocumentView } from '../nodes/DocumentView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkInfo } from '../nodes/LinkDocPreview';
import './LinkMenu.scss';
import { LinkMenuGroup } from './LinkMenuGroup';
-import React = require('react');
-import { SettingsManager } from '../../util/SettingsManager';
interface Props {
docView: DocumentView;
@@ -20,9 +21,13 @@ interface Props {
* the outermost component for the link menu of a node that contains a list of its linked nodes
*/
@observer
-export class LinkMenu extends React.Component<Props> {
+export class LinkMenu extends ObservableReactComponent<Props> {
_editorRef = React.createRef<HTMLDivElement>();
@observable _linkMenuRef = React.createRef<HTMLDivElement>();
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
clear = () => this.props.clearLinkEditor?.();
@@ -34,7 +39,7 @@ export class LinkMenu extends React.Component<Props> {
}
onPointerDown = action((e: PointerEvent) => {
- LinkDocPreview.Clear();
+ LinkInfo.Clear();
if (!this._linkMenuRef.current?.contains(e.target as any) && !this._editorRef.current?.contains(e.target as any)) {
this.clear();
}
@@ -47,16 +52,16 @@ export class LinkMenu extends React.Component<Props> {
*/
renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => {
const linkItems = Array.from(groups.entries()).map(group => (
- <LinkMenuGroup key={group[0]} itemHandler={this.props.itemHandler} docView={this.props.docView} sourceDoc={this.props.docView.props.Document} group={group[1]} groupType={group[0]} clearLinkEditor={this.clear} />
+ <LinkMenuGroup key={group[0]} itemHandler={this.props.itemHandler} docView={this.props.docView} sourceDoc={this.props.docView.Document} group={group[1]} groupType={group[0]} clearLinkEditor={this.clear} />
));
return linkItems.length ? linkItems : this.props.style ? [] : [<p key="none">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
};
render() {
- const sourceDoc = this.props.docView.rootDoc;
+ const sourceDoc = this.props.docView.Document;
const sourceAnchor = this.props.docView.anchorViewDoc ?? sourceDoc;
- const style = this.props.style ?? (dv => ({ left: dv?.left || 0, top: this.props.docView.topMost ? undefined : (dv?.bottom || 0) + 15, bottom: this.props.docView.topMost ? 20 : undefined, maxWidth: 200 }))(this.props.docView.getBounds());
+ const style = this.props.style ?? (dv => ({ left: dv?.left || 0, top: this.props.docView.topMost ? undefined : (dv?.bottom || 0) + 15, bottom: this.props.docView.topMost ? 20 : undefined, maxWidth: 200 }))(this.props.docView.getBounds);
return (
<div className="linkMenu" ref={this._linkMenuRef} style={{ ...style, background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}>
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index c1a5a634c..028d3da53 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -7,7 +7,7 @@ import { LinkManager } from '../../util/LinkManager';
import { DocumentView } from '../nodes/DocumentView';
import './LinkMenu.scss';
import { LinkMenuItem } from './LinkMenuItem';
-import React = require('react');
+import * as React from 'react';
import { DocumentType } from '../../documents/DocumentTypes';
interface LinkMenuGroupProps {
@@ -46,19 +46,19 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
const groupItems = Array.from(set.keys()).map(linkDoc => {
const sourceDoc =
this.props.docView.anchorViewDoc ??
- (this.props.docView.rootDoc.type === DocumentType.LINK //
- ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
+ (this.props.docView.Document.type === DocumentType.LINK //
+ ? this.props.docView._props.LayoutTemplateString?.includes('link_anchor_1')
? DocCast(linkDoc.link_anchor_1)
: DocCast(linkDoc.link_anchor_2)
: this.props.sourceDoc);
const destDoc = !sourceDoc
? undefined
- : this.props.docView.rootDoc.type === DocumentType.LINK
- ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
- ? DocCast(linkDoc.link_anchor_2)
- : DocCast(linkDoc.link_anchor_1)
- : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) ||
- LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null));
+ : this.props.docView.Document.type === DocumentType.LINK
+ ? this.props.docView._props.LayoutTemplateString?.includes('link_anchor_1')
+ ? DocCast(linkDoc.link_anchor_2)
+ : DocCast(linkDoc.link_anchor_1)
+ : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) ||
+ LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null));
return !destDoc || !sourceDoc ? null : (
<LinkMenuItem
key={linkDoc[Id]}
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index e83f631a1..44c74236f 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.linkMenu-item {
// border-top: 0.5px solid $medium-gray;
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index bf2a4e1a9..dc4aee1ca 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,12 +1,13 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { action, computed, observable } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { Cast, DocCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
@@ -14,10 +15,10 @@ import { LinkManager } from '../../util/LinkManager';
import { SelectionManager } from '../../util/SelectionManager';
import { SettingsManager } from '../../util/SettingsManager';
import { undoBatch } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkInfo } from '../nodes/LinkDocPreview';
import './LinkMenuItem.scss';
-import React = require('react');
interface LinkMenuItemProps {
groupType: string;
@@ -50,10 +51,13 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
}
@observer
-export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
+export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
private _drag = React.createRef<HTMLDivElement>();
-
_editRef = React.createRef<HTMLDivElement>();
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
@observable private _showMore: boolean = false;
@action toggleShowMore(e: React.PointerEvent) {
@@ -62,12 +66,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
@computed get sourceAnchor() {
- const ldoc = this.props.linkDoc;
- if (this.props.sourceDoc !== ldoc.link_anchor_1 && this.props.sourceDoc !== ldoc.link_anchor_2) {
- if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_1);
- if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_2);
+ const ldoc = this._props.linkDoc;
+ if (this._props.sourceDoc !== ldoc.link_anchor_1 && this._props.sourceDoc !== ldoc.link_anchor_2) {
+ if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1).annotationOn), this._props.sourceDoc)) return DocCast(ldoc.link_anchor_1);
+ if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2).annotationOn), this._props.sourceDoc)) return DocCast(ldoc.link_anchor_2);
}
- return this.props.sourceDoc;
+ return this._props.sourceDoc;
}
onEdit = (e: React.PointerEvent) => {
@@ -75,24 +79,24 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
this,
e,
e => {
- const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed');
+ const dragData = new DragManager.DocumentDragData([this._props.linkDoc], 'embed');
dragData.dropPropertiesToRemove = ['hidden'];
DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
return true;
},
emptyFunction,
action(() => {
- const trail = DocCast(this.props.docView.rootDoc.presentationTrail);
+ const trail = DocCast(this._props.docView.Document.presentationTrail);
if (trail) {
Doc.ActivePresentation = trail;
DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
} else {
- SelectionManager.SelectView(this.props.docView, false);
- LinkManager.currentLink = this.props.linkDoc === LinkManager.currentLink ? undefined : this.props.linkDoc;
+ SelectionManager.SelectView(this._props.docView, false);
+ LinkManager.currentLink = this._props.linkDoc === LinkManager.currentLink ? undefined : this._props.linkDoc;
LinkManager.currentLinkAnchor = LinkManager.currentLink ? this.sourceAnchor : undefined;
- if ((SettingsManager.propertiesWidth ?? 0) < 100) {
- setTimeout(action(() => (SettingsManager.propertiesWidth = 250)));
+ if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
+ setTimeout(action(() => (SettingsManager.Instance.propertiesWidth = 250)));
}
}
})
@@ -106,43 +110,44 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
e => {
const eleClone: any = this._drag.current!.cloneNode(true);
eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
- StartLinkTargetsDrag(eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]);
- this.props.clearLinkEditor?.();
+ StartLinkTargetsDrag(eleClone, this._props.docView, e.x, e.y, this._props.sourceDoc, [this._props.linkDoc]);
+ this._props.clearLinkEditor?.();
return true;
},
emptyFunction,
() => {
- this.props.clearLinkEditor?.();
- if (this.props.itemHandler) {
- this.props.itemHandler?.(this.props.linkDoc);
+ this._props.clearLinkEditor?.();
+ if (this._props.itemHandler) {
+ this._props.itemHandler?.(this._props.linkDoc);
} else {
const focusDoc =
- Cast(this.props.linkDoc.link_anchor_1, Doc, null)?.annotationOn === this.props.sourceDoc
- ? Cast(this.props.linkDoc.link_anchor_1, Doc, null)
- : Cast(this.props.linkDoc.link_anchor_2, Doc, null)?.annotationOn === this.props.sourceDoc
- ? Cast(this.props.linkDoc.link_anchor_12, Doc, null)
- : undefined;
-
- if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true });
- LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, false);
+ Cast(this._props.linkDoc.link_anchor_1, Doc, null)?.annotationOn === this._props.sourceDoc
+ ? Cast(this._props.linkDoc.link_anchor_1, Doc, null)
+ : Cast(this._props.linkDoc.link_anchor_2, Doc, null)?.annotationOn === this._props.sourceDoc
+ ? Cast(this._props.linkDoc.link_anchor_12, Doc, null)
+ : undefined;
+
+ if (focusDoc) this._props.docView._props.focus(focusDoc, { instant: true });
+ LinkFollower.FollowLink(this._props.linkDoc, this._props.sourceDoc, false);
}
}
);
};
- deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this.props.linkDoc))));
+ deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this._props.linkDoc))));
@observable _hover = false;
+ docView = () => this.props.docView;
render() {
- const destinationIcon = Doc.toIcon(this.props.destinationDoc) as any as IconProp;
+ const destinationIcon = Doc.toIcon(this._props.destinationDoc) as any as IconProp;
- const title = StrCast(this.props.destinationDoc.title).length > 18 ? StrCast(this.props.destinationDoc.title).substr(0, 14) + '...' : this.props.destinationDoc.title;
+ const title = StrCast(this._props.destinationDoc.title).length > 18 ? StrCast(this._props.destinationDoc.title).substr(0, 14) + '...' : this._props.destinationDoc.title;
const source =
- this.props.sourceDoc.type === DocumentType.RTF
- ? this.props.linkDoc.storedText
- ? StrCast(this.props.linkDoc.storedText).length > 17
- ? StrCast(this.props.linkDoc.storedText).substr(0, 18)
- : this.props.linkDoc.storedText
+ this._props.sourceDoc.type === DocumentType.RTF
+ ? this._props.linkDoc.storedText
+ ? StrCast(this._props.linkDoc.storedText).length > 17
+ ? StrCast(this._props.linkDoc.storedText).substr(0, 18)
+ : this._props.linkDoc.storedText
: undefined
: undefined;
@@ -154,7 +159,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
style={{
fontSize: this._hover ? 'larger' : undefined,
fontWeight: this._hover ? 'bold' : undefined,
- background: LinkManager.currentLink === this.props.linkDoc ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
+ background: LinkManager.currentLink === this._props.linkDoc ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
}}>
<div className="linkMenu-item-content expand-two">
<div
@@ -170,14 +175,15 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
</div>
<div
className="linkMenu-text"
- onPointerLeave={LinkDocPreview.Clear}
+ onPointerLeave={LinkInfo.Clear}
onPointerEnter={e =>
- this.props.linkDoc &&
- this.props.clearLinkEditor &&
- LinkDocPreview.SetLinkInfo({
- docProps: this.props.docView.props,
- linkSrc: this.props.sourceDoc,
- linkDoc: this.props.linkDoc,
+ this._props.linkDoc &&
+ this._props.clearLinkEditor &&
+ LinkInfo.SetLinkInfo({
+ DocumentView: this.docView,
+ styleProvider: this._props.docView._props.styleProvider,
+ linkSrc: this._props.sourceDoc,
+ linkDoc: this._props.linkDoc,
showHeader: false,
location: [(this._drag.current?.getBoundingClientRect().left ?? 100) + 40, (this._drag.current?.getBoundingClientRect().top ?? e.clientY) + 25],
noPreview: false,
@@ -194,10 +200,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
<FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" />
</div>
<p className="linkMenu-destination-title">
- {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
+ {this._props.linkDoc.linksToAnnotation && Cast(this._props.destinationDoc.data, WebField)?.url.href === this._props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
</p>
</div>
- {!this.props.linkDoc.link_description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.link_description).split('\n')[0].substring(0, 50)}</p>}
+ {!this._props.linkDoc.link_description ? null : <p className="linkMenu-description">{StrCast(this._props.linkDoc.link_description).split('\n')[0].substring(0, 50)}</p>}
</div>
<div className="linkMenu-item-buttons">
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 9a9a89732..c9e3c203d 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -1,16 +1,16 @@
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { EditorView } from 'prosemirror-view';
-import { Doc } from '../../../fields/Doc';
+import * as React from 'react';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
-import { OpenWhere } from '../nodes/DocumentView';
+import { DefaultStyleProvider } from '../StyleProvider';
+import { OpenWhere, returnEmptyDocViewList } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { SearchBox } from '../search/SearchBox';
-import { DefaultStyleProvider } from '../StyleProvider';
import './LinkPopup.scss';
-import React = require('react');
interface LinkPopupProps {
linkFrom?: () => Doc | undefined;
@@ -29,7 +29,7 @@ interface LinkPopupProps {
@observer
export class LinkPopup extends React.Component<LinkPopupProps> {
@observable private linkURL: string = '';
- @observable public view?: EditorView;
+ @observable public view?: EditorView = undefined;
// TODO: should check for valid URL
@undoBatch
@@ -61,7 +61,7 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
<SearchBox
Document={Doc.MySearcher}
- DataDoc={Doc.MySearcher}
+ docViewPath={returnEmptyDocViewList}
linkFrom={linkDoc}
linkCreateAnchor={this.props.linkCreateAnchor}
linkSearch={true}
@@ -70,11 +70,10 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
isSelected={returnTrue}
isContentActive={returnTrue}
select={returnTrue}
- setHeight={returnFalse}
addDocument={undefined}
addDocTab={returnTrue}
pinToPres={emptyFunction}
- rootSelected={returnTrue}
+ rootSelected={returnFalse}
styleProvider={DefaultStyleProvider}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
@@ -82,9 +81,7 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
PanelHeight={this.getPHeight}
renderDepth={0}
focus={emptyFunction}
- docViewPath={returnEmptyDoclist}
whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx
index 9662b2fea..0902d53b2 100644
--- a/src/client/views/linking/LinkRelationshipSearch.tsx
+++ b/src/client/views/linking/LinkRelationshipSearch.tsx
@@ -1,6 +1,6 @@
import { observer } from 'mobx-react';
+import * as React from 'react';
import './LinkEditor.scss';
-import React = require('react');
interface link_relationshipSearchProps {
results: string[] | undefined;