diff options
Diffstat (limited to 'src/client/views/linking')
| -rw-r--r-- | src/client/views/linking/LinkEditor.tsx | 84 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenu.scss | 40 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenu.tsx | 75 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenuGroup.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenuItem.tsx | 113 |
5 files changed, 128 insertions, 194 deletions
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 435b9d904..f74b422d3 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -4,7 +4,6 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../fields/Doc"; import { DateCast, StrCast } from "../../../fields/Types"; -import { Utils } from "../../../Utils"; import { LinkManager } from "../../util/LinkManager"; import { undoBatch } from "../../util/UndoManager"; import './LinkEditor.scss'; @@ -21,31 +20,41 @@ interface LinkEditorProps { export class LinkEditor extends React.Component<LinkEditorProps> { @observable description = StrCast(LinkManager.currentLink?.description); + @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); @observable openDropdown: boolean = false; @observable showInfo: boolean = false; @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; } @observable private buttonColor: string = ""; - + @observable private relationshipButtonColor: string = ""; //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION"; - @undoBatch @action + @undoBatch deleteLink = (): void => { LinkManager.Instance.deleteLink(this.props.linkDoc); this.props.showLinks(); } - @undoBatch @action - setDescripValue = (value: string) => { + @undoBatch + setRelationshipValue = action((value: string) => { + if (LinkManager.currentLink) { + LinkManager.currentLink.linkRelationship = value; + this.relationshipButtonColor = "rgb(62, 133, 55)"; + setTimeout(action(() => this.relationshipButtonColor = ""), 750); + return true; + } + }); + + @undoBatch + setDescripValue = action((value: string) => { if (LinkManager.currentLink) { LinkManager.currentLink.description = value; this.buttonColor = "rgb(62, 133, 55)"; setTimeout(action(() => this.buttonColor = ""), 750); return true; } - } + }); - @action onKey = (e: React.KeyboardEvent<HTMLInputElement>) => { if (e.key === "Enter") { this.setDescripValue(this.description); @@ -53,22 +62,48 @@ export class LinkEditor extends React.Component<LinkEditorProps> { } } - @action - onDown = () => { - this.setDescripValue(this.description); + onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => { + if (e.key === "Enter") { + this.setRelationshipValue(this.relationship); + document.getElementById('input')?.blur(); + } } + onDown = () => this.setDescripValue(this.description); + onRelationshipDown = () => this.setRelationshipValue(this.description); + @action - handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { - this.description = e.target.value; - } + handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; } + @action + handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; } + @computed + get editRelationship() { + return <div className="linkEditor-description"> + <div className="linkEditor-description-label">Link Relationship:</div> + <div className="linkEditor-description-input"> + <div className="linkEditor-description-editing"> + <input + style={{ width: "100%" }} + id="input" + value={this.relationship} + placeholder={"enter link label"} + // color={"rgb(88, 88, 88)"} + onKeyDown={this.onRelationshipKey} + onChange={this.handleRelationshipChange} + ></input> + </div> + <div className="linkEditor-description-add-button" + style={{ background: this.relationshipButtonColor }} + onPointerDown={this.onRelationshipDown}>Set</div> + </div> + </div>; + } @computed get editDescription() { return <div className="linkEditor-description"> - <div className="linkEditor-description-label"> - Link Label:</div> + <div className="linkEditor-description-label">Link Description:</div> <div className="linkEditor-description-input"> <div className="linkEditor-description-editing"> <input @@ -84,13 +119,12 @@ export class LinkEditor extends React.Component<LinkEditorProps> { <div className="linkEditor-description-add-button" style={{ background: this.buttonColor }} onPointerDown={this.onDown}>Set</div> - </div></div>; + </div> + </div>; } @action - changeDropdown = () => { - this.openDropdown = !this.openDropdown; - } + changeDropdown = () => { this.openDropdown = !this.openDropdown; } @undoBatch changeFollowBehavior = action((follow: string) => { @@ -101,8 +135,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> { @computed get followingDropdown() { return <div className="linkEditor-followingDropdown"> - <div className="linkEditor-followingDropdown-label"> - Follow Behavior:</div> + <div className="linkEditor-followingDropdown-label">Follow Behavior:</div> <div className="linkEditor-followingDropdown-dropdown"> <div className="linkEditor-followingDropdown-header" onPointerDown={this.changeDropdown}> @@ -157,9 +190,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> { } @action - changeInfo = () => { - this.showInfo = !this.showInfo; - } + changeInfo = () => { this.showInfo = !this.showInfo; } render() { const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); @@ -186,8 +217,9 @@ export class LinkEditor extends React.Component<LinkEditorProps> { {DateCast(this.props.linkDoc.creationDate).toString()}</div> : null}</div> </div> : null} - <div>{this.editDescription}</div> - <div>{this.followingDropdown}</div> + {this.editDescription} + {this.editRelationship} + {this.followingDropdown} </div> ); diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 0e03b46db..a90bf8b0a 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -4,29 +4,23 @@ width: auto; height: auto; position: absolute; - z-index: 999; - - .linkMenu-list { + z-index: 2001; + .linkMenu-list, + .linkMenu-listEditor + { display: inline-block; - + position: relative; border: 1px solid black; - box-shadow: 3px 3px 1.5px grey; + background: white; + min-width: 170px; max-height: 170px; overflow-y: scroll; - position: relative; z-index: 10; - background: white; - min-width: 170px; - //border-radius: 5px; - //padding-top: 6.5px; - //padding-bottom: 6.5px; - //padding-left: 6.5px; - //padding-right: 2px; - //width: calc(auto + 50px); - + } + .linkMenu-list { white-space: nowrap; overflow-x: hidden; width: 240px; @@ -40,22 +34,6 @@ scrollbar-color: rgb(201, 239, 252); } } - - .linkMenu-listEditor { - - display: inline-block; - - border: 1px solid black; - - box-shadow: 3px 3px 1.5px grey; - - max-height: 170px; - overflow-y: scroll; - position: relative; - z-index: 10; - background: white; - min-width: 170px; - } } .linkMenu-group { diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index b32022024..c7888c5ee 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -13,49 +13,27 @@ import React = require("react"); interface Props { docView: DocumentView; changeFlyout: () => void; - docprops: DocumentViewSharedProps; } @observer export class LinkMenu extends React.Component<Props> { - - @observable private _editingLink?: Doc; - @observable private _linkMenuRef = React.createRef<HTMLDivElement>(); private _editorRef = React.createRef<HTMLDivElement>(); + @observable _editingLink?: Doc; + @observable _linkMenuRef = React.createRef<HTMLDivElement>(); - //@observable private _numLinks: number = 0; - - // @computed get overflow() { - // if (this._numLinks) { - // return "scroll"; - // } - // return "auto"; - // } - - @action - onClick = (e: PointerEvent) => { - - LinkDocPreview.LinkInfo = undefined; - - - if (this._linkMenuRef && !this._linkMenuRef.current?.contains(e.target as any)) { - if (this._editorRef && !this._editorRef.current?.contains(e.target as any)) { - DocumentLinksButton.EditLink = undefined; - } - } - } - @action - componentDidMount() { - this._editingLink = undefined; - document.addEventListener("pointerdown", this.onClick); + @computed get position() { + return ((dv) => ({ x: dv?.left || 0, y: dv?.top || 0, r: dv?.right || 0, b: dv?.bottom || 0 }))(this.props.docView.getBounds()); } - componentWillUnmount() { - document.removeEventListener("pointerdown", this.onClick); - } + componentDidMount() { document.addEventListener("pointerdown", this.onPointerDown); } + componentWillUnmount() { document.removeEventListener("pointerdown", this.onPointerDown); } - clearAllLinks = () => { - LinkManager.Instance.deleteAllLinksOnAnchor(this.props.docView.props.Document); + onPointerDown = (e: PointerEvent) => { + LinkDocPreview.Clear(); + if (!this._linkMenuRef.current?.contains(e.target as any) && + !this._editorRef.current?.contains(e.target as any)) { + DocumentLinksButton.ClearLinkEditor(); + } } renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => { @@ -66,32 +44,23 @@ export class LinkMenu extends React.Component<Props> { sourceDoc={this.props.docView.props.Document} group={group[1]} groupType={group[0]} - showEditor={action(linkDoc => this._editingLink = linkDoc)} - docprops={this.props.docprops} />); + showEditor={action(linkDoc => this._editingLink = linkDoc)} />); return linkItems.length ? linkItems : [<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>]; } - @computed - get position() { - const docView = this.props.docView.getBounds(); - return { x: docView?.left || 0, y: docView?.top || 0, r: docView?.right || 0, b: docView?.bottom || 0 }; - } - render() { const sourceDoc = this.props.docView.props.Document; - const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); - return <div className="linkMenu" style={{ left: this.position.x, top: this.props.docView.topMost ? undefined : this.position.b + 15, bottom: this.props.docView.topMost ? 20 : undefined }} ref={this._linkMenuRef} > - {!this._editingLink ? - <div className="linkMenu-list" > - {this.renderAllGroups(groups)} - </div> : + return <div className="linkMenu" ref={this._linkMenuRef} + style={{ left: this.position.x, top: this.props.docView.topMost ? undefined : this.position.b + 15, bottom: this.props.docView.topMost ? 20 : undefined }} + > + {this._editingLink ? <div className="linkMenu-listEditor"> - <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} - showLinks={action(() => this._editingLink = undefined)} /> - </div> - } - + <LinkEditor sourceDoc={sourceDoc} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)} /> + </div> : + <div className="linkMenu-list" > + {this.renderAllGroups(LinkManager.Instance.getRelatedGroupedLinks(sourceDoc))} + </div>} </div>; } }
\ No newline at end of file diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index 7db908393..74af78234 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -3,7 +3,7 @@ import { Doc } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { Cast } from "../../../fields/Types"; import { LinkManager } from "../../util/LinkManager"; -import { DocumentView, DocumentViewSharedProps } from "../nodes/DocumentView"; +import { DocumentView } from "../nodes/DocumentView"; import './LinkMenu.scss'; import { LinkMenuItem } from "./LinkMenuItem"; import React = require("react"); @@ -13,14 +13,11 @@ interface LinkMenuGroupProps { group: Doc[]; groupType: string; showEditor: (linkDoc: Doc) => void; - docprops: DocumentViewSharedProps; docView: DocumentView; } @observer export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { - - private _drag = React.createRef<HTMLDivElement>(); private _menuRef = React.createRef<HTMLDivElement>(); render() { @@ -31,7 +28,6 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { if (destination && this.props.sourceDoc) { return <LinkMenuItem key={linkDoc[Id]} groupType={this.props.groupType} - docprops={this.props.docprops} docView={this.props.docView} linkDoc={linkDoc} sourceDoc={this.props.sourceDoc} @@ -43,11 +39,9 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { return ( <div className="linkMenu-group" ref={this._menuRef}> - <div className="linkMenu-group-name"> - <p ref={this._drag} className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p> + <p className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p> </div> - <div className="linkMenu-group-wrapper"> {groupItems} </div> diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 19ef03a31..53a7ae9ab 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react"; import { Doc, DocListCast } from '../../../fields/Doc'; import { Cast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; -import { emptyFunction, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, setupMoveUpEvents, returnFalse } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -18,6 +18,7 @@ import { DocumentView, DocumentViewSharedProps } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; import './LinkMenuItem.scss'; import React = require("react"); +import { setup } from 'mocha'; interface LinkMenuItemProps { @@ -27,7 +28,6 @@ interface LinkMenuItemProps { sourceDoc: Doc; destinationDoc: Doc; showEditor: (linkDoc: Doc) => void; - docprops: DocumentViewSharedProps; menuRef: React.Ref<HTMLDivElement>; } @@ -69,9 +69,6 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume @observer export class LinkMenuItem extends React.Component<LinkMenuItemProps> { private _drag = React.createRef<HTMLDivElement>(); - private _downX = 0; - private _downY = 0; - private _eleClone: any; _editRef = React.createRef<HTMLDivElement>(); _buttonRef = React.createRef<HTMLDivElement>(); @@ -81,82 +78,45 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { onEdit = (e: React.PointerEvent): void => { LinkManager.currentLink = this.props.linkDoc; - setupMoveUpEvents(this, e, this.editMoved, emptyFunction, () => this.props.showEditor(this.props.linkDoc)); - } - - editMoved = (e: PointerEvent) => { - const dragData = new DragManager.DocumentDragData([this.props.linkDoc]); - DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y); - return true; + setupMoveUpEvents(this, e, e => { + DragManager.StartDocumentDrag([this._editRef.current!], new DragManager.DocumentDragData([this.props.linkDoc]), e.x, e.y); + return true; + }, emptyFunction, () => this.props.showEditor(this.props.linkDoc)); } - @action onLinkButtonDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - this._eleClone = this._drag.current!.cloneNode(true); - e.stopPropagation(); - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.addEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - document.addEventListener("pointerup", this.onLinkButtonUp); - - if (this._buttonRef && !!!this._buttonRef.current?.contains(e.target as any)) { - LinkDocPreview.LinkInfo = undefined; - } - } - - onLinkButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); - - e.stopPropagation(); - } - - onLinkButtonMoved = async (e: PointerEvent) => { - if (this._drag.current !== null && Math.abs((e.clientX - this._downX) * (e.clientX - this._downX) + (e.clientY - this._downY) * (e.clientY - this._downY)) > 5) { - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - - this._eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`; - StartLinkTargetsDrag(this._eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]); - } - e.stopPropagation(); + setupMoveUpEvents(this, e, + 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]); + DocumentLinksButton.ClearLinkEditor(); + return true; + }, + emptyFunction, + () => { + DocumentLinksButton.ClearLinkEditor(); + LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); + }); } - @undoBatch - @action deleteLink = (e: React.PointerEvent): void => { - this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); - LinkManager.Instance.deleteLink(this.props.linkDoc); - e.stopPropagation(); - - runInAction(() => { - LinkDocPreview.LinkInfo = undefined; - DocumentLinksButton.EditLink = undefined; - }); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => { + this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); + LinkManager.Instance.deleteLink(this.props.linkDoc); + }))); } - @undoBatch - @action autoMove = (e: React.PointerEvent) => { - e.stopPropagation(); - this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove; + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove))); } - @undoBatch - @action showLink = (e: React.PointerEvent) => { - e.stopPropagation(); - this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay; + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay))); } - @undoBatch - @action showAnchor = (e: React.PointerEvent) => { - e.stopPropagation(); - this.props.linkDoc.hidden = !this.props.linkDoc.hidden; + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.hidden = !this.props.linkDoc.hidden))); } render() { @@ -178,13 +138,14 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { <div className={"linkMenu-item-content expand-two"}> <div ref={this._drag} className="linkMenu-name" //title="drag to view target. click to customize." - onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)} - onPointerEnter={action(e => this.props.linkDoc && (LinkDocPreview.LinkInfo = { - docprops: this.props.docprops, + onPointerLeave={LinkDocPreview.Clear} + onPointerEnter={e => this.props.linkDoc && LinkDocPreview.SetLinkInfo({ + docProps: this.props.docView.props, linkSrc: this.props.sourceDoc, linkDoc: this.props.linkDoc, - Location: [e.clientX, e.clientY + 20] - }))} + showHeader: false, + location: [e.clientX, e.clientY + 20] + })} onPointerDown={this.onLinkButtonDown}> <div className="linkMenu-text"> @@ -203,26 +164,26 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { <div className="linkMenu-item-buttons" ref={this._buttonRef} > <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.hidden ? "Show Anchor" : "Hide Anchor"}</div></>}> - <div className="button" ref={this._editRef} onPointerDown={this.showAnchor}> + <div className="button" ref={this._editRef} onPointerDown={this.showAnchor} onClick={e => e.stopPropagation()}> <FontAwesomeIcon className="fa-icon" icon={this.props.linkDoc.hidden ? "eye-slash" : "eye"} size="sm" /></div> </Tooltip> <Tooltip title={<><div className="dash-tooltip">{!this.props.linkDoc.linkDisplay ? "Show link" : "Hide link"}</div></>}> - <div className="button" ref={this._editRef} onPointerDown={this.showLink}> + <div className="button" ref={this._editRef} onPointerDown={this.showLink} onClick={e => e.stopPropagation()}> <FontAwesomeIcon className="fa-icon" icon={!this.props.linkDoc.linkDisplay ? "eye-slash" : "eye"} size="sm" /></div> </Tooltip> <Tooltip title={<><div className="dash-tooltip">{!this.props.linkDoc.linkAutoMove ? "Auto move dot" : "Freeze dot position"}</div></>}> - <div className="button" ref={this._editRef} onPointerDown={this.autoMove}> + <div className="button" ref={this._editRef} onPointerDown={this.autoMove} onClick={e => e.stopPropagation()}> <FontAwesomeIcon className="fa-icon" icon={this.props.linkDoc.linkAutoMove ? "play" : "pause"} size="sm" /></div> </Tooltip> <Tooltip title={<><div className="dash-tooltip">Edit Link</div></>}> - <div className="button" ref={this._editRef} onPointerDown={this.onEdit}> + <div className="button" ref={this._editRef} onPointerDown={this.onEdit} onClick={e => e.stopPropagation()}> <FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div> </Tooltip> <Tooltip title={<><div className="dash-tooltip">Delete Link</div></>}> - <div className="button" onPointerDown={this.deleteLink}> + <div className="button" onPointerDown={this.deleteLink} onClick={e => e.stopPropagation()}> <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div> </Tooltip> </div> |
