diff options
Diffstat (limited to 'src/client/views/linking')
| -rw-r--r-- | src/client/views/linking/LinkEditor.scss | 170 | ||||
| -rw-r--r-- | src/client/views/linking/LinkEditor.tsx | 152 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenu.scss | 81 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenu.tsx | 42 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenuGroup.tsx | 16 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenuItem.scss | 120 | ||||
| -rw-r--r-- | src/client/views/linking/LinkMenuItem.tsx | 128 |
7 files changed, 630 insertions, 79 deletions
diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index b47c8976e..d26b7920a 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -3,38 +3,188 @@ .linkEditor { width: 100%; height: auto; - font-size: 12px; // TODO + font-size: 13px; // TODO user-select: none; } -.linkEditor-back { - margin-bottom: 6px; +.linkEditor-button-back { + //margin-bottom: 6px; + border-radius: 10px; + width: 18px; + height: 18px; + padding: 0; + + &:hover { + cursor: pointer; + } } .linkEditor-info { - border-bottom: 0.5px solid $light-color-secondary; - padding-bottom: 6px; - margin-bottom: 6px; + //border-bottom: 0.5px solid $light-color-secondary; + //padding-bottom: 1px; + padding-top: 5px; + padding-left: 5px; + //margin-bottom: 6px; display: flex; justify-content: space-between; + color: black; .linkEditor-linkedTo { width: calc(100% - 26px); + padding-left: 5px; + padding-right: 5px; + + .linkEditor-downArrow { + &:hover { + cursor: pointer; + } + } + } +} + +.linkEditor-moreInfo { + margin-left: 12px; + padding-left: 13px; + padding-right: 6.5px; + padding-bottom: 4px; + font-size: 9px; + //font-style: italic; + text-decoration-color: grey; + + .button { + color: black; + + &:hover { + cursor: pointer; + } + } +} + +.linkEditor-description { + padding-left: 6.5px; + padding-right: 6.5px; + padding-bottom: 3.5px; + + .linkEditor-description-label { + text-decoration-color: black; + color: black; + } + + .linkEditor-description-input { + display: flex; + + .linkEditor-description-editing { + min-width: 85%; + //border: 1px solid grey; + //border-radius: 4px; + padding-left: 2px; + padding-right: 2px; + //margin-right: 4px; + color: black; + text-decoration-color: grey; + } + + .linkEditor-description-add-button { + display: inline; + /* float: right; */ + border-radius: 7px; + font-size: 9px; + background-color: black; + /* padding: 3px; */ + padding-top: 4px; + padding-left: 7px; + padding-bottom: 4px; + padding-right: 8px; + height: 80%; + color: white; + + &:hover { + cursor: pointer; + } + } } } -.linkEditor-button, .linkEditor-addbutton { +.linkEditor-followingDropdown { + padding-left: 6.5px; + padding-right: 6.5px; + padding-bottom: 6px; + + &:hover { + cursor: pointer; + } + + .linkEditor-followingDropdown-label { + color: black; + } + + .linkEditor-followingDropdown-dropdown { + + .linkEditor-followingDropdown-header { + + border: 1px solid grey; + border-radius: 4px; + //background-color: rgb(236, 236, 236); + padding-left: 2px; + padding-right: 2px; + text-decoration-color: black; + color: rgb(94, 94, 94); + + .linkEditor-followingDropdown-icon { + float: right; + color: black; + } + } + + .linkEditor-followingDropdown-optionsList { + padding-left: 3px; + padding-right: 3px; + + &:last-child { + border-bottom: none; + } + + .linkEditor-followingDropdown-option { + border: 0.25px solid grey; + //background-color: rgb(236, 236, 236); + padding-left: 2px; + padding-right: 2px; + color: grey; + text-decoration-color: grey; + font-size: 9px; + border-top: none; + + &:hover { + background-color: rgb(187, 220, 231); + } + } + + } + } + + +} + + + + + + +.linkEditor-button, +.linkEditor-addbutton { width: 18px; height: 18px; padding: 0; // font-size: 12px; border-radius: 10px; + &:disabled { background-color: gray; } } -.linkEditor-addbutton{ + +.linkEditor-addbutton { margin-left: 0px; } @@ -44,7 +194,7 @@ } .linkEditor-group { - background-color: $light-color-secondary; + background-color: $light-color-secondary; padding: 6px; margin: 3px 0; border-radius: 3px; @@ -56,7 +206,7 @@ .linkEditor-group-row-label { margin-right: 6px; - display:inline-block; + display: inline-block; } .linkEditor-metadata-row { diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 13b9a2459..04329182e 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -1,14 +1,20 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable } from "mobx"; +import { action, observable, computed, toJS } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../fields/Doc"; -import { StrCast } from "../../../fields/Types"; +import { Doc, Opt } from "../../../fields/Doc"; +import { StrCast, DateCast } from "../../../fields/Types"; import { Utils } from "../../../Utils"; import { LinkManager } from "../../util/LinkManager"; import './LinkEditor.scss'; import React = require("react"); +import { DocumentView } from "../nodes/DocumentView"; +import { DocumentLinksButton } from "../nodes/DocumentLinksButton"; +import { EditableView } from "../EditableView"; +import { RefObject } from "react"; +import { Tooltip } from "@material-ui/core"; +import { undoBatch } from "../../util/UndoManager"; library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus); @@ -281,27 +287,157 @@ interface LinkEditorProps { @observer export class LinkEditor extends React.Component<LinkEditorProps> { + @observable description = StrCast(LinkManager.currentLink?.description); + @observable openDropdown: boolean = false; + @observable showInfo: boolean = false; + @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; } + @observable private buttonColor: string = "black"; + + + //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION"; + + @undoBatch @action deleteLink = (): void => { LinkManager.Instance.deleteLink(this.props.linkDoc); this.props.showLinks(); } + @action + setDescripValue = (value: string) => { + if (LinkManager.currentLink) { + LinkManager.currentLink.description = value; + this.buttonColor = "rgb(62, 133, 55)"; + setTimeout(action(() => { + this.buttonColor = "black"; + }), 750); + return true; + } + } + + @action + onKey = (e: React.KeyboardEvent<HTMLInputElement>) => { + if (e.key === "Enter") { + this.setDescripValue(this.description); + document.getElementById('input')?.blur(); + } + } + + @action + onDown = () => { + this.setDescripValue(this.description); + } + + @action + handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { + this.description = e.target.value; + } + + + @computed + get editDescription() { + return <div className="linkEditor-description"> + <div className="linkEditor-description-label"> + Link Label:</div> + <div className="linkEditor-description-input"> + <div className="linkEditor-description-editing"> + <input + style={{ width: "100%" }} + id="input" + value={this.description} + placeholder={"enter link label"} + // color={"rgb(88, 88, 88)"} + onKeyDown={this.onKey} + onChange={this.handleChange} + ></input> + </div> + <div className="linkEditor-description-add-button" + style={{ backgroundColor: this.buttonColor }} + onPointerDown={this.onDown}>Set</div> + </div></div>; + } + + @action + changeDropdown = () => { + this.openDropdown = !this.openDropdown; + } + + @action + changeFollowBehavior = (follow: string) => { + this.openDropdown = false; + Doc.GetProto(this.props.linkDoc).followLinkLocation = follow; + } + + @computed + get followingDropdown() { + return <div className="linkEditor-followingDropdown"> + <div className="linkEditor-followingDropdown-label"> + Follow Behavior:</div> + <div className="linkEditor-followingDropdown-dropdown"> + <div className="linkEditor-followingDropdown-header" + onPointerDown={this.changeDropdown}> + {StrCast(this.props.linkDoc.followLinkLocation, "Default")} + <FontAwesomeIcon className="linkEditor-followingDropdown-icon" + icon={this.openDropdown ? "chevron-up" : "chevron-down"} + size={"lg"} /> + </div> + <div className="linkEditor-followingDropdown-optionsList" + style={{ display: this.openDropdown ? "" : "none" }}> + <div className="linkEditor-followingDropdown-option" + onPointerDown={() => this.changeFollowBehavior("Default")}> + Default + </div> + <div className="linkEditor-followingDropdown-option" + onPointerDown={() => this.changeFollowBehavior("onRight")}> + Always open in right tab + </div> + <div className="linkEditor-followingDropdown-option" + onPointerDown={() => this.changeFollowBehavior("inTab")}> + Always open in new tab + </div> + </div> + </div> + </div>; + } + + @action + changeInfo = () => { + this.showInfo = !this.showInfo; + } + render() { const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); const groups = [this.props.linkDoc].map(groupDoc => { - return <LinkGroupEditor key={"gred-" + StrCast(groupDoc.linkRelationship)} linkDoc={this.props.linkDoc} sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />; + return <LinkGroupEditor key={"gred-" + StrCast(groupDoc.linkRelationship)} linkDoc={this.props.linkDoc} + sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />; }); return !destination ? (null) : ( <div className="linkEditor"> - {this.props.hideback ? (null) : <button className="linkEditor-back" onPointerDown={() => this.props.showLinks()}><FontAwesomeIcon icon="arrow-left" size="sm" /></button>} <div className="linkEditor-info"> - <p className="linkEditor-linkedTo">editing link to: <b>{destination.proto?.title ?? destination.title ?? "untitled"}</b></p> - <button className="linkEditor-button" onPointerDown={() => this.deleteLink()} title="Delete link"><FontAwesomeIcon icon="trash" size="sm" /></button> + <Tooltip title={<><div className="dash-tooltip">Return to link menu</div></>} placement="top"> + <button className="linkEditor-button-back" + style={{ display: this.props.hideback ? "none" : "" }} + onClick={this.props.showLinks}> + <FontAwesomeIcon icon="arrow-left" size="sm" /> </button> + </Tooltip> + <p className="linkEditor-linkedTo">Editing Link to: <b>{ + destination.proto?.title ?? destination.title ?? "untitled"}</b></p> + <Tooltip title={<><div className="dash-tooltip">Show more link information</div></>} placement="top"> + <div className="linkEditor-downArrow"><FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} /></div> + </Tooltip> </div> - {groups.length > 0 ? groups : <div className="linkEditor-group">There are currently no relationships associated with this link.</div>} + {this.showInfo ? <div className="linkEditor-moreInfo"> + <div>{this.props.linkDoc.author ? <div> <b>Author:</b> {this.props.linkDoc.author}</div> : null}</div> + <div>{this.props.linkDoc.creationDate ? <div> <b>Creation Date:</b> + {DateCast(this.props.linkDoc.creationDate).toString()}</div> : null}</div> + </div> : null} + + <div>{this.editDescription}</div> + <div>{this.followingDropdown}</div> + + {/* {groups.length > 0 ? groups : <div className="linkEditor-group">There are currently no relationships associated with this link.</div>} */} </div> ); diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index c372e7098..98e4171f0 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -1,22 +1,67 @@ @import "../globalCssVariables"; .linkMenu { - width: 100%; + width: auto; height: auto; -} - -.linkMenu-list { - max-height: 200px; - overflow-y: scroll; position: absolute; - z-index: 10; - background: $link-color; - min-width: 150px + top: 0; + left: 0; + + .linkMenu-list { + + 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; + //border-radius: 5px; + //padding-top: 6.5px; + //padding-bottom: 6.5px; + //padding-left: 6.5px; + //padding-right: 2px; + //width: calc(auto + 50px); + + white-space: nowrap; + overflow-x: hidden; + width: 240px; + scrollbar-color: white; + + &:last-child { + border-bottom: none; + } + + &:hover { + 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 { - border-bottom: 0.5px solid lightgray; - padding: 5px 0; + border-bottom: 0.5px solid lightgray; + //@extend: 5px 0; &:last-child { @@ -24,15 +69,18 @@ } .linkMenu-group-name { - display: flex; + &:hover { p { background-color: lightgray; + } + p.expand-one { - width: calc(100% - 26px); + width: calc(100% + 20px); } + .linkEditor-tableButton { display: block; } @@ -40,7 +88,7 @@ p { width: 100%; - padding: 4px 6px; + //padding: 4px 6px; line-height: 12px; border-radius: 5px; font-weight: bold; @@ -50,7 +98,4 @@ display: none; } } -} - - - +}
\ No newline at end of file diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index de1d60a09..7b5fb0127 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -1,4 +1,4 @@ -import { action, observable } from "mobx"; +import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import { DocumentView } from "../nodes/DocumentView"; import { LinkEditor } from "./LinkEditor"; @@ -10,6 +10,8 @@ import { LinkMenuGroup } from "./LinkMenuGroup"; import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { DocumentLinksButton } from "../nodes/DocumentLinksButton"; +import { LinkDocPreview } from "../nodes/LinkDocPreview"; +import { isUndefined } from "util"; library.add(faTrash); @@ -24,12 +26,28 @@ interface Props { export class LinkMenu extends React.Component<Props> { @observable private _editingLink?: Doc; - @observable private _linkMenuRef: Opt<HTMLDivElement | null>; + @observable private _linkMenuRef = React.createRef<HTMLDivElement>(); + private _editorRef = React.createRef<HTMLDivElement>(); + + //@observable private _numLinks: number = 0; + + // @computed get overflow() { + // if (this._numLinks) { + // return "scroll"; + // } + // return "auto"; + // } @action onClick = (e: PointerEvent) => { - if (this._linkMenuRef?.contains(e.target as any)) { - DocumentLinksButton.EditLink = undefined; + + 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 @@ -70,11 +88,19 @@ export class LinkMenu extends React.Component<Props> { render() { const sourceDoc = this.props.docView.props.Document; const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); - return <div className="linkMenu-list" ref={(r) => this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}> - {!this._editingLink ? - this.renderAllGroups(groups) : - <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)} /> + return <div className="linkMenu" ref={this._linkMenuRef} > + {!this._editingLink ? <div className="linkMenu-list" style={{ + left: this.props.location[0], top: this.props.location[1] + }}> + {this.renderAllGroups(groups)} + </div> : <div className="linkMenu-listEditor" style={{ + left: this.props.location[0], top: this.props.location[1] + }}> + <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} + showLinks={action(() => this._editingLink = undefined)} /> + </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 89deb3a55..2ae87ac13 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -11,6 +11,7 @@ import { DocumentView } from "../nodes/DocumentView"; import './LinkMenu.scss'; import { LinkMenuItem, StartLinkTargetsDrag } from "./LinkMenuItem"; import React = require("react"); +import { Cast } from "../../../fields/Types"; interface LinkMenuGroupProps { sourceDoc: Doc; @@ -26,6 +27,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { private _drag = React.createRef<HTMLDivElement>(); private _table = React.createRef<HTMLDivElement>(); + private _menuRef = React.createRef<HTMLDivElement>(); onLinkButtonDown = (e: React.PointerEvent): void => { e.stopPropagation(); @@ -65,7 +67,8 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { render() { const groupItems = this.props.group.map(linkDoc => { - const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc); + const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc) || + LinkManager.Instance.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === this.props.sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null)); if (destination && this.props.sourceDoc) { return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]} groupType={this.props.groupType} @@ -74,17 +77,20 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { linkDoc={linkDoc} sourceDoc={this.props.sourceDoc} destinationDoc={destination} - showEditor={this.props.showEditor} />; + showEditor={this.props.showEditor} + menuRef={this._menuRef} />; } }); return ( - <div className="linkMenu-group"> - <div className="linkMenu-group-name"> + <div className="linkMenu-group" ref={this._menuRef}> + + {/* <div className="linkMenu-group-name"> <p ref={this._drag} onPointerDown={this.onLinkButtonDown} className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p> {this.props.groupType === "*" || this.props.groupType === "" ? <></> : this.viewGroupAsTable(this.props.groupType)} - </div> + </div> */} + <div className="linkMenu-group-wrapper"> {groupItems} </div> diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index fd0954f65..4e13ef8c8 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -4,25 +4,101 @@ // border-top: 0.5px solid $main-accent; position: relative; display: flex; - font-size: 12px; - + border-bottom: 0.5px solid black; + + padding-left: 6.5px; + padding-right: 2px; + + padding-top: 6.5px; + padding-bottom: 6.5px; + + background-color: white; + .linkMenu-name { position: relative; + width: auto; + + .linkMenu-text { + + padding: 4px 2px; + //display: inline; + + .linkMenu-source-title { + text-decoration: none; + color: rgb(43, 43, 43); + font-size: 7px; + padding-bottom: 0.75px; + + margin-left: 20px; + } + + + .linkMenu-title-wrapper { + + display: flex; + + .destination-icon-wrapper { + //border: 0.5px solid rgb(161, 161, 161); + margin-right: 2px; + padding-right: 2px; + + .destination-icon { + float: left; + width: 12px; + height: 12px; + padding: 1px; + margin-right: 3px; + color: rgb(161, 161, 161); + } + } + + .linkMenu-destination-title { + text-decoration: none; + color: rgb(85, 120, 196); + font-size: 14px; + padding-bottom: 2px; + padding-right: 4px; + margin-right: 4px; + float: right; + + &:hover { + text-decoration: underline; + color: rgb(60, 90, 156); + //display: inline; + text-overflow: break; + cursor: pointer; + } + } + } + + .linkMenu-description { + text-decoration: none; + font-style: italic; + color: rgb(95, 97, 102); + font-size: 10px; + margin-left: 20px; + max-width: 125px; + height: auto; + white-space: break-spaces; + } + + p { + padding-right: 4px; + line-height: 12px; + border-radius: 5px; + //overflow-wrap: break-word; + user-select: none; + } - p { - padding: 4px 6px; - line-height: 12px; - border-radius: 5px; - overflow-wrap: break-word; - user-select: none; } + } .linkMenu-item-content { width: 100%; } - + .link-metadata { padding: 0 10px 0 16px; margin-bottom: 4px; @@ -32,36 +108,40 @@ } &:hover { + + background-color: rgb(201, 239, 252); + .linkMenu-item-buttons { display: flex; } + .linkMenu-item-content { - &.expand-two p { - width: calc(100% - 52px); - background-color: lightgray; - } - &.expand-three p { - width: calc(100% - 84px); - background-color: lightgray; + + .linkMenu-destination-title { + text-decoration: underline; + color: rgb(60, 90, 156); + //display: inline; + text-overflow: break; } } } } .linkMenu-item-buttons { - display: none; + //@extend: right; position: absolute; top: 50%; right: 0; transform: translateY(-50%); + display: none; .button { width: 20px; height: 20px; margin: 0; - margin-right: 6px; + margin-right: 4px; + padding-right: 6px; border-radius: 50%; - cursor: pointer; pointer-events: auto; background-color: $dark-color; color: $light-color; @@ -80,8 +160,10 @@ &:last-child { margin-right: 0; } + &:hover { background: $main-accent; + cursor: pointer; } } }
\ No newline at end of file diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index edc18b6a9..d1c839c3b 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt, faEyeSlash } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; import { Doc, DocListCast } from '../../../fields/Doc'; @@ -13,7 +13,12 @@ import React = require("react"); import { DocumentManager } from '../../util/DocumentManager'; import { setupMoveUpEvents, emptyFunction } from '../../../Utils'; import { DocumentView } from '../nodes/DocumentView'; -library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); +import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; +import { LinkDocPreview } from '../nodes/LinkDocPreview'; +import { Tooltip } from '@material-ui/core'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { undoBatch } from '../../util/UndoManager'; +library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt, faEyeSlash); interface LinkMenuItemProps { @@ -24,6 +29,7 @@ interface LinkMenuItemProps { destinationDoc: Doc; showEditor: (linkDoc: Doc) => void; addDocTab: (document: Doc, where: string) => boolean; + menuRef: React.Ref<HTMLDivElement>; } // drag links and drop link targets (aliasing them if needed) @@ -69,10 +75,13 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { private _eleClone: any; _editRef = React.createRef<HTMLDivElement>(); + _buttonRef = React.createRef<HTMLDivElement>(); + @observable private _showMore: boolean = false; @action toggleShowMore(e: React.PointerEvent) { e.stopPropagation(); this._showMore = !this._showMore; } onEdit = (e: React.PointerEvent): void => { + LinkManager.currentLink = this.props.linkDoc; setupMoveUpEvents(this, e, this.editMoved, emptyFunction, () => this.props.showEditor(this.props.linkDoc)); } @@ -95,6 +104,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { return (<div className="link-metadata">{mdRows}</div>); } + @action onLinkButtonDown = (e: React.PointerEvent): void => { this._downX = e.clientX; this._downY = e.clientY; @@ -104,6 +114,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { 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 => { @@ -124,7 +138,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { e.stopPropagation(); } + @action onContextMenu = (e: React.MouseEvent) => { + DocumentLinksButton.EditLink = undefined; + LinkDocPreview.LinkInfo = undefined; e.preventDefault(); ContextMenu.Instance.addItem({ description: "Follow Default Link", event: () => this.followDefault(), icon: "arrow-right" }); ContextMenu.Instance.displayMenu(e.clientX, e.clientY); @@ -132,25 +149,114 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { @action.bound async followDefault() { - DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false); + DocumentLinksButton.EditLink = undefined; + LinkDocPreview.LinkInfo = undefined; + + if (this.props.linkDoc.followLinkLocation && this.props.linkDoc.followLinkLocation !== "Default") { + this.props.addDocTab(this.props.destinationDoc, StrCast(this.props.linkDoc.followLinkLocation)); + } else { + DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false); + } + } + + @undoBatch + @action + deleteLink = (): void => { + LinkManager.Instance.deleteLink(this.props.linkDoc); + LinkDocPreview.LinkInfo = undefined; + DocumentLinksButton.EditLink = undefined; + } + + @action + showLink = () => { + this.props.linkDoc.hidden = !this.props.linkDoc.hidden; } render() { const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); const canExpand = keys ? keys.length > 0 : false; + const eyeIcon = this.props.linkDoc.hidden ? "eye-slash" : "eye"; + + let destinationIcon: FontAwesomeIconProps["icon"] = "question"; + switch (this.props.destinationDoc.type) { + case DocumentType.IMG: destinationIcon = "image"; break; + case DocumentType.COMPARISON: destinationIcon = "columns"; break; + case DocumentType.RTF: destinationIcon = "font"; break; + case DocumentType.COL: destinationIcon = "folder"; break; + case DocumentType.WEB: destinationIcon = "globe-asia"; break; + case DocumentType.SCREENSHOT: destinationIcon = "photo-video"; break; + case DocumentType.WEBCAM: destinationIcon = "video"; break; + case DocumentType.AUDIO: destinationIcon = "microphone"; break; + case DocumentType.BUTTON: destinationIcon = "bolt"; break; + case DocumentType.PRES: destinationIcon = "tv"; break; + case DocumentType.QUERY: destinationIcon = "search"; break; + case DocumentType.SCRIPTING: destinationIcon = "terminal"; break; + case DocumentType.IMPORT: destinationIcon = "cloud-upload-alt"; break; + case DocumentType.DOCHOLDER: destinationIcon = "expand"; break; + case DocumentType.VID: destinationIcon = "video"; break; + case DocumentType.INK: destinationIcon = "pen-nib"; break; + default: destinationIcon = "question"; break; + } + + const title = StrCast(this.props.destinationDoc.title).length > 18 ? + StrCast(this.props.destinationDoc.title).substr(0, 14) + "..." : this.props.destinationDoc.title; + + // ... + // from anika to bob: here's where the text that is specifically linked would show up (linkDoc.storedText) + // ... + 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 : undefined : undefined; + return ( <div className="linkMenu-item"> <div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}> - <div ref={this._drag} className="linkMenu-name" title="drag to view target. click to customize." onPointerDown={this.onLinkButtonDown}> - <p >{StrCast(this.props.destinationDoc.title)}</p> - <div className="linkMenu-item-buttons"> + + <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 = { + addDocTab: this.props.addDocTab, + linkSrc: this.props.sourceDoc, + linkDoc: this.props.linkDoc, + Location: [e.clientX, e.clientY + 20] + }))} + onPointerDown={this.onLinkButtonDown}> + + <div className="linkMenu-text"> + {source ? <p className="linkMenu-source-title"> + <b>Source: {source}</b></p> : null} + <div className="linkMenu-title-wrapper"> + <div className="destination-icon-wrapper" > + <FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" /></div> + <p className="linkMenu-destination-title" + onPointerDown={this.followDefault}> + {title} + </p> + </div> + {this.props.linkDoc.description !== "" ? <p className="linkMenu-description"> + {StrCast(this.props.linkDoc.description)}</p> : null} </div> + + <div className="linkMenu-item-buttons" ref={this._buttonRef} > {canExpand ? <div title="Show more" className="button" onPointerDown={e => this.toggleShowMore(e)}> <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>} - <div title="Edit link" className="button" ref={this._editRef} onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div> - <div title="Follow link" className="button" onClick={this.followDefault} onContextMenu={this.onContextMenu}> - <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /> - </div> + + <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.hidden ? "Show link" : "Hide link"}</div></>}> + <div className="button" ref={this._editRef} onPointerDown={this.showLink}> + <FontAwesomeIcon className="fa-icon" icon={eyeIcon} size="sm" /></div> + </Tooltip> + + <Tooltip title={<><div className="dash-tooltip">Edit Link</div></>}> + <div className="button" ref={this._editRef} onPointerDown={this.onEdit}> + <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}> + <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div> + </Tooltip> + {/* <div title="Follow link" className="button" onPointerDown={this.followDefault} onContextMenu={this.onContextMenu}> + <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div> */} </div> </div> {this._showMore ? this.renderMetadata() : <></>} |
